Defect #7293 » activity_new_issue_event_status_v4.patch
vendor/plugins/acts_as_searchable/lib/acts_as_searchable.rb (working copy) | ||
---|---|---|
31 | 31 |
# * :permission - permission required to search the model (default to :view_"objects") |
32 | 32 |
def acts_as_searchable(options = {}) |
33 | 33 |
return if self.included_modules.include?(Redmine::Acts::Searchable::InstanceMethods) |
34 |
|
|
34 |
|
|
35 | 35 |
cattr_accessor :searchable_options |
36 |
options.assert_valid_keys(:result_type, :columns, :project_key, :date_column, :order_column, :permission, :include) |
|
36 | 37 |
self.searchable_options = options |
37 | 38 | |
38 | 39 |
if searchable_options[:columns].nil? |
... | ... | |
44 | 45 |
searchable_options[:project_key] ||= "#{table_name}.project_id" |
45 | 46 |
searchable_options[:date_column] ||= "#{table_name}.created_on" |
46 | 47 |
searchable_options[:order_column] ||= searchable_options[:date_column] |
47 |
|
|
48 |
searchable_options[:result_type] ||= self.name.underscore |
|
49 |
|
|
48 | 50 |
# Permission needed to search this model |
49 | 51 |
searchable_options[:permission] = "view_#{self.name.underscore.pluralize}".to_sym unless searchable_options.has_key?(:permission) |
50 |
|
|
52 |
|
|
51 | 53 |
# Should we search custom fields on this model ? |
52 | 54 |
searchable_options[:search_custom_fields] = !reflect_on_association(:custom_values).nil? |
53 |
|
|
55 |
|
|
54 | 56 |
send :include, Redmine::Acts::Searchable::InstanceMethods |
55 | 57 |
end |
56 | 58 |
end |
... | ... | |
60 | 62 |
base.extend ClassMethods |
61 | 63 |
end |
62 | 64 | |
65 |
def searchable_result_type |
|
66 |
searchable_options[:result_type] |
|
67 |
end |
|
68 |
|
|
63 | 69 |
module ClassMethods |
64 | 70 |
# Searches the model for the given tokens |
65 | 71 |
# projects argument can be either nil (will search all projects), a project or an array of projects |
... | ... | |
67 | 73 |
def search(tokens, projects=nil, options={}) |
68 | 74 |
tokens = [] << tokens unless tokens.is_a?(Array) |
69 | 75 |
projects = [] << projects unless projects.nil? || projects.is_a?(Array) |
70 |
|
|
76 |
|
|
71 | 77 |
find_options = {:include => searchable_options[:include]} |
72 | 78 |
find_options[:order] = "#{searchable_options[:order_column]} " + (options[:before] ? 'DESC' : 'ASC') |
73 |
|
|
79 |
|
|
74 | 80 |
limit_options = {} |
75 | 81 |
limit_options[:limit] = options[:limit] if options[:limit] |
76 | 82 |
if options[:offset] |
77 | 83 |
limit_options[:conditions] = "(#{searchable_options[:date_column]} " + (options[:before] ? '<' : '>') + "'#{connection.quoted_date(options[:offset])}')" |
78 | 84 |
end |
79 |
|
|
85 |
|
|
80 | 86 |
columns = searchable_options[:columns] |
81 | 87 |
columns = columns[0..0] if options[:titles_only] |
82 |
|
|
88 |
|
|
83 | 89 |
token_clauses = columns.collect {|column| "(LOWER(#{column}) LIKE ?)"} |
84 |
|
|
90 |
|
|
85 | 91 |
if !options[:titles_only] && searchable_options[:search_custom_fields] |
86 | 92 |
searchable_custom_field_ids = CustomField.find(:all, |
87 | 93 |
:select => 'id', |
... | ... | |
94 | 100 |
token_clauses << custom_field_sql |
95 | 101 |
end |
96 | 102 |
end |
97 |
|
|
103 |
|
|
98 | 104 |
sql = (['(' + token_clauses.join(' OR ') + ')'] * tokens.size).join(options[:all_words] ? ' AND ' : ' OR ') |
99 | 105 |
|
100 | 106 |
find_options[:conditions] = [sql, * (tokens.collect {|w| "%#{w.downcase}%"} * token_clauses.size).sort] |
101 |
|
|
107 |
|
|
102 | 108 |
project_conditions = [] |
103 | 109 |
project_conditions << (searchable_options[:permission].nil? ? Project.visible_by(User.current) : |
104 | 110 |
Project.allowed_to_condition(User.current, searchable_options[:permission])) |
105 | 111 |
project_conditions << "#{searchable_options[:project_key]} IN (#{projects.collect(&:id).join(',')})" unless projects.nil? |
106 |
|
|
112 |
|
|
107 | 113 |
results = [] |
108 | 114 |
results_count = 0 |
109 |
|
|
115 |
|
|
110 | 116 |
with_scope(:find => {:conditions => project_conditions.join(' AND ')}) do |
111 | 117 |
with_scope(:find => find_options) do |
112 | 118 |
results_count = count(:all) |
app/models/issue.rb (working copy) | ||
---|---|---|
42 | 42 |
:include => [:project, :journals], |
43 | 43 |
# sort by id so that limited eager loading doesn't break with postgresql |
44 | 44 |
:order_column => "#{table_name}.id" |
45 |
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"}, |
|
45 |
|
|
46 |
acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.initial_status}): #{o.subject}"}, |
|
46 | 47 |
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}}, |
47 | 48 |
:type => Proc.new {|o| 'issue' + (o.closed? ? ' closed' : '') } |
48 |
|
|
49 |
|
|
49 | 50 |
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]}, |
50 | 51 |
:author_key => :author_id |
51 | 52 | |
... | ... | |
85 | 86 |
before_save :close_duplicates, :update_done_ratio_from_issue_status |
86 | 87 |
after_save :reschedule_following_issues, :update_nested_set_attributes, :update_parent_attributes, :create_journal |
87 | 88 |
after_destroy :update_parent_attributes |
88 |
|
|
89 |
|
|
90 |
# Retrieves issue's original status from journal if modified since issue creation |
|
91 |
def initial_status |
|
92 |
|
|
93 |
first_status_modification_journal = self.journals.first( { |
|
94 |
:joins => :details, |
|
95 |
:conditions => { JournalDetail.table_name => { :property => 'attr', :prop_key => 'status_id' } }, |
|
96 |
:order => :created_on } ) |
|
97 |
|
|
98 |
first_status_modification_journal ? first_status_modification_journal.prev_status : self.status |
|
99 |
end |
|
100 |
|
|
89 | 101 |
# Returns a SQL conditions string used to find all issues visible by the specified user |
90 | 102 |
def self.visible_condition(user, options={}) |
91 | 103 |
Project.allowed_to_condition(user, :view_issues, options) |
app/models/journal.rb (working copy) | ||
---|---|---|
37 | 37 |
:find_options => {:include => [{:issue => :project}, :details, :user], |
38 | 38 |
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" + |
39 | 39 |
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"} |
40 |
|
|
40 |
|
|
41 | 41 |
named_scope :visible, lambda {|*args| { |
42 | 42 |
:include => {:issue => :project}, |
43 | 43 |
:conditions => Issue.visible_condition(args.first || User.current) |
... | ... | |
53 | 53 |
c = details.detect {|detail| detail.prop_key == 'status_id'} |
54 | 54 |
(c && c.value) ? IssueStatus.find_by_id(c.value.to_i) : nil |
55 | 55 |
end |
56 |
|
|
56 |
|
|
57 |
def prev_status |
|
58 |
c = details.detect {|detail| detail.prop_key == 'status_id'} |
|
59 |
(c && c.old_value) ? IssueStatus.find_by_id(c.old_value.to_i) : nil |
|
60 |
end |
|
61 |
|
|
57 | 62 |
def new_value_for(prop) |
58 | 63 |
c = details.detect {|detail| detail.prop_key == prop} |
59 | 64 |
c ? c.value : nil |
app/views/search/results/_changeset.html.erb (revision 0) | ||
---|---|---|
1 |
<%= render :partial => "search/results/event", :object => changeset %> |
app/views/search/results/_document.html.erb (revision 0) | ||
---|---|---|
1 |
<%= render :partial => "search/results/event", :object => document %> |
app/views/search/results/_event.html.erb (revision 0) | ||
---|---|---|
1 |
<dt class="<%= event.event_type %>"><%= content_tag('span', h(event.project), :class => 'project') unless @project == event.project %> <%= link_to highlight_tokens(truncate(event.event_title, :length => 255), @tokens), event.event_url %></dt> |
|
2 |
<dd><span class="description"><%= highlight_tokens(event.event_description, @tokens) %></span> |
|
3 |
<span class="author"><%= format_time(event.event_datetime) %></span></dd> |
app/views/search/results/_issue.html.erb (revision 0) | ||
---|---|---|
1 |
<dt class="<%= issue.event_type %>"> |
|
2 |
<%= content_tag('span', h(issue.project), :class => 'project') unless @project == issue.project %> |
|
3 |
<%= link_to highlight_tokens(truncate("#{issue.tracker.name} ##{issue.id} (#{issue.status()}): #{issue.subject}", :length => 255), @tokens), issue.event_url %></dt> |
|
4 |
<dd><span class="description"><%= highlight_tokens(issue.event_description, @tokens) %></span> |
|
5 |
<span class="datetime"><%= format_time(issue.event_datetime) %></span></dd> |
app/views/search/results/_journal.html.erb (revision 0) | ||
---|---|---|
1 |
<%= render :partial => "search/results/event", :object => journal %> |
app/views/search/results/_message.html.erb (revision 0) | ||
---|---|---|
1 |
<%= render :partial => "search/results/event", :object => message %> |
app/views/search/results/_news.html.erb (revision 0) | ||
---|---|---|
1 |
<%= render :partial => "search/results/event", :object => news %> |
app/views/search/results/_project.html.erb (revision 0) | ||
---|---|---|
1 |
<%= render :partial => "search/results/event", :object => project %> |
app/views/search/results/_wiki_page.html.erb (revision 0) | ||
---|---|---|
1 |
<%= render :partial => "search/results/event", :object => wiki_page %> |
app/views/search/index.rhtml (working copy) | ||
---|---|---|
22 | 22 |
<div id="search-results-counts"> |
23 | 23 |
<%= render_results_by_type(@results_by_type) unless @scope.size == 1 %> |
24 | 24 |
</div> |
25 |
|
|
25 |
|
|
26 | 26 |
<h3><%= l(:label_result_plural) %> (<%= @results_by_type.values.sum %>)</h3> |
27 | 27 |
<dl id="search-results"> |
28 |
<% @results.each do |e| %> |
|
29 |
<dt class="<%= e.event_type %>"><%= content_tag('span', h(e.project), :class => 'project') unless @project == e.project %> <%= link_to highlight_tokens(truncate(e.event_title, :length => 255), @tokens), e.event_url %></dt> |
|
30 |
<dd><span class="description"><%= highlight_tokens(e.event_description, @tokens) %></span> |
|
31 |
<span class="author"><%= format_time(e.event_datetime) %></span></dd> |
|
28 |
<% @results.each do |r| %> |
|
29 |
<%= render :partial => "search/results/#{r.searchable_result_type.underscore}", :object => r %> |
|
32 | 30 |
<% end %> |
33 | 31 |
</dl> |
34 | 32 |
<% end %> |
test/unit/activity_test.rb (working copy) | ||
---|---|---|
24 | 24 |
def setup |
25 | 25 |
@project = Project.find(1) |
26 | 26 |
end |
27 |
|
|
27 |
|
|
28 |
def test_activity_contains_issue_status_update_events |
|
29 |
events = find_events(User.anonymous, :project => @project) |
|
30 |
assert_not_nil events |
|
31 |
|
|
32 |
events.sort! { |x,y| x.event_datetime <=> y.event_datetime } |
|
33 |
|
|
34 |
issue_creation_events = events.find_all { |event| event == Issue.find(8) } |
|
35 |
|
|
36 |
assert_equal 1, issue_creation_events.size |
|
37 |
assert_equal IssueStatus.find_by_name('New'), issue_creation_events.first.initial_status |
|
38 |
|
|
39 |
issue_status_update_events = events.find_all { |event| event.is_a?(Journal) && event.issue == Issue.find(8) } |
|
40 |
|
|
41 |
assert_equal 2, issue_status_update_events.size |
|
42 |
assert_equal IssueStatus.find_by_name('New'), issue_status_update_events.first.prev_status |
|
43 |
assert_equal IssueStatus.find_by_name('Resolved'), issue_status_update_events.first.new_status |
|
44 |
|
|
45 |
assert_equal IssueStatus.find_by_name('Resolved'), issue_status_update_events.last.prev_status |
|
46 |
assert_equal IssueStatus.find_by_name('Closed'), issue_status_update_events.last.new_status |
|
47 |
end |
|
48 |
|
|
28 | 49 |
def test_activity_without_subprojects |
29 | 50 |
events = find_events(User.anonymous, :project => @project) |
30 | 51 |
assert_not_nil events |
test/fixtures/journal_details.yml (working copy) | ||
---|---|---|
34 | 34 |
value: New value |
35 | 35 |
prop_key: 2 |
36 | 36 |
journal_id: 3 |
37 |
journal_details_006: |
|
38 |
old_value: "1" |
|
39 |
property: attr |
|
40 |
id: 6 |
|
41 |
value: "3" |
|
42 |
prop_key: status_id |
|
43 |
journal_id: 5 |
|
44 |
journal_details_007: |
|
45 |
old_value: "3" |
|
46 |
property: attr |
|
47 |
id: 7 |
|
48 |
value: "5" |
|
49 |
prop_key: status_id |
|
50 |
journal_id: 6 |
test/fixtures/issues.yml (working copy) | ||
---|---|---|
132 | 132 |
lft: 1 |
133 | 133 |
rgt: 2 |
134 | 134 |
issues_008: |
135 |
created_on: <%= 10.days.ago.to_date.to_s(:db) %>
|
|
135 |
created_on: <%= 12.days.ago.to_date.to_s(:db) %>
|
|
136 | 136 |
project_id: 1 |
137 | 137 |
updated_on: <%= 10.days.ago.to_date.to_s(:db) %> |
138 | 138 |
priority_id: 5 |
test/fixtures/journals.yml (working copy) | ||
---|---|---|
27 | 27 |
journalized_type: Issue |
28 | 28 |
user_id: 1 |
29 | 29 |
journalized_id: 6 |
30 |
journals_005: |
|
31 |
created_on: <%= 11.days.ago.to_date.to_s(:db) %> |
|
32 |
notes: "Resolving issue 8." |
|
33 |
id: 5 |
|
34 |
journalized_type: Issue |
|
35 |
user_id: 1 |
|
36 |
journalized_id: 8 |
|
37 |
journals_006: |
|
38 |
created_on: <%= 10.days.ago.to_date.to_s(:db) %> |
|
39 |
notes: "Closing issue 8." |
|
40 |
id: 6 |
|
41 |
journalized_type: Issue |
|
42 |
user_id: 1 |
|
43 |
journalized_id: 8 |
- « Previous
- 1
- 2
- 3
- 4
- Next »