##// END OF EJS Templates
Merged r5944 from trunk....
Jean-Philippe Lang -
r5827:fabf54a36801
parent child
Show More
@@ -1,89 +1,88
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Journal < ActiveRecord::Base
18 class Journal < ActiveRecord::Base
19 belongs_to :journalized, :polymorphic => true
19 belongs_to :journalized, :polymorphic => true
20 # added as a quick fix to allow eager loading of the polymorphic association
20 # added as a quick fix to allow eager loading of the polymorphic association
21 # since always associated to an issue, for now
21 # since always associated to an issue, for now
22 belongs_to :issue, :foreign_key => :journalized_id
22 belongs_to :issue, :foreign_key => :journalized_id
23
23
24 belongs_to :user
24 belongs_to :user
25 has_many :details, :class_name => "JournalDetail", :dependent => :delete_all
25 has_many :details, :class_name => "JournalDetail", :dependent => :delete_all
26 attr_accessor :indice
26 attr_accessor :indice
27
27
28 acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" },
28 acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" },
29 :description => :notes,
29 :description => :notes,
30 :author => :user,
30 :author => :user,
31 :type => Proc.new {|o| (s = o.new_status) ? (s.is_closed? ? 'issue-closed' : 'issue-edit') : 'issue-note' },
31 :type => Proc.new {|o| (s = o.new_status) ? (s.is_closed? ? 'issue-closed' : 'issue-edit') : 'issue-note' },
32 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}}
32 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}}
33
33
34 acts_as_activity_provider :type => 'issues',
34 acts_as_activity_provider :type => 'issues',
35 :permission => :view_issues,
36 :author_key => :user_id,
35 :author_key => :user_id,
37 :find_options => {:include => [{:issue => :project}, :details, :user],
36 :find_options => {:include => [{:issue => :project}, :details, :user],
38 :conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
37 :conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
39 " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
38 " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
40
39
41 named_scope :visible, lambda {|*args| {
40 named_scope :visible, lambda {|*args| {
42 :include => {:issue => :project},
41 :include => {:issue => :project},
43 :conditions => Issue.visible_condition(args.shift || User.current, *args)
42 :conditions => Issue.visible_condition(args.shift || User.current, *args)
44 }}
43 }}
45
44
46 def save(*args)
45 def save(*args)
47 # Do not save an empty journal
46 # Do not save an empty journal
48 (details.empty? && notes.blank?) ? false : super
47 (details.empty? && notes.blank?) ? false : super
49 end
48 end
50
49
51 # Returns the new status if the journal contains a status change, otherwise nil
50 # Returns the new status if the journal contains a status change, otherwise nil
52 def new_status
51 def new_status
53 c = details.detect {|detail| detail.prop_key == 'status_id'}
52 c = details.detect {|detail| detail.prop_key == 'status_id'}
54 (c && c.value) ? IssueStatus.find_by_id(c.value.to_i) : nil
53 (c && c.value) ? IssueStatus.find_by_id(c.value.to_i) : nil
55 end
54 end
56
55
57 def new_value_for(prop)
56 def new_value_for(prop)
58 c = details.detect {|detail| detail.prop_key == prop}
57 c = details.detect {|detail| detail.prop_key == prop}
59 c ? c.value : nil
58 c ? c.value : nil
60 end
59 end
61
60
62 def editable_by?(usr)
61 def editable_by?(usr)
63 usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project)))
62 usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project)))
64 end
63 end
65
64
66 def project
65 def project
67 journalized.respond_to?(:project) ? journalized.project : nil
66 journalized.respond_to?(:project) ? journalized.project : nil
68 end
67 end
69
68
70 def attachments
69 def attachments
71 journalized.respond_to?(:attachments) ? journalized.attachments : nil
70 journalized.respond_to?(:attachments) ? journalized.attachments : nil
72 end
71 end
73
72
74 # Returns a string of css classes
73 # Returns a string of css classes
75 def css_classes
74 def css_classes
76 s = 'journal'
75 s = 'journal'
77 s << ' has-notes' unless notes.blank?
76 s << ' has-notes' unless notes.blank?
78 s << ' has-details' unless details.blank?
77 s << ' has-details' unless details.blank?
79 s
78 s
80 end
79 end
81
80
82 def notify?
81 def notify?
83 @notify != false
82 @notify != false
84 end
83 end
85
84
86 def notify=(arg)
85 def notify=(arg)
87 @notify = arg
86 @notify = arg
88 end
87 end
89 end
88 end
@@ -1,29 +1,36
1 ---
1 ---
2 journals_001:
2 journals_001:
3 created_on: <%= 2.days.ago.to_date.to_s(:db) %>
3 created_on: <%= 2.days.ago.to_date.to_s(:db) %>
4 notes: "Journal notes"
4 notes: "Journal notes"
5 id: 1
5 id: 1
6 journalized_type: Issue
6 journalized_type: Issue
7 user_id: 1
7 user_id: 1
8 journalized_id: 1
8 journalized_id: 1
9 journals_002:
9 journals_002:
10 created_on: <%= 1.days.ago.to_date.to_s(:db) %>
10 created_on: <%= 1.days.ago.to_date.to_s(:db) %>
11 notes: "Some notes with Redmine links: #2, r2."
11 notes: "Some notes with Redmine links: #2, r2."
12 id: 2
12 id: 2
13 journalized_type: Issue
13 journalized_type: Issue
14 user_id: 2
14 user_id: 2
15 journalized_id: 1
15 journalized_id: 1
16 journals_003:
16 journals_003:
17 created_on: <%= 1.days.ago.to_date.to_s(:db) %>
17 created_on: <%= 1.days.ago.to_date.to_s(:db) %>
18 notes: "A comment with inline image: !picture.jpg!"
18 notes: "A comment with inline image: !picture.jpg!"
19 id: 3
19 id: 3
20 journalized_type: Issue
20 journalized_type: Issue
21 user_id: 2
21 user_id: 2
22 journalized_id: 2
22 journalized_id: 2
23 journals_004:
23 journals_004:
24 created_on: <%= 1.days.ago.to_date.to_s(:db) %>
24 created_on: <%= 1.days.ago.to_date.to_s(:db) %>
25 notes: "A comment with a private version."
25 notes: "A comment with a private version."
26 id: 4
26 id: 4
27 journalized_type: Issue
27 journalized_type: Issue
28 user_id: 1
28 user_id: 1
29 journalized_id: 6
29 journalized_id: 6
30 journals_005:
31 id: 5
32 created_on: <%= 1.days.ago.to_date.to_s(:db) %>
33 notes: "A comment on a private issue."
34 user_id: 2
35 journalized_type: Issue
36 journalized_id: 14
@@ -1,92 +1,95
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class ActivityTest < ActiveSupport::TestCase
20 class ActivityTest < ActiveSupport::TestCase
21 fixtures :projects, :versions, :attachments, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
21 fixtures :projects, :versions, :attachments, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
22 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
22 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages
23
23
24 def setup
24 def setup
25 @project = Project.find(1)
25 @project = Project.find(1)
26 end
26 end
27
27
28 def test_activity_without_subprojects
28 def test_activity_without_subprojects
29 events = find_events(User.anonymous, :project => @project)
29 events = find_events(User.anonymous, :project => @project)
30 assert_not_nil events
30 assert_not_nil events
31
31
32 assert events.include?(Issue.find(1))
32 assert events.include?(Issue.find(1))
33 assert !events.include?(Issue.find(4))
33 assert !events.include?(Issue.find(4))
34 # subproject issue
34 # subproject issue
35 assert !events.include?(Issue.find(5))
35 assert !events.include?(Issue.find(5))
36 end
36 end
37
37
38 def test_activity_with_subprojects
38 def test_activity_with_subprojects
39 events = find_events(User.anonymous, :project => @project, :with_subprojects => 1)
39 events = find_events(User.anonymous, :project => @project, :with_subprojects => 1)
40 assert_not_nil events
40 assert_not_nil events
41
41
42 assert events.include?(Issue.find(1))
42 assert events.include?(Issue.find(1))
43 # subproject issue
43 # subproject issue
44 assert events.include?(Issue.find(5))
44 assert events.include?(Issue.find(5))
45 end
45 end
46
46
47 def test_global_activity_anonymous
47 def test_global_activity_anonymous
48 events = find_events(User.anonymous)
48 events = find_events(User.anonymous)
49 assert_not_nil events
49 assert_not_nil events
50
50
51 assert events.include?(Issue.find(1))
51 assert events.include?(Issue.find(1))
52 assert events.include?(Message.find(5))
52 assert events.include?(Message.find(5))
53 # Issue of a private project
53 # Issue of a private project
54 assert !events.include?(Issue.find(4))
54 assert !events.include?(Issue.find(4))
55 # Private issue and comment
56 assert !events.include?(Issue.find(14))
57 assert !events.include?(Journal.find(5))
55 end
58 end
56
59
57 def test_global_activity_logged_user
60 def test_global_activity_logged_user
58 events = find_events(User.find(2)) # manager
61 events = find_events(User.find(2)) # manager
59 assert_not_nil events
62 assert_not_nil events
60
63
61 assert events.include?(Issue.find(1))
64 assert events.include?(Issue.find(1))
62 # Issue of a private project the user belongs to
65 # Issue of a private project the user belongs to
63 assert events.include?(Issue.find(4))
66 assert events.include?(Issue.find(4))
64 end
67 end
65
68
66 def test_user_activity
69 def test_user_activity
67 user = User.find(2)
70 user = User.find(2)
68 events = Redmine::Activity::Fetcher.new(User.anonymous, :author => user).events(nil, nil, :limit => 10)
71 events = Redmine::Activity::Fetcher.new(User.anonymous, :author => user).events(nil, nil, :limit => 10)
69
72
70 assert(events.size > 0)
73 assert(events.size > 0)
71 assert(events.size <= 10)
74 assert(events.size <= 10)
72 assert_nil(events.detect {|e| e.event_author != user})
75 assert_nil(events.detect {|e| e.event_author != user})
73 end
76 end
74
77
75 def test_files_activity
78 def test_files_activity
76 f = Redmine::Activity::Fetcher.new(User.anonymous, :project => Project.find(1))
79 f = Redmine::Activity::Fetcher.new(User.anonymous, :project => Project.find(1))
77 f.scope = ['files']
80 f.scope = ['files']
78 events = f.events
81 events = f.events
79
82
80 assert_kind_of Array, events
83 assert_kind_of Array, events
81 assert events.include?(Attachment.find_by_container_type_and_container_id('Project', 1))
84 assert events.include?(Attachment.find_by_container_type_and_container_id('Project', 1))
82 assert events.include?(Attachment.find_by_container_type_and_container_id('Version', 1))
85 assert events.include?(Attachment.find_by_container_type_and_container_id('Version', 1))
83 assert_equal [Attachment], events.collect(&:class).uniq
86 assert_equal [Attachment], events.collect(&:class).uniq
84 assert_equal %w(Project Version), events.collect(&:container_type).uniq.sort
87 assert_equal %w(Project Version), events.collect(&:container_type).uniq.sort
85 end
88 end
86
89
87 private
90 private
88
91
89 def find_events(user, options={})
92 def find_events(user, options={})
90 Redmine::Activity::Fetcher.new(user, options).events(Date.today - 30, Date.today + 1)
93 Redmine::Activity::Fetcher.new(user, options).events(Date.today - 30, Date.today + 1)
91 end
94 end
92 end
95 end
General Comments 0
You need to be logged in to leave comments. Login now