diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb index d6af26d949d..b19e9f57450 100644 --- a/app/services/search/global_service.rb +++ b/app/services/search/global_service.rb @@ -23,7 +23,7 @@ module Search def allowed_scopes strong_memoize(:allowed_scopes) do - %w[issues merge_requests milestones] + %w[issues merge_requests milestones users] end end diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml index aaf9b973cda..65c1a30716b 100644 --- a/app/views/search/_category.html.haml +++ b/app/views/search/_category.html.haml @@ -78,3 +78,8 @@ = _("Milestones") %span.badge.badge-pill = limited_count(@search_results.limited_milestones_count) + %li{ class: active_when(@scope == 'users') } + = link_to search_filter_path(scope: 'users') do + Users + %span.badge.badge-pill + = limited_count(@search_results.limited_users_count) diff --git a/app/views/search/results/_user.html.haml b/app/views/search/results/_user.html.haml new file mode 100644 index 00000000000..fd454950ef0 --- /dev/null +++ b/app/views/search/results/_user.html.haml @@ -0,0 +1,8 @@ +%ul.content-list + %li + .avatar-cell.d-none.d-sm-block + = user_avatar(user: user, user_name: user.name, css_class: 'd-none d-sm-inline avatar s40') + .user-info + = link_to user_path(user), class: 'd-none d-sm-inline' do + .item-title= user.name + .cgray= user.to_reference diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 491148ec1a6..28946ceacf6 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -32,6 +32,8 @@ module Gitlab merge_requests.page(page).per(per_page) when 'milestones' milestones.page(page).per(per_page) + when 'users' + users.page(page).per(per_page) else Kaminari.paginate_array([]).page(page).per(per_page) end @@ -71,6 +73,12 @@ module Gitlab end # rubocop: enable CodeReuse/ActiveRecord + # rubocop:disable CodeReuse/ActiveRecord + def limited_users_count + @limited_users_count ||= users.limit(count_limit).count + end + # rubocop:enable CodeReuse/ActiveRecord + def single_commit_result? false end @@ -129,6 +137,12 @@ module Gitlab end # rubocop: enable CodeReuse/ActiveRecord + def users + return [] unless Ability.allowed?(current_user, :read_users_list) + + UsersFinder.new(current_user, search: query).execute + end + def default_scope 'projects' end diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb index d7692181453..9ecf2cab3b5 100644 --- a/spec/features/global_search_spec.rb +++ b/spec/features/global_search_spec.rb @@ -25,4 +25,22 @@ describe 'Global search' do expect(page).to have_selector('.gl-pagination .next') end end + + describe 'users search' do + it 'shows the found user under the Users tab' do + create(:user, username: 'gob_bluth', name: 'Gob Bluth') + + visit dashboard_projects_path + + fill_in 'search', with: 'gob' + click_button 'Go' + + expect(page).to have_content('Users 1') + + click_on('Users 1') + + expect(page).to have_content('Gob Bluth') + expect(page).to have_content('@gob_bluth') + end + end end diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb index 87288baedb0..3d14eebc2c1 100644 --- a/spec/lib/gitlab/search_results_spec.rb +++ b/spec/lib/gitlab/search_results_spec.rb @@ -121,6 +121,18 @@ describe Gitlab::SearchResults do results.objects('issues') end end + + describe '#users' do + it 'returns an empty array when the current_user is not allowed to read users list' do + expect(results.objects('users')).to be_empty + end + + it 'calls the UsersFinder' do + expect(UsersFinder).to receive(:new).with(user, search: 'foo').and_call_original + + results.objects('users') + end + end end it 'does not list issues on private projects' do