From 73ce521bb3d9c51a94dce83db2a6ed7e656c1913 Mon Sep 17 00:00:00 2001
From: Steffen van Bergerem <svbergerem@online.de>
Date: Tue, 9 Aug 2016 15:09:49 +0200
Subject: [PATCH] Modify search to include contacts

---
 .../app/views/publisher/mention_view.js       |  3 +-
 .../javascripts/app/views/search_base_view.js | 13 +++++----
 app/models/person.rb                          |  9 +++---
 spec/factories.rb                             |  6 ----
 .../app/views/publisher_mention_view_spec.js  |  1 +
 spec/models/person_spec.rb                    | 29 ++++++++++++++-----
 6 files changed, 37 insertions(+), 24 deletions(-)

diff --git a/app/assets/javascripts/app/views/publisher/mention_view.js b/app/assets/javascripts/app/views/publisher/mention_view.js
index f19ec7d1e2..b1c6e39b0e 100644
--- a/app/assets/javascripts/app/views/publisher/mention_view.js
+++ b/app/assets/javascripts/app/views/publisher/mention_view.js
@@ -31,7 +31,8 @@ app.views.PublisherMention = app.views.SearchBase.extend({
     app.views.SearchBase.prototype.initialize.call(this, {
       typeaheadInput: this.typeaheadInput,
       customSearch: true,
-      autoselect: true
+      autoselect: true,
+      prefetch: true
     });
   },
 
diff --git a/app/assets/javascripts/app/views/search_base_view.js b/app/assets/javascripts/app/views/search_base_view.js
index d70f21cebb..352ff7ddc0 100644
--- a/app/assets/javascripts/app/views/search_base_view.js
+++ b/app/assets/javascripts/app/views/search_base_view.js
@@ -25,11 +25,6 @@ app.views.SearchBase = app.views.Base.extend({
         return this.bloodhoundTokenizer(datum.name).concat(datum.handle);
       }.bind(this),
       queryTokenizer: Bloodhound.tokenizers.whitespace,
-      prefetch: {
-        url: "/contacts.json",
-        transform: this.transformBloodhoundResponse,
-        cache: false
-      },
       sufficient: 5
     };
 
@@ -42,6 +37,14 @@ app.views.SearchBase = app.views.Base.extend({
       };
     }
 
+    if (options.prefetch) {
+      bloodhoundOptions.prefetch = {
+        url: "/contacts.json",
+        transform: this.transformBloodhoundResponse,
+        cache: false
+      };
+    }
+
     this.bloodhound = new Bloodhound(bloodhoundOptions);
   },
 
diff --git a/app/models/person.rb b/app/models/person.rb
index 5b400cd30b..1fa3acd6c2 100644
--- a/app/models/person.rb
+++ b/app/models/person.rb
@@ -56,7 +56,9 @@ class Person < ActiveRecord::Base
   validates :serialized_public_key, :presence => true
   validates :diaspora_handle, :uniqueness => true
 
-  scope :searchable, -> { joins(:profile).where(:profiles => {:searchable => true}) }
+  scope :searchable, -> (user) {
+    joins(:profile).where("profiles.searchable = true OR contacts.user_id = ?", user.id)
+  }
   scope :remote, -> { where('people.owner_id IS NULL') }
   scope :local, -> { where('people.owner_id IS NOT NULL') }
   scope :for_json, -> {
@@ -148,10 +150,9 @@ class Person < ActiveRecord::Base
 
     sql, tokens = self.search_query_string(query)
 
-    Person.searchable.where(sql, *tokens).joins(
+    Person.searchable(user).where(sql, *tokens).joins(
       "LEFT OUTER JOIN contacts ON contacts.user_id = #{user.id} AND contacts.person_id = people.id"
-    ).includes(:profile
-    ).order(search_order)
+    ).includes(:profile).order(search_order)
   end
 
   # @return [Array<String>] postgreSQL and mysql deal with null values in orders differently, it seems.
diff --git a/spec/factories.rb b/spec/factories.rb
index 1c1c5e839e..4b5363f706 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -52,12 +52,6 @@ FactoryGirl.define do
     end
   end
 
-  factory :searchable_person, :parent => :person do
-    after(:build) do |person|
-      person.profile = FactoryGirl.build(:profile, :person => person, :searchable => true)
-    end
-  end
-
   factory :like do
     association :author, :factory => :person
     association :target, :factory => :status_message
diff --git a/spec/javascripts/app/views/publisher_mention_view_spec.js b/spec/javascripts/app/views/publisher_mention_view_spec.js
index 68397e36e3..985ec49e8a 100644
--- a/spec/javascripts/app/views/publisher_mention_view_spec.js
+++ b/spec/javascripts/app/views/publisher_mention_view_spec.js
@@ -19,6 +19,7 @@ describe("app.views.PublisherMention", function() {
       expect(call.args[0].typeaheadInput.selector).toBe("#publisher .typeahead-mention-box");
       expect(call.args[0].customSearch).toBeTruthy();
       expect(call.args[0].autoselect).toBeTruthy();
+      expect(call.args[0].prefetch).toBeTruthy();
     });
 
     it("calls bindTypeaheadEvents", function() {
diff --git a/spec/models/person_spec.rb b/spec/models/person_spec.rb
index c376da2b18..04e45066f2 100644
--- a/spec/models/person_spec.rb
+++ b/spec/models/person_spec.rb
@@ -294,10 +294,11 @@ describe Person, :type => :model do
       user_profile.last_name = "asdji"
       user_profile.save
 
-      @robert_grimm = FactoryGirl.build(:searchable_person)
-      @eugene_weinstein = FactoryGirl.build(:searchable_person)
-      @yevgeniy_dodis = FactoryGirl.build(:searchable_person)
-      @casey_grippi = FactoryGirl.build(:searchable_person)
+      @robert_grimm = FactoryGirl.build(:person)
+      @eugene_weinstein = FactoryGirl.build(:person)
+      @yevgeniy_dodis = FactoryGirl.build(:person)
+      @casey_grippi = FactoryGirl.build(:person)
+      @invisible_person = FactoryGirl.build(:person)
 
       @robert_grimm.profile.first_name = "Robert"
       @robert_grimm.profile.last_name = "Grimm"
@@ -318,7 +319,14 @@ describe Person, :type => :model do
       @casey_grippi.profile.last_name = "Grippi"
       @casey_grippi.profile.save
       @casey_grippi.reload
+
+      @invisible_person.profile.first_name = "Johnson"
+      @invisible_person.profile.last_name = "Invisible"
+      @invisible_person.profile.searchable = false
+      @invisible_person.profile.save
+      @invisible_person.reload
     end
+
     it 'orders results by last name' do
       @robert_grimm.profile.first_name = "AAA"
       @robert_grimm.profile.save!
@@ -367,10 +375,15 @@ describe Person, :type => :model do
       expect(people.first).to eq(@casey_grippi)
     end
 
-    it 'only displays searchable people' do
-      invisible_person = FactoryGirl.build(:person, :profile => FactoryGirl.build(:profile, :searchable => false, :first_name => "johnson"))
-      expect(Person.search("johnson", @user)).not_to include invisible_person
-      expect(Person.search("", @user)).not_to include invisible_person
+    it "doesn't display people that are neither searchable nor contacts" do
+      expect(Person.search("Johnson", @user)).to be_empty
+    end
+
+    it "displays contacts that are not searchable" do
+      @user.contacts.create(person: @invisible_person, aspects: [@user.aspects.first])
+      people = Person.search("Johnson", @user)
+      expect(people.count).to eq(1)
+      expect(people.first).to eq(@invisible_person)
     end
 
     it 'returns results for Diaspora handles' do
-- 
GitLab