annotate vendor/plugins/acts_as_searchable/lib/acts_as_searchable.rb @ 8:0c83d98252d9 yuya

* Add custom repo prefix and proper auth realm, remove auth cache (seems like an unwise feature), pass DB handle around, various other bits of tidying
author Chris Cannam
date Thu, 12 Aug 2010 15:31:37 +0100
parents 513646585e45
children cbce1fd3b1b7
rev   line source
Chris@0 1 # redMine - project management software
Chris@0 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
Chris@0 3 #
Chris@0 4 # This program is free software; you can redistribute it and/or
Chris@0 5 # modify it under the terms of the GNU General Public License
Chris@0 6 # as published by the Free Software Foundation; either version 2
Chris@0 7 # of the License, or (at your option) any later version.
Chris@0 8 #
Chris@0 9 # This program is distributed in the hope that it will be useful,
Chris@0 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@0 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@0 12 # GNU General Public License for more details.
Chris@0 13 #
Chris@0 14 # You should have received a copy of the GNU General Public License
Chris@0 15 # along with this program; if not, write to the Free Software
Chris@0 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Chris@0 17
Chris@0 18 module Redmine
Chris@0 19 module Acts
Chris@0 20 module Searchable
Chris@0 21 def self.included(base)
Chris@0 22 base.extend ClassMethods
Chris@0 23 end
Chris@0 24
Chris@0 25 module ClassMethods
Chris@0 26 # Options:
Chris@0 27 # * :columns - a column or an array of columns to search
Chris@0 28 # * :project_key - project foreign key (default to project_id)
Chris@0 29 # * :date_column - name of the datetime column (default to created_on)
Chris@0 30 # * :sort_order - name of the column used to sort results (default to :date_column or created_on)
Chris@0 31 # * :permission - permission required to search the model (default to :view_"objects")
Chris@0 32 def acts_as_searchable(options = {})
Chris@0 33 return if self.included_modules.include?(Redmine::Acts::Searchable::InstanceMethods)
Chris@0 34
Chris@0 35 cattr_accessor :searchable_options
Chris@0 36 self.searchable_options = options
Chris@0 37
Chris@0 38 if searchable_options[:columns].nil?
Chris@0 39 raise 'No searchable column defined.'
Chris@0 40 elsif !searchable_options[:columns].is_a?(Array)
Chris@0 41 searchable_options[:columns] = [] << searchable_options[:columns]
Chris@0 42 end
Chris@0 43
Chris@0 44 searchable_options[:project_key] ||= "#{table_name}.project_id"
Chris@0 45 searchable_options[:date_column] ||= "#{table_name}.created_on"
Chris@0 46 searchable_options[:order_column] ||= searchable_options[:date_column]
Chris@0 47
Chris@0 48 # Permission needed to search this model
Chris@0 49 searchable_options[:permission] = "view_#{self.name.underscore.pluralize}".to_sym unless searchable_options.has_key?(:permission)
Chris@0 50
Chris@0 51 # Should we search custom fields on this model ?
Chris@0 52 searchable_options[:search_custom_fields] = !reflect_on_association(:custom_values).nil?
Chris@0 53
Chris@0 54 send :include, Redmine::Acts::Searchable::InstanceMethods
Chris@0 55 end
Chris@0 56 end
Chris@0 57
Chris@0 58 module InstanceMethods
Chris@0 59 def self.included(base)
Chris@0 60 base.extend ClassMethods
Chris@0 61 end
Chris@0 62
Chris@0 63 module ClassMethods
Chris@0 64 # Searches the model for the given tokens
Chris@0 65 # projects argument can be either nil (will search all projects), a project or an array of projects
Chris@0 66 # Returns the results and the results count
Chris@0 67 def search(tokens, projects=nil, options={})
Chris@0 68 tokens = [] << tokens unless tokens.is_a?(Array)
Chris@0 69 projects = [] << projects unless projects.nil? || projects.is_a?(Array)
Chris@0 70
Chris@0 71 find_options = {:include => searchable_options[:include]}
Chris@0 72 find_options[:order] = "#{searchable_options[:order_column]} " + (options[:before] ? 'DESC' : 'ASC')
Chris@0 73
Chris@0 74 limit_options = {}
Chris@0 75 limit_options[:limit] = options[:limit] if options[:limit]
Chris@0 76 if options[:offset]
Chris@0 77 limit_options[:conditions] = "(#{searchable_options[:date_column]} " + (options[:before] ? '<' : '>') + "'#{connection.quoted_date(options[:offset])}')"
Chris@0 78 end
Chris@0 79
Chris@0 80 columns = searchable_options[:columns]
Chris@0 81 columns = columns[0..0] if options[:titles_only]
Chris@0 82
Chris@0 83 token_clauses = columns.collect {|column| "(LOWER(#{column}) LIKE ?)"}
Chris@0 84
Chris@0 85 if !options[:titles_only] && searchable_options[:search_custom_fields]
Chris@0 86 searchable_custom_field_ids = CustomField.find(:all,
Chris@0 87 :select => 'id',
Chris@0 88 :conditions => { :type => "#{self.name}CustomField",
Chris@0 89 :searchable => true }).collect(&:id)
Chris@0 90 if searchable_custom_field_ids.any?
Chris@0 91 custom_field_sql = "#{table_name}.id IN (SELECT customized_id FROM #{CustomValue.table_name}" +
Chris@0 92 " WHERE customized_type='#{self.name}' AND customized_id=#{table_name}.id AND LOWER(value) LIKE ?" +
Chris@0 93 " AND #{CustomValue.table_name}.custom_field_id IN (#{searchable_custom_field_ids.join(',')}))"
Chris@0 94 token_clauses << custom_field_sql
Chris@0 95 end
Chris@0 96 end
Chris@0 97
Chris@0 98 sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ')
Chris@0 99
Chris@0 100 find_options[:conditions] = [sql, * (tokens.collect {|w| "%#{w.downcase}%"} * token_clauses.size).sort]
Chris@0 101
Chris@0 102 project_conditions = []
Chris@0 103 project_conditions << (searchable_options[:permission].nil? ? Project.visible_by(User.current) :
Chris@0 104 Project.allowed_to_condition(User.current, searchable_options[:permission]))
Chris@0 105 project_conditions << "#{searchable_options[:project_key]} IN (#{projects.collect(&:id).join(',')})" unless projects.nil?
Chris@0 106
Chris@0 107 results = []
Chris@0 108 results_count = 0
Chris@0 109
Chris@0 110 with_scope(:find => {:conditions => project_conditions.join(' AND ')}) do
Chris@0 111 with_scope(:find => find_options) do
Chris@0 112 results_count = count(:all)
Chris@0 113 results = find(:all, limit_options)
Chris@0 114 end
Chris@0 115 end
Chris@0 116 [results, results_count]
Chris@0 117 end
Chris@0 118 end
Chris@0 119 end
Chris@0 120 end
Chris@0 121 end
Chris@0 122 end