Project

General

Profile

Feature #13919 » draft-feature-13919-v3.patch

Patch developed based on feature-13919-v2.pacth - Mizuki ISHIKAWA, 2020-02-13 02:23

View differences:

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; }
(4-4/12)