To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / app / models / journal.rb @ 1592:72d9219f2f19
History | View | Annotate | Download (6.46 KB)
| 1 |
# Redmine - project management software
|
|---|---|
| 2 |
# Copyright (C) 2006-2014 Jean-Philippe Lang
|
| 3 |
#
|
| 4 |
# This program is free software; you can redistribute it and/or
|
| 5 |
# modify it under the terms of the GNU General Public License
|
| 6 |
# as published by the Free Software Foundation; either version 2
|
| 7 |
# of the License, or (at your option) any later version.
|
| 8 |
#
|
| 9 |
# This program is distributed in the hope that it will be useful,
|
| 10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 12 |
# GNU General Public License for more details.
|
| 13 |
#
|
| 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
|
| 16 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
| 17 |
|
| 18 |
class Journal < ActiveRecord::Base |
| 19 |
belongs_to :journalized, :polymorphic => true |
| 20 |
# added as a quick fix to allow eager loading of the polymorphic association
|
| 21 |
# since always associated to an issue, for now
|
| 22 |
belongs_to :issue, :foreign_key => :journalized_id |
| 23 |
|
| 24 |
belongs_to :user
|
| 25 |
has_many :details, :class_name => "JournalDetail", :dependent => :delete_all |
| 26 |
attr_accessor :indice
|
| 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}" }, |
| 29 |
:description => :notes, |
| 30 |
:author => :user, |
| 31 |
:group => :issue, |
| 32 |
:type => Proc.new {|o| (s = o.new_status) ? (s.is_closed? ? 'issue-closed' : 'issue-edit') : 'issue-note' }, |
| 33 |
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}} |
| 34 |
|
| 35 |
acts_as_activity_provider :type => 'issues', |
| 36 |
:author_key => :user_id, |
| 37 |
:find_options => {:include => [{:issue => :project}, :details, :user], |
| 38 |
:conditions => "#{Journal.table_name}.journalized_type = 'Issue' AND" + |
| 39 |
" (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')"}
|
| 40 |
|
| 41 |
before_create :split_private_notes
|
| 42 |
after_create :send_notification
|
| 43 |
|
| 44 |
scope :visible, lambda {|*args|
|
| 45 |
user = args.shift || User.current
|
| 46 |
|
| 47 |
includes(:issue => :project). |
| 48 |
where(Issue.visible_condition(user, *args)).
|
| 49 |
where("(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes, *args)}))", false) |
| 50 |
} |
| 51 |
|
| 52 |
def save(*args) |
| 53 |
# Do not save an empty journal
|
| 54 |
(details.empty? && notes.blank?) ? false : super |
| 55 |
end
|
| 56 |
|
| 57 |
# Returns journal details that are visible to user
|
| 58 |
def visible_details(user=User.current) |
| 59 |
details.select do |detail|
|
| 60 |
if detail.property == 'cf' |
| 61 |
detail.custom_field && detail.custom_field.visible_by?(project, user) |
| 62 |
elsif detail.property == 'relation' |
| 63 |
Issue.find_by_id(detail.value || detail.old_value).try(:visible?, user) |
| 64 |
else
|
| 65 |
true
|
| 66 |
end
|
| 67 |
end
|
| 68 |
end
|
| 69 |
|
| 70 |
def each_notification(users, &block) |
| 71 |
if users.any?
|
| 72 |
users_by_details_visibility = users.group_by do |user|
|
| 73 |
visible_details(user) |
| 74 |
end
|
| 75 |
users_by_details_visibility.each do |visible_details, users|
|
| 76 |
if notes? || visible_details.any?
|
| 77 |
yield(users)
|
| 78 |
end
|
| 79 |
end
|
| 80 |
end
|
| 81 |
end
|
| 82 |
|
| 83 |
# Returns the new status if the journal contains a status change, otherwise nil
|
| 84 |
def new_status |
| 85 |
c = details.detect {|detail| detail.prop_key == 'status_id'}
|
| 86 |
(c && c.value) ? IssueStatus.find_by_id(c.value.to_i) : nil |
| 87 |
end
|
| 88 |
|
| 89 |
def new_value_for(prop) |
| 90 |
c = details.detect {|detail| detail.prop_key == prop}
|
| 91 |
c ? c.value : nil
|
| 92 |
end
|
| 93 |
|
| 94 |
def editable_by?(usr) |
| 95 |
usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project))) |
| 96 |
end
|
| 97 |
|
| 98 |
def project |
| 99 |
journalized.respond_to?(:project) ? journalized.project : nil |
| 100 |
end
|
| 101 |
|
| 102 |
def attachments |
| 103 |
journalized.respond_to?(:attachments) ? journalized.attachments : nil |
| 104 |
end
|
| 105 |
|
| 106 |
# Returns a string of css classes
|
| 107 |
def css_classes |
| 108 |
s = 'journal'
|
| 109 |
s << ' has-notes' unless notes.blank? |
| 110 |
s << ' has-details' unless details.blank? |
| 111 |
s << ' private-notes' if private_notes? |
| 112 |
s |
| 113 |
end
|
| 114 |
|
| 115 |
def notify? |
| 116 |
@notify != false |
| 117 |
end
|
| 118 |
|
| 119 |
def notify=(arg) |
| 120 |
@notify = arg
|
| 121 |
end
|
| 122 |
|
| 123 |
def notified_users |
| 124 |
notified = journalized.notified_users |
| 125 |
if private_notes?
|
| 126 |
notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)}
|
| 127 |
end
|
| 128 |
notified |
| 129 |
end
|
| 130 |
|
| 131 |
def recipients |
| 132 |
notified_users.map(&:mail)
|
| 133 |
end
|
| 134 |
|
| 135 |
def notified_watchers |
| 136 |
notified = journalized.notified_watchers |
| 137 |
if private_notes?
|
| 138 |
notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)}
|
| 139 |
end
|
| 140 |
notified |
| 141 |
end
|
| 142 |
|
| 143 |
def watcher_recipients |
| 144 |
notified_watchers.map(&:mail)
|
| 145 |
end
|
| 146 |
|
| 147 |
# Sets @custom_field instance variable on journals details using a single query
|
| 148 |
def self.preload_journals_details_custom_fields(journals) |
| 149 |
field_ids = journals.map(&:details).flatten.select {|d| d.property == 'cf'}.map(&:prop_key).uniq |
| 150 |
if field_ids.any?
|
| 151 |
fields_by_id = CustomField.where(:id => field_ids).inject({}) {|h, f| h[f.id] = f; h} |
| 152 |
journals.each do |journal|
|
| 153 |
journal.details.each do |detail|
|
| 154 |
if detail.property == 'cf' |
| 155 |
detail.instance_variable_set "@custom_field", fields_by_id[detail.prop_key.to_i]
|
| 156 |
end
|
| 157 |
end
|
| 158 |
end
|
| 159 |
end
|
| 160 |
journals |
| 161 |
end
|
| 162 |
|
| 163 |
private |
| 164 |
|
| 165 |
def split_private_notes |
| 166 |
if private_notes?
|
| 167 |
if notes.present?
|
| 168 |
if details.any?
|
| 169 |
# Split the journal (notes/changes) so we don't have half-private journals
|
| 170 |
journal = Journal.new(:journalized => journalized, :user => user, :notes => nil, :private_notes => false) |
| 171 |
journal.details = details |
| 172 |
journal.save |
| 173 |
self.details = []
|
| 174 |
self.created_on = journal.created_on
|
| 175 |
end
|
| 176 |
else
|
| 177 |
# Blank notes should not be private
|
| 178 |
self.private_notes = false |
| 179 |
end
|
| 180 |
end
|
| 181 |
true
|
| 182 |
end
|
| 183 |
|
| 184 |
def send_notification |
| 185 |
if notify? && (Setting.notified_events.include?('issue_updated') || |
| 186 |
(Setting.notified_events.include?('issue_note_added') && notes.present?) || |
| 187 |
(Setting.notified_events.include?('issue_status_updated') && new_status.present?) || |
| 188 |
(Setting.notified_events.include?('issue_priority_updated') && new_value_for('priority_id').present?) |
| 189 |
) |
| 190 |
Mailer.deliver_issue_edit(self) |
| 191 |
end
|
| 192 |
end
|
| 193 |
end
|