Index: vendor/plugins/acts_as_searchable/lib/acts_as_searchable.rb =================================================================== --- vendor/plugins/acts_as_searchable/lib/acts_as_searchable.rb (revision 5265) +++ vendor/plugins/acts_as_searchable/lib/acts_as_searchable.rb (working copy) @@ -31,8 +31,9 @@ # * :permission - permission required to search the model (default to :view_"objects") def acts_as_searchable(options = {}) return if self.included_modules.include?(Redmine::Acts::Searchable::InstanceMethods) - + cattr_accessor :searchable_options + options.assert_valid_keys(:result_type, :columns, :project_key, :date_column, :order_column, :permission, :include) self.searchable_options = options if searchable_options[:columns].nil? @@ -44,13 +45,14 @@ searchable_options[:project_key] ||= "#{table_name}.project_id" searchable_options[:date_column] ||= "#{table_name}.created_on" searchable_options[:order_column] ||= searchable_options[:date_column] - + searchable_options[:result_type] ||= self.name.underscore + # Permission needed to search this model searchable_options[:permission] = "view_#{self.name.underscore.pluralize}".to_sym unless searchable_options.has_key?(:permission) - + # Should we search custom fields on this model ? searchable_options[:search_custom_fields] = !reflect_on_association(:custom_values).nil? - + send :include, Redmine::Acts::Searchable::InstanceMethods end end @@ -60,6 +62,10 @@ base.extend ClassMethods end + def searchable_result_type + searchable_options[:result_type] + end + module ClassMethods # Searches the model for the given tokens # projects argument can be either nil (will search all projects), a project or an array of projects @@ -67,21 +73,21 @@ def search(tokens, projects=nil, options={}) tokens = [] << tokens unless tokens.is_a?(Array) projects = [] << projects unless projects.nil? || projects.is_a?(Array) - + find_options = {:include => searchable_options[:include]} find_options[:order] = "#{searchable_options[:order_column]} " + (options[:before] ? 'DESC' : 'ASC') - + limit_options = {} limit_options[:limit] = options[:limit] if options[:limit] if options[:offset] limit_options[:conditions] = "(#{searchable_options[:date_column]} " + (options[:before] ? '<' : '>') + "'#{connection.quoted_date(options[:offset])}')" end - + columns = searchable_options[:columns] columns = columns[0..0] if options[:titles_only] - + token_clauses = columns.collect {|column| "(LOWER(#{column}) LIKE ?)"} - + if !options[:titles_only] && searchable_options[:search_custom_fields] searchable_custom_field_ids = CustomField.find(:all, :select => 'id', @@ -94,19 +100,19 @@ token_clauses << custom_field_sql end end - + sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ') find_options[:conditions] = [sql, * (tokens.collect {|w| "%#{w.downcase}%"} * token_clauses.size).sort] - + project_conditions = [] project_conditions << (searchable_options[:permission].nil? ? Project.visible_by(User.current) : Project.allowed_to_condition(User.current, searchable_options[:permission])) project_conditions << "#{searchable_options[:project_key]} IN (#{projects.collect(&:id).join(',')})" unless projects.nil? - + results = [] results_count = 0 - + with_scope(:find => {:conditions => project_conditions.join(' AND ')}) do with_scope(:find => find_options) do results_count = count(:all) Index: app/models/issue.rb =================================================================== --- app/models/issue.rb (revision 5265) +++ app/models/issue.rb (working copy) @@ -42,10 +42,11 @@ :include => [:project, :journals], # sort by id so that limited eager loading doesn't break with postgresql :order_column => "#{table_name}.id" - acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"}, + + acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.initial_status}): #{o.subject}"}, :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}, :type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') } - + acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]}, :author_key => :author_id @@ -85,7 +86,18 @@ before_save :close_duplicates, :update_done_ratio_from_issue_status after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal after_destroy :update_parent_attributes - + + # Retrieves issue's original status from journal if modified since issue creation + def initial_status + + first_status_modification_journal = self.journals.first( { + :joins => :details, + :conditions => { JournalDetail.table_name => { :property => 'attr', :prop_key => 'status_id' } }, + :order => :created_on } ) + + first_status_modification_journal ? first_status_modification_journal.prev_status : self.status + end + # Returns a SQL conditions string used to find all issues visible by the specified user def self.visible_condition(user, options={}) Project.allowed_to_condition(user, :view_issues, options) Index: app/models/journal.rb =================================================================== --- app/models/journal.rb (revision 5265) +++ app/models/journal.rb (working copy) @@ -37,7 +37,7 @@ :find_options => {:include => [{:issue => :project}, :details, :user], :conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" + " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"} - + named_scope :visible, lambda {|*args| { :include => {:issue => :project}, :conditions => Issue.visible_condition(args.first || User.current) @@ -53,7 +53,12 @@ c = details.detect {|detail| detail.prop_key == 'status_id'} (c && c.value) ? IssueStatus.find_by_id(c.value.to_i) : nil end - + + def prev_status + c = details.detect {|detail| detail.prop_key == 'status_id'} + (c && c.old_value) ? IssueStatus.find_by_id(c.old_value.to_i) : nil + end + def new_value_for(prop) c = details.detect {|detail| detail.prop_key == prop} c ? c.value : nil Index: app/views/search/results/_changeset.html.erb =================================================================== --- app/views/search/results/_changeset.html.erb (revision 0) +++ app/views/search/results/_changeset.html.erb (revision 0) @@ -0,0 +1 @@ +<%= render :partial => "search/results/event", :object => changeset %> Index: app/views/search/results/_document.html.erb =================================================================== --- app/views/search/results/_document.html.erb (revision 0) +++ app/views/search/results/_document.html.erb (revision 0) @@ -0,0 +1 @@ +<%= render :partial => "search/results/event", :object => document %> Index: app/views/search/results/_event.html.erb =================================================================== --- app/views/search/results/_event.html.erb (revision 0) +++ app/views/search/results/_event.html.erb (revision 0) @@ -0,0 +1,3 @@ +