OSDN Git Service

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