Feature #13919 » draft-feature-13919-v3.patch
app/helpers/application_helper.rb | ||
---|---|---|
53 | 53 |
name = h(user.name(options[:format])) |
54 | 54 |
if user.active? || (User.current.admin? && user.logged?) |
55 | 55 |
only_path = options[:only_path].nil? ? true : options[:only_path] |
56 |
link_to name, user_url(user, :only_path => only_path), :class => user.css_classes |
|
56 |
css_classes = options[:class] ? "#{user.css_classes} #{options[:class]}" : user.css_classes |
|
57 |
link_to name, user_url(user, :only_path => only_path), :class => css_classes |
|
57 | 58 |
else |
58 | 59 |
name |
59 | 60 |
end |
... | ... | |
1093 | 1094 |
if p = Project.visible.find_by_id(oid) |
1094 | 1095 |
link = link_to_project(p, {:only_path => only_path}, :class => 'project') |
1095 | 1096 |
end |
1096 |
when 'user' |
|
1097 |
u = User.visible.find_by(:id => oid, :type => 'User') |
|
1098 |
link = link_to_user(u, :only_path => only_path) if u |
|
1099 | 1097 |
end |
1100 | 1098 |
elsif sep == ':' |
1101 | 1099 |
name = remove_double_quotes(identifier) |
... | ... | |
1170 | 1168 |
if p = Project.visible.where("identifier = :s OR LOWER(name) = :s", :s => name.downcase).first |
1171 | 1169 |
link = link_to_project(p, {:only_path => only_path}, :class => 'project') |
1172 | 1170 |
end |
1173 |
when 'user' |
|
1174 |
u = User.visible.find_by("LOWER(login) = :s AND type = 'User'", :s => name.downcase) |
|
1175 |
link = link_to_user(u, :only_path => only_path) if u |
|
1176 | 1171 |
end |
1177 |
elsif sep == "@" |
|
1178 |
name = remove_double_quotes(identifier) |
|
1179 |
u = User.visible.find_by("LOWER(login) = :s AND type = 'User'", :s => name.downcase) |
|
1180 |
link = link_to_user(u, :only_path => only_path) if u |
|
1172 |
end |
|
1173 | ||
1174 |
if link.nil? && $~ |
|
1175 |
user = User.mentioned_user($~.named_captures.symbolize_keys) |
|
1176 |
if user |
|
1177 |
css_classes = (user.notify_mentioned_user?(obj) ? 'notified' : nil) if obj |
|
1178 |
link = link_to_user(user, :only_path => only_path, :class => css_classes) |
|
1179 |
end |
|
1181 | 1180 |
end |
1182 | 1181 |
end |
1183 | 1182 |
(leading + (link || "#{project_prefix}#{prefix}#{repo_prefix}#{sep}#{identifier}#{comment_suffix}")) |
app/models/document.rb | ||
---|---|---|
63 | 63 |
end |
64 | 64 | |
65 | 65 |
def notified_users |
66 |
project.notified_users.reject {|user| !visible?(user)}
|
|
66 |
project.notified_users.select {|user| user.allowed_to_view_notify_target?(self) }
|
|
67 | 67 |
end |
68 | 68 | |
69 | 69 |
private |
app/models/issue.rb | ||
---|---|---|
1045 | 1045 |
notified += project.users.preload(:preference).select(&:notify_about_high_priority_issues?) if priority.high? |
1046 | 1046 |
notified.uniq! |
1047 | 1047 |
# Remove users that can not view the issue |
1048 |
notified.reject! {|user| !visible?(user)} |
|
1049 |
notified |
|
1048 |
notified.select {|user| user.allowed_to_view_notify_target?(self)} |
|
1050 | 1049 |
end |
1051 | 1050 | |
1052 | 1051 |
# Returns the email addresses that should be notified |
app/models/journal.rb | ||
---|---|---|
145 | 145 | |
146 | 146 |
def notified_users |
147 | 147 |
notified = journalized.notified_users |
148 |
if private_notes? |
|
149 |
notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)} |
|
150 |
end |
|
151 |
notified |
|
148 |
notified.select{ |u| u.allowed_to_view_notify_target?(self) } |
|
152 | 149 |
end |
153 | 150 | |
154 | 151 |
def recipients |
app/models/mailer.rb | ||
---|---|---|
68 | 68 |
end |
69 | 69 | |
70 | 70 |
# Builds a mail for notifying user about a new issue |
71 |
def issue_add(user, issue) |
|
71 |
def issue_add(user, issue, mentioned)
|
|
72 | 72 |
redmine_headers 'Project' => issue.project.identifier, |
73 | 73 |
'Issue-Tracker' => issue.tracker.name, |
74 | 74 |
'Issue-Id' => issue.id, |
... | ... | |
82 | 82 |
@issue_url = url_for(:controller => 'issues', :action => 'show', :id => issue) |
83 | 83 |
subject = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]" |
84 | 84 |
subject += " (#{issue.status.name})" if Setting.show_status_changes_in_mail_subject? |
85 |
subject += " #{issue.subject}" |
|
85 |
if mentioned |
|
86 |
subject = l(:text_mentioned, :id => subject, :author => @author) |
|
87 |
else |
|
88 |
subject += " #{issue.subject}" |
|
89 |
end |
|
86 | 90 |
mail :to => user, |
87 | 91 |
:subject => subject |
88 | 92 |
end |
... | ... | |
93 | 97 |
# Mailer.deliver_issue_add(issue) |
94 | 98 |
def self.deliver_issue_add(issue) |
95 | 99 |
users = issue.notified_users | issue.notified_watchers |
100 |
mentioned_users = mentioned_users(issue.description, issue) |
|
101 |
users |= mentioned_users |
|
96 | 102 |
users.each do |user| |
97 |
issue_add(user, issue).deliver_later |
|
103 |
issue_add(user, issue, mentioned_users.include?(user)).deliver_later
|
|
98 | 104 |
end |
99 | 105 |
end |
100 | 106 | |
101 | 107 |
# Builds a mail for notifying user about an issue update |
102 |
def issue_edit(user, journal) |
|
108 |
def issue_edit(user, journal, mentioned)
|
|
103 | 109 |
issue = journal.journalized |
104 | 110 |
redmine_headers 'Project' => issue.project.identifier, |
105 | 111 |
'Issue-Tracker' => issue.tracker.name, |
... | ... | |
111 | 117 |
@author = journal.user |
112 | 118 |
s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] " |
113 | 119 |
s += "(#{issue.status.name}) " if journal.new_value_for('status_id') && Setting.show_status_changes_in_mail_subject? |
114 |
s += issue.subject |
|
120 |
if mentioned |
|
121 |
s = l(:text_mentioned, :id => s, :author => @author) |
|
122 |
else |
|
123 |
s += issue.subject |
|
124 |
end |
|
115 | 125 |
@issue = issue |
116 | 126 |
@user = user |
117 | 127 |
@journal = journal |
... | ... | |
128 | 138 |
# Mailer.deliver_issue_edit(journal) |
129 | 139 |
def self.deliver_issue_edit(journal) |
130 | 140 |
users = journal.notified_users | journal.notified_watchers |
141 |
mentioned_users = mentioned_users(journal.notes, journal) |
|
142 |
users |= mentioned_users |
|
131 | 143 |
users.select! do |user| |
132 | 144 |
journal.notes? || journal.visible_details(user).any? |
133 | 145 |
end |
134 | 146 |
users.each do |user| |
135 |
issue_edit(user, journal).deliver_later |
|
147 |
issue_edit(user, journal, mentioned_users.include?(user)).deliver_later
|
|
136 | 148 |
end |
137 | 149 |
end |
138 | 150 | |
... | ... | |
203 | 215 |
end |
204 | 216 | |
205 | 217 |
# Builds a mail to user about a new news. |
206 |
def news_added(user, news) |
|
218 |
def news_added(user, news, mentioned)
|
|
207 | 219 |
redmine_headers 'Project' => news.project.identifier |
208 | 220 |
@author = news.author |
209 | 221 |
message_id news |
... | ... | |
211 | 223 |
@news = news |
212 | 224 |
@user = user |
213 | 225 |
@news_url = url_for(:controller => 'news', :action => 'show', :id => news) |
226 |
subject = "[#{news.project.name}] #{l(:label_news)}" |
|
227 |
if mentioned |
|
228 |
subject = l(:text_mentioned, :id => subject, :author => @author) |
|
229 |
else |
|
230 |
subject += " #{news.title}" |
|
231 |
end |
|
214 | 232 |
mail :to => user, |
215 |
:subject => "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
|
|
233 |
:subject => subject
|
|
216 | 234 |
end |
217 | 235 | |
218 | 236 |
# Notifies users about new news |
... | ... | |
221 | 239 |
# Mailer.deliver_news_added(news) |
222 | 240 |
def self.deliver_news_added(news) |
223 | 241 |
users = news.notified_users | news.notified_watchers_for_added_news |
242 |
mentioned_users = mentioned_users(news.description, news) |
|
243 |
users |= mentioned_users |
|
244 | ||
224 | 245 |
users.each do |user| |
225 |
news_added(user, news).deliver_later |
|
246 |
news_added(user, news, mentioned_users.include?(user)).deliver_later
|
|
226 | 247 |
end |
227 | 248 |
end |
228 | 249 | |
229 | 250 |
# Builds a mail to user about a new news comment. |
230 |
def news_comment_added(user, comment) |
|
251 |
def news_comment_added(user, comment, mentioned)
|
|
231 | 252 |
news = comment.commented |
232 | 253 |
redmine_headers 'Project' => news.project.identifier |
233 | 254 |
@author = comment.author |
... | ... | |
237 | 258 |
@comment = comment |
238 | 259 |
@user = user |
239 | 260 |
@news_url = url_for(:controller => 'news', :action => 'show', :id => news) |
261 |
subject = |
|
262 |
if mentioned |
|
263 |
l(:text_mentioned, :id => "Re: [#{news.project.name}] #{l(:label_news)}", :author => @user) |
|
264 |
else |
|
265 |
"Re: [#{news.project.name}] #{l(:label_news)}: #{news.title}" |
|
266 |
end |
|
240 | 267 |
mail :to => user, |
241 |
:subject => "Re: [#{news.project.name}] #{l(:label_news)}: #{news.title}"
|
|
268 |
:subject => subject
|
|
242 | 269 |
end |
243 | 270 | |
244 | 271 |
# Notifies users about a new comment on a news |
... | ... | |
248 | 275 |
def self.deliver_news_comment_added(comment) |
249 | 276 |
news = comment.commented |
250 | 277 |
users = news.notified_users | news.notified_watchers |
278 |
mentioned_users = mentioned_users(comment.content, news) |
|
279 |
users |= mentioned_users |
|
251 | 280 |
users.each do |user| |
252 |
news_comment_added(user, comment).deliver_later |
|
281 |
news_comment_added(user, comment, mentioned_users.include?(user)).deliver_later
|
|
253 | 282 |
end |
254 | 283 |
end |
255 | 284 | |
256 | 285 |
# Builds a mail to user about a new message. |
257 |
def message_posted(user, message) |
|
286 |
def message_posted(user, message, mentioned)
|
|
258 | 287 |
redmine_headers 'Project' => message.project.identifier, |
259 | 288 |
'Topic-Id' => (message.parent_id || message.id) |
260 | 289 |
@author = message.author |
... | ... | |
263 | 292 |
@message = message |
264 | 293 |
@user = user |
265 | 294 |
@message_url = url_for(message.event_url) |
295 |
subject = |
|
296 |
if mentioned |
|
297 |
l(:text_mentioned, :id => "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}]", :author => @user) |
|
298 |
else |
|
299 |
"[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}" |
|
300 |
end |
|
266 | 301 |
mail :to => user, |
267 |
:subject => "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] #{message.subject}"
|
|
302 |
:subject => subject
|
|
268 | 303 |
end |
269 | 304 | |
270 | 305 |
# Notifies users about a new forum message. |
... | ... | |
275 | 310 |
users = message.notified_users |
276 | 311 |
users |= message.root.notified_watchers |
277 | 312 |
users |= message.board.notified_watchers |
313 |
mentioned_users = mentioned_users(message.content, message) |
|
314 |
users |= mentioned_users |
|
278 | 315 | |
279 | 316 |
users.each do |user| |
280 |
message_posted(user, message).deliver_later |
|
317 |
message_posted(user, message, mentioned_users.include?(user)).deliver_later
|
|
281 | 318 |
end |
282 | 319 |
end |
283 | 320 | |
... | ... | |
780 | 817 |
@references_objects ||= [] |
781 | 818 |
@references_objects << object |
782 | 819 |
end |
820 | ||
821 |
def self.mentioned_users(text, obj) |
|
822 |
users = [] |
|
823 |
return users if text.blank? |
|
824 |
text.scan(ApplicationHelper::LINKS_RE) do |_| |
|
825 |
target = User.mentioned_user($~.named_captures.symbolize_keys) |
|
826 |
next if target.blank? || users.include?(target) |
|
827 |
users << target if target.notify_mentioned_user?(obj) |
|
828 |
end |
|
829 |
users |
|
830 |
end |
|
783 | 831 |
end |
app/models/message.rb | ||
---|---|---|
114 | 114 |
end |
115 | 115 | |
116 | 116 |
def notified_users |
117 |
project.notified_users.reject {|user| !visible?(user)}
|
|
117 |
project.notified_users.select {|user| user.allowed_to_view_notify_target?(self) }
|
|
118 | 118 |
end |
119 | 119 | |
120 | 120 |
private |
app/models/news.rb | ||
---|---|---|
56 | 56 |
end |
57 | 57 | |
58 | 58 |
def notified_users |
59 |
project.users.select {|user| user.notify_about?(self) && user.allowed_to?(:view_news, project)}
|
|
59 |
project.users.select {|user| user.notify_about?(self) && user.allowed_to_view_notify_target?(self)}
|
|
60 | 60 |
end |
61 | 61 | |
62 | 62 |
def recipients |
app/models/user.rb | ||
---|---|---|
823 | 823 |
RequestStore.store[:current_user] ||= User.anonymous |
824 | 824 |
end |
825 | 825 | |
826 |
# Return the mentioned user to based on the match data |
|
827 |
# of ApplicationHelper::LINKS_RE. |
|
828 |
# user:jsmith -> Link to user with login jsmith |
|
829 |
# @jsmith -> Link to user with login jsmith |
|
830 |
# user#2 -> Link to user with id 2 |
|
831 |
def self.mentioned_user(match_data) |
|
832 |
return nil if match_data[:esc] |
|
833 |
sep = match_data[:sep1] || match_data[:sep2] || match_data[:sep3] || match_data[:sep4] |
|
834 |
identifier = match_data[:identifier1] || match_data[:identifier2] || match_data[:identifier3] |
|
835 |
prefix = match_data[:prefix] |
|
836 |
if (sep == '#' || sep == '##') && prefix == 'user' |
|
837 |
User.visible.find_by(:id => identifier.to_i, :type => 'User') |
|
838 |
elsif sep == '@' || (sep == ':' && prefix == 'user') |
|
839 |
name = identifier.gsub(%r{^"(.*)"$}, "\\1") |
|
840 |
User.find_by_login(CGI.unescapeHTML(name).downcase) |
|
841 |
end |
|
842 |
end |
|
843 | ||
844 |
# Return true if notify the mentioned user. |
|
845 |
def notify_mentioned_user?(object) |
|
846 |
self.active? && |
|
847 |
self.mail.present? && |
|
848 |
self.mail_notification.present? && self.mail_notification != 'none' && |
|
849 |
self.allowed_to_view_notify_target?(object) |
|
850 |
end |
|
851 | ||
852 |
# Return true if the user is allowed to view the notify target. |
|
853 |
def allowed_to_view_notify_target?(object) |
|
854 |
case object |
|
855 |
when Journal |
|
856 |
self.allowed_to_view_notify_target?(object.journalized) && |
|
857 |
(!object.private_notes? || self.allowed_to?(:view_private_notes, object.journalized.project)) |
|
858 |
when Comment |
|
859 |
self.allowed_to_view_notify_target?(object.commented) |
|
860 |
when nil |
|
861 |
false |
|
862 |
else |
|
863 |
object.visible?(self) |
|
864 |
end |
|
865 |
end |
|
866 | ||
826 | 867 |
# Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only |
827 | 868 |
# one anonymous user per database. |
828 | 869 |
def self.anonymous |
app/models/wiki_content.rb | ||
---|---|---|
53 | 53 |
end |
54 | 54 | |
55 | 55 |
def notified_users |
56 |
project.notified_users.reject {|user| !visible?(user)}
|
|
56 |
project.notified_users.select {|user| user.allowed_to_view_notify_target?(self) }
|
|
57 | 57 |
end |
58 | 58 | |
59 | 59 |
# Returns the mail addresses of users that should be notified |
app/views/news/show.html.erb | ||
---|---|---|
44 | 44 |
</div> |
45 | 45 |
<h4><%= avatar(comment.author) %><%= authoring comment.created_on, comment.author %></h4> |
46 | 46 |
<div class="wiki"> |
47 |
<%= textilizable(comment.comments) %>
|
|
47 |
<%= textilizable(comment, :comments) %>
|
|
48 | 48 |
</div> |
49 | 49 |
<% end if @comments.any? %> |
50 | 50 |
</div> |
config/locales/en.yml | ||
---|---|---|
1180 | 1180 |
text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages |
1181 | 1181 |
text_issue_added: "Issue %{id} has been reported by %{author}." |
1182 | 1182 |
text_issue_updated: "Issue %{id} has been updated by %{author}." |
1183 |
text_mentioned: "You have been mentioned in %{id} by %{author}." |
|
1183 | 1184 |
text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content? |
1184 | 1185 |
text_issue_category_destroy_question: "Some issues (%{count}) are assigned to this category. What do you want to do?" |
1185 | 1186 |
text_issue_category_destroy_assignments: Remove category assignments |
public/stylesheets/application.css | ||
---|---|---|
137 | 137 |
a, a:link, a:visited{ color: #169; text-decoration: none; } |
138 | 138 |
a:hover, a:active{ color: #c61a1a; text-decoration: underline;} |
139 | 139 |
a img{ border: 0; } |
140 |
a.user.notified, a.user.notified:link, a.user.notified:visited {padding: 2px; border-radius: 3px; background-color: #bae9f5} |
|
140 | 141 | |
141 | 142 |
a.issue.closed, a.issue.closed:link, a.issue.closed:visited { color: #999; text-decoration: line-through; } |
142 | 143 |
a.project.closed, a.project.closed:link, a.project.closed:visited { color: #999; } |