From 9a4ef7e7eb1fe73938578d82c2662913e3d51ad6 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 26 Aug 2014 23:32:41 +0300 Subject: Search results libraries added Gitlab::SearchResults and Gitlab::ProjectSearchResults are libraries we are going to use to get search results based on query, enitity type and pagination. It will allow us to get only issues from project #23 where title or description includes 'foo'. Ex: search_results = Gitlab::ProjectSearchResults.new(project.id, 'foo', 'issues') search_results.objects => # [, ] search_results.issues_count => 2 search_results.total_count => 12 (it includes results from comments and merge requests too) Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/project_search_results.rb | 62 +++++++++++++++++++++++++++++ lib/gitlab/search_results.rb | 75 ++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 lib/gitlab/project_search_results.rb create mode 100644 lib/gitlab/search_results.rb diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb new file mode 100644 index 00000000000..b0a0bee1c81 --- /dev/null +++ b/lib/gitlab/project_search_results.rb @@ -0,0 +1,62 @@ +module Gitlab + class ProjectSearchResults < SearchResults + attr_reader :project, :repository_ref + + def initialize(project_id, query, scope = nil, page = nil, repository_ref = nil) + @project = Project.find(project_id) + @repository_ref = repository_ref + @page = page + @query = Shellwords.shellescape(query) if query.present? + @scope = scope + + unless %w(blobs notes issues merge_requests).include?(@scope) + @scope = default_scope + end + end + + def objects + case scope + when 'notes' + notes.page(page).per(per_page) + when 'blobs' + Kaminari.paginate_array(blobs).page(page).per(per_page) + else + super + end + end + + def total_count + @total_count ||= issues_count + merge_requests_count + blobs_count + notes_count + end + + def blobs_count + @blobs_count ||= blobs.count + end + + def notes_count + @notes_count ||= notes.count + end + + private + + def blobs + if project.empty_repo? + [] + else + project.repository.search_files(query, repository_ref) + end + end + + def notes + Note.where(project_id: limit_project_ids).search(query).order('updated_at DESC') + end + + def default_scope + 'blobs' + end + + def limit_project_ids + [project.id] + end + end +end diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb new file mode 100644 index 00000000000..1325d542a0f --- /dev/null +++ b/lib/gitlab/search_results.rb @@ -0,0 +1,75 @@ +module Gitlab + class SearchResults + attr_reader :scope, :objects, :query, :page + + # Limit search results by passed project ids + # It allows us to search only for projects user has access to + attr_reader :limit_project_ids + + def initialize(limit_project_ids, query, scope = nil, page = nil) + @limit_project_ids = limit_project_ids || Project.all + @page = page + @query = Shellwords.shellescape(query) if query.present? + @scope = scope + + unless %w(projects issues merge_requests).include?(@scope) + @scope = default_scope + end + end + + def objects + case scope + when 'projects' + projects.page(page).per(per_page) + when 'issues' + issues.page(page).per(per_page) + when 'merge_requests' + merge_requests.page(page).per(per_page) + else + Kaminari.paginate_array([]).page(page).per(per_page) + end + end + + def total_count + @total_count ||= projects_count + issues_count + merge_requests_count + end + + def projects_count + @projects_count ||= projects.count + end + + def issues_count + @issues_count ||= issues.count + end + + def merge_requests_count + @merge_requests_count ||= merge_requests.count + end + + def empty? + total_count.zero? + end + + private + + def projects + Project.where(id: limit_project_ids).search(query) + end + + def issues + Issue.where(project_id: limit_project_ids).search(query).order('updated_at DESC') + end + + def merge_requests + MergeRequest.in_projects(limit_project_ids).search(query).order('updated_at DESC') + end + + def default_scope + 'projects' + end + + def per_page + 20 + end + end +end -- cgit v1.2.1 From 5d9a5c02d83c2aa9fed66c045eb88762679fb60e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Tue, 26 Aug 2014 23:39:37 +0300 Subject: Add search method to Note class Signed-off-by: Dmitriy Zaporozhets --- app/models/note.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/note.rb b/app/models/note.rb index 7ff6444cc9b..01f72b95c48 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -146,6 +146,10 @@ class Note < ActiveRecord::Base def cross_reference_exists?(noteable, mentioner) where(noteable_id: noteable.id, system: true, note: "_mentioned in #{mentioner.gfm_reference}_").any? end + + def search(query) + where("note like :query", query: "%#{query}%") + end end def commit_author -- cgit v1.2.1 From 9e5bc432630d04867cea9f38383d1a4fc49b62cd Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 27 Aug 2014 00:04:14 +0300 Subject: Pass scope and page to Gitlab::SearchResults#objects instead of initialize Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/project_search_results.rb | 14 ++------------ lib/gitlab/search_results.rb | 12 +++--------- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index b0a0bee1c81..71b8f4f452e 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -2,19 +2,13 @@ module Gitlab class ProjectSearchResults < SearchResults attr_reader :project, :repository_ref - def initialize(project_id, query, scope = nil, page = nil, repository_ref = nil) + def initialize(project_id, query, repository_ref = nil) @project = Project.find(project_id) @repository_ref = repository_ref - @page = page @query = Shellwords.shellescape(query) if query.present? - @scope = scope - - unless %w(blobs notes issues merge_requests).include?(@scope) - @scope = default_scope - end end - def objects + def objects(scope, page) case scope when 'notes' notes.page(page).per(per_page) @@ -51,10 +45,6 @@ module Gitlab Note.where(project_id: limit_project_ids).search(query).order('updated_at DESC') end - def default_scope - 'blobs' - end - def limit_project_ids [project.id] end diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 1325d542a0f..57b2ad887e3 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -1,23 +1,17 @@ module Gitlab class SearchResults - attr_reader :scope, :objects, :query, :page + attr_reader :query # Limit search results by passed project ids # It allows us to search only for projects user has access to attr_reader :limit_project_ids - def initialize(limit_project_ids, query, scope = nil, page = nil) + def initialize(limit_project_ids, query) @limit_project_ids = limit_project_ids || Project.all - @page = page @query = Shellwords.shellescape(query) if query.present? - @scope = scope - - unless %w(projects issues merge_requests).include?(@scope) - @scope = default_scope - end end - def objects + def objects(scope, page) case scope when 'projects' projects.page(page).per(per_page) -- cgit v1.2.1 From ede08dbdd787fdd3a30b62dc0e7e2c796bb6d43a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 27 Aug 2014 09:57:50 +0300 Subject: Implement search page with filtering of results and pagination Signed-off-by: Dmitriy Zaporozhets --- app/controllers/search_controller.rb | 23 ++++++++---- app/helpers/search_helper.rb | 15 ++++++++ app/services/search/global_service.rb | 20 +---------- app/services/search/project_service.rb | 36 ++----------------- app/views/search/_global_results.html.haml | 32 ++++++++++++++--- app/views/search/_project_results.html.haml | 56 +++++++++++++++++------------ app/views/search/_results.html.haml | 3 +- 7 files changed, 98 insertions(+), 87 deletions(-) diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 8df84e9884a..a58b24de643 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -4,14 +4,25 @@ class SearchController < ApplicationController def show @project = Project.find_by(id: params[:project_id]) if params[:project_id].present? @group = Group.find_by(id: params[:group_id]) if params[:group_id].present? + @scope = params[:scope] - if @project - return access_denied! unless can?(current_user, :download_code, @project) + @search_results = if @project + return access_denied! unless can?(current_user, :download_code, @project) - @search_results = Search::ProjectService.new(@project, current_user, params).execute - else - @search_results = Search::GlobalService.new(current_user, params).execute - end + unless %w(blobs notes issues merge_requests).include?(@scope) + @scope = 'blobs' + end + + Search::ProjectService.new(@project, current_user, params).execute + else + unless %w(projects issues merge_requests).include?(@scope) + @scope = 'projects' + end + + Search::GlobalService.new(current_user, params).execute + end + + @objects = @search_results.objects(@scope, params[:page]) end def autocomplete diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index ecd8d3994d0..8c805f79c36 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -91,4 +91,19 @@ module SearchHelper def search_result_sanitize(str) Sanitize.clean(str) end + + def search_filter_path(options={}) + exist_opts = { + search: params[:search], + project_id: params[:project_id], + group_id: params[:group_id], + scope: params[:scope] + } + + options = exist_opts.merge(options) + + path = request.path + path << "?#{options.to_param}" + path + end end diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb index d213e1375e0..0bcc50c81a7 100644 --- a/app/services/search/global_service.rb +++ b/app/services/search/global_service.rb @@ -7,30 +7,12 @@ module Search end def execute - query = params[:search] - query = Shellwords.shellescape(query) if query.present? - return result unless query.present? - group = Group.find_by(id: params[:group_id]) if params[:group_id].present? projects = ProjectsFinder.new.execute(current_user) projects = projects.where(namespace_id: group.id) if group project_ids = projects.pluck(:id) - result[:projects] = projects.search(query).limit(20) - result[:merge_requests] = MergeRequest.in_projects(project_ids).search(query).order('updated_at DESC').limit(20) - result[:issues] = Issue.where(project_id: project_ids).search(query).order('updated_at DESC').limit(20) - result[:total_results] = %w(projects issues merge_requests).sum { |items| result[items.to_sym].size } - result - end - - def result - @result ||= { - projects: [], - merge_requests: [], - issues: [], - notes: [], - total_results: 0, - } + Gitlab::SearchResults.new(project_ids, params[:search]) end end end diff --git a/app/services/search/project_service.rb b/app/services/search/project_service.rb index 8aac18840e4..f630c0a3790 100644 --- a/app/services/search/project_service.rb +++ b/app/services/search/project_service.rb @@ -7,39 +7,9 @@ module Search end def execute - query = params[:search] - query = Shellwords.shellescape(query) if query.present? - return result unless query.present? - - if params[:search_code].present? - if !@project.empty_repo? - blobs = project.repository.search_files(query, - params[:repository_ref]) - else - blobs = Array.new - end - - blobs = Kaminari.paginate_array(blobs).page(params[:page]).per(20) - result[:blobs] = blobs - result[:total_results] = blobs.total_count - else - result[:merge_requests] = project.merge_requests.search(query).order('updated_at DESC').limit(20) - result[:issues] = project.issues.where("title like :query OR description like :query ", query: "%#{query}%").order('updated_at DESC').limit(20) - result[:notes] = Note.where(noteable_type: 'issue').where(project_id: project.id).where("note like :query", query: "%#{query}%").order('updated_at DESC').limit(20) - result[:total_results] = %w(issues merge_requests notes).sum { |items| result[items.to_sym].size } - end - - result - end - - def result - @result ||= { - merge_requests: [], - issues: [], - blobs: [], - notes: [], - total_results: 0, - } + Gitlab::ProjectSearchResults.new(project.id, + params[:search], + params[:repository_ref]) end end end diff --git a/app/views/search/_global_results.html.haml b/app/views/search/_global_results.html.haml index 7f4f0e5e000..afecf1d3ac0 100644 --- a/app/views/search/_global_results.html.haml +++ b/app/views/search/_global_results.html.haml @@ -1,5 +1,27 @@ -.search_results - %ul.bordered-list - = render partial: "search/results/project", collection: @search_results[:projects] - = render partial: "search/results/merge_request", collection: @search_results[:merge_requests] - = render partial: "search/results/issue", collection: @search_results[:issues] +.row + .col-sm-3 + %ul.nav.nav-pills.nav-stacked + %li{class: ("active" if @scope == 'projects')} + = link_to search_filter_path(scope: 'projects') do + Projects + .pull-right + = @search_results.projects_count + %li{class: ("active" if @scope == 'issues')} + = link_to search_filter_path(scope: 'issues') do + Issues + .pull-right + = @search_results.issues_count + %li{class: ("active" if @scope == 'merge_requests')} + = link_to search_filter_path(scope: 'merge_requests') do + Merge requests + .pull-right + = @search_results.merge_requests_count + + .col-sm-9 + .search_results + - if @search_results.empty? + = render partial: "search/results/empty", locals: { message: "We couldn't find any matchind results" } + + %ul.bordered-list + = render partial: "search/results/#{@scope.singularize}", collection: @objects + = paginate @objects, theme: 'gitlab' diff --git a/app/views/search/_project_results.html.haml b/app/views/search/_project_results.html.haml index 5e8346a8262..35bc436dd18 100644 --- a/app/views/search/_project_results.html.haml +++ b/app/views/search/_project_results.html.haml @@ -1,24 +1,36 @@ -%ul.nav.nav-tabs - %li{class: ("active" if params[:search_code].present?)} - = link_to search_path(params.merge(search_code: true)) do - Repository Code - %li{class: ("active" if params[:search_code].blank?)} - = link_to search_path(params.merge(search_code: nil)) do - Issues and Merge requests +.row + .col-sm-3 + %ul.nav.nav-pills.nav-stacked + %li{class: ("active" if @scope == 'blobs')} + = link_to search_filter_path(scope: 'blobs') do + %i.icon-code + Code + .pull-right + = @search_results.blobs_count + %li{class: ("active" if @scope == 'issues')} + = link_to search_filter_path(scope: 'issues') do + %i.icon-exclamation-sign + Issues + .pull-right + = @search_results.issues_count + %li{class: ("active" if @scope == 'merge_requests')} + = link_to search_filter_path(scope: 'merge_requests') do + %i.icon-code-fork + Merge requests + .pull-right + = @search_results.merge_requests_count + %li{class: ("active" if @scope == 'notes')} + = link_to search_filter_path(scope: 'notes') do + %i.icon-comments + Comments + .pull-right + = @search_results.notes_count + + .col-sm-9 + .search_results + - if @search_results.empty? + = render partial: "search/results/empty", locals: { message: "We couldn't find any matchind results" } -.search_results - - if params[:search_code].present? - .blob-results - - if !@search_results[:blobs].empty? - = render partial: "search/results/blob", collection: @search_results[:blobs] - = paginate @search_results[:blobs], theme: 'gitlab' - - else - = render partial: "search/results/empty", :locals => { message: "We couldn't find any matching code" } - - else - - if @search_results[:merge_requests].present? || @search_results[:issues].present? || @search_results[:notes].present? %ul.bordered-list - = render partial: "search/results/merge_request", collection: @search_results[:merge_requests] - = render partial: "search/results/issue", collection: @search_results[:issues] - = render partial: "search/results/note", collection: @search_results[:notes] - - else - = render partial: "search/results/empty", locals: { message: "We couldn't find any issues, merge requests or notes" } + = render partial: "search/results/#{@scope.singularize}", collection: @objects + = paginate @objects, theme: 'gitlab' diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml index 2336d0f71d5..93bbe9cf7e9 100644 --- a/app/views/search/_results.html.haml +++ b/app/views/search/_results.html.haml @@ -1,5 +1,5 @@ %h4 - #{@search_results[:total_results]} results found + #{@search_results.total_count} results found - if @project for #{link_to @project.name_with_namespace, @project} - elsif @group @@ -14,4 +14,3 @@ :javascript $(".search_results .term").highlight("#{escape_javascript(params[:search])}"); - -- cgit v1.2.1 From c3ad51a0e492108825093e12d441cca90d597a19 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 27 Aug 2014 10:48:51 +0300 Subject: Improve search tests Signed-off-by: Dmitriy Zaporozhets --- app/assets/stylesheets/generic/common.scss | 2 +- app/views/search/_filter.html.haml | 2 +- app/views/search/_global_results.html.haml | 2 +- app/views/search/_project_results.html.haml | 2 +- app/views/search/results/_issue.html.haml | 15 +++--- features/dashboard/search.feature | 10 ---- features/search.feature | 29 ++++++++++++ features/steps/dashboard/search.rb | 19 -------- features/steps/search.rb | 73 +++++++++++++++++++++++++++++ 9 files changed, 114 insertions(+), 40 deletions(-) delete mode 100644 features/dashboard/search.feature create mode 100644 features/search.feature delete mode 100644 features/steps/dashboard/search.rb create mode 100644 features/steps/search.rb diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 9d3c9f372a9..803219a2e86 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -128,7 +128,7 @@ p.time { } .highlight_word { - border-bottom: 2px solid #F90; + background: #fafe3d; } .thin_area{ diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml index 979b18e3856..416ae8fc9f5 100644 --- a/app/views/search/_filter.html.haml +++ b/app/views/search/_filter.html.haml @@ -16,7 +16,7 @@ = link_to search_path(group_id: group.id, search: params[:search]) do = group.name -.dropdown.inline.prepend-left-10 +.dropdown.inline.prepend-left-10.project-filter %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} %i.icon-tags %span.light Project: diff --git a/app/views/search/_global_results.html.haml b/app/views/search/_global_results.html.haml index afecf1d3ac0..2d7968f8498 100644 --- a/app/views/search/_global_results.html.haml +++ b/app/views/search/_global_results.html.haml @@ -1,6 +1,6 @@ .row .col-sm-3 - %ul.nav.nav-pills.nav-stacked + %ul.nav.nav-pills.nav-stacked.search-filter %li{class: ("active" if @scope == 'projects')} = link_to search_filter_path(scope: 'projects') do Projects diff --git a/app/views/search/_project_results.html.haml b/app/views/search/_project_results.html.haml index 35bc436dd18..9b2d485fdce 100644 --- a/app/views/search/_project_results.html.haml +++ b/app/views/search/_project_results.html.haml @@ -1,6 +1,6 @@ .row .col-sm-3 - %ul.nav.nav-pills.nav-stacked + %ul.nav.nav-pills.nav-stacked.search-filter %li{class: ("active" if @scope == 'blobs')} = link_to search_filter_path(scope: 'blobs') do %i.icon-code diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml index 8147cf272fb..7579c99c9ed 100644 --- a/app/views/search/results/_issue.html.haml +++ b/app/views/search/results/_issue.html.haml @@ -1,9 +1,10 @@ %li - issue: - = link_to [issue.project, issue] do - %span ##{issue.iid} - %strong.term - = truncate issue.title, length: 50 - %span.light (#{issue.project.name_with_namespace}) + %h4 + = link_to [issue.project, issue] do + %span.term.str-truncated= issue.title + .pull-right ##{issue.iid} + %span.light + #{issue.project.name_with_namespace} - if issue.closed? - %span.label.label-danger Closed + .pull-right + %span.label.label-danger Closed diff --git a/features/dashboard/search.feature b/features/dashboard/search.feature deleted file mode 100644 index 24c45028697..00000000000 --- a/features/dashboard/search.feature +++ /dev/null @@ -1,10 +0,0 @@ -@dashboard -Feature: Dashboard Search - Background: - Given I sign in as a user - And I own project "Shop" - And I visit dashboard search page - - Scenario: I should see project I am looking for - Given I search for "Sho" - Then I should see "Shop" project link diff --git a/features/search.feature b/features/search.feature new file mode 100644 index 00000000000..b174d973122 --- /dev/null +++ b/features/search.feature @@ -0,0 +1,29 @@ +@dashboard +Feature: Search + Background: + Given I sign in as a user + And I own project "Shop" + And I visit dashboard search page + + Scenario: I should see project I am looking for + Given I search for "Sho" + Then I should see "Shop" project link + + Scenario: I should see issues I am looking for + And project has issues + When I search for "Foo" + And I click "Issues" link + Then I should see "Foo" link + And I should not see "Bar" link + + Scenario: I should see merge requests I am looking for + And project has merge requests + When I search for "Foo" + When I click "Merge requests" link + Then I should see "Foo" link + And I should not see "Bar" link + + Scenario: I should see project code I am looking for + When I search for "rspec" + And I click project "Shop" link + Then I should see code results for project "Shop" diff --git a/features/steps/dashboard/search.rb b/features/steps/dashboard/search.rb deleted file mode 100644 index 32966a8617a..00000000000 --- a/features/steps/dashboard/search.rb +++ /dev/null @@ -1,19 +0,0 @@ -class DashboardSearch < Spinach::FeatureSteps - include SharedAuthentication - include SharedPaths - include SharedProject - - Given 'I search for "Sho"' do - fill_in "dashboard_search", with: "Sho" - click_button "Search" - end - - Then 'I should see "Shop" project link' do - page.should have_link "Shop" - end - - Given 'I search for "Contibuting"' do - fill_in "dashboard_search", with: "Contibuting" - click_button "Search" - end -end diff --git a/features/steps/search.rb b/features/steps/search.rb new file mode 100644 index 00000000000..b1058989d0b --- /dev/null +++ b/features/steps/search.rb @@ -0,0 +1,73 @@ +class Spinach::Features::Search < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedProject + + step 'I search for "Sho"' do + fill_in "dashboard_search", with: "Sho" + click_button "Search" + end + + step 'I search for "Foo"' do + fill_in "dashboard_search", with: "Foo" + click_button "Search" + end + + step 'I search for "rspec"' do + fill_in "dashboard_search", with: "rspec" + click_button "Search" + end + + step 'I click "Issues" link' do + within '.search-filter' do + click_link 'Issues' + end + end + + step 'I click project "Shop" link' do + within '.project-filter' do + click_link project.name_with_namespace + end + end + + step 'I click "Merge requests" link' do + within '.search-filter' do + click_link 'Merge requests' + end + end + + step 'I should see "Shop" project link' do + page.should have_link "Shop" + end + + step 'I should see code results for project "Shop"' do + page.should have_content 'Update capybara, rspec-rails, poltergeist to recent versions' + end + + step 'I search for "Contibuting"' do + fill_in "dashboard_search", with: "Contibuting" + click_button "Search" + end + + step 'project has issues' do + create(:issue, title: "Foo", project: project) + create(:issue, title: "Bar", project: project) + end + + step 'project has merge requests' do + create(:merge_request, title: "Foo", source_project: project, target_project: project) + create(:merge_request, :simple, title: "Bar", source_project: project, target_project: project) + end + + step 'I should see "Foo" link' do + page.should have_link "Foo" + end + + step 'I should not see "Bar" link' do + page.should_not have_link "Bar" + end + + def project + @project ||= Project.find_by(name: "Shop") + end +end -- cgit v1.2.1 From 13f6dc1a9476ef2c86538ede8420e915eb999a1c Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 27 Aug 2014 11:55:42 +0300 Subject: Save search options when switch between filter Signed-off-by: Dmitriy Zaporozhets --- app/helpers/search_helper.rb | 5 +---- app/views/search/_filter.html.haml | 8 ++++---- app/views/search/_global_results.html.haml | 2 +- app/views/search/_project_results.html.haml | 2 +- app/views/search/results/_merge_request.html.haml | 24 +++++++++++------------ app/views/search/show.html.haml | 2 +- features/search.feature | 21 ++++++++++++++++++-- features/steps/project/search_code.rb | 3 +-- 8 files changed, 39 insertions(+), 28 deletions(-) diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 8c805f79c36..fff850c1fbb 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -101,9 +101,6 @@ module SearchHelper } options = exist_opts.merge(options) - - path = request.path - path << "?#{options.to_param}" - path + search_path(options) end end diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml index 416ae8fc9f5..049aff0bc9b 100644 --- a/app/views/search/_filter.html.haml +++ b/app/views/search/_filter.html.haml @@ -9,11 +9,11 @@ %b.caret %ul.dropdown-menu %li - = link_to search_path(group_id: nil, search: params[:search]) do + = link_to search_filter_path(group_id: nil) do Any - current_user.authorized_groups.sort_by(&:name).each do |group| %li - = link_to search_path(group_id: group.id, search: params[:search]) do + = link_to search_filter_path(group_id: group.id, project_id: nil) do = group.name .dropdown.inline.prepend-left-10.project-filter @@ -27,9 +27,9 @@ %b.caret %ul.dropdown-menu %li - = link_to search_path(project_id: nil, search: params[:search]) do + = link_to search_filter_path(project_id: nil) do Any - current_user.authorized_projects.sort_by(&:name_with_namespace).each do |project| %li - = link_to search_path(project_id: project.id, search: params[:search]) do + = link_to search_filter_path(project_id: project.id, group_id: nil) do = project.name_with_namespace diff --git a/app/views/search/_global_results.html.haml b/app/views/search/_global_results.html.haml index 2d7968f8498..0225491e215 100644 --- a/app/views/search/_global_results.html.haml +++ b/app/views/search/_global_results.html.haml @@ -20,7 +20,7 @@ .col-sm-9 .search_results - if @search_results.empty? - = render partial: "search/results/empty", locals: { message: "We couldn't find any matchind results" } + = render partial: "search/results/empty", locals: { message: "We couldn't find any matching results" } %ul.bordered-list = render partial: "search/results/#{@scope.singularize}", collection: @objects diff --git a/app/views/search/_project_results.html.haml b/app/views/search/_project_results.html.haml index 9b2d485fdce..dd63d5d04cb 100644 --- a/app/views/search/_project_results.html.haml +++ b/app/views/search/_project_results.html.haml @@ -29,7 +29,7 @@ .col-sm-9 .search_results - if @search_results.empty? - = render partial: "search/results/empty", locals: { message: "We couldn't find any matchind results" } + = render partial: "search/results/empty", locals: { message: "We couldn't find any matching results" } %ul.bordered-list = render partial: "search/results/#{@scope.singularize}", collection: @objects diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml index de2a79970c1..96a2e722ba3 100644 --- a/app/views/search/results/_merge_request.html.haml +++ b/app/views/search/results/_merge_request.html.haml @@ -1,14 +1,12 @@ %li - merge request: - = link_to [merge_request.target_project, merge_request] do - %span ##{merge_request.iid} - %strong.term - = truncate merge_request.title, length: 50 - - if merge_request.for_fork? - %span.light (#{merge_request.source_project.name_with_namespace}:#{merge_request.source_branch} → #{merge_request.target_project.name_with_namespace}:#{merge_request.target_branch}) - - else - %span.light (#{merge_request.source_branch} → #{merge_request.target_branch}) - - if merge_request.merged? - %span.label.label-primary Merged - - elsif merge_request.closed? - %span.label.label-danger Closed + %h4 + = link_to [merge_request.target_project, merge_request] do + %span.term.str-truncated= merge_request.title + .pull-right ##{merge_request.iid} + %span.light + #{merge_request.project.name_with_namespace} + .pull-right + - if merge_request.merged? + %span.label.label-primary Merged + - elsif merge_request.closed? + %span.label.label-danger Closed diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml index 3b6f10d4d9c..8d1614bfbd4 100644 --- a/app/views/search/show.html.haml +++ b/app/views/search/show.html.haml @@ -13,7 +13,7 @@ = render 'filter', f: f = hidden_field_tag :project_id, params[:project_id] = hidden_field_tag :group_id, params[:group_id] - = hidden_field_tag :search_code, params[:search_code] + = hidden_field_tag :scope, params[:scope] .results.prepend-top-10 - if params[:search].present? diff --git a/features/search.feature b/features/search.feature index b174d973122..54708c17575 100644 --- a/features/search.feature +++ b/features/search.feature @@ -24,6 +24,23 @@ Feature: Search And I should not see "Bar" link Scenario: I should see project code I am looking for - When I search for "rspec" - And I click project "Shop" link + When I click project "Shop" link + And I search for "rspec" Then I should see code results for project "Shop" + + Scenario: I should see project issues + And project has issues + When I click project "Shop" link + And I search for "Foo" + And I click "Issues" link + Then I should see "Foo" link + And I should not see "Bar" link + + Scenario: I should see project merge requests + And project has merge requests + When I click project "Shop" link + And I search for "Foo" + And I click "Merge requests" link + Then I should see "Foo" link + And I should not see "Bar" link + diff --git a/features/steps/project/search_code.rb b/features/steps/project/search_code.rb index affa7d3b43b..55218b6e745 100644 --- a/features/steps/project/search_code.rb +++ b/features/steps/project/search_code.rb @@ -6,7 +6,6 @@ class ProjectSearchCode < Spinach::FeatureSteps step 'I search for term "coffee"' do fill_in "search", with: "coffee" click_button "Go" - click_link 'Repository Code' end step 'I should see files from repository containing "coffee"' do @@ -15,6 +14,6 @@ class ProjectSearchCode < Spinach::FeatureSteps end step 'I should see empty result' do - page.should have_content "We couldn't find any matching code" + page.should have_content "We couldn't find any matching" end end -- cgit v1.2.1 From 7e59a8fee8c89d6217bd48bf1050526f59cb96aa Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 27 Aug 2014 12:37:31 +0300 Subject: Improve comment search results Signed-off-by: Dmitriy Zaporozhets --- app/helpers/search_helper.rb | 5 +++++ app/views/search/_global_results.html.haml | 2 +- app/views/search/_project_results.html.haml | 2 +- app/views/search/results/_note.html.haml | 33 ++++++++++++++++++++++------- app/views/search/results/_project.html.haml | 7 +++--- 5 files changed, 35 insertions(+), 14 deletions(-) diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index fff850c1fbb..51b6812cac3 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -103,4 +103,9 @@ module SearchHelper options = exist_opts.merge(options) search_path(options) end + + # Sanitize html generated after parsing markdown from issue description or comment + def search_md_sanitize(html) + sanitize(html, tags: %w(a p ul li pre code)) + end end diff --git a/app/views/search/_global_results.html.haml b/app/views/search/_global_results.html.haml index 0225491e215..cedb6b249bb 100644 --- a/app/views/search/_global_results.html.haml +++ b/app/views/search/_global_results.html.haml @@ -22,6 +22,6 @@ - if @search_results.empty? = render partial: "search/results/empty", locals: { message: "We couldn't find any matching results" } - %ul.bordered-list + %ul.bordered-list.top-list = render partial: "search/results/#{@scope.singularize}", collection: @objects = paginate @objects, theme: 'gitlab' diff --git a/app/views/search/_project_results.html.haml b/app/views/search/_project_results.html.haml index dd63d5d04cb..fc047637b35 100644 --- a/app/views/search/_project_results.html.haml +++ b/app/views/search/_project_results.html.haml @@ -31,6 +31,6 @@ - if @search_results.empty? = render partial: "search/results/empty", locals: { message: "We couldn't find any matching results" } - %ul.bordered-list + %ul.bordered-list.top-list = render partial: "search/results/#{@scope.singularize}", collection: @objects = paginate @objects, theme: 'gitlab' diff --git a/app/views/search/results/_note.html.haml b/app/views/search/results/_note.html.haml index 97e892bdd4d..6316212bc90 100644 --- a/app/views/search/results/_note.html.haml +++ b/app/views/search/results/_note.html.haml @@ -1,9 +1,26 @@ +- project = note.project %li - note on issue: - = link_to [note.project, note.noteable] do - %span ##{note.noteable.iid} - %strong.term - = truncate note.noteable.title, length: 50 - %span.light (#{note.project.name_with_namespace}) - - if note.noteable.closed? - %span.label Closed + %h5.note-search-caption + %i.icon-comment + = link_to_member(project, note.author, avatar: false) + commented on + + - if note.for_commit? + = link_to project do + = project.name_with_namespace + · + = link_to project_commit_path(project, note.commit_id, anchor: dom_id(note)) do + Commit #{note.commit_id[0..8]} + - else + = link_to project do + = project.name_with_namespace + · + %span #{note.noteable_type.titleize} ##{note.noteable.iid} + · + = link_to [project, note.noteable, anchor: dom_id(note)] do + = note.noteable.title + + .note-search-result + .term + = preserve do + = search_md_sanitize(markdown(note.note, {no_header_anchors: true})) diff --git a/app/views/search/results/_project.html.haml b/app/views/search/results/_project.html.haml index abc86c72bef..8c8baab8a5b 100644 --- a/app/views/search/results/_project.html.haml +++ b/app/views/search/results/_project.html.haml @@ -1,7 +1,6 @@ %li - project: - = link_to project do - %strong.term= project.name_with_namespace + %h4 + = link_to project do + %span.term= project.name_with_namespace - if project.description.present? - – %span.light.term= project.description -- cgit v1.2.1 From 8b00d01c6711918951a7951cff3e57660c807ae7 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 27 Aug 2014 12:47:30 +0300 Subject: Search by issue/mr title and description Signed-off-by: Dmitriy Zaporozhets --- app/controllers/projects/issues_controller.rb | 2 +- app/models/concerns/issuable.rb | 4 ++++ app/models/project.rb | 4 ++-- app/views/search/results/_issue.html.haml | 3 +++ app/views/search/results/_merge_request.html.haml | 3 +++ lib/gitlab/search_results.rb | 4 ++-- 6 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index bde90466ea1..0b49803cfec 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -20,7 +20,7 @@ class Projects::IssuesController < Projects::ApplicationController terms = params['issue_search'] @issues = issues_filtered - @issues = @issues.where("title LIKE ? OR description LIKE ?", "%#{terms}%", "%#{terms}%") if terms.present? + @issues = @issues.full_search(terms) if terms.present? @issues = @issues.page(params[:page]).per(20) assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 5c9b44812be..698b5b8c30a 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -49,6 +49,10 @@ module Issuable where("LOWER(title) like :query", query: "%#{query.downcase}%") end + def full_search(query) + where("LOWER(title) like :query OR LOWER(description) like :query", query: "%#{query.downcase}%") + end + def sort(method) case method.to_s when 'newest' then reorder("#{table_name}.created_at DESC") diff --git a/app/models/project.rb b/app/models/project.rb index c991bf6467c..5cc35f20cad 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -177,11 +177,11 @@ class Project < ActiveRecord::Base joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC") end - def search query + def search(query) joins(:namespace).where("projects.archived = ?", false).where("projects.name LIKE :query OR projects.path LIKE :query OR namespaces.name LIKE :query OR projects.description LIKE :query", query: "%#{query}%") end - def search_by_title query + def search_by_title(query) where("projects.archived = ?", false).where("LOWER(projects.name) LIKE :query", query: "%#{query.downcase}%") end diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml index 7579c99c9ed..94234a83a71 100644 --- a/app/views/search/results/_issue.html.haml +++ b/app/views/search/results/_issue.html.haml @@ -3,6 +3,9 @@ = link_to [issue.project, issue] do %span.term.str-truncated= issue.title .pull-right ##{issue.iid} + .description.term + = preserve do + = search_md_sanitize(markdown(issue.description)) %span.light #{issue.project.name_with_namespace} - if issue.closed? diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml index 96a2e722ba3..fce996343d8 100644 --- a/app/views/search/results/_merge_request.html.haml +++ b/app/views/search/results/_merge_request.html.haml @@ -3,6 +3,9 @@ = link_to [merge_request.target_project, merge_request] do %span.term.str-truncated= merge_request.title .pull-right ##{merge_request.iid} + .description.term + = preserve do + = search_md_sanitize(markdown(merge_request.description)) %span.light #{merge_request.project.name_with_namespace} .pull-right diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 57b2ad887e3..a4d27e0cd27 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -51,11 +51,11 @@ module Gitlab end def issues - Issue.where(project_id: limit_project_ids).search(query).order('updated_at DESC') + Issue.where(project_id: limit_project_ids).full_search(query).order('updated_at DESC') end def merge_requests - MergeRequest.in_projects(limit_project_ids).search(query).order('updated_at DESC') + MergeRequest.in_projects(limit_project_ids).full_search(query).order('updated_at DESC') end def default_scope -- cgit v1.2.1 From 043f275900c833bd0d4efa3c08d4f3e4158f00f8 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 27 Aug 2014 12:49:45 +0300 Subject: Skip description if not exist Signed-off-by: Dmitriy Zaporozhets --- app/views/search/results/_issue.html.haml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/search/results/_issue.html.haml b/app/views/search/results/_issue.html.haml index 94234a83a71..ac1566c9592 100644 --- a/app/views/search/results/_issue.html.haml +++ b/app/views/search/results/_issue.html.haml @@ -3,9 +3,10 @@ = link_to [issue.project, issue] do %span.term.str-truncated= issue.title .pull-right ##{issue.iid} - .description.term - = preserve do - = search_md_sanitize(markdown(issue.description)) + - if issue.description.present? + .description.term + = preserve do + = search_md_sanitize(markdown(issue.description)) %span.light #{issue.project.name_with_namespace} - if issue.closed? -- cgit v1.2.1 From 7f4b993793083abad99c12e2ea223ea08843ccb9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 27 Aug 2014 12:54:18 +0300 Subject: Forgot to save file :) Signed-off-by: Dmitriy Zaporozhets --- app/views/search/results/_merge_request.html.haml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml index fce996343d8..7f6d765c1f3 100644 --- a/app/views/search/results/_merge_request.html.haml +++ b/app/views/search/results/_merge_request.html.haml @@ -3,9 +3,10 @@ = link_to [merge_request.target_project, merge_request] do %span.term.str-truncated= merge_request.title .pull-right ##{merge_request.iid} - .description.term - = preserve do - = search_md_sanitize(markdown(merge_request.description)) + - if merge_request.description.present? + .description.term + = preserve do + = search_md_sanitize(markdown(merge_request.description)) %span.light #{merge_request.project.name_with_namespace} .pull-right -- cgit v1.2.1 From c5c906fe6472f953b379aca4c34fad23b3b7742e Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Wed, 27 Aug 2014 15:26:35 +0300 Subject: Fix tests Signed-off-by: Dmitriy Zaporozhets --- lib/gitlab/project_search_results.rb | 2 +- lib/gitlab/search_results.rb | 2 +- spec/services/search_service_spec.rb | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 71b8f4f452e..90511662b20 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -8,7 +8,7 @@ module Gitlab @query = Shellwords.shellescape(query) if query.present? end - def objects(scope, page) + def objects(scope, page = nil) case scope when 'notes' notes.page(page).per(per_page) diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index a4d27e0cd27..75a3dfe37c3 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -11,7 +11,7 @@ module Gitlab @query = Shellwords.shellescape(query) if query.present? end - def objects(scope, page) + def objects(scope, page = nil) case scope when 'projects' projects.page(page).per(per_page) diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb index daffe98a8ed..3217c571e67 100644 --- a/spec/services/search_service_spec.rb +++ b/spec/services/search_service_spec.rb @@ -19,7 +19,7 @@ describe 'Search::GlobalService' do it 'should return public projects only' do context = Search::GlobalService.new(nil, search: "searchable") results = context.execute - results[:projects].should match_array [public_project] + results.objects('projects').should match_array [public_project] end end @@ -27,19 +27,19 @@ describe 'Search::GlobalService' do it 'should return public, internal and private projects' do context = Search::GlobalService.new(user, search: "searchable") results = context.execute - results[:projects].should match_array [public_project, found_project, internal_project] + results.objects('projects').should match_array [public_project, found_project, internal_project] end it 'should return only public & internal projects' do context = Search::GlobalService.new(internal_user, search: "searchable") results = context.execute - results[:projects].should match_array [internal_project, public_project] + results.objects('projects').should match_array [internal_project, public_project] end it 'namespace name should be searchable' do context = Search::GlobalService.new(user, search: found_project.namespace.path) results = context.execute - results[:projects].should match_array [found_project] + results.objects('projects').should match_array [found_project] end end end -- cgit v1.2.1