diff --git a/app/assets/javascripts/lib/utils/grammar.js b/app/assets/javascripts/lib/utils/grammar.js index 18f9e2ed846..b1f38429369 100644 --- a/app/assets/javascripts/lib/utils/grammar.js +++ b/app/assets/javascripts/lib/utils/grammar.js @@ -20,18 +20,22 @@ export const toNounSeriesText = items => { if (items.length === 0) { return ''; } else if (items.length === 1) { - return items[0]; + return sprintf(s__(`nounSeries|%{item}`), { item: items[0] }, false); } else if (items.length === 2) { - return sprintf(s__('nounSeries|%{firstItem} and %{lastItem}'), { - firstItem: items[0], - lastItem: items[1], - }); + return sprintf( + s__('nounSeries|%{firstItem} and %{lastItem}'), + { + firstItem: items[0], + lastItem: items[1], + }, + false, + ); } return items.reduce((item, nextItem, idx) => idx === items.length - 1 - ? sprintf(s__('nounSeries|%{item}, and %{lastItem}'), { item, lastItem: nextItem }) - : sprintf(s__('nounSeries|%{item}, %{nextItem}'), { item, nextItem }), + ? sprintf(s__('nounSeries|%{item}, and %{lastItem}'), { item, lastItem: nextItem }, false) + : sprintf(s__('nounSeries|%{item}, %{nextItem}'), { item, nextItem }, false), ); }; diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb index dd8b2f29425..5f24b15156c 100644 --- a/app/finders/group_projects_finder.rb +++ b/app/finders/group_projects_finder.rb @@ -19,6 +19,9 @@ # personal: boolean # search: string # non_archived: boolean +# with_issues_enabled: boolean +# with_merge_requests_enabled: boolean +# min_access_level: int # class GroupProjectsFinder < ProjectsFinder DEFAULT_PROJECTS_LIMIT = 100 @@ -42,6 +45,12 @@ class GroupProjectsFinder < ProjectsFinder private + def filter_projects(collection) + projects = super + projects = by_feature_availability(projects) + projects + end + def limit(collection) limit = options[:limit] @@ -49,35 +58,37 @@ class GroupProjectsFinder < ProjectsFinder end def init_collection - projects = if current_user - collection_with_user - else - collection_without_user - end + projects = + if only_shared? + [shared_projects] + elsif only_owned? + [owned_projects] + else + [owned_projects, shared_projects] + end + + projects.map! do |project_relation| + filter_by_visibility(project_relation) + end union(projects) end - def collection_with_user - if only_shared? - [shared_projects.public_or_visible_to_user(current_user)] - elsif only_owned? - [owned_projects.public_or_visible_to_user(current_user)] - else - [ - owned_projects.public_or_visible_to_user(current_user), - shared_projects.public_or_visible_to_user(current_user) - ] - end + def by_feature_availability(projects) + projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled].present? + projects = projects.with_merge_requests_available_for_user(current_user) if params[:with_merge_requests_enabled].present? + projects end - def collection_without_user - if only_shared? - [shared_projects.public_only] - elsif only_owned? - [owned_projects.public_only] + def filter_by_visibility(relation) + if current_user + if min_access_level? + relation.visible_to_user_and_access_level(current_user, params[:min_access_level]) + else + relation.public_or_visible_to_user(current_user) + end else - [shared_projects.public_only, owned_projects.public_only] + relation.public_only end end diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index f5a4889b4bb..081f9dca6bc 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -1,7 +1,7 @@ = form_tag project_compare_index_path(@project), method: :post, class: 'form-inline js-requires-input js-signature-container', data: { 'signatures-path' => signatures_namespace_project_compare_index_path } do - if params[:to] && params[:from] .compare-switch-container - = link_to icon('exchange'), { from: params[:to], to: params[:from] }, class: 'commits-compare-switch has-tooltip btn btn-white', title: 'Swap revisions' + = link_to sprite_icon('substitute'), { from: params[:to], to: params[:from] }, class: 'commits-compare-switch has-tooltip btn btn-white', title: 'Swap revisions' .form-group.dropdown.compare-form-group.to.js-compare-to-dropdown .input-group.inline-input-group %span.input-group-prepend diff --git a/changelogs/unreleased/grp-finder-code-refactor.yml b/changelogs/unreleased/grp-finder-code-refactor.yml new file mode 100644 index 00000000000..a80f2286559 --- /dev/null +++ b/changelogs/unreleased/grp-finder-code-refactor.yml @@ -0,0 +1,5 @@ +--- +title: Move filter code into finder +merge_request: 34470 +author: Ravishankar +type: other diff --git a/changelogs/unreleased/justin_ho-replace-fa-exchange-icon.yml b/changelogs/unreleased/justin_ho-replace-fa-exchange-icon.yml new file mode 100644 index 00000000000..f593421e0c6 --- /dev/null +++ b/changelogs/unreleased/justin_ho-replace-fa-exchange-icon.yml @@ -0,0 +1,5 @@ +--- +title: Replace FA exchange icon with GitLab SVG +merge_request: 35634 +author: +type: changed diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md index 8e84f4cb4e0..9d3ff3c5f5b 100644 --- a/doc/ci/variables/predefined_variables.md +++ b/doc/ci/variables/predefined_variables.md @@ -23,6 +23,9 @@ future GitLab releases.** You can add a command to your `.gitlab-ci.yml` file to [output the values of all variables available for a job](README.md#list-all-environment-variables). +Kubernetes-specific environment variables are detailed in the +[Kubernetes deployment variables](../../user/project/clusters/index.md#deployment-variables) section. + | Variable | GitLab | Runner | Description | |-----------------------------------------------|--------|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `CHAT_CHANNEL` | 10.6 | all | Source chat channel which triggered the [ChatOps](../chatops/README.md) command | diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 4671e82ab66..85c0c6bfa17 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -76,9 +76,6 @@ module API params: project_finder_params, options: finder_options ).execute - projects = projects.with_issues_available_for_user(current_user) if params[:with_issues_enabled] - projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled] - projects = projects.visible_to_user_and_access_level(current_user, params[:min_access_level]) if params[:min_access_level] projects = reorder_projects(projects) paginate(projects) end diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index d8569dea4f1..101a8ed9a4b 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -528,6 +528,8 @@ module API def project_finder_params_ce finder_params = project_finder_params_visibility_ce + finder_params[:with_issues_enabled] = true if params[:with_issues_enabled].present? + finder_params[:with_merge_requests_enabled] = true if params[:with_merge_requests_enabled].present? finder_params[:without_deleted] = true finder_params[:search] = params[:search] if params[:search] finder_params[:search_namespaces] = true if params[:search_namespaces].present? diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 05c403d4025..b026af14642 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -27740,6 +27740,9 @@ msgstr "" msgid "nounSeries|%{firstItem} and %{lastItem}" msgstr "" +msgid "nounSeries|%{item}" +msgstr "" + msgid "nounSeries|%{item}, %{nextItem}" msgstr "" diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb index 208e2fa2a41..04f0f34cbbb 100644 --- a/qa/qa/page/project/issue/show.rb +++ b/qa/qa/page/project/issue/show.rb @@ -67,7 +67,7 @@ module QA # attachment option should be an absolute path def comment(text, attachment: nil, filter: :all_activities) method("select_#{filter}_filter").call - fill_element :comment_input, text + fill_element :comment_input, "#{text}\n" unless attachment.nil? QA::Page::Component::Dropzone.new(self, '.new-note') diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb index 7499461ad8f..14f2bb017c6 100644 --- a/spec/finders/group_projects_finder_spec.rb +++ b/spec/finders/group_projects_finder_spec.rb @@ -46,6 +46,18 @@ RSpec.describe GroupProjectsFinder do context 'without subgroups projects' do it { is_expected.to match_array([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) } end + + context "with min access level" do + let!(:shared_project_4) { create(:project, :internal, path: '8') } + + before do + shared_project_4.project_group_links.create(group_access: Gitlab::Access::REPORTER, group: group) + end + + let(:params) { { min_access_level: Gitlab::Access::MAINTAINER } } + + it { is_expected.to match_array([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) } + end end end @@ -171,6 +183,38 @@ RSpec.describe GroupProjectsFinder do end end + describe 'feature availability' do + let!(:project_with_issues_disabled) { create(:project, :issues_disabled, :internal, path: '9') } + let!(:project_with_merge_request_disabled) { create(:project, :merge_requests_disabled, :internal, path: '10') } + + before do + project_with_issues_disabled.project_group_links.create!(group_access: Gitlab::Access::REPORTER, group: group) + project_with_merge_request_disabled.project_group_links.create!(group_access: Gitlab::Access::REPORTER, group: group) + end + + context 'without issues and merge request enabled' do + it { is_expected.to match_array([public_project, shared_project_1, shared_project_3, project_with_issues_disabled, project_with_merge_request_disabled]) } + end + + context 'with issues enabled' do + let(:params) { { with_issues_enabled: true } } + + it { is_expected.to match_array([public_project, shared_project_1, shared_project_3, project_with_merge_request_disabled]) } + end + + context 'with merge request enabled' do + let(:params) { { with_merge_requests_enabled: true } } + + it { is_expected.to match_array([public_project, shared_project_1, shared_project_3, project_with_issues_disabled]) } + end + + context 'with issues and merge request enabled' do + let(:params) { { with_merge_requests_enabled: true, with_issues_enabled: true } } + + it { is_expected.to match_array([public_project, shared_project_1, shared_project_3]) } + end + end + describe 'limiting' do context 'without limiting' do it 'returns all projects' do diff --git a/spec/frontend/lib/utils/grammar_spec.js b/spec/frontend/lib/utils/grammar_spec.js index 377b2ffb48c..7f2431af7ed 100644 --- a/spec/frontend/lib/utils/grammar_spec.js +++ b/spec/frontend/lib/utils/grammar_spec.js @@ -7,27 +7,27 @@ describe('utils/grammar', () => { }); it('with single item returns item', () => { - const items = ['Lorem Ipsum']; + const items = ['Lorem & Ipsum']; expect(grammar.toNounSeriesText(items)).toBe(items[0]); }); it('with 2 items returns item1 and item2', () => { - const items = ['Dolar', 'Sit Amit']; + const items = ['Dolar', 'Sit & Amit']; expect(grammar.toNounSeriesText(items)).toBe(`${items[0]} and ${items[1]}`); }); it('with 3 items returns comma separated series', () => { - const items = ['Lorem', 'Ipsum', 'dolar']; - const expected = 'Lorem, Ipsum, and dolar'; + const items = ['Lorem', 'Ipsum', 'Sit & Amit']; + const expected = 'Lorem, Ipsum, and Sit & Amit'; expect(grammar.toNounSeriesText(items)).toBe(expected); }); it('with 6 items returns comma separated series', () => { - const items = ['Lorem', 'ipsum', 'dolar', 'sit', 'amit', 'consectetur']; - const expected = 'Lorem, ipsum, dolar, sit, amit, and consectetur'; + const items = ['Lorem', 'ipsum', 'dolar', 'sit', 'amit', 'consectetur & adipiscing']; + const expected = 'Lorem, ipsum, dolar, sit, amit, and consectetur & adipiscing'; expect(grammar.toNounSeriesText(items)).toBe(expected); });