summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorDouwe Maan <douwe@selenight.nl>2017-08-30 10:14:56 +0200
committerDouwe Maan <douwe@selenight.nl>2017-08-30 10:14:56 +0200
commitb181d3124e4aefca59fb8fe64400eab8d9c73ad8 (patch)
treed1a0964c8380abb5781a034dab95e95ac478f7f9 /app
parente5c8d2ca51566f18ef9f203ef90d4fe1fb4fbbfa (diff)
parent43ba967b0556e2ff01526ff8298f015ec77830f6 (diff)
downloadgitlab-ce-b181d3124e4aefca59fb8fe64400eab8d9c73ad8.tar.gz
Merge branch 'master' into issue-discussions-refactor
# Conflicts: # app/models/issue.rb
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/api.js24
-rw-r--r--app/assets/javascripts/copy_to_clipboard.js14
-rw-r--r--app/assets/javascripts/dispatcher.js4
-rw-r--r--app/assets/javascripts/droplab/drop_down.js7
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_emoji.js82
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js2
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_bundle.js1
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js5
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js14
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_token_keys.js20
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js21
-rw-r--r--app/assets/javascripts/lib/utils/sticky.js2
-rw-r--r--app/assets/javascripts/merge_request_tabs.js1
-rw-r--r--app/assets/javascripts/monitoring/components/monitoring_column.vue18
-rw-r--r--app/assets/javascripts/monitoring/components/monitoring_deployment.vue9
-rw-r--r--app/assets/javascripts/monitoring/components/monitoring_flag.vue7
-rw-r--r--app/assets/javascripts/monitoring/constants.js4
-rw-r--r--app/assets/javascripts/monitoring/utils/date_time_formatters.js15
-rw-r--r--app/assets/javascripts/new_sidebar.js1
-rw-r--r--app/assets/javascripts/pipelines/components/navigation_tabs.vue54
-rw-r--r--app/assets/javascripts/repo/components/repo_commit_section.vue4
-rw-r--r--app/assets/javascripts/repo/services/repo_service.js20
-rw-r--r--app/assets/javascripts/users_select.js7
-rw-r--r--app/assets/stylesheets/framework/avatar.scss2
-rw-r--r--app/assets/stylesheets/framework/badges.scss2
-rw-r--r--app/assets/stylesheets/framework/blocks.scss4
-rw-r--r--app/assets/stylesheets/framework/buttons.scss2
-rw-r--r--app/assets/stylesheets/framework/calendar.scss6
-rw-r--r--app/assets/stylesheets/framework/common.scss16
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss12
-rw-r--r--app/assets/stylesheets/framework/filters.scss18
-rw-r--r--app/assets/stylesheets/framework/flash.scss2
-rw-r--r--app/assets/stylesheets/framework/forms.scss6
-rw-r--r--app/assets/stylesheets/framework/header.scss6
-rw-r--r--app/assets/stylesheets/framework/lists.scss4
-rw-r--r--app/assets/stylesheets/framework/mixins.scss2
-rw-r--r--app/assets/stylesheets/framework/modal.scss10
-rw-r--r--app/assets/stylesheets/framework/nav.scss4
-rw-r--r--app/assets/stylesheets/framework/page-header.scss2
-rw-r--r--app/assets/stylesheets/framework/selects.scss14
-rw-r--r--app/assets/stylesheets/framework/snippets.scss2
-rw-r--r--app/assets/stylesheets/framework/tables.scss2
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap.scss4
-rw-r--r--app/assets/stylesheets/framework/typography.scss12
-rw-r--r--app/assets/stylesheets/framework/variables.scss3
-rw-r--r--app/assets/stylesheets/framework/wells.scss2
-rw-r--r--app/assets/stylesheets/highlight/dark.scss8
-rw-r--r--app/assets/stylesheets/highlight/monokai.scss2
-rw-r--r--app/assets/stylesheets/highlight/solarized_dark.scss2
-rw-r--r--app/assets/stylesheets/highlight/solarized_light.scss2
-rw-r--r--app/assets/stylesheets/highlight/white.scss32
-rw-r--r--app/assets/stylesheets/mailers/highlighted_diff_email.scss32
-rw-r--r--app/assets/stylesheets/new_nav.scss10
-rw-r--r--app/assets/stylesheets/new_sidebar.scss13
-rw-r--r--app/assets/stylesheets/pages/boards.scss2
-rw-r--r--app/assets/stylesheets/pages/builds.scss4
-rw-r--r--app/assets/stylesheets/pages/ci_projects.scss2
-rw-r--r--app/assets/stylesheets/pages/commits.scss9
-rw-r--r--app/assets/stylesheets/pages/convdev_index.scss6
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss10
-rw-r--r--app/assets/stylesheets/pages/diff.scss6
-rw-r--r--app/assets/stylesheets/pages/environments.scss6
-rw-r--r--app/assets/stylesheets/pages/events.scss2
-rw-r--r--app/assets/stylesheets/pages/issuable.scss6
-rw-r--r--app/assets/stylesheets/pages/issues.scss4
-rw-r--r--app/assets/stylesheets/pages/login.scss8
-rw-r--r--app/assets/stylesheets/pages/members.scss6
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss18
-rw-r--r--app/assets/stylesheets/pages/milestone.scss4
-rw-r--r--app/assets/stylesheets/pages/note_form.scss2
-rw-r--r--app/assets/stylesheets/pages/notes.scss14
-rw-r--r--app/assets/stylesheets/pages/pipeline_schedules.scss2
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss12
-rw-r--r--app/assets/stylesheets/pages/profile.scss4
-rw-r--r--app/assets/stylesheets/pages/projects.scss16
-rw-r--r--app/assets/stylesheets/pages/repo.scss27
-rw-r--r--app/assets/stylesheets/pages/runners.scss2
-rw-r--r--app/assets/stylesheets/pages/search.scss2
-rw-r--r--app/assets/stylesheets/pages/sherlock.scss2
-rw-r--r--app/assets/stylesheets/pages/todos.scss10
-rw-r--r--app/assets/stylesheets/pages/tree.scss2
-rw-r--r--app/assets/stylesheets/pages/ui_dev_kit.scss2
-rw-r--r--app/assets/stylesheets/pages/wiki.scss4
-rw-r--r--app/assets/stylesheets/pages/xterm.scss2
-rw-r--r--app/assets/stylesheets/print.scss2
-rw-r--r--app/controllers/admin/projects_controller.rb6
-rw-r--r--app/controllers/autocomplete_controller.rb18
-rw-r--r--app/controllers/concerns/issuable_collections.rb12
-rw-r--r--app/controllers/groups/milestones_controller.rb6
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb8
-rw-r--r--app/controllers/projects/issues_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb4
-rw-r--r--app/controllers/sessions_controller.rb8
-rw-r--r--app/finders/admin/projects_finder.rb80
-rw-r--r--app/finders/groups_finder.rb36
-rw-r--r--app/finders/issuable_finder.rb10
-rw-r--r--app/finders/projects_finder.rb17
-rw-r--r--app/helpers/application_helper.rb2
-rw-r--r--app/helpers/avatars_helper.rb11
-rw-r--r--app/helpers/button_helper.rb20
-rw-r--r--app/helpers/commits_helper.rb2
-rw-r--r--app/helpers/events_helper.rb1
-rw-r--r--app/helpers/issuables_helper.rb2
-rw-r--r--app/helpers/milestones_helper.rb2
-rw-r--r--app/helpers/milestones_routing_helper.rb8
-rw-r--r--app/models/ci/build.rb5
-rw-r--r--app/models/ci/pipeline.rb3
-rw-r--r--app/models/ci/runner.rb2
-rw-r--r--app/models/ci/stage.rb4
-rw-r--r--app/models/concerns/awardable.rb15
-rw-r--r--app/models/concerns/editable.rb2
-rw-r--r--app/models/concerns/milestoneish.rb8
-rw-r--r--app/models/concerns/protected_ref.rb2
-rw-r--r--app/models/dashboard_milestone.rb2
-rw-r--r--app/models/deployment.rb2
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/group.rb4
-rw-r--r--app/models/group_milestone.rb2
-rw-r--r--app/models/issue.rb7
-rw-r--r--app/models/merge_request.rb22
-rw-r--r--app/models/milestone.rb6
-rw-r--r--app/models/network/graph.rb4
-rw-r--r--app/models/notification_recipient.rb53
-rw-r--r--app/models/project.rb9
-rw-r--r--app/models/project_services/chat_notification_service.rb6
-rw-r--r--app/models/project_services/hipchat_service.rb6
-rw-r--r--app/models/repository.rb21
-rw-r--r--app/policies/project_policy.rb6
-rw-r--r--app/services/akismet_service.rb2
-rw-r--r--app/services/ci/create_pipeline_service.rb2
-rw-r--r--app/services/commits/create_service.rb2
-rw-r--r--app/services/concerns/users/new_user_notifier.rb9
-rw-r--r--app/services/create_branch_service.rb2
-rw-r--r--app/services/delete_branch_service.rb2
-rw-r--r--app/services/git_hooks_service.rb32
-rw-r--r--app/services/git_operation_service.rb14
-rw-r--r--app/services/git_push_service.rb10
-rw-r--r--app/services/groups/nested_create_service.rb49
-rw-r--r--app/services/issuable_base_service.rb4
-rw-r--r--app/services/issues/create_service.rb2
-rw-r--r--app/services/merge_requests/create_service.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb8
-rw-r--r--app/services/merge_requests/merge_when_pipeline_succeeds_service.rb2
-rw-r--r--app/services/merge_requests/update_service.rb2
-rw-r--r--app/services/milestones/close_service.rb2
-rw-r--r--app/services/milestones/create_service.rb2
-rw-r--r--app/services/milestones/destroy_service.rb2
-rw-r--r--app/services/milestones/reopen_service.rb2
-rw-r--r--app/services/notification_recipient_service.rb2
-rw-r--r--app/services/projects/count_service.rb43
-rw-r--r--app/services/projects/create_service.rb17
-rw-r--r--app/services/projects/forks_count_service.rb28
-rw-r--r--app/services/projects/open_issues_count_service.rb15
-rw-r--r--app/services/projects/open_merge_requests_count_service.rb13
-rw-r--r--app/services/spam_service.rb2
-rw-r--r--app/services/system_note_service.rb2
-rw-r--r--app/services/tags/create_service.rb2
-rw-r--r--app/services/tags/destroy_service.rb2
-rw-r--r--app/services/test_hooks/system_service.rb37
-rw-r--r--app/services/user_project_access_changed_service.rb10
-rw-r--r--app/services/users/create_service.rb8
-rw-r--r--app/services/users/update_service.rb6
-rw-r--r--app/services/validate_new_branch_service.rb2
-rw-r--r--app/views/admin/dashboard/_head.html.haml4
-rw-r--r--app/views/admin/monitoring/_head.html.haml4
-rw-r--r--app/views/doorkeeper/authorizations/new.html.haml78
-rw-r--r--app/views/events/_event_push.atom.haml13
-rw-r--r--app/views/events/event/_push.html.haml4
-rw-r--r--app/views/groups/merge_requests.html.haml6
-rw-r--r--app/views/groups/milestones/show.html.haml2
-rw-r--r--app/views/layouts/errors.html.haml4
-rw-r--r--app/views/layouts/nav/_breadcrumbs.html.haml3
-rw-r--r--app/views/layouts/nav/_new_admin_sidebar.html.haml12
-rw-r--r--app/views/layouts/nav/_new_group_sidebar.html.haml2
-rw-r--r--app/views/layouts/nav/_new_profile_sidebar.html.haml2
-rw-r--r--app/views/layouts/nav/_new_project_sidebar.html.haml8
-rw-r--r--app/views/layouts/nav/_project.html.haml6
-rw-r--r--app/views/layouts/oauth_error.html.haml2
-rw-r--r--app/views/projects/_project_templates.html.haml2
-rw-r--r--app/views/projects/boards/_show.html.haml1
-rw-r--r--app/views/projects/commits/_commit.html.haml6
-rw-r--r--app/views/projects/issues/show.html.haml3
-rw-r--r--app/views/projects/new.html.haml7
-rw-r--r--app/views/projects/notes/_actions.html.haml8
-rw-r--r--app/views/projects/notes/_more_actions_dropdown.html.haml2
-rw-r--r--app/views/projects/pipelines/charts/_pipelines.haml6
-rw-r--r--app/views/projects/runners/_runner.html.haml2
-rw-r--r--app/views/search/_form.html.haml2
-rwxr-xr-xapp/views/shared/icons/_icon_status_success.svg2
-rw-r--r--app/views/shared/icons/_icon_status_success_solid.svg1
-rw-r--r--app/views/shared/issuable/_close_reopen_button.html.haml3
-rw-r--r--app/views/shared/issuable/_close_reopen_report_toggle.html.haml22
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml7
-rw-r--r--app/views/shared/issuable/_user_dropdown_item.html.haml2
-rw-r--r--app/views/shared/milestones/_milestone.html.haml8
-rw-r--r--app/views/shared/milestones/_top.html.haml8
-rw-r--r--app/views/shared/projects/_dropdown.html.haml5
-rw-r--r--app/workers/authorized_projects_worker.rb22
-rw-r--r--app/workers/build_coverage_worker.rb2
-rw-r--r--app/workers/build_finished_worker.rb4
-rw-r--r--app/workers/build_hooks_worker.rb4
-rw-r--r--app/workers/build_queue_worker.rb4
-rw-r--r--app/workers/build_success_worker.rb4
-rw-r--r--app/workers/concerns/build_queue.rb8
-rw-r--r--app/workers/concerns/exception_backtrace.rb8
-rw-r--r--app/workers/concerns/pipeline_queue.rb12
-rw-r--r--app/workers/expire_job_cache_worker.rb4
-rw-r--r--app/workers/expire_pipeline_cache_worker.rb2
-rw-r--r--app/workers/group_destroy_worker.rb1
-rw-r--r--app/workers/merge_worker.rb2
-rw-r--r--app/workers/namespaceless_project_destroy_worker.rb1
-rw-r--r--app/workers/pipeline_hooks_worker.rb2
-rw-r--r--app/workers/pipeline_process_worker.rb2
-rw-r--r--app/workers/pipeline_success_worker.rb2
-rw-r--r--app/workers/pipeline_update_worker.rb2
-rw-r--r--app/workers/project_destroy_worker.rb1
-rw-r--r--app/workers/project_export_worker.rb1
-rw-r--r--app/workers/repository_import_worker.rb1
-rw-r--r--app/workers/stage_update_worker.rb2
219 files changed, 1239 insertions, 660 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 56f91e95bb9..78cb3def879 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -55,13 +55,18 @@ const Api = {
// Return projects list. Filtered by query
projects(query, options, callback) {
const url = Api.buildUrl(Api.projectsPath);
+ const defaults = {
+ search: query,
+ per_page: 20,
+ };
+
+ if (gon.current_user_id) {
+ defaults.membership = true;
+ }
+
return $.ajax({
url,
- data: Object.assign({
- search: query,
- per_page: 20,
- membership: true,
- }, options),
+ data: Object.assign(defaults, options),
dataType: 'json',
})
.done(projects => callback(projects));
@@ -96,18 +101,17 @@ const Api = {
.done(projects => callback(projects));
},
- commitMultiple(id, data, callback) {
+ commitMultiple(id, data) {
+ // see https://docs.gitlab.com/ce/api/commits.html#create-a-commit-with-multiple-files-and-actions
const url = Api.buildUrl(Api.commitPath)
.replace(':id', id);
- return $.ajax({
+ return this.wrapAjaxCall({
url,
type: 'POST',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data),
dataType: 'json',
- })
- .done(commitData => callback(commitData))
- .fail(message => callback(message.responseJSON));
+ });
},
// Return text for a specific license
diff --git a/app/assets/javascripts/copy_to_clipboard.js b/app/assets/javascripts/copy_to_clipboard.js
index ab9a8e43dd1..1f3c7e1772d 100644
--- a/app/assets/javascripts/copy_to_clipboard.js
+++ b/app/assets/javascripts/copy_to_clipboard.js
@@ -29,12 +29,14 @@ showTooltip = function(target, title) {
var $target = $(target);
var originalTitle = $target.data('original-title');
- $target
- .attr('title', 'Copied')
- .tooltip('fixTitle')
- .tooltip('show')
- .attr('title', originalTitle)
- .tooltip('fixTitle');
+ if (!$target.data('hideTooltip')) {
+ $target
+ .attr('title', 'Copied')
+ .tooltip('fixTitle')
+ .tooltip('show')
+ .attr('title', originalTitle)
+ .tooltip('fixTitle');
+ }
};
$(function() {
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index e8ef8dcc0c5..6a10e409b97 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -183,13 +183,13 @@ import initChangesDropdown from './init_changes_dropdown';
break;
case 'dashboard:issues':
case 'dashboard:merge_requests':
- case 'groups:merge_requests':
new ProjectSelect();
initLegacyFilters();
break;
case 'groups:issues':
+ case 'groups:merge_requests':
if (filteredSearchEnabled) {
- const filteredSearchManager = new gl.FilteredSearchManager('issues');
+ const filteredSearchManager = new gl.FilteredSearchManager(page === 'groups:issues' ? 'issues' : 'merge_requests');
filteredSearchManager.setup();
}
new ProjectSelect();
diff --git a/app/assets/javascripts/droplab/drop_down.js b/app/assets/javascripts/droplab/drop_down.js
index 70cd337fb8a..3901bb177fe 100644
--- a/app/assets/javascripts/droplab/drop_down.js
+++ b/app/assets/javascripts/droplab/drop_down.js
@@ -85,6 +85,13 @@ class DropDown {
const renderableList = this.list.querySelector('ul[data-dynamic]') || this.list;
renderableList.innerHTML = children.join('');
+
+ const listEvent = new CustomEvent('render.dl', {
+ detail: {
+ list: this,
+ },
+ });
+ this.list.dispatchEvent(listEvent);
}
renderChildren(data) {
diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js
new file mode 100644
index 00000000000..f9bbbf0cbc1
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js
@@ -0,0 +1,82 @@
+/* global Flash */
+
+import Ajax from '~/droplab/plugins/ajax';
+import Filter from '~/droplab/plugins/filter';
+import './filtered_search_dropdown';
+
+class DropdownEmoji extends gl.FilteredSearchDropdown {
+ constructor(options = {}) {
+ super(options);
+ this.config = {
+ Ajax: {
+ endpoint: `${gon.relative_url_root || ''}/autocomplete/award_emojis`,
+ method: 'setData',
+ loadingTemplate: this.loadingTemplate,
+ onError() {
+ /* eslint-disable no-new */
+ new Flash('An error occured fetching the dropdown data.');
+ /* eslint-enable no-new */
+ },
+ },
+ Filter: {
+ template: 'name',
+ },
+ };
+
+ import(/* webpackChunkName: 'emoji' */ '~/emoji')
+ .then(({ glEmojiTag }) => { this.glEmojiTag = glEmojiTag; })
+ .catch(() => { /* ignore error and leave emoji name in the search bar */ });
+
+ this.unbindEvents();
+ this.bindEvents();
+ }
+
+ bindEvents() {
+ super.bindEvents();
+
+ this.listRenderedWrapper = this.listRendered.bind(this);
+ this.dropdown.addEventListener('render.dl', this.listRenderedWrapper);
+ }
+
+ unbindEvents() {
+ this.dropdown.removeEventListener('render.dl', this.listRenderedWrapper);
+ super.unbindEvents();
+ }
+
+ listRendered() {
+ this.replaceEmojiElement();
+ }
+
+ itemClicked(e) {
+ super.itemClicked(e, (selected) => {
+ const name = selected.querySelector('.js-data-value').innerText.trim();
+ return gl.DropdownUtils.getEscapedText(name);
+ });
+ }
+
+ renderContent(forceShowList = false) {
+ this.droplab.changeHookList(this.hookId, this.dropdown, [Ajax, Filter], this.config);
+ super.renderContent(forceShowList);
+ }
+
+ replaceEmojiElement() {
+ if (!this.glEmojiTag) return;
+
+ // Replace empty gl-emoji tag to real content
+ const dropdownItems = [...this.dropdown.querySelectorAll('.filter-dropdown-item')];
+ dropdownItems.forEach((dropdownItem) => {
+ const name = dropdownItem.querySelector('.js-data-value').innerText;
+ const emojiTag = this.glEmojiTag(name);
+ const emojiElement = dropdownItem.querySelector('gl-emoji');
+ emojiElement.outerHTML = emojiTag;
+ });
+ }
+
+ init() {
+ this.droplab
+ .addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
+ }
+}
+
+window.gl = window.gl || {};
+gl.DropdownEmoji = DropdownEmoji;
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index a81389ab088..1c5ca1d3cf9 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -61,7 +61,7 @@ class DropdownHint extends gl.FilteredSearchDropdown {
.map(tokenKey => ({
icon: `fa-${tokenKey.icon}`,
hint: tokenKey.key,
- tag: `<${tokenKey.symbol}${tokenKey.key}>`,
+ tag: `<${tokenKey.tag}>`,
type: tokenKey.type,
}));
diff --git a/app/assets/javascripts/filtered_search/filtered_search_bundle.js b/app/assets/javascripts/filtered_search/filtered_search_bundle.js
index 132b6fe698a..6d5dd747224 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_bundle.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_bundle.js
@@ -1,3 +1,4 @@
+import './dropdown_emoji';
import './dropdown_hint';
import './dropdown_non_user';
import './dropdown_user';
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index dd1c067df87..46c80dfd45e 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -58,6 +58,11 @@ class FilteredSearchDropdownManager {
},
element: this.container.querySelector('#js-dropdown-label'),
},
+ 'my-reaction': {
+ reference: null,
+ gl: 'DropdownEmoji',
+ element: this.container.querySelector('#js-dropdown-my-reaction'),
+ },
hint: {
reference: null,
gl: 'DropdownHint',
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index a31be2b0bc7..038239bf466 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -439,8 +439,13 @@ class FilteredSearchManager {
const match = this.filteredSearchTokenKeys.searchByKeyParam(keyParam);
if (match) {
- const indexOf = keyParam.indexOf('_');
- const sanitizedKey = indexOf !== -1 ? keyParam.slice(0, keyParam.indexOf('_')) : keyParam;
+ // Use lastIndexOf because the token key is allowed to contain underscore
+ // e.g. 'my_reaction' is the token key of 'my_reaction_emoji'
+ const lastIndexOf = keyParam.lastIndexOf('_');
+ let sanitizedKey = lastIndexOf !== -1 ? keyParam.slice(0, lastIndexOf) : keyParam;
+ // Replace underscore with hyphen in the sanitizedkey.
+ // e.g. 'my_reaction' => 'my-reaction'
+ sanitizedKey = sanitizedKey.replace('_', '-');
const symbol = match.symbol;
let quotationsToUse = '';
@@ -515,7 +520,10 @@ class FilteredSearchManager {
const condition = this.filteredSearchTokenKeys
.searchByConditionKeyValue(token.key, token.value.toLowerCase());
const { param } = this.filteredSearchTokenKeys.searchByKey(token.key) || {};
- const keyParam = param ? `${token.key}_${param}` : token.key;
+ // Replace hyphen with underscore to use as request parameter
+ // e.g. 'my-reaction' => 'my_reaction'
+ const underscoredKey = token.key.replace('-', '_');
+ const keyParam = param ? `${underscoredKey}_${param}` : underscoredKey;
let tokenPath = '';
if (condition) {
diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
index 025d4d8795b..be595d7df1a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
@@ -4,26 +4,42 @@ const tokenKeys = [{
param: 'username',
symbol: '@',
icon: 'pencil',
+ tag: '@author',
}, {
key: 'assignee',
type: 'string',
param: 'username',
symbol: '@',
icon: 'user',
+ tag: '@assignee',
}, {
key: 'milestone',
type: 'string',
param: 'title',
symbol: '%',
icon: 'clock-o',
+ tag: '%milestone',
}, {
key: 'label',
type: 'array',
param: 'name[]',
symbol: '~',
icon: 'tag',
+ tag: '~label',
}];
+if (gon.current_user_id) {
+ // Appending tokenkeys only logged-in
+ tokenKeys.push({
+ key: 'my-reaction',
+ type: 'string',
+ param: 'emoji',
+ symbol: '',
+ icon: 'thumbs-up',
+ tag: 'emoji',
+ });
+}
+
const alternativeTokenKeys = [{
key: 'label',
type: 'string',
@@ -84,6 +100,10 @@ class FilteredSearchTokenKeys {
return tokenKeysWithAlternative.find((tokenKey) => {
let tokenKeyParam = tokenKey.key;
+ // Replace hyphen with underscore to compare keyParam with tokenKeyParam
+ // e.g. 'my-reaction' => 'my_reaction'
+ tokenKeyParam = tokenKeyParam.replace('-', '_');
+
if (tokenKey.param) {
tokenKeyParam += `_${tokenKey.param}`;
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
index 243ee4d723a..28e8240169d 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -132,6 +132,23 @@ class FilteredSearchVisualTokens {
.catch(() => { });
}
+ static updateEmojiTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) {
+ const container = tokenValueContainer;
+ const element = tokenValueElement;
+
+ return import(/* webpackChunkName: 'emoji' */ '../emoji')
+ .then((Emoji) => {
+ if (!Emoji.isEmojiNameValid(tokenValue)) {
+ return;
+ }
+
+ container.dataset.originalValue = tokenValue;
+ element.innerHTML = Emoji.glEmojiTag(tokenValue);
+ })
+ // ignore error and leave emoji name in the search bar
+ .catch(() => { });
+ }
+
static renderVisualTokenValue(parentElement, tokenName, tokenValue) {
const tokenValueContainer = parentElement.querySelector('.value-container');
const tokenValueElement = tokenValueContainer.querySelector('.value');
@@ -144,6 +161,10 @@ class FilteredSearchVisualTokens {
FilteredSearchVisualTokens.updateUserTokenAppearance(
tokenValueContainer, tokenValueElement, tokenValue,
);
+ } else if (tokenType === 'my-reaction') {
+ FilteredSearchVisualTokens.updateEmojiTokenAppearance(
+ tokenValueContainer, tokenValueElement, tokenValue,
+ );
}
}
diff --git a/app/assets/javascripts/lib/utils/sticky.js b/app/assets/javascripts/lib/utils/sticky.js
index ff2b66046b4..283c0ec0410 100644
--- a/app/assets/javascripts/lib/utils/sticky.js
+++ b/app/assets/javascripts/lib/utils/sticky.js
@@ -1,5 +1,5 @@
export const isSticky = (el, scrollY, stickyTop) => {
- const top = el.offsetTop - scrollY;
+ const top = Math.floor(el.offsetTop - scrollY);
if (top <= stickyTop) {
el.classList.add('is-stuck');
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 5a9b3d19f84..3b3620fe61b 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -253,6 +253,7 @@ import bp from './breakpoints';
loadDiff(source) {
if (this.diffsLoaded) {
+ document.dispatchEvent(new CustomEvent('scroll'));
return;
}
diff --git a/app/assets/javascripts/monitoring/components/monitoring_column.vue b/app/assets/javascripts/monitoring/components/monitoring_column.vue
index 407af51cb7a..a31c26fb4fc 100644
--- a/app/assets/javascripts/monitoring/components/monitoring_column.vue
+++ b/app/assets/javascripts/monitoring/components/monitoring_column.vue
@@ -7,6 +7,7 @@
import eventHub from '../event_hub';
import measurements from '../utils/measurements';
import { formatRelevantDigits } from '../../lib/utils/number_utils';
+ import { timeScaleFormat } from '../utils/date_time_formatters';
import bp from '../../breakpoints';
const bisectDate = d3.bisector(d => d.time).left;
@@ -159,6 +160,7 @@
const xAxis = d3.svg.axis()
.scale(axisXScale)
.ticks(measurements.xTicks)
+ .tickFormat(timeScaleFormat)
.orient('bottom');
const yAxis = d3.svg.axis()
@@ -266,14 +268,6 @@
stroke-width="2"
transform="translate(-5, 20)">
</path>
- <rect
- class="prometheus-graph-overlay"
- :width="(graphWidth - 70)"
- :height="(graphHeight - 100)"
- transform="translate(-5, 20)"
- ref="graphOverlay"
- @mousemove="handleMouseOverGraph($event)">
- </rect>
<monitoring-deployment
:show-deploy-info="showDeployInfo"
:deployment-data="reducedDeploymentData"
@@ -289,6 +283,14 @@
:graph-height="graphHeight"
:graph-height-offset="graphHeightOffset"
/>
+ <rect
+ class="prometheus-graph-overlay"
+ :width="(graphWidth - 70)"
+ :height="(graphHeight - 100)"
+ transform="translate(-5, 20)"
+ ref="graphOverlay"
+ @mousemove="handleMouseOverGraph($event)">
+ </rect>
</svg>
</svg>
</div>
diff --git a/app/assets/javascripts/monitoring/components/monitoring_deployment.vue b/app/assets/javascripts/monitoring/components/monitoring_deployment.vue
index e6432ba3191..dadbcd1aaa6 100644
--- a/app/assets/javascripts/monitoring/components/monitoring_deployment.vue
+++ b/app/assets/javascripts/monitoring/components/monitoring_deployment.vue
@@ -1,8 +1,5 @@
<script>
- import {
- dateFormat,
- timeFormat,
- } from '../constants';
+ import { dateFormat, timeFormat } from '../utils/date_time_formatters';
export default {
props: {
@@ -58,7 +55,7 @@
class="deploy-info"
v-if="showDeployInfo">
<g
- v-for="(deployment, index) in deploymentData"
+ v-for="(deployment, index) in deploymentData"
:key="index"
:class="nameDeploymentClass(deployment)"
:transform="transformDeploymentGroup(deployment)">
@@ -92,7 +89,7 @@
width="90"
height="58">
</rect>
- <g
+ <g
transform="translate(5, 2)">
<text
class="deploy-info-text text-metric-bold">
diff --git a/app/assets/javascripts/monitoring/components/monitoring_flag.vue b/app/assets/javascripts/monitoring/components/monitoring_flag.vue
index 5a0e50fcab3..61cbeeebb17 100644
--- a/app/assets/javascripts/monitoring/components/monitoring_flag.vue
+++ b/app/assets/javascripts/monitoring/components/monitoring_flag.vue
@@ -1,8 +1,5 @@
<script>
- import {
- dateFormat,
- timeFormat,
- } from '../constants';
+ import { dateFormat, timeFormat } from '../utils/date_time_formatters';
export default {
props: {
@@ -72,7 +69,7 @@
r="5"
transform="translate(-5, 20)">
</circle>
- <svg
+ <svg
class="rect-text-metric"
:x="currentFlagPosition"
y="0">
diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js
deleted file mode 100644
index c3a8da52404..00000000000
--- a/app/assets/javascripts/monitoring/constants.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import d3 from 'd3';
-
-export const dateFormat = d3.time.format('%b %d, %Y');
-export const timeFormat = d3.time.format('%H:%M%p');
diff --git a/app/assets/javascripts/monitoring/utils/date_time_formatters.js b/app/assets/javascripts/monitoring/utils/date_time_formatters.js
new file mode 100644
index 00000000000..26bcaa02511
--- /dev/null
+++ b/app/assets/javascripts/monitoring/utils/date_time_formatters.js
@@ -0,0 +1,15 @@
+import d3 from 'd3';
+
+export const dateFormat = d3.time.format('%b %-d, %Y');
+export const timeFormat = d3.time.format('%-I:%M%p');
+
+export const timeScaleFormat = d3.time.format.multi([
+ ['.%L', d => d.getMilliseconds()],
+ [':%S', d => d.getSeconds()],
+ ['%-I:%M', d => d.getMinutes()],
+ ['%-I %p', d => d.getHours()],
+ ['%a %-d', d => d.getDay() && d.getDate() !== 1],
+ ['%b %-d', d => d.getDate() !== 1],
+ ['%B', d => d.getMonth()],
+ ['%Y', () => true],
+]);
diff --git a/app/assets/javascripts/new_sidebar.js b/app/assets/javascripts/new_sidebar.js
index 2d1ed9e4076..b18d12b48b5 100644
--- a/app/assets/javascripts/new_sidebar.js
+++ b/app/assets/javascripts/new_sidebar.js
@@ -47,7 +47,6 @@ export default class NewNavSidebar {
if (this.$sidebar.length) {
this.$sidebar.toggleClass('sidebar-icons-only', collapsed);
- this.$page.toggleClass('page-with-new-sidebar', !collapsed);
this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
}
NewNavSidebar.setCollapsedCookie(collapsed);
diff --git a/app/assets/javascripts/pipelines/components/navigation_tabs.vue b/app/assets/javascripts/pipelines/components/navigation_tabs.vue
index d2f6d47f043..73f7e3a0cad 100644
--- a/app/assets/javascripts/pipelines/components/navigation_tabs.vue
+++ b/app/assets/javascripts/pipelines/components/navigation_tabs.vue
@@ -1,23 +1,29 @@
<script>
-export default {
- name: 'PipelineNavigationTabs',
- props: {
- scope: {
- type: String,
- required: true,
+ export default {
+ name: 'PipelineNavigationTabs',
+ props: {
+ scope: {
+ type: String,
+ required: true,
+ },
+ count: {
+ type: Object,
+ required: true,
+ },
+ paths: {
+ type: Object,
+ required: true,
+ },
},
- count: {
- type: Object,
- required: true,
+ mounted() {
+ $(document).trigger('init.scrolling-tabs');
},
- paths: {
- type: Object,
- required: true,
+ methods: {
+ shouldRenderBadge(count) {
+ // 0 is valid in a badge, but evaluates to false, we need to check for undefined
+ return count !== undefined;
+ },
},
- },
- mounted() {
- $(document).trigger('init.scrolling-tabs');
- },
};
</script>
<template>
@@ -27,7 +33,9 @@ export default {
:class="{ active: scope === 'all'}">
<a :href="paths.allPath">
All
- <span class="badge js-totalbuilds-count">
+ <span
+ v-if="shouldRenderBadge(count.all)"
+ class="badge js-totalbuilds-count">
{{count.all}}
</span>
</a>
@@ -37,7 +45,9 @@ export default {
:class="{ active: scope === 'pending'}">
<a :href="paths.pendingPath">
Pending
- <span class="badge">
+ <span
+ v-if="shouldRenderBadge(count.pending)"
+ class="badge">
{{count.pending}}
</span>
</a>
@@ -47,7 +57,9 @@ export default {
:class="{ active: scope === 'running'}">
<a :href="paths.runningPath">
Running
- <span class="badge">
+ <span
+ v-if="shouldRenderBadge(count.running)"
+ class="badge">
{{count.running}}
</span>
</a>
@@ -57,7 +69,9 @@ export default {
:class="{ active: scope === 'finished'}">
<a :href="paths.finishedPath">
Finished
- <span class="badge">
+ <span
+ v-if="shouldRenderBadge(count.finished)"
+ class="badge">
{{count.finished}}
</span>
</a>
diff --git a/app/assets/javascripts/repo/components/repo_commit_section.vue b/app/assets/javascripts/repo/components/repo_commit_section.vue
index 5ec4a9b6593..1282828b504 100644
--- a/app/assets/javascripts/repo/components/repo_commit_section.vue
+++ b/app/assets/javascripts/repo/components/repo_commit_section.vue
@@ -42,7 +42,9 @@ export default {
actions,
};
Store.submitCommitsLoading = true;
- Service.commitFiles(payload, this.resetCommitState);
+ Service.commitFiles(payload)
+ .then(this.resetCommitState)
+ .catch(() => Flash('An error occured while committing your changes'));
},
resetCommitState() {
diff --git a/app/assets/javascripts/repo/services/repo_service.js b/app/assets/javascripts/repo/services/repo_service.js
index 3cf204e6ec8..af83497fa39 100644
--- a/app/assets/javascripts/repo/services/repo_service.js
+++ b/app/assets/javascripts/repo/services/repo_service.js
@@ -65,15 +65,17 @@ const RepoService = {
return urlArray.join('/');
},
- commitFiles(payload, cb) {
- Api.commitMultiple(Store.projectId, payload, (data) => {
- if (data.short_id && data.stats) {
- Flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice');
- } else {
- Flash(data.message);
- }
- cb();
- });
+ commitFiles(payload) {
+ return Api.commitMultiple(Store.projectId, payload)
+ .then(this.commitFlash);
+ },
+
+ commitFlash(data) {
+ if (data.short_id && data.stats) {
+ window.Flash(`Your changes have been committed. Commit ${data.short_id} with ${data.stats.additions} additions, ${data.stats.deletions} deletions.`, 'notice');
+ } else {
+ window.Flash(data.message);
+ }
},
};
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 16ebf5916dc..a31fedee021 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -588,9 +588,10 @@ function UsersSelect(currentUser, els) {
if (showEmailUser && data.results.length === 0 && query.term.match(/^[^@]+@[^@]+$/)) {
var trimmed = query.term.trim();
emailUser = {
- name: "Invite \"" + query.term + "\"",
+ name: "Invite \"" + query.term + "\" by email",
username: trimmed,
- id: trimmed
+ id: trimmed,
+ invite: true
};
data.results.unshift(emailUser);
}
@@ -642,7 +643,7 @@ UsersSelect.prototype.formatResult = function(user) {
} else {
avatar = gon.default_avatar_url;
}
- return "<div class='user-result " + (!user.username ? 'no-username' : void 0) + "'> <div class='user-image'><img class='avatar avatar-inline s32' src='" + avatar + "'></div> <div class='user-name dropdown-menu-user-full-name'>" + user.name + "</div> <div class='user-username dropdown-menu-user-username'>" + ("@" + user.username || "") + "</div> </div>";
+ return "<div class='user-result " + (!user.username ? 'no-username' : void 0) + "'> <div class='user-image'><img class='avatar avatar-inline s32' src='" + avatar + "'></div> <div class='user-name dropdown-menu-user-full-name'>" + user.name + "</div> <div class='user-username dropdown-menu-user-username'>" + (!user.invite ? "@" + _.escape(user.username) : "") + "</div> </div>";
};
UsersSelect.prototype.formatSelection = function(user) {
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index 486d88efbc5..bdcbd4021b3 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -78,7 +78,7 @@
&.s60 { font-size: 32px; line-height: 58px; }
&.s70 { font-size: 34px; line-height: 70px; }
&.s90 { font-size: 36px; line-height: 88px; }
- &.s110 { font-size: 40px; line-height: 108px; font-weight: 300; }
+ &.s110 { font-size: 40px; line-height: 108px; font-weight: $gl-font-weight-normal; }
&.s140 { font-size: 72px; line-height: 138px; }
&.s160 { font-size: 96px; line-height: 158px; }
}
diff --git a/app/assets/stylesheets/framework/badges.scss b/app/assets/stylesheets/framework/badges.scss
index 47a8f44c709..6bbe32df772 100644
--- a/app/assets/stylesheets/framework/badges.scss
+++ b/app/assets/stylesheets/framework/badges.scss
@@ -1,5 +1,5 @@
.badge {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
background-color: $badge-bg;
color: $badge-color;
vertical-align: baseline;
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 95a08c960ea..b575ec9de18 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -8,7 +8,7 @@
text-align: center;
padding: 20px;
color: $gl-text-color;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
font-size: 14px;
line-height: 36px;
@@ -213,7 +213,7 @@
h1 {
display: inline;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
font-size: 24px;
color: $gl-text-color;
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 6eabdc63d9e..b4a6b214e98 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -1,7 +1,7 @@
@mixin btn-default {
border-radius: 3px;
font-size: $gl-font-size;
- font-weight: 400;
+ font-weight: $gl-font-weight-normal;
padding: $gl-vert-padding $gl-btn-padding;
&:focus,
diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
index 0ded4a3b423..4ce767e4cc4 100644
--- a/app/assets/stylesheets/framework/calendar.scss
+++ b/app/assets/stylesheets/framework/calendar.scss
@@ -52,13 +52,13 @@
.pika-label {
color: $gl-text-color-secondary;
font-size: 14px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
th {
padding: 2px 0;
color: $note-disabled-comment-color;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
text-transform: lowercase;
border-top: 1px solid $calendar-border-color;
}
@@ -88,7 +88,7 @@
.is-today {
.pika-day {
color: inherit;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 293aa194528..e16fbbf43b5 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -36,12 +36,12 @@
color: $common-gray;
font-size: 14px;
margin-bottom: 12px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
line-height: 24px;
}
.bold {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
.tab-content {
@@ -89,7 +89,7 @@ hr {
}
}
-.item-title { font-weight: 600; }
+.item-title { font-weight: $gl-font-weight-bold; }
/** FLASH message **/
.author_link,
@@ -118,18 +118,18 @@ table a code {
span.update-author {
display: block;
color: $update-author-color;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
font-style: italic;
strong {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
font-style: normal;
}
}
.user-mention {
color: $user-mention-color;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
.field_with_errors {
@@ -222,7 +222,7 @@ li.note {
text-align: center;
background: $error-bg;
color: $white-light;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
a {
color: $white-light;
@@ -339,7 +339,7 @@ table {
.header-with-avatar {
h3 {
margin: 0;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
.username {
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 7322aaae46c..d2951279734 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -195,7 +195,7 @@
margin-top: 2px;
margin-bottom: 0;
font-size: 14px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
padding: 8px 0;
background-color: $white-light;
border: 1px solid $dropdown-border-color;
@@ -268,7 +268,7 @@
}
.dropdown-bold-header {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
line-height: 22px;
padding: 0 16px;
}
@@ -436,7 +436,7 @@
.dropdown-menu-user-full-name {
display: block;
- font-weight: 500;
+ font-weight: $gl-font-weight-normal;
line-height: 16px;
text-overflow: ellipsis;
overflow: hidden;
@@ -472,7 +472,7 @@
&.is-indeterminate,
&.is-active {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
color: $gl-text-color;
&::before {
@@ -506,7 +506,7 @@
position: relative;
padding: 2px 25px 10px;
margin: 0 10px 10px;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
line-height: 1;
text-align: center;
text-overflow: ellipsis;
@@ -689,7 +689,7 @@
.dropdown-menu-inner-title {
display: block;
color: $gl-text-color;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
.dropdown-menu-inner-content {
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 8dcaa879b3f..8ebe3da0681 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -225,6 +225,18 @@
color: $common-gray-dark;
}
+ gl-emoji {
+ display: inline-block;
+ font-family: inherit;
+ font-size: inherit;
+ vertical-align: inherit;
+
+ img {
+ height: 18px;
+ width: 18px;
+ }
+ }
+
.form-control {
position: relative;
min-width: 200px;
@@ -277,7 +289,7 @@
}
.filtered-search-input-dropdown-menu {
- max-height: 225px;
+ max-height: 260px;
max-width: 280px;
overflow: auto;
@@ -371,7 +383,7 @@
}
> .value {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -452,7 +464,7 @@
.dropdown-light-content {
font-size: 14px;
- font-weight: 400;
+ font-weight: $gl-font-weight-normal;
}
.dropdown-user {
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index 38d884bc7eb..e1b086ebb2b 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -25,7 +25,7 @@
a.flash-action {
margin-left: 5px;
text-decoration: none;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
border-bottom: 1px solid;
&:hover {
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 61e3897f369..be96c8ee964 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -32,7 +32,7 @@ label {
}
&.label-light {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -73,7 +73,7 @@ label {
margin-right: 0;
.control-label {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
padding-top: 4px;
}
@@ -157,7 +157,7 @@ label {
.form-group .control-label,
.form-group .control-label-full-width {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
.form-control::-webkit-input-placeholder {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index b677882eba4..35bd97980e2 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -160,7 +160,7 @@ header {
li {
&.active a {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
}
}
@@ -250,7 +250,7 @@ header {
font-size: 18px;
line-height: 22px;
display: inline-block;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
color: $gl-text-color;
vertical-align: top;
white-space: nowrap;
@@ -326,7 +326,7 @@ header {
.badge {
position: inherit;
top: -8px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
margin-left: -11px;
font-size: 11px;
color: $white-light;
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index df2bf561194..0fb19344510 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -113,7 +113,7 @@ ul.content-list {
}
.title {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
a {
@@ -212,7 +212,7 @@ ul.content-list {
}
.row-title {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
.row-second-line {
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 6f91d11b369..d40b65bb2cc 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -43,7 +43,7 @@
background: $gray-light;
a {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index a28f54936be..5b581780447 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -8,7 +8,7 @@
}
.text-danger {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -16,6 +16,14 @@ body.modal-open {
overflow: hidden;
}
+.modal-no-backdrop {
+ @extend .modal-dialog;
+
+ .modal-content {
+ box-shadow: none;
+ }
+}
+
@media (min-width: $screen-md-min) {
.modal-dialog {
width: 860px;
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 071f20fc457..e20108b171b 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -70,7 +70,7 @@
&.active a {
border-bottom: 2px solid $link-underline-blue;
color: $black;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
.badge {
color: $black;
@@ -352,7 +352,7 @@
z-index: 300;
li.active {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
}
}
diff --git a/app/assets/stylesheets/framework/page-header.scss b/app/assets/stylesheets/framework/page-header.scss
index f1ecd050a0a..0c879f40930 100644
--- a/app/assets/stylesheets/framework/page-header.scss
+++ b/app/assets/stylesheets/framework/page-header.scss
@@ -43,7 +43,7 @@
.commit-committer-link,
.commit-author-link {
color: $gl-text-color;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
.commit-info {
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index f7a0b355bf1..9fccc68b5b6 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -76,7 +76,7 @@
}
.select2-results li.select2-result-with-children > .select2-result-label {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
color: $gl-text-color;
}
@@ -227,7 +227,7 @@
}
.group-name {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
.group-path {
@@ -252,12 +252,12 @@
.namespace-result {
.namespace-kind {
color: $namespace-kind-color;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
.namespace-path {
margin-left: 10px;
- font-weight: bolder;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -266,7 +266,9 @@
}
// TODO: change global style
-.ajax-project-dropdown {
+.ajax-project-dropdown,
+body[data-page="projects:blob:new"] #select2-drop,
+body[data-page="projects:blob:edit"] #select2-drop {
&.select2-drop {
color: $gl-text-color;
}
@@ -283,7 +285,7 @@
padding: 0 1px;
.select2-match {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
text-decoration: none;
}
diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss
index 5f7e1b17cc7..30c15c231d5 100644
--- a/app/assets/stylesheets/framework/snippets.scss
+++ b/app/assets/stylesheets/framework/snippets.scss
@@ -30,7 +30,7 @@
.snippet-title {
font-size: 24px;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
.snippet-edited-ago {
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index 6d9fa74a030..4dd31bf28cd 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -32,7 +32,7 @@ table {
th {
background-color: $gray-light;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
border-bottom: none;
&.wide {
diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index e54cc2866a7..d5c6ddbb4a5 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -103,7 +103,7 @@ summary {
padding: 4px 5px;
font-size: 12px;
font-style: normal;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
display: inline-block;
&.label-gray {
@@ -165,7 +165,7 @@ summary {
.panel-heading {
padding: 6px 15px;
font-size: 13px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
a {
color: $panel-heading-link-color;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index d13f9996518..71eec0e1a5e 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -74,7 +74,7 @@
h1 {
font-size: 1.75em;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
margin: 24px 0 16px;
padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark;
@@ -87,7 +87,7 @@
h2 {
font-size: 1.5em;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
margin: 24px 0 16px;
padding-bottom: 0.3em;
border-bottom: 1px solid $white-dark;
@@ -280,7 +280,7 @@ body {
margin-top: $gl-padding;
line-height: 1.3;
font-size: 1.25em;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
&:last-child {
margin-bottom: 0;
@@ -291,7 +291,7 @@ body {
margin-top: 0;
line-height: 1.3;
font-size: 1.25em;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
margin: 12px 7px;
}
@@ -302,11 +302,11 @@ h4,
h5,
h6 {
color: $gl-text-color;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
.light-header {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
/** CODE **/
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 225d116e9c7..26920869bec 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -111,6 +111,8 @@ $well-light-text-color: #5b6169;
* Text
*/
$gl-font-size: 14px;
+$gl-font-weight-normal: 400;
+$gl-font-weight-bold: 600;
$gl-text-color: #2e2e2e;
$gl-text-color-secondary: #707070;
$gl-text-color-tertiary: #949494;
@@ -118,6 +120,7 @@ $gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1.0);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, .85);
$gl-text-green: $green-600;
+$gl-text-green-hover: $green-700;
$gl-text-red: $red-500;
$gl-text-orange: $orange-600;
$gl-link-color: $blue-600;
diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss
index b1ff2659131..5f9756bf58a 100644
--- a/app/assets/stylesheets/framework/wells.scss
+++ b/app/assets/stylesheets/framework/wells.scss
@@ -69,7 +69,7 @@
.well-centered {
h1 {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
text-align: center;
font-size: 48px;
}
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index 6e3829d994f..f0ac9b46f91 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -204,11 +204,11 @@ $dark-il: #de935f;
.cs { color: $dark-cs; } /* Comment.Special */
.gd { color: $dark-gd; } /* Generic.Deleted */
.ge { font-style: italic; } /* Generic.Emph */
- .gh { color: $dark-gh; font-weight: bold; } /* Generic.Heading */
+ .gh { color: $dark-gh; font-weight: $gl-font-weight-bold; } /* Generic.Heading */
.gi { color: $dark-gi; } /* Generic.Inserted */
- .gp { color: $dark-gp; font-weight: bold; } /* Generic.Prompt */
- .gs { font-weight: bold; } /* Generic.Strong */
- .gu { color: $dark-gu; font-weight: bold; } /* Generic.Subheading */
+ .gp { color: $dark-gp; font-weight: $gl-font-weight-bold; } /* Generic.Prompt */
+ .gs { font-weight: $gl-font-weight-bold; } /* Generic.Strong */
+ .gu { color: $dark-gu; font-weight: $gl-font-weight-bold; } /* Generic.Subheading */
.kc { color: $dark-kc; } /* Keyword.Constant */
.kd { color: $dark-kd; } /* Keyword.Declaration */
.kn { color: $dark-kn; } /* Keyword.Namespace */
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index 68eb0c7720f..eba7919ada9 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -203,7 +203,7 @@ $monokai-gi: #a6e22e;
.c1 { color: $monokai-c1; } /* Comment.Single */
.cs { color: $monokai-cs; } /* Comment.Special */
.ge { font-style: italic; } /* Generic.Emph */
- .gs { font-weight: bold; } /* Generic.Strong */
+ .gs { font-weight: $gl-font-weight-bold; } /* Generic.Strong */
.kc { color: $monokai-kc; } /* Keyword.Constant */
.kd { color: $monokai-kd; } /* Keyword.Declaration */
.kn { color: $monokai-kn; } /* Keyword.Namespace */
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index 2cc968c32f2..ba53ef0352b 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -231,7 +231,7 @@ $solarized-dark-il: #2aa198;
.gi { color: $solarized-dark-gi; } /* Generic.Inserted */
.go { color: $solarized-dark-go; } /* Generic.Output */
.gp { color: $solarized-dark-gp; } /* Generic.Prompt */
- .gs { color: $solarized-dark-gs; font-weight: bold; } /* Generic.Strong */
+ .gs { color: $solarized-dark-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */
.gu { color: $solarized-dark-gu; } /* Generic.Subheading */
.gt { color: $solarized-dark-gt; } /* Generic.Traceback */
.kc { color: $solarized-dark-kc; } /* Keyword.Constant */
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index b61b85a2cd1..e9fccf1b58a 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -239,7 +239,7 @@ $solarized-light-il: #2aa198;
.gi { color: $solarized-light-gi; } /* Generic.Inserted */
.go { color: $solarized-light-go; } /* Generic.Output */
.gp { color: $solarized-light-gp; } /* Generic.Prompt */
- .gs { color: $solarized-light-gs; font-weight: bold; } /* Generic.Strong */
+ .gs { color: $solarized-light-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */
.gu { color: $solarized-light-gu; } /* Generic.Subheading */
.gt { color: $solarized-light-gt; } /* Generic.Traceback */
.kc { color: $solarized-light-kc; } /* Keyword.Constant */
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
index 578f1902cce..65b140cd7f8 100644
--- a/app/assets/stylesheets/highlight/white.scss
+++ b/app/assets/stylesheets/highlight/white.scss
@@ -211,12 +211,12 @@ $white-gc-bg: #eaf2f5;
.hll { background-color: $white-hll-bg; }
.c { color: $white-c; font-style: italic; }
.err { color: $white-err; background-color: $white-err-bg; }
- .k { font-weight: bold; }
- .o { font-weight: bold; }
+ .k { font-weight: $gl-font-weight-bold; }
+ .o { font-weight: $gl-font-weight-bold; }
.cm { color: $white-cm; font-style: italic; }
- .cp { color: $white-cp; font-weight: bold; }
+ .cp { color: $white-cp; font-weight: $gl-font-weight-bold; }
.c1 { color: $white-c1; font-style: italic; }
- .cs { color: $white-cs; font-weight: bold; font-style: italic; }
+ .cs { color: $white-cs; font-weight: $gl-font-weight-bold; font-style: italic; }
.gd { color: $white-gd; background-color: $white-gd-bg; }
.gd .x { color: $white-gd-x; background-color: $white-gd-x-bg; }
.ge { font-style: italic; }
@@ -226,29 +226,29 @@ $white-gc-bg: #eaf2f5;
.gi .x { color: $white-gi-x; background-color: $white-gi-x-bg; }
.go { color: $white-go; }
.gp { color: $white-gp; }
- .gs { font-weight: bold; }
- .gu { color: $white-gu; font-weight: bold; }
+ .gs { font-weight: $gl-font-weight-bold; }
+ .gu { color: $white-gu; font-weight: $gl-font-weight-bold; }
.gt { color: $white-gt; }
- .kc { font-weight: bold; }
- .kd { font-weight: bold; }
- .kn { font-weight: bold; }
- .kp { font-weight: bold; }
- .kr { font-weight: bold; }
- .kt { color: $white-kt; font-weight: bold; }
+ .kc { font-weight: $gl-font-weight-bold; }
+ .kd { font-weight: $gl-font-weight-bold; }
+ .kn { font-weight: $gl-font-weight-bold; }
+ .kp { font-weight: $gl-font-weight-bold; }
+ .kr { font-weight: $gl-font-weight-bold; }
+ .kt { color: $white-kt; font-weight: $gl-font-weight-bold; }
.m { color: $white-m; }
.s { color: $white-s; }
.n { color: $white-n; }
.na { color: $white-na; }
.nb { color: $white-nb; }
- .nc { color: $white-nc; font-weight: bold; }
+ .nc { color: $white-nc; font-weight: $gl-font-weight-bold; }
.no { color: $white-no; }
.ni { color: $white-ni; }
- .ne { color: $white-ne; font-weight: bold; }
- .nf { color: $white-nf; font-weight: bold; }
+ .ne { color: $white-ne; font-weight: $gl-font-weight-bold; }
+ .nf { color: $white-nf; font-weight: $gl-font-weight-bold; }
.nn { color: $white-nn; }
.nt { color: $white-nt; }
.nv { color: $white-nv; }
- .ow { font-weight: bold; }
+ .ow { font-weight: $gl-font-weight-bold; }
.w { color: $white-w; }
.mf { color: $white-mf; }
.mh { color: $white-mh; }
diff --git a/app/assets/stylesheets/mailers/highlighted_diff_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
index ea40f449134..fbe538ad1d7 100644
--- a/app/assets/stylesheets/mailers/highlighted_diff_email.scss
+++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
@@ -152,12 +152,12 @@ span.highlight_word {
.hll { background-color: $highlighted-hll-bg; }
.c { color: $highlighted-c; font-style: italic; }
.err { color: $highlighted-err; background-color: $highlighted-err-bg; }
-.k { font-weight: bold; }
-.o { font-weight: bold; }
+.k { font-weight: $gl-font-weight-bold; }
+.o { font-weight: $gl-font-weight-bold; }
.cm { color: $highlighted-cm; font-style: italic; }
-.cp { color: $highlighted-cp; font-weight: bold; }
+.cp { color: $highlighted-cp; font-weight: $gl-font-weight-bold; }
.c1 { color: $highlighted-c1; font-style: italic; }
-.cs { color: $highlighted-cs; font-weight: bold; font-style: italic; }
+.cs { color: $highlighted-cs; font-weight: $gl-font-weight-bold; font-style: italic; }
.gd { color: $highlighted-gd; background-color: $highlighted-gd-bg; }
.gd .x { color: $highlighted-gd; background-color: $highlighted-gd-x-bg; }
.ge { font-style: italic; }
@@ -167,29 +167,29 @@ span.highlight_word {
.gi .x { color: $highlighted-gi; background-color: $highlighted-gi-x-bg; }
.go { color: $highlighted-go; }
.gp { color: $highlighted-gp; }
-.gs { font-weight: bold; }
-.gu { color: $highlighted-gu; font-weight: bold; }
+.gs { font-weight: $gl-font-weight-bold; }
+.gu { color: $highlighted-gu; font-weight: $gl-font-weight-bold; }
.gt { color: $highlighted-gt; }
-.kc { font-weight: bold; }
-.kd { font-weight: bold; }
-.kn { font-weight: bold; }
-.kp { font-weight: bold; }
-.kr { font-weight: bold; }
-.kt { color: $highlighted-kt; font-weight: bold; }
+.kc { font-weight: $gl-font-weight-bold; }
+.kd { font-weight: $gl-font-weight-bold; }
+.kn { font-weight: $gl-font-weight-bold; }
+.kp { font-weight: $gl-font-weight-bold; }
+.kr { font-weight: $gl-font-weight-bold; }
+.kt { color: $highlighted-kt; font-weight: $gl-font-weight-bold; }
.m { color: $highlighted-m; }
.s { color: $highlighted-s; }
.n { color: $highlighted-n; }
.na { color: $highlighted-na; }
.nb { color: $highlighted-nb; }
-.nc { color: $highlighted-nc; font-weight: bold; }
+.nc { color: $highlighted-nc; font-weight: $gl-font-weight-bold; }
.no { color: $highlighted-no; }
.ni { color: $highlighted-ni; }
-.ne { color: $highlighted-ne; font-weight: bold; }
-.nf { color: $highlighted-nf; font-weight: bold; }
+.ne { color: $highlighted-ne; font-weight: $gl-font-weight-bold; }
+.nf { color: $highlighted-nf; font-weight: $gl-font-weight-bold; }
.nn { color: $highlighted-nn; }
.nt { color: $highlighted-nt; }
.nv { color: $highlighted-nv; }
-.ow { font-weight: bold; }
+.ow { font-weight: $gl-font-weight-bold; }
.w { color: $highlighted-w; }
.mf { color: $highlighted-mf; }
.mh { color: $highlighted-mh; }
diff --git a/app/assets/stylesheets/new_nav.scss b/app/assets/stylesheets/new_nav.scss
index 3e2f23e6b2a..54fa4109f8b 100644
--- a/app/assets/stylesheets/new_nav.scss
+++ b/app/assets/stylesheets/new_nav.scss
@@ -134,7 +134,7 @@ header.navbar-gitlab-new {
li {
.badge {
box-shadow: none;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
}
@@ -193,7 +193,7 @@ header.navbar-gitlab-new {
&.active > a {
box-shadow: inset 0 -3px 0 $indigo-500;
color: $white-light;
- font-weight: 700;
+ font-weight: $gl-font-weight-bold;
}
> a {
@@ -371,7 +371,7 @@ header.navbar-gitlab-new {
> a {
&:last-of-type:not(:first-child) {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
}
@@ -411,7 +411,7 @@ header.navbar-gitlab-new {
.breadcrumbs-sub-title {
margin: 2px 0;
font-size: 16px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
line-height: 1;
ul {
@@ -430,7 +430,7 @@ header.navbar-gitlab-new {
}
&:last-child a {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
diff --git a/app/assets/stylesheets/new_sidebar.scss b/app/assets/stylesheets/new_sidebar.scss
index cee5b22adb9..f624b130e19 100644
--- a/app/assets/stylesheets/new_sidebar.scss
+++ b/app/assets/stylesheets/new_sidebar.scss
@@ -46,7 +46,7 @@ $new-sidebar-collapsed-width: 50px;
a {
border-bottom: 1px solid $border-color;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
display: flex;
align-items: center;
padding: 10px 16px 10px 10px;
@@ -70,8 +70,7 @@ $new-sidebar-collapsed-width: 50px;
background-color: $white-light;
}
- .project-title,
- .group-title {
+ .sidebar-context-title {
overflow: hidden;
text-overflow: ellipsis;
}
@@ -109,7 +108,7 @@ $new-sidebar-collapsed-width: 50px;
}
.badge,
- .project-title {
+ .sidebar-context-title {
display: none;
}
@@ -160,7 +159,7 @@ $new-sidebar-collapsed-width: 50px;
> a {
color: $active-color;
- font-weight: 700;
+ font-weight: $gl-font-weight-bold;
}
svg {
@@ -308,7 +307,7 @@ $new-sidebar-collapsed-width: 50px;
.badge {
color: $active-color;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
.sidebar-sub-level-items {
@@ -474,6 +473,6 @@ $new-sidebar-collapsed-width: 50px;
border-bottom-color: $active-border;
.badge {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index e5b467a2691..0f3074076ce 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -471,7 +471,7 @@
padding-right: 35px;
> strong {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
}
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 486424fb729..3d04df8d820 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -277,7 +277,7 @@
}
.trigger-build-variable {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
color: $code-color;
}
@@ -378,7 +378,7 @@
}
&.active {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
.fa-arrow-right {
display: block;
diff --git a/app/assets/stylesheets/pages/ci_projects.scss b/app/assets/stylesheets/pages/ci_projects.scss
index 7b4eb689f1b..bf6a48889bf 100644
--- a/app/assets/stylesheets/pages/ci_projects.scss
+++ b/app/assets/stylesheets/pages/ci_projects.scss
@@ -22,7 +22,7 @@
vertical-align: middle !important;
a {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 46fbfe5f91e..c051d37aad6 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -213,7 +213,7 @@
.commit-sha {
font-size: 14px;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -286,6 +286,9 @@
.gpg-status-box {
+ padding: 2px 10px;
+ margin-right: $gl-padding;
+
&:empty {
display: none;
}
@@ -303,7 +306,7 @@
.gpg-popover-status {
display: flex;
align-items: center;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
line-height: 1.5;
}
@@ -314,7 +317,6 @@
&.valid {
svg {
border: 1px solid $brand-success;
-
fill: $brand-success;
}
}
@@ -322,7 +324,6 @@
&.invalid {
svg {
border: 1px solid $common-gray-light;
-
fill: $common-gray-light;
}
}
diff --git a/app/assets/stylesheets/pages/convdev_index.scss b/app/assets/stylesheets/pages/convdev_index.scss
index 0413114c279..16702442f50 100644
--- a/app/assets/stylesheets/pages/convdev_index.scss
+++ b/app/assets/stylesheets/pages/convdev_index.scss
@@ -23,7 +23,7 @@ $space-between-cards: 8px;
line-height: 1;
color: $gl-text-color-secondary;
margin-left: 8px;
- font-weight: 500;
+ font-weight: $gl-font-weight-normal;
a {
font-size: 18px;
@@ -139,7 +139,7 @@ $space-between-cards: 8px;
.card-score-value {
font-size: 16px;
color: $gl-text-color;
- font-weight: 500;
+ font-weight: $gl-font-weight-normal;
}
.card-score-big {
@@ -147,7 +147,7 @@ $space-between-cards: 8px;
border-bottom: 1px solid $border-color;
font-size: 22px;
padding: 10px 0;
- font-weight: 500;
+ font-weight: $gl-font-weight-normal;
}
.card-buttons {
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index 6753eb08285..2a92673d9fa 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -68,7 +68,7 @@
}
.stage-name {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -93,7 +93,7 @@
.header {
font-size: 30px;
line-height: 38px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
margin: 0;
}
@@ -130,7 +130,7 @@
&.title {
line-height: 19px;
font-size: 14px;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
color: $gl-text-color;
}
@@ -211,7 +211,7 @@
box-shadow: inset 2px 0 0 0 $active-item-blue;
.stage-name {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -404,7 +404,7 @@
color: $gl-link-color;
line-height: 1.3;
vertical-align: top;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
.fa {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 913a1a95dca..8cbf0ec6180 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -40,7 +40,7 @@
// "Changes suppressed. Click to show." link
.show-suppressed-diff {
font-size: 110%;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -104,7 +104,7 @@
a {
float: left;
width: 35px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
&[disabled] {
cursor: default;
@@ -395,7 +395,7 @@
background-color: transparent;
border: 0;
color: $gl-link-color;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
&:hover,
&:focus {
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 00ebf4e26ac..a8d2ae0af28 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -6,7 +6,7 @@
}
.environments-folder-name {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
padding-top: 20px;
}
@@ -246,13 +246,13 @@
}
.text-metric-bold {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
.label-axis-text,
.text-metric-usage {
fill: $black;
- font-weight: 500;
+ font-weight: $gl-font-weight-normal;
font-size: 12px;
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 4c3fa1fb8d4..1723d716805 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -57,7 +57,7 @@
.event-title {
@include str-truncated(calc(100% - 174px));
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
color: $list-text-color;
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 4b8f8783628..4b0b238a767 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -271,7 +271,7 @@
}
.light {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
.no-value {
@@ -306,7 +306,7 @@
display: block;
margin-top: 4px;
font-size: 13px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
.hide-expanded {
@@ -690,7 +690,7 @@
.issuable-info,
.task-status,
.issuable-updated-at {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
color: $gl-text-color-secondary;
a {
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index e25694fd0cf..518bb270b88 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -75,7 +75,7 @@ ul.related-merge-requests > li {
.merge-requests-title,
.related-branches-title {
font-size: 16px;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
.merge-request-id {
@@ -244,7 +244,7 @@ ul.related-merge-requests > li {
strong {
display: block;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 3cbe8dededb..d4dc43035eb 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -22,7 +22,7 @@
}
h1:first-child {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
margin-bottom: 0.68em;
margin-top: 0;
font-size: 34px;
@@ -38,7 +38,7 @@
}
a {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -54,7 +54,7 @@
padding: 15px;
.login-heading h3 {
- font-weight: 300;
+ font-weight: $gl-font-weight-normal;
line-height: 1.5;
margin: 0 0 10px;
}
@@ -186,7 +186,7 @@
}
label {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
.submit-container {
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index e7c07ef67f0..3fb02e9964f 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -46,7 +46,7 @@
}
strong {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -96,6 +96,8 @@
}
.member-search-form {
+ @include new-style-dropdown;
+
position: relative;
@media (min-width: $screen-sm-min) {
@@ -221,7 +223,7 @@
}
.member {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
overflow-wrap: break-word;
word-break: break-all;
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 6bb013cca85..334bec8dd7e 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -197,7 +197,7 @@
@extend .ref-name;
color: $gl-text-color;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
overflow: hidden;
word-break: break-all;
@@ -228,7 +228,7 @@
.mr-widget-body {
h4 {
float: left;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
font-size: 14px;
line-height: inherit;
margin-top: 0;
@@ -239,7 +239,7 @@
}
time {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
}
@@ -249,7 +249,7 @@
}
label {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
.spacing {
@@ -257,12 +257,12 @@
}
.bold {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
color: $gl-gray-light;
}
.state-label {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
padding-right: 10px;
}
@@ -336,7 +336,7 @@
.text {
span {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
p {
@@ -489,6 +489,8 @@
}
.mr-source-target {
+ @include new-style-dropdown;
+
display: flex;
flex-wrap: wrap;
justify-content: space-between;
@@ -505,7 +507,7 @@
.panel-new-merge-request {
.panel-heading {
padding: 5px 10px;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
line-height: 25px;
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 55e0ee1936e..32039936be7 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -7,7 +7,7 @@
padding: 10px 16px;
h4 {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
.progress {
@@ -81,7 +81,7 @@
}
.remaining-days strong {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
.milestone-stat {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 84466f36d9a..8932cff22a8 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -184,7 +184,7 @@
.close {
color: $white-light;
opacity: 0.85;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
&:hover {
opacity: 1;
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 838ca92d905..764984c5772 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -784,17 +784,25 @@ ul.notes {
background-color: transparent;
border: none;
outline: 0;
+ transition: color $general-hover-transition-duration $general-hover-transition-curve;
&.is-disabled {
cursor: default;
}
- &:not(.is-disabled):hover,
+ &:not(.is-disabled) {
+ &:hover,
+ &:focus {
+ color: $gl-text-green;
+ }
+ }
+
&.is-active {
color: $gl-text-green;
- svg {
- fill: $gl-text-green;
+ &:hover,
+ &:focus {
+ color: $gl-text-green-hover;
}
}
diff --git a/app/assets/stylesheets/pages/pipeline_schedules.scss b/app/assets/stylesheets/pages/pipeline_schedules.scss
index dc1654e006e..7e2297c283f 100644
--- a/app/assets/stylesheets/pages/pipeline_schedules.scss
+++ b/app/assets/stylesheets/pages/pipeline_schedules.scss
@@ -12,7 +12,7 @@
.interval-pattern-form-group {
label {
margin-right: 10px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
&[for='custom'] {
margin-right: 0;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 85d1905ad40..a408bde37d6 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -128,7 +128,7 @@
.branch-commit {
.ref-name {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
max-width: 120px;
overflow: hidden;
display: inline-block;
@@ -272,7 +272,7 @@
.build-name {
float: right;
- font-weight: 500;
+ font-weight: $gl-font-weight-normal;
}
.ci-status-icon-failed svg {
@@ -281,7 +281,7 @@
.stage {
color: $gl-text-color-secondary;
- font-weight: 500;
+ font-weight: $gl-font-weight-normal;
vertical-align: middle;
}
}
@@ -420,7 +420,7 @@
.stage-name {
margin: 0 0 15px 10px;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
width: 176px;
white-space: nowrap;
overflow: hidden;
@@ -580,7 +580,7 @@
vertical-align: bottom;
display: inline-block;
position: relative;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
@mixin mini-pipeline-graph-color($color-light, $color-main, $color-dark) {
@@ -724,7 +724,7 @@ button.mini-pipeline-graph-dropdown-toggle {
.mini-pipeline-graph-dropdown-item {
padding: 3px 7px 4px;
clear: both;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
line-height: 1.428571429;
white-space: nowrap;
margin: 0 5px;
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 14ad06b0ac2..c5d6ff66dd6 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -83,7 +83,7 @@
&::after {
content: "\00B7"; // Middle Dot
padding: 0 6px;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
&:last-child {
@@ -277,7 +277,7 @@ table.u2f-registrations {
.oauth-application-show {
.scope-name {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
.scopes-list {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index d01326637ea..39c4264e496 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -2,7 +2,7 @@
margin: -16px;
.alert-link {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
}
@@ -114,7 +114,7 @@
margin-top: 10px;
margin-bottom: 10px;
font-size: 24px;
- font-weight: 400;
+ font-weight: $gl-font-weight-normal;
line-height: 1;
word-wrap: break-word;
@@ -259,7 +259,7 @@
border-width: 1px;
border-style: solid;
font-size: 13px;
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
line-height: 13px;
letter-spacing: .4px;
padding: 6px 14px;
@@ -309,7 +309,7 @@
}
.option-title {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
display: inline-block;
color: $gl-text-color;
}
@@ -575,7 +575,7 @@ a.deploy-project-label {
color: $gl-text-color-tertiary;
transform: translateY(-50%);
font-size: 12px;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
line-height: 20px;
// Mobile
@@ -826,7 +826,7 @@ pre.light-well {
.new-protected-tag {
label {
margin-top: 6px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
}
@@ -853,7 +853,7 @@ pre.light-well {
}
&.is-active {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
@@ -952,7 +952,7 @@ pre.light-well {
&::before {
font-family: FontAwesome;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
font-style: normal;
}
}
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 1f4d4698199..efc47861768 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -99,6 +99,30 @@
.blob-viewer-container {
flex: 1;
overflow: auto;
+
+ > div,
+ .file-content {
+ display: flex;
+ }
+
+ > div,
+ .file-content,
+ .blob-viewer,
+ .line-number,
+ .blob-content,
+ .code {
+ min-height: 100%;
+ width: 100%;
+ }
+
+ .line-numbers {
+ min-width: 44px;
+ }
+
+ .blob-content {
+ flex: 1;
+ overflow-x: auto;
+ }
}
#tabs {
@@ -182,7 +206,6 @@
padding: 5px 10px;
position: relative;
border-top: 1px solid $white-normal;
- margin-top: -5px;
}
#binary-viewer {
@@ -267,7 +290,7 @@
display: inline-block;
font-size: 10px;
text-transform: uppercase;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
color: $gray-darkest;
white-space: nowrap;
overflow: hidden;
diff --git a/app/assets/stylesheets/pages/runners.scss b/app/assets/stylesheets/pages/runners.scss
index 57c73295d1e..6cac37a4e28 100644
--- a/app/assets/stylesheets/pages/runners.scss
+++ b/app/assets/stylesheets/pages/runners.scss
@@ -30,7 +30,7 @@
}
h4 {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index b9818ffcf42..8d73246223d 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -94,7 +94,7 @@ input[type="checkbox"]:hover {
&::before {
font-family: FontAwesome;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
font-style: normal;
}
}
diff --git a/app/assets/stylesheets/pages/sherlock.scss b/app/assets/stylesheets/pages/sherlock.scss
index 23a9c2ada80..bfe065dbbaf 100644
--- a/app/assets/stylesheets/pages/sherlock.scss
+++ b/app/assets/stylesheets/pages/sherlock.scss
@@ -29,5 +29,5 @@ table .sherlock-code {
.sherlock-line-samples-table .slow {
color: $red-500;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index d7a9dda3770..6c8d87185e9 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -108,14 +108,14 @@
margin: 0;
float: none;
display: inline-block;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
padding: 0 5px;
line-height: inherit;
font-size: 14px;
}
.action-name {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
}
.todo-body {
@@ -262,6 +262,10 @@
}
a {
- font-weight: 600;
+ font-weight: $gl-font-weight-bold;
}
}
+
+.todos-filters {
+ @include new-style-dropdown;
+}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 0028e207f3e..224eee90a3f 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -231,7 +231,7 @@
}
.upload-link {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
color: $md-link-color;
}
diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss
index 798e060a261..48ac5b21db8 100644
--- a/app/assets/stylesheets/pages/ui_dev_kit.scss
+++ b/app/assets/stylesheets/pages/ui_dev_kit.scss
@@ -1,7 +1,7 @@
.gitlab-ui-dev-kit {
> h2 {
margin: 35px 0 20px;
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
.example {
diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss
index fa6bdd297eb..b7d4e7bf582 100644
--- a/app/assets/stylesheets/pages/wiki.scss
+++ b/app/assets/stylesheets/pages/wiki.scss
@@ -37,7 +37,7 @@
}
.light {
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
color: $gl-text-color-secondary;
}
@@ -89,7 +89,7 @@
h3 {
font-size: 19px;
- font-weight: normal;
+ font-weight: $gl-font-weight-normal;
margin: $gl-padding 0;
}
}
diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
index b085c56390d..c7297a34ad8 100644
--- a/app/assets/stylesheets/pages/xterm.scss
+++ b/app/assets/stylesheets/pages/xterm.scss
@@ -281,7 +281,7 @@
$xterm-fg-255: #eee;
.term-bold {
- font-weight: bold;
+ font-weight: $gl-font-weight-bold;
}
.term-italic {
diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss
index 113e6e86bb5..b07a5ae22cd 100644
--- a/app/assets/stylesheets/print.scss
+++ b/app/assets/stylesheets/print.scss
@@ -17,7 +17,7 @@
.wiki h3 {
font-size: 18px;
- font-weight: bold;
+ font-weight: 600;
}
header,
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 0b6cd71e651..50cf2643390 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -3,9 +3,9 @@ class Admin::ProjectsController < Admin::ApplicationController
before_action :group, only: [:show, :transfer]
def index
- finder = Admin::ProjectsFinder.new(params: params, current_user: current_user)
- @projects = finder.execute
- @sort = finder.sort
+ params[:sort] ||= 'latest_activity_desc'
+ @sort = params[:sort]
+ @projects = Admin::ProjectsFinder.new(params: params, current_user: current_user).execute
respond_to do |format|
format.html
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index 3120916c5bb..54f78fc8719 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -1,5 +1,7 @@
class AutocompleteController < ApplicationController
- skip_before_action :authenticate_user!, only: [:users]
+ AWARD_EMOJI_MAX = 100
+
+ skip_before_action :authenticate_user!, only: [:users, :award_emojis]
before_action :load_project, only: [:users]
before_action :find_users, only: [:users]
@@ -48,6 +50,20 @@ class AutocompleteController < ApplicationController
render json: projects.to_json(only: [:id, :name_with_namespace], methods: :name_with_namespace)
end
+ def award_emojis
+ emoji_with_count = AwardEmoji
+ .limit(AWARD_EMOJI_MAX)
+ .where(user: current_user)
+ .group(:name)
+ .order(count: :desc, name: :asc)
+ .count
+
+ # Transform from hash to array to guarantee json order
+ # e.g. { 'thumbsup' => 2, 'thumbsdown' = 1 }
+ # => [{ name: 'thumbsup' }, { name: 'thumbsdown' }]
+ render json: emoji_with_count.map { |k, v| { name: k } }
+ end
+
private
def find_users
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index b43b2c5621f..a34a82b7ba6 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -15,7 +15,17 @@ module IssuableCollections
end
def merge_requests_collection
- merge_requests_finder.execute.preload(:source_project, :target_project, :author, :assignee, :labels, :milestone, :head_pipeline, target_project: :namespace, merge_request_diff: :merge_request_diff_commits)
+ merge_requests_finder.execute.preload(
+ :source_project,
+ :target_project,
+ :author,
+ :assignee,
+ :labels,
+ :milestone,
+ head_pipeline: :project,
+ target_project: :namespace,
+ merge_request_diff: :merge_request_diff_commits
+ )
end
def issues_finder
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 5c10d7bc261..7a7bcb1a3d2 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -35,13 +35,13 @@ class Groups::MilestonesController < Groups::ApplicationController
end
def edit
- render_404 if @milestone.is_legacy_group_milestone?
+ render_404 if @milestone.legacy_group_milestone?
end
def update
# Keep this compatible with legacy group milestones where we have to update
# all projects milestones states at once.
- if @milestone.is_legacy_group_milestone?
+ if @milestone.legacy_group_milestone?
update_params = milestone_params.select { |key| key == "state_event" }
milestones = @milestone.milestones
else
@@ -67,7 +67,7 @@ class Groups::MilestonesController < Groups::ApplicationController
end
def milestone_path
- if @milestone.is_legacy_group_milestone?
+ if @milestone.legacy_group_milestone?
group_milestone_path(group, @milestone.safe_title, title: @milestone.title)
else
group_milestone_path(group, @milestone.iid)
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 7444826a5d1..9612b8d8514 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -10,6 +10,14 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
end
+ if Gitlab::LDAP::Config.enabled?
+ Gitlab::LDAP::Config.available_servers.each do |server|
+ define_method server['provider_name'] do
+ ldap
+ end
+ end
+ end
+
# Extend the standard message generation to accept our custom exception
def failure_message
exception = env["omniauth.error"]
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 76bb2b7f811..349b19f72e2 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -216,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController
task_status: @issue.task_status
}
- if @issue.is_edited?
+ if @issue.edited?
response[:updated_at] = @issue.updated_at
response[:updated_by_name] = @issue.last_edited_by.name
response[:updated_by_path] = user_path(@issue.last_edited_by)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 2a3b73577a5..e3fa3736808 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -318,14 +318,14 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
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)
+ @merge_request.merge_async(current_user.id, params)
:success
else
:failed
end
else
- MergeWorker.perform_async(@merge_request.id, current_user.id, params)
+ @merge_request.merge_async(current_user.id, params)
:success
end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 9e743685d60..be6491d042c 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -5,14 +5,6 @@ class SessionsController < Devise::SessionsController
skip_before_action :check_two_factor_requirement, only: [:destroy]
- # Explicitly call protect from forgery before anything else. Otherwise the
- # CSFR-token might be cleared before authentication is done. This was the case
- # when LDAP was enabled and the `OmniauthCallbacksController` is loaded
- #
- # *Note:* `prepend: true` is the default for rails4, but this will be changed
- # to `prepend: false` in rails5.
- protect_from_forgery prepend: true, with: :exception
-
prepend_before_action :check_initial_setup, only: [:new]
prepend_before_action :authenticate_with_two_factor,
if: :two_factor_enabled?, only: [:create]
diff --git a/app/finders/admin/projects_finder.rb b/app/finders/admin/projects_finder.rb
index 7176bfe22d6..d6bcd939522 100644
--- a/app/finders/admin/projects_finder.rb
+++ b/app/finders/admin/projects_finder.rb
@@ -1,33 +1,67 @@
class Admin::ProjectsFinder
- attr_reader :sort, :namespace_id, :visibility_level, :with_push,
- :abandoned, :last_repository_check_failed, :archived,
- :personal, :name, :page, :current_user
+ attr_reader :params, :current_user
def initialize(params:, current_user:)
+ @params = params
@current_user = current_user
- @sort = params.fetch(:sort) { 'latest_activity_desc' }
- @namespace_id = params[:namespace_id]
- @visibility_level = params[:visibility_level]
- @with_push = params[:with_push]
- @abandoned = params[:abandoned]
- @last_repository_check_failed = params[:last_repository_check_failed]
- @archived = params[:archived]
- @personal = params[:personal]
- @name = params[:name]
- @page = params[:page]
end
def execute
items = Project.without_deleted.with_statistics
- items = items.in_namespace(namespace_id) if namespace_id.present?
- items = items.where(visibility_level: visibility_level) if visibility_level.present?
- items = items.with_push if with_push.present?
- items = items.abandoned if abandoned.present?
- items = items.where(last_repository_check_failed: true) if last_repository_check_failed.present?
- items = items.non_archived unless archived.present?
- items = items.personal(current_user) if personal.present?
- items = items.search(name) if name.present?
- items = items.sort(sort)
- items.includes(:namespace).order("namespaces.path, projects.name ASC").page(page)
+ items = by_namespace_id(items)
+ items = by_visibilty_level(items)
+ items = by_with_push(items)
+ items = by_abandoned(items)
+ items = by_last_repository_check_failed(items)
+ items = by_archived(items)
+ items = by_personal(items)
+ items = by_name(items)
+ items = sort(items)
+ items.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page])
+ end
+
+ private
+
+ def by_namespace_id(items)
+ params[:namespace_id].present? ? items.in_namespace(params[:namespace_id]) : items
+ end
+
+ def by_visibilty_level(items)
+ params[:visibility_level].present? ? items.where(visibility_level: params[:visibility_level]) : items
+ end
+
+ def by_with_push(items)
+ params[:with_push].present? ? items.with_push : items
+ end
+
+ def by_abandoned(items)
+ params[:abandoned].present? ? items.abandoned : items
+ end
+
+ def by_last_repository_check_failed(items)
+ params[:last_repository_check_failed].present? ? items.where(last_repository_check_failed: true) : items
+ end
+
+ def by_archived(items)
+ if params[:archived] == 'only'
+ items.archived
+ elsif params[:archived].blank?
+ items.non_archived
+ else
+ items
+ end
+ end
+
+ def by_personal(items)
+ params[:personal].present? ? items.personal(current_user) : items
+ end
+
+ def by_name(items)
+ params[:name].present? ? items.search(params[:name]) : items
+ end
+
+ def sort(items)
+ sort = params.fetch(:sort) { 'latest_activity_desc' }
+ items.sort(sort)
end
end
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index e6fb112e7f2..88d71b0a87b 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -1,3 +1,19 @@
+# GroupsFinder
+#
+# Used to filter Groups by a set of params
+#
+# Arguments:
+# current_user - which user is requesting groups
+# params:
+# owned: boolean
+# parent: Group
+# all_available: boolean (defaults to true)
+#
+# Users with full private access can see all groups. The `owned` and `parent`
+# params can be used to restrict the groups that are returned.
+#
+# Anonymous users will never return any `owned` groups. They will return all
+# public groups instead, even if `all_available` is set to false.
class GroupsFinder < UnionFinder
def initialize(current_user = nil, params = {})
@current_user = current_user
@@ -16,13 +32,13 @@ class GroupsFinder < UnionFinder
attr_reader :current_user, :params
def all_groups
- groups = []
-
- if current_user
- groups << Gitlab::GroupHierarchy.new(groups_for_ancestors, groups_for_descendants).all_groups
- end
- groups << Group.unscoped.public_to_user(current_user)
+ return [owned_groups] if params[:owned]
+ return [Group.all] if current_user&.full_private_access?
+ groups = []
+ groups << Gitlab::GroupHierarchy.new(groups_for_ancestors, groups_for_descendants).all_groups if current_user
+ groups << Group.unscoped.public_to_user(current_user) if include_public_groups?
+ groups << Group.none if groups.empty?
groups
end
@@ -39,4 +55,12 @@ class GroupsFinder < UnionFinder
groups.where(parent: params[:parent])
end
+
+ def owned_groups
+ current_user&.groups || Group.none
+ end
+
+ def include_public_groups?
+ current_user.nil? || params.fetch(:all_available, true)
+ end
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 08a843ada97..7e0d3b5c979 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -18,6 +18,7 @@
# sort: string
# non_archived: boolean
# iids: integer[]
+# my_reaction_emoji: string
#
class IssuableFinder
include CreatedAtFilter
@@ -46,6 +47,7 @@ class IssuableFinder
items = by_iids(items)
items = by_milestone(items)
items = by_label(items)
+ items = by_my_reaction_emoji(items)
# Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far
items = by_project(items)
@@ -371,6 +373,14 @@ class IssuableFinder
items
end
+ def by_my_reaction_emoji(items)
+ if params[:my_reaction_emoji].present? && current_user
+ items = items.awarded(current_user, params[:my_reaction_emoji])
+ end
+
+ items
+ end
+
def by_due_date(items)
if due_date?
if filter_by_no_due_date?
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index aa80dfc3f37..fa6fea2588a 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -125,9 +125,18 @@ class ProjectsFinder < UnionFinder
end
def by_archived(projects)
- # Back-compatibility with the places where `params[:archived]` can be set explicitly to `false`
- params[:non_archived] = !Gitlab::Utils.to_boolean(params[:archived]) if params.key?(:archived)
-
- params[:non_archived] ? projects.non_archived : projects
+ if params[:non_archived]
+ projects.non_archived
+ elsif params.key?(:archived) # Back-compatibility with the places where `params[:archived]` can be set explicitly to `false`
+ if params[:archived] == 'only'
+ projects.archived
+ elsif Gitlab::Utils.to_boolean(params[:archived])
+ projects
+ else
+ projects.non_archived
+ end
+ else
+ projects
+ end
end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index bcee81bdc15..07775a8b159 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -178,7 +178,7 @@ module ApplicationHelper
end
def edited_time_ago_with_tooltip(object, placement: 'top', html_class: 'time_ago', exclude_author: false)
- return unless object.is_edited?
+ return unless object.edited?
content_tag :small, class: 'edited-text' do
output = content_tag(:span, 'Edited ')
diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb
index 4b51269533c..a4c226a6aad 100644
--- a/app/helpers/avatars_helper.rb
+++ b/app/helpers/avatars_helper.rb
@@ -12,11 +12,18 @@ module AvatarsHelper
avatar_size = options[:size] || 16
user_name = options[:user].try(:name) || options[:user_name]
avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size)
- data_attributes = { container: 'body' }
+ has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip]
+ data_attributes = {}
+ css_class = %W[avatar s#{avatar_size}].push(*options[:css_class])
+
+ if has_tooltip
+ css_class.push('has-tooltip')
+ data_attributes = { container: 'body' }
+ end
image_tag(
avatar_url,
- class: %W[avatar has-tooltip s#{avatar_size}].push(*options[:css_class]),
+ class: css_class,
alt: "#{user_name}'s avatar",
title: user_name,
data: data_attributes,
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index bf9ad95b7c2..48cf30a48ab 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -20,6 +20,9 @@ module ButtonHelper
def clipboard_button(data = {})
css_class = data[:class] || 'btn-clipboard btn-transparent'
title = data[:title] || 'Copy to clipboard'
+ button_text = data[:button_text] || ''
+ hide_tooltip = data[:hide_tooltip] || false
+ hide_button_icon = data[:hide_button_icon] || false
# This supports code in app/assets/javascripts/copy_to_clipboard.js that
# works around ClipboardJS limitations to allow the context-specific copy/pasting of plain text or GFM.
@@ -35,17 +38,22 @@ module ButtonHelper
target = data.delete(:target)
data[:clipboard_target] = target if target
- data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
+ unless hide_tooltip
+ data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
+ end
- content_tag :button,
- icon('clipboard', 'aria-hidden': 'true'),
+ button_attributes = {
class: "btn #{css_class}",
data: data,
type: :button,
title: title,
- aria: {
- label: title
- }
+ aria: { label: title }
+ }
+
+ content_tag :button, button_attributes do
+ concat(icon('clipboard', 'aria-hidden': 'true')) unless hide_button_icon
+ concat(button_text)
+ end
end
def http_clone_button(project, placement = 'right', append_link: true)
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 72e26b64e60..9651f9733f9 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -114,7 +114,7 @@ module CommitsHelper
end
def commit_signature_badge_classes(additional_classes)
- %w(btn status-box gpg-status-box) + Array(additional_classes)
+ %w(btn gpg-status-box) + Array(additional_classes)
end
protected
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index c6f98e7e782..b331693c789 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -181,6 +181,7 @@ module EventsHelper
end
def event_commit_title(message)
+ message ||= ''
(message.split("\n").first || "").truncate(70)
rescue
"--broken encoding"
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index d3b6a9ff548..b95d9d38533 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -229,7 +229,7 @@ module IssuablesHelper
end
def updated_at_by(issuable)
- return {} unless issuable.is_edited?
+ return {} unless issuable.edited?
{
updatedAt: issuable.updated_at.to_time.iso8601,
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 86666022a2a..446a59030a6 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -164,7 +164,7 @@ module MilestonesHelper
def group_milestone_route(milestone, params = {})
params = nil if params.empty?
- if milestone.is_legacy_group_milestone?
+ if milestone.legacy_group_milestone?
group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: params)
else
group_milestone_path(@group, milestone.iid, milestone: params)
diff --git a/app/helpers/milestones_routing_helper.rb b/app/helpers/milestones_routing_helper.rb
index 766d5262018..a0b2616f224 100644
--- a/app/helpers/milestones_routing_helper.rb
+++ b/app/helpers/milestones_routing_helper.rb
@@ -1,16 +1,16 @@
module MilestonesRoutingHelper
def milestone_path(milestone, *args)
- if milestone.is_group_milestone?
+ if milestone.group_milestone?
group_milestone_path(milestone.group, milestone, *args)
- elsif milestone.is_project_milestone?
+ elsif milestone.project_milestone?
project_milestone_path(milestone.project, milestone, *args)
end
end
def milestone_url(milestone, *args)
- if milestone.is_group_milestone?
+ if milestone.group_milestone?
group_milestone_url(milestone.group, milestone, *args)
- elsif milestone.is_project_milestone?
+ elsif milestone.project_milestone?
project_milestone_url(milestone.project, milestone, *args)
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 4692fb5644a..095192e9894 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -46,7 +46,10 @@ module Ci
before_save :ensure_token
before_destroy { unscoped_project }
- after_create :execute_hooks
+ after_create do |build|
+ run_after_commit { BuildHooksWorker.perform_async(build.id) }
+ end
+
after_commit :update_project_statistics_after_save, on: [:create, :update]
after_commit :update_project_statistics, on: :destroy
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index ea7331cb27f..2d40f8012a3 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -393,7 +393,8 @@ module Ci
def predefined_variables
[
{ key: 'CI_PIPELINE_ID', value: id.to_s, public: true },
- { key: 'CI_CONFIG_PATH', value: ci_yaml_file_path, public: true }
+ { key: 'CI_CONFIG_PATH', value: ci_yaml_file_path, public: true },
+ { key: 'CI_PIPELINE_SOURCE', value: source.to_s, public: true }
]
end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index c6d23898560..906a76ec560 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -142,7 +142,7 @@ module Ci
expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: false)
end
- def is_runner_queue_value_latest?(value)
+ def runner_queue_value_latest?(value)
ensure_runner_queue_value == value if value.present?
end
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 4ee972fa68d..754c37518b3 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -17,6 +17,10 @@ module Ci
validates :pipeline, presence: true, unless: :importing?
validates :name, presence: true, unless: :importing?
+ after_initialize do |stage|
+ self.status = DEFAULT_STATUS if self.status.nil?
+ end
+
state_machine :status, initial: :created do
event :enqueue do
transition created: :pending
diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb
index f4f9b037957..9adc309a22b 100644
--- a/app/models/concerns/awardable.rb
+++ b/app/models/concerns/awardable.rb
@@ -11,6 +11,21 @@ module Awardable
end
module ClassMethods
+ def awarded(user, name)
+ sql = <<~EOL
+ EXISTS (
+ SELECT TRUE
+ FROM award_emoji
+ WHERE user_id = :user_id AND
+ name = :name AND
+ awardable_type = :awardable_type AND
+ awardable_id = #{self.arel_table.name}.id
+ )
+ EOL
+
+ where(sql, user_id: user.id, name: name, awardable_type: self.name)
+ end
+
def order_upvotes_desc
order_votes_desc(AwardEmoji::UPVOTE_NAME)
end
diff --git a/app/models/concerns/editable.rb b/app/models/concerns/editable.rb
index 28623d257a6..c0a3099f676 100644
--- a/app/models/concerns/editable.rb
+++ b/app/models/concerns/editable.rb
@@ -1,7 +1,7 @@
module Editable
extend ActiveSupport::Concern
- def is_edited?
+ def edited?
last_edited_at.present? && last_edited_at != created_at
end
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index f0998465822..710fc1ed647 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -70,19 +70,19 @@ module Milestoneish
due_date && due_date.past?
end
- def is_group_milestone?
+ def group_milestone?
false
end
- def is_project_milestone?
+ def project_milestone?
false
end
- def is_legacy_group_milestone?
+ def legacy_group_milestone?
false
end
- def is_dashboard_milestone?
+ def dashboard_milestone?
false
end
diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb
index ef95d6b0f98..454374121f3 100644
--- a/app/models/concerns/protected_ref.rb
+++ b/app/models/concerns/protected_ref.rb
@@ -23,7 +23,7 @@ module ProtectedRef
# If we don't `protected_branch` or `protected_tag` would be empty and
# `project` cannot be delegated to it, which in turn would cause validations
# to fail.
- has_many :"#{type}_access_levels", dependent: :destroy, inverse_of: self.model_name.singular # rubocop:disable Cop/ActiveRecordDependent
+ has_many :"#{type}_access_levels", inverse_of: self.model_name.singular # rubocop:disable Cop/ActiveRecordDependent
validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." }
diff --git a/app/models/dashboard_milestone.rb b/app/models/dashboard_milestone.rb
index fac7c5e5c85..86eb4ec76fc 100644
--- a/app/models/dashboard_milestone.rb
+++ b/app/models/dashboard_milestone.rb
@@ -3,7 +3,7 @@ class DashboardMilestone < GlobalMilestone
{ authorized_only: true }
end
- def is_dashboard_milestone?
+ def dashboard_milestone?
true
end
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 056c49e7162..7bcded5b5e1 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -49,7 +49,7 @@ class Deployment < ActiveRecord::Base
# created before then could have a `sha` referring to a commit that no
# longer exists in the repository, so just ignore those.
begin
- project.repository.is_ancestor?(commit.id, sha)
+ project.repository.ancestor?(commit.id, sha)
rescue Rugged::OdbError
false
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 15ee170ca75..996768a267b 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -406,7 +406,7 @@ class Event < ActiveRecord::Base
def body?
if push?
- push_with_commits? || rm_ref?
+ push_with_commits?
elsif note?
true
else
diff --git a/app/models/group.rb b/app/models/group.rb
index 2816a68257c..cb3ee032f69 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -206,9 +206,9 @@ class Group < Namespace
SystemHooksService.new
end
- def refresh_members_authorized_projects
+ def refresh_members_authorized_projects(blocking: true)
UserProjectAccessChangedService.new(user_ids_for_project_authorizations)
- .execute
+ .execute(blocking: blocking)
end
def user_ids_for_project_authorizations
diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb
index 65249bd7bfc..98135ee3c8b 100644
--- a/app/models/group_milestone.rb
+++ b/app/models/group_milestone.rb
@@ -17,7 +17,7 @@ class GroupMilestone < GlobalMilestone
{ group_id: group.id }
end
- def is_legacy_group_milestone?
+ def legacy_group_milestone?
true
end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 4cd30dfeb94..5820e57e531 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -50,7 +50,10 @@ class Issue < ActiveRecord::Base
scope :preload_associations, -> { preload(:labels, project: :namespace) }
+ scope :public_only, -> { where(confidential: false) }
+
after_save :expire_etag_cache
+ after_commit :update_project_counter_caches, on: :destroy
attr_spammable :title, spam_title: true
attr_spammable :description, spam_description: true
@@ -270,6 +273,10 @@ class Issue < ActiveRecord::Base
true
end
+ def update_project_counter_caches
+ Projects::OpenIssuesCountService.new(project).refresh_cache
+ end
+
private
# Returns `true` if the given User can read the current Issue.
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index f028d2395c1..7f73de67625 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -31,6 +31,7 @@ class MergeRequest < ActiveRecord::Base
after_create :ensure_merge_request_diff, unless: :importing?
after_update :reload_diff_if_branch_changed
+ after_commit :update_project_counter_caches, on: :destroy
# When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests
@@ -240,6 +241,14 @@ class MergeRequest < ActiveRecord::Base
end
end
+ # Calls `MergeWorker` to proceed with the merge process and
+ # updates `merge_jid` with the MergeWorker#jid.
+ # This helps tracking enqueued and ongoing merge jobs.
+ def merge_async(user_id, params)
+ jid = MergeWorker.perform_async(id, user_id, params)
+ update_column(:merge_jid, jid)
+ end
+
def first_commit
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end
@@ -383,9 +392,7 @@ class MergeRequest < ActiveRecord::Base
end
def merge_ongoing?
- return false unless merge_jid
-
- Gitlab::SidekiqStatus.num_running([merge_jid]) > 0
+ !!merge_jid && !merged?
end
def closed_without_fork?
@@ -682,9 +689,8 @@ class MergeRequest < ActiveRecord::Base
if !include_description && closes_issues_references.present?
message << "Closes #{closes_issues_references.to_sentence}"
end
-
message << "#{description}" if include_description && description.present?
- message << "See merge request #{to_reference}"
+ message << "See merge request #{to_reference(full: true)}"
message.join("\n\n")
end
@@ -819,7 +825,7 @@ class MergeRequest < ActiveRecord::Base
lock_mr
yield
ensure
- unlock_mr if locked?
+ unlock_mr
end
end
@@ -936,6 +942,10 @@ class MergeRequest < ActiveRecord::Base
true
end
+ def update_project_counter_caches
+ Projects::OpenMergeRequestsCountService.new(target_project).refresh_cache
+ end
+
private
def write_ref
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 01e0d0155a3..a3070a12b7c 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -163,7 +163,7 @@ class Milestone < ActiveRecord::Base
# Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1"
#
def to_reference(from_project = nil, format: :iid, full: false)
- return if is_group_milestone? && format != :name
+ return if group_milestone? && format != :name
format_reference = milestone_format_reference(format)
reference = "#{self.class.reference_prefix}#{format_reference}"
@@ -207,11 +207,11 @@ class Milestone < ActiveRecord::Base
group || project
end
- def is_group_milestone?
+ def group_milestone?
group_id.present?
end
- def is_project_milestone?
+ def project_milestone?
project_id.present?
end
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index 0e5acb22d50..3845e485413 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -152,14 +152,14 @@ module Network
end
def find_free_parent_space(range, space_base, space_step, space_default)
- if is_overlap?(range, space_default)
+ if overlap?(range, space_default)
find_free_space(range, space_step, space_base, space_default)
else
space_default
end
end
- def is_overlap?(range, overlap_space)
+ def overlap?(range, overlap_space)
range.each do |i|
if i != range.first &&
i != range.last &&
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index dc862565a71..183e098d819 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -27,46 +27,45 @@ class NotificationRecipient
@notification_setting ||= find_notification_setting
end
- def raw_notification_level
- notification_setting&.level&.to_sym
- end
-
def notification_level
- # custom is treated the same as watch if it's enabled - otherwise it's
- # set to :custom, meaning to send exactly when our type is :participating
- # or :mention.
- @notification_level ||=
- case raw_notification_level
- when :custom
- if @custom_action && notification_setting&.event_enabled?(@custom_action)
- :watch
- else
- :custom
- end
- else
- raw_notification_level
- end
+ @notification_level ||= notification_setting&.level&.to_sym
end
def notifiable?
return false unless has_access?
return false if own_activity?
- return true if @type == :subscription
-
- return false if notification_level.nil? || notification_level == :disabled
-
- return %i[participating mention].include?(@type) if notification_level == :custom
+ # even users with :disabled notifications receive manual subscriptions
+ return !unsubscribed? if @type == :subscription
- return false if %i[watch participating].include?(notification_level) && excluded_watcher_action?
-
- return false unless NotificationSetting.levels[notification_level] <= NotificationSetting.levels[@type]
+ return false unless suitable_notification_level?
+ # check this last because it's expensive
+ # nobody should receive notifications if they've specifically unsubscribed
return false if unsubscribed?
true
end
+ def suitable_notification_level?
+ case notification_level
+ when :disabled, nil
+ false
+ when :custom
+ custom_enabled? || %i[participating mention].include?(@type)
+ when :watch, :participating
+ !excluded_watcher_action?
+ when :mention
+ @type == :mention
+ else
+ false
+ end
+ end
+
+ def custom_enabled?
+ @custom_action && notification_setting&.event_enabled?(@custom_action)
+ end
+
def unsubscribed?
return false unless @target
return false unless @target.respond_to?(:subscriptions)
@@ -98,7 +97,7 @@ class NotificationRecipient
def excluded_watcher_action?
return false unless @custom_action
- return false if raw_notification_level == :custom
+ return false if notification_level == :custom
NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(@custom_action)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 37f4dd08355..d5324ceac31 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -247,6 +247,7 @@ class Project < ActiveRecord::Base
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
+ scope :archived, -> { where(archived: true) }
scope :non_archived, -> { where(archived: false) }
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) }
@@ -1017,7 +1018,7 @@ class Project < ActiveRecord::Base
name: name,
description: description,
web_url: web_url,
- avatar_url: avatar_url,
+ avatar_url: avatar_url(only_path: false),
git_ssh_url: ssh_url_to_repo,
git_http_url: http_url_to_repo,
namespace: namespace.name,
@@ -1167,7 +1168,11 @@ class Project < ActiveRecord::Base
end
def open_issues_count
- issues.opened.count
+ Projects::OpenIssuesCountService.new(self).count
+ end
+
+ def open_merge_requests_count
+ Projects::OpenMergeRequestsCountService.new(self).count
end
def visibility_level_allowed_as_fork?(level = self.visibility_level)
diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb
index 7b15a5dd04d..818cfb01b14 100644
--- a/app/models/project_services/chat_notification_service.rb
+++ b/app/models/project_services/chat_notification_service.rb
@@ -101,9 +101,9 @@ class ChatNotificationService < Service
when "push", "tag_push"
ChatMessage::PushMessage.new(data)
when "issue"
- ChatMessage::IssueMessage.new(data) unless is_update?(data)
+ ChatMessage::IssueMessage.new(data) unless update?(data)
when "merge_request"
- ChatMessage::MergeMessage.new(data) unless is_update?(data)
+ ChatMessage::MergeMessage.new(data) unless update?(data)
when "note"
ChatMessage::NoteMessage.new(data)
when "pipeline"
@@ -136,7 +136,7 @@ class ChatNotificationService < Service
project.web_url
end
- def is_update?(data)
+ def update?(data)
data[:object_attributes][:action] == 'update'
end
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index f422e0ea036..976d85246a8 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -85,9 +85,9 @@ class HipchatService < Service
when "push", "tag_push"
create_push_message(data)
when "issue"
- create_issue_message(data) unless is_update?(data)
+ create_issue_message(data) unless update?(data)
when "merge_request"
- create_merge_request_message(data) unless is_update?(data)
+ create_merge_request_message(data) unless update?(data)
when "note"
create_note_message(data)
when "pipeline"
@@ -282,7 +282,7 @@ class HipchatService < Service
"<a href=\"#{project_url}\">#{project_name}</a>"
end
- def is_update?(data)
+ def update?(data)
data[:object_attributes][:action] == 'update'
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c1e4fcf94a4..cb7aba89020 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -206,12 +206,18 @@ class Repository
end
def branch_exists?(branch_name)
- branch_names.include?(branch_name)
+ return false unless raw_repository
+
+ @branch_exists_memo ||= Hash.new do |hash, key|
+ hash[key] = raw_repository.branch_exists?(key)
+ end
+
+ @branch_exists_memo[branch_name]
end
def ref_exists?(ref)
- rugged.references.exist?(ref)
- rescue Rugged::ReferenceError
+ !!raw_repository&.ref_exists?(ref)
+ rescue ArgumentError
false
end
@@ -266,6 +272,7 @@ class Repository
def expire_branches_cache
expire_method_caches(%i(branch_names branch_count))
@local_branches = nil
+ @branch_exists_memo = nil
end
def expire_statistics_caches
@@ -937,7 +944,7 @@ class Repository
if branch_commit
same_head = branch_commit.id == root_ref_commit.id
- !same_head && is_ancestor?(branch_commit.id, root_ref_commit.id)
+ !same_head && ancestor?(branch_commit.id, root_ref_commit.id)
else
nil
end
@@ -951,12 +958,12 @@ class Repository
nil
end
- def is_ancestor?(ancestor_id, descendant_id)
+ def ancestor?(ancestor_id, descendant_id)
return false if ancestor_id.nil? || descendant_id.nil?
Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
if is_enabled
- raw_repository.is_ancestor?(ancestor_id, descendant_id)
+ raw_repository.ancestor?(ancestor_id, descendant_id)
else
rugged_is_ancestor?(ancestor_id, descendant_id)
end
@@ -1185,7 +1192,7 @@ class Repository
end
def initialize_raw_repository
- Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git')
+ Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, false))
end
def circuit_breaker
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 0133091db57..a925fac7d3e 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -17,13 +17,13 @@ class ProjectPolicy < BasePolicy
desc "Project has public builds enabled"
condition(:public_builds, scope: :subject) { project.public_builds? }
- # For guest access we use #is_team_member? so we can use
+ # For guest access we use #team_member? so we can use
# project.members, which gets cached in subject scope.
# This is safe because team_access_level is guaranteed
# by ProjectAuthorization's validation to be at minimum
# GUEST
desc "User has guest access"
- condition(:guest) { is_team_member? }
+ condition(:guest) { team_member? }
desc "User has reporter access"
condition(:reporter) { team_access_level >= Gitlab::Access::REPORTER }
@@ -293,7 +293,7 @@ class ProjectPolicy < BasePolicy
private
- def is_team_member?
+ def team_member?
return false if @user.nil?
greedy_load_subject = false
diff --git a/app/services/akismet_service.rb b/app/services/akismet_service.rb
index 59153cbbc0a..7b5482b3cd1 100644
--- a/app/services/akismet_service.rb
+++ b/app/services/akismet_service.rb
@@ -7,7 +7,7 @@ class AkismetService
@options = options
end
- def is_spam?
+ def spam?
return false unless akismet_enabled?
params = {
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index d0ba9f89460..de2cd7e87be 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -15,7 +15,7 @@ module Ci
pipeline_schedule: schedule
)
- result = validate(current_user || trigger_request.trigger.owner,
+ result = validate(current_user,
ignore_skip_ci: ignore_skip_ci,
save_on_errors: save_on_errors)
diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb
index c58f04a252b..dbd0b9ef43a 100644
--- a/app/services/commits/create_service.rb
+++ b/app/services/commits/create_service.rb
@@ -17,7 +17,7 @@ module Commits
new_commit = create_commit!
success(result: new_commit)
- rescue ValidationError, ChangeError, Gitlab::Git::Index::IndexError, Repository::CommitError, GitHooksService::PreReceiveError => ex
+ rescue ValidationError, ChangeError, Gitlab::Git::Index::IndexError, Repository::CommitError, Gitlab::Git::HooksService::PreReceiveError => ex
error(ex.message)
end
diff --git a/app/services/concerns/users/new_user_notifier.rb b/app/services/concerns/users/new_user_notifier.rb
new file mode 100644
index 00000000000..231693ce7a9
--- /dev/null
+++ b/app/services/concerns/users/new_user_notifier.rb
@@ -0,0 +1,9 @@
+module Users
+ module NewUserNotifier
+ def notify_new_user(user, reset_token)
+ log_info("User \"#{user.name}\" (#{user.email}) was created")
+ notification_service.new_user(user, reset_token) if reset_token
+ system_hook_service.execute_hooks_for(user, :create)
+ end
+ end
+end
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index 673ed02f952..0ba6a0ac6b5 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -14,7 +14,7 @@ class CreateBranchService < BaseService
else
error('Invalid reference name')
end
- rescue GitHooksService::PreReceiveError => ex
+ rescue Gitlab::Git::HooksService::PreReceiveError => ex
error(ex.message)
end
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index 64b3c0118fb..1f059c31944 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -16,7 +16,7 @@ class DeleteBranchService < BaseService
else
error('Failed to remove branch')
end
- rescue GitHooksService::PreReceiveError => ex
+ rescue Gitlab::Git::HooksService::PreReceiveError => ex
error(ex.message)
end
diff --git a/app/services/git_hooks_service.rb b/app/services/git_hooks_service.rb
deleted file mode 100644
index eab65d09299..00000000000
--- a/app/services/git_hooks_service.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-class GitHooksService
- PreReceiveError = Class.new(StandardError)
-
- attr_accessor :oldrev, :newrev, :ref
-
- def execute(user, project, oldrev, newrev, ref)
- @project = project
- @user = Gitlab::GlId.gl_id(user)
- @oldrev = oldrev
- @newrev = newrev
- @ref = ref
-
- %w(pre-receive update).each do |hook_name|
- status, message = run_hook(hook_name)
-
- unless status
- raise PreReceiveError, message
- end
- end
-
- yield(self).tap do
- run_hook('post-receive')
- end
- end
-
- private
-
- def run_hook(name)
- hook = Gitlab::Git::Hook.new(name, @project)
- hook.trigger(@user, oldrev, newrev, ref)
- end
-end
diff --git a/app/services/git_operation_service.rb b/app/services/git_operation_service.rb
index 545ca0742e4..6b7a56e6922 100644
--- a/app/services/git_operation_service.rb
+++ b/app/services/git_operation_service.rb
@@ -1,8 +1,10 @@
class GitOperationService
- attr_reader :user, :repository
+ attr_reader :committer, :repository
+
+ def initialize(committer, new_repository)
+ committer = Gitlab::Git::Committer.from_user(committer) if committer.is_a?(User)
+ @committer = committer
- def initialize(new_user, new_repository)
- @user = new_user
@repository = new_repository
end
@@ -118,9 +120,9 @@ class GitOperationService
end
def with_hooks(ref, newrev, oldrev)
- GitHooksService.new.execute(
- user,
- repository.project,
+ Gitlab::Git::HooksService.new.execute(
+ committer,
+ repository,
oldrev,
newrev,
ref) do |service|
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index e81a56672e2..bb61136e33b 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -30,7 +30,7 @@ class GitPushService < BaseService
@project.repository.after_create_branch
# Re-find the pushed commits.
- if is_default_branch?
+ if default_branch?
# Initial push to the default branch. Take the full history of that branch as "newly pushed".
process_default_branch
else
@@ -50,7 +50,7 @@ class GitPushService < BaseService
# Update the bare repositories info/attributes file using the contents of the default branches
# .gitattributes file
- update_gitattributes if is_default_branch?
+ update_gitattributes if default_branch?
end
execute_related_hooks
@@ -66,7 +66,7 @@ class GitPushService < BaseService
end
def update_caches
- if is_default_branch?
+ if default_branch?
if push_to_new_branch?
# If this is the initial push into the default branch, the file type caches
# will already be reset as a result of `Project#change_head`.
@@ -108,7 +108,7 @@ class GitPushService < BaseService
# Schedules processing of commit messages.
def process_commit_messages
- default = is_default_branch?
+ default = default_branch?
@push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
if commit.matches_cross_reference_regex?
@@ -202,7 +202,7 @@ class GitPushService < BaseService
Gitlab::Git.branch_ref?(params[:ref])
end
- def is_default_branch?
+ def default_branch?
Gitlab::Git.branch_ref?(params[:ref]) &&
(Gitlab::Git.ref_name(params[:ref]) == project.default_branch || project.default_branch.nil?)
end
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
new file mode 100644
index 00000000000..d6f08fc3cce
--- /dev/null
+++ b/app/services/groups/nested_create_service.rb
@@ -0,0 +1,49 @@
+module Groups
+ class NestedCreateService < Groups::BaseService
+ attr_reader :group_path
+
+ def initialize(user, params)
+ @current_user, @params = user, params.dup
+
+ @group_path = @params.delete(:group_path)
+ end
+
+ def execute
+ return nil unless group_path
+
+ if group = Group.find_by_full_path(group_path)
+ return group
+ end
+
+ if group_path.include?('/') && !Group.supports_nested_groups?
+ raise 'Nested groups are not supported on MySQL'
+ end
+
+ create_group_path
+ end
+
+ private
+
+ def create_group_path
+ group_path_segments = group_path.split('/')
+
+ last_group = nil
+ partial_path_segments = []
+ while subgroup_name = group_path_segments.shift
+ partial_path_segments << subgroup_name
+ partial_path = partial_path_segments.join('/')
+
+ new_params = params.reverse_merge(
+ path: subgroup_name,
+ name: subgroup_name,
+ parent: last_group
+ )
+ new_params[:visibility_level] ||= Gitlab::CurrentSettings.current_application_settings.default_group_visibility
+
+ last_group = Group.find_by_full_path(partial_path) || Groups::CreateService.new(current_user, new_params).execute
+ end
+
+ last_group
+ end
+ end
+end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 4a4f2b91182..1486db046b5 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -192,6 +192,8 @@ class IssuableBaseService < BaseService
def after_create(issuable)
# To be overridden by subclasses
+
+ issuable.update_project_counter_caches
end
def before_update(issuable)
@@ -200,6 +202,8 @@ class IssuableBaseService < BaseService
def after_update(issuable)
# To be overridden by subclasses
+
+ issuable.update_project_counter_caches
end
def update(issuable)
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 234fcbede03..0307634c0b6 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -27,6 +27,8 @@ module Issues
todo_service.new_issue(issuable, current_user)
user_agent_detail_service.create
resolve_discussions_with_issue(issuable)
+
+ super
end
def resolve_discussions_with_issue(issue)
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index 194413bf321..3d53fe0646b 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -28,6 +28,8 @@ module MergeRequests
todo_service.new_merge_request(issuable, current_user)
issuable.cache_merge_request_closes_issues!(current_user)
update_merge_requests_head_pipeline(issuable)
+
+ super
end
private
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index bc846e07f24..b2b6c5627fb 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -26,10 +26,12 @@ module MergeRequests
merge_request.in_locked_state do
if commit
after_merge
+ clean_merge_jid
success
end
end
rescue MergeError => e
+ clean_merge_jid
log_merge_error(e.message, save_message_on_model: true)
end
@@ -49,7 +51,7 @@ module MergeRequests
raise MergeError, 'Conflicts detected during merge' unless commit_id
merge_request.update(merge_commit_sha: commit_id)
- rescue GitHooksService::PreReceiveError => e
+ rescue Gitlab::Git::HooksService::PreReceiveError => e
raise MergeError, e.message
rescue StandardError => e
raise MergeError, "Something went wrong during merge: #{e.message}"
@@ -70,6 +72,10 @@ module MergeRequests
end
end
+ def clean_merge_jid
+ merge_request.update_column(:merge_jid, nil)
+ end
+
def branch_deletion_user
@merge_request.force_remove_source_branch? ? @merge_request.author : current_user
end
diff --git a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
index aed5287940e..850deb0ac7a 100644
--- a/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
+++ b/app/services/merge_requests/merge_when_pipeline_succeeds_service.rb
@@ -30,7 +30,7 @@ module MergeRequests
next
end
- MergeWorker.perform_async(merge_request.id, merge_request.merge_user_id, merge_request.merge_params)
+ merge_request.merge_async(merge_request.merge_user_id, merge_request.merge_params)
end
end
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 75a65aecd1a..2832d893e95 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -83,7 +83,7 @@ module MergeRequests
if merge_request.head_pipeline && merge_request.head_pipeline.active?
MergeRequests::MergeWhenPipelineSucceedsService.new(project, current_user).execute(merge_request)
else
- MergeWorker.perform_async(merge_request.id, current_user.id, {})
+ merge_request.merge_async(current_user.id, {})
end
end
diff --git a/app/services/milestones/close_service.rb b/app/services/milestones/close_service.rb
index 776ec4b287b..5b06c4b601d 100644
--- a/app/services/milestones/close_service.rb
+++ b/app/services/milestones/close_service.rb
@@ -1,7 +1,7 @@
module Milestones
class CloseService < Milestones::BaseService
def execute(milestone)
- if milestone.close && milestone.is_project_milestone?
+ if milestone.close && milestone.project_milestone?
event_service.close_milestone(milestone, current_user)
end
diff --git a/app/services/milestones/create_service.rb b/app/services/milestones/create_service.rb
index aef3124c7e3..ed2e833d833 100644
--- a/app/services/milestones/create_service.rb
+++ b/app/services/milestones/create_service.rb
@@ -3,7 +3,7 @@ module Milestones
def execute
milestone = parent.milestones.new(params)
- if milestone.save && milestone.is_project_milestone?
+ if milestone.save && milestone.project_milestone?
event_service.open_milestone(milestone, current_user)
end
diff --git a/app/services/milestones/destroy_service.rb b/app/services/milestones/destroy_service.rb
index 600ebcfbecb..b18651476a8 100644
--- a/app/services/milestones/destroy_service.rb
+++ b/app/services/milestones/destroy_service.rb
@@ -1,7 +1,7 @@
module Milestones
class DestroyService < Milestones::BaseService
def execute(milestone)
- return unless milestone.is_project_milestone?
+ return unless milestone.project_milestone?
Milestone.transaction do
update_params = { milestone: nil }
diff --git a/app/services/milestones/reopen_service.rb b/app/services/milestones/reopen_service.rb
index 5b8b682caaf..3efb33157c5 100644
--- a/app/services/milestones/reopen_service.rb
+++ b/app/services/milestones/reopen_service.rb
@@ -1,7 +1,7 @@
module Milestones
class ReopenService < Milestones::BaseService
def execute(milestone)
- if milestone.activate && milestone.is_project_milestone?
+ if milestone.activate && milestone.project_milestone?
event_service.reopen_milestone(milestone, current_user)
end
diff --git a/app/services/notification_recipient_service.rb b/app/services/notification_recipient_service.rb
index 21c9c314a2a..c9f07c140f7 100644
--- a/app/services/notification_recipient_service.rb
+++ b/app/services/notification_recipient_service.rb
@@ -95,7 +95,7 @@ module NotificationRecipientService
def add_participants(user)
return unless target.respond_to?(:participants)
- self << [target.participants(user), :watch]
+ self << [target.participants(user), :participating]
end
# Get project/group users with CUSTOM notification level
diff --git a/app/services/projects/count_service.rb b/app/services/projects/count_service.rb
new file mode 100644
index 00000000000..5e633c37bf8
--- /dev/null
+++ b/app/services/projects/count_service.rb
@@ -0,0 +1,43 @@
+module Projects
+ # Base class for the various service classes that count project data (e.g.
+ # issues or forks).
+ class CountService
+ def initialize(project)
+ @project = project
+ end
+
+ def relation_for_count
+ raise(
+ NotImplementedError,
+ '"relation_for_count" must be implemented and return an ActiveRecord::Relation'
+ )
+ end
+
+ def count
+ Rails.cache.fetch(cache_key) { uncached_count }
+ end
+
+ def refresh_cache
+ Rails.cache.write(cache_key, uncached_count)
+ end
+
+ def uncached_count
+ relation_for_count.count
+ end
+
+ def delete_cache
+ Rails.cache.delete(cache_key)
+ end
+
+ def cache_key_name
+ raise(
+ NotImplementedError,
+ '"cache_key_name" must be implemented and return a String'
+ )
+ end
+
+ def cache_key
+ ['projects', @project.id, cache_key_name]
+ end
+ end
+end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 48578b6d9e5..a0cd52014a2 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -99,12 +99,19 @@ module Projects
event_service.create_project(@project, current_user)
system_hook_service.execute_hooks_for(@project, :create)
- unless @project.group || @project.gitlab_project_import?
- owners = [current_user, @project.namespace.owner].compact.uniq
- @project.add_master(owners, current_user: current_user)
- end
+ setup_authorizations
+ end
- @project.group&.refresh_members_authorized_projects
+ # Refresh the current user's authorizations inline (so they can access the
+ # project immediately after this request completes), and any other affected
+ # users in the background
+ def setup_authorizations
+ if @project.group
+ @project.group.refresh_members_authorized_projects(blocking: false)
+ current_user.refresh_authorized_projects
+ else
+ @project.add_master(@project.namespace.owner, current_user: current_user)
+ end
end
def skip_wiki?
diff --git a/app/services/projects/forks_count_service.rb b/app/services/projects/forks_count_service.rb
index e2e2b1da91d..3a0fa84b868 100644
--- a/app/services/projects/forks_count_service.rb
+++ b/app/services/projects/forks_count_service.rb
@@ -1,30 +1,12 @@
module Projects
# Service class for getting and caching the number of forks of a project.
- class ForksCountService
- def initialize(project)
- @project = project
+ class ForksCountService < CountService
+ def relation_for_count
+ @project.forks
end
- def count
- Rails.cache.fetch(cache_key) { uncached_count }
- end
-
- def refresh_cache
- Rails.cache.write(cache_key, uncached_count)
- end
-
- def delete_cache
- Rails.cache.delete(cache_key)
- end
-
- private
-
- def uncached_count
- @project.forks.count
- end
-
- def cache_key
- ['projects', @project.id, 'forks_count']
+ def cache_key_name
+ 'forks_count'
end
end
end
diff --git a/app/services/projects/open_issues_count_service.rb b/app/services/projects/open_issues_count_service.rb
new file mode 100644
index 00000000000..3c0d186a73c
--- /dev/null
+++ b/app/services/projects/open_issues_count_service.rb
@@ -0,0 +1,15 @@
+module Projects
+ # Service class for counting and caching the number of open issues of a
+ # project.
+ class OpenIssuesCountService < CountService
+ def relation_for_count
+ # We don't include confidential issues in this number since this would
+ # expose the number of confidential issues to non project members.
+ @project.issues.opened.public_only
+ end
+
+ def cache_key_name
+ 'open_issues_count'
+ end
+ end
+end
diff --git a/app/services/projects/open_merge_requests_count_service.rb b/app/services/projects/open_merge_requests_count_service.rb
new file mode 100644
index 00000000000..2a90f78b90d
--- /dev/null
+++ b/app/services/projects/open_merge_requests_count_service.rb
@@ -0,0 +1,13 @@
+module Projects
+ # Service class for counting and caching the number of open merge requests of
+ # a project.
+ class OpenMergeRequestsCountService < CountService
+ def relation_for_count
+ @project.merge_requests.opened
+ end
+
+ def cache_key_name
+ 'open_merge_requests_count'
+ end
+ end
+end
diff --git a/app/services/spam_service.rb b/app/services/spam_service.rb
index 3e65b7d31a3..73ea3018fbd 100644
--- a/app/services/spam_service.rb
+++ b/app/services/spam_service.rb
@@ -45,7 +45,7 @@ class SpamService
def check(api)
return false unless request && check_for_spam?
- return false unless akismet.is_spam?
+ return false unless akismet.spam?
create_spam_log(api)
true
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 1763f64a4e4..1f66a2668f9 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -142,7 +142,7 @@ module SystemNoteService
#
# Returns the created Note object
def change_milestone(noteable, project, author, milestone)
- format = milestone&.is_group_milestone? ? :name : :iid
+ format = milestone&.group_milestone? ? :name : :iid
body = milestone.nil? ? 'removed milestone' : "changed milestone to #{milestone.to_reference(project, format: format)}"
create_note(NoteSummary.new(noteable, project, author, body, action: 'milestone'))
diff --git a/app/services/tags/create_service.rb b/app/services/tags/create_service.rb
index 674792f6138..b3f4a72d6fe 100644
--- a/app/services/tags/create_service.rb
+++ b/app/services/tags/create_service.rb
@@ -13,7 +13,7 @@ module Tags
new_tag = repository.add_tag(current_user, tag_name, target, message)
rescue Rugged::TagError
return error("Tag #{tag_name} already exists")
- rescue GitHooksService::PreReceiveError => ex
+ rescue Gitlab::Git::HooksService::PreReceiveError => ex
return error(ex.message)
end
diff --git a/app/services/tags/destroy_service.rb b/app/services/tags/destroy_service.rb
index a368f4f5b61..d3d46064729 100644
--- a/app/services/tags/destroy_service.rb
+++ b/app/services/tags/destroy_service.rb
@@ -21,7 +21,7 @@ module Tags
else
error('Failed to remove tag')
end
- rescue GitHooksService::PreReceiveError => ex
+ rescue Gitlab::Git::HooksService::PreReceiveError => ex
error(ex.message)
end
diff --git a/app/services/test_hooks/system_service.rb b/app/services/test_hooks/system_service.rb
index 76c3c19bd74..67552edefc9 100644
--- a/app/services/test_hooks/system_service.rb
+++ b/app/services/test_hooks/system_service.rb
@@ -2,47 +2,16 @@ module TestHooks
class SystemService < TestHooks::BaseService
private
- def project
- @project ||= begin
- project = Project.first
-
- throw(:validation_error, 'Ensure that at least one project exists.') unless project
-
- project
- end
- end
-
def push_events_data
- if project.empty_repo?
- throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
- end
-
- Gitlab::DataBuilder::Push.build_sample(project, current_user)
+ Gitlab::DataBuilder::Push.sample_data
end
def tag_push_events_data
- if project.repository.tags.empty?
- throw(:validation_error, "Ensure project \"#{project.human_name}\" has tags.")
- end
-
- Gitlab::DataBuilder::Push.build_sample(project, current_user)
+ Gitlab::DataBuilder::Push.sample_data
end
def repository_update_events_data
- commit = project.commit
- ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}"
-
- unless commit
- throw(:validation_error, "Ensure project \"#{project.human_name}\" has commits.")
- end
-
- change = Gitlab::DataBuilder::Repository.single_change(
- commit.parent_id || Gitlab::Git::BLANK_SHA,
- commit.id,
- ref
- )
-
- Gitlab::DataBuilder::Repository.update(project, current_user, [change], [ref])
+ Gitlab::DataBuilder::Repository.sample_data
end
end
end
diff --git a/app/services/user_project_access_changed_service.rb b/app/services/user_project_access_changed_service.rb
index d7a6804ee88..8630e572624 100644
--- a/app/services/user_project_access_changed_service.rb
+++ b/app/services/user_project_access_changed_service.rb
@@ -3,7 +3,13 @@ class UserProjectAccessChangedService
@user_ids = Array.wrap(user_ids)
end
- def execute
- AuthorizedProjectsWorker.bulk_perform_and_wait(@user_ids.map { |id| [id] })
+ def execute(blocking: true)
+ bulk_args = @user_ids.map { |id| [id] }
+
+ if blocking
+ AuthorizedProjectsWorker.bulk_perform_and_wait(bulk_args)
+ else
+ AuthorizedProjectsWorker.bulk_perform_async(bulk_args)
+ end
end
end
diff --git a/app/services/users/create_service.rb b/app/services/users/create_service.rb
index 74abc017cea..c8a3c461d60 100644
--- a/app/services/users/create_service.rb
+++ b/app/services/users/create_service.rb
@@ -1,5 +1,7 @@
module Users
class CreateService < BaseService
+ include NewUserNotifier
+
def initialize(current_user, params = {})
@current_user = current_user
@params = params.dup
@@ -10,11 +12,7 @@ module Users
@reset_token = user.generate_reset_token if user.recently_sent_password_reset?
- if user.save
- log_info("User \"#{user.name}\" (#{user.email}) was created")
- notification_service.new_user(user, @reset_token) if @reset_token
- system_hook_service.execute_hooks_for(user, :create)
- end
+ notify_new_user(user, @reset_token) if user.save
user
end
diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb
index dfbd6016c3f..2f9855273dc 100644
--- a/app/services/users/update_service.rb
+++ b/app/services/users/update_service.rb
@@ -1,5 +1,7 @@
module Users
class UpdateService < BaseService
+ include NewUserNotifier
+
def initialize(user, params = {})
@user = user
@params = params.dup
@@ -10,7 +12,11 @@ module Users
assign_attributes(&block)
+ user_exists = @user.persisted?
+
if @user.save(validate: validate)
+ notify_new_user(@user, nil) unless user_exists
+
success
else
error(@user.errors.full_messages.uniq.join('. '))
diff --git a/app/services/validate_new_branch_service.rb b/app/services/validate_new_branch_service.rb
index d232e85cd33..7d1ed768ee8 100644
--- a/app/services/validate_new_branch_service.rb
+++ b/app/services/validate_new_branch_service.rb
@@ -13,7 +13,7 @@ class ValidateNewBranchService < BaseService
end
success
- rescue GitHooksService::PreReceiveError => ex
+ rescue Gitlab::Git::HooksService::PreReceiveError => ex
error(ex.message)
end
end
diff --git a/app/views/admin/dashboard/_head.html.haml b/app/views/admin/dashboard/_head.html.haml
index dff549f502c..c2151710884 100644
--- a/app/views/admin/dashboard/_head.html.haml
+++ b/app/views/admin/dashboard/_head.html.haml
@@ -31,3 +31,7 @@
= link_to admin_cohorts_path, title: 'Cohorts' do
%span
Cohorts
+ = nav_link(controller: :conversational_development_index) do
+ = link_to admin_conversational_development_index_path, title: 'ConvDev Index' do
+ %span
+ ConvDev Index
diff --git a/app/views/admin/monitoring/_head.html.haml b/app/views/admin/monitoring/_head.html.haml
index 901e30275fd..b3530915068 100644
--- a/app/views/admin/monitoring/_head.html.haml
+++ b/app/views/admin/monitoring/_head.html.haml
@@ -3,10 +3,6 @@
= render 'shared/nav_scroll'
.nav-links.sub-nav.scrolling-tabs
%ul{ class: (container_class) }
- = nav_link(controller: :conversational_development_index) do
- = link_to admin_conversational_development_index_path, title: 'ConvDev Index' do
- %span
- ConvDev Index
= nav_link(controller: :system_info) do
= link_to admin_system_info_path, title: 'System Info' do
%span
diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml
index 82aa51f9778..8ba88906714 100644
--- a/app/views/doorkeeper/authorizations/new.html.haml
+++ b/app/views/doorkeeper/authorizations/new.html.haml
@@ -1,39 +1,43 @@
-%h3.page-title Authorization required
%main{ :role => "main" }
- %p.h4
- Authorize
- %strong.text-info= @pre_auth.client.name
- to use your account?
+ .modal-no-backdrop
+ .modal-content
+ .modal-header
+ %h3.page-title
+ Authorize
+ = link_to @pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer'
+ to use your account?
- - if current_user.admin?
- .text-warning.prepend-top-20
- %p
- = icon("exclamation-triangle fw")
- You are an admin, which means granting access to
- %strong= @pre_auth.client.name
- will allow them to interact with GitLab as an admin as well. Proceed with caution.
-
- - if @pre_auth.scopes
- #oauth-permissions
- %p This application will be able to:
- %ul.text-info
- - @pre_auth.scopes.each do |scope|
- %li= t scope, scope: [:doorkeeper, :scopes]
- %hr/
- .actions
- = form_tag oauth_authorization_path, method: :post do
- = hidden_field_tag :client_id, @pre_auth.client.uid
- = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
- = hidden_field_tag :state, @pre_auth.state
- = hidden_field_tag :response_type, @pre_auth.response_type
- = hidden_field_tag :scope, @pre_auth.scope
- = hidden_field_tag :nonce, @pre_auth.nonce
- = submit_tag "Authorize", class: "btn btn-success wide pull-left"
- = form_tag oauth_authorization_path, method: :delete do
- = hidden_field_tag :client_id, @pre_auth.client.uid
- = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
- = hidden_field_tag :state, @pre_auth.state
- = hidden_field_tag :response_type, @pre_auth.response_type
- = hidden_field_tag :scope, @pre_auth.scope
- = hidden_field_tag :nonce, @pre_auth.nonce
- = submit_tag "Deny", class: "btn btn-danger prepend-left-10"
+ .modal-body
+ - if current_user.admin?
+ .text-warning
+ %p
+ = icon("exclamation-triangle fw")
+ You are an admin, which means granting access to
+ %strong= @pre_auth.client.name
+ will allow them to interact with GitLab as an admin as well. Proceed with caution.
+ %p
+ You are about to authorize
+ = link_to @pre_auth.client.name, @pre_auth.redirect_uri, target: '_blank', rel: 'noopener noreferrer'
+ to use your account.
+ - if @pre_auth.scopes
+ This application will be able to:
+ %ul
+ - @pre_auth.scopes.each do |scope|
+ %li= t scope, scope: [:doorkeeper, :scopes]
+ .form-actions.text-right
+ = form_tag oauth_authorization_path, method: :delete, class: 'inline' do
+ = hidden_field_tag :client_id, @pre_auth.client.uid
+ = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
+ = hidden_field_tag :state, @pre_auth.state
+ = hidden_field_tag :response_type, @pre_auth.response_type
+ = hidden_field_tag :scope, @pre_auth.scope
+ = hidden_field_tag :nonce, @pre_auth.nonce
+ = submit_tag "Deny", class: "btn btn-danger"
+ = form_tag oauth_authorization_path, method: :post, class: 'inline' do
+ = hidden_field_tag :client_id, @pre_auth.client.uid
+ = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri
+ = hidden_field_tag :state, @pre_auth.state
+ = hidden_field_tag :response_type, @pre_auth.response_type
+ = hidden_field_tag :scope, @pre_auth.scope
+ = hidden_field_tag :nonce, @pre_auth.nonce
+ = submit_tag "Authorize", class: "btn btn-success prepend-left-10"
diff --git a/app/views/events/_event_push.atom.haml b/app/views/events/_event_push.atom.haml
index bf655f9d21a..e3c5fd55f08 100644
--- a/app/views/events/_event_push.atom.haml
+++ b/app/views/events/_event_push.atom.haml
@@ -5,9 +5,10 @@
%i
at
= event.created_at.to_s(:short)
- %blockquote= markdown(escape_once(event.commit_title), pipeline: :atom, project: event.project, author: event.author)
- - if event.commits_count > 1
- %p
- %i
- \... and
- = pluralize(event.commits_count - 1, "more commit")
+ - unless event.rm_ref?
+ %blockquote= markdown(escape_once(event.commit_title), pipeline: :atom, project: event.project, author: event.author)
+ - if event.commits_count > 1
+ %p
+ %i
+ \... and
+ = pluralize(event.commits_count - 1, "more commit")
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index 973c652ad88..53ebdd6d2ff 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -41,7 +41,3 @@
%li.commits-stat
= link_to create_mr_path(project.default_branch, event.ref_name, project) do
Create Merge Request
-- elsif event.rm_ref?
- .event-body
- %ul.well-list.event_commits
- = render "events/commit", project: project, event: event
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 569eef46e6e..184df6f5406 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -1,5 +1,9 @@
- page_title "Merge Requests"
+- content_for :page_specific_javascripts do
+ = webpack_bundle_tag 'common_vue'
+ = webpack_bundle_tag 'filtered_search'
+
- if show_new_nav? && current_user
- content_for :breadcrumbs_extra do
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests
@@ -13,7 +17,7 @@
.nav-controls{ class: ("visible-xs" if show_new_nav?) }
= render 'shared/new_project_item_select', path: 'merge_requests/new', label: "New merge request", type: :merge_requests
- = render 'shared/issuable/filter', type: :merge_requests
+ = render 'shared/issuable/search_bar', type: :merge_requests
.row-content-block.second-block
Only merge requests from
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index 54b1b7a734a..23b1a22240f 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,4 +1,4 @@
= render "header_title"
= render 'shared/milestones/top', milestone: @milestone, group: @group
-= render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true if @milestone.is_legacy_group_milestone?
+= render 'shared/milestones/tabs', milestone: @milestone, show_project_name: true if @milestone.legacy_group_milestone?
= render 'shared/milestones/sidebar', milestone: @milestone, affix_offset: 102
diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml
index 6d9ec043590..9382ee8715e 100644
--- a/app/views/layouts/errors.html.haml
+++ b/app/views/layouts/errors.html.haml
@@ -15,7 +15,7 @@
h1 {
font-size: 56px;
line-height: 100px;
- font-weight: normal;
+ font-weight: 400;
color: #456;
}
@@ -28,7 +28,7 @@
h3 {
color: #456;
font-size: 20px;
- font-weight: normal;
+ font-weight: 400;
line-height: 28px;
}
diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml
index 4db84771f4e..653452871a0 100644
--- a/app/views/layouts/nav/_breadcrumbs.html.haml
+++ b/app/views/layouts/nav/_breadcrumbs.html.haml
@@ -1,8 +1,9 @@
- breadcrumb_link = breadcrumb_title_link
+- container = @no_breadcrumb_container ? 'container-fluid' : container_class
- hide_top_links = @hide_top_links || false
%nav.breadcrumbs{ role: "navigation" }
- .breadcrumbs-container{ class: [container_class, @content_class] }
+ .breadcrumbs-container{ class: [container, @content_class] }
- if defined?(@new_sidebar)
= button_tag class: 'toggle-mobile-nav', type: 'button' do
%span.sr-only Open sidebar
diff --git a/app/views/layouts/nav/_new_admin_sidebar.html.haml b/app/views/layouts/nav/_new_admin_sidebar.html.haml
index 3cbcd841aff..9294529f496 100644
--- a/app/views/layouts/nav/_new_admin_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_admin_sidebar.html.haml
@@ -4,7 +4,7 @@
= link_to admin_root_path, title: 'Admin Overview' do
.avatar-container.s40.settings-avatar
= icon('wrench')
- .project-title Admin Area
+ .sidebar-context-title Admin Area
%ul.sidebar-top-level-items
= nav_link(controller: %w(dashboard admin projects users groups jobs runners cohorts), html_options: {class: 'home'}) do
= link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
@@ -42,6 +42,10 @@
= link_to admin_cohorts_path, title: 'Cohorts' do
%span
Cohorts
+ = nav_link(controller: :conversational_development_index) do
+ = link_to admin_conversational_development_index_path, title: 'ConvDev Index' do
+ %span
+ ConvDev Index
= nav_link(controller: %w(conversational_development_index system_info background_jobs logs health_check requests_profiles)) do
= link_to admin_conversational_development_index_path, title: 'Monitoring' do
@@ -51,10 +55,6 @@
Monitoring
%ul.sidebar-sub-level-items
- = nav_link(controller: :conversational_development_index) do
- = link_to admin_conversational_development_index_path, title: 'ConvDev Index' do
- %span
- ConvDev Index
= nav_link(controller: :system_info) do
= link_to admin_system_info_path, title: 'System Info' do
%span
@@ -82,6 +82,7 @@
= custom_icon('messages')
%span.nav-item-name
Messages
+
= nav_link(controller: [:hooks, :hook_logs]) do
= link_to admin_hooks_path, title: 'Hooks' do
.nav-icon-container
@@ -140,7 +141,6 @@
%span.nav-item-name
Appearance
- %li.divider
= nav_link(controller: :application_settings) do
= link_to admin_application_settings_path, title: 'Settings' do
.nav-icon-container
diff --git a/app/views/layouts/nav/_new_group_sidebar.html.haml b/app/views/layouts/nav/_new_group_sidebar.html.haml
index ed5793f09fe..d90aea2e361 100644
--- a/app/views/layouts/nav/_new_group_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_group_sidebar.html.haml
@@ -4,7 +4,7 @@
= link_to group_path(@group), title: @group.name do
.avatar-container.s40.group-avatar
= image_tag group_icon(@group), class: "avatar s40 avatar-tile"
- .group-title
+ .sidebar-context-title
= @group.name
%ul.sidebar-top-level-items
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
diff --git a/app/views/layouts/nav/_new_profile_sidebar.html.haml b/app/views/layouts/nav/_new_profile_sidebar.html.haml
index 4234df56d1d..85b2c7630c8 100644
--- a/app/views/layouts/nav/_new_profile_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_profile_sidebar.html.haml
@@ -4,7 +4,7 @@
= link_to profile_path, title: 'Profile Settings' do
.avatar-container.s40.settings-avatar
= icon('user')
- .project-title User Settings
+ .sidebar-context-title User Settings
%ul.sidebar-top-level-items
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
= link_to profile_path, title: 'Profile Settings' do
diff --git a/app/views/layouts/nav/_new_project_sidebar.html.haml b/app/views/layouts/nav/_new_project_sidebar.html.haml
index 0ef81375c3a..341943cf833 100644
--- a/app/views/layouts/nav/_new_project_sidebar.html.haml
+++ b/app/views/layouts/nav/_new_project_sidebar.html.haml
@@ -5,7 +5,7 @@
= link_to project_path(@project), title: @project.name do
.avatar-container.s40.project-avatar
= project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile')
- .project-title
+ .sidebar-context-title
= @project.name
%ul.sidebar-top-level-items
= nav_link(path: ['projects#show', 'projects#activity', 'cycle_analytics#show'], html_options: { class: 'home' }) do
@@ -86,7 +86,8 @@
%span.nav-item-name
Issues
- if @project.issues_enabled?
- %span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
+ %span.badge.count.issue_counter
+ = number_with_delimiter(@project.open_issues_count)
%ul.sidebar-sub-level-items
= nav_link(controller: :issues) do
@@ -116,7 +117,8 @@
= custom_icon('mr_bold')
%span.nav-item-name
Merge Requests
- %span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.count)
+ %span.badge.count.merge_counter.js-merge-counter
+ = number_with_delimiter(@project.open_merge_requests_count)
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :environments, :artifacts]) do
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 924cd2e9681..b88465848e3 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -28,7 +28,8 @@
%span
Issues
- if @project.issues_enabled?
- %span.badge.count.issue_counter= number_with_delimiter(issuables_count_for_state(:issues, :opened, finder: IssuesFinder.new(current_user, project_id: @project.id)))
+ %span.badge.count.issue_counter
+ = number_with_delimiter(@project.open_issues_count)
- if project_nav_tab? :merge_requests
- controllers = [:merge_requests, 'projects/merge_requests/conflicts']
@@ -37,7 +38,8 @@
= link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%span
Merge Requests
- %span.badge.count.merge_counter.js-merge-counter= number_with_delimiter(issuables_count_for_state(:merge_requests, :opened, finder: MergeRequestsFinder.new(current_user, project_id: @project.id)))
+ %span.badge.count.merge_counter.js-merge-counter
+ = number_with_delimiter(@project.open_merge_requests_count)
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :environments, :artifacts]) do
diff --git a/app/views/layouts/oauth_error.html.haml b/app/views/layouts/oauth_error.html.haml
index 34bcd2a8b3a..03b387f8181 100644
--- a/app/views/layouts/oauth_error.html.haml
+++ b/app/views/layouts/oauth_error.html.haml
@@ -19,7 +19,7 @@
h3 {
color: #456;
font-size: 22px;
- font-weight: bold;
+ font-weight: 600;
margin-bottom: 6px;
}
diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml
index 97cf13df070..5638b7da1b0 100644
--- a/app/views/projects/_project_templates.html.haml
+++ b/app/views/projects/_project_templates.html.haml
@@ -1,6 +1,6 @@
.project-templates-buttons.import-buttons{ data: { toggle: "buttons" } }
.btn.blank-option.active
- %input{ type: "radio", autocomplete: "off", name: "project_templates", id: "blank", checked: "true" }
+ %input{ type: "radio", autocomplete: "off", name: "project[template_name]", id: "blank", checked: "true", value: "" }
= icon('file-o', class: 'btn-template-icon')
Blank
- Gitlab::ProjectTemplate.all.each do |template|
diff --git a/app/views/projects/boards/_show.html.haml b/app/views/projects/boards/_show.html.haml
index 2076e46fde8..5354ec8522e 100644
--- a/app/views/projects/boards/_show.html.haml
+++ b/app/views/projects/boards/_show.html.haml
@@ -1,3 +1,4 @@
+- @no_breadcrumb_container = true
- @no_container = true
- @content_class = "issue-boards-content"
- page_title "Boards"
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 7e8a5a38086..1214aabe837 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -37,14 +37,14 @@
.commit-actions.hidden-xs
- - if commit.status(ref)
- = render_commit_status(commit, ref: ref)
-
- if request.xhr?
= render partial: 'projects/commit/signature', object: commit.signature
- else
= render partial: 'projects/commit/ajax_signature', locals: { commit: commit }
+ - if commit.status(ref)
+ = render_commit_status(commit, ref: ref)
+
= link_to commit.short_id, project_commit_path(project, commit), class: "commit-sha btn btn-transparent"
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
= link_to_browse_code(project, commit)
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 04b4ed95a2d..fd7ff176c5e 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -37,7 +37,8 @@
%ul
- if can_update_issue
%li= link_to 'Edit', edit_project_issue_path(@project, @issue)
- - unless current_user == @issue.author
+ / TODO: simplify condition back #36860
+ - if @issue.author && current_user != @issue.author
%li= link_to 'Report abuse', new_abuse_report_path(user_id: @issue.author.id, ref_url: issue_url(@issue))
- if can_update_issue
%li= link_to 'Close issue', issue_path(@issue, issue: { state_event: :close }, format: 'json'), class: "btn-close js-btn-issue-action #{issue_button_visibility(@issue, true)}", title: 'Close issue'
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 647e0a772b1..5698bb281b4 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -68,9 +68,10 @@
- if git_import_enabled?
%button.btn.js-toggle-button.import_git{ type: "button" }
= icon('git', text: 'Repo by URL')
- .import_gitlab_project.has-tooltip{ data: { container: 'body' } }
- = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
- = icon('gitlab', text: 'GitLab export')
+ - if gitlab_project_import_enabled?
+ .import_gitlab_project.has-tooltip{ data: { container: 'body' } }
+ = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
+ = icon('gitlab', text: 'GitLab export')
.row
.col-lg-12
diff --git a/app/views/projects/notes/_actions.html.haml b/app/views/projects/notes/_actions.html.haml
index cb737d129f0..b04f5efe1f9 100644
--- a/app/views/projects/notes/_actions.html.haml
+++ b/app/views/projects/notes/_actions.html.haml
@@ -26,8 +26,12 @@
":title" => "buttonText",
":ref" => "'button'" }
- = icon('spin spinner', 'v-show' => 'loading', class: 'loading', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
- %div{ 'v-show' => '!loading' }= render 'shared/icons/icon_status_success.svg'
+ = icon('spin spinner', 'v-if' => 'loading', class: 'loading', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
+ %div{ 'v-else' => '' }
+ %template{ 'v-if' => 'isResolved' }
+ = render 'shared/icons/icon_status_success_solid.svg'
+ %template{ 'v-else' => '' }
+ = render 'shared/icons/icon_status_success.svg'
- if current_user
- if note.emoji_awardable?
diff --git a/app/views/projects/notes/_more_actions_dropdown.html.haml b/app/views/projects/notes/_more_actions_dropdown.html.haml
index 5930209a682..7e854186973 100644
--- a/app/views/projects/notes/_more_actions_dropdown.html.haml
+++ b/app/views/projects/notes/_more_actions_dropdown.html.haml
@@ -6,6 +6,8 @@
%span.icon
= custom_icon('ellipsis_v')
%ul.dropdown-menu.more-actions-dropdown.dropdown-open-left
+ %li
+ = clipboard_button(text: noteable_note_url(note), title: "Copy reference to clipboard", button_text: 'Copy link', hide_tooltip: true, hide_button_icon: true)
- unless is_current_user
%li
= link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
diff --git a/app/views/projects/pipelines/charts/_pipelines.haml b/app/views/projects/pipelines/charts/_pipelines.haml
index 02f1ef4b6da..7a100843f5e 100644
--- a/app/views/projects/pipelines/charts/_pipelines.haml
+++ b/app/views/projects/pipelines/charts/_pipelines.haml
@@ -14,19 +14,19 @@
.prepend-top-default
%p.light
- = _("Jobs for last week")
+ = _("Pipelines for last week")
(#{date_from_to(Date.today - 7.days, Date.today)})
%canvas#weekChart{ height: 200 }
.prepend-top-default
%p.light
- = _("Jobs for last month")
+ = _("Pipelines for last month")
(#{date_from_to(Date.today - 30.days, Date.today)})
%canvas#monthChart{ height: 200 }
.prepend-top-default
%p.light
- = _("Jobs for last year")
+ = _("Pipelines for last year")
%canvas#yearChart.padded{ height: 250 }
%script#pipelinesChartsData{ type: "application/json" }
diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml
index abc97bcdff5..25d862ab4de 100644
--- a/app/views/projects/runners/_runner.html.haml
+++ b/app/views/projects/runners/_runner.html.haml
@@ -8,7 +8,7 @@
- if runner.locked?
= icon('lock', class: 'has-tooltip', title: 'Locked to current projects')
- %small
+ %small.edit-runner
= link_to edit_project_runner_path(@project, runner) do
%i.fa.fa-edit.btn
- else
diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml
index 3139be1cd37..a4a5cec1314 100644
--- a/app/views/search/_form.html.haml
+++ b/app/views/search/_form.html.haml
@@ -11,5 +11,5 @@
%span.sr-only
Clear search
- unless params[:snippets].eql? 'true'
- = render 'filter' if current_user
+ = render 'filter'
= button_tag "Search", class: "btn btn-success btn-search"
diff --git a/app/views/shared/icons/_icon_status_success.svg b/app/views/shared/icons/_icon_status_success.svg
index eed5006bebe..845562e9320 100755
--- a/app/views/shared/icons/_icon_status_success.svg
+++ b/app/views/shared/icons/_icon_status_success.svg
@@ -1 +1 @@
-<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z"/><path d="M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill="#FFF"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></g></svg>
+<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z M13 7A6 6 0 1 0 1 7a6 6 0 0 0 12 0z" fill-rule="evenodd"/><path d="M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z"/></svg>
diff --git a/app/views/shared/icons/_icon_status_success_solid.svg b/app/views/shared/icons/_icon_status_success_solid.svg
new file mode 100644
index 00000000000..0aac6d933e1
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_success_solid.svg
@@ -0,0 +1 @@
+<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7a7 7 0 1 1 14 0A7 7 0 0 1 0 7z M6.278 7.697L5.045 6.464a.296.296 0 0 0-.42-.002l-.613.614a.298.298 0 0 0 .002.42l1.91 1.909a.5.5 0 0 0 .703.005l.265-.265L9.997 6.04a.291.291 0 0 0-.009-.408l-.614-.614a.29.29 0 0 0-.408-.009L6.278 7.697z" fill-rule="evenodd"/></svg>
diff --git a/app/views/shared/issuable/_close_reopen_button.html.haml b/app/views/shared/issuable/_close_reopen_button.html.haml
index f16bc8dd430..cb706d80f23 100644
--- a/app/views/shared/issuable/_close_reopen_button.html.haml
+++ b/app/views/shared/issuable/_close_reopen_button.html.haml
@@ -9,6 +9,7 @@
class: "hidden-xs hidden-sm btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}", title: "Reopen #{display_issuable_type}"
- elsif can_update && !is_current_user
= render 'shared/issuable/close_reopen_report_toggle', issuable: issuable
-- else
+- elsif issuable.author
+ / TODO: change back to else #36860
= link_to 'Report abuse', new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
class: 'hidden-xs hidden-sm btn btn-grouped btn-close-color', title: 'Report abuse'
diff --git a/app/views/shared/issuable/_close_reopen_report_toggle.html.haml b/app/views/shared/issuable/_close_reopen_report_toggle.html.haml
index a38cd319e3c..d8144a39b23 100644
--- a/app/views/shared/issuable/_close_reopen_report_toggle.html.haml
+++ b/app/views/shared/issuable/_close_reopen_report_toggle.html.haml
@@ -37,13 +37,15 @@
%li.divider.droplab-item-ignore
- %li.report-item{ data: { text: 'Report abuse', url: new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
- button_class: "#{button_class} btn-close-color", toggle_class: "#{toggle_class} btn-close-color", method: '' } }
- %button.btn.btn-transparent
- = icon('check', class: 'icon')
- .description
- %strong.title Report abuse
- %p.text
- Report
- = display_issuable_type.pluralize
- that are abusive, inappropriate or spam.
+ / TODO: remove condition #36860
+ - if issuable.author
+ %li.report-item{ data: { text: 'Report abuse', url: new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
+ button_class: "#{button_class} btn-close-color", toggle_class: "#{toggle_class} btn-close-color", method: '' } }
+ %button.btn.btn-transparent
+ = icon('check', class: 'icon')
+ .description
+ %strong.title Report abuse
+ %p.text
+ Report
+ = display_issuable_type.pluralize
+ that are abusive, inappropriate or spam.
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index f63b9698408..e81789ea7a2 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -93,6 +93,13 @@
%span.dropdown-label-box{ style: 'background: {{color}}' }
%span.label-title.js-data-value
{{title}}
+ #js-dropdown-my-reaction.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
+ %li.filter-dropdown-item
+ %button.btn.btn-link
+ %gl-emoji
+ %span.js-data-value.prepend-left-10
+ {{name}}
%button.clear-search.hidden{ type: 'button' }
= icon('times')
.filter-dropdown-container
diff --git a/app/views/shared/issuable/_user_dropdown_item.html.haml b/app/views/shared/issuable/_user_dropdown_item.html.haml
index c18e4975bb8..48d04678d47 100644
--- a/app/views/shared/issuable/_user_dropdown_item.html.haml
+++ b/app/views/shared/issuable/_user_dropdown_item.html.haml
@@ -4,7 +4,7 @@
%li.filter-dropdown-item{ class: ('js-current-user' if user == current_user) }
%button.btn.btn-link.dropdown-user{ type: :button }
.avatar-container.s40
- = user_avatar_without_link(user: user, lazy: avatar[:lazy], url: avatar[:url], size: 40).gsub('/images/{{avatar_url}}','{{avatar_url}}').html_safe
+ = user_avatar_without_link(user: user, lazy: avatar[:lazy], url: avatar[:url], size: 40, has_tooltip: false).gsub('/images/{{avatar_url}}','{{avatar_url}}').html_safe
.dropdown-user-details
%span
= user.name
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 6a85f7d0564..305e2542281 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -5,7 +5,7 @@
.row
.col-sm-6
%strong= link_to truncate(milestone.title, length: 100), milestone_path
- - if milestone.is_group_milestone?
+ - if milestone.group_milestone?
%span - Group Milestone
- else
%span - Project Milestone
@@ -18,10 +18,10 @@
&middot;
= link_to pluralize(milestone.merge_requests.size, 'Merge Request'), merge_requests_path
.col-sm-6= milestone_progress_bar(milestone)
- - if milestone.is_a?(GlobalMilestone) || milestone.is_group_milestone?
+ - if milestone.is_a?(GlobalMilestone) || milestone.group_milestone?
.row
.col-sm-6
- - if milestone.is_legacy_group_milestone?
+ - if milestone.legacy_group_milestone?
.expiration= render('shared/milestone_expired', milestone: milestone)
.projects
- milestone.milestones.each do |milestone|
@@ -31,7 +31,7 @@
- if @group
.col-sm-6.milestone-actions
- if can?(current_user, :admin_milestones, @group)
- - if milestone.is_group_milestone?
+ - if milestone.group_milestone?
= link_to edit_group_milestone_path(@group, milestone), class: "btn btn-xs btn-grouped" do
Edit
\
diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml
index 3014300fbe7..fd0760d83a5 100644
--- a/app/views/shared/milestones/_top.html.haml
+++ b/app/views/shared/milestones/_top.html.haml
@@ -22,7 +22,7 @@
- if group
.pull-right
- if can?(current_user, :admin_milestones, group)
- - if milestone.is_group_milestone?
+ - if milestone.group_milestone?
= link_to edit_group_milestone_path(group, milestone), class: "btn btn btn-grouped" do
Edit
- if milestone.active?
@@ -33,7 +33,7 @@
.detail-page-description.milestone-detail
%h2.title
= markdown_field(milestone, :title)
- - if @milestone.is_group_milestone? && @milestone.description.present?
+ - if @milestone.group_milestone? && @milestone.description.present?
%div
.description
.wiki
@@ -44,7 +44,7 @@
- close_msg = group ? 'You may close the milestone now.' : 'Navigate to the project to close the milestone.'
%span All issues for this milestone are closed. #{close_msg}
-- if @milestone.is_legacy_group_milestone? || @milestone.is_dashboard_milestone?
+- if @milestone.legacy_group_milestone? || @milestone.dashboard_milestone?
.table-holder
%table.table
%thead
@@ -67,7 +67,7 @@
Open
%td
= ms.expires_at
-- elsif @milestone.is_group_milestone?
+- elsif @milestone.group_milestone?
%br
View
= link_to 'Issues', issues_group_path(@group, milestone_title: milestone.title)
diff --git a/app/views/shared/projects/_dropdown.html.haml b/app/views/shared/projects/_dropdown.html.haml
index 8939aeb6c3a..80432a73e4e 100644
--- a/app/views/shared/projects/_dropdown.html.haml
+++ b/app/views/shared/projects/_dropdown.html.haml
@@ -15,8 +15,11 @@
= link_to filter_projects_path(archived: nil), class: ("is-active" unless params[:archived].present?) do
Hide archived projects
%li
- = link_to filter_projects_path(archived: true), class: ("is-active" if params[:archived].present?) do
+ = link_to filter_projects_path(archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do
Show archived projects
+ %li
+ = link_to filter_projects_path(archived: 'only'), class: ("is-active" if params[:archived] == 'only') do
+ Show archived projects only
- if current_user
%li.divider
%li
diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb
index be4c77503bb..55d8d0c69d1 100644
--- a/app/workers/authorized_projects_worker.rb
+++ b/app/workers/authorized_projects_worker.rb
@@ -4,20 +4,40 @@ class AuthorizedProjectsWorker
# Schedules multiple jobs and waits for them to be completed.
def self.bulk_perform_and_wait(args_list)
+ # Short-circuit: it's more efficient to do small numbers of jobs inline
+ return bulk_perform_inline(args_list) if args_list.size <= 3
+
waiter = Gitlab::JobWaiter.new(args_list.size)
# Point all the bulk jobs at the same JobWaiter. Converts, [[1], [2], [3]]
# into [[1, "key"], [2, "key"], [3, "key"]]
- waiting_args_list = args_list.map { |args| args << waiter.key }
+ waiting_args_list = args_list.map { |args| [*args, waiter.key] }
bulk_perform_async(waiting_args_list)
waiter.wait
end
+ # Schedules multiple jobs to run in sidekiq without waiting for completion
def self.bulk_perform_async(args_list)
Sidekiq::Client.push_bulk('class' => self, 'queue' => sidekiq_options['queue'], 'args' => args_list)
end
+ # Performs multiple jobs directly. Failed jobs will be put into sidekiq so
+ # they can benefit from retries
+ def self.bulk_perform_inline(args_list)
+ failed = []
+
+ args_list.each do |args|
+ begin
+ new.perform(*args)
+ rescue
+ failed << args
+ end
+ end
+
+ bulk_perform_async(failed) if failed.present?
+ end
+
def perform(user_id, notify_key = nil)
user = User.find_by(id: user_id)
diff --git a/app/workers/build_coverage_worker.rb b/app/workers/build_coverage_worker.rb
index f7ae996bb17..cd4af85d047 100644
--- a/app/workers/build_coverage_worker.rb
+++ b/app/workers/build_coverage_worker.rb
@@ -1,6 +1,6 @@
class BuildCoverageWorker
include Sidekiq::Worker
- include BuildQueue
+ include PipelineQueue
def perform(build_id)
Ci::Build.find_by(id: build_id)&.update_coverage
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index 466410bf08c..e2a1b3dcc41 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -1,6 +1,8 @@
class BuildFinishedWorker
include Sidekiq::Worker
- include BuildQueue
+ include PipelineQueue
+
+ enqueue_in group: :processing
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
diff --git a/app/workers/build_hooks_worker.rb b/app/workers/build_hooks_worker.rb
index 9965af935d4..dedaf2835e6 100644
--- a/app/workers/build_hooks_worker.rb
+++ b/app/workers/build_hooks_worker.rb
@@ -1,6 +1,8 @@
class BuildHooksWorker
include Sidekiq::Worker
- include BuildQueue
+ include PipelineQueue
+
+ enqueue_in group: :hooks
def perform(build_id)
Ci::Build.find_by(id: build_id)
diff --git a/app/workers/build_queue_worker.rb b/app/workers/build_queue_worker.rb
index fa9e097e40a..e5ceb9ef715 100644
--- a/app/workers/build_queue_worker.rb
+++ b/app/workers/build_queue_worker.rb
@@ -1,6 +1,8 @@
class BuildQueueWorker
include Sidekiq::Worker
- include BuildQueue
+ include PipelineQueue
+
+ enqueue_in group: :processing
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb
index bf009dfab0f..20ec24bd18a 100644
--- a/app/workers/build_success_worker.rb
+++ b/app/workers/build_success_worker.rb
@@ -1,6 +1,8 @@
class BuildSuccessWorker
include Sidekiq::Worker
- include BuildQueue
+ include PipelineQueue
+
+ enqueue_in group: :processing
def perform(build_id)
Ci::Build.find_by(id: build_id).try do |build|
diff --git a/app/workers/concerns/build_queue.rb b/app/workers/concerns/build_queue.rb
deleted file mode 100644
index cf0ead40a8b..00000000000
--- a/app/workers/concerns/build_queue.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# Concern for setting Sidekiq settings for the various CI build workers.
-module BuildQueue
- extend ActiveSupport::Concern
-
- included do
- sidekiq_options queue: :build
- end
-end
diff --git a/app/workers/concerns/exception_backtrace.rb b/app/workers/concerns/exception_backtrace.rb
new file mode 100644
index 00000000000..ea0f1f8d19b
--- /dev/null
+++ b/app/workers/concerns/exception_backtrace.rb
@@ -0,0 +1,8 @@
+# Concern for enabling a few lines of exception backtraces in Sidekiq
+module ExceptionBacktrace
+ extend ActiveSupport::Concern
+
+ included do
+ sidekiq_options backtrace: 5
+ end
+end
diff --git a/app/workers/concerns/pipeline_queue.rb b/app/workers/concerns/pipeline_queue.rb
index ca3860e1d38..ddf45b91345 100644
--- a/app/workers/concerns/pipeline_queue.rb
+++ b/app/workers/concerns/pipeline_queue.rb
@@ -1,8 +1,18 @@
+##
# Concern for setting Sidekiq settings for the various CI pipeline workers.
+#
module PipelineQueue
extend ActiveSupport::Concern
included do
- sidekiq_options queue: :pipeline
+ sidekiq_options queue: 'pipeline_default'
+ end
+
+ class_methods do
+ def enqueue_in(group:)
+ raise ArgumentError, 'Unspecified queue group!' if group.empty?
+
+ sidekiq_options queue: "pipeline_#{group}"
+ end
end
end
diff --git a/app/workers/expire_job_cache_worker.rb b/app/workers/expire_job_cache_worker.rb
index e383202260d..98a7500bffe 100644
--- a/app/workers/expire_job_cache_worker.rb
+++ b/app/workers/expire_job_cache_worker.rb
@@ -1,6 +1,8 @@
class ExpireJobCacheWorker
include Sidekiq::Worker
- include BuildQueue
+ include PipelineQueue
+
+ enqueue_in group: :cache
def perform(job_id)
job = CommitStatus.joins(:pipeline, :project).find_by(id: job_id)
diff --git a/app/workers/expire_pipeline_cache_worker.rb b/app/workers/expire_pipeline_cache_worker.rb
index 7c02d6cf892..1a0e7f92875 100644
--- a/app/workers/expire_pipeline_cache_worker.rb
+++ b/app/workers/expire_pipeline_cache_worker.rb
@@ -2,6 +2,8 @@ class ExpirePipelineCacheWorker
include Sidekiq::Worker
include PipelineQueue
+ enqueue_in group: :cache
+
def perform(pipeline_id)
pipeline = Ci::Pipeline.find_by(id: pipeline_id)
return unless pipeline
diff --git a/app/workers/group_destroy_worker.rb b/app/workers/group_destroy_worker.rb
index 07e82767b06..bd8e212e928 100644
--- a/app/workers/group_destroy_worker.rb
+++ b/app/workers/group_destroy_worker.rb
@@ -1,6 +1,7 @@
class GroupDestroyWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ include ExceptionBacktrace
def perform(group_id, user_id)
begin
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index c3b58df92c1..48e2da338f6 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -7,8 +7,6 @@ class MergeWorker
current_user = User.find(current_user_id)
merge_request = MergeRequest.find(merge_request_id)
- merge_request.update_column(:merge_jid, jid)
-
MergeRequests::MergeService.new(merge_request.target_project, current_user, params)
.execute(merge_request)
end
diff --git a/app/workers/namespaceless_project_destroy_worker.rb b/app/workers/namespaceless_project_destroy_worker.rb
index 1cfb0be759e..f1cd1769421 100644
--- a/app/workers/namespaceless_project_destroy_worker.rb
+++ b/app/workers/namespaceless_project_destroy_worker.rb
@@ -7,6 +7,7 @@
class NamespacelessProjectDestroyWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ include ExceptionBacktrace
def self.bulk_perform_async(args_list)
Sidekiq::Client.push_bulk('class' => self, 'queue' => sidekiq_options['queue'], 'args' => args_list)
diff --git a/app/workers/pipeline_hooks_worker.rb b/app/workers/pipeline_hooks_worker.rb
index 7e36eacebf8..30a75ec8435 100644
--- a/app/workers/pipeline_hooks_worker.rb
+++ b/app/workers/pipeline_hooks_worker.rb
@@ -2,6 +2,8 @@ class PipelineHooksWorker
include Sidekiq::Worker
include PipelineQueue
+ enqueue_in group: :hooks
+
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
.try(:execute_hooks)
diff --git a/app/workers/pipeline_process_worker.rb b/app/workers/pipeline_process_worker.rb
index 357e4a9a1c3..8c067d05081 100644
--- a/app/workers/pipeline_process_worker.rb
+++ b/app/workers/pipeline_process_worker.rb
@@ -2,6 +2,8 @@ class PipelineProcessWorker
include Sidekiq::Worker
include PipelineQueue
+ enqueue_in group: :processing
+
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
.try(:process!)
diff --git a/app/workers/pipeline_success_worker.rb b/app/workers/pipeline_success_worker.rb
index cc0eb708cf9..cb8bb2ffe75 100644
--- a/app/workers/pipeline_success_worker.rb
+++ b/app/workers/pipeline_success_worker.rb
@@ -2,6 +2,8 @@ class PipelineSuccessWorker
include Sidekiq::Worker
include PipelineQueue
+ enqueue_in group: :processing
+
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id).try do |pipeline|
MergeRequests::MergeWhenPipelineSucceedsService
diff --git a/app/workers/pipeline_update_worker.rb b/app/workers/pipeline_update_worker.rb
index 96c4152c674..5fa399dff4c 100644
--- a/app/workers/pipeline_update_worker.rb
+++ b/app/workers/pipeline_update_worker.rb
@@ -2,6 +2,8 @@ class PipelineUpdateWorker
include Sidekiq::Worker
include PipelineQueue
+ enqueue_in group: :processing
+
def perform(pipeline_id)
Ci::Pipeline.find_by(id: pipeline_id)
.try(:update_status)
diff --git a/app/workers/project_destroy_worker.rb b/app/workers/project_destroy_worker.rb
index a9188b78460..3be7e686609 100644
--- a/app/workers/project_destroy_worker.rb
+++ b/app/workers/project_destroy_worker.rb
@@ -1,6 +1,7 @@
class ProjectDestroyWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ include ExceptionBacktrace
def perform(project_id, user_id, params)
project = Project.find(project_id)
diff --git a/app/workers/project_export_worker.rb b/app/workers/project_export_worker.rb
index 6009aa1b191..f13ac9e5db2 100644
--- a/app/workers/project_export_worker.rb
+++ b/app/workers/project_export_worker.rb
@@ -1,6 +1,7 @@
class ProjectExportWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ include ExceptionBacktrace
sidekiq_options retry: 3
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index 2c2d1e8b91f..00a021abbdc 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -3,6 +3,7 @@ class RepositoryImportWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
+ include ExceptionBacktrace
sidekiq_options status_expiration: StuckImportJobsWorker::IMPORT_JOBS_EXPIRATION
diff --git a/app/workers/stage_update_worker.rb b/app/workers/stage_update_worker.rb
index eef0b11e70b..c301cea5ad6 100644
--- a/app/workers/stage_update_worker.rb
+++ b/app/workers/stage_update_worker.rb
@@ -2,6 +2,8 @@ class StageUpdateWorker
include Sidekiq::Worker
include PipelineQueue
+ enqueue_in group: :processing
+
def perform(stage_id)
Ci::Stage.find_by(id: stage_id).try do |stage|
stage.update_status