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 »