From 073a269831d5bf6d9b9c55d91826d87721746bc9 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 18 Jan 2014 14:16:46 +0200 Subject: Refactor search autocomplete. Use ajax for source Signed-off-by: Dmitriy Zaporozhets --- app/assets/javascripts/dispatcher.js.coffee | 8 +++- .../javascripts/search_autocomplete.js.coffee | 9 +++- app/controllers/search_controller.rb | 10 ++++ app/helpers/search_helper.rb | 53 +++++++++++++++------- app/models/project.rb | 4 ++ app/views/layouts/_search.html.haml | 2 +- config/routes.rb | 1 + 7 files changed, 65 insertions(+), 22 deletions(-) diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index e264e281309..8ea302f256a 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -47,5 +47,9 @@ class Dispatcher initSearch: -> - autocomplete_json = $('.search-autocomplete-json').data('autocomplete-opts') - new SearchAutocomplete(autocomplete_json) + opts = $('.search-autocomplete-opts') + path = opts.data('autocomplete-path') + project_id = opts.data('autocomplete-project-id') + project_ref = opts.data('autocomplete-project-ref') + + new SearchAutocomplete(path, project_id, project_ref) diff --git a/app/assets/javascripts/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee index 3418690e109..e144dfa1d68 100644 --- a/app/assets/javascripts/search_autocomplete.js.coffee +++ b/app/assets/javascripts/search_autocomplete.js.coffee @@ -1,7 +1,12 @@ class SearchAutocomplete - constructor: (json) -> + constructor: (search_autocomplete_path, project_id, project_ref) -> + project_id = '' unless project_id + project_ref = '' unless project_ref + query = "?project_id=" + project_id + "&project_ref=" + project_ref + $("#search").autocomplete - source: json + source: search_autocomplete_path + query + minLength: 1 select: (event, ui) -> location.href = ui.item.url diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index e853c22800a..0152d53f7fc 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -1,4 +1,6 @@ class SearchController < ApplicationController + include SearchHelper + 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? @@ -10,4 +12,12 @@ class SearchController < ApplicationController @search_results = Search::GlobalService.new(current_user, params).execute end end + + def autocomplete + term = params[:term] + @project = Project.find(params[:project_id]) if params[:project_id].present? + @ref = params[:project_ref] if params[:project_ref].present? + + render json: search_autocomplete_opts(term).to_json + end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index f24156e4d85..20b50debb44 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -1,16 +1,22 @@ module SearchHelper - def search_autocomplete_source + def search_autocomplete_opts(term) return unless current_user + + resources_results = [ + groups_autocomplete(term), + projects_autocomplete(term), + public_projects_autocomplete(term), + ].flatten + + generic_results = project_autocomplete + default_autocomplete + help_autocomplete + generic_results.select! { |result| result[:label] =~ Regexp.new(term, "i") } + [ - groups_autocomplete, - projects_autocomplete, - public_projects_autocomplete, - default_autocomplete, - project_autocomplete, - help_autocomplete + resources_results, + generic_results ].flatten.uniq do |item| item[:label] - end.to_json + end end private @@ -65,23 +71,36 @@ module SearchHelper end # Autocomplete results for the current user's groups - def groups_autocomplete - current_user.authorized_groups.map do |group| - { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } + def groups_autocomplete(term, limit = 5) + current_user.authorized_groups.search(term).limit(limit).map do |group| + { + label: "group: #{simple_sanitize(group.name)}", + url: group_path(group) + } end end # Autocomplete results for the current user's projects - def projects_autocomplete - current_user.authorized_projects.non_archived.map do |p| - { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } + def projects_autocomplete(term, limit = 5) + current_user.authorized_projects.search_by_title(term).non_archived.limit(limit).map do |p| + { + label: "project: #{simple_sanitize(p.name_with_namespace)}", + url: project_path(p) + } end end # Autocomplete results for the current user's projects - def public_projects_autocomplete - Project.public_or_internal_only(current_user).non_archived.map do |p| - { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } + def public_projects_autocomplete(term, limit = 5) + Project.public_or_internal_only(current_user).search_by_title(term).non_archived.limit(limit).map do |p| + { + label: "project: #{simple_sanitize(p.name_with_namespace)}", + url: project_path(p) + } end end + + def simple_sanitize(str) + Sanitize.clean(str) + end end diff --git a/app/models/project.rb b/app/models/project.rb index f322b5a2e25..2a792f0bbd3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -138,6 +138,10 @@ class Project < ActiveRecord::Base 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 + where("projects.archived = ?", false).where("projects.name LIKE :query", query: "%#{query}%") + end + def find_with_namespace(id) if id.include?("/") id = id.split("/") diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index 9a0db99332a..a0e55b21c32 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -7,4 +7,4 @@ = hidden_field_tag :search_code, true = hidden_field_tag :repository_ref, @ref = submit_tag 'Go' if ENV['RAILS_ENV'] == 'test' - .search-autocomplete-json.hide{:'data-autocomplete-opts' => search_autocomplete_source } + .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } diff --git a/config/routes.rb b/config/routes.rb index 734421ede1d..315c339016b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,6 +6,7 @@ Gitlab::Application.routes.draw do # Search # get 'search' => "search#show" + get 'search/autocomplete' => "search#autocomplete", as: :search_autocomplete # API API::API.logger Rails.logger -- cgit v1.2.1 From ffbbccb7ea3e3c667b4cfdeaeeedfbef0a5f190a Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 18 Jan 2014 15:02:53 +0200 Subject: Fix search autocomplete Signed-off-by: Dmitriy Zaporozhets --- spec/helpers/search_helper_spec.rb | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb index 33ecb980202..d04945dfe35 100644 --- a/spec/helpers/search_helper_spec.rb +++ b/spec/helpers/search_helper_spec.rb @@ -13,52 +13,41 @@ describe SearchHelper do end it "it returns nil" do - search_autocomplete_source.should be_nil + search_autocomplete_opts("q").should be_nil end end context "with a user" do let(:user) { create(:user) } - let(:result) { JSON.parse(search_autocomplete_source) } before do allow(self).to receive(:current_user).and_return(user) end it "includes Help sections" do - result.select { |h| h['label'] =~ /^help:/ }.length.should == 9 + search_autocomplete_opts("hel").size.should == 9 end it "includes default sections" do - result.count { |h| h['label'] =~ /^(My|Admin)\s/ }.should == 4 + search_autocomplete_opts("adm").size.should == 1 end it "includes the user's groups" do create(:group).add_owner(user) - result.count { |h| h['label'] =~ /^group:/ }.should == 1 + search_autocomplete_opts("gro").size.should == 1 end it "includes the user's projects" do - create(:project, namespace: create(:namespace, owner: user)) - result.count { |h| h['label'] =~ /^project:/ }.should == 1 + project = create(:project, namespace: create(:namespace, owner: user)) + search_autocomplete_opts(project.name).size.should == 1 end context "with a current project" do before { @project = create(:project_with_code) } it "includes project-specific sections" do - result.count { |h| h['label'] =~ /^#{@project.name_with_namespace} - / }.should == 11 - end - - it "uses @ref in urls if defined" do - @ref = "foo_bar" - result.count { |h| h['url'] == project_tree_path(@project, @ref) }.should == 1 - end - end - - context "with no current project" do - it "does not include project-specific sections" do - result.count { |h| h['label'] =~ /Files$/ }.should == 0 + search_autocomplete_opts("Files").size.should == 1 + search_autocomplete_opts("Commits").size.should == 1 end end end -- cgit v1.2.1 From 757e92d52eadd0fea43a4998e89f309f196c4396 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 18 Jan 2014 16:06:20 +0200 Subject: Prevent duplicate method names in helpers Signed-off-by: Dmitriy Zaporozhets --- app/helpers/search_helper.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index 20b50debb44..470a495f036 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -49,7 +49,7 @@ module SearchHelper # Autocomplete results for the current project, if it's defined def project_autocomplete if @project && @project.repository.exists? && @project.repository.root_ref - prefix = simple_sanitize(@project.name_with_namespace) + prefix = search_result_sanitize(@project.name_with_namespace) ref = @ref || @project.repository.root_ref [ @@ -74,7 +74,7 @@ module SearchHelper def groups_autocomplete(term, limit = 5) current_user.authorized_groups.search(term).limit(limit).map do |group| { - label: "group: #{simple_sanitize(group.name)}", + label: "group: #{search_result_sanitize(group.name)}", url: group_path(group) } end @@ -84,7 +84,7 @@ module SearchHelper def projects_autocomplete(term, limit = 5) current_user.authorized_projects.search_by_title(term).non_archived.limit(limit).map do |p| { - label: "project: #{simple_sanitize(p.name_with_namespace)}", + label: "project: #{search_result_sanitize(p.name_with_namespace)}", url: project_path(p) } end @@ -94,13 +94,13 @@ module SearchHelper def public_projects_autocomplete(term, limit = 5) Project.public_or_internal_only(current_user).search_by_title(term).non_archived.limit(limit).map do |p| { - label: "project: #{simple_sanitize(p.name_with_namespace)}", + label: "project: #{search_result_sanitize(p.name_with_namespace)}", url: project_path(p) } end end - def simple_sanitize(str) + def search_result_sanitize(str) Sanitize.clean(str) end end -- cgit v1.2.1 From 712a2ec4fd7f2d6fb27bdb029ddac7e357558f63 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 18 Jan 2014 16:22:57 +0200 Subject: Case-insensetive search autocomplete for projets Signed-off-by: Dmitriy Zaporozhets --- app/models/project.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/project.rb b/app/models/project.rb index 2a792f0bbd3..30faaf00fff 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -139,7 +139,7 @@ class Project < ActiveRecord::Base end def search_by_title query - where("projects.archived = ?", false).where("projects.name LIKE :query", query: "%#{query}%") + where("projects.archived = ?", false).where("LOWER(projects.name) LIKE :query", query: "%#{query.downcase}%") end def find_with_namespace(id) -- cgit v1.2.1 From 1016b547f51ade65fa67684d787b53a440a1e000 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Sat, 18 Jan 2014 16:26:39 +0200 Subject: Mention change of search autocomplete in CHANGELOG Signed-off-by: Dmitriy Zaporozhets --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 89656265289..a7ab8f569b6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,6 +16,7 @@ v 6.5.0 - Use jquery timeago plugin - Fix 500 error for rdoc files - Ability to customize merge commit message (sponsored by Say Media) + - Search autocomplete via ajax v6.4.3 - Don't use unicorn worker killer if PhusionPassenger is defined -- cgit v1.2.1