summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-11-30 12:21:33 +0100
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2016-11-30 12:21:33 +0100
commit00ca7adca2de8ff05cca3df9eb2df8a67f638cfe (patch)
tree86764da0f493a7169b2fea59e5618b669cd64d56 /app
parentadb3f3d4e494e8f8d41c1b9e676e395a49cd96b2 (diff)
parent7e5fa10b665835e3160eee4d333a17fbaef9c113 (diff)
downloadgitlab-ce-00ca7adca2de8ff05cca3df9eb2df8a67f638cfe.tar.gz
Merge branch 'master' into fix/rename-mwbs-to-merge-when-pipeline-succeeds
* master: (110 commits) Rewrite an HTTP link to use HTTPS Edit /spec/features/profiles/preferences_spec.rb to match changes in 084d90ac Add blue back to sub nav active Remove JSX/React eslint plugins. Fix a transient spec failure Adds hoverstates for collapsed Issue/Merge Request sidebar Moved groups above projects Add StackProf to the Gemfile, along with a utility to get a profile for a spec Update Sidekiq-cron to fix compatibility issues with Sidekiq 4.2.1 Add a CHANGELOG entry Alert user when logged in user email is not the same as the invitation Expose timestamp in build entity used by serializer Rename `MergeRequest#pipeline` to `head_pipeline` Remove unnecessary database indexes CE-specific changes gitlab-org/gitlab-ee#1137 Fixing typo & Clarifying Key name fix started_at check fix blob controller spec failure - updated not to use file-path- fix blob controller spec failure Merge branch 'jej-use-issuable-finder-instead-of-access-check' into 'security' ... Conflicts: app/controllers/projects/merge_requests_controller.rb lib/api/merge_requests.rb spec/requests/api/merge_requests_spec.rb
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/cycle_analytics/components/total_time_component.js.es613
-rw-r--r--app/assets/javascripts/dispatcher.js.es63
-rw-r--r--app/assets/javascripts/environments/components/environment.js.es62
-rw-r--r--app/assets/javascripts/extensions/array.js8
-rw-r--r--app/assets/javascripts/extensions/array.js.es624
-rw-r--r--app/assets/javascripts/labels_select.js2
-rw-r--r--app/assets/javascripts/notes.js2
-rw-r--r--app/assets/javascripts/project_variables.js.es643
-rw-r--r--app/assets/stylesheets/framework/avatar.scss1
-rw-r--r--app/assets/stylesheets/framework/buttons.scss26
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss85
-rw-r--r--app/assets/stylesheets/framework/nav.scss51
-rw-r--r--app/assets/stylesheets/framework/variables.scss84
-rw-r--r--app/assets/stylesheets/mailers/highlighted_diff_email.scss (renamed from app/assets/stylesheets/mailers/repository_push_email.scss)0
-rw-r--r--app/assets/stylesheets/pages/issuable.scss16
-rw-r--r--app/assets/stylesheets/pages/projects.scss8
-rw-r--r--app/controllers/admin/application_settings_controller.rb1
-rw-r--r--app/controllers/concerns/lfs_request.rb (renamed from app/helpers/lfs_helper.rb)76
-rw-r--r--app/controllers/concerns/toggle_award_emoji.rb5
-rw-r--r--app/controllers/concerns/workhorse_request.rb13
-rw-r--r--app/controllers/help_controller.rb6
-rw-r--r--app/controllers/profiles/avatars_controller.rb1
-rw-r--r--app/controllers/projects/avatars_controller.rb1
-rw-r--r--app/controllers/projects/blob_controller.rb20
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/cycle_analytics_controller.rb2
-rw-r--r--app/controllers/projects/git_http_client_controller.rb16
-rw-r--r--app/controllers/projects/git_http_controller.rb12
-rw-r--r--app/controllers/projects/lfs_api_controller.rb21
-rw-r--r--app/controllers/projects/lfs_storage_controller.rb7
-rw-r--r--app/controllers/projects/merge_requests_controller.rb18
-rw-r--r--app/controllers/projects/todos_controller.rb8
-rw-r--r--app/finders/issuable_finder.rb12
-rw-r--r--app/finders/notes_finder.rb2
-rw-r--r--app/helpers/dropdowns_helper.rb2
-rw-r--r--app/helpers/groups_helper.rb6
-rw-r--r--app/helpers/issuables_helper.rb20
-rw-r--r--app/mailers/emails/notes.rb2
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/concerns/protected_branch_access.rb9
-rw-r--r--app/models/cycle_analytics.rb5
-rw-r--r--app/models/cycle_analytics/summary.rb5
-rw-r--r--app/models/discussion.rb12
-rw-r--r--app/models/event.rb10
-rw-r--r--app/models/issue.rb12
-rw-r--r--app/models/merge_request.rb20
-rw-r--r--app/models/note.rb13
-rw-r--r--app/models/project.rb21
-rw-r--r--app/models/protected_branch/merge_access_level.rb9
-rw-r--r--app/models/protected_branch/push_access_level.rb6
-rw-r--r--app/models/repository.rb14
-rw-r--r--app/models/user.rb50
-rw-r--r--app/serializers/analytics_build_entity.rb2
-rw-r--r--app/serializers/build_entity.rb3
-rw-r--r--app/serializers/entity_date_helper.rb2
-rw-r--r--app/services/ci/create_pipeline_service.rb12
-rw-r--r--app/services/ci/process_pipeline_service.rb22
-rw-r--r--app/services/git_hooks_service.rb6
-rw-r--r--app/services/issuable_base_service.rb10
-rw-r--r--app/services/labels/find_or_create_service.rb7
-rw-r--r--app/services/merge_requests/base_service.rb4
-rw-r--r--app/services/merge_requests/build_service.rb2
-rw-r--r--app/services/notes/delete_service.rb1
-rw-r--r--app/services/notes/update_service.rb1
-rw-r--r--app/services/projects/transfer_service.rb3
-rw-r--r--app/uploaders/avatar_uploader.rb6
-rw-r--r--app/views/admin/application_settings/_form.html.haml11
-rw-r--r--app/views/dashboard/_projects_head.html.haml6
-rw-r--r--app/views/dashboard/projects/_zero_authorized_projects.html.haml23
-rw-r--r--app/views/dashboard/todos/index.html.haml4
-rw-r--r--app/views/events/_event.html.haml19
-rw-r--r--app/views/events/event/_push.html.haml6
-rw-r--r--app/views/explore/groups/index.html.haml4
-rw-r--r--app/views/explore/projects/_filter.html.haml8
-rw-r--r--app/views/invites/show.html.haml13
-rw-r--r--app/views/layouts/_head.html.haml2
-rw-r--r--app/views/layouts/_user_styles.html.haml24
-rw-r--r--app/views/layouts/nav/_project.html.haml2
-rw-r--r--app/views/notify/_note_message.text.erb5
-rw-r--r--app/views/notify/_note_mr_or_commit_email.html.haml18
-rw-r--r--app/views/notify/_note_mr_or_commit_email.text.erb8
-rw-r--r--app/views/notify/_simple_diff.text.erb3
-rw-r--r--app/views/notify/note_commit_email.html.haml4
-rw-r--r--app/views/notify/note_commit_email.text.erb11
-rw-r--r--app/views/notify/note_merge_request_email.html.haml9
-rw-r--r--app/views/notify/note_merge_request_email.text.erb11
-rw-r--r--app/views/notify/repository_push_email.html.haml2
-rw-r--r--app/views/projects/blob/edit.html.haml2
-rw-r--r--app/views/projects/branches/index.html.haml4
-rw-r--r--app/views/projects/builds/_sidebar.html.haml2
-rw-r--r--app/views/projects/diffs/_file.html.haml2
-rw-r--r--app/views/projects/diffs/_line.html.haml2
-rw-r--r--app/views/projects/forks/index.html.haml4
-rw-r--r--app/views/projects/issues/_merge_requests.html.haml6
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml4
-rw-r--r--app/views/projects/merge_requests/_new_submit.html.haml8
-rw-r--r--app/views/projects/merge_requests/_show.html.haml5
-rw-r--r--app/views/projects/merge_requests/widget/_heading.html.haml2
-rw-r--r--app/views/projects/merge_requests/widget/_show.html.haml2
-rw-r--r--app/views/projects/tags/index.html.haml4
-rw-r--r--app/views/projects/variables/_table.html.haml4
-rw-r--r--app/views/projects/variables/index.html.haml1
-rw-r--r--app/views/search/_filter.html.haml4
-rw-r--r--app/views/shared/_group_form.html.haml5
-rw-r--r--app/views/shared/_sort_dropdown.html.haml4
-rw-r--r--app/views/shared/issuable/_filter.html.haml4
-rw-r--r--app/views/shared/issuable/_form.html.haml48
-rw-r--r--app/views/shared/issuable/_label_dropdown.html.haml2
-rw-r--r--app/views/shared/issuable/form/_description.html.haml15
-rw-r--r--app/views/shared/issuable/form/_metadata.html.haml38
-rw-r--r--app/workers/authorized_projects_worker.rb23
111 files changed, 698 insertions, 580 deletions
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6 b/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6
index b9675f50e31..0d85e1a4678 100644
--- a/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6
+++ b/app/assets/javascripts/cycle_analytics/components/total_time_component.js.es6
@@ -10,10 +10,15 @@
},
template: `
<span class="total-time">
- <template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
- <template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
- <template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
- <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
+ <template v-if="Object.keys(time).length">
+ <template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
+ <template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
+ <template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
+ <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
+ </template>
+ <template v-else>
+ --
+ </template>
</span>
`,
});
diff --git a/app/assets/javascripts/dispatcher.js.es6 b/app/assets/javascripts/dispatcher.js.es6
index c2d4670b7e9..16df4b0b005 100644
--- a/app/assets/javascripts/dispatcher.js.es6
+++ b/app/assets/javascripts/dispatcher.js.es6
@@ -208,6 +208,9 @@
new gl.ProtectedBranchCreate();
new gl.ProtectedBranchEditList();
break;
+ case 'projects:variables:index':
+ new gl.ProjectVariables();
+ break;
}
switch (path.first()) {
case 'admin':
diff --git a/app/assets/javascripts/environments/components/environment.js.es6 b/app/assets/javascripts/environments/components/environment.js.es6
index 35e183a9086..84faabf938a 100644
--- a/app/assets/javascripts/environments/components/environment.js.es6
+++ b/app/assets/javascripts/environments/components/environment.js.es6
@@ -181,7 +181,7 @@
<div class="environments-container">
<div class="environments-list-loading text-center" v-if="isLoading">
- <i class="fa fa-spinner spin"></i>
+ <i class="fa fa-spinner fa-spin"></i>
</div>
<div class="blank-state blank-state-no-icon"
diff --git a/app/assets/javascripts/extensions/array.js b/app/assets/javascripts/extensions/array.js
deleted file mode 100644
index fc6c130113d..00000000000
--- a/app/assets/javascripts/extensions/array.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/* eslint-disable no-extend-native, func-names, space-before-function-paren, semi, space-infix-ops, max-len */
-Array.prototype.first = function() {
- return this[0];
-}
-
-Array.prototype.last = function() {
- return this[this.length-1];
-}
diff --git a/app/assets/javascripts/extensions/array.js.es6 b/app/assets/javascripts/extensions/array.js.es6
new file mode 100644
index 00000000000..717566a4715
--- /dev/null
+++ b/app/assets/javascripts/extensions/array.js.es6
@@ -0,0 +1,24 @@
+/* eslint-disable no-extend-native, func-names, space-before-function-paren, semi, space-infix-ops, max-len */
+Array.prototype.first = function() {
+ return this[0];
+}
+
+Array.prototype.last = function() {
+ return this[this.length-1];
+}
+
+Array.prototype.find = Array.prototype.find || function(predicate, ...args) {
+ if (!this) throw new TypeError('Array.prototype.find called on null or undefined');
+ if (typeof predicate !== 'function') throw new TypeError('predicate must be a function');
+
+ const list = Object(this);
+ const thisArg = args[1];
+ let value = {};
+
+ for (let i = 0; i < list.length; i += 1) {
+ value = list[i];
+ if (predicate.call(thisArg, value, i, list)) return value;
+ }
+
+ return undefined;
+};
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 812d5cde685..f334f35594d 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
+/* eslint-disable no-useless-return, func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, no-unused-vars, one-var-declaration-per-line, prefer-template, no-new, consistent-return, object-shorthand, comma-dangle, no-shadow, no-param-reassign, brace-style, vars-on-top, quotes, no-lonely-if, no-else-return, no-undef, semi, dot-notation, no-empty, no-return-assign, camelcase, prefer-spread, padded-blocks, max-len */
(function() {
this.LabelsSelect = (function() {
function LabelsSelect() {
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 47e7b6f831b..0ca0e255595 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
+/* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, space-before-blocks, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, no-undef, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, semi, indent, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape, radix, padded-blocks, max-len */
/*= require autosave */
/*= require autosize */
diff --git a/app/assets/javascripts/project_variables.js.es6 b/app/assets/javascripts/project_variables.js.es6
new file mode 100644
index 00000000000..4ee2e49306d
--- /dev/null
+++ b/app/assets/javascripts/project_variables.js.es6
@@ -0,0 +1,43 @@
+(() => {
+ const HIDDEN_VALUE_TEXT = '******';
+
+ class ProjectVariables {
+ constructor() {
+ this.$revealBtn = $('.js-btn-toggle-reveal-values');
+ this.$revealBtn.on('click', this.toggleRevealState.bind(this));
+ }
+
+ toggleRevealState(e) {
+ e.preventDefault();
+
+ const oldStatus = this.$revealBtn.attr('data-status');
+ let newStatus = 'hidden';
+ let newAction = 'Reveal Values';
+
+ if (oldStatus === 'hidden') {
+ newStatus = 'revealed';
+ newAction = 'Hide Values';
+ }
+
+ this.$revealBtn.attr('data-status', newStatus);
+
+ const $variables = $('.variable-value');
+
+ $variables.each((_, variable) => {
+ const $variable = $(variable);
+ let newText = HIDDEN_VALUE_TEXT;
+
+ if (newStatus === 'revealed') {
+ newText = $variable.attr('data-value');
+ }
+
+ $variable.text(newText);
+ });
+
+ this.$revealBtn.text(newAction);
+ }
+ }
+
+ window.gl = window.gl || {};
+ window.gl.ProjectVariables = ProjectVariables;
+})();
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index ad0d387067f..c0dd1cb3667 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -80,6 +80,7 @@
border-radius: 0;
border: none;
height: auto;
+ width: 100%;
margin: 0;
align-self: center;
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 4a9aa0f8717..36f530af685 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -15,7 +15,7 @@
@include btn-default;
}
-@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border) {
+@mixin btn-outline($background, $text, $border, $hover-background, $hover-text, $hover-border, $active-background, $active-border) {
background-color: $background;
color: $text;
border-color: $border;
@@ -23,8 +23,14 @@
&:hover,
&:focus {
background-color: $hover-background;
- color: $hover-text;
border-color: $hover-border;
+ color: $hover-text;
+ }
+
+ &:active {
+ background-color: $active-background;
+ border-color: $active-border;
+ color: $hover-text;
}
}
@@ -82,11 +88,11 @@
}
@mixin btn-gray {
- @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-light, $gray-dark, $border-gray-dark, $gl-gray-dark);
+ @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, $gl-gray-dark);
}
@mixin btn-white {
- @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $btn-white-active);
+ @include btn-color($white-light, $border-color, $white-normal, $border-white-normal, $white-dark, $border-white-dark, $gl-text-color);
}
@mixin btn-with-margin {
@@ -139,11 +145,11 @@
&.btn-new,
&.btn-create,
&.btn-save {
- @include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light);
+ @include btn-outline($white-light, $border-green-light, $border-green-light, $green-light, $white-light, $border-green-light, $green-normal, $border-green-normal);
}
&.btn-remove {
- @include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
+ @include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
}
}
@@ -165,11 +171,11 @@
}
&.btn-close {
- @include btn-outline($white-light, $orange-normal, $orange-normal, $orange-light, $white-light, $orange-light);
+ @include btn-outline($white-light, $border-orange-light, $border-orange-light, $orange-light, $white-light, $border-orange-light, $orange-normal, $border-orange-normal);
}
&.btn-spam {
- @include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
+ @include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
}
&.btn-danger,
@@ -199,7 +205,7 @@
}
.fa-caret-down,
- .fa-caret-up {
+ .fa-chevron-down {
margin-left: 5px;
}
@@ -351,7 +357,7 @@
.btn-inverted {
&-secondary {
- @include btn-outline($white-light, $blue-normal, $blue-normal, $blue-light, $white-light, $blue-light);
+ @include btn-outline($white-light, $border-blue-light, $border-blue-light, $blue-light, $white-light, $border-blue-light, $blue-normal, $border-blue-normal);
}
}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 583c17e4a83..6d77aadd753 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -8,6 +8,12 @@
}
}
+@mixin chevron-active {
+ .fa-chevron-down {
+ color: $dropdown-toggle-hover-icon-color;
+ }
+}
+
.open {
.dropdown-menu,
.dropdown-menu-nav {
@@ -19,53 +25,27 @@
}
}
+ .dropdown-toggle,
.dropdown-menu-toggle {
+ @include chevron-active;
border-color: $dropdown-toggle-hover-border-color;
-
- .fa {
- color: $dropdown-toggle-hover-icon-color;
- }
}
}
-.dropdown-menu-toggle {
- position: relative;
- width: 160px;
- padding: 6px 20px 6px 10px;
+.dropdown-toggle {
+ padding: 6px 8px 6px 10px;
background-color: $dropdown-toggle-bg;
color: $dropdown-toggle-color;
font-size: 15px;
text-align: left;
border: 1px solid $border-color;
border-radius: $border-radius-base;
- text-overflow: ellipsis;
white-space: nowrap;
- overflow: hidden;
-
- .fa {
- position: absolute;
- top: 10px;
- right: 8px;
- color: $dropdown-toggle-icon-color;
-
- &.fa-spinner {
- font-size: 16px;
- margin-top: -8px;
- }
- }
&.no-outline {
outline: 0;
}
- &:hover, {
- border-color: $dropdown-toggle-hover-border-color;
-
- .fa {
- color: $dropdown-toggle-hover-icon-color;
- }
- }
-
&.large {
width: 200px;
}
@@ -86,6 +66,51 @@
max-width: 100%;
padding-right: 25px;
}
+
+ .fa {
+ color: $dropdown-toggle-icon-color;
+ }
+
+ .fa-chevron-down {
+ font-size: $dropdown-chevron-size;
+ position: relative;
+ top: -3px;
+ margin-left: 5px;
+ }
+
+ &:hover {
+ @include chevron-active;
+ border-color: $dropdown-toggle-hover-border-color;
+ }
+
+ &:focus:active {
+ @include chevron-active;
+ border-color: $dropdown-toggle-active-border-color;
+ }
+}
+
+.dropdown-menu-toggle {
+ @extend .dropdown-toggle;
+ padding-right: 20px;
+ position: relative;
+ width: 160px;
+ text-overflow: ellipsis;
+ overflow: hidden;
+
+ .fa {
+ position: absolute;
+
+ &.fa-spinner {
+ font-size: 16px;
+ margin-top: -8px;
+ }
+ }
+
+ .fa-chevron-down {
+ position: absolute;
+ top: 11px;
+ right: 8px;
+ }
}
.dropdown-menu,
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index ce864c2de5e..1839ffa0976 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -51,19 +51,26 @@
margin-bottom: -1px;
font-size: 15px;
line-height: 28px;
- color: #959494;
+ color: $note-toolbar-color;
border-bottom: 2px solid transparent;
&:hover,
&:active,
&:focus {
text-decoration: none;
+ border-bottom: 2px solid $gray-darkest;
+ color: $black;
+
+ .badge {
+ color: $black;
+ }
}
}
&.active a {
border-bottom: 2px solid $link-underline-blue;
color: $black;
+ font-weight: 600;
}
.badge {
@@ -85,14 +92,20 @@
li {
+ &.active a {
+ border-bottom: none;
+ color: $link-underline-blue;
+ }
+
a {
margin: 0;
padding: 11px 10px 9px;
- }
- &.active a {
- border-bottom: none;
- color: $link-underline-blue;
+ &:hover,
+ &:active,
+ &:focus {
+ border-bottom: none;
+ }
}
}
}
@@ -310,37 +323,9 @@
height: 51px;
li {
-
a {
padding-top: 10px;
}
-
- a,
- i {
- color: $layout-link-gray;
- }
-
- &.active {
-
- a,
- i {
- color: $black;
- }
-
- svg {
- path,
- polygon {
- fill: $black;
- }
- }
- }
-
- &:hover {
- a,
- i {
- color: $black;
- }
- }
}
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 750d99ebabe..8a9c279d124 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -12,67 +12,71 @@ $sidebar-breakpoint: 1024px;
/*
* Color schema
*/
+$darken-normal-factor: 7%;
+$darken-dark-factor: 10%;
+$darken-border-factor: 5%;
+
$white-light: #fff;
-$white-normal: #ededed;
-$white-dark: #ececec;
+$white-normal: darken($white-light, $darken-normal-factor);
+$white-dark: darken($white-light, $darken-dark-factor);
$gray-lightest: #fdfdfd;
$gray-light: #fafafa;
$gray-lighter: #f9f9f9;
-$gray-normal: #f5f5f5;
-$gray-dark: #ededed;
+$gray-normal: darken($gray-light, $darken-normal-factor);
+$gray-dark: darken($gray-light, $darken-dark-factor);
$gray-darker: #eee;
$gray-darkest: #c9c9c9;
-$green-light: #38ae67;
-$green-normal: #2faa60;
-$green-dark: #2ca05b;
+$green-light: #3cbd70;
+$green-normal: darken($green-light, $darken-normal-factor);
+$green-dark: darken($green-light, $darken-dark-factor);
$blue-light: #2ea8e5;
-$blue-normal: #2d9fd8;
-$blue-dark: #2897ce;
+$blue-normal: darken($blue-light, $darken-normal-factor);
+$blue-dark: darken($blue-light, $darken-dark-factor);
$blue-medium-light: #3498cb;
-$blue-medium: #2f8ebf;
-$blue-medium-dark: #2d86b4;
+$blue-medium: darken($blue-medium-light, $darken-normal-factor);
+$blue-medium-dark: darken($blue-medium-light, $darken-dark-factor);
$blue-light-transparent: rgba(44, 159, 216, 0.05);
$orange-light: #fc8a51;
-$orange-normal: #e75e40;
-$orange-dark: #ce5237;
+$orange-normal: darken($orange-light, $darken-normal-factor);
+$orange-dark: darken($orange-light, $darken-dark-factor);
$red-light: #e52c5a;
-$red-normal: #d22852;
-$red-dark: darken($red-normal, 5%);
+$red-normal: darken($red-light, $darken-normal-factor);
+$red-dark: darken($red-light, $darken-dark-factor);
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
-$border-white-light: #f1f2f4;
-$border-white-normal: #d6dae2;
-$border-white-dark: #c6cacf;
+$border-white-light: darken($white-light, $darken-border-factor);
+$border-white-normal: darken($white-normal, $darken-border-factor);
+$border-white-dark: darken($white-dark, $darken-border-factor);
-$border-gray-light: #dcdcdc;
-$border-gray-normal: #d7d7d7;
-$border-gray-dark: #c6cacf;
+$border-gray-light: darken($gray-light, $darken-border-factor);
+$border-gray-normal: darken($gray-normal, $darken-border-factor);
+$border-gray-dark: darken($gray-dark, $darken-border-factor);
$border-green-extra-light: #9adb84;
-$border-green-light: #2faa60;
-$border-green-normal: #2ca05b;
-$border-green-dark: #279654;
+$border-green-light: darken($green-light, $darken-border-factor);
+$border-green-normal: darken($green-normal, $darken-border-factor);
+$border-green-dark: darken($green-dark, $darken-border-factor);
-$border-blue-light: #2d9fd8;
-$border-blue-normal: #2897ce;
-$border-blue-dark: #258dc1;
+$border-blue-light: darken($blue-light, $darken-border-factor);
+$border-blue-normal: darken($blue-normal, $darken-border-factor);
+$border-blue-dark: darken($blue-dark, $darken-border-factor);
-$border-orange-light: #fc6d26;
-$border-orange-normal: #ce5237;
-$border-orange-dark: #c14e35;
+$border-orange-light: darken($orange-light, $darken-border-factor);
+$border-orange-normal: darken($orange-normal, $darken-border-factor);
+$border-orange-dark: darken($orange-dark, $darken-border-factor);
-$border-red-light: #d22852;
-$border-red-normal: #ca264f;
-$border-red-dark: darken($border-red-normal, 5%);
+$border-red-light: darken($red-light, $darken-border-factor);
+$border-red-normal: darken($red-normal, $darken-border-factor);
+$border-red-dark: darken($red-dark, $darken-border-factor);
$help-well-bg: $gray-light;
$help-well-border: #e5e5e5;
@@ -216,7 +220,7 @@ $dropdown-bg: #fff;
$dropdown-link-color: #555;
$dropdown-link-hover-bg: $row-hover;
$dropdown-empty-row-bg: rgba(#000, .04);
-$dropdown-border-color: rgba(#000, .1);
+$dropdown-border-color: $border-color;
$dropdown-shadow-color: rgba(#000, .1);
$dropdown-divider-color: rgba(#000, .1);
$dropdown-header-color: #959494;
@@ -225,13 +229,15 @@ $dropdown-input-color: #555;
$dropdown-input-focus-border: $focus-border-color;
$dropdown-input-focus-shadow: rgba($dropdown-input-focus-border, .4);
$dropdown-loading-bg: rgba(#fff, .6);
+$dropdown-chevron-size: 10px;
$dropdown-toggle-bg: #fff;
-$dropdown-toggle-color: #626262;
-$dropdown-toggle-border-color: #eaeaea;
-$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 15%);
+$dropdown-toggle-color: #5c5c5c;
+$dropdown-toggle-border-color: #e5e5e5;
+$dropdown-toggle-hover-border-color: darken($dropdown-toggle-border-color, 13%);
+$dropdown-toggle-active-border-color: darken($dropdown-toggle-border-color, 14%);
$dropdown-toggle-icon-color: #c4c4c4;
-$dropdown-toggle-hover-icon-color: $dropdown-toggle-hover-border-color;
+$dropdown-toggle-hover-icon-color: darken($dropdown-toggle-icon-color, 7%);
/*
* Buttons
@@ -255,7 +261,7 @@ $search-input-border-color: rgba(#4688f1, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: 220px;
$location-badge-color: #aaa;
-$location-badge-bg: $gray-normal;
+$location-badge-bg: $dark-background-color;
$location-badge-active-bg: #4f91f8;
$location-icon-color: #e7e9ed;
$location-icon-active-color: #807e7e;
diff --git a/app/assets/stylesheets/mailers/repository_push_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
index 8d1a6020ca4..8d1a6020ca4 100644
--- a/app/assets/stylesheets/mailers/repository_push_email.scss
+++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 773155fe80a..7aad99eee4e 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -132,7 +132,7 @@
display: none;
}
- .btn-clipboard {
+ .btn-clipboard:hover {
color: $gl-gray;
}
}
@@ -235,6 +235,10 @@
padding-bottom: 10px;
color: #999;
+ &:hover {
+ color: $gl-gray;
+ }
+
span {
display: block;
margin-top: 0;
@@ -244,15 +248,17 @@
display: none;
}
+ .avatar:hover {
+ border-color: #999;
+ }
+
.btn-clipboard {
border: none;
+ color: #999;
&:hover {
background: transparent;
- }
-
- i {
- color: #999;
+ color: $gl-gray;
}
}
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 19a7a97ea0d..0562ee7b178 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -876,3 +876,11 @@ pre.light-well {
pointer-events: none;
}
}
+
+.variables-table {
+ table-layout: fixed;
+
+ .variable-key {
+ width: 30%;
+ }
+}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index b81842e319b..c2bb8464824 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -112,6 +112,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:koding_enabled,
:koding_url,
:email_author_in_body,
+ :html_emails_enabled,
:repository_checks_enabled,
:metrics_packet_size,
:send_user_confirmation_email,
diff --git a/app/helpers/lfs_helper.rb b/app/controllers/concerns/lfs_request.rb
index 2425c3a8bc8..ed22b1e5470 100644
--- a/app/helpers/lfs_helper.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -1,5 +1,21 @@
-module LfsHelper
- include Gitlab::Routing.url_helpers
+# This concern assumes:
+# - a `#project` accessor
+# - a `#user` accessor
+# - a `#authentication_result` accessor
+# - a `#can?(object, action, subject)` method
+# - a `#ci?` method
+# - a `#download_request?` method
+# - a `#upload_request?` method
+# - a `#has_authentication_ability?(ability)` method
+module LfsRequest
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :require_lfs_enabled!
+ before_action :lfs_check_access!
+ end
+
+ private
def require_lfs_enabled!
return if Gitlab.config.lfs.enabled
@@ -17,35 +33,15 @@ module LfsHelper
return if download_request? && lfs_download_access?
return if upload_request? && lfs_upload_access?
- if project.public? || (user && user.can?(:read_project, project))
- render_lfs_forbidden
+ if project.public? || can?(user, :read_project, project)
+ lfs_forbidden!
else
render_lfs_not_found
end
end
- def lfs_download_access?
- return false unless project.lfs_enabled?
-
- ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
- end
-
- def objects
- @objects ||= (params[:objects] || []).to_a
- end
-
- def user_can_download_code?
- has_authentication_ability?(:download_code) && can?(user, :download_code, project)
- end
-
- def build_can_download_code?
- has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project)
- end
-
- def lfs_upload_access?
- return false unless project.lfs_enabled?
-
- has_authentication_ability?(:push_code) && can?(user, :push_code, project)
+ def lfs_forbidden!
+ render_lfs_forbidden
end
def render_lfs_forbidden
@@ -70,6 +66,30 @@ module LfsHelper
)
end
+ def lfs_download_access?
+ return false unless project.lfs_enabled?
+
+ ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
+ end
+
+ def lfs_upload_access?
+ return false unless project.lfs_enabled?
+
+ has_authentication_ability?(:push_code) && can?(user, :push_code, project)
+ end
+
+ def lfs_deploy_token?
+ authentication_result.lfs_deploy_token?(project)
+ end
+
+ def user_can_download_code?
+ has_authentication_ability?(:download_code) && can?(user, :download_code, project)
+ end
+
+ def build_can_download_code?
+ has_authentication_ability?(:build_download_code) && can?(user, :build_download_code, project)
+ end
+
def storage_project
@storage_project ||= begin
result = project
@@ -82,4 +102,8 @@ module LfsHelper
result
end
end
+
+ def objects
+ @objects ||= (params[:objects] || []).to_a
+ end
end
diff --git a/app/controllers/concerns/toggle_award_emoji.rb b/app/controllers/concerns/toggle_award_emoji.rb
index 3717c49f272..fbf9a026b10 100644
--- a/app/controllers/concerns/toggle_award_emoji.rb
+++ b/app/controllers/concerns/toggle_award_emoji.rb
@@ -1,11 +1,8 @@
module ToggleAwardEmoji
extend ActiveSupport::Concern
- included do
- before_action :authenticate_user!, only: [:toggle_award_emoji]
- end
-
def toggle_award_emoji
+ authenticate_user!
name = params.require(:name)
if awardable.user_can_award?(current_user, name)
diff --git a/app/controllers/concerns/workhorse_request.rb b/app/controllers/concerns/workhorse_request.rb
new file mode 100644
index 00000000000..43c0f1b173c
--- /dev/null
+++ b/app/controllers/concerns/workhorse_request.rb
@@ -0,0 +1,13 @@
+module WorkhorseRequest
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :verify_workhorse_api!
+ end
+
+ private
+
+ def verify_workhorse_api!
+ Gitlab::Workhorse.verify_api_request!(request.headers)
+ end
+end
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index 4b3c71874be..a10cdcce72b 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -6,9 +6,9 @@ class HelpController < ApplicationController
def index
@help_index = File.read(Rails.root.join('doc', 'README.md'))
- # Prefix Markdown links with `help/` unless they already have been
- # See http://rubular.com/r/ie2MlpdUMq
- @help_index.gsub!(/(\]\()(\/?help\/)?([^\)\(]+\))/, '\1/help/\3')
+ # Prefix Markdown links with `help/` unless they are external links
+ # See http://rubular.com/r/MioSrVLK3S
+ @help_index.gsub!(%r{(\]\()(?!.+://)([^\)\(]+\))}, '\1/help/\2')
end
def show
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
index f193adb46b4..daa51ae41df 100644
--- a/app/controllers/profiles/avatars_controller.rb
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -4,7 +4,6 @@ class Profiles::AvatarsController < Profiles::ApplicationController
@user.remove_avatar!
@user.save
- @user.reset_events_cache
redirect_to profile_path
end
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index ada7db3c552..53788687076 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -20,7 +20,6 @@ class Projects::AvatarsController < Projects::ApplicationController
@project.remove_avatar!
@project.save
- @project.reset_events_cache
redirect_to edit_project_path(@project)
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 56ced786311..9940263ae24 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -13,7 +13,6 @@ class Projects::BlobController < Projects::ApplicationController
before_action :assign_blob_vars
before_action :commit, except: [:new, :create]
before_action :blob, except: [:new, :create]
- before_action :from_merge_request, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
before_action :validate_diff_params, only: :diff
@@ -39,14 +38,6 @@ class Projects::BlobController < Projects::ApplicationController
def update
@path = params[:file_path] if params[:file_path].present?
- after_edit_path =
- if from_merge_request && @target_branch == @ref
- diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
- "##{hexdigest(@path)}"
- else
- namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
- end
-
create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
@@ -124,9 +115,14 @@ class Projects::BlobController < Projects::ApplicationController
render_404
end
- def from_merge_request
- # If blob edit was initiated from merge request page
- @from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
+ def after_edit_path
+ from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid])
+ if from_merge_request && @target_branch == @ref
+ diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
+ "##{hexdigest(@path)}"
+ else
+ namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
+ end
end
def editor_variables
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 6b9f37983c4..89d84809e3a 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -36,7 +36,7 @@ class Projects::BranchesController < Projects::ApplicationController
execute(branch_name, ref)
if params[:issue_iid]
- issue = @project.issues.find_by(iid: params[:issue_iid])
+ issue = IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:issue_iid])
SystemNoteService.new_issue_branch(issue, @project, current_user, branch_name) if issue
end
diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb
index fd263960b93..ac639ef015b 100644
--- a/app/controllers/projects/cycle_analytics_controller.rb
+++ b/app/controllers/projects/cycle_analytics_controller.rb
@@ -6,7 +6,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
before_action :authorize_read_cycle_analytics!
def show
- @cycle_analytics = ::CycleAnalytics.new(@project, from: start_date(cycle_analytics_params))
+ @cycle_analytics = ::CycleAnalytics.new(@project, current_user, from: start_date(cycle_analytics_params))
stats_values, cycle_analytics_json = generate_cycle_analytics_data
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index 3f41916e6d3..8714349e27f 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -18,6 +18,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController
private
+ def download_request?
+ raise NotImplementedError
+ end
+
+ def upload_request?
+ raise NotImplementedError
+ end
+
def authenticate_user
@authentication_result = Gitlab::Auth::Result.new
@@ -130,10 +138,6 @@ class Projects::GitHttpClientController < Projects::ApplicationController
authentication_result.ci?(project)
end
- def lfs_deploy_token?
- authentication_result.lfs_deploy_token?(project)
- end
-
def authentication_has_download_access?
has_authentication_ability?(:download_code) || has_authentication_ability?(:build_download_code)
end
@@ -149,8 +153,4 @@ class Projects::GitHttpClientController < Projects::ApplicationController
def authentication_project
authentication_result.project
end
-
- def verify_workhorse_api!
- Gitlab::Workhorse.verify_api_request!(request.headers)
- end
end
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index 13caeb42d40..9184dcccac5 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -1,7 +1,5 @@
-# This file should be identical in GitLab Community Edition and Enterprise Edition
-
class Projects::GitHttpController < Projects::GitHttpClientController
- before_action :verify_workhorse_api!
+ include WorkhorseRequest
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
@@ -67,14 +65,18 @@ class Projects::GitHttpController < Projects::GitHttpClientController
end
def render_denied
- if user && user.can?(:read_project, project)
- render plain: 'Access denied', status: :forbidden
+ if user && can?(user, :read_project, project)
+ render plain: access_denied_message, status: :forbidden
else
# Do not leak information about project existence
render_not_found
end
end
+ def access_denied_message
+ 'Access denied'
+ end
+
def upload_pack_allowed?
return false unless Gitlab.config.gitlab_shell.upload_pack
diff --git a/app/controllers/projects/lfs_api_controller.rb b/app/controllers/projects/lfs_api_controller.rb
index 2d493276941..440259b643c 100644
--- a/app/controllers/projects/lfs_api_controller.rb
+++ b/app/controllers/projects/lfs_api_controller.rb
@@ -1,8 +1,7 @@
class Projects::LfsApiController < Projects::GitHttpClientController
- include LfsHelper
+ include LfsRequest
- before_action :require_lfs_enabled!
- before_action :lfs_check_access!, except: [:deprecated]
+ skip_before_action :lfs_check_access!, only: [:deprecated]
def batch
unless objects.present?
@@ -31,6 +30,14 @@ class Projects::LfsApiController < Projects::GitHttpClientController
private
+ def download_request?
+ params[:operation] == 'download'
+ end
+
+ def upload_request?
+ params[:operation] == 'upload'
+ end
+
def existing_oids
@existing_oids ||= begin
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
@@ -79,12 +86,4 @@ class Projects::LfsApiController < Projects::GitHttpClientController
}
}
end
-
- def download_request?
- params[:operation] == 'download'
- end
-
- def upload_request?
- params[:operation] == 'upload'
- end
end
diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb
index 9005b104e90..32759672b6c 100644
--- a/app/controllers/projects/lfs_storage_controller.rb
+++ b/app/controllers/projects/lfs_storage_controller.rb
@@ -1,9 +1,8 @@
class Projects::LfsStorageController < Projects::GitHttpClientController
- include LfsHelper
+ include LfsRequest
+ include WorkhorseRequest
- before_action :require_lfs_enabled!
- before_action :lfs_check_access!
- before_action :verify_workhorse_api!, only: [:upload_authorize]
+ skip_before_action :verify_workhorse_api!, only: [:download, :upload_finalize]
def download
lfs_object = LfsObject.find_by_oid(oid)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 707cc018a12..c7cde9e6a14 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -329,17 +329,18 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present?
- unless @merge_request.pipeline
+ unless @merge_request.head_pipeline
@status = :failed
return
end
- if @merge_request.pipeline.active?
+ if @merge_request.head_pipeline.active?
MergeRequests::MergeWhenPipelineSucceedsService
.new(@project, current_user, merge_params)
.execute(@merge_request)
+
@status = :merge_when_build_succeeds
- elsif @merge_request.pipeline.success?
+ elsif @merge_request.head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time
MergeWorker.perform_async(@merge_request.id, current_user.id, params)
@@ -403,7 +404,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def ci_status
- pipeline = @merge_request.pipeline
+ pipeline = @merge_request.head_pipeline
+
if pipeline
status = pipeline.status
coverage = pipeline.try(:coverage)
@@ -539,7 +541,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def define_widget_vars
- @pipeline = @merge_request.pipeline
+ @pipeline = @merge_request.head_pipeline
end
def define_commit_vars
@@ -568,8 +570,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_pipelines_vars
@pipelines = @merge_request.all_pipelines
- @pipeline = @merge_request.pipeline
- @statuses = @pipeline.statuses.relevant if @pipeline.present?
+ @pipeline = @merge_request.head_pipeline
+ @statuses_count = @pipeline.present? ? @pipeline.statuses.relevant.count : 0
end
def define_new_vars
@@ -636,7 +638,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge_when_build_succeeds_active?
params[:merge_when_build_succeeds].present? &&
- @merge_request.pipeline && @merge_request.pipeline.active?
+ @merge_request.head_pipeline && @merge_request.head_pipeline.active?
end
def build_merge_request
diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb
index 5685d0f4e7c..52517381c65 100644
--- a/app/controllers/projects/todos_controller.rb
+++ b/app/controllers/projects/todos_controller.rb
@@ -16,13 +16,7 @@ class Projects::TodosController < Projects::ApplicationController
@issuable ||= begin
case params[:issuable_type]
when "issue"
- issue = @project.issues.find(params[:issuable_id])
-
- if can?(current_user, :read_issue, issue)
- issue
- else
- render_404
- end
+ IssuesFinder.new(current_user, project_id: @project.id).find(params[:issuable_id])
when "merge_request"
@project.merge_requests.find(params[:issuable_id])
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 6297b2db369..9a74e36870b 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -16,14 +16,12 @@
# label_name: string
# sort: string
#
-require_relative 'projects_finder'
-
class IssuableFinder
NONE = '0'
attr_accessor :current_user, :params
- def initialize(current_user, params)
+ def initialize(current_user, params = {})
@current_user = current_user
@params = params
end
@@ -43,6 +41,14 @@ class IssuableFinder
sort(items)
end
+ def find(*params)
+ execute.find(*params)
+ end
+
+ def find_by(*params)
+ execute.find_by(*params)
+ end
+
def group
return @group if defined?(@group)
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 0b7832e6583..a653a6d59c6 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -12,7 +12,7 @@ class NotesFinder
when "commit"
project.notes.for_commit_id(target_id).non_diff_notes
when "issue"
- project.issues.visible_to_user(current_user).find(target_id).notes.inc_author
+ IssuesFinder.new(current_user, project_id: project.id).find(target_id).notes.inc_author
when "merge_request"
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author
when "snippet", "project_snippet"
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index cbab1fd5967..81e0b6bb5ae 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -43,7 +43,7 @@ module DropdownsHelper
default_label = data_attr[:default_label]
content_tag(:button, class: "dropdown-menu-toggle #{options[:toggle_class] if options.has_key?(:toggle_class)}", id: (options[:id] if options.has_key?(:id)), type: "button", data: data_attr) do
output = content_tag(:span, toggle_text, class: "dropdown-toggle-text #{'is-default' if toggle_text == default_label}")
- output << icon('caret-down')
+ output << icon('chevron-down')
output.html_safe
end
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 75cd9eece5c..19ab059aea6 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -8,11 +8,7 @@ module GroupsHelper
group = Group.find_by(path: group)
end
- if group && group.avatar.present?
- group.avatar.url
- else
- image_path('no_group_avatar.png')
- end
+ group.try(:avatar_url) || image_path('no_group_avatar.png')
end
def group_title(group, name = nil, url = nil)
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 8bebda07787..6584aa3edd5 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -143,6 +143,20 @@ module IssuablesHelper
end
end
+ def issuable_filter_params
+ [
+ :search,
+ :author_id,
+ :assignee_id,
+ :milestone_title,
+ :label_name
+ ]
+ end
+
+ def issuable_filter_present?
+ issuable_filter_params.any? { |k| params.key?(k) }
+ end
+
private
def assigned_issuables_count(assignee, issuable_type, state)
@@ -165,13 +179,9 @@ module IssuablesHelper
end
end
- def issuable_filters_present
- params[:search] || params[:author_id] || params[:assignee_id] || params[:milestone_title] || params[:label_name]
- end
-
def issuables_count_for_state(issuable_type, state)
issuables_finder = public_send("#{issuable_type}_finder")
-
+
params = issuables_finder.params.merge(state: state)
finder = issuables_finder.class.new(issuables_finder.current_user, params)
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index 96116e916dd..0d20c9092c4 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -4,6 +4,7 @@ module Emails
setup_note_mail(note_id, recipient_id)
@commit = @note.noteable
+ @discussion = @note.to_discussion if @note.diff_note?
@target_url = namespace_project_commit_url(*note_target_url_options)
mail_answer_thread(@commit,
@@ -24,6 +25,7 @@ module Emails
setup_note_mail(note_id, recipient_id)
@merge_request = @note.noteable
+ @discussion = @note.to_discussion if @note.diff_note?
@target_url = namespace_project_merge_request_url(*note_target_url_options)
mail_answer_thread(@merge_request, note_thread_options(recipient_id))
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 4294a10e9e3..fabbf97d4db 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -317,7 +317,7 @@ module Ci
def merge_requests
@merge_requests ||= project.merge_requests
.where(source_branch: self.ref)
- .select { |merge_request| merge_request.pipeline.try(:id) == self.id }
+ .select { |merge_request| merge_request.head_pipeline.try(:id) == self.id }
end
private
diff --git a/app/models/concerns/protected_branch_access.rb b/app/models/concerns/protected_branch_access.rb
index 7fd0905ee81..9dd4d9c6f24 100644
--- a/app/models/concerns/protected_branch_access.rb
+++ b/app/models/concerns/protected_branch_access.rb
@@ -2,6 +2,9 @@ module ProtectedBranchAccess
extend ActiveSupport::Concern
included do
+ belongs_to :protected_branch
+ delegate :project, to: :protected_branch
+
scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
end
@@ -9,4 +12,10 @@ module ProtectedBranchAccess
def humanize
self.class.human_access_levels[self.access_level]
end
+
+ def check_access(user)
+ return true if user.is_admin?
+
+ project.team.max_member_access(user.id) >= access_level
+ end
end
diff --git a/app/models/cycle_analytics.rb b/app/models/cycle_analytics.rb
index cb8e088d21d..ba4ee6fcf9d 100644
--- a/app/models/cycle_analytics.rb
+++ b/app/models/cycle_analytics.rb
@@ -1,14 +1,15 @@
class CycleAnalytics
STAGES = %i[issue plan code test review staging production].freeze
- def initialize(project, from:)
+ def initialize(project, current_user, from:)
@project = project
+ @current_user = current_user
@from = from
@fetcher = Gitlab::CycleAnalytics::MetricsFetcher.new(project: project, from: from, branch: nil)
end
def summary
- @summary ||= Summary.new(@project, from: @from)
+ @summary ||= Summary.new(@project, @current_user, from: @from)
end
def permissions(user:)
diff --git a/app/models/cycle_analytics/summary.rb b/app/models/cycle_analytics/summary.rb
index b46db449bf3..82f53d17ddd 100644
--- a/app/models/cycle_analytics/summary.rb
+++ b/app/models/cycle_analytics/summary.rb
@@ -1,12 +1,13 @@
class CycleAnalytics
class Summary
- def initialize(project, from:)
+ def initialize(project, current_user, from:)
@project = project
+ @current_user = current_user
@from = from
end
def new_issues
- @project.issues.created_after(@from).count
+ IssuesFinder.new(@current_user, project_id: @project.id).execute.created_after(@from).count
end
def commits
diff --git a/app/models/discussion.rb b/app/models/discussion.rb
index de06c13481a..75a85563235 100644
--- a/app/models/discussion.rb
+++ b/app/models/discussion.rb
@@ -25,7 +25,12 @@ class Discussion
to: :last_resolved_note,
allow_nil: true
- delegate :blob, :highlighted_diff_lines, to: :diff_file, allow_nil: true
+ delegate :blob,
+ :highlighted_diff_lines,
+ :diff_lines,
+
+ to: :diff_file,
+ allow_nil: true
def self.for_notes(notes)
notes.group_by(&:discussion_id).values.map { |notes| new(notes) }
@@ -159,10 +164,11 @@ class Discussion
end
# Returns an array of at most 16 highlighted lines above a diff note
- def truncated_diff_lines
+ def truncated_diff_lines(highlight: true)
+ lines = highlight ? highlighted_diff_lines : diff_lines
prev_lines = []
- highlighted_diff_lines.each do |line|
+ lines.each do |line|
if line.meta?
prev_lines.clear
else
diff --git a/app/models/event.rb b/app/models/event.rb
index 21eaca917b8..2662f170765 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -43,12 +43,6 @@ class Event < ActiveRecord::Base
scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
class << self
- def reset_event_cache_for(target)
- Event.where(target_id: target.id, target_type: target.class.to_s).
- order('id DESC').limit(100).
- update_all(updated_at: Time.now)
- end
-
# Update Gitlab::ContributionsCalendar#activity_dates if this changes
def contributions
where("action = ? OR (target_type in (?) AND action in (?))",
@@ -353,6 +347,10 @@ class Event < ActiveRecord::Base
update_all(last_activity_at: created_at)
end
+ def authored_by?(user)
+ user ? author_id == user.id : false
+ end
+
private
def recent_update?
diff --git a/app/models/issue.rb b/app/models/issue.rb
index dd0cb75f9a8..fbf07040301 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -182,18 +182,6 @@ class Issue < ActiveRecord::Base
branches_with_iid - branches_with_merge_request
end
- # Reset issue events cache
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when an issue is updated
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.reset_event_cache_for(self)
- end
-
# To allow polymorphism with MergeRequest.
def source_project
project
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index fdf54cc8a7e..64990f8134e 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -605,18 +605,6 @@ class MergeRequest < ActiveRecord::Base
self.target_project.repository.branch_names.include?(self.target_branch)
end
- # Reset merge request events cache
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when a merge request is updated
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.reset_event_cache_for(self)
- end
-
def merge_commit_message
message = "Merge branch '#{source_branch}' into '#{target_branch}'\n\n"
message << "#{title}\n\n"
@@ -690,7 +678,7 @@ class MergeRequest < ActiveRecord::Base
def mergeable_ci_state?
return true unless project.only_allow_merge_if_build_succeeds?
- !pipeline || pipeline.success? || pipeline.skipped?
+ !head_pipeline || head_pipeline.success? || head_pipeline.skipped?
end
def environments
@@ -786,14 +774,14 @@ class MergeRequest < ActiveRecord::Base
commits.map(&:sha)
end
- def pipeline
+ def head_pipeline
return unless diff_head_sha && source_project
- @pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha)
+ @head_pipeline ||= source_project.pipeline_for(source_branch, diff_head_sha)
end
def all_pipelines
- return unless source_project
+ return Ci::Pipeline.none unless source_project
@all_pipelines ||= source_project.pipelines
.where(sha: all_commits_sha, ref: source_branch)
diff --git a/app/models/note.rb b/app/models/note.rb
index ed4224e3046..5b50ca285c3 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -201,19 +201,6 @@ class Note < ActiveRecord::Base
super(noteable_type.to_s.classify.constantize.base_class.to_s)
end
- # Reset notes events cache
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when a note is updated
- # * when a note is removed
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.reset_event_cache_for(self)
- end
-
def editable?
!system?
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 9256e9ddd95..f01cb613b85 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -687,9 +687,9 @@ class Project < ActiveRecord::Base
self.id
end
- def get_issue(issue_id)
+ def get_issue(issue_id, current_user)
if default_issues_tracker?
- issues.find_by(iid: issue_id)
+ IssuesFinder.new(current_user, project_id: id).find_by(iid: issue_id)
else
ExternalIssue.new(issue_id, self)
end
@@ -976,7 +976,6 @@ class Project < ActiveRecord::Base
begin
gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki")
send_move_instructions(old_path_with_namespace)
- reset_events_cache
@old_path_with_namespace = old_path_with_namespace
@@ -1043,22 +1042,6 @@ class Project < ActiveRecord::Base
attrs
end
- # Reset events cache related to this project
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when project was moved
- # * when project was renamed
- # * when the project avatar changes
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.where(project_id: self.id).
- order('id DESC').limit(100).
- update_all(updated_at: Time.now)
- end
-
def project_member(user)
project_members.find_by(user_id: user)
end
diff --git a/app/models/protected_branch/merge_access_level.rb b/app/models/protected_branch/merge_access_level.rb
index 806b3ccd275..771e3376613 100644
--- a/app/models/protected_branch/merge_access_level.rb
+++ b/app/models/protected_branch/merge_access_level.rb
@@ -1,9 +1,6 @@
class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
- belongs_to :protected_branch
- delegate :project, to: :protected_branch
-
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER] }
@@ -13,10 +10,4 @@ class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base
Gitlab::Access::DEVELOPER => "Developers + Masters"
}.with_indifferent_access
end
-
- def check_access(user)
- return true if user.is_admin?
-
- project.team.max_member_access(user.id) >= access_level
- end
end
diff --git a/app/models/protected_branch/push_access_level.rb b/app/models/protected_branch/push_access_level.rb
index 92e9c51d883..14610cb42b7 100644
--- a/app/models/protected_branch/push_access_level.rb
+++ b/app/models/protected_branch/push_access_level.rb
@@ -1,9 +1,6 @@
class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
include ProtectedBranchAccess
- belongs_to :protected_branch
- delegate :project, to: :protected_branch
-
validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
Gitlab::Access::DEVELOPER,
Gitlab::Access::NO_ACCESS] }
@@ -18,8 +15,7 @@ class ProtectedBranch::PushAccessLevel < ActiveRecord::Base
def check_access(user)
return false if access_level == Gitlab::Access::NO_ACCESS
- return true if user.is_admin?
- project.team.max_member_access(user.id) >= access_level
+ super
end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index bf136ccdb6c..e2e7d08abac 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -196,18 +196,12 @@ class Repository
options = { message: message, tagger: user_to_committer(user) } if message
- rugged.tags.create(tag_name, target, options)
- tag = find_tag(tag_name)
-
- GitHooksService.new.execute(user, path_to_repo, oldrev, tag.target, ref) do
- # we already created a tag, because we need tag SHA to pass correct
- # values to hooks
+ GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do |service|
+ raw_tag = rugged.tags.create(tag_name, target, options)
+ service.newrev = raw_tag.target_id
end
- tag
- rescue GitHooksService::PreReceiveError
- rugged.tags.delete(tag_name)
- raise
+ find_tag(tag_name)
end
def rm_branch(user, branch_name)
diff --git a/app/models/user.rb b/app/models/user.rb
index 513a19d81d2..b54ce14f0bf 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -445,27 +445,21 @@ class User < ActiveRecord::Base
end
def refresh_authorized_projects
- loop do
- begin
- Gitlab::Database.serialized_transaction do
- project_authorizations.delete_all
-
- # project_authorizations_union can return multiple records for the same project/user with
- # different access_level so we take row with the maximum access_level
- project_authorizations.connection.execute <<-SQL
- INSERT INTO project_authorizations (user_id, project_id, access_level)
- SELECT user_id, project_id, MAX(access_level) AS access_level
- FROM (#{project_authorizations_union.to_sql}) sub
- GROUP BY user_id, project_id
- SQL
-
- update_column(:authorized_projects_populated, true) unless authorized_projects_populated
- end
-
- break
- # In the event of a concurrent modification Rails raises StatementInvalid.
- # In this case we want to keep retrying until the transaction succeeds
- rescue ActiveRecord::StatementInvalid
+ transaction do
+ project_authorizations.delete_all
+
+ # project_authorizations_union can return multiple records for the same
+ # project/user with different access_level so we take row with the maximum
+ # access_level
+ project_authorizations.connection.execute <<-SQL
+ INSERT INTO project_authorizations (user_id, project_id, access_level)
+ SELECT user_id, project_id, MAX(access_level) AS access_level
+ FROM (#{project_authorizations_union.to_sql}) sub
+ GROUP BY user_id, project_id
+ SQL
+
+ unless authorized_projects_populated
+ update_column(:authorized_projects_populated, true)
end
end
end
@@ -708,20 +702,6 @@ class User < ActiveRecord::Base
project.project_member(self)
end
- # Reset project events cache related to this user
- #
- # Since we do cache @event we need to reset cache in special cases:
- # * when the user changes their avatar
- # Events cache stored like events/23-20130109142513.
- # The cache key includes updated_at timestamp.
- # Thus it will automatically generate a new fragment
- # when the event is updated because the key changes.
- def reset_events_cache
- Event.where(author_id: id).
- order('id DESC').limit(1000).
- update_all(updated_at: Time.now)
- end
-
def full_website_url
return "http://#{website_url}" if website_url !~ /\Ahttps?:\/\//
diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb
index abefcd5cc02..a0db5b8f0f4 100644
--- a/app/serializers/analytics_build_entity.rb
+++ b/app/serializers/analytics_build_entity.rb
@@ -13,7 +13,7 @@ class AnalyticsBuildEntity < Grape::Entity
end
expose :duration, as: :total_time do |build|
- distance_of_time_as_hash(build.duration.to_f)
+ build.duration ? distance_of_time_as_hash(build.duration.to_f) : {}
end
expose :branch do
diff --git a/app/serializers/build_entity.rb b/app/serializers/build_entity.rb
index cf1c418a88e..b5384e6462b 100644
--- a/app/serializers/build_entity.rb
+++ b/app/serializers/build_entity.rb
@@ -16,6 +16,9 @@ class BuildEntity < Grape::Entity
path_to(:play_namespace_project_build, build)
end
+ expose :created_at
+ expose :updated_at
+
private
def path_to(route, build)
diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb
index 918abba8d99..9607ad55a8b 100644
--- a/app/serializers/entity_date_helper.rb
+++ b/app/serializers/entity_date_helper.rb
@@ -2,6 +2,8 @@ module EntityDateHelper
include ActionView::Helpers::DateHelper
def interval_in_words(diff)
+ return 'Not started' unless diff
+
"#{distance_of_time_in_words(Time.now, diff)} ago"
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index cde856b0186..e3bc9847200 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -45,9 +45,15 @@ module Ci
return error('No builds for this pipeline.')
end
- pipeline.save
- pipeline.process!
- pipeline
+ Ci::Pipeline.transaction do
+ pipeline.save
+
+ Ci::CreatePipelineBuildsService
+ .new(project, current_user)
+ .execute(pipeline)
+ end
+
+ pipeline.tap(&:process!)
end
private
diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb
index 8face432d97..2e028c44d8b 100644
--- a/app/services/ci/process_pipeline_service.rb
+++ b/app/services/ci/process_pipeline_service.rb
@@ -5,10 +5,7 @@ module Ci
def execute(pipeline)
@pipeline = pipeline
- # This method will ensure that our pipeline does have all builds for all stages created
- if created_builds.empty?
- create_builds!
- end
+ ensure_created_builds! # TODO, remove me in 9.0
new_builds =
stage_indexes_of_created_builds.map do |index|
@@ -22,10 +19,6 @@ module Ci
private
- def create_builds!
- Ci::CreatePipelineBuildsService.new(project, current_user).execute(pipeline)
- end
-
def process_stage(index)
current_status = status_for_prior_stages(index)
@@ -76,5 +69,18 @@ module Ci
def created_builds
pipeline.builds.created
end
+
+ # This method is DEPRECATED and should be removed in 9.0.
+ #
+ # We need it to maintain backwards compatibility with previous versions
+ # when builds were not created within one transaction with the pipeline.
+ #
+ def ensure_created_builds!
+ return if created_builds.any?
+
+ Ci::CreatePipelineBuildsService
+ .new(project, current_user)
+ .execute(pipeline)
+ end
end
end
diff --git a/app/services/git_hooks_service.rb b/app/services/git_hooks_service.rb
index 172bd85dade..6cd3908d43a 100644
--- a/app/services/git_hooks_service.rb
+++ b/app/services/git_hooks_service.rb
@@ -1,6 +1,8 @@
class GitHooksService
PreReceiveError = Class.new(StandardError)
+ attr_accessor :oldrev, :newrev, :ref
+
def execute(user, repo_path, oldrev, newrev, ref)
@repo_path = repo_path
@user = Gitlab::GlId.gl_id(user)
@@ -16,7 +18,7 @@ class GitHooksService
end
end
- yield
+ yield self
run_hook('post-receive')
end
@@ -25,6 +27,6 @@ class GitHooksService
def run_hook(name)
hook = Gitlab::Git::Hook.new(name, @repo_path)
- hook.trigger(@user, @oldrev, @newrev, @ref)
+ hook.trigger(@user, oldrev, newrev, ref)
end
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 575795788de..ce68e433ab8 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -85,14 +85,15 @@ class IssuableBaseService < BaseService
def find_or_create_label_ids
labels = params.delete(:labels)
+
return unless labels
- params[:label_ids] = labels.split(',').map do |label_name|
+ params[:label_ids] = labels.split(",").map do |label_name|
service = Labels::FindOrCreateService.new(current_user, project, title: label_name.strip)
label = service.execute
- label.id
- end
+ label.try(:id)
+ end.compact
end
def process_label_ids(attributes, existing_label_ids: nil)
@@ -140,6 +141,7 @@ class IssuableBaseService < BaseService
params.delete(:state_event)
params[:author] ||= current_user
+
label_ids = process_label_ids(params)
issuable.assign_attributes(params)
@@ -184,8 +186,6 @@ class IssuableBaseService < BaseService
params[:label_ids] = process_label_ids(params, existing_label_ids: issuable.label_ids)
if params.present? && update_issuable(issuable, params)
- issuable.reset_events_cache
-
# We do not touch as it will affect a update on updated_at field
ActiveRecord::Base.no_touching do
handle_common_system_notes(issuable, old_labels: old_labels)
diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb
index d622f9edd33..cf4f7606c94 100644
--- a/app/services/labels/find_or_create_service.rb
+++ b/app/services/labels/find_or_create_service.rb
@@ -22,9 +22,14 @@ module Labels
).execute(skip_authorization: skip_authorization)
end
+ # Only creates the label if current_user can do so, if the label does not exist
+ # and the user can not create the label, nil is returned
def find_or_create_label
new_label = available_labels.find_by(title: title)
- new_label ||= project.labels.create(params)
+
+ if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, project))
+ new_label = project.labels.create(params)
+ end
new_label
end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 58f69a41e14..800fd39c424 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -55,7 +55,7 @@ module MergeRequests
def pipeline_merge_requests(pipeline)
merge_requests_for(pipeline.ref).each do |merge_request|
- next unless pipeline == merge_request.pipeline
+ next unless pipeline == merge_request.head_pipeline
yield merge_request
end
@@ -63,7 +63,7 @@ module MergeRequests
def commit_status_merge_requests(commit_status)
merge_requests_for(commit_status.ref).each do |merge_request|
- pipeline = merge_request.pipeline
+ pipeline = merge_request.head_pipeline
next unless pipeline
next unless pipeline.sha == commit_status.sha
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index dd0d738674e..bebfca7537b 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -81,7 +81,7 @@ module MergeRequests
commit = commits.first
merge_request.title = commit.title
merge_request.description ||= commit.description.try(:strip)
- elsif iid && (issue = merge_request.target_project.get_issue(iid)) && !issue.try(:confidential?)
+ elsif iid && issue = merge_request.target_project.get_issue(iid, current_user)
case issue
when Issue
merge_request.title = "Resolve \"#{issue.title}\""
diff --git a/app/services/notes/delete_service.rb b/app/services/notes/delete_service.rb
index 7f1b30ec84e..a673e8e9dde 100644
--- a/app/services/notes/delete_service.rb
+++ b/app/services/notes/delete_service.rb
@@ -2,7 +2,6 @@ module Notes
class DeleteService < BaseService
def execute(note)
note.destroy
- note.reset_events_cache
end
end
end
diff --git a/app/services/notes/update_service.rb b/app/services/notes/update_service.rb
index 1361b1e0300..75a4b3ed826 100644
--- a/app/services/notes/update_service.rb
+++ b/app/services/notes/update_service.rb
@@ -5,7 +5,6 @@ module Notes
note.update_attributes(params.merge(updated_by: current_user))
note.create_new_cross_references!(current_user)
- note.reset_events_cache
if note.previous_changes.include?('note')
TodoService.new.update_note(note, current_user)
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 28470f59807..34ec575e808 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -61,9 +61,6 @@ module Projects
# Move missing group labels to project
Labels::TransferService.new(current_user, old_group, project).execute
- # clear project cached events
- project.reset_events_cache
-
# Move uploads
Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.path, new_namespace.path)
diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
index 71ff14a3f20..38683fdf6d7 100644
--- a/app/uploaders/avatar_uploader.rb
+++ b/app/uploaders/avatar_uploader.rb
@@ -3,16 +3,10 @@ class AvatarUploader < CarrierWave::Uploader::Base
storage :file
- after :store, :reset_events_cache
-
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
- def reset_events_cache(file)
- model.reset_events_cache if model.is_a?(User)
- end
-
def exists?
model.avatar.file && model.avatar.file.exists?
end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index ce803f329f9..7accd2529af 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -443,7 +443,16 @@
Some email servers do not support overriding the email sender name.
Enable this option to include the name of the author of the issue,
merge request or comment in the email body instead.
-
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :html_emails_enabled do
+ = f.check_box :html_emails_enabled
+ Enable HTML emails
+ .help-block
+ By default GitLab sends emails in HTML and plain text formats so mail
+ clients can choose what format to use. Disable this option if you only
+ want to send emails in plain text format.
%fieldset
%legend Automatic Git repository housekeeping
.form-group
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index f7abad54286..48b0fd504f4 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -4,13 +4,13 @@
%ul.nav-links
= nav_link(page: [dashboard_projects_path, root_path]) do
= link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
- Your Projects
+ Your projects
= nav_link(page: starred_dashboard_projects_path) do
= link_to starred_dashboard_projects_path, title: 'Starred Projects', data: {placement: 'right'} do
- Starred Projects
+ Starred projects
= nav_link(page: [explore_root_path, trending_explore_projects_path, starred_explore_projects_path, explore_projects_path]) do
= link_to explore_root_path, title: 'Explore', data: {placement: 'right'} do
- Explore Projects
+ Explore projects
.nav-controls
= form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
diff --git a/app/views/dashboard/projects/_zero_authorized_projects.html.haml b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
index fdea834ff45..4a55aac0df6 100644
--- a/app/views/dashboard/projects/_zero_authorized_projects.html.haml
+++ b/app/views/dashboard/projects/_zero_authorized_projects.html.haml
@@ -4,6 +4,18 @@
Welcome to GitLab
%p.blank-state-text
Code, test, and deploy together
+
+- if current_user.can_create_group?
+ .blank-state
+ .blank-state-icon
+ = custom_icon("group", size: 50)
+ %h3.blank-state-title
+ You can create a group for several dependent projects.
+ %p.blank-state-text
+ Groups are the best way to manage projects and members.
+ = link_to new_group_path, class: "btn btn-new" do
+ New group
+
.blank-state
.blank-state-icon
= custom_icon("project", size: 50)
@@ -21,17 +33,6 @@
= link_to new_project_path, class: "btn btn-new" do
New project
-- if current_user.can_create_group?
- .blank-state
- .blank-state-icon
- = custom_icon("group", size: 50)
- %h3.blank-state-title
- You can create a group for several dependent projects.
- %p.blank-state-text
- Groups are the best way to manage projects and members.
- = link_to new_group_path, class: "btn btn-new" do
- New group
-
-if publicish_project_count > 0
.blank-state
.blank-state-icon
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 472d698486b..62f52086be4 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -50,13 +50,13 @@
data: { data: todo_actions_options }})
.pull-right
.dropdown.inline.prepend-left-10
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
%li
= link_to todos_filter_path(sort: sort_value_priority) do
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 5c318cd3b8b..a0bd14df209 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -3,14 +3,13 @@
.event-item-timestamp
#{time_ago_with_tooltip(event.created_at)}
- = cache [event, current_application_settings, "v2.2"] do
- = author_avatar(event, size: 40)
+ = author_avatar(event, size: 40)
- - if event.created_project?
- = render "events/event/created_project", event: event
- - elsif event.push?
- = render "events/event/push", event: event
- - elsif event.commented?
- = render "events/event/note", event: event
- - else
- = render "events/event/common", event: event
+ - if event.created_project?
+ = render "events/event/created_project", event: event
+ - elsif event.push?
+ = render "events/event/push", event: event
+ - elsif event.commented?
+ = render "events/event/note", event: event
+ - else
+ = render "events/event/common", event: event
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index 44fff49d99c..64ca3c32e01 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -18,7 +18,7 @@
- few_commits.each do |commit|
= render "events/commit", commit: commit, project: project, event: event
- - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project)
+ - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project) && event.authored_by?(current_user)
- if event.commits_count > 1
%li.commits-stat
- if event.commits_count > 2
@@ -35,12 +35,12 @@
Compare #{from_label}...#{truncate_sha(event.commit_to)}
- if create_mr
- %span{"data-user-is" => event.author_id, "data-display" => "inline"}
+ %span
or
= link_to create_mr_path(project.default_branch, event.ref_name, project) do
create a merge request
- elsif create_mr
- %li.commits-stat{"data-user-is" => event.author_id}
+ %li.commits-stat
= link_to create_mr_path(project.default_branch, event.ref_name, project) do
Create Merge Request
- elsif event.rm_ref?
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index a1b39d9e1a0..4e5d965ccbe 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -17,13 +17,13 @@
.pull-right
.dropdown.inline
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to explore_groups_path(sort: sort_value_recently_created) do
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 4cff14b096b..5ea154c36b4 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,13 +1,13 @@
- if current_user
.dropdown
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
= icon('globe')
%span.light Visibility:
- if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i)
- else
Any
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_projects_path(visibility_level: nil) do
@@ -20,14 +20,14 @@
- if @tags.present?
.dropdown
- %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+ %button.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
= icon('tags')
%span.light Tags:
- if params[:tag].present?
= params[:tag]
- else
Any
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_projects_path(tag: nil) do
diff --git a/app/views/invites/show.html.haml b/app/views/invites/show.html.haml
index 2fd4859c1c6..882fdf1317d 100644
--- a/app/views/invites/show.html.haml
+++ b/app/views/invites/show.html.haml
@@ -6,7 +6,7 @@
- if inviter = @member.created_by
by
= link_to inviter.name, user_url(inviter)
- to join
+ to join
- case @member.source
- when Project
- project = @member.source
@@ -20,11 +20,18 @@
= link_to group.name, group_url(group)
as #{@member.human_access}.
-- if @member.source.users.include?(current_user)
+- is_member = @member.source.users.include?(current_user)
+
+- if is_member
%p
However, you are already a member of this #{@member.source.is_a?(Group) ? "group" : "project"}.
Sign in using a different account to accept the invitation.
-- else
+
+- if @member.invite_email != current_user.email
+ %p
+ Note that this invitation was sent to #{mail_to @member.invite_email}, but you are signed in as #{link_to current_user.to_reference, user_url(current_user)} with email #{mail_to current_user.email}.
+
+- unless is_member
.actions
= link_to "Accept invitation", accept_invite_url(@token), method: :post, class: "btn btn-success"
= link_to "Decline", decline_invite_url(@token), method: :post, class: "btn btn-danger prepend-left-10"
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 757de92d6d4..3e488cf73b9 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -56,5 +56,3 @@
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
= render 'layouts/bootlint' if Rails.env.development?
-
- = render 'layouts/user_styles'
diff --git a/app/views/layouts/_user_styles.html.haml b/app/views/layouts/_user_styles.html.haml
deleted file mode 100644
index b76b3cb5510..00000000000
--- a/app/views/layouts/_user_styles.html.haml
+++ /dev/null
@@ -1,24 +0,0 @@
-:css
- [data-user-is] {
- display: none !important;
- }
-
- [data-user-is="#{current_user.try(:id)}"] {
- display: block !important;
- }
-
- [data-user-is="#{current_user.try(:id)}"][data-display="inline"] {
- display: inline !important;
- }
-
- [data-user-is-not] {
- display: block !important;
- }
-
- [data-user-is-not][data-display="inline"] {
- display: inline !important;
- }
-
- [data-user-is-not="#{current_user.try(:id)}"] {
- display: none !important;
- }
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 99a58bbb676..701bcd3ab71 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -70,7 +70,7 @@
%span
Issues
- if @project.default_issues_tracker?
- %span.badge.count.issue_counter= number_with_delimiter(@project.issues.visible_to_user(current_user).opened.count)
+ %span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
diff --git a/app/views/notify/_note_message.text.erb b/app/views/notify/_note_message.text.erb
new file mode 100644
index 00000000000..f82cbc9a3fc
--- /dev/null
+++ b/app/views/notify/_note_message.text.erb
@@ -0,0 +1,5 @@
+<% if current_application_settings.email_author_in_body %>
+ <%= @note.author_name %> wrote:
+<% end -%>
+
+<%= @note.note %>
diff --git a/app/views/notify/_note_mr_or_commit_email.html.haml b/app/views/notify/_note_mr_or_commit_email.html.haml
new file mode 100644
index 00000000000..edf8dfe7e9e
--- /dev/null
+++ b/app/views/notify/_note_mr_or_commit_email.html.haml
@@ -0,0 +1,18 @@
+= content_for :head do
+ = stylesheet_link_tag 'mailers/highlighted_diff_email'
+
+New comment
+
+- if @discussion && @discussion.diff_file
+ on
+ = link_to @note.diff_file.file_path, @target_url, class: 'details'
+ \:
+ %table
+ = render partial: "projects/diffs/line",
+ collection: @discussion.truncated_diff_lines,
+ as: :line,
+ locals: { diff_file: @note.diff_file,
+ plain: true,
+ email: true }
+
+= render 'note_message'
diff --git a/app/views/notify/_note_mr_or_commit_email.text.erb b/app/views/notify/_note_mr_or_commit_email.text.erb
new file mode 100644
index 00000000000..b4fcdf6b1e9
--- /dev/null
+++ b/app/views/notify/_note_mr_or_commit_email.text.erb
@@ -0,0 +1,8 @@
+<% if @discussion && @discussion.diff_file -%>
+ on <%= @note.diff_file.file_path -%>
+<% end -%>:
+
+<%= url %>
+
+<%= render 'simple_diff' if @discussion -%>
+<%= render 'note_message' %>
diff --git a/app/views/notify/_simple_diff.text.erb b/app/views/notify/_simple_diff.text.erb
new file mode 100644
index 00000000000..c28d1cc34d3
--- /dev/null
+++ b/app/views/notify/_simple_diff.text.erb
@@ -0,0 +1,3 @@
+<% @discussion.truncated_diff_lines(highlight: false).each do |line| %>
+> <%= line.text %>
+<% end %>
diff --git a/app/views/notify/note_commit_email.html.haml b/app/views/notify/note_commit_email.html.haml
index 1d961e4424c..0a650e3b2ca 100644
--- a/app/views/notify/note_commit_email.html.haml
+++ b/app/views/notify/note_commit_email.html.haml
@@ -1,2 +1,2 @@
-= render 'note_message'
-
+%p.details
+ = render 'note_mr_or_commit_email'
diff --git a/app/views/notify/note_commit_email.text.erb b/app/views/notify/note_commit_email.text.erb
index aaeaf5fdf73..6aa085a172e 100644
--- a/app/views/notify/note_commit_email.text.erb
+++ b/app/views/notify/note_commit_email.text.erb
@@ -1,9 +1,2 @@
-New comment for Commit <%= @commit.short_id %>
-
-<%= url_for(namespace_project_commit_url(@note.project.namespace, @note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
-
-
-Author: <%= @note.author_name %>
-
-<%= @note.note %>
-
+New comment for Commit <%= @commit.short_id -%>
+<%= render partial: 'note_mr_or_commit_email', locals: { url: @target_url } %>
diff --git a/app/views/notify/note_merge_request_email.html.haml b/app/views/notify/note_merge_request_email.html.haml
index ea7e3d199fd..0a650e3b2ca 100644
--- a/app/views/notify/note_merge_request_email.html.haml
+++ b/app/views/notify/note_merge_request_email.html.haml
@@ -1,7 +1,2 @@
-- if @note.diff_note? && @note.diff_file
- %p.details
- New comment on diff for
- = link_to @note.diff_file.file_path, @target_url
- \:
-
-= render 'note_message'
+%p.details
+ = render 'note_mr_or_commit_email'
diff --git a/app/views/notify/note_merge_request_email.text.erb b/app/views/notify/note_merge_request_email.text.erb
index 8cdab63829e..2ce64c494cf 100644
--- a/app/views/notify/note_merge_request_email.text.erb
+++ b/app/views/notify/note_merge_request_email.text.erb
@@ -1,9 +1,2 @@
-New comment for Merge Request <%= @merge_request.to_reference %>
-
-<%= url_for(namespace_project_merge_request_url(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %>
-
-
-<%= @note.author_name %>
-
-<%= @note.note %>
-
+New comment for Merge Request <%= @merge_request.to_reference -%>
+<%= render partial: 'note_mr_or_commit_email', locals: { url: @target_url }%>
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
index 307c5a11206..25883de257c 100644
--- a/app/views/notify/repository_push_email.html.haml
+++ b/app/views/notify/repository_push_email.html.haml
@@ -1,5 +1,5 @@
= content_for :head do
- = stylesheet_link_tag 'mailers/repository_push_email'
+ = stylesheet_link_tag 'mailers/highlighted_diff_email'
%h3
#{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name}
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 2a0352a71b7..a5dcd93f42e 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -27,5 +27,5 @@
= render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
= hidden_field_tag 'last_commit_sha', @last_commit_sha
= hidden_field_tag 'content', '', id: "file-content"
- = hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
+ = hidden_field_tag 'from_merge_request_iid', params[:from_merge_request_iid]
= render 'projects/commit_button', ref: @ref, cancel_path: namespace_project_blob_path(@project.namespace, @project, @id)
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 2246316b540..5fd664c7a93 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -12,10 +12,10 @@
= search_field_tag :search, params[:search], { placeholder: 'Filter by branch name', id: 'branch-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light
= projects_sort_options_hash[@sort]
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_branches_path(sort: sort_value_name) do
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
index f5562046953..d5004f6a066 100644
--- a/app/views/projects/builds/_sidebar.html.haml
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -116,7 +116,7 @@
.title Stage
%button.dropdown-menu-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.stage-selection More
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu
- @build.pipeline.stages.each do |stage|
%li
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 120ba9ffcd2..6c33d80becd 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -9,7 +9,7 @@
= icon('comment')
\
- if editable_diff?(diff_file)
- - link_opts = @merge_request.id ? { from_merge_request_id: @merge_request.id } : {}
+ - link_opts = @merge_request.persisted? ? { from_merge_request_iid: @merge_request.iid } : {}
= edit_blob_link(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path,
blob: blob, link_opts: link_opts)
diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml
index a3e4b5b777e..16c96b66714 100644
--- a/app/views/projects/diffs/_line.html.haml
+++ b/app/views/projects/diffs/_line.html.haml
@@ -25,7 +25,7 @@
%a{href: "##{line_code}", data: { linenumber: link_text }}
%td.line_content.noteable_line{ class: type, data: (diff_view_line_data(line_code, diff_file.position(line), type) unless plain) }<
- if email
- %pre= diff_line_content(line.text)
+ %pre= line.text
- else
= diff_line_content(line.text)
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index abf4f697f86..5ee3979c7e7 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -9,13 +9,13 @@
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown
- %button.dropdown-toggle.btn.sort-forks{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', 'data-toggle' => 'dropdown'}
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
- excluded_filters = [:state, :scope, :label_name, :milestone_id, :assignee_id, :author_id]
diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml
index 747bfa554cb..d48923b422a 100644
--- a/app/views/projects/issues/_merge_requests.html.haml
+++ b/app/views/projects/issues/_merge_requests.html.haml
@@ -2,12 +2,12 @@
%h2.merge-requests-title
= pluralize(@merge_requests.count, 'Related Merge Request')
%ul.unstyled-list.related-merge-requests
- - has_any_ci = @merge_requests.any?(&:pipeline)
+ - has_any_ci = @merge_requests.any?(&:head_pipeline)
- @merge_requests.each do |merge_request|
%li
%span.merge-request-ci-status
- - if merge_request.pipeline
- = render_pipeline_status(merge_request.pipeline)
+ - if merge_request.head_pipeline
+ = render_pipeline_status(merge_request.head_pipeline)
- elsif has_any_ci
= icon('blank fw')
%span.merge-request-id
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 9ffcc48eb80..fa189ae62d8 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -15,9 +15,9 @@
= icon('ban')
CLOSED
- - if merge_request.pipeline
+ - if merge_request.head_pipeline
%li
- = render_pipeline_status(merge_request.pipeline)
+ = render_pipeline_status(merge_request.head_pipeline)
- if merge_request.open? && merge_request.broken?
%li
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 9c6f562f7db..4a08ed045f4 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -34,10 +34,11 @@
= link_to url_for(params), data: {target: 'div#pipelines', action: 'pipelines', toggle: 'tab'} do
Pipelines
%span.badge= @pipelines.size
+ - if @pipeline.present?
%li.builds-tab
= link_to url_for(params), data: {target: 'div#builds', action: 'builds', toggle: 'tab'} do
Builds
- %span.badge= @statuses.size
+ %span.badge= @statuses_count
%li.diffs-tab
= link_to url_for(params.merge(action: 'new_diffs')), data: {target: 'div#diffs', action: 'new/diffs', toggle: 'tab'} do
Changes
@@ -48,9 +49,10 @@
= render "projects/merge_requests/show/commits"
#diffs.diffs.tab-pane
- # This tab is always loaded via AJAX
- - if @pipelines.any?
+ - if @pipeline.present?
#builds.builds.tab-pane
= render "projects/merge_requests/show/builds"
+ - if @pipelines.any?
#pipelines.pipelines.tab-pane
= render "projects/merge_requests/show/pipelines"
@@ -65,5 +67,5 @@
:javascript
var merge_request = new MergeRequest({
action: "#{(@show_changes_tab ? 'new/diffs' : 'new')}",
- buildsLoaded: "#{@pipelines.any? ? 'true' : 'false'}"
+ buildsLoaded: "#{@pipeline.present? ? 'true' : 'false'}"
});
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index a497f418c7c..0e2975bd551 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -59,15 +59,16 @@
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
Commits
%span.badge= @commits_count
- - if @pipeline
+ - if @pipelines.any?
%li.pipelines-tab
= link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
Pipelines
%span.badge= @pipelines.size
+ - if @pipeline.present?
%li.builds-tab
= link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#builds', action: 'builds', toggle: 'tab' } do
Builds
- %span.badge= @statuses.size
+ %span.badge= @statuses_count
%li.diffs-tab
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
Changes
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index 18c72ed875c..6d9b91ad0e7 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -1,7 +1,7 @@
- if @pipeline
.mr-widget-heading
- %w[success success_with_warnings skipped canceled failed running pending].each do |status|
- .ci_widget{ class: "ci-status-icon-#{status}", style: ("display:none" unless @pipeline.status == status) }
+ .ci_widget{ class: "ci-#{status} ci-status-icon-#{status}", style: ("display:none" unless @pipeline.status == status) }
= ci_icon_for_status(status)
%span
Pipeline
diff --git a/app/views/projects/merge_requests/widget/_show.html.haml b/app/views/projects/merge_requests/widget/_show.html.haml
index 608fdf1c5f5..a8918c85dde 100644
--- a/app/views/projects/merge_requests/widget/_show.html.haml
+++ b/app/views/projects/merge_requests/widget/_show.html.haml
@@ -14,7 +14,7 @@
ci_status_url: "#{ci_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
ci_environments_status_url: "#{ci_environments_status_namespace_project_merge_request_path(@project.namespace, @project, @merge_request)}",
gitlab_icon: "#{asset_path 'gitlab_logo.png'}",
- ci_status: "#{@merge_request.pipeline ? @merge_request.pipeline.status : ''}",
+ ci_status: "#{@merge_request.head_pipeline ? @merge_request.head_pipeline.status : ''}",
ci_message: {
normal: "Build {{status}} for \"{{title}}\"",
preparing: "{{status}} build for \"{{title}}\""
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index b43b13de4ca..1d39f3a7534 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -12,10 +12,10 @@
= search_field_tag :search, params[:search], { placeholder: 'Filter by tag name', id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown.inline
- %button.dropdown-toggle.btn{ type: 'button', data: { toggle: 'dropdown'} }
+ %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%span.light
= projects_sort_options_hash[@sort]
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right
%li
= link_to filter_tags_path(sort: sort_value_name) do
diff --git a/app/views/projects/variables/_table.html.haml b/app/views/projects/variables/_table.html.haml
index 07cee86ba4c..c7cebf45160 100644
--- a/app/views/projects/variables/_table.html.haml
+++ b/app/views/projects/variables/_table.html.haml
@@ -12,8 +12,8 @@
- @project.variables.order_key_asc.each do |variable|
- if variable.id?
%tr
- %td= variable.key
- %td= variable.value
+ %td.variable-key= variable.key
+ %td.variable-value{ "data-value" => variable.value }******
%td
= link_to namespace_project_variable_path(@project.namespace, @project, variable), class: "btn btn-transparent btn-variable-edit" do
%span.sr-only
diff --git a/app/views/projects/variables/index.html.haml b/app/views/projects/variables/index.html.haml
index 09bb54600af..39303700131 100644
--- a/app/views/projects/variables/index.html.haml
+++ b/app/views/projects/variables/index.html.haml
@@ -15,3 +15,4 @@
No variables found, add one with the form above.
- else
= render "table"
+ %button.btn.btn-info.js-btn-toggle-reveal-values{"data-status" => 'hidden'} Reveal Values
diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml
index ef1c0296d49..938be20c7cf 100644
--- a/app/views/search/_filter.html.haml
+++ b/app/views/search/_filter.html.haml
@@ -3,7 +3,7 @@
- if params[:project_id].present?
= hidden_field_tag :project_id, params[:project_id]
.dropdown
- %button.dropdown-menu-toggle.btn.js-search-group-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Group:" } }
+ %button.dropdown-menu-toggle.js-search-group-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Group:" } }
%span.dropdown-toggle-text
Group:
- if @group.present?
@@ -18,7 +18,7 @@
= dropdown_loading
.dropdown.project-filter
- %button.dropdown-menu-toggle.btn.js-search-project-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Project:" } }
+ %button.dropdown-menu-toggle.js-search-project-dropdown{ type: "button", data: { toggle: "dropdown", default_label: "Project:" } }
%span.dropdown-toggle-text
Project:
- if @project.present?
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index ba25e09d638..0bc851b4256 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -13,8 +13,9 @@
.input-group-addon
= root_url
= f.text_field :path, placeholder: 'open-source', class: 'form-control',
- autofocus: local_assigns[:autofocus] || false, pattern: "[a-zA-Z0-9-_]+",
- required: true, title: 'Please choose a group name with no special characters.'
+ autofocus: local_assigns[:autofocus] || false, required: true,
+ pattern: Gitlab::Regex::NAMESPACE_REGEX_STR_SIMPLE,
+ title: 'Please choose a group name with no special characters.'
- if @group.persisted?
.alert.alert-warning.prepend-top-10
diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml
index 68e05cb72e1..ede3c7090d7 100644
--- a/app/views/shared/_sort_dropdown.html.haml
+++ b/app/views/shared/_sort_dropdown.html.haml
@@ -1,11 +1,11 @@
.dropdown.inline.prepend-left-10
- %button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
+ %button.dropdown-toggle{type: 'button', data: {toggle: 'dropdown'}}
%span.light
- if @sort.present?
= sort_options_hash[@sort]
- else
= sort_title_recently_created
- = icon('caret-down')
+ = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
%li
= link_to page_filter_path(sort: sort_value_priority, label: true) do
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index b7e5e928993..e3503981afe 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -29,9 +29,9 @@
.filter-item.inline.labels-filter
= render "shared/issuable/label_dropdown", selected: finder.labels.select(:title).uniq, use_id: false, selected_toggle: params[:label_name], data_options: { field_name: "label_name[]" }
- - if issuable_filters_present
+ - if issuable_filter_present?
.filter-item.inline.reset-filters
- %a{href: page_filter_path(without: [:assignee_id, :author_id, :milestone_title, :label_name, :search])} Reset filters
+ %a{href: page_filter_path(without: issuable_filter_params)} Reset filters
.pull-right
- if boards_page
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 9b9ad510444..3d515a05d46 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -16,20 +16,9 @@
= render 'shared/issuable/form/template_selector', issuable: issuable
= render 'shared/issuable/form/title', issuable: issuable, form: form
-.form-group.detail-page-description
- = form.label :description, 'Description', class: 'control-label'
- .col-sm-10
+= render 'shared/issuable/form/description', issuable: issuable, form: form
- = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
- = render 'projects/zen', f: form, attr: :description,
- classes: 'note-textarea',
- placeholder: "Write a comment or drag your files here...",
- supports_slash_commands: !issuable.persisted?
- = render 'projects/notes/hints', supports_slash_commands: !issuable.persisted?
- .clearfix
- .error-alert
-
-- if issuable.is_a?(Issue)
+- if issuable.respond_to?(:confidential)
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
@@ -37,38 +26,7 @@
= form.check_box :confidential
This issue is confidential and should only be visible to team members with at least Reporter access.
-- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
- - has_due_date = issuable.has_attribute?(:due_date)
- %hr
- .row
- %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
- .form-group.issue-assignee
- = form.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
- .col-sm-10{ class: ("col-lg-8" if has_due_date) }
- .issuable-form-select-holder
- - if issuable.assignee_id
- = form.hidden_field :assignee_id
- = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
- placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
- .form-group.issue-milestone
- = form.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
- .col-sm-10{ class: ("col-lg-8" if has_due_date) }
- .issuable-form-select-holder
- = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
- .form-group
- - has_labels = @labels && @labels.any?
- = form.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
- = form.hidden_field :label_ids, multiple: true, value: ''
- .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
- .issuable-form-select-holder
- = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label"
- - if has_due_date
- .col-lg-6
- .form-group
- = form.label :due_date, "Due date", class: "control-label"
- .col-sm-10
- .issuable-form-select-holder
- = form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
+= render 'shared/issuable/form/metadata', issuable: issuable, form: form
- if issuable.can_move?(current_user)
%hr
diff --git a/app/views/shared/issuable/_label_dropdown.html.haml b/app/views/shared/issuable/_label_dropdown.html.haml
index 1d778bc88de..22b5a6aa11b 100644
--- a/app/views/shared/issuable/_label_dropdown.html.haml
+++ b/app/views/shared/issuable/_label_dropdown.html.haml
@@ -22,7 +22,7 @@
%button.dropdown-menu-toggle.js-label-select.js-multiselect{class: classes.join(' '), type: "button", data: dropdown_data}
%span.dropdown-toggle-text{ class: ("is-default" if selected.nil? || selected.empty?) }
= multi_label_name(selected, "Labels")
- = icon('caret-down')
+ = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
= render partial: "shared/issuable/label_page_default", locals: { title: dropdown_title, show_footer: show_footer, show_create: show_create }
- if show_create && project && can?(current_user, :admin_label, project)
diff --git a/app/views/shared/issuable/form/_description.html.haml b/app/views/shared/issuable/form/_description.html.haml
new file mode 100644
index 00000000000..dbace9ce401
--- /dev/null
+++ b/app/views/shared/issuable/form/_description.html.haml
@@ -0,0 +1,15 @@
+- issuable = local_assigns.fetch(:issuable)
+- form = local_assigns.fetch(:form)
+
+.form-group.detail-page-description
+ = form.label :description, 'Description', class: 'control-label'
+ .col-sm-10
+
+ = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
+ = render 'projects/zen', f: form, attr: :description,
+ classes: 'note-textarea',
+ placeholder: "Write a comment or drag your files here...",
+ supports_slash_commands: !issuable.persisted?
+ = render 'projects/notes/hints', supports_slash_commands: !issuable.persisted?
+ .clearfix
+ .error-alert
diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml
new file mode 100644
index 00000000000..a47085230b8
--- /dev/null
+++ b/app/views/shared/issuable/form/_metadata.html.haml
@@ -0,0 +1,38 @@
+- issuable = local_assigns.fetch(:issuable)
+
+- return unless can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
+
+- has_due_date = issuable.has_attribute?(:due_date)
+- has_labels = @labels && @labels.any?
+- form = local_assigns.fetch(:form)
+
+%hr
+.row
+ %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
+ .form-group.issue-assignee
+ = form.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
+ .col-sm-10{ class: ("col-lg-8" if has_due_date) }
+ .issuable-form-select-holder
+ - if issuable.assignee_id
+ = form.hidden_field :assignee_id
+ = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
+ placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: issuable.project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
+ .form-group.issue-milestone
+ = form.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
+ .col-sm-10{ class: ("col-lg-8" if has_due_date) }
+ .issuable-form-select-holder
+ = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
+ .form-group
+ - has_labels = @labels && @labels.any?
+ = form.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
+ = form.hidden_field :label_ids, multiple: true, value: ''
+ .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
+ .issuable-form-select-holder
+ = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label"
+ - if has_due_date
+ .col-lg-6
+ .form-group
+ = form.label :due_date, "Due date", class: "control-label"
+ .col-sm-10
+ .issuable-form-select-holder
+ = form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb
index 331727ba9d8..fccddb70d18 100644
--- a/app/workers/authorized_projects_worker.rb
+++ b/app/workers/authorized_projects_worker.rb
@@ -2,14 +2,33 @@ class AuthorizedProjectsWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ LEASE_TIMEOUT = 1.minute.to_i
+
def self.bulk_perform_async(args_list)
Sidekiq::Client.push_bulk('class' => self, 'args' => args_list)
end
def perform(user_id)
user = User.find_by(id: user_id)
- return unless user
- user.refresh_authorized_projects
+ refresh(user) if user
+ end
+
+ def refresh(user)
+ lease_key = "refresh_authorized_projects:#{user.id}"
+ lease = Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT)
+
+ until uuid = lease.try_obtain
+ # Keep trying until we obtain the lease. If we don't do so we may end up
+ # not updating the list of authorized projects properly. To prevent
+ # hammering Redis too much we'll wait for a bit between retries.
+ sleep(1)
+ end
+
+ begin
+ user.refresh_authorized_projects
+ ensure
+ Gitlab::ExclusiveLease.cancel(lease_key, uuid)
+ end
end
end