Private issue notes (#1554).
Adds 2 new permissions for viewing/adding private comments to issues. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10547 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
+26
-4
@@ -28,6 +28,14 @@ class Issue < ActiveRecord::Base
|
||||
belongs_to :category, :class_name => 'IssueCategory', :foreign_key => 'category_id'
|
||||
|
||||
has_many :journals, :as => :journalized, :dependent => :destroy
|
||||
has_many :visible_journals,
|
||||
:class_name => 'Journal',
|
||||
:as => :journalized,
|
||||
:conditions => Proc.new {
|
||||
["(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(User.current, :view_private_notes)}))", false]
|
||||
},
|
||||
:readonly => true
|
||||
|
||||
has_many :time_entries, :dependent => :delete_all
|
||||
has_and_belongs_to_many :changesets, :order => "#{Changeset.table_name}.committed_on ASC, #{Changeset.table_name}.id ASC"
|
||||
|
||||
@@ -39,7 +47,7 @@ class Issue < ActiveRecord::Base
|
||||
acts_as_customizable
|
||||
acts_as_watchable
|
||||
acts_as_searchable :columns => ['subject', "#{table_name}.description", "#{Journal.table_name}.notes"],
|
||||
:include => [:project, :journals],
|
||||
:include => [:project, :visible_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}"},
|
||||
@@ -52,6 +60,7 @@ class Issue < ActiveRecord::Base
|
||||
DONE_RATIO_OPTIONS = %w(issue_field issue_status)
|
||||
|
||||
attr_reader :current_journal
|
||||
delegate :notes, :notes=, :private_notes, :private_notes=, :to => :current_journal, :allow_nil => true
|
||||
|
||||
validates_presence_of :subject, :priority, :project, :tracker, :author, :status
|
||||
|
||||
@@ -335,6 +344,7 @@ class Issue < ActiveRecord::Base
|
||||
'custom_field_values',
|
||||
'custom_fields',
|
||||
'lock_version',
|
||||
'notes',
|
||||
:if => lambda {|issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) }
|
||||
|
||||
safe_attributes 'status_id',
|
||||
@@ -342,8 +352,15 @@ class Issue < ActiveRecord::Base
|
||||
'fixed_version_id',
|
||||
'done_ratio',
|
||||
'lock_version',
|
||||
'notes',
|
||||
:if => lambda {|issue, user| issue.new_statuses_allowed_to(user).any? }
|
||||
|
||||
safe_attributes 'notes',
|
||||
:if => lambda {|issue, user| user.allowed_to?(:add_issue_notes, issue.project)}
|
||||
|
||||
safe_attributes 'private_notes',
|
||||
:if => lambda {|issue, user| !issue.new_record? && user.allowed_to?(:set_notes_private, issue.project)}
|
||||
|
||||
safe_attributes 'watcher_user_ids',
|
||||
:if => lambda {|issue, user| issue.new_record? && user.allowed_to?(:add_issue_watchers, issue.project)}
|
||||
|
||||
@@ -715,8 +732,8 @@ class Issue < ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the mail adresses of users that should be notified
|
||||
def recipients
|
||||
# Returns the users that should be notified
|
||||
def notified_users
|
||||
notified = []
|
||||
# Author and assignee are always notified unless they have been
|
||||
# locked or don't want to be notified
|
||||
@@ -733,7 +750,12 @@ class Issue < ActiveRecord::Base
|
||||
notified.uniq!
|
||||
# Remove users that can not view the issue
|
||||
notified.reject! {|user| !visible?(user)}
|
||||
notified.collect(&:mail)
|
||||
notified
|
||||
end
|
||||
|
||||
# Returns the email addresses that should be notified
|
||||
def recipients
|
||||
notified_users.collect(&:mail)
|
||||
end
|
||||
|
||||
# Returns the number of hours spent on this issue
|
||||
|
||||
+39
-4
@@ -37,10 +37,15 @@ class Journal < ActiveRecord::Base
|
||||
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" +
|
||||
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
|
||||
|
||||
scope :visible, lambda {|*args| {
|
||||
:include => {:issue => :project},
|
||||
:conditions => Issue.visible_condition(args.shift || User.current, *args)
|
||||
}}
|
||||
before_create :split_private_notes
|
||||
|
||||
scope :visible, lambda {|*args|
|
||||
user = args.shift || User.current
|
||||
|
||||
includes(:issue => :project).
|
||||
where(Issue.visible_condition(user, *args)).
|
||||
where("(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes, *args)}))", false)
|
||||
}
|
||||
|
||||
def save(*args)
|
||||
# Do not save an empty journal
|
||||
@@ -75,6 +80,7 @@ class Journal < ActiveRecord::Base
|
||||
s = 'journal'
|
||||
s << ' has-notes' unless notes.blank?
|
||||
s << ' has-details' unless details.blank?
|
||||
s << ' private-notes' if private_notes?
|
||||
s
|
||||
end
|
||||
|
||||
@@ -85,4 +91,33 @@ class Journal < ActiveRecord::Base
|
||||
def notify=(arg)
|
||||
@notify = arg
|
||||
end
|
||||
|
||||
def recipients
|
||||
notified = journalized.notified_users
|
||||
if private_notes?
|
||||
notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)}
|
||||
end
|
||||
notified.map(&:mail)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def split_private_notes
|
||||
if private_notes?
|
||||
if notes.present?
|
||||
if details.any?
|
||||
# Split the journal (notes/changes) so we don't have half-private journals
|
||||
journal = Journal.new(:journalized => journalized, :user => user, :notes => nil, :private_notes => false)
|
||||
journal.details = details
|
||||
journal.save
|
||||
self.details = []
|
||||
self.created_on = journal.created_on
|
||||
end
|
||||
else
|
||||
# Blank notes should not be private
|
||||
self.private_notes = false
|
||||
end
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -181,7 +181,7 @@ class MailHandler < ActionMailer::Base
|
||||
end
|
||||
|
||||
# Adds a note to an existing issue
|
||||
def receive_issue_reply(issue_id)
|
||||
def receive_issue_reply(issue_id, from_journal=nil)
|
||||
issue = Issue.find_by_id(issue_id)
|
||||
return unless issue
|
||||
# check permission
|
||||
@@ -196,6 +196,10 @@ class MailHandler < ActionMailer::Base
|
||||
@@handler_options[:issue].clear
|
||||
|
||||
journal = issue.init_journal(user)
|
||||
if from_journal && from_journal.private_notes?
|
||||
# If the received email was a reply to a private note, make the added note private
|
||||
issue.private_notes = true
|
||||
end
|
||||
issue.safe_attributes = issue_attributes_from_keywords(issue)
|
||||
issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
|
||||
journal.notes = cleaned_up_text_body
|
||||
@@ -211,7 +215,7 @@ class MailHandler < ActionMailer::Base
|
||||
def receive_journal_reply(journal_id)
|
||||
journal = Journal.find_by_id(journal_id)
|
||||
if journal && journal.journalized_type == 'Issue'
|
||||
receive_issue_reply(journal.journalized_id)
|
||||
receive_issue_reply(journal.journalized_id, journal)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class Mailer < ActionMailer::Base
|
||||
message_id journal
|
||||
references issue
|
||||
@author = journal.user
|
||||
recipients = issue.recipients
|
||||
recipients = journal.recipients
|
||||
# Watchers in cc
|
||||
cc = issue.watcher_recipients - recipients
|
||||
s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
|
||||
|
||||
Reference in New Issue
Block a user