From ede08dbdd787fdd3a30b62dc0e7e2c796bb6d43a Mon Sep 17 00:00:00 2001
From: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
Date: Wed, 27 Aug 2014 09:57:50 +0300
Subject: [PATCH] Implement search page with filtering of results and
 pagination

Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>
---
 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])}");
-
-- 
GitLab