##// END OF EJS Templates
Do not use @:skip_relative_url_root@ to generate urls in Mailer (#2122)....
Jean-Philippe Lang -
r1990:077723c90a97
parent child
Show More
@@ -1,243 +1,247
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Mailer < ActionMailer::Base
19 19 helper :application
20 20 helper :issues
21 21 helper :custom_fields
22 22
23 23 include ActionController::UrlWriter
24 24
25 25 def issue_add(issue)
26 26 redmine_headers 'Project' => issue.project.identifier,
27 27 'Issue-Id' => issue.id,
28 28 'Issue-Author' => issue.author.login
29 29 redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
30 30 recipients issue.recipients
31 31 subject "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] (#{issue.status.name}) #{issue.subject}"
32 32 body :issue => issue,
33 33 :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
34 34 end
35 35
36 36 def issue_edit(journal)
37 37 issue = journal.journalized
38 38 redmine_headers 'Project' => issue.project.identifier,
39 39 'Issue-Id' => issue.id,
40 40 'Issue-Author' => issue.author.login
41 41 redmine_headers 'Issue-Assignee' => issue.assigned_to.login if issue.assigned_to
42 42 recipients issue.recipients
43 43 # Watchers in cc
44 44 cc(issue.watcher_recipients - @recipients)
45 45 s = "[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}] "
46 46 s << "(#{issue.status.name}) " if journal.new_value_for('status_id')
47 47 s << issue.subject
48 48 subject s
49 49 body :issue => issue,
50 50 :journal => journal,
51 51 :issue_url => url_for(:controller => 'issues', :action => 'show', :id => issue)
52 52 end
53 53
54 54 def reminder(user, issues, days)
55 55 set_language_if_valid user.language
56 56 recipients user.mail
57 57 subject l(:mail_subject_reminder, issues.size)
58 58 body :issues => issues,
59 59 :days => days,
60 60 :issues_url => url_for(:controller => 'issues', :action => 'index', :set_filter => 1, :assigned_to_id => user.id, :sort_key => 'issues.due_date', :sort_order => 'asc')
61 61 end
62 62
63 63 def document_added(document)
64 64 redmine_headers 'Project' => document.project.identifier
65 65 recipients document.project.recipients
66 66 subject "[#{document.project.name}] #{l(:label_document_new)}: #{document.title}"
67 67 body :document => document,
68 68 :document_url => url_for(:controller => 'documents', :action => 'show', :id => document)
69 69 end
70 70
71 71 def attachments_added(attachments)
72 72 container = attachments.first.container
73 73 added_to = ''
74 74 added_to_url = ''
75 75 case container.class.name
76 76 when 'Version'
77 77 added_to_url = url_for(:controller => 'projects', :action => 'list_files', :id => container.project_id)
78 78 added_to = "#{l(:label_version)}: #{container.name}"
79 79 when 'Document'
80 80 added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id)
81 81 added_to = "#{l(:label_document)}: #{container.title}"
82 82 end
83 83 redmine_headers 'Project' => container.project.identifier
84 84 recipients container.project.recipients
85 85 subject "[#{container.project.name}] #{l(:label_attachment_new)}"
86 86 body :attachments => attachments,
87 87 :added_to => added_to,
88 88 :added_to_url => added_to_url
89 89 end
90 90
91 91 def news_added(news)
92 92 redmine_headers 'Project' => news.project.identifier
93 93 recipients news.project.recipients
94 94 subject "[#{news.project.name}] #{l(:label_news)}: #{news.title}"
95 95 body :news => news,
96 96 :news_url => url_for(:controller => 'news', :action => 'show', :id => news)
97 97 end
98 98
99 99 def message_posted(message, recipients)
100 100 redmine_headers 'Project' => message.project.identifier,
101 101 'Topic-Id' => (message.parent_id || message.id)
102 102 recipients(recipients)
103 103 subject "[#{message.board.project.name} - #{message.board.name}] #{message.subject}"
104 104 body :message => message,
105 105 :message_url => url_for(:controller => 'messages', :action => 'show', :board_id => message.board_id, :id => message.root)
106 106 end
107 107
108 108 def account_information(user, password)
109 109 set_language_if_valid user.language
110 110 recipients user.mail
111 111 subject l(:mail_subject_register, Setting.app_title)
112 112 body :user => user,
113 113 :password => password,
114 114 :login_url => url_for(:controller => 'account', :action => 'login')
115 115 end
116 116
117 117 def account_activation_request(user)
118 118 # Send the email to all active administrators
119 119 recipients User.find_active(:all, :conditions => {:admin => true}).collect { |u| u.mail }.compact
120 120 subject l(:mail_subject_account_activation_request, Setting.app_title)
121 121 body :user => user,
122 122 :url => url_for(:controller => 'users', :action => 'index', :status => User::STATUS_REGISTERED, :sort_key => 'created_on', :sort_order => 'desc')
123 123 end
124 124
125 125 def lost_password(token)
126 126 set_language_if_valid(token.user.language)
127 127 recipients token.user.mail
128 128 subject l(:mail_subject_lost_password, Setting.app_title)
129 129 body :token => token,
130 130 :url => url_for(:controller => 'account', :action => 'lost_password', :token => token.value)
131 131 end
132 132
133 133 def register(token)
134 134 set_language_if_valid(token.user.language)
135 135 recipients token.user.mail
136 136 subject l(:mail_subject_register, Setting.app_title)
137 137 body :token => token,
138 138 :url => url_for(:controller => 'account', :action => 'activate', :token => token.value)
139 139 end
140 140
141 141 def test(user)
142 142 set_language_if_valid(user.language)
143 143 recipients user.mail
144 144 subject 'Redmine test'
145 145 body :url => url_for(:controller => 'welcome')
146 146 end
147 147
148 148 # Overrides default deliver! method to prevent from sending an email
149 149 # with no recipient, cc or bcc
150 150 def deliver!(mail = @mail)
151 151 return false if (recipients.nil? || recipients.empty?) &&
152 152 (cc.nil? || cc.empty?) &&
153 153 (bcc.nil? || bcc.empty?)
154 154 super
155 155 end
156 156
157 157 # Sends reminders to issue assignees
158 158 # Available options:
159 159 # * :days => how many days in the future to remind about (defaults to 7)
160 160 # * :tracker => id of tracker for filtering issues (defaults to all trackers)
161 161 # * :project => id or identifier of project to process (defaults to all projects)
162 162 def self.reminders(options={})
163 163 days = options[:days] || 7
164 164 project = options[:project] ? Project.find(options[:project]) : nil
165 165 tracker = options[:tracker] ? Tracker.find(options[:tracker]) : nil
166 166
167 167 s = ARCondition.new ["#{IssueStatus.table_name}.is_closed = ? AND #{Issue.table_name}.due_date <= ?", false, days.day.from_now.to_date]
168 168 s << "#{Issue.table_name}.assigned_to_id IS NOT NULL"
169 169 s << "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}"
170 170 s << "#{Issue.table_name}.project_id = #{project.id}" if project
171 171 s << "#{Issue.table_name}.tracker_id = #{tracker.id}" if tracker
172 172
173 173 issues_by_assignee = Issue.find(:all, :include => [:status, :assigned_to, :project, :tracker],
174 174 :conditions => s.conditions
175 175 ).group_by(&:assigned_to)
176 176 issues_by_assignee.each do |assignee, issues|
177 177 deliver_reminder(assignee, issues, days) unless assignee.nil?
178 178 end
179 179 end
180 180
181 181 private
182 182 def initialize_defaults(method_name)
183 183 super
184 184 set_language_if_valid Setting.default_language
185 185 from Setting.mail_from
186 default_url_options[:host] = Setting.host_name
186
187 # URL options
188 h = Setting.host_name
189 h = h.to_s.gsub(%r{\/.*$}, '') unless ActionController::AbstractRequest.relative_url_root.blank?
190 default_url_options[:host] = h
187 191 default_url_options[:protocol] = Setting.protocol
188 default_url_options[:skip_relative_url_root] = true
192
189 193 # Common headers
190 194 headers 'X-Mailer' => 'Redmine',
191 195 'X-Redmine-Host' => Setting.host_name,
192 196 'X-Redmine-Site' => Setting.app_title
193 197 end
194 198
195 199 # Appends a Redmine header field (name is prepended with 'X-Redmine-')
196 200 def redmine_headers(h)
197 201 h.each { |k,v| headers["X-Redmine-#{k}"] = v }
198 202 end
199 203
200 204 # Overrides the create_mail method
201 205 def create_mail
202 206 # Removes the current user from the recipients and cc
203 207 # if he doesn't want to receive notifications about what he does
204 208 if User.current.pref[:no_self_notified]
205 209 recipients.delete(User.current.mail) if recipients
206 210 cc.delete(User.current.mail) if cc
207 211 end
208 212 # Blind carbon copy recipients
209 213 if Setting.bcc_recipients?
210 214 bcc([recipients, cc].flatten.compact.uniq)
211 215 recipients []
212 216 cc []
213 217 end
214 218 super
215 219 end
216 220
217 221 # Renders a message with the corresponding layout
218 222 def render_message(method_name, body)
219 223 layout = method_name.match(%r{text\.html\.(rhtml|rxml)}) ? 'layout.text.html.rhtml' : 'layout.text.plain.rhtml'
220 224 body[:content_for_layout] = render(:file => method_name, :body => body)
221 225 ActionView::Base.new(template_root, body, self).render(:file => "mailer/#{layout}", :use_full_path => true)
222 226 end
223 227
224 228 # for the case of plain text only
225 229 def body(*params)
226 230 value = super(*params)
227 231 if Setting.plain_text_mail?
228 232 templates = Dir.glob("#{template_path}/#{@template}.text.plain.{rhtml,erb}")
229 233 unless String === @body or templates.empty?
230 234 template = File.basename(templates.first)
231 235 @body[:content_for_layout] = render(:file => template, :body => @body)
232 236 @body = ActionView::Base.new(template_root, @body, self).render(:file => "mailer/layout.text.plain.rhtml", :use_full_path => true)
233 237 return @body
234 238 end
235 239 end
236 240 return value
237 241 end
238 242
239 243 # Makes partial rendering work with Rails 1.2 (retro-compatibility)
240 244 def self.controller_path
241 245 ''
242 246 end unless respond_to?('controller_path')
243 247 end
@@ -1,138 +1,186
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class MailerTest < Test::Unit::TestCase
21 21 fixtures :projects, :issues, :users, :members, :documents, :attachments, :news, :tokens, :journals, :journal_details, :changesets, :trackers, :issue_statuses, :enumerations, :messages, :boards, :repositories
22 22
23 23 def test_generated_links_in_emails
24 24 ActionMailer::Base.deliveries.clear
25 25 Setting.host_name = 'mydomain.foo'
26 26 Setting.protocol = 'https'
27 27
28 28 journal = Journal.find(2)
29 29 assert Mailer.deliver_issue_edit(journal)
30 30
31 31 mail = ActionMailer::Base.deliveries.last
32 32 assert_kind_of TMail::Mail, mail
33 33 # link to the main ticket
34 34 assert mail.body.include?('<a href="https://mydomain.foo/issues/show/1">Bug #1: Can\'t print recipes</a>')
35 35
36 36 # link to a referenced ticket
37 37 assert mail.body.include?('<a href="https://mydomain.foo/issues/show/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
38 38 # link to a changeset
39 39 assert mail.body.include?('<a href="https://mydomain.foo/repositories/revision/ecookbook/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
40 40 end
41
42 def test_generated_links_with_prefix
43 relative_url_root = ActionController::AbstractRequest.relative_url_root
44 ActionMailer::Base.deliveries.clear
45 Setting.host_name = 'mydomain.foo/rdm'
46 Setting.protocol = 'http'
47 ActionController::AbstractRequest.relative_url_root = '/rdm'
48
49 journal = Journal.find(2)
50 assert Mailer.deliver_issue_edit(journal)
51
52 mail = ActionMailer::Base.deliveries.last
53 assert_kind_of TMail::Mail, mail
54 # link to the main ticket
55 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/1">Bug #1: Can\'t print recipes</a>')
56
57 # link to a referenced ticket
58 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
59 # link to a changeset
60 assert mail.body.include?('<a href="http://mydomain.foo/rdm/repositories/revision/ecookbook/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
61 ensure
62 # restore it
63 ActionController::AbstractRequest.relative_url_root = relative_url_root
64 end
65
66 def test_generated_links_with_prefix_and_no_relative_url_root
67 relative_url_root = ActionController::AbstractRequest.relative_url_root
68 ActionMailer::Base.deliveries.clear
69 Setting.host_name = 'mydomain.foo/rdm'
70 Setting.protocol = 'http'
71 ActionController::AbstractRequest.relative_url_root = nil
72
73 journal = Journal.find(2)
74 assert Mailer.deliver_issue_edit(journal)
75
76 mail = ActionMailer::Base.deliveries.last
77 assert_kind_of TMail::Mail, mail
78 # link to the main ticket
79 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/1">Bug #1: Can\'t print recipes</a>')
80
81 # link to a referenced ticket
82 assert mail.body.include?('<a href="http://mydomain.foo/rdm/issues/show/2" class="issue" title="Add ingredients categories (Assigned)">#2</a>')
83 # link to a changeset
84 assert mail.body.include?('<a href="http://mydomain.foo/rdm/repositories/revision/ecookbook/2" class="changeset" title="This commit fixes #1, #2 and references #1 &amp; #3">r2</a>')
85 ensure
86 # restore it
87 ActionController::AbstractRequest.relative_url_root = relative_url_root
88 end
41 89
42 90 def test_plain_text_mail
43 91 Setting.plain_text_mail = 1
44 92 journal = Journal.find(2)
45 93 Mailer.deliver_issue_edit(journal)
46 94 mail = ActionMailer::Base.deliveries.last
47 95 assert !mail.body.include?('<a href="https://mydomain.foo/issues/show/1">Bug #1: Can\'t print recipes</a>')
48 96 end
49 97
50 98
51 99
52 100 # test mailer methods for each language
53 101 def test_issue_add
54 102 issue = Issue.find(1)
55 103 GLoc.valid_languages.each do |lang|
56 104 Setting.default_language = lang.to_s
57 105 assert Mailer.deliver_issue_add(issue)
58 106 end
59 107 end
60 108
61 109 def test_issue_edit
62 110 journal = Journal.find(1)
63 111 GLoc.valid_languages.each do |lang|
64 112 Setting.default_language = lang.to_s
65 113 assert Mailer.deliver_issue_edit(journal)
66 114 end
67 115 end
68 116
69 117 def test_document_added
70 118 document = Document.find(1)
71 119 GLoc.valid_languages.each do |lang|
72 120 Setting.default_language = lang.to_s
73 121 assert Mailer.deliver_document_added(document)
74 122 end
75 123 end
76 124
77 125 def test_attachments_added
78 126 attachements = [ Attachment.find_by_container_type('Document') ]
79 127 GLoc.valid_languages.each do |lang|
80 128 Setting.default_language = lang.to_s
81 129 assert Mailer.deliver_attachments_added(attachements)
82 130 end
83 131 end
84 132
85 133 def test_news_added
86 134 news = News.find(:first)
87 135 GLoc.valid_languages.each do |lang|
88 136 Setting.default_language = lang.to_s
89 137 assert Mailer.deliver_news_added(news)
90 138 end
91 139 end
92 140
93 141 def test_message_posted
94 142 message = Message.find(:first)
95 143 recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author}
96 144 recipients = recipients.compact.uniq
97 145 GLoc.valid_languages.each do |lang|
98 146 Setting.default_language = lang.to_s
99 147 assert Mailer.deliver_message_posted(message, recipients)
100 148 end
101 149 end
102 150
103 151 def test_account_information
104 152 user = User.find(:first)
105 153 GLoc.valid_languages.each do |lang|
106 154 user.update_attribute :language, lang.to_s
107 155 user.reload
108 156 assert Mailer.deliver_account_information(user, 'pAsswORd')
109 157 end
110 158 end
111 159
112 160 def test_lost_password
113 161 token = Token.find(2)
114 162 GLoc.valid_languages.each do |lang|
115 163 token.user.update_attribute :language, lang.to_s
116 164 token.reload
117 165 assert Mailer.deliver_lost_password(token)
118 166 end
119 167 end
120 168
121 169 def test_register
122 170 token = Token.find(1)
123 171 GLoc.valid_languages.each do |lang|
124 172 token.user.update_attribute :language, lang.to_s
125 173 token.reload
126 174 assert Mailer.deliver_register(token)
127 175 end
128 176 end
129 177
130 178 def test_reminders
131 179 ActionMailer::Base.deliveries.clear
132 180 Mailer.reminders(:days => 42)
133 181 assert_equal 1, ActionMailer::Base.deliveries.size
134 182 mail = ActionMailer::Base.deliveries.last
135 183 assert mail.bcc.include?('dlopper@somenet.foo')
136 184 assert mail.body.include?('Bug #3: Error 281 when updating a recipe')
137 185 end
138 186 end
General Comments 0
You need to be logged in to leave comments. Login now