Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-12 12:08:48 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-12 12:08:48 +0300
commit7f5e08060f261a63ebf5058a95419da66928173a (patch)
treeb2f77e0445bb28e4eaf156b3d3b71bd46dac8951 /app
parent22864cafe7a3509342c3c880881ade40ce06f752 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/pages/search/show/refresh_counts.js24
-rw-r--r--app/assets/javascripts/search/index.js2
-rw-r--r--app/assets/javascripts/search/store/actions.js56
-rw-r--r--app/assets/javascripts/search/store/mutation_types.js3
-rw-r--r--app/assets/javascripts/search/store/mutations.js11
-rw-r--r--app/assets/javascripts/search/store/state.js1
-rw-r--r--app/assets/javascripts/search/topbar/components/app.vue66
-rw-r--r--app/assets/javascripts/search/topbar/components/scope_tabs.vue73
-rw-r--r--app/assets/javascripts/search/topbar/constants.js14
-rw-r--r--app/assets/javascripts/search/topbar/index.js6
-rw-r--r--app/assets/stylesheets/pages/search.scss12
-rw-r--r--app/controllers/concerns/boards_actions.rb14
-rw-r--r--app/controllers/concerns/multiple_boards_actions.rb1
-rw-r--r--app/controllers/groups/boards_controller.rb23
-rw-r--r--app/controllers/projects/boards_controller.rb22
-rw-r--r--app/graphql/resolvers/board_resolver.rb2
-rw-r--r--app/graphql/resolvers/boards_resolver.rb2
-rw-r--r--app/helpers/boards_helper.rb3
-rw-r--r--app/helpers/projects_helper.rb3
-rw-r--r--app/helpers/search_helper.rb42
-rw-r--r--app/models/board.rb8
-rw-r--r--app/presenters/ci/pipeline_presenter.rb3
-rw-r--r--app/services/boards/list_service.rb8
-rw-r--r--app/services/boards/visits/create_service.rb1
-rw-r--r--app/views/search/_category.html.haml35
-rw-r--r--app/views/search/show.html.haml3
-rw-r--r--app/views/shared/boards/_show.html.haml5
27 files changed, 199 insertions, 244 deletions
diff --git a/app/assets/javascripts/pages/search/show/refresh_counts.js b/app/assets/javascripts/pages/search/show/refresh_counts.js
new file mode 100644
index 00000000000..f3f6312cb7c
--- /dev/null
+++ b/app/assets/javascripts/pages/search/show/refresh_counts.js
@@ -0,0 +1,24 @@
+import axios from '~/lib/utils/axios_utils';
+
+function showCount(el, count) {
+ el.textContent = count;
+ el.classList.remove('hidden');
+}
+
+function refreshCount(el) {
+ const { url } = el.dataset;
+
+ return axios
+ .get(url)
+ .then(({ data }) => showCount(el, data.count))
+ .catch((e) => {
+ // eslint-disable-next-line no-console
+ console.error(`Failed to fetch search count from '${url}'.`, e);
+ });
+}
+
+export default function refreshCounts() {
+ const elements = Array.from(document.querySelectorAll('.js-search-count'));
+
+ return Promise.all(elements.map(refreshCount));
+}
diff --git a/app/assets/javascripts/search/index.js b/app/assets/javascripts/search/index.js
index 0c5d68ab96b..3050b628cd5 100644
--- a/app/assets/javascripts/search/index.js
+++ b/app/assets/javascripts/search/index.js
@@ -1,5 +1,6 @@
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
import Project from '~/pages/projects/project';
+import refreshCounts from '~/pages/search/show/refresh_counts';
import { queryToObject } from '~/lib/utils/url_utility';
import createStore from './store';
import { initTopbar } from './topbar';
@@ -19,5 +20,6 @@ export const initSearchApp = () => {
initSearchSort(store);
setHighlightClass(query.search); // Code Highlighting
+ refreshCounts(); // Other Scope Tab Counts
Project.initRefSwitcher(); // Code Search Branch Picker
};
diff --git a/app/assets/javascripts/search/store/actions.js b/app/assets/javascripts/search/store/actions.js
index e1722d8dee3..bdfe966d990 100644
--- a/app/assets/javascripts/search/store/actions.js
+++ b/app/assets/javascripts/search/store/actions.js
@@ -1,30 +1,9 @@
-import axios from '~/lib/utils/axios_utils';
import Api from '~/api';
import createFlash from '~/flash';
import { __ } from '~/locale';
import { visitUrl, setUrlParams } from '~/lib/utils/url_utility';
import * as types from './mutation_types';
-/* private */
-const getCount = ({ params, state, activeCount }) => {
- const globalSearchCountsPath = '/search/count';
- const url = Api.buildUrl(globalSearchCountsPath);
-
- // count is known for active tab, so return it and skip the Api call
- if (params.scope === state.query?.scope) {
- return { scope: params.scope, count: activeCount };
- }
-
- return axios
- .get(url, { params })
- .then(({ data }) => {
- return { scope: params.scope, count: data.count };
- })
- .catch((e) => {
- throw e;
- });
-};
-
export const fetchGroups = ({ commit }, search) => {
commit(types.REQUEST_GROUPS);
Api.groups(search)
@@ -59,21 +38,6 @@ export const fetchProjects = ({ commit, state }, search) => {
}
};
-export const fetchSearchCounts = ({ commit, state }, { scopeTabs, activeCount }) => {
- commit(types.REQUEST_SEARCH_COUNTS, { scopeTabs, activeCount });
- const promises = scopeTabs.map((scope) =>
- getCount({ params: { ...state.query, scope }, state, activeCount }),
- );
-
- Promise.all(promises)
- .then((data) => {
- commit(types.RECEIVE_SEARCH_COUNTS_SUCCESS, data);
- })
- .catch(() => {
- createFlash({ message: __('There was an error fetching the Search Counts') });
- });
-};
-
export const setQuery = ({ commit }, { key, value }) => {
commit(types.SET_QUERY, { key, value });
};
@@ -82,22 +46,6 @@ export const applyQuery = ({ state }) => {
visitUrl(setUrlParams({ ...state.query, page: null }));
};
-export const resetQuery = ({ state }, snippets = false) => {
- let defaultQuery = {
- page: null,
- state: null,
- confidential: null,
- nav_source: null,
- };
-
- if (snippets) {
- defaultQuery = {
- snippets: true,
- group_id: null,
- project_id: null,
- ...defaultQuery,
- };
- }
-
- visitUrl(setUrlParams({ ...state.query, ...defaultQuery }));
+export const resetQuery = ({ state }) => {
+ visitUrl(setUrlParams({ ...state.query, page: null, state: null, confidential: null }));
};
diff --git a/app/assets/javascripts/search/store/mutation_types.js b/app/assets/javascripts/search/store/mutation_types.js
index 5ba3845bfcc..a6430b53c4f 100644
--- a/app/assets/javascripts/search/store/mutation_types.js
+++ b/app/assets/javascripts/search/store/mutation_types.js
@@ -6,7 +6,4 @@ export const REQUEST_PROJECTS = 'REQUEST_PROJECTS';
export const RECEIVE_PROJECTS_SUCCESS = 'RECEIVE_PROJECTS_SUCCESS';
export const RECEIVE_PROJECTS_ERROR = 'RECEIVE_PROJECTS_ERROR';
-export const REQUEST_SEARCH_COUNTS = 'REQUEST_SEARCH_COUNTS';
-export const RECEIVE_SEARCH_COUNTS_SUCCESS = 'RECEIVE_SEARCH_COUNTS_SUCCESS';
-
export const SET_QUERY = 'SET_QUERY';
diff --git a/app/assets/javascripts/search/store/mutations.js b/app/assets/javascripts/search/store/mutations.js
index 57a577060b3..91d7cf66c8f 100644
--- a/app/assets/javascripts/search/store/mutations.js
+++ b/app/assets/javascripts/search/store/mutations.js
@@ -1,4 +1,3 @@
-import { ALL_SCOPE_TABS } from '~/search/topbar/constants';
import * as types from './mutation_types';
export default {
@@ -24,16 +23,6 @@ export default {
state.fetchingProjects = false;
state.projects = [];
},
- [types.REQUEST_SEARCH_COUNTS](state, { scopeTabs, activeCount }) {
- state.inflatedScopeTabs = scopeTabs.map((tab) => {
- return { ...ALL_SCOPE_TABS[tab], count: tab === state.query?.scope ? activeCount : '' };
- });
- },
- [types.RECEIVE_SEARCH_COUNTS_SUCCESS](state, data) {
- state.inflatedScopeTabs = data.map((tab) => {
- return { ...ALL_SCOPE_TABS[tab.scope], count: tab.count };
- });
- },
[types.SET_QUERY](state, { key, value }) {
state.query[key] = value;
},
diff --git a/app/assets/javascripts/search/store/state.js b/app/assets/javascripts/search/store/state.js
index 9528b0400a4..9a0d61d0b93 100644
--- a/app/assets/javascripts/search/store/state.js
+++ b/app/assets/javascripts/search/store/state.js
@@ -4,6 +4,5 @@ const createState = ({ query }) => ({
fetchingGroups: false,
projects: [],
fetchingProjects: false,
- inflatedScopeTabs: [],
});
export default createState;
diff --git a/app/assets/javascripts/search/topbar/components/app.vue b/app/assets/javascripts/search/topbar/components/app.vue
index c858f1fcc42..639cff591c3 100644
--- a/app/assets/javascripts/search/topbar/components/app.vue
+++ b/app/assets/javascripts/search/topbar/components/app.vue
@@ -3,7 +3,6 @@ import { mapState, mapActions } from 'vuex';
import { GlForm, GlSearchBoxByType, GlButton } from '@gitlab/ui';
import GroupFilter from './group_filter.vue';
import ProjectFilter from './project_filter.vue';
-import ScopeTabs from './scope_tabs.vue';
export default {
name: 'GlobalSearchTopbar',
@@ -13,7 +12,6 @@ export default {
GroupFilter,
ProjectFilter,
GlButton,
- ScopeTabs,
},
props: {
groupInitialData: {
@@ -26,16 +24,6 @@ export default {
required: false,
default: () => ({}),
},
- scopeTabs: {
- type: Array,
- required: false,
- default: () => [],
- },
- count: {
- type: String,
- required: false,
- default: '',
- },
},
computed: {
...mapState(['query']),
@@ -50,9 +38,6 @@ export default {
showFilters() {
return !this.query.snippets || this.query.snippets === 'false';
},
- showScopeTabs() {
- return this.query.search;
- },
},
methods: {
...mapActions(['applyQuery', 'setQuery']),
@@ -61,31 +46,28 @@ export default {
</script>
<template>
- <section>
- <gl-form class="search-page-form" @submit.prevent="applyQuery">
- <section class="gl-lg-display-flex gl-align-items-flex-end">
- <div class="gl-flex-fill-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
- <label>{{ __('What are you searching for?') }}</label>
- <gl-search-box-by-type
- id="dashboard_search"
- v-model="search"
- name="search"
- :placeholder="__(`Search for projects, issues, etc.`)"
- />
- </div>
- <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
- <label class="gl-display-block">{{ __('Group') }}</label>
- <group-filter :initial-data="groupInitialData" />
- </div>
- <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
- <label class="gl-display-block">{{ __('Project') }}</label>
- <project-filter :initial-data="projectInitialData" />
- </div>
- <gl-button class="btn-search gl-lg-ml-2" variant="success" type="submit">{{
- __('Search')
- }}</gl-button>
- </section>
- </gl-form>
- <scope-tabs v-if="showScopeTabs" :scope-tabs="scopeTabs" :count="count" />
- </section>
+ <gl-form class="search-page-form" @submit.prevent="applyQuery">
+ <section class="gl-lg-display-flex gl-align-items-flex-end">
+ <div class="gl-flex-fill-1 gl-mb-4 gl-lg-mb-0 gl-lg-mr-2">
+ <label>{{ __('What are you searching for?') }}</label>
+ <gl-search-box-by-type
+ id="dashboard_search"
+ v-model="search"
+ name="search"
+ :placeholder="__(`Search for projects, issues, etc.`)"
+ />
+ </div>
+ <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
+ <label class="gl-display-block">{{ __('Group') }}</label>
+ <group-filter :initial-data="groupInitialData" />
+ </div>
+ <div v-if="showFilters" class="gl-mb-4 gl-lg-mb-0 gl-lg-mx-2">
+ <label class="gl-display-block">{{ __('Project') }}</label>
+ <project-filter :initial-data="projectInitialData" />
+ </div>
+ <gl-button class="btn-search gl-lg-ml-2" variant="success" type="submit">{{
+ __('Search')
+ }}</gl-button>
+ </section>
+ </gl-form>
</template>
diff --git a/app/assets/javascripts/search/topbar/components/scope_tabs.vue b/app/assets/javascripts/search/topbar/components/scope_tabs.vue
deleted file mode 100644
index 83264a1b411..00000000000
--- a/app/assets/javascripts/search/topbar/components/scope_tabs.vue
+++ /dev/null
@@ -1,73 +0,0 @@
-<script>
-import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
-
-export default {
- name: 'ScopeTabs',
- components: {
- GlTabs,
- GlTab,
- GlBadge,
- },
- props: {
- scopeTabs: {
- type: Array,
- required: true,
- },
- count: {
- type: String,
- required: false,
- default: '',
- },
- },
- computed: {
- ...mapState(['query', 'inflatedScopeTabs']),
- },
- created() {
- this.fetchSearchCounts({ scopeTabs: this.scopeTabs, activeCount: this.count });
- },
- methods: {
- ...mapActions(['fetchSearchCounts', 'setQuery', 'resetQuery']),
- handleTabChange(scope) {
- this.setQuery({ key: 'scope', value: scope });
- this.resetQuery(scope === 'snippet_titles');
- },
- isTabActive(scope) {
- return scope === this.query.scope;
- },
- },
-};
-</script>
-
-<template>
- <div>
- <gl-tabs
- content-class="gl-p-0"
- nav-class="search-filter search-nav-tabs gl-display-flex gl-overflow-x-auto"
- >
- <gl-tab
- v-for="tab in inflatedScopeTabs"
- :key="tab.scope"
- class="gl-display-flex"
- :active="isTabActive(tab.scope)"
- :data-testid="`tab-${tab.scope}`"
- :title-link-attributes="{ 'data-qa-selector': tab.qaSelector }"
- title-link-class="gl-white-space-nowrap"
- @click="handleTabChange(tab.scope)"
- >
- <template #title>
- <span data-testid="tab-title"> {{ tab.title }} </span>
- <gl-badge
- v-show="tab.count"
- :data-scope="tab.scope"
- :data-testid="`badge-${tab.scope}`"
- :variant="isTabActive(tab.scope) ? 'neutral' : 'muted'"
- size="sm"
- >
- {{ tab.count }}
- </gl-badge>
- </template>
- </gl-tab>
- </gl-tabs>
- </div>
-</template>
diff --git a/app/assets/javascripts/search/topbar/constants.js b/app/assets/javascripts/search/topbar/constants.js
index ec65667cde3..3944b2c8374 100644
--- a/app/assets/javascripts/search/topbar/constants.js
+++ b/app/assets/javascripts/search/topbar/constants.js
@@ -19,17 +19,3 @@ export const PROJECT_DATA = {
selectedDisplayValue: 'name_with_namespace',
itemsDisplayValue: 'name_with_namespace',
};
-
-export const ALL_SCOPE_TABS = {
- blobs: { scope: 'blobs', title: __('Code'), qaSelector: 'code_tab' },
- issues: { scope: 'issues', title: __('Issues') },
- merge_requests: { scope: 'merge_requests', title: __('Merge requests') },
- milestones: { scope: 'milestones', title: __('Milestones') },
- notes: { scope: 'notes', title: __('Comments') },
- wiki_blobs: { scope: 'wiki_blobs', title: __('Wiki') },
- commits: { scope: 'commits', title: __('Commits') },
- epics: { scope: 'epics', title: __('Epics') },
- users: { scope: 'users', title: __('Users') },
- snippet_titles: { scope: 'snippet_titles', title: __('Titles and Descriptions') },
- projects: { scope: 'projects', title: __('Projects'), qaSelector: 'projects_tab' },
-};
diff --git a/app/assets/javascripts/search/topbar/index.js b/app/assets/javascripts/search/topbar/index.js
index f9564b5cfbb..87316e10e8d 100644
--- a/app/assets/javascripts/search/topbar/index.js
+++ b/app/assets/javascripts/search/topbar/index.js
@@ -11,12 +11,10 @@ export const initTopbar = (store) => {
return false;
}
- let { groupInitialData, projectInitialData, scopeTabs } = el.dataset;
- const { count } = el.dataset;
+ let { groupInitialData, projectInitialData } = el.dataset;
groupInitialData = JSON.parse(groupInitialData);
projectInitialData = JSON.parse(projectInitialData);
- scopeTabs = JSON.parse(scopeTabs);
return new Vue({
el,
@@ -26,8 +24,6 @@ export const initTopbar = (store) => {
props: {
groupInitialData,
projectInitialData,
- scopeTabs,
- count,
},
});
},
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index a1961d24bbf..cd99c667001 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -2,7 +2,6 @@ $search-dropdown-max-height: 400px;
$search-avatar-size: 16px;
$search-sidebar-min-width: 240px;
$search-sidebar-max-width: 300px;
-$search-topbar-min-height: 111px;
.search-results {
.search-result-row {
@@ -20,12 +19,6 @@ $search-topbar-min-height: 111px;
}
}
-.search-topbar {
- @include media-breakpoint-up(md) {
- min-height: $search-topbar-min-height;
- }
-}
-
.search-sidebar {
@include media-breakpoint-up(md) {
min-width: $search-sidebar-min-width;
@@ -33,11 +26,6 @@ $search-topbar-min-height: 111px;
}
}
-.search-nav-tabs {
- overflow-y: hidden;
- flex-wrap: nowrap;
-}
-
.search form:hover,
.file-finder-input:hover,
.issuable-search-form:hover,
diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb
index b382e338a78..79e6f027c2f 100644
--- a/app/controllers/concerns/boards_actions.rb
+++ b/app/controllers/concerns/boards_actions.rb
@@ -34,16 +34,26 @@ module BoardsActions
def boards
strong_memoize(:boards) do
- Boards::ListService.new(parent, current_user).execute
+ existing_boards = boards_finder.execute
+ if existing_boards.any?
+ existing_boards
+ else
+ # if no board exists, create one
+ [board_create_service.execute.payload]
+ end
end
end
def board
strong_memoize(:board) do
- boards.find(params[:id])
+ board_finder.execute.first
end
end
+ def board_type
+ board_klass.to_type
+ end
+
def serializer
BoardSerializer.new(current_user: current_user)
end
diff --git a/app/controllers/concerns/multiple_boards_actions.rb b/app/controllers/concerns/multiple_boards_actions.rb
index 370b8c72bfe..5206f5759d8 100644
--- a/app/controllers/concerns/multiple_boards_actions.rb
+++ b/app/controllers/concerns/multiple_boards_actions.rb
@@ -65,6 +65,7 @@ module MultipleBoardsActions
private
def redirect_to_recent_board
+ return unless board_type == Board.to_type
return if request.format.json? || !parent.multiple_issue_boards_available? || !latest_visited_board
redirect_to board_path(latest_visited_board.board)
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index b34b826a695..fa109021b7d 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -3,6 +3,7 @@
class Groups::BoardsController < Groups::ApplicationController
include BoardsActions
include RecordUserLastActivity
+ include Gitlab::Utils::StrongMemoize
before_action :authorize_read_board!, only: [:index, :show]
before_action :assign_endpoint_vars
@@ -14,6 +15,28 @@ class Groups::BoardsController < Groups::ApplicationController
private
+ def board_klass
+ Board
+ end
+
+ def boards_finder
+ strong_memoize :boards_finder do
+ Boards::ListService.new(parent, current_user)
+ end
+ end
+
+ def board_finder
+ strong_memoize :board_finder do
+ Boards::ListService.new(parent, current_user, board_id: params[:id])
+ end
+ end
+
+ def board_create_service
+ strong_memoize :board_create_service do
+ Boards::CreateService.new(parent, current_user)
+ end
+ end
+
def assign_endpoint_vars
@boards_endpoint = group_boards_path(group)
@namespace_path = group.to_param
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index 51c9bf3699a..d2e5d319f96 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -15,6 +15,28 @@ class Projects::BoardsController < Projects::ApplicationController
private
+ def board_klass
+ Board
+ end
+
+ def boards_finder
+ strong_memoize :boards_finder do
+ Boards::ListService.new(parent, current_user)
+ end
+ end
+
+ def board_finder
+ strong_memoize :board_finder do
+ Boards::ListService.new(parent, current_user, board_id: params[:id])
+ end
+ end
+
+ def board_create_service
+ strong_memoize :board_create_service do
+ Boards::CreateService.new(parent, current_user)
+ end
+ end
+
def assign_endpoint_vars
@boards_endpoint = project_boards_path(project)
@bulk_issues_path = bulk_update_project_issues_path(project)
diff --git a/app/graphql/resolvers/board_resolver.rb b/app/graphql/resolvers/board_resolver.rb
index 582707cc1e4..2c2922c3fbf 100644
--- a/app/graphql/resolvers/board_resolver.rb
+++ b/app/graphql/resolvers/board_resolver.rb
@@ -13,7 +13,7 @@ module Resolvers
def resolve(id: nil)
return unless parent
- ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false).first
+ ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute.first
rescue ActiveRecord::RecordNotFound
nil
end
diff --git a/app/graphql/resolvers/boards_resolver.rb b/app/graphql/resolvers/boards_resolver.rb
index cdb15dc8f37..be2f22175dc 100644
--- a/app/graphql/resolvers/boards_resolver.rb
+++ b/app/graphql/resolvers/boards_resolver.rb
@@ -16,7 +16,7 @@ module Resolvers
return Board.none unless parent
- ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute(create_default_board: false)
+ ::Boards::ListService.new(parent, context[:current_user], board_id: extract_board_id(id)).execute
rescue ActiveRecord::RecordNotFound
Board.none
end
diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb
index c827fb4dd95..80b6c62cb0b 100644
--- a/app/helpers/boards_helper.rb
+++ b/app/helpers/boards_helper.rb
@@ -21,7 +21,8 @@ module BoardsHelper
group_id: @group&.id,
labels_filter_base_path: build_issue_link_base,
labels_fetch_path: labels_fetch_path,
- labels_manage_path: labels_manage_path
+ labels_manage_path: labels_manage_path,
+ board_type: board.to_type
}
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 0d43032a184..a2e9952f350 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -511,8 +511,7 @@ module ProjectsHelper
commits: :download_code,
merge_requests: :read_merge_request,
notes: [:read_merge_request, :download_code, :read_issue, :read_snippet],
- members: :read_project_member,
- wiki_blobs: :read_wiki
+ members: :read_project_member
)
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 1a5b578cc75..a7acc0cd7db 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -1,8 +1,16 @@
# frozen_string_literal: true
module SearchHelper
- PROJECT_SEARCH_TABS = %i{blobs issues merge_requests milestones notes wiki_blobs commits}.freeze
- BASIC_SEARCH_TABS = %i{projects issues merge_requests milestones}.freeze
+ SEARCH_GENERIC_PARAMS = [
+ :search,
+ :scope,
+ :project_id,
+ :group_id,
+ :repository_ref,
+ :snippets,
+ :sort,
+ :force_search_results
+ ].freeze
def search_autocomplete_opts(term)
return unless current_user
@@ -284,19 +292,27 @@ module SearchHelper
Sanitize.clean(str)
end
- def search_nav_tabs
- return [:snippet_titles] if !@project && @show_snippets
+ def search_filter_link(scope, label, data: {}, search: {})
+ search_params = params
+ .merge(search)
+ .merge({ scope: scope })
+ .permit(SEARCH_GENERIC_PARAMS)
- tabs =
- if @project
- PROJECT_SEARCH_TABS.select { |tab| project_search_tabs?(tab) }
- else
- BASIC_SEARCH_TABS.dup
- end
-
- tabs << :users if show_user_search_tab?
+ if @scope == scope
+ li_class = 'active'
+ count = @search_results.formatted_count(scope)
+ else
+ badge_class = 'js-search-count hidden'
+ badge_data = { url: search_count_path(search_params) }
+ end
- tabs
+ content_tag :li, class: li_class, data: data do
+ link_to search_path(search_params) do
+ concat label
+ concat ' '
+ concat content_tag(:span, count, class: ['badge badge-pill', badge_class], data: badge_data)
+ end
+ end
end
def search_filter_input_options(type, placeholder = _('Search or filter results...'))
diff --git a/app/models/board.rb b/app/models/board.rb
index a57d101b30a..85fad762ebe 100644
--- a/app/models/board.rb
+++ b/app/models/board.rb
@@ -44,6 +44,14 @@ class Board < ApplicationRecord
def scoped?
false
end
+
+ def self.to_type
+ name.demodulize
+ end
+
+ def to_type
+ self.class.to_type
+ end
end
Board.prepend_if_ee('EE::Board')
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index f3bb63b31c3..0c68c33cbbe 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -11,6 +11,9 @@ module Ci
{ unknown_failure: 'Unknown pipeline failure!',
config_error: 'CI/CD YAML configuration error!',
external_validation_failure: 'External pipeline validation failed!',
+ activity_limit_exceeded: 'Pipeline activity limit exceeded!',
+ size_limit_exceeded: 'Pipeline size limit exceeded!',
+ job_activity_limit_exceeded: 'Pipeline job activity limit exceeded!',
deployments_limit_exceeded: 'Pipeline deployments limit exceeded!' }
end
diff --git a/app/services/boards/list_service.rb b/app/services/boards/list_service.rb
index 729bca6580e..80ceb91f56d 100644
--- a/app/services/boards/list_service.rb
+++ b/app/services/boards/list_service.rb
@@ -2,9 +2,7 @@
module Boards
class ListService < Boards::BaseService
- def execute(create_default_board: true)
- create_board! if create_default_board && parent.boards.empty?
-
+ def execute
find_boards
end
@@ -18,10 +16,6 @@ module Boards
parent.boards.first_board
end
- def create_board!
- Boards::CreateService.new(parent, current_user).execute
- end
-
def find_boards
found =
if parent.multiple_issue_boards_available?
diff --git a/app/services/boards/visits/create_service.rb b/app/services/boards/visits/create_service.rb
index e2adf755511..428ed1a8bcc 100644
--- a/app/services/boards/visits/create_service.rb
+++ b/app/services/boards/visits/create_service.rb
@@ -5,6 +5,7 @@ module Boards
class CreateService < Boards::BaseService
def execute(board)
return unless current_user && Gitlab::Database.read_write?
+ return unless board.is_a?(Board) # other board types do not support board visits yet
if parent.is_a?(Group)
BoardGroupRecentVisit.visited!(current_user, board)
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
new file mode 100644
index 00000000000..f094a6f5e3b
--- /dev/null
+++ b/app/views/search/_category.html.haml
@@ -0,0 +1,35 @@
+- users = capture_haml do
+ - if show_user_search_tab?
+ = search_filter_link 'users', _("Users")
+
+.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
+ .fade-left= sprite_icon('chevron-lg-left', size: 12)
+ .fade-right= sprite_icon('chevron-lg-right', size: 12)
+ %ul.nav-links.search-filter.scrolling-tabs.nav.nav-tabs
+ - if @project
+ - if project_search_tabs?(:blobs)
+ = search_filter_link 'blobs', _("Code"), data: { qa_selector: 'code_tab' }
+ - if project_search_tabs?(:issues)
+ = search_filter_link 'issues', _("Issues")
+ - if project_search_tabs?(:merge_requests)
+ = search_filter_link 'merge_requests', _("Merge requests")
+ - if project_search_tabs?(:milestones)
+ = search_filter_link 'milestones', _("Milestones")
+ - if project_search_tabs?(:notes)
+ = search_filter_link 'notes', _("Comments")
+ - if project_search_tabs?(:wiki)
+ = search_filter_link 'wiki_blobs', _("Wiki")
+ - if project_search_tabs?(:commits)
+ = search_filter_link 'commits', _("Commits")
+ = users
+
+ - elsif @show_snippets
+ = search_filter_link 'snippet_titles', _("Titles and Descriptions"), search: { snippets: true, group_id: nil, project_id: nil }
+ - else
+ = search_filter_link 'projects', _("Projects"), data: { qa_selector: 'projects_tab' }
+ = search_filter_link 'issues', _("Issues")
+ = search_filter_link 'merge_requests', _("Merge requests")
+ = search_filter_link 'milestones', _("Milestones")
+ = render_if_exists 'search/epics_filter_link'
+ = render_if_exists 'search/category_elasticsearch'
+ = users
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index c0dca74bb8f..d54310bfa82 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -16,6 +16,7 @@
= render_if_exists 'search/form_elasticsearch', attrs: { class: 'mb-2 mb-sm-0 align-self-center' }
.gl-mt-3
- #js-search-topbar.search-topbar{ data: { "group-initial-data": @group.to_json, "project-initial-data": project_attributes.to_json, "scope-tabs": search_nav_tabs.to_json, count: @search_results&.formatted_count(@scope) } }
+ #js-search-topbar{ data: { "group-initial-data": @group.to_json, "project-initial-data": project_attributes.to_json } }
- if @search_term
+ = render 'search/category'
= render 'search/results'
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index e4222d8a4fe..ababbdc7eb9 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -6,7 +6,10 @@
- @no_breadcrumb_container = true
- @no_container = true
- @content_class = "issue-boards-content js-focus-mode-board"
-- breadcrumb_title _("Issue Boards")
+- if board.to_type == "EpicBoard"
+ - breadcrumb_title _("Epic Boards")
+- else
+ - breadcrumb_title _("Issue Boards")
- page_title("#{board.name}", _("Boards"))
- add_page_specific_style 'page_bundles/boards'