From 7e080c96d7dabdfe905073dbb18dff63ffbf867c Mon Sep 17 00:00:00 2001 From: Steve Kenworthy Date: Sat, 24 Nov 2012 20:07:33 +0800 Subject: [PATCH] Contact.text_search is now using arel. Can rely on proper query sanitization. --- app/models/entities/contact.rb | 18 ++++++++---- spec/models/entities/contact_spec.rb | 41 ++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/app/models/entities/contact.rb b/app/models/entities/contact.rb index a71b525546..95992cc614 100644 --- a/app/models/entities/contact.rb +++ b/app/models/entities/contact.rb @@ -72,17 +72,23 @@ class Contact < ActiveRecord::Base scope :assigned_to, lambda { |user| { :conditions => ["assigned_to = ?", user.id ] } } scope :text_search, lambda { |query| - query = query.gsub(/[^@\w\s\-\.'\p{L}]/u, '').strip + t = Contact.arel_table # We can't always be sure that names are entered in the right order, so we must # split the query into all possible first/last name permutations. name_query = if query.include?(" ") - query.name_permutations.map{ |first, last| - "(upper(first_name) LIKE upper('%#{first}%') AND upper(last_name) LIKE upper('%#{last}%'))" - }.join(" OR ") + scope, *rest = query.name_permutations.map{ |first, last| + t[:first_name].matches("%#{first}%").and(t[:last_name].matches("%#{last}%")) + } + rest.map{|r| scope = scope.or(r)} if scope + scope else - "upper(first_name) LIKE upper('%#{query}%') OR upper(last_name) LIKE upper('%#{query}%')" + t[:first_name].matches("%#{query}%").or(t[:last_name].matches("%#{query}%")) end - where("#{name_query} OR upper(email) LIKE upper(:m) OR upper(alt_email) LIKE upper(:m) OR phone LIKE :m OR mobile LIKE :m", :m => "%#{query}%") + + other = t[:email].matches("%#{query}%").or(t[:alt_email].matches("%#{query}%")) + other = other.or(t[:phone].matches("%#{query}%")).or(t[:mobile].matches("%#{query}%")) + + where( name_query.nil? ? other : name_query.or(other) ) } uses_user_permissions diff --git a/spec/models/entities/contact_spec.rb b/spec/models/entities/contact_spec.rb index ff21c2534e..5928946887 100644 --- a/spec/models/entities/contact_spec.rb +++ b/spec/models/entities/contact_spec.rb @@ -208,4 +208,45 @@ describe "permissions" do it_should_behave_like Ability, Contact end + + describe "text_search" do + + before(:each) do + @contact = FactoryGirl.create(:contact, :first_name => "Bob", :last_name => "Dillion", :email => 'bob_dillion@example.com', :phone => '+1 123 456 789') + end + + it "should search first_name" do + Contact.text_search('Bob').should == [@contact] + end + + it "should search last_name" do + Contact.text_search('Dillion').should == [@contact] + end + + it "should search whole name" do + Contact.text_search('Bob Dillion').should == [@contact] + end + + it "should search whole name reversed" do + Contact.text_search('Dillion Bob').should == [@contact] + end + + it "should search email" do + Contact.text_search('example').should == [@contact] + end + + it "should search phone" do + Contact.text_search('123').should == [@contact] + end + + it "should not break with a single quote" do + contact2 = FactoryGirl.create(:contact, :first_name => "Shamus", :last_name => "O'Connell", :email => 'bob_dillion@example.com', :phone => '+1 123 456 789') + Contact.text_search("O'Connell").should == [contact2] + end + + it "should not break on special characters" do + Contact.text_search('@$%#^@!').should == [] + end + + end end