@@ -1,279 +1,279 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class AccountController < ApplicationController |
|
18 | class AccountController < ApplicationController | |
19 | helper :custom_fields |
|
19 | helper :custom_fields | |
20 | include CustomFieldsHelper |
|
20 | include CustomFieldsHelper | |
21 |
|
21 | |||
22 | # prevents login action to be filtered by check_if_login_required application scope filter |
|
22 | # prevents login action to be filtered by check_if_login_required application scope filter | |
23 | skip_before_filter :check_if_login_required |
|
23 | skip_before_filter :check_if_login_required | |
24 |
|
24 | |||
25 | # Login request and validation |
|
25 | # Login request and validation | |
26 | def login |
|
26 | def login | |
27 | if request.get? |
|
27 | if request.get? | |
28 | logout_user |
|
28 | logout_user | |
29 | else |
|
29 | else | |
30 | authenticate_user |
|
30 | authenticate_user | |
31 | end |
|
31 | end | |
32 | rescue AuthSourceException => e |
|
32 | rescue AuthSourceException => e | |
33 | logger.error "An error occured when authenticating #{params[:username]}: #{e.message}" |
|
33 | logger.error "An error occured when authenticating #{params[:username]}: #{e.message}" | |
34 | render_error :message => e.message |
|
34 | render_error :message => e.message | |
35 | end |
|
35 | end | |
36 |
|
36 | |||
37 | # Log out current user and redirect to welcome page |
|
37 | # Log out current user and redirect to welcome page | |
38 | def logout |
|
38 | def logout | |
39 | logout_user |
|
39 | logout_user | |
40 | redirect_to home_url |
|
40 | redirect_to home_url | |
41 | end |
|
41 | end | |
42 |
|
42 | |||
43 | # Enable user to choose a new password |
|
43 | # Enable user to choose a new password | |
44 | def lost_password |
|
44 | def lost_password | |
45 | redirect_to(home_url) && return unless Setting.lost_password? |
|
45 | redirect_to(home_url) && return unless Setting.lost_password? | |
46 | if params[:token] |
|
46 | if params[:token] | |
47 | @token = Token.find_by_action_and_value("recovery", params[:token]) |
|
47 | @token = Token.find_by_action_and_value("recovery", params[:token]) | |
48 | redirect_to(home_url) && return unless @token and !@token.expired? |
|
48 | redirect_to(home_url) && return unless @token and !@token.expired? | |
49 | @user = @token.user |
|
49 | @user = @token.user | |
50 | if request.post? |
|
50 | if request.post? | |
51 | @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] |
|
51 | @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] | |
52 | if @user.save |
|
52 | if @user.save | |
53 | @token.destroy |
|
53 | @token.destroy | |
54 | flash[:notice] = l(:notice_account_password_updated) |
|
54 | flash[:notice] = l(:notice_account_password_updated) | |
55 | redirect_to :action => 'login' |
|
55 | redirect_to :action => 'login' | |
56 | return |
|
56 | return | |
57 | end |
|
57 | end | |
58 | end |
|
58 | end | |
59 | render :template => "account/password_recovery" |
|
59 | render :template => "account/password_recovery" | |
60 | return |
|
60 | return | |
61 | else |
|
61 | else | |
62 | if request.post? |
|
62 | if request.post? | |
63 | user = User.find_by_mail(params[:mail]) |
|
63 | user = User.find_by_mail(params[:mail]) | |
64 | # user not found in db |
|
64 | # user not found in db | |
65 | (flash.now[:error] = l(:notice_account_unknown_email); return) unless user |
|
65 | (flash.now[:error] = l(:notice_account_unknown_email); return) unless user | |
66 | # user uses an external authentification |
|
66 | # user uses an external authentification | |
67 | (flash.now[:error] = l(:notice_can_t_change_password); return) if user.auth_source_id |
|
67 | (flash.now[:error] = l(:notice_can_t_change_password); return) if user.auth_source_id | |
68 | # create a new token for password recovery |
|
68 | # create a new token for password recovery | |
69 | token = Token.new(:user => user, :action => "recovery") |
|
69 | token = Token.new(:user => user, :action => "recovery") | |
70 | if token.save |
|
70 | if token.save | |
71 |
Mailer. |
|
71 | Mailer.lost_password(token).deliver | |
72 | flash[:notice] = l(:notice_account_lost_email_sent) |
|
72 | flash[:notice] = l(:notice_account_lost_email_sent) | |
73 | redirect_to :action => 'login' |
|
73 | redirect_to :action => 'login' | |
74 | return |
|
74 | return | |
75 | end |
|
75 | end | |
76 | end |
|
76 | end | |
77 | end |
|
77 | end | |
78 | end |
|
78 | end | |
79 |
|
79 | |||
80 | # User self-registration |
|
80 | # User self-registration | |
81 | def register |
|
81 | def register | |
82 | redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration] |
|
82 | redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration] | |
83 | if request.get? |
|
83 | if request.get? | |
84 | session[:auth_source_registration] = nil |
|
84 | session[:auth_source_registration] = nil | |
85 | @user = User.new(:language => Setting.default_language) |
|
85 | @user = User.new(:language => Setting.default_language) | |
86 | else |
|
86 | else | |
87 | @user = User.new |
|
87 | @user = User.new | |
88 | @user.safe_attributes = params[:user] |
|
88 | @user.safe_attributes = params[:user] | |
89 | @user.admin = false |
|
89 | @user.admin = false | |
90 | @user.register |
|
90 | @user.register | |
91 | if session[:auth_source_registration] |
|
91 | if session[:auth_source_registration] | |
92 | @user.activate |
|
92 | @user.activate | |
93 | @user.login = session[:auth_source_registration][:login] |
|
93 | @user.login = session[:auth_source_registration][:login] | |
94 | @user.auth_source_id = session[:auth_source_registration][:auth_source_id] |
|
94 | @user.auth_source_id = session[:auth_source_registration][:auth_source_id] | |
95 | if @user.save |
|
95 | if @user.save | |
96 | session[:auth_source_registration] = nil |
|
96 | session[:auth_source_registration] = nil | |
97 | self.logged_user = @user |
|
97 | self.logged_user = @user | |
98 | flash[:notice] = l(:notice_account_activated) |
|
98 | flash[:notice] = l(:notice_account_activated) | |
99 | redirect_to :controller => 'my', :action => 'account' |
|
99 | redirect_to :controller => 'my', :action => 'account' | |
100 | end |
|
100 | end | |
101 | else |
|
101 | else | |
102 | @user.login = params[:user][:login] |
|
102 | @user.login = params[:user][:login] | |
103 | @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] |
|
103 | @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] | |
104 |
|
104 | |||
105 | case Setting.self_registration |
|
105 | case Setting.self_registration | |
106 | when '1' |
|
106 | when '1' | |
107 | register_by_email_activation(@user) |
|
107 | register_by_email_activation(@user) | |
108 | when '3' |
|
108 | when '3' | |
109 | register_automatically(@user) |
|
109 | register_automatically(@user) | |
110 | else |
|
110 | else | |
111 | register_manually_by_administrator(@user) |
|
111 | register_manually_by_administrator(@user) | |
112 | end |
|
112 | end | |
113 | end |
|
113 | end | |
114 | end |
|
114 | end | |
115 | end |
|
115 | end | |
116 |
|
116 | |||
117 | # Token based account activation |
|
117 | # Token based account activation | |
118 | def activate |
|
118 | def activate | |
119 | redirect_to(home_url) && return unless Setting.self_registration? && params[:token] |
|
119 | redirect_to(home_url) && return unless Setting.self_registration? && params[:token] | |
120 | token = Token.find_by_action_and_value('register', params[:token]) |
|
120 | token = Token.find_by_action_and_value('register', params[:token]) | |
121 | redirect_to(home_url) && return unless token and !token.expired? |
|
121 | redirect_to(home_url) && return unless token and !token.expired? | |
122 | user = token.user |
|
122 | user = token.user | |
123 | redirect_to(home_url) && return unless user.registered? |
|
123 | redirect_to(home_url) && return unless user.registered? | |
124 | user.activate |
|
124 | user.activate | |
125 | if user.save |
|
125 | if user.save | |
126 | token.destroy |
|
126 | token.destroy | |
127 | flash[:notice] = l(:notice_account_activated) |
|
127 | flash[:notice] = l(:notice_account_activated) | |
128 | end |
|
128 | end | |
129 | redirect_to :action => 'login' |
|
129 | redirect_to :action => 'login' | |
130 | end |
|
130 | end | |
131 |
|
131 | |||
132 | private |
|
132 | private | |
133 |
|
133 | |||
134 | def authenticate_user |
|
134 | def authenticate_user | |
135 | if Setting.openid? && using_open_id? |
|
135 | if Setting.openid? && using_open_id? | |
136 | open_id_authenticate(params[:openid_url]) |
|
136 | open_id_authenticate(params[:openid_url]) | |
137 | else |
|
137 | else | |
138 | password_authentication |
|
138 | password_authentication | |
139 | end |
|
139 | end | |
140 | end |
|
140 | end | |
141 |
|
141 | |||
142 | def password_authentication |
|
142 | def password_authentication | |
143 | user = User.try_to_login(params[:username], params[:password]) |
|
143 | user = User.try_to_login(params[:username], params[:password]) | |
144 |
|
144 | |||
145 | if user.nil? |
|
145 | if user.nil? | |
146 | invalid_credentials |
|
146 | invalid_credentials | |
147 | elsif user.new_record? |
|
147 | elsif user.new_record? | |
148 | onthefly_creation_failed(user, {:login => user.login, :auth_source_id => user.auth_source_id }) |
|
148 | onthefly_creation_failed(user, {:login => user.login, :auth_source_id => user.auth_source_id }) | |
149 | else |
|
149 | else | |
150 | # Valid user |
|
150 | # Valid user | |
151 | successful_authentication(user) |
|
151 | successful_authentication(user) | |
152 | end |
|
152 | end | |
153 | end |
|
153 | end | |
154 |
|
154 | |||
155 | def open_id_authenticate(openid_url) |
|
155 | def open_id_authenticate(openid_url) | |
156 | authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url, :method => :post) do |result, identity_url, registration| |
|
156 | authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url, :method => :post) do |result, identity_url, registration| | |
157 | if result.successful? |
|
157 | if result.successful? | |
158 | user = User.find_or_initialize_by_identity_url(identity_url) |
|
158 | user = User.find_or_initialize_by_identity_url(identity_url) | |
159 | if user.new_record? |
|
159 | if user.new_record? | |
160 | # Self-registration off |
|
160 | # Self-registration off | |
161 | redirect_to(home_url) && return unless Setting.self_registration? |
|
161 | redirect_to(home_url) && return unless Setting.self_registration? | |
162 |
|
162 | |||
163 | # Create on the fly |
|
163 | # Create on the fly | |
164 | user.login = registration['nickname'] unless registration['nickname'].nil? |
|
164 | user.login = registration['nickname'] unless registration['nickname'].nil? | |
165 | user.mail = registration['email'] unless registration['email'].nil? |
|
165 | user.mail = registration['email'] unless registration['email'].nil? | |
166 | user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil? |
|
166 | user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil? | |
167 | user.random_password |
|
167 | user.random_password | |
168 | user.register |
|
168 | user.register | |
169 |
|
169 | |||
170 | case Setting.self_registration |
|
170 | case Setting.self_registration | |
171 | when '1' |
|
171 | when '1' | |
172 | register_by_email_activation(user) do |
|
172 | register_by_email_activation(user) do | |
173 | onthefly_creation_failed(user) |
|
173 | onthefly_creation_failed(user) | |
174 | end |
|
174 | end | |
175 | when '3' |
|
175 | when '3' | |
176 | register_automatically(user) do |
|
176 | register_automatically(user) do | |
177 | onthefly_creation_failed(user) |
|
177 | onthefly_creation_failed(user) | |
178 | end |
|
178 | end | |
179 | else |
|
179 | else | |
180 | register_manually_by_administrator(user) do |
|
180 | register_manually_by_administrator(user) do | |
181 | onthefly_creation_failed(user) |
|
181 | onthefly_creation_failed(user) | |
182 | end |
|
182 | end | |
183 | end |
|
183 | end | |
184 | else |
|
184 | else | |
185 | # Existing record |
|
185 | # Existing record | |
186 | if user.active? |
|
186 | if user.active? | |
187 | successful_authentication(user) |
|
187 | successful_authentication(user) | |
188 | else |
|
188 | else | |
189 | account_pending |
|
189 | account_pending | |
190 | end |
|
190 | end | |
191 | end |
|
191 | end | |
192 | end |
|
192 | end | |
193 | end |
|
193 | end | |
194 | end |
|
194 | end | |
195 |
|
195 | |||
196 | def successful_authentication(user) |
|
196 | def successful_authentication(user) | |
197 | # Valid user |
|
197 | # Valid user | |
198 | self.logged_user = user |
|
198 | self.logged_user = user | |
199 | # generate a key and set cookie if autologin |
|
199 | # generate a key and set cookie if autologin | |
200 | if params[:autologin] && Setting.autologin? |
|
200 | if params[:autologin] && Setting.autologin? | |
201 | set_autologin_cookie(user) |
|
201 | set_autologin_cookie(user) | |
202 | end |
|
202 | end | |
203 | call_hook(:controller_account_success_authentication_after, {:user => user }) |
|
203 | call_hook(:controller_account_success_authentication_after, {:user => user }) | |
204 | redirect_back_or_default :controller => 'my', :action => 'page' |
|
204 | redirect_back_or_default :controller => 'my', :action => 'page' | |
205 | end |
|
205 | end | |
206 |
|
206 | |||
207 | def set_autologin_cookie(user) |
|
207 | def set_autologin_cookie(user) | |
208 | token = Token.create(:user => user, :action => 'autologin') |
|
208 | token = Token.create(:user => user, :action => 'autologin') | |
209 | cookie_name = Redmine::Configuration['autologin_cookie_name'] || 'autologin' |
|
209 | cookie_name = Redmine::Configuration['autologin_cookie_name'] || 'autologin' | |
210 | cookie_options = { |
|
210 | cookie_options = { | |
211 | :value => token.value, |
|
211 | :value => token.value, | |
212 | :expires => 1.year.from_now, |
|
212 | :expires => 1.year.from_now, | |
213 | :path => (Redmine::Configuration['autologin_cookie_path'] || '/'), |
|
213 | :path => (Redmine::Configuration['autologin_cookie_path'] || '/'), | |
214 | :secure => (Redmine::Configuration['autologin_cookie_secure'] ? true : false), |
|
214 | :secure => (Redmine::Configuration['autologin_cookie_secure'] ? true : false), | |
215 | :httponly => true |
|
215 | :httponly => true | |
216 | } |
|
216 | } | |
217 | cookies[cookie_name] = cookie_options |
|
217 | cookies[cookie_name] = cookie_options | |
218 | end |
|
218 | end | |
219 |
|
219 | |||
220 | # Onthefly creation failed, display the registration form to fill/fix attributes |
|
220 | # Onthefly creation failed, display the registration form to fill/fix attributes | |
221 | def onthefly_creation_failed(user, auth_source_options = { }) |
|
221 | def onthefly_creation_failed(user, auth_source_options = { }) | |
222 | @user = user |
|
222 | @user = user | |
223 | session[:auth_source_registration] = auth_source_options unless auth_source_options.empty? |
|
223 | session[:auth_source_registration] = auth_source_options unless auth_source_options.empty? | |
224 | render :action => 'register' |
|
224 | render :action => 'register' | |
225 | end |
|
225 | end | |
226 |
|
226 | |||
227 | def invalid_credentials |
|
227 | def invalid_credentials | |
228 | logger.warn "Failed login for '#{params[:username]}' from #{request.remote_ip} at #{Time.now.utc}" |
|
228 | logger.warn "Failed login for '#{params[:username]}' from #{request.remote_ip} at #{Time.now.utc}" | |
229 | flash.now[:error] = l(:notice_account_invalid_creditentials) |
|
229 | flash.now[:error] = l(:notice_account_invalid_creditentials) | |
230 | end |
|
230 | end | |
231 |
|
231 | |||
232 | # Register a user for email activation. |
|
232 | # Register a user for email activation. | |
233 | # |
|
233 | # | |
234 | # Pass a block for behavior when a user fails to save |
|
234 | # Pass a block for behavior when a user fails to save | |
235 | def register_by_email_activation(user, &block) |
|
235 | def register_by_email_activation(user, &block) | |
236 | token = Token.new(:user => user, :action => "register") |
|
236 | token = Token.new(:user => user, :action => "register") | |
237 | if user.save and token.save |
|
237 | if user.save and token.save | |
238 |
Mailer. |
|
238 | Mailer.register(token).deliver | |
239 | flash[:notice] = l(:notice_account_register_done) |
|
239 | flash[:notice] = l(:notice_account_register_done) | |
240 | redirect_to :action => 'login' |
|
240 | redirect_to :action => 'login' | |
241 | else |
|
241 | else | |
242 | yield if block_given? |
|
242 | yield if block_given? | |
243 | end |
|
243 | end | |
244 | end |
|
244 | end | |
245 |
|
245 | |||
246 | # Automatically register a user |
|
246 | # Automatically register a user | |
247 | # |
|
247 | # | |
248 | # Pass a block for behavior when a user fails to save |
|
248 | # Pass a block for behavior when a user fails to save | |
249 | def register_automatically(user, &block) |
|
249 | def register_automatically(user, &block) | |
250 | # Automatic activation |
|
250 | # Automatic activation | |
251 | user.activate |
|
251 | user.activate | |
252 | user.last_login_on = Time.now |
|
252 | user.last_login_on = Time.now | |
253 | if user.save |
|
253 | if user.save | |
254 | self.logged_user = user |
|
254 | self.logged_user = user | |
255 | flash[:notice] = l(:notice_account_activated) |
|
255 | flash[:notice] = l(:notice_account_activated) | |
256 | redirect_to :controller => 'my', :action => 'account' |
|
256 | redirect_to :controller => 'my', :action => 'account' | |
257 | else |
|
257 | else | |
258 | yield if block_given? |
|
258 | yield if block_given? | |
259 | end |
|
259 | end | |
260 | end |
|
260 | end | |
261 |
|
261 | |||
262 | # Manual activation by the administrator |
|
262 | # Manual activation by the administrator | |
263 | # |
|
263 | # | |
264 | # Pass a block for behavior when a user fails to save |
|
264 | # Pass a block for behavior when a user fails to save | |
265 | def register_manually_by_administrator(user, &block) |
|
265 | def register_manually_by_administrator(user, &block) | |
266 | if user.save |
|
266 | if user.save | |
267 | # Sends an email to the administrators |
|
267 | # Sends an email to the administrators | |
268 |
Mailer. |
|
268 | Mailer.account_activation_request(user).deliver | |
269 | account_pending |
|
269 | account_pending | |
270 | else |
|
270 | else | |
271 | yield if block_given? |
|
271 | yield if block_given? | |
272 | end |
|
272 | end | |
273 | end |
|
273 | end | |
274 |
|
274 | |||
275 | def account_pending |
|
275 | def account_pending | |
276 | flash[:notice] = l(:notice_account_pending) |
|
276 | flash[:notice] = l(:notice_account_pending) | |
277 | redirect_to :action => 'login' |
|
277 | redirect_to :action => 'login' | |
278 | end |
|
278 | end | |
279 | end |
|
279 | end |
@@ -1,84 +1,84 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class AdminController < ApplicationController |
|
18 | class AdminController < ApplicationController | |
19 | layout 'admin' |
|
19 | layout 'admin' | |
20 | menu_item :projects, :only => :projects |
|
20 | menu_item :projects, :only => :projects | |
21 | menu_item :plugins, :only => :plugins |
|
21 | menu_item :plugins, :only => :plugins | |
22 | menu_item :info, :only => :info |
|
22 | menu_item :info, :only => :info | |
23 |
|
23 | |||
24 | before_filter :require_admin |
|
24 | before_filter :require_admin | |
25 | helper :sort |
|
25 | helper :sort | |
26 | include SortHelper |
|
26 | include SortHelper | |
27 |
|
27 | |||
28 | def index |
|
28 | def index | |
29 | @no_configuration_data = Redmine::DefaultData::Loader::no_data? |
|
29 | @no_configuration_data = Redmine::DefaultData::Loader::no_data? | |
30 | end |
|
30 | end | |
31 |
|
31 | |||
32 | def projects |
|
32 | def projects | |
33 | @status = params[:status] || 1 |
|
33 | @status = params[:status] || 1 | |
34 |
|
34 | |||
35 | scope = Project.status(@status) |
|
35 | scope = Project.status(@status) | |
36 | scope = scope.like(params[:name]) if params[:name].present? |
|
36 | scope = scope.like(params[:name]) if params[:name].present? | |
37 |
|
37 | |||
38 | @projects = scope.all(:order => 'lft') |
|
38 | @projects = scope.all(:order => 'lft') | |
39 |
|
39 | |||
40 | render :action => "projects", :layout => false if request.xhr? |
|
40 | render :action => "projects", :layout => false if request.xhr? | |
41 | end |
|
41 | end | |
42 |
|
42 | |||
43 | def plugins |
|
43 | def plugins | |
44 | @plugins = Redmine::Plugin.all |
|
44 | @plugins = Redmine::Plugin.all | |
45 | end |
|
45 | end | |
46 |
|
46 | |||
47 | # Loads the default configuration |
|
47 | # Loads the default configuration | |
48 | # (roles, trackers, statuses, workflow, enumerations) |
|
48 | # (roles, trackers, statuses, workflow, enumerations) | |
49 | def default_configuration |
|
49 | def default_configuration | |
50 | if request.post? |
|
50 | if request.post? | |
51 | begin |
|
51 | begin | |
52 | Redmine::DefaultData::Loader::load(params[:lang]) |
|
52 | Redmine::DefaultData::Loader::load(params[:lang]) | |
53 | flash[:notice] = l(:notice_default_data_loaded) |
|
53 | flash[:notice] = l(:notice_default_data_loaded) | |
54 | rescue Exception => e |
|
54 | rescue Exception => e | |
55 | flash[:error] = l(:error_can_t_load_default_data, e.message) |
|
55 | flash[:error] = l(:error_can_t_load_default_data, e.message) | |
56 | end |
|
56 | end | |
57 | end |
|
57 | end | |
58 | redirect_to :action => 'index' |
|
58 | redirect_to :action => 'index' | |
59 | end |
|
59 | end | |
60 |
|
60 | |||
61 | def test_email |
|
61 | def test_email | |
62 | raise_delivery_errors = ActionMailer::Base.raise_delivery_errors |
|
62 | raise_delivery_errors = ActionMailer::Base.raise_delivery_errors | |
63 | # Force ActionMailer to raise delivery errors so we can catch it |
|
63 | # Force ActionMailer to raise delivery errors so we can catch it | |
64 | ActionMailer::Base.raise_delivery_errors = true |
|
64 | ActionMailer::Base.raise_delivery_errors = true | |
65 | begin |
|
65 | begin | |
66 |
@test = Mailer. |
|
66 | @test = Mailer.test_email(User.current).deliver | |
67 | flash[:notice] = l(:notice_email_sent, User.current.mail) |
|
67 | flash[:notice] = l(:notice_email_sent, User.current.mail) | |
68 | rescue Exception => e |
|
68 | rescue Exception => e | |
69 | flash[:error] = l(:notice_email_error, e.message) |
|
69 | flash[:error] = l(:notice_email_error, e.message) | |
70 | end |
|
70 | end | |
71 | ActionMailer::Base.raise_delivery_errors = raise_delivery_errors |
|
71 | ActionMailer::Base.raise_delivery_errors = raise_delivery_errors | |
72 | redirect_to :controller => 'settings', :action => 'edit', :tab => 'notifications' |
|
72 | redirect_to :controller => 'settings', :action => 'edit', :tab => 'notifications' | |
73 | end |
|
73 | end | |
74 |
|
74 | |||
75 | def info |
|
75 | def info | |
76 | @db_adapter_name = ActiveRecord::Base.connection.adapter_name |
|
76 | @db_adapter_name = ActiveRecord::Base.connection.adapter_name | |
77 | @checklist = [ |
|
77 | @checklist = [ | |
78 | [:text_default_administrator_account_changed, User.default_admin_account_changed?], |
|
78 | [:text_default_administrator_account_changed, User.default_admin_account_changed?], | |
79 | [:text_file_repository_writable, File.writable?(Attachment.storage_path)], |
|
79 | [:text_file_repository_writable, File.writable?(Attachment.storage_path)], | |
80 | [:text_plugin_assets_writable, File.writable?(Redmine::Plugin.public_directory)], |
|
80 | [:text_plugin_assets_writable, File.writable?(Redmine::Plugin.public_directory)], | |
81 | [:text_rmagick_available, Object.const_defined?(:Magick)] |
|
81 | [:text_rmagick_available, Object.const_defined?(:Magick)] | |
82 | ] |
|
82 | ] | |
83 | end |
|
83 | end | |
84 | end |
|
84 | end |
@@ -1,92 +1,94 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class DocumentsController < ApplicationController |
|
18 | class DocumentsController < ApplicationController | |
19 | default_search_scope :documents |
|
19 | default_search_scope :documents | |
20 | model_object Document |
|
20 | model_object Document | |
21 | before_filter :find_project_by_project_id, :only => [:index, :new, :create] |
|
21 | before_filter :find_project_by_project_id, :only => [:index, :new, :create] | |
22 | before_filter :find_model_object, :except => [:index, :new, :create] |
|
22 | before_filter :find_model_object, :except => [:index, :new, :create] | |
23 | before_filter :find_project_from_association, :except => [:index, :new, :create] |
|
23 | before_filter :find_project_from_association, :except => [:index, :new, :create] | |
24 | before_filter :authorize |
|
24 | before_filter :authorize | |
25 |
|
25 | |||
26 | helper :attachments |
|
26 | helper :attachments | |
27 |
|
27 | |||
28 | def index |
|
28 | def index | |
29 | @sort_by = %w(category date title author).include?(params[:sort_by]) ? params[:sort_by] : 'category' |
|
29 | @sort_by = %w(category date title author).include?(params[:sort_by]) ? params[:sort_by] : 'category' | |
30 | documents = @project.documents.find :all, :include => [:attachments, :category] |
|
30 | documents = @project.documents.find :all, :include => [:attachments, :category] | |
31 | case @sort_by |
|
31 | case @sort_by | |
32 | when 'date' |
|
32 | when 'date' | |
33 | @grouped = documents.group_by {|d| d.updated_on.to_date } |
|
33 | @grouped = documents.group_by {|d| d.updated_on.to_date } | |
34 | when 'title' |
|
34 | when 'title' | |
35 | @grouped = documents.group_by {|d| d.title.first.upcase} |
|
35 | @grouped = documents.group_by {|d| d.title.first.upcase} | |
36 | when 'author' |
|
36 | when 'author' | |
37 | @grouped = documents.select{|d| d.attachments.any?}.group_by {|d| d.attachments.last.author} |
|
37 | @grouped = documents.select{|d| d.attachments.any?}.group_by {|d| d.attachments.last.author} | |
38 | else |
|
38 | else | |
39 | @grouped = documents.group_by(&:category) |
|
39 | @grouped = documents.group_by(&:category) | |
40 | end |
|
40 | end | |
41 | @document = @project.documents.build |
|
41 | @document = @project.documents.build | |
42 | render :layout => false if request.xhr? |
|
42 | render :layout => false if request.xhr? | |
43 | end |
|
43 | end | |
44 |
|
44 | |||
45 | def show |
|
45 | def show | |
46 | @attachments = @document.attachments.find(:all, :order => "created_on DESC") |
|
46 | @attachments = @document.attachments.find(:all, :order => "created_on DESC") | |
47 | end |
|
47 | end | |
48 |
|
48 | |||
49 | def new |
|
49 | def new | |
50 | @document = @project.documents.build |
|
50 | @document = @project.documents.build | |
51 | @document.safe_attributes = params[:document] |
|
51 | @document.safe_attributes = params[:document] | |
52 | end |
|
52 | end | |
53 |
|
53 | |||
54 | def create |
|
54 | def create | |
55 | @document = @project.documents.build |
|
55 | @document = @project.documents.build | |
56 | @document.safe_attributes = params[:document] |
|
56 | @document.safe_attributes = params[:document] | |
57 | @document.save_attachments(params[:attachments]) |
|
57 | @document.save_attachments(params[:attachments]) | |
58 | if @document.save |
|
58 | if @document.save | |
59 | render_attachment_warning_if_needed(@document) |
|
59 | render_attachment_warning_if_needed(@document) | |
60 | flash[:notice] = l(:notice_successful_create) |
|
60 | flash[:notice] = l(:notice_successful_create) | |
61 | redirect_to :action => 'index', :project_id => @project |
|
61 | redirect_to :action => 'index', :project_id => @project | |
62 | else |
|
62 | else | |
63 | render :action => 'new' |
|
63 | render :action => 'new' | |
64 | end |
|
64 | end | |
65 | end |
|
65 | end | |
66 |
|
66 | |||
67 | def edit |
|
67 | def edit | |
68 | end |
|
68 | end | |
69 |
|
69 | |||
70 | def update |
|
70 | def update | |
71 | @document.safe_attributes = params[:document] |
|
71 | @document.safe_attributes = params[:document] | |
72 | if request.put? and @document.save |
|
72 | if request.put? and @document.save | |
73 | flash[:notice] = l(:notice_successful_update) |
|
73 | flash[:notice] = l(:notice_successful_update) | |
74 | redirect_to :action => 'show', :id => @document |
|
74 | redirect_to :action => 'show', :id => @document | |
75 | else |
|
75 | else | |
76 | render :action => 'edit' |
|
76 | render :action => 'edit' | |
77 | end |
|
77 | end | |
78 | end |
|
78 | end | |
79 |
|
79 | |||
80 | def destroy |
|
80 | def destroy | |
81 | @document.destroy if request.delete? |
|
81 | @document.destroy if request.delete? | |
82 | redirect_to :controller => 'documents', :action => 'index', :project_id => @project |
|
82 | redirect_to :controller => 'documents', :action => 'index', :project_id => @project | |
83 | end |
|
83 | end | |
84 |
|
84 | |||
85 | def add_attachment |
|
85 | def add_attachment | |
86 | attachments = Attachment.attach_files(@document, params[:attachments]) |
|
86 | attachments = Attachment.attach_files(@document, params[:attachments]) | |
87 | render_attachment_warning_if_needed(@document) |
|
87 | render_attachment_warning_if_needed(@document) | |
88 |
|
88 | |||
89 |
|
|
89 | if attachments.present? && attachments[:files].present? && Setting.notified_events.include?('document_added') | |
|
90 | Mailer.attachments_added(attachments[:files]).deliver | |||
|
91 | end | |||
90 | redirect_to :action => 'show', :id => @document |
|
92 | redirect_to :action => 'show', :id => @document | |
91 | end |
|
93 | end | |
92 | end |
|
94 | end |
@@ -1,53 +1,53 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class FilesController < ApplicationController |
|
18 | class FilesController < ApplicationController | |
19 | menu_item :files |
|
19 | menu_item :files | |
20 |
|
20 | |||
21 | before_filter :find_project_by_project_id |
|
21 | before_filter :find_project_by_project_id | |
22 | before_filter :authorize |
|
22 | before_filter :authorize | |
23 |
|
23 | |||
24 | helper :sort |
|
24 | helper :sort | |
25 | include SortHelper |
|
25 | include SortHelper | |
26 |
|
26 | |||
27 | def index |
|
27 | def index | |
28 | sort_init 'filename', 'asc' |
|
28 | sort_init 'filename', 'asc' | |
29 | sort_update 'filename' => "#{Attachment.table_name}.filename", |
|
29 | sort_update 'filename' => "#{Attachment.table_name}.filename", | |
30 | 'created_on' => "#{Attachment.table_name}.created_on", |
|
30 | 'created_on' => "#{Attachment.table_name}.created_on", | |
31 | 'size' => "#{Attachment.table_name}.filesize", |
|
31 | 'size' => "#{Attachment.table_name}.filesize", | |
32 | 'downloads' => "#{Attachment.table_name}.downloads" |
|
32 | 'downloads' => "#{Attachment.table_name}.downloads" | |
33 |
|
33 | |||
34 | @containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)] |
|
34 | @containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)] | |
35 | @containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse |
|
35 | @containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse | |
36 | render :layout => !request.xhr? |
|
36 | render :layout => !request.xhr? | |
37 | end |
|
37 | end | |
38 |
|
38 | |||
39 | def new |
|
39 | def new | |
40 | @versions = @project.versions.sort |
|
40 | @versions = @project.versions.sort | |
41 | end |
|
41 | end | |
42 |
|
42 | |||
43 | def create |
|
43 | def create | |
44 | container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id])) |
|
44 | container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id])) | |
45 | attachments = Attachment.attach_files(container, params[:attachments]) |
|
45 | attachments = Attachment.attach_files(container, params[:attachments]) | |
46 | render_attachment_warning_if_needed(container) |
|
46 | render_attachment_warning_if_needed(container) | |
47 |
|
47 | |||
48 | if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added') |
|
48 | if !attachments.empty? && !attachments[:files].blank? && Setting.notified_events.include?('file_added') | |
49 |
Mailer. |
|
49 | Mailer.attachments_added(attachments[:files]).deliver | |
50 | end |
|
50 | end | |
51 | redirect_to project_files_path(@project) |
|
51 | redirect_to project_files_path(@project) | |
52 | end |
|
52 | end | |
53 | end |
|
53 | end |
@@ -1,227 +1,227 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class UsersController < ApplicationController |
|
18 | class UsersController < ApplicationController | |
19 | layout 'admin' |
|
19 | layout 'admin' | |
20 |
|
20 | |||
21 | before_filter :require_admin, :except => :show |
|
21 | before_filter :require_admin, :except => :show | |
22 | before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership] |
|
22 | before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership] | |
23 | accept_api_auth :index, :show, :create, :update, :destroy |
|
23 | accept_api_auth :index, :show, :create, :update, :destroy | |
24 |
|
24 | |||
25 | helper :sort |
|
25 | helper :sort | |
26 | include SortHelper |
|
26 | include SortHelper | |
27 | helper :custom_fields |
|
27 | helper :custom_fields | |
28 | include CustomFieldsHelper |
|
28 | include CustomFieldsHelper | |
29 |
|
29 | |||
30 | def index |
|
30 | def index | |
31 | sort_init 'login', 'asc' |
|
31 | sort_init 'login', 'asc' | |
32 | sort_update %w(login firstname lastname mail admin created_on last_login_on) |
|
32 | sort_update %w(login firstname lastname mail admin created_on last_login_on) | |
33 |
|
33 | |||
34 | case params[:format] |
|
34 | case params[:format] | |
35 | when 'xml', 'json' |
|
35 | when 'xml', 'json' | |
36 | @offset, @limit = api_offset_and_limit |
|
36 | @offset, @limit = api_offset_and_limit | |
37 | else |
|
37 | else | |
38 | @limit = per_page_option |
|
38 | @limit = per_page_option | |
39 | end |
|
39 | end | |
40 |
|
40 | |||
41 | @status = params[:status] || 1 |
|
41 | @status = params[:status] || 1 | |
42 |
|
42 | |||
43 | scope = User.logged.status(@status) |
|
43 | scope = User.logged.status(@status) | |
44 | scope = scope.like(params[:name]) if params[:name].present? |
|
44 | scope = scope.like(params[:name]) if params[:name].present? | |
45 | scope = scope.in_group(params[:group_id]) if params[:group_id].present? |
|
45 | scope = scope.in_group(params[:group_id]) if params[:group_id].present? | |
46 |
|
46 | |||
47 | @user_count = scope.count |
|
47 | @user_count = scope.count | |
48 | @user_pages = Paginator.new self, @user_count, @limit, params['page'] |
|
48 | @user_pages = Paginator.new self, @user_count, @limit, params['page'] | |
49 | @offset ||= @user_pages.current.offset |
|
49 | @offset ||= @user_pages.current.offset | |
50 | @users = scope.find :all, |
|
50 | @users = scope.find :all, | |
51 | :order => sort_clause, |
|
51 | :order => sort_clause, | |
52 | :limit => @limit, |
|
52 | :limit => @limit, | |
53 | :offset => @offset |
|
53 | :offset => @offset | |
54 |
|
54 | |||
55 | respond_to do |format| |
|
55 | respond_to do |format| | |
56 | format.html { |
|
56 | format.html { | |
57 | @groups = Group.all.sort |
|
57 | @groups = Group.all.sort | |
58 | render :layout => !request.xhr? |
|
58 | render :layout => !request.xhr? | |
59 | } |
|
59 | } | |
60 | format.api |
|
60 | format.api | |
61 | end |
|
61 | end | |
62 | end |
|
62 | end | |
63 |
|
63 | |||
64 | def show |
|
64 | def show | |
65 | # show projects based on current user visibility |
|
65 | # show projects based on current user visibility | |
66 | @memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current)) |
|
66 | @memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current)) | |
67 |
|
67 | |||
68 | events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10) |
|
68 | events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10) | |
69 | @events_by_day = events.group_by(&:event_date) |
|
69 | @events_by_day = events.group_by(&:event_date) | |
70 |
|
70 | |||
71 | unless User.current.admin? |
|
71 | unless User.current.admin? | |
72 | if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?) |
|
72 | if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?) | |
73 | render_404 |
|
73 | render_404 | |
74 | return |
|
74 | return | |
75 | end |
|
75 | end | |
76 | end |
|
76 | end | |
77 |
|
77 | |||
78 | respond_to do |format| |
|
78 | respond_to do |format| | |
79 | format.html { render :layout => 'base' } |
|
79 | format.html { render :layout => 'base' } | |
80 | format.api |
|
80 | format.api | |
81 | end |
|
81 | end | |
82 | end |
|
82 | end | |
83 |
|
83 | |||
84 | def new |
|
84 | def new | |
85 | @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option) |
|
85 | @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option) | |
86 | @auth_sources = AuthSource.find(:all) |
|
86 | @auth_sources = AuthSource.find(:all) | |
87 | end |
|
87 | end | |
88 |
|
88 | |||
89 | def create |
|
89 | def create | |
90 | @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option) |
|
90 | @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option) | |
91 | @user.safe_attributes = params[:user] |
|
91 | @user.safe_attributes = params[:user] | |
92 | @user.admin = params[:user][:admin] || false |
|
92 | @user.admin = params[:user][:admin] || false | |
93 | @user.login = params[:user][:login] |
|
93 | @user.login = params[:user][:login] | |
94 | @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id |
|
94 | @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id | |
95 |
|
95 | |||
96 | if @user.save |
|
96 | if @user.save | |
97 | @user.pref.attributes = params[:pref] |
|
97 | @user.pref.attributes = params[:pref] | |
98 | @user.pref[:no_self_notified] = (params[:no_self_notified] == '1') |
|
98 | @user.pref[:no_self_notified] = (params[:no_self_notified] == '1') | |
99 | @user.pref.save |
|
99 | @user.pref.save | |
100 | @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : []) |
|
100 | @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : []) | |
101 |
|
101 | |||
102 |
Mailer. |
|
102 | Mailer.account_information(@user, params[:user][:password]).deliver if params[:send_information] | |
103 |
|
103 | |||
104 | respond_to do |format| |
|
104 | respond_to do |format| | |
105 | format.html { |
|
105 | format.html { | |
106 | flash[:notice] = l(:notice_successful_create) |
|
106 | flash[:notice] = l(:notice_successful_create) | |
107 | redirect_to(params[:continue] ? |
|
107 | redirect_to(params[:continue] ? | |
108 | {:controller => 'users', :action => 'new'} : |
|
108 | {:controller => 'users', :action => 'new'} : | |
109 | {:controller => 'users', :action => 'edit', :id => @user} |
|
109 | {:controller => 'users', :action => 'edit', :id => @user} | |
110 | ) |
|
110 | ) | |
111 | } |
|
111 | } | |
112 | format.api { render :action => 'show', :status => :created, :location => user_url(@user) } |
|
112 | format.api { render :action => 'show', :status => :created, :location => user_url(@user) } | |
113 | end |
|
113 | end | |
114 | else |
|
114 | else | |
115 | @auth_sources = AuthSource.find(:all) |
|
115 | @auth_sources = AuthSource.find(:all) | |
116 | # Clear password input |
|
116 | # Clear password input | |
117 | @user.password = @user.password_confirmation = nil |
|
117 | @user.password = @user.password_confirmation = nil | |
118 |
|
118 | |||
119 | respond_to do |format| |
|
119 | respond_to do |format| | |
120 | format.html { render :action => 'new' } |
|
120 | format.html { render :action => 'new' } | |
121 | format.api { render_validation_errors(@user) } |
|
121 | format.api { render_validation_errors(@user) } | |
122 | end |
|
122 | end | |
123 | end |
|
123 | end | |
124 | end |
|
124 | end | |
125 |
|
125 | |||
126 | def edit |
|
126 | def edit | |
127 | @auth_sources = AuthSource.find(:all) |
|
127 | @auth_sources = AuthSource.find(:all) | |
128 | @membership ||= Member.new |
|
128 | @membership ||= Member.new | |
129 | end |
|
129 | end | |
130 |
|
130 | |||
131 | def update |
|
131 | def update | |
132 | @user.admin = params[:user][:admin] if params[:user][:admin] |
|
132 | @user.admin = params[:user][:admin] if params[:user][:admin] | |
133 | @user.login = params[:user][:login] if params[:user][:login] |
|
133 | @user.login = params[:user][:login] if params[:user][:login] | |
134 | if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?) |
|
134 | if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?) | |
135 | @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] |
|
135 | @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] | |
136 | end |
|
136 | end | |
137 | @user.safe_attributes = params[:user] |
|
137 | @user.safe_attributes = params[:user] | |
138 | # Was the account actived ? (do it before User#save clears the change) |
|
138 | # Was the account actived ? (do it before User#save clears the change) | |
139 | was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE]) |
|
139 | was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE]) | |
140 | # TODO: Similar to My#account |
|
140 | # TODO: Similar to My#account | |
141 | @user.pref.attributes = params[:pref] |
|
141 | @user.pref.attributes = params[:pref] | |
142 | @user.pref[:no_self_notified] = (params[:no_self_notified] == '1') |
|
142 | @user.pref[:no_self_notified] = (params[:no_self_notified] == '1') | |
143 |
|
143 | |||
144 | if @user.save |
|
144 | if @user.save | |
145 | @user.pref.save |
|
145 | @user.pref.save | |
146 | @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : []) |
|
146 | @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : []) | |
147 |
|
147 | |||
148 | if was_activated |
|
148 | if was_activated | |
149 |
Mailer. |
|
149 | Mailer.account_activated(@user).deliver | |
150 | elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil? |
|
150 | elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil? | |
151 |
Mailer. |
|
151 | Mailer.account_information(@user, params[:user][:password]).deliver | |
152 | end |
|
152 | end | |
153 |
|
153 | |||
154 | respond_to do |format| |
|
154 | respond_to do |format| | |
155 | format.html { |
|
155 | format.html { | |
156 | flash[:notice] = l(:notice_successful_update) |
|
156 | flash[:notice] = l(:notice_successful_update) | |
157 | redirect_to_referer_or edit_user_path(@user) |
|
157 | redirect_to_referer_or edit_user_path(@user) | |
158 | } |
|
158 | } | |
159 | format.api { head :ok } |
|
159 | format.api { head :ok } | |
160 | end |
|
160 | end | |
161 | else |
|
161 | else | |
162 | @auth_sources = AuthSource.find(:all) |
|
162 | @auth_sources = AuthSource.find(:all) | |
163 | @membership ||= Member.new |
|
163 | @membership ||= Member.new | |
164 | # Clear password input |
|
164 | # Clear password input | |
165 | @user.password = @user.password_confirmation = nil |
|
165 | @user.password = @user.password_confirmation = nil | |
166 |
|
166 | |||
167 | respond_to do |format| |
|
167 | respond_to do |format| | |
168 | format.html { render :action => :edit } |
|
168 | format.html { render :action => :edit } | |
169 | format.api { render_validation_errors(@user) } |
|
169 | format.api { render_validation_errors(@user) } | |
170 | end |
|
170 | end | |
171 | end |
|
171 | end | |
172 | end |
|
172 | end | |
173 |
|
173 | |||
174 | def destroy |
|
174 | def destroy | |
175 | @user.destroy |
|
175 | @user.destroy | |
176 | respond_to do |format| |
|
176 | respond_to do |format| | |
177 | format.html { redirect_to(users_url) } |
|
177 | format.html { redirect_to(users_url) } | |
178 | format.api { head :ok } |
|
178 | format.api { head :ok } | |
179 | end |
|
179 | end | |
180 | end |
|
180 | end | |
181 |
|
181 | |||
182 | def edit_membership |
|
182 | def edit_membership | |
183 | @membership = Member.edit_membership(params[:membership_id], params[:membership], @user) |
|
183 | @membership = Member.edit_membership(params[:membership_id], params[:membership], @user) | |
184 | @membership.save |
|
184 | @membership.save | |
185 | respond_to do |format| |
|
185 | respond_to do |format| | |
186 | if @membership.valid? |
|
186 | if @membership.valid? | |
187 | format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } |
|
187 | format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } | |
188 | format.js { |
|
188 | format.js { | |
189 | render(:update) {|page| |
|
189 | render(:update) {|page| | |
190 | page.replace_html "tab-content-memberships", :partial => 'users/memberships' |
|
190 | page.replace_html "tab-content-memberships", :partial => 'users/memberships' | |
191 | page.visual_effect(:highlight, "member-#{@membership.id}") |
|
191 | page.visual_effect(:highlight, "member-#{@membership.id}") | |
192 | } |
|
192 | } | |
193 | } |
|
193 | } | |
194 | else |
|
194 | else | |
195 | format.js { |
|
195 | format.js { | |
196 | render(:update) {|page| |
|
196 | render(:update) {|page| | |
197 | page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', '))) |
|
197 | page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', '))) | |
198 | } |
|
198 | } | |
199 | } |
|
199 | } | |
200 | end |
|
200 | end | |
201 | end |
|
201 | end | |
202 | end |
|
202 | end | |
203 |
|
203 | |||
204 | def destroy_membership |
|
204 | def destroy_membership | |
205 | @membership = Member.find(params[:membership_id]) |
|
205 | @membership = Member.find(params[:membership_id]) | |
206 | if @membership.deletable? |
|
206 | if @membership.deletable? | |
207 | @membership.destroy |
|
207 | @membership.destroy | |
208 | end |
|
208 | end | |
209 | respond_to do |format| |
|
209 | respond_to do |format| | |
210 | format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } |
|
210 | format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' } | |
211 | format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} } |
|
211 | format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} } | |
212 | end |
|
212 | end | |
213 | end |
|
213 | end | |
214 |
|
214 | |||
215 | private |
|
215 | private | |
216 |
|
216 | |||
217 | def find_user |
|
217 | def find_user | |
218 | if params[:id] == 'current' |
|
218 | if params[:id] == 'current' | |
219 | require_login || return |
|
219 | require_login || return | |
220 | @user = User.current |
|
220 | @user = User.current | |
221 | else |
|
221 | else | |
222 | @user = User.find(params[:id]) |
|
222 | @user = User.find(params[:id]) | |
223 | end |
|
223 | end | |
224 | rescue ActiveRecord::RecordNotFound |
|
224 | rescue ActiveRecord::RecordNotFound | |
225 | render_404 |
|
225 | render_404 | |
226 | end |
|
226 | end | |
227 | end |
|
227 | end |
@@ -1,24 +1,24 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class CommentObserver < ActiveRecord::Observer |
|
18 | class CommentObserver < ActiveRecord::Observer | |
19 | def after_create(comment) |
|
19 | def after_create(comment) | |
20 | if comment.commented.is_a?(News) && Setting.notified_events.include?('news_comment_added') |
|
20 | if comment.commented.is_a?(News) && Setting.notified_events.include?('news_comment_added') | |
21 |
Mailer. |
|
21 | Mailer.news_comment_added(comment).deliver | |
22 | end |
|
22 | end | |
23 | end |
|
23 | end | |
24 | end |
|
24 | end |
@@ -1,22 +1,22 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class DocumentObserver < ActiveRecord::Observer |
|
18 | class DocumentObserver < ActiveRecord::Observer | |
19 | def after_create(document) |
|
19 | def after_create(document) | |
20 |
Mailer. |
|
20 | Mailer.document_added(document).deliver if Setting.notified_events.include?('document_added') | |
21 | end |
|
21 | end | |
22 | end |
|
22 | end |
@@ -1,22 +1,22 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class IssueObserver < ActiveRecord::Observer |
|
18 | class IssueObserver < ActiveRecord::Observer | |
19 | def after_create(issue) |
|
19 | def after_create(issue) | |
20 |
Mailer. |
|
20 | Mailer.issue_add(issue).deliver if Setting.notified_events.include?('issue_added') | |
21 | end |
|
21 | end | |
22 | end |
|
22 | end |
@@ -1,29 +1,29 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class JournalObserver < ActiveRecord::Observer |
|
18 | class JournalObserver < ActiveRecord::Observer | |
19 | def after_create(journal) |
|
19 | def after_create(journal) | |
20 | if journal.notify? && |
|
20 | if journal.notify? && | |
21 | (Setting.notified_events.include?('issue_updated') || |
|
21 | (Setting.notified_events.include?('issue_updated') || | |
22 | (Setting.notified_events.include?('issue_note_added') && journal.notes.present?) || |
|
22 | (Setting.notified_events.include?('issue_note_added') && journal.notes.present?) || | |
23 | (Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) || |
|
23 | (Setting.notified_events.include?('issue_status_updated') && journal.new_status.present?) || | |
24 | (Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?) |
|
24 | (Setting.notified_events.include?('issue_priority_updated') && journal.new_value_for('priority_id').present?) | |
25 | ) |
|
25 | ) | |
26 |
Mailer. |
|
26 | Mailer.issue_edit(journal).deliver | |
27 | end |
|
27 | end | |
28 | end |
|
28 | end | |
29 | end |
|
29 | end |
@@ -1,459 +1,459 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class MailHandler < ActionMailer::Base |
|
18 | class MailHandler < ActionMailer::Base | |
19 | include ActionView::Helpers::SanitizeHelper |
|
19 | include ActionView::Helpers::SanitizeHelper | |
20 | include Redmine::I18n |
|
20 | include Redmine::I18n | |
21 |
|
21 | |||
22 | class UnauthorizedAction < StandardError; end |
|
22 | class UnauthorizedAction < StandardError; end | |
23 | class MissingInformation < StandardError; end |
|
23 | class MissingInformation < StandardError; end | |
24 |
|
24 | |||
25 | attr_reader :email, :user |
|
25 | attr_reader :email, :user | |
26 |
|
26 | |||
27 | def self.receive(email, options={}) |
|
27 | def self.receive(email, options={}) | |
28 | @@handler_options = options.dup |
|
28 | @@handler_options = options.dup | |
29 |
|
29 | |||
30 | @@handler_options[:issue] ||= {} |
|
30 | @@handler_options[:issue] ||= {} | |
31 |
|
31 | |||
32 | if @@handler_options[:allow_override].is_a?(String) |
|
32 | if @@handler_options[:allow_override].is_a?(String) | |
33 | @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) |
|
33 | @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip) | |
34 | end |
|
34 | end | |
35 | @@handler_options[:allow_override] ||= [] |
|
35 | @@handler_options[:allow_override] ||= [] | |
36 | # Project needs to be overridable if not specified |
|
36 | # Project needs to be overridable if not specified | |
37 | @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project) |
|
37 | @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project) | |
38 | # Status overridable by default |
|
38 | # Status overridable by default | |
39 | @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status) |
|
39 | @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status) | |
40 |
|
40 | |||
41 | @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1' ? true : false) |
|
41 | @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1' ? true : false) | |
42 |
|
42 | |||
43 | email.force_encoding('ASCII-8BIT') if email.respond_to?(:force_encoding) |
|
43 | email.force_encoding('ASCII-8BIT') if email.respond_to?(:force_encoding) | |
44 | super(email) |
|
44 | super(email) | |
45 | end |
|
45 | end | |
46 |
|
46 | |||
47 | def logger |
|
47 | def logger | |
48 | Rails.logger |
|
48 | Rails.logger | |
49 | end |
|
49 | end | |
50 |
|
50 | |||
51 | cattr_accessor :ignored_emails_headers |
|
51 | cattr_accessor :ignored_emails_headers | |
52 | @@ignored_emails_headers = { |
|
52 | @@ignored_emails_headers = { | |
53 | 'X-Auto-Response-Suppress' => 'OOF', |
|
53 | 'X-Auto-Response-Suppress' => 'OOF', | |
54 | 'Auto-Submitted' => 'auto-replied' |
|
54 | 'Auto-Submitted' => 'auto-replied' | |
55 | } |
|
55 | } | |
56 |
|
56 | |||
57 | # Processes incoming emails |
|
57 | # Processes incoming emails | |
58 | # Returns the created object (eg. an issue, a message) or false |
|
58 | # Returns the created object (eg. an issue, a message) or false | |
59 | def receive(email) |
|
59 | def receive(email) | |
60 | @email = email |
|
60 | @email = email | |
61 | sender_email = email.from.to_a.first.to_s.strip |
|
61 | sender_email = email.from.to_a.first.to_s.strip | |
62 | # Ignore emails received from the application emission address to avoid hell cycles |
|
62 | # Ignore emails received from the application emission address to avoid hell cycles | |
63 | if sender_email.downcase == Setting.mail_from.to_s.strip.downcase |
|
63 | if sender_email.downcase == Setting.mail_from.to_s.strip.downcase | |
64 | if logger && logger.info |
|
64 | if logger && logger.info | |
65 | logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]" |
|
65 | logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]" | |
66 | end |
|
66 | end | |
67 | return false |
|
67 | return false | |
68 | end |
|
68 | end | |
69 | # Ignore auto generated emails |
|
69 | # Ignore auto generated emails | |
70 | self.class.ignored_emails_headers.each do |key, ignored_value| |
|
70 | self.class.ignored_emails_headers.each do |key, ignored_value| | |
71 | value = email.header[key] |
|
71 | value = email.header[key] | |
72 | if value && value.to_s.downcase == ignored_value.downcase |
|
72 | if value && value.to_s.downcase == ignored_value.downcase | |
73 | if logger && logger.info |
|
73 | if logger && logger.info | |
74 | logger.info "MailHandler: ignoring email with #{key}:#{value} header" |
|
74 | logger.info "MailHandler: ignoring email with #{key}:#{value} header" | |
75 | end |
|
75 | end | |
76 | return false |
|
76 | return false | |
77 | end |
|
77 | end | |
78 | end |
|
78 | end | |
79 | @user = User.find_by_mail(sender_email) if sender_email.present? |
|
79 | @user = User.find_by_mail(sender_email) if sender_email.present? | |
80 | if @user && !@user.active? |
|
80 | if @user && !@user.active? | |
81 | if logger && logger.info |
|
81 | if logger && logger.info | |
82 | logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]" |
|
82 | logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]" | |
83 | end |
|
83 | end | |
84 | return false |
|
84 | return false | |
85 | end |
|
85 | end | |
86 | if @user.nil? |
|
86 | if @user.nil? | |
87 | # Email was submitted by an unknown user |
|
87 | # Email was submitted by an unknown user | |
88 | case @@handler_options[:unknown_user] |
|
88 | case @@handler_options[:unknown_user] | |
89 | when 'accept' |
|
89 | when 'accept' | |
90 | @user = User.anonymous |
|
90 | @user = User.anonymous | |
91 | when 'create' |
|
91 | when 'create' | |
92 | @user = create_user_from_email |
|
92 | @user = create_user_from_email | |
93 | if @user |
|
93 | if @user | |
94 | if logger && logger.info |
|
94 | if logger && logger.info | |
95 | logger.info "MailHandler: [#{@user.login}] account created" |
|
95 | logger.info "MailHandler: [#{@user.login}] account created" | |
96 | end |
|
96 | end | |
97 |
Mailer. |
|
97 | Mailer.account_information(@user, @user.password).deliver | |
98 | else |
|
98 | else | |
99 | if logger && logger.error |
|
99 | if logger && logger.error | |
100 | logger.error "MailHandler: could not create account for [#{sender_email}]" |
|
100 | logger.error "MailHandler: could not create account for [#{sender_email}]" | |
101 | end |
|
101 | end | |
102 | return false |
|
102 | return false | |
103 | end |
|
103 | end | |
104 | else |
|
104 | else | |
105 | # Default behaviour, emails from unknown users are ignored |
|
105 | # Default behaviour, emails from unknown users are ignored | |
106 | if logger && logger.info |
|
106 | if logger && logger.info | |
107 | logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]" |
|
107 | logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]" | |
108 | end |
|
108 | end | |
109 | return false |
|
109 | return false | |
110 | end |
|
110 | end | |
111 | end |
|
111 | end | |
112 | User.current = @user |
|
112 | User.current = @user | |
113 | dispatch |
|
113 | dispatch | |
114 | end |
|
114 | end | |
115 |
|
115 | |||
116 | private |
|
116 | private | |
117 |
|
117 | |||
118 | MESSAGE_ID_RE = %r{^<?redmine\.([a-z0-9_]+)\-(\d+)\.\d+@} |
|
118 | MESSAGE_ID_RE = %r{^<?redmine\.([a-z0-9_]+)\-(\d+)\.\d+@} | |
119 | ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]} |
|
119 | ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]} | |
120 | MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]} |
|
120 | MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]} | |
121 |
|
121 | |||
122 | def dispatch |
|
122 | def dispatch | |
123 | headers = [email.in_reply_to, email.references].flatten.compact |
|
123 | headers = [email.in_reply_to, email.references].flatten.compact | |
124 | if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE} |
|
124 | if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE} | |
125 | klass, object_id = $1, $2.to_i |
|
125 | klass, object_id = $1, $2.to_i | |
126 | method_name = "receive_#{klass}_reply" |
|
126 | method_name = "receive_#{klass}_reply" | |
127 | if self.class.private_instance_methods.collect(&:to_s).include?(method_name) |
|
127 | if self.class.private_instance_methods.collect(&:to_s).include?(method_name) | |
128 | send method_name, object_id |
|
128 | send method_name, object_id | |
129 | else |
|
129 | else | |
130 | # ignoring it |
|
130 | # ignoring it | |
131 | end |
|
131 | end | |
132 | elsif m = email.subject.match(ISSUE_REPLY_SUBJECT_RE) |
|
132 | elsif m = email.subject.match(ISSUE_REPLY_SUBJECT_RE) | |
133 | receive_issue_reply(m[1].to_i) |
|
133 | receive_issue_reply(m[1].to_i) | |
134 | elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE) |
|
134 | elsif m = email.subject.match(MESSAGE_REPLY_SUBJECT_RE) | |
135 | receive_message_reply(m[1].to_i) |
|
135 | receive_message_reply(m[1].to_i) | |
136 | else |
|
136 | else | |
137 | dispatch_to_default |
|
137 | dispatch_to_default | |
138 | end |
|
138 | end | |
139 | rescue ActiveRecord::RecordInvalid => e |
|
139 | rescue ActiveRecord::RecordInvalid => e | |
140 | # TODO: send a email to the user |
|
140 | # TODO: send a email to the user | |
141 | logger.error e.message if logger |
|
141 | logger.error e.message if logger | |
142 | false |
|
142 | false | |
143 | rescue MissingInformation => e |
|
143 | rescue MissingInformation => e | |
144 | logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger |
|
144 | logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger | |
145 | false |
|
145 | false | |
146 | rescue UnauthorizedAction => e |
|
146 | rescue UnauthorizedAction => e | |
147 | logger.error "MailHandler: unauthorized attempt from #{user}" if logger |
|
147 | logger.error "MailHandler: unauthorized attempt from #{user}" if logger | |
148 | false |
|
148 | false | |
149 | end |
|
149 | end | |
150 |
|
150 | |||
151 | def dispatch_to_default |
|
151 | def dispatch_to_default | |
152 | receive_issue |
|
152 | receive_issue | |
153 | end |
|
153 | end | |
154 |
|
154 | |||
155 | # Creates a new issue |
|
155 | # Creates a new issue | |
156 | def receive_issue |
|
156 | def receive_issue | |
157 | project = target_project |
|
157 | project = target_project | |
158 | # check permission |
|
158 | # check permission | |
159 | unless @@handler_options[:no_permission_check] |
|
159 | unless @@handler_options[:no_permission_check] | |
160 | raise UnauthorizedAction unless user.allowed_to?(:add_issues, project) |
|
160 | raise UnauthorizedAction unless user.allowed_to?(:add_issues, project) | |
161 | end |
|
161 | end | |
162 |
|
162 | |||
163 | issue = Issue.new(:author => user, :project => project) |
|
163 | issue = Issue.new(:author => user, :project => project) | |
164 | issue.safe_attributes = issue_attributes_from_keywords(issue) |
|
164 | issue.safe_attributes = issue_attributes_from_keywords(issue) | |
165 | issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} |
|
165 | issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} | |
166 | issue.subject = email.subject.to_s.chomp[0,255] |
|
166 | issue.subject = email.subject.to_s.chomp[0,255] | |
167 | if issue.subject.blank? |
|
167 | if issue.subject.blank? | |
168 | issue.subject = '(no subject)' |
|
168 | issue.subject = '(no subject)' | |
169 | end |
|
169 | end | |
170 | issue.description = cleaned_up_text_body |
|
170 | issue.description = cleaned_up_text_body | |
171 |
|
171 | |||
172 | # add To and Cc as watchers before saving so the watchers can reply to Redmine |
|
172 | # add To and Cc as watchers before saving so the watchers can reply to Redmine | |
173 | add_watchers(issue) |
|
173 | add_watchers(issue) | |
174 | issue.save! |
|
174 | issue.save! | |
175 | add_attachments(issue) |
|
175 | add_attachments(issue) | |
176 | logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info |
|
176 | logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info | |
177 | issue |
|
177 | issue | |
178 | end |
|
178 | end | |
179 |
|
179 | |||
180 | # Adds a note to an existing issue |
|
180 | # Adds a note to an existing issue | |
181 | def receive_issue_reply(issue_id) |
|
181 | def receive_issue_reply(issue_id) | |
182 | issue = Issue.find_by_id(issue_id) |
|
182 | issue = Issue.find_by_id(issue_id) | |
183 | return unless issue |
|
183 | return unless issue | |
184 | # check permission |
|
184 | # check permission | |
185 | unless @@handler_options[:no_permission_check] |
|
185 | unless @@handler_options[:no_permission_check] | |
186 | unless user.allowed_to?(:add_issue_notes, issue.project) || |
|
186 | unless user.allowed_to?(:add_issue_notes, issue.project) || | |
187 | user.allowed_to?(:edit_issues, issue.project) |
|
187 | user.allowed_to?(:edit_issues, issue.project) | |
188 | raise UnauthorizedAction |
|
188 | raise UnauthorizedAction | |
189 | end |
|
189 | end | |
190 | end |
|
190 | end | |
191 |
|
191 | |||
192 | # ignore CLI-supplied defaults for new issues |
|
192 | # ignore CLI-supplied defaults for new issues | |
193 | @@handler_options[:issue].clear |
|
193 | @@handler_options[:issue].clear | |
194 |
|
194 | |||
195 | journal = issue.init_journal(user) |
|
195 | journal = issue.init_journal(user) | |
196 | issue.safe_attributes = issue_attributes_from_keywords(issue) |
|
196 | issue.safe_attributes = issue_attributes_from_keywords(issue) | |
197 | issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} |
|
197 | issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)} | |
198 | journal.notes = cleaned_up_text_body |
|
198 | journal.notes = cleaned_up_text_body | |
199 | add_attachments(issue) |
|
199 | add_attachments(issue) | |
200 | issue.save! |
|
200 | issue.save! | |
201 | if logger && logger.info |
|
201 | if logger && logger.info | |
202 | logger.info "MailHandler: issue ##{issue.id} updated by #{user}" |
|
202 | logger.info "MailHandler: issue ##{issue.id} updated by #{user}" | |
203 | end |
|
203 | end | |
204 | journal |
|
204 | journal | |
205 | end |
|
205 | end | |
206 |
|
206 | |||
207 | # Reply will be added to the issue |
|
207 | # Reply will be added to the issue | |
208 | def receive_journal_reply(journal_id) |
|
208 | def receive_journal_reply(journal_id) | |
209 | journal = Journal.find_by_id(journal_id) |
|
209 | journal = Journal.find_by_id(journal_id) | |
210 | if journal && journal.journalized_type == 'Issue' |
|
210 | if journal && journal.journalized_type == 'Issue' | |
211 | receive_issue_reply(journal.journalized_id) |
|
211 | receive_issue_reply(journal.journalized_id) | |
212 | end |
|
212 | end | |
213 | end |
|
213 | end | |
214 |
|
214 | |||
215 | # Receives a reply to a forum message |
|
215 | # Receives a reply to a forum message | |
216 | def receive_message_reply(message_id) |
|
216 | def receive_message_reply(message_id) | |
217 | message = Message.find_by_id(message_id) |
|
217 | message = Message.find_by_id(message_id) | |
218 | if message |
|
218 | if message | |
219 | message = message.root |
|
219 | message = message.root | |
220 |
|
220 | |||
221 | unless @@handler_options[:no_permission_check] |
|
221 | unless @@handler_options[:no_permission_check] | |
222 | raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project) |
|
222 | raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project) | |
223 | end |
|
223 | end | |
224 |
|
224 | |||
225 | if !message.locked? |
|
225 | if !message.locked? | |
226 | reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip, |
|
226 | reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip, | |
227 | :content => cleaned_up_text_body) |
|
227 | :content => cleaned_up_text_body) | |
228 | reply.author = user |
|
228 | reply.author = user | |
229 | reply.board = message.board |
|
229 | reply.board = message.board | |
230 | message.children << reply |
|
230 | message.children << reply | |
231 | add_attachments(reply) |
|
231 | add_attachments(reply) | |
232 | reply |
|
232 | reply | |
233 | else |
|
233 | else | |
234 | if logger && logger.info |
|
234 | if logger && logger.info | |
235 | logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" |
|
235 | logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic" | |
236 | end |
|
236 | end | |
237 | end |
|
237 | end | |
238 | end |
|
238 | end | |
239 | end |
|
239 | end | |
240 |
|
240 | |||
241 | def add_attachments(obj) |
|
241 | def add_attachments(obj) | |
242 | if email.attachments && email.attachments.any? |
|
242 | if email.attachments && email.attachments.any? | |
243 | email.attachments.each do |attachment| |
|
243 | email.attachments.each do |attachment| | |
244 | obj.attachments << Attachment.create(:container => obj, |
|
244 | obj.attachments << Attachment.create(:container => obj, | |
245 | :file => attachment.decoded, |
|
245 | :file => attachment.decoded, | |
246 | :filename => attachment.filename, |
|
246 | :filename => attachment.filename, | |
247 | :author => user, |
|
247 | :author => user, | |
248 | :content_type => attachment.mime_type) |
|
248 | :content_type => attachment.mime_type) | |
249 | end |
|
249 | end | |
250 | end |
|
250 | end | |
251 | end |
|
251 | end | |
252 |
|
252 | |||
253 | # Adds To and Cc as watchers of the given object if the sender has the |
|
253 | # Adds To and Cc as watchers of the given object if the sender has the | |
254 | # appropriate permission |
|
254 | # appropriate permission | |
255 | def add_watchers(obj) |
|
255 | def add_watchers(obj) | |
256 | if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project) |
|
256 | if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project) | |
257 | addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase} |
|
257 | addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase} | |
258 | unless addresses.empty? |
|
258 | unless addresses.empty? | |
259 | watchers = User.active.find(:all, :conditions => ['LOWER(mail) IN (?)', addresses]) |
|
259 | watchers = User.active.find(:all, :conditions => ['LOWER(mail) IN (?)', addresses]) | |
260 | watchers.each {|w| obj.add_watcher(w)} |
|
260 | watchers.each {|w| obj.add_watcher(w)} | |
261 | end |
|
261 | end | |
262 | end |
|
262 | end | |
263 | end |
|
263 | end | |
264 |
|
264 | |||
265 | def get_keyword(attr, options={}) |
|
265 | def get_keyword(attr, options={}) | |
266 | @keywords ||= {} |
|
266 | @keywords ||= {} | |
267 | if @keywords.has_key?(attr) |
|
267 | if @keywords.has_key?(attr) | |
268 | @keywords[attr] |
|
268 | @keywords[attr] | |
269 | else |
|
269 | else | |
270 | @keywords[attr] = begin |
|
270 | @keywords[attr] = begin | |
271 | if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && |
|
271 | if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) && | |
272 | (v = extract_keyword!(plain_text_body, attr, options[:format])) |
|
272 | (v = extract_keyword!(plain_text_body, attr, options[:format])) | |
273 | v |
|
273 | v | |
274 | elsif !@@handler_options[:issue][attr].blank? |
|
274 | elsif !@@handler_options[:issue][attr].blank? | |
275 | @@handler_options[:issue][attr] |
|
275 | @@handler_options[:issue][attr] | |
276 | end |
|
276 | end | |
277 | end |
|
277 | end | |
278 | end |
|
278 | end | |
279 | end |
|
279 | end | |
280 |
|
280 | |||
281 | # Destructively extracts the value for +attr+ in +text+ |
|
281 | # Destructively extracts the value for +attr+ in +text+ | |
282 | # Returns nil if no matching keyword found |
|
282 | # Returns nil if no matching keyword found | |
283 | def extract_keyword!(text, attr, format=nil) |
|
283 | def extract_keyword!(text, attr, format=nil) | |
284 | keys = [attr.to_s.humanize] |
|
284 | keys = [attr.to_s.humanize] | |
285 | if attr.is_a?(Symbol) |
|
285 | if attr.is_a?(Symbol) | |
286 | if user && user.language.present? |
|
286 | if user && user.language.present? | |
287 | keys << l("field_#{attr}", :default => '', :locale => user.language) |
|
287 | keys << l("field_#{attr}", :default => '', :locale => user.language) | |
288 | end |
|
288 | end | |
289 | if Setting.default_language.present? |
|
289 | if Setting.default_language.present? | |
290 | keys << l("field_#{attr}", :default => '', :locale => Setting.default_language) |
|
290 | keys << l("field_#{attr}", :default => '', :locale => Setting.default_language) | |
291 | end |
|
291 | end | |
292 | end |
|
292 | end | |
293 | keys.reject! {|k| k.blank?} |
|
293 | keys.reject! {|k| k.blank?} | |
294 | keys.collect! {|k| Regexp.escape(k)} |
|
294 | keys.collect! {|k| Regexp.escape(k)} | |
295 | format ||= '.+' |
|
295 | format ||= '.+' | |
296 | keyword = nil |
|
296 | keyword = nil | |
297 | regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i |
|
297 | regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i | |
298 | if m = text.match(regexp) |
|
298 | if m = text.match(regexp) | |
299 | keyword = m[2].strip |
|
299 | keyword = m[2].strip | |
300 | text.gsub!(regexp, '') |
|
300 | text.gsub!(regexp, '') | |
301 | end |
|
301 | end | |
302 | keyword |
|
302 | keyword | |
303 | end |
|
303 | end | |
304 |
|
304 | |||
305 | def target_project |
|
305 | def target_project | |
306 | # TODO: other ways to specify project: |
|
306 | # TODO: other ways to specify project: | |
307 | # * parse the email To field |
|
307 | # * parse the email To field | |
308 | # * specific project (eg. Setting.mail_handler_target_project) |
|
308 | # * specific project (eg. Setting.mail_handler_target_project) | |
309 | target = Project.find_by_identifier(get_keyword(:project)) |
|
309 | target = Project.find_by_identifier(get_keyword(:project)) | |
310 | raise MissingInformation.new('Unable to determine target project') if target.nil? |
|
310 | raise MissingInformation.new('Unable to determine target project') if target.nil? | |
311 | target |
|
311 | target | |
312 | end |
|
312 | end | |
313 |
|
313 | |||
314 | # Returns a Hash of issue attributes extracted from keywords in the email body |
|
314 | # Returns a Hash of issue attributes extracted from keywords in the email body | |
315 | def issue_attributes_from_keywords(issue) |
|
315 | def issue_attributes_from_keywords(issue) | |
316 | assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue) |
|
316 | assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue) | |
317 |
|
317 | |||
318 | attrs = { |
|
318 | attrs = { | |
319 | 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id), |
|
319 | 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id), | |
320 | 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id), |
|
320 | 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id), | |
321 | 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id), |
|
321 | 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id), | |
322 | 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id), |
|
322 | 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id), | |
323 | 'assigned_to_id' => assigned_to.try(:id), |
|
323 | 'assigned_to_id' => assigned_to.try(:id), | |
324 | 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && |
|
324 | 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) && | |
325 | issue.project.shared_versions.named(k).first.try(:id), |
|
325 | issue.project.shared_versions.named(k).first.try(:id), | |
326 | 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), |
|
326 | 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), | |
327 | 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), |
|
327 | 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'), | |
328 | 'estimated_hours' => get_keyword(:estimated_hours, :override => true), |
|
328 | 'estimated_hours' => get_keyword(:estimated_hours, :override => true), | |
329 | 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0') |
|
329 | 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0') | |
330 | }.delete_if {|k, v| v.blank? } |
|
330 | }.delete_if {|k, v| v.blank? } | |
331 |
|
331 | |||
332 | if issue.new_record? && attrs['tracker_id'].nil? |
|
332 | if issue.new_record? && attrs['tracker_id'].nil? | |
333 | attrs['tracker_id'] = issue.project.trackers.find(:first).try(:id) |
|
333 | attrs['tracker_id'] = issue.project.trackers.find(:first).try(:id) | |
334 | end |
|
334 | end | |
335 |
|
335 | |||
336 | attrs |
|
336 | attrs | |
337 | end |
|
337 | end | |
338 |
|
338 | |||
339 | # Returns a Hash of issue custom field values extracted from keywords in the email body |
|
339 | # Returns a Hash of issue custom field values extracted from keywords in the email body | |
340 | def custom_field_values_from_keywords(customized) |
|
340 | def custom_field_values_from_keywords(customized) | |
341 | customized.custom_field_values.inject({}) do |h, v| |
|
341 | customized.custom_field_values.inject({}) do |h, v| | |
342 | if value = get_keyword(v.custom_field.name, :override => true) |
|
342 | if value = get_keyword(v.custom_field.name, :override => true) | |
343 | h[v.custom_field.id.to_s] = value |
|
343 | h[v.custom_field.id.to_s] = value | |
344 | end |
|
344 | end | |
345 | h |
|
345 | h | |
346 | end |
|
346 | end | |
347 | end |
|
347 | end | |
348 |
|
348 | |||
349 | # Returns the text/plain part of the email |
|
349 | # Returns the text/plain part of the email | |
350 | # If not found (eg. HTML-only email), returns the body with tags removed |
|
350 | # If not found (eg. HTML-only email), returns the body with tags removed | |
351 | def plain_text_body |
|
351 | def plain_text_body | |
352 | return @plain_text_body unless @plain_text_body.nil? |
|
352 | return @plain_text_body unless @plain_text_body.nil? | |
353 |
|
353 | |||
354 | part = email.text_part || email.html_part || email |
|
354 | part = email.text_part || email.html_part || email | |
355 | @plain_text_body = Redmine::CodesetUtil.to_utf8(part.body.decoded, part.charset) |
|
355 | @plain_text_body = Redmine::CodesetUtil.to_utf8(part.body.decoded, part.charset) | |
356 |
|
356 | |||
357 | if @plain_text_body.respond_to?(:force_encoding) |
|
357 | if @plain_text_body.respond_to?(:force_encoding) | |
358 | # @plain_text_body = @plain_text_body.force_encoding(@email.charset).encode("UTF-8") |
|
358 | # @plain_text_body = @plain_text_body.force_encoding(@email.charset).encode("UTF-8") | |
359 | end |
|
359 | end | |
360 |
|
360 | |||
361 | # strip html tags and remove doctype directive |
|
361 | # strip html tags and remove doctype directive | |
362 | @plain_text_body = strip_tags(@plain_text_body.strip) |
|
362 | @plain_text_body = strip_tags(@plain_text_body.strip) | |
363 | @plain_text_body.sub! %r{^<!DOCTYPE .*$}, '' |
|
363 | @plain_text_body.sub! %r{^<!DOCTYPE .*$}, '' | |
364 | @plain_text_body |
|
364 | @plain_text_body | |
365 | end |
|
365 | end | |
366 |
|
366 | |||
367 | def cleaned_up_text_body |
|
367 | def cleaned_up_text_body | |
368 | cleanup_body(plain_text_body) |
|
368 | cleanup_body(plain_text_body) | |
369 | end |
|
369 | end | |
370 |
|
370 | |||
371 | def self.full_sanitizer |
|
371 | def self.full_sanitizer | |
372 | @full_sanitizer ||= HTML::FullSanitizer.new |
|
372 | @full_sanitizer ||= HTML::FullSanitizer.new | |
373 | end |
|
373 | end | |
374 |
|
374 | |||
375 | def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil) |
|
375 | def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil) | |
376 | limit ||= object.class.columns_hash[attribute.to_s].limit || 255 |
|
376 | limit ||= object.class.columns_hash[attribute.to_s].limit || 255 | |
377 | value = value.to_s.slice(0, limit) |
|
377 | value = value.to_s.slice(0, limit) | |
378 | object.send("#{attribute}=", value) |
|
378 | object.send("#{attribute}=", value) | |
379 | end |
|
379 | end | |
380 |
|
380 | |||
381 | # Returns a User from an email address and a full name |
|
381 | # Returns a User from an email address and a full name | |
382 | def self.new_user_from_attributes(email_address, fullname=nil) |
|
382 | def self.new_user_from_attributes(email_address, fullname=nil) | |
383 | user = User.new |
|
383 | user = User.new | |
384 |
|
384 | |||
385 | # Truncating the email address would result in an invalid format |
|
385 | # Truncating the email address would result in an invalid format | |
386 | user.mail = email_address |
|
386 | user.mail = email_address | |
387 | assign_string_attribute_with_limit(user, 'login', email_address, User::LOGIN_LENGTH_LIMIT) |
|
387 | assign_string_attribute_with_limit(user, 'login', email_address, User::LOGIN_LENGTH_LIMIT) | |
388 |
|
388 | |||
389 | names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split |
|
389 | names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split | |
390 | assign_string_attribute_with_limit(user, 'firstname', names.shift) |
|
390 | assign_string_attribute_with_limit(user, 'firstname', names.shift) | |
391 | assign_string_attribute_with_limit(user, 'lastname', names.join(' ')) |
|
391 | assign_string_attribute_with_limit(user, 'lastname', names.join(' ')) | |
392 | user.lastname = '-' if user.lastname.blank? |
|
392 | user.lastname = '-' if user.lastname.blank? | |
393 |
|
393 | |||
394 | password_length = [Setting.password_min_length.to_i, 10].max |
|
394 | password_length = [Setting.password_min_length.to_i, 10].max | |
395 | user.password = Redmine::Utils.random_hex(password_length / 2 + 1) |
|
395 | user.password = Redmine::Utils.random_hex(password_length / 2 + 1) | |
396 | user.language = Setting.default_language |
|
396 | user.language = Setting.default_language | |
397 |
|
397 | |||
398 | unless user.valid? |
|
398 | unless user.valid? | |
399 | user.login = "user#{Redmine::Utils.random_hex(6)}" unless user.errors[:login].blank? |
|
399 | user.login = "user#{Redmine::Utils.random_hex(6)}" unless user.errors[:login].blank? | |
400 | user.firstname = "-" unless user.errors[:firstname].blank? |
|
400 | user.firstname = "-" unless user.errors[:firstname].blank? | |
401 | user.lastname = "-" unless user.errors[:lastname].blank? |
|
401 | user.lastname = "-" unless user.errors[:lastname].blank? | |
402 | end |
|
402 | end | |
403 |
|
403 | |||
404 | user |
|
404 | user | |
405 | end |
|
405 | end | |
406 |
|
406 | |||
407 | # Creates a User for the +email+ sender |
|
407 | # Creates a User for the +email+ sender | |
408 | # Returns the user or nil if it could not be created |
|
408 | # Returns the user or nil if it could not be created | |
409 | def create_user_from_email |
|
409 | def create_user_from_email | |
410 | from = email.header['from'].to_s |
|
410 | from = email.header['from'].to_s | |
411 | addr, name = from, nil |
|
411 | addr, name = from, nil | |
412 | if m = from.match(/^"?(.+?)"?\s+<(.+@.+)>$/) |
|
412 | if m = from.match(/^"?(.+?)"?\s+<(.+@.+)>$/) | |
413 | addr, name = m[2], m[1] |
|
413 | addr, name = m[2], m[1] | |
414 | end |
|
414 | end | |
415 | if addr.present? |
|
415 | if addr.present? | |
416 | user = self.class.new_user_from_attributes(addr, name) |
|
416 | user = self.class.new_user_from_attributes(addr, name) | |
417 | if user.save |
|
417 | if user.save | |
418 | user |
|
418 | user | |
419 | else |
|
419 | else | |
420 | logger.error "MailHandler: failed to create User: #{user.errors.full_messages}" if logger |
|
420 | logger.error "MailHandler: failed to create User: #{user.errors.full_messages}" if logger | |
421 | nil |
|
421 | nil | |
422 | end |
|
422 | end | |
423 | else |
|
423 | else | |
424 | logger.error "MailHandler: failed to create User: no FROM address found" if logger |
|
424 | logger.error "MailHandler: failed to create User: no FROM address found" if logger | |
425 | nil |
|
425 | nil | |
426 | end |
|
426 | end | |
427 | end |
|
427 | end | |
428 |
|
428 | |||
429 | # Removes the email body of text after the truncation configurations. |
|
429 | # Removes the email body of text after the truncation configurations. | |
430 | def cleanup_body(body) |
|
430 | def cleanup_body(body) | |
431 | delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)} |
|
431 | delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)} | |
432 | unless delimiters.empty? |
|
432 | unless delimiters.empty? | |
433 | regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE) |
|
433 | regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE) | |
434 | body = body.gsub(regex, '') |
|
434 | body = body.gsub(regex, '') | |
435 | end |
|
435 | end | |
436 | body.strip |
|
436 | body.strip | |
437 | end |
|
437 | end | |
438 |
|
438 | |||
439 | def find_assignee_from_keyword(keyword, issue) |
|
439 | def find_assignee_from_keyword(keyword, issue) | |
440 | keyword = keyword.to_s.downcase |
|
440 | keyword = keyword.to_s.downcase | |
441 | assignable = issue.assignable_users |
|
441 | assignable = issue.assignable_users | |
442 | assignee = nil |
|
442 | assignee = nil | |
443 | assignee ||= assignable.detect {|a| |
|
443 | assignee ||= assignable.detect {|a| | |
444 | a.mail.to_s.downcase == keyword || |
|
444 | a.mail.to_s.downcase == keyword || | |
445 | a.login.to_s.downcase == keyword |
|
445 | a.login.to_s.downcase == keyword | |
446 | } |
|
446 | } | |
447 | if assignee.nil? && keyword.match(/ /) |
|
447 | if assignee.nil? && keyword.match(/ /) | |
448 | firstname, lastname = *(keyword.split) # "First Last Throwaway" |
|
448 | firstname, lastname = *(keyword.split) # "First Last Throwaway" | |
449 | assignee ||= assignable.detect {|a| |
|
449 | assignee ||= assignable.detect {|a| | |
450 | a.is_a?(User) && a.firstname.to_s.downcase == firstname && |
|
450 | a.is_a?(User) && a.firstname.to_s.downcase == firstname && | |
451 | a.lastname.to_s.downcase == lastname |
|
451 | a.lastname.to_s.downcase == lastname | |
452 | } |
|
452 | } | |
453 | end |
|
453 | end | |
454 | if assignee.nil? |
|
454 | if assignee.nil? | |
455 | assignee ||= assignable.detect {|a| a.is_a?(Group) && a.name.downcase == keyword} |
|
455 | assignee ||= assignable.detect {|a| a.is_a?(Group) && a.name.downcase == keyword} | |
456 | end |
|
456 | end | |
457 | assignee |
|
457 | assignee | |
458 | end |
|
458 | end | |
459 | end |
|
459 | end |
@@ -1,22 +1,22 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class MessageObserver < ActiveRecord::Observer |
|
18 | class MessageObserver < ActiveRecord::Observer | |
19 | def after_create(message) |
|
19 | def after_create(message) | |
20 |
Mailer. |
|
20 | Mailer.message_posted(message).deliver if Setting.notified_events.include?('message_posted') | |
21 | end |
|
21 | end | |
22 | end |
|
22 | end |
@@ -1,22 +1,22 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class NewsObserver < ActiveRecord::Observer |
|
18 | class NewsObserver < ActiveRecord::Observer | |
19 | def after_create(news) |
|
19 | def after_create(news) | |
20 |
Mailer. |
|
20 | Mailer.news_added(news).deliver if Setting.notified_events.include?('news_added') | |
21 | end |
|
21 | end | |
22 | end |
|
22 | end |
@@ -1,28 +1,28 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | class WikiContentObserver < ActiveRecord::Observer |
|
18 | class WikiContentObserver < ActiveRecord::Observer | |
19 | def after_create(wiki_content) |
|
19 | def after_create(wiki_content) | |
20 |
Mailer. |
|
20 | Mailer.wiki_content_added(wiki_content).deliver if Setting.notified_events.include?('wiki_content_added') | |
21 | end |
|
21 | end | |
22 |
|
22 | |||
23 | def after_update(wiki_content) |
|
23 | def after_update(wiki_content) | |
24 | if wiki_content.text_changed? |
|
24 | if wiki_content.text_changed? | |
25 |
Mailer. |
|
25 | Mailer.wiki_content_updated(wiki_content).deliver if Setting.notified_events.include?('wiki_content_updated') | |
26 | end |
|
26 | end | |
27 | end |
|
27 | end | |
28 | end |
|
28 | end |
@@ -1,188 +1,188 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | namespace :redmine do |
|
18 | namespace :redmine do | |
19 | namespace :email do |
|
19 | namespace :email do | |
20 |
|
20 | |||
21 | desc <<-END_DESC |
|
21 | desc <<-END_DESC | |
22 | Read an email from standard input. |
|
22 | Read an email from standard input. | |
23 |
|
23 | |||
24 | General options: |
|
24 | General options: | |
25 | unknown_user=ACTION how to handle emails from an unknown user |
|
25 | unknown_user=ACTION how to handle emails from an unknown user | |
26 | ACTION can be one of the following values: |
|
26 | ACTION can be one of the following values: | |
27 | ignore: email is ignored (default) |
|
27 | ignore: email is ignored (default) | |
28 | accept: accept as anonymous user |
|
28 | accept: accept as anonymous user | |
29 | create: create a user account |
|
29 | create: create a user account | |
30 | no_permission_check=1 disable permission checking when receiving |
|
30 | no_permission_check=1 disable permission checking when receiving | |
31 | the email |
|
31 | the email | |
32 |
|
32 | |||
33 | Issue attributes control options: |
|
33 | Issue attributes control options: | |
34 | project=PROJECT identifier of the target project |
|
34 | project=PROJECT identifier of the target project | |
35 | status=STATUS name of the target status |
|
35 | status=STATUS name of the target status | |
36 | tracker=TRACKER name of the target tracker |
|
36 | tracker=TRACKER name of the target tracker | |
37 | category=CATEGORY name of the target category |
|
37 | category=CATEGORY name of the target category | |
38 | priority=PRIORITY name of the target priority |
|
38 | priority=PRIORITY name of the target priority | |
39 | allow_override=ATTRS allow email content to override attributes |
|
39 | allow_override=ATTRS allow email content to override attributes | |
40 | specified by previous options |
|
40 | specified by previous options | |
41 | ATTRS is a comma separated list of attributes |
|
41 | ATTRS is a comma separated list of attributes | |
42 |
|
42 | |||
43 | Examples: |
|
43 | Examples: | |
44 | # No project specified. Emails MUST contain the 'Project' keyword: |
|
44 | # No project specified. Emails MUST contain the 'Project' keyword: | |
45 | rake redmine:email:read RAILS_ENV="production" < raw_email |
|
45 | rake redmine:email:read RAILS_ENV="production" < raw_email | |
46 |
|
46 | |||
47 | # Fixed project and default tracker specified, but emails can override |
|
47 | # Fixed project and default tracker specified, but emails can override | |
48 | # both tracker and priority attributes: |
|
48 | # both tracker and priority attributes: | |
49 | rake redmine:email:read RAILS_ENV="production" \\ |
|
49 | rake redmine:email:read RAILS_ENV="production" \\ | |
50 | project=foo \\ |
|
50 | project=foo \\ | |
51 | tracker=bug \\ |
|
51 | tracker=bug \\ | |
52 | allow_override=tracker,priority < raw_email |
|
52 | allow_override=tracker,priority < raw_email | |
53 | END_DESC |
|
53 | END_DESC | |
54 |
|
54 | |||
55 | task :read => :environment do |
|
55 | task :read => :environment do | |
56 | options = { :issue => {} } |
|
56 | options = { :issue => {} } | |
57 | %w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] } |
|
57 | %w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] } | |
58 | options[:allow_override] = ENV['allow_override'] if ENV['allow_override'] |
|
58 | options[:allow_override] = ENV['allow_override'] if ENV['allow_override'] | |
59 | options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user'] |
|
59 | options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user'] | |
60 | options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check'] |
|
60 | options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check'] | |
61 |
|
61 | |||
62 | MailHandler.receive(STDIN.read, options) |
|
62 | MailHandler.receive(STDIN.read, options) | |
63 | end |
|
63 | end | |
64 |
|
64 | |||
65 | desc <<-END_DESC |
|
65 | desc <<-END_DESC | |
66 | Read emails from an IMAP server. |
|
66 | Read emails from an IMAP server. | |
67 |
|
67 | |||
68 | General options: |
|
68 | General options: | |
69 | unknown_user=ACTION how to handle emails from an unknown user |
|
69 | unknown_user=ACTION how to handle emails from an unknown user | |
70 | ACTION can be one of the following values: |
|
70 | ACTION can be one of the following values: | |
71 | ignore: email is ignored (default) |
|
71 | ignore: email is ignored (default) | |
72 | accept: accept as anonymous user |
|
72 | accept: accept as anonymous user | |
73 | create: create a user account |
|
73 | create: create a user account | |
74 | no_permission_check=1 disable permission checking when receiving |
|
74 | no_permission_check=1 disable permission checking when receiving | |
75 | the email |
|
75 | the email | |
76 |
|
76 | |||
77 | Available IMAP options: |
|
77 | Available IMAP options: | |
78 | host=HOST IMAP server host (default: 127.0.0.1) |
|
78 | host=HOST IMAP server host (default: 127.0.0.1) | |
79 | port=PORT IMAP server port (default: 143) |
|
79 | port=PORT IMAP server port (default: 143) | |
80 | ssl=SSL Use SSL? (default: false) |
|
80 | ssl=SSL Use SSL? (default: false) | |
81 | username=USERNAME IMAP account |
|
81 | username=USERNAME IMAP account | |
82 | password=PASSWORD IMAP password |
|
82 | password=PASSWORD IMAP password | |
83 | folder=FOLDER IMAP folder to read (default: INBOX) |
|
83 | folder=FOLDER IMAP folder to read (default: INBOX) | |
84 |
|
84 | |||
85 | Issue attributes control options: |
|
85 | Issue attributes control options: | |
86 | project=PROJECT identifier of the target project |
|
86 | project=PROJECT identifier of the target project | |
87 | status=STATUS name of the target status |
|
87 | status=STATUS name of the target status | |
88 | tracker=TRACKER name of the target tracker |
|
88 | tracker=TRACKER name of the target tracker | |
89 | category=CATEGORY name of the target category |
|
89 | category=CATEGORY name of the target category | |
90 | priority=PRIORITY name of the target priority |
|
90 | priority=PRIORITY name of the target priority | |
91 | allow_override=ATTRS allow email content to override attributes |
|
91 | allow_override=ATTRS allow email content to override attributes | |
92 | specified by previous options |
|
92 | specified by previous options | |
93 | ATTRS is a comma separated list of attributes |
|
93 | ATTRS is a comma separated list of attributes | |
94 |
|
94 | |||
95 | Processed emails control options: |
|
95 | Processed emails control options: | |
96 | move_on_success=MAILBOX move emails that were successfully received |
|
96 | move_on_success=MAILBOX move emails that were successfully received | |
97 | to MAILBOX instead of deleting them |
|
97 | to MAILBOX instead of deleting them | |
98 | move_on_failure=MAILBOX move emails that were ignored to MAILBOX |
|
98 | move_on_failure=MAILBOX move emails that were ignored to MAILBOX | |
99 |
|
99 | |||
100 | Examples: |
|
100 | Examples: | |
101 | # No project specified. Emails MUST contain the 'Project' keyword: |
|
101 | # No project specified. Emails MUST contain the 'Project' keyword: | |
102 |
|
102 | |||
103 | rake redmine:email:receive_imap RAILS_ENV="production" \\ |
|
103 | rake redmine:email:receive_imap RAILS_ENV="production" \\ | |
104 | host=imap.foo.bar username=redmine@example.net password=xxx |
|
104 | host=imap.foo.bar username=redmine@example.net password=xxx | |
105 |
|
105 | |||
106 |
|
106 | |||
107 | # Fixed project and default tracker specified, but emails can override |
|
107 | # Fixed project and default tracker specified, but emails can override | |
108 | # both tracker and priority attributes: |
|
108 | # both tracker and priority attributes: | |
109 |
|
109 | |||
110 | rake redmine:email:receive_imap RAILS_ENV="production" \\ |
|
110 | rake redmine:email:receive_imap RAILS_ENV="production" \\ | |
111 | host=imap.foo.bar username=redmine@example.net password=xxx ssl=1 \\ |
|
111 | host=imap.foo.bar username=redmine@example.net password=xxx ssl=1 \\ | |
112 | project=foo \\ |
|
112 | project=foo \\ | |
113 | tracker=bug \\ |
|
113 | tracker=bug \\ | |
114 | allow_override=tracker,priority |
|
114 | allow_override=tracker,priority | |
115 | END_DESC |
|
115 | END_DESC | |
116 |
|
116 | |||
117 | task :receive_imap => :environment do |
|
117 | task :receive_imap => :environment do | |
118 | imap_options = {:host => ENV['host'], |
|
118 | imap_options = {:host => ENV['host'], | |
119 | :port => ENV['port'], |
|
119 | :port => ENV['port'], | |
120 | :ssl => ENV['ssl'], |
|
120 | :ssl => ENV['ssl'], | |
121 | :username => ENV['username'], |
|
121 | :username => ENV['username'], | |
122 | :password => ENV['password'], |
|
122 | :password => ENV['password'], | |
123 | :folder => ENV['folder'], |
|
123 | :folder => ENV['folder'], | |
124 | :move_on_success => ENV['move_on_success'], |
|
124 | :move_on_success => ENV['move_on_success'], | |
125 | :move_on_failure => ENV['move_on_failure']} |
|
125 | :move_on_failure => ENV['move_on_failure']} | |
126 |
|
126 | |||
127 | options = { :issue => {} } |
|
127 | options = { :issue => {} } | |
128 | %w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] } |
|
128 | %w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] } | |
129 | options[:allow_override] = ENV['allow_override'] if ENV['allow_override'] |
|
129 | options[:allow_override] = ENV['allow_override'] if ENV['allow_override'] | |
130 | options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user'] |
|
130 | options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user'] | |
131 | options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check'] |
|
131 | options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check'] | |
132 |
|
132 | |||
133 | Redmine::IMAP.check(imap_options, options) |
|
133 | Redmine::IMAP.check(imap_options, options) | |
134 | end |
|
134 | end | |
135 |
|
135 | |||
136 | desc <<-END_DESC |
|
136 | desc <<-END_DESC | |
137 | Read emails from an POP3 server. |
|
137 | Read emails from an POP3 server. | |
138 |
|
138 | |||
139 | Available POP3 options: |
|
139 | Available POP3 options: | |
140 | host=HOST POP3 server host (default: 127.0.0.1) |
|
140 | host=HOST POP3 server host (default: 127.0.0.1) | |
141 | port=PORT POP3 server port (default: 110) |
|
141 | port=PORT POP3 server port (default: 110) | |
142 | username=USERNAME POP3 account |
|
142 | username=USERNAME POP3 account | |
143 | password=PASSWORD POP3 password |
|
143 | password=PASSWORD POP3 password | |
144 | apop=1 use APOP authentication (default: false) |
|
144 | apop=1 use APOP authentication (default: false) | |
145 | delete_unprocessed=1 delete messages that could not be processed |
|
145 | delete_unprocessed=1 delete messages that could not be processed | |
146 | successfully from the server (default |
|
146 | successfully from the server (default | |
147 | behaviour is to leave them on the server) |
|
147 | behaviour is to leave them on the server) | |
148 |
|
148 | |||
149 | See redmine:email:receive_imap for more options and examples. |
|
149 | See redmine:email:receive_imap for more options and examples. | |
150 | END_DESC |
|
150 | END_DESC | |
151 |
|
151 | |||
152 | task :receive_pop3 => :environment do |
|
152 | task :receive_pop3 => :environment do | |
153 | pop_options = {:host => ENV['host'], |
|
153 | pop_options = {:host => ENV['host'], | |
154 | :port => ENV['port'], |
|
154 | :port => ENV['port'], | |
155 | :apop => ENV['apop'], |
|
155 | :apop => ENV['apop'], | |
156 | :username => ENV['username'], |
|
156 | :username => ENV['username'], | |
157 | :password => ENV['password'], |
|
157 | :password => ENV['password'], | |
158 | :delete_unprocessed => ENV['delete_unprocessed']} |
|
158 | :delete_unprocessed => ENV['delete_unprocessed']} | |
159 |
|
159 | |||
160 | options = { :issue => {} } |
|
160 | options = { :issue => {} } | |
161 | %w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] } |
|
161 | %w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] } | |
162 | options[:allow_override] = ENV['allow_override'] if ENV['allow_override'] |
|
162 | options[:allow_override] = ENV['allow_override'] if ENV['allow_override'] | |
163 | options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user'] |
|
163 | options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user'] | |
164 | options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check'] |
|
164 | options[:no_permission_check] = ENV['no_permission_check'] if ENV['no_permission_check'] | |
165 |
|
165 | |||
166 | Redmine::POP3.check(pop_options, options) |
|
166 | Redmine::POP3.check(pop_options, options) | |
167 | end |
|
167 | end | |
168 |
|
168 | |||
169 | desc "Send a test email to the user with the provided login name" |
|
169 | desc "Send a test email to the user with the provided login name" | |
170 | task :test, [:login] => :environment do |task, args| |
|
170 | task :test, [:login] => :environment do |task, args| | |
171 | include Redmine::I18n |
|
171 | include Redmine::I18n | |
172 | abort l(:notice_email_error, "Please include the user login to test with. Example: rake redmine:email:test[login]") if args[:login].blank? |
|
172 | abort l(:notice_email_error, "Please include the user login to test with. Example: rake redmine:email:test[login]") if args[:login].blank? | |
173 |
|
173 | |||
174 | user = User.find_by_login(args[:login]) |
|
174 | user = User.find_by_login(args[:login]) | |
175 | abort l(:notice_email_error, "User #{args[:login]} not found") unless user && user.logged? |
|
175 | abort l(:notice_email_error, "User #{args[:login]} not found") unless user && user.logged? | |
176 |
|
176 | |||
177 | ActionMailer::Base.raise_delivery_errors = true |
|
177 | ActionMailer::Base.raise_delivery_errors = true | |
178 | begin |
|
178 | begin | |
179 | Mailer.with_synched_deliveries do |
|
179 | Mailer.with_synched_deliveries do | |
180 |
Mailer. |
|
180 | Mailer.test_email(user).deliver | |
181 | end |
|
181 | end | |
182 | puts l(:notice_email_sent, user.mail) |
|
182 | puts l(:notice_email_sent, user.mail) | |
183 | rescue Exception => e |
|
183 | rescue Exception => e | |
184 | abort l(:notice_email_error, e.message) |
|
184 | abort l(:notice_email_error, e.message) | |
185 | end |
|
185 | end | |
186 | end |
|
186 | end | |
187 | end |
|
187 | end | |
188 | end |
|
188 | end |
@@ -1,172 +1,172 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | require File.expand_path('../../../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class Redmine::Hook::ManagerTest < ActionView::TestCase |
|
20 | class Redmine::Hook::ManagerTest < ActionView::TestCase | |
21 |
|
21 | |||
22 | fixtures :issues |
|
22 | fixtures :issues | |
23 |
|
23 | |||
24 | # Some hooks that are manually registered in these tests |
|
24 | # Some hooks that are manually registered in these tests | |
25 | class TestHook < Redmine::Hook::ViewListener; end |
|
25 | class TestHook < Redmine::Hook::ViewListener; end | |
26 |
|
26 | |||
27 | class TestHook1 < TestHook |
|
27 | class TestHook1 < TestHook | |
28 | def view_layouts_base_html_head(context) |
|
28 | def view_layouts_base_html_head(context) | |
29 | 'Test hook 1 listener.' |
|
29 | 'Test hook 1 listener.' | |
30 | end |
|
30 | end | |
31 | end |
|
31 | end | |
32 |
|
32 | |||
33 | class TestHook2 < TestHook |
|
33 | class TestHook2 < TestHook | |
34 | def view_layouts_base_html_head(context) |
|
34 | def view_layouts_base_html_head(context) | |
35 | 'Test hook 2 listener.' |
|
35 | 'Test hook 2 listener.' | |
36 | end |
|
36 | end | |
37 | end |
|
37 | end | |
38 |
|
38 | |||
39 | class TestHook3 < TestHook |
|
39 | class TestHook3 < TestHook | |
40 | def view_layouts_base_html_head(context) |
|
40 | def view_layouts_base_html_head(context) | |
41 | "Context keys: #{context.keys.collect(&:to_s).sort.join(', ')}." |
|
41 | "Context keys: #{context.keys.collect(&:to_s).sort.join(', ')}." | |
42 | end |
|
42 | end | |
43 | end |
|
43 | end | |
44 |
|
44 | |||
45 | class TestLinkToHook < TestHook |
|
45 | class TestLinkToHook < TestHook | |
46 | def view_layouts_base_html_head(context) |
|
46 | def view_layouts_base_html_head(context) | |
47 | link_to('Issues', :controller => 'issues') |
|
47 | link_to('Issues', :controller => 'issues') | |
48 | end |
|
48 | end | |
49 | end |
|
49 | end | |
50 |
|
50 | |||
51 | class TestHookHelperController < ActionController::Base |
|
51 | class TestHookHelperController < ActionController::Base | |
52 | include Redmine::Hook::Helper |
|
52 | include Redmine::Hook::Helper | |
53 | end |
|
53 | end | |
54 |
|
54 | |||
55 | class TestHookHelperView < ActionView::Base |
|
55 | class TestHookHelperView < ActionView::Base | |
56 | include Redmine::Hook::Helper |
|
56 | include Redmine::Hook::Helper | |
57 | end |
|
57 | end | |
58 |
|
58 | |||
59 | Redmine::Hook.clear_listeners |
|
59 | Redmine::Hook.clear_listeners | |
60 |
|
60 | |||
61 | def setup |
|
61 | def setup | |
62 | @hook_module = Redmine::Hook |
|
62 | @hook_module = Redmine::Hook | |
63 | end |
|
63 | end | |
64 |
|
64 | |||
65 | def teardown |
|
65 | def teardown | |
66 | @hook_module.clear_listeners |
|
66 | @hook_module.clear_listeners | |
67 | end |
|
67 | end | |
68 |
|
68 | |||
69 | def test_clear_listeners |
|
69 | def test_clear_listeners | |
70 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
70 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
71 | @hook_module.add_listener(TestHook1) |
|
71 | @hook_module.add_listener(TestHook1) | |
72 | @hook_module.add_listener(TestHook2) |
|
72 | @hook_module.add_listener(TestHook2) | |
73 | assert_equal 2, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
73 | assert_equal 2, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
74 |
|
74 | |||
75 | @hook_module.clear_listeners |
|
75 | @hook_module.clear_listeners | |
76 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
76 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
77 | end |
|
77 | end | |
78 |
|
78 | |||
79 | def test_add_listener |
|
79 | def test_add_listener | |
80 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
80 | assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
81 | @hook_module.add_listener(TestHook1) |
|
81 | @hook_module.add_listener(TestHook1) | |
82 | assert_equal 1, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
|
82 | assert_equal 1, @hook_module.hook_listeners(:view_layouts_base_html_head).size | |
83 | end |
|
83 | end | |
84 |
|
84 | |||
85 | def test_call_hook |
|
85 | def test_call_hook | |
86 | @hook_module.add_listener(TestHook1) |
|
86 | @hook_module.add_listener(TestHook1) | |
87 | assert_equal ['Test hook 1 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) |
|
87 | assert_equal ['Test hook 1 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) | |
88 | end |
|
88 | end | |
89 |
|
89 | |||
90 | def test_call_hook_with_context |
|
90 | def test_call_hook_with_context | |
91 | @hook_module.add_listener(TestHook3) |
|
91 | @hook_module.add_listener(TestHook3) | |
92 | assert_equal ['Context keys: bar, controller, foo, project, request.'], |
|
92 | assert_equal ['Context keys: bar, controller, foo, project, request.'], | |
93 | hook_helper.call_hook(:view_layouts_base_html_head, :foo => 1, :bar => 'a') |
|
93 | hook_helper.call_hook(:view_layouts_base_html_head, :foo => 1, :bar => 'a') | |
94 | end |
|
94 | end | |
95 |
|
95 | |||
96 | def test_call_hook_with_multiple_listeners |
|
96 | def test_call_hook_with_multiple_listeners | |
97 | @hook_module.add_listener(TestHook1) |
|
97 | @hook_module.add_listener(TestHook1) | |
98 | @hook_module.add_listener(TestHook2) |
|
98 | @hook_module.add_listener(TestHook2) | |
99 | assert_equal ['Test hook 1 listener.', 'Test hook 2 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) |
|
99 | assert_equal ['Test hook 1 listener.', 'Test hook 2 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) | |
100 | end |
|
100 | end | |
101 |
|
101 | |||
102 | # Context: Redmine::Hook::Helper.call_hook default_url |
|
102 | # Context: Redmine::Hook::Helper.call_hook default_url | |
103 | def test_call_hook_default_url_options |
|
103 | def test_call_hook_default_url_options | |
104 | @hook_module.add_listener(TestLinkToHook) |
|
104 | @hook_module.add_listener(TestLinkToHook) | |
105 |
|
105 | |||
106 | assert_equal ['<a href="/issues">Issues</a>'], hook_helper.call_hook(:view_layouts_base_html_head) |
|
106 | assert_equal ['<a href="/issues">Issues</a>'], hook_helper.call_hook(:view_layouts_base_html_head) | |
107 | end |
|
107 | end | |
108 |
|
108 | |||
109 | # Context: Redmine::Hook::Helper.call_hook |
|
109 | # Context: Redmine::Hook::Helper.call_hook | |
110 | def test_call_hook_with_project_added_to_context |
|
110 | def test_call_hook_with_project_added_to_context | |
111 | @hook_module.add_listener(TestHook3) |
|
111 | @hook_module.add_listener(TestHook3) | |
112 | assert_match /project/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] |
|
112 | assert_match /project/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] | |
113 | end |
|
113 | end | |
114 |
|
114 | |||
115 | def test_call_hook_from_controller_with_controller_added_to_context |
|
115 | def test_call_hook_from_controller_with_controller_added_to_context | |
116 | @hook_module.add_listener(TestHook3) |
|
116 | @hook_module.add_listener(TestHook3) | |
117 | assert_match /controller/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] |
|
117 | assert_match /controller/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] | |
118 | end |
|
118 | end | |
119 |
|
119 | |||
120 | def test_call_hook_from_controller_with_request_added_to_context |
|
120 | def test_call_hook_from_controller_with_request_added_to_context | |
121 | @hook_module.add_listener(TestHook3) |
|
121 | @hook_module.add_listener(TestHook3) | |
122 | assert_match /request/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] |
|
122 | assert_match /request/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] | |
123 | end |
|
123 | end | |
124 |
|
124 | |||
125 | def test_call_hook_from_view_with_project_added_to_context |
|
125 | def test_call_hook_from_view_with_project_added_to_context | |
126 | @hook_module.add_listener(TestHook3) |
|
126 | @hook_module.add_listener(TestHook3) | |
127 | assert_match /project/i, view_hook_helper.call_hook(:view_layouts_base_html_head) |
|
127 | assert_match /project/i, view_hook_helper.call_hook(:view_layouts_base_html_head) | |
128 | end |
|
128 | end | |
129 |
|
129 | |||
130 | def test_call_hook_from_view_with_controller_added_to_context |
|
130 | def test_call_hook_from_view_with_controller_added_to_context | |
131 | @hook_module.add_listener(TestHook3) |
|
131 | @hook_module.add_listener(TestHook3) | |
132 | assert_match /controller/i, view_hook_helper.call_hook(:view_layouts_base_html_head) |
|
132 | assert_match /controller/i, view_hook_helper.call_hook(:view_layouts_base_html_head) | |
133 | end |
|
133 | end | |
134 |
|
134 | |||
135 | def test_call_hook_from_view_with_request_added_to_context |
|
135 | def test_call_hook_from_view_with_request_added_to_context | |
136 | @hook_module.add_listener(TestHook3) |
|
136 | @hook_module.add_listener(TestHook3) | |
137 | assert_match /request/i, view_hook_helper.call_hook(:view_layouts_base_html_head) |
|
137 | assert_match /request/i, view_hook_helper.call_hook(:view_layouts_base_html_head) | |
138 | end |
|
138 | end | |
139 |
|
139 | |||
140 | def test_call_hook_from_view_should_join_responses_with_a_space |
|
140 | def test_call_hook_from_view_should_join_responses_with_a_space | |
141 | @hook_module.add_listener(TestHook1) |
|
141 | @hook_module.add_listener(TestHook1) | |
142 | @hook_module.add_listener(TestHook2) |
|
142 | @hook_module.add_listener(TestHook2) | |
143 | assert_equal 'Test hook 1 listener. Test hook 2 listener.', |
|
143 | assert_equal 'Test hook 1 listener. Test hook 2 listener.', | |
144 | view_hook_helper.call_hook(:view_layouts_base_html_head) |
|
144 | view_hook_helper.call_hook(:view_layouts_base_html_head) | |
145 | end |
|
145 | end | |
146 |
|
146 | |||
147 | def test_call_hook_should_not_change_the_default_url_for_email_notifications |
|
147 | def test_call_hook_should_not_change_the_default_url_for_email_notifications | |
148 | issue = Issue.find(1) |
|
148 | issue = Issue.find(1) | |
149 |
|
149 | |||
150 | ActionMailer::Base.deliveries.clear |
|
150 | ActionMailer::Base.deliveries.clear | |
151 |
Mailer. |
|
151 | Mailer.issue_add(issue).deliver | |
152 | mail = ActionMailer::Base.deliveries.last |
|
152 | mail = ActionMailer::Base.deliveries.last | |
153 |
|
153 | |||
154 | @hook_module.add_listener(TestLinkToHook) |
|
154 | @hook_module.add_listener(TestLinkToHook) | |
155 | hook_helper.call_hook(:view_layouts_base_html_head) |
|
155 | hook_helper.call_hook(:view_layouts_base_html_head) | |
156 |
|
156 | |||
157 | ActionMailer::Base.deliveries.clear |
|
157 | ActionMailer::Base.deliveries.clear | |
158 |
Mailer. |
|
158 | Mailer.issue_add(issue).deliver | |
159 | mail2 = ActionMailer::Base.deliveries.last |
|
159 | mail2 = ActionMailer::Base.deliveries.last | |
160 |
|
160 | |||
161 | assert_equal mail_body(mail), mail_body(mail2) |
|
161 | assert_equal mail_body(mail), mail_body(mail2) | |
162 | end |
|
162 | end | |
163 |
|
163 | |||
164 | def hook_helper |
|
164 | def hook_helper | |
165 | @hook_helper ||= TestHookHelperController.new |
|
165 | @hook_helper ||= TestHookHelperController.new | |
166 | end |
|
166 | end | |
167 |
|
167 | |||
168 | def view_hook_helper |
|
168 | def view_hook_helper | |
169 | @view_hook_helper ||= TestHookHelperView.new(Rails.root.to_s + '/app/views') |
|
169 | @view_hook_helper ||= TestHookHelperView.new(Rails.root.to_s + '/app/views') | |
170 | end |
|
170 | end | |
171 | end |
|
171 | end | |
172 |
|
172 |
@@ -1,540 +1,540 | |||||
1 | # Redmine - project management software |
|
1 | # Redmine - project management software | |
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2012 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
6 | # as published by the Free Software Foundation; either version 2 |
|
6 | # as published by the Free Software Foundation; either version 2 | |
7 | # of the License, or (at your option) any later version. |
|
7 | # of the License, or (at your option) any later version. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU General Public License |
|
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 |
|
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. |
|
16 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
17 |
|
17 | |||
18 | require File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class MailerTest < ActiveSupport::TestCase |
|
20 | class MailerTest < ActiveSupport::TestCase | |
21 | include Redmine::I18n |
|
21 | include Redmine::I18n | |
22 | include ActionDispatch::Assertions::SelectorAssertions |
|
22 | include ActionDispatch::Assertions::SelectorAssertions | |
23 | fixtures :projects, :enabled_modules, :issues, :users, :members, |
|
23 | fixtures :projects, :enabled_modules, :issues, :users, :members, | |
24 | :member_roles, :roles, :documents, :attachments, :news, |
|
24 | :member_roles, :roles, :documents, :attachments, :news, | |
25 | :tokens, :journals, :journal_details, :changesets, |
|
25 | :tokens, :journals, :journal_details, :changesets, | |
26 | :trackers, :projects_trackers, |
|
26 | :trackers, :projects_trackers, | |
27 | :issue_statuses, :enumerations, :messages, :boards, :repositories, |
|
27 | :issue_statuses, :enumerations, :messages, :boards, :repositories, | |
28 | :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, |
|
28 | :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions, | |
29 | :versions, |
|
29 | :versions, | |
30 | :comments |
|
30 | :comments | |
31 |
|
31 | |||
32 | def setup |
|
32 | def setup | |
33 | ActionMailer::Base.deliveries.clear |
|
33 | ActionMailer::Base.deliveries.clear | |
34 | Setting.host_name = 'mydomain.foo' |
|
34 | Setting.host_name = 'mydomain.foo' | |
35 | Setting.protocol = 'http' |
|
35 | Setting.protocol = 'http' | |
36 | Setting.plain_text_mail = '0' |
|
36 | Setting.plain_text_mail = '0' | |
37 | end |
|
37 | end | |
38 |
|
38 | |||
39 | def test_generated_links_in_emails |
|
39 | def test_generated_links_in_emails | |
40 | Setting.default_language = 'en' |
|
40 | Setting.default_language = 'en' | |
41 | Setting.host_name = 'mydomain.foo' |
|
41 | Setting.host_name = 'mydomain.foo' | |
42 | Setting.protocol = 'https' |
|
42 | Setting.protocol = 'https' | |
43 |
|
43 | |||
44 | journal = Journal.find(3) |
|
44 | journal = Journal.find(3) | |
45 |
assert Mailer. |
|
45 | assert Mailer.issue_edit(journal).deliver | |
46 |
|
46 | |||
47 | mail = last_email |
|
47 | mail = last_email | |
48 | assert_not_nil mail |
|
48 | assert_not_nil mail | |
49 |
|
49 | |||
50 | assert_select_email do |
|
50 | assert_select_email do | |
51 | # link to the main ticket |
|
51 | # link to the main ticket | |
52 | assert_select 'a[href=?]', |
|
52 | assert_select 'a[href=?]', | |
53 | 'https://mydomain.foo/issues/2#change-3', |
|
53 | 'https://mydomain.foo/issues/2#change-3', | |
54 | :text => 'Feature request #2: Add ingredients categories' |
|
54 | :text => 'Feature request #2: Add ingredients categories' | |
55 | # link to a referenced ticket |
|
55 | # link to a referenced ticket | |
56 | assert_select 'a[href=?][title=?]', |
|
56 | assert_select 'a[href=?][title=?]', | |
57 | 'https://mydomain.foo/issues/1', |
|
57 | 'https://mydomain.foo/issues/1', | |
58 | 'Can\'t print recipes (New)', |
|
58 | 'Can\'t print recipes (New)', | |
59 | :text => '#1' |
|
59 | :text => '#1' | |
60 | # link to a changeset |
|
60 | # link to a changeset | |
61 | assert_select 'a[href=?][title=?]', |
|
61 | assert_select 'a[href=?][title=?]', | |
62 | 'https://mydomain.foo/projects/ecookbook/repository/revisions/2', |
|
62 | 'https://mydomain.foo/projects/ecookbook/repository/revisions/2', | |
63 | 'This commit fixes #1, #2 and references #1 & #3', |
|
63 | 'This commit fixes #1, #2 and references #1 & #3', | |
64 | :text => 'r2' |
|
64 | :text => 'r2' | |
65 | # link to a description diff |
|
65 | # link to a description diff | |
66 | assert_select 'a[href=?][title=?]', |
|
66 | assert_select 'a[href=?][title=?]', | |
67 | 'https://mydomain.foo/journals/diff/3?detail_id=4', |
|
67 | 'https://mydomain.foo/journals/diff/3?detail_id=4', | |
68 | 'View differences', |
|
68 | 'View differences', | |
69 | :text => 'diff' |
|
69 | :text => 'diff' | |
70 | # link to an attachment |
|
70 | # link to an attachment | |
71 | assert_select 'a[href=?]', |
|
71 | assert_select 'a[href=?]', | |
72 | 'https://mydomain.foo/attachments/download/4/source.rb', |
|
72 | 'https://mydomain.foo/attachments/download/4/source.rb', | |
73 | :text => 'source.rb' |
|
73 | :text => 'source.rb' | |
74 | end |
|
74 | end | |
75 | end |
|
75 | end | |
76 |
|
76 | |||
77 | def test_generated_links_with_prefix |
|
77 | def test_generated_links_with_prefix | |
78 | Setting.default_language = 'en' |
|
78 | Setting.default_language = 'en' | |
79 | relative_url_root = Redmine::Utils.relative_url_root |
|
79 | relative_url_root = Redmine::Utils.relative_url_root | |
80 | Setting.host_name = 'mydomain.foo/rdm' |
|
80 | Setting.host_name = 'mydomain.foo/rdm' | |
81 | Setting.protocol = 'http' |
|
81 | Setting.protocol = 'http' | |
82 |
|
82 | |||
83 | journal = Journal.find(3) |
|
83 | journal = Journal.find(3) | |
84 |
assert Mailer. |
|
84 | assert Mailer.issue_edit(journal).deliver | |
85 |
|
85 | |||
86 | mail = last_email |
|
86 | mail = last_email | |
87 | assert_not_nil mail |
|
87 | assert_not_nil mail | |
88 |
|
88 | |||
89 | assert_select_email do |
|
89 | assert_select_email do | |
90 | # link to the main ticket |
|
90 | # link to the main ticket | |
91 | assert_select 'a[href=?]', |
|
91 | assert_select 'a[href=?]', | |
92 | 'http://mydomain.foo/rdm/issues/2#change-3', |
|
92 | 'http://mydomain.foo/rdm/issues/2#change-3', | |
93 | :text => 'Feature request #2: Add ingredients categories' |
|
93 | :text => 'Feature request #2: Add ingredients categories' | |
94 | # link to a referenced ticket |
|
94 | # link to a referenced ticket | |
95 | assert_select 'a[href=?][title=?]', |
|
95 | assert_select 'a[href=?][title=?]', | |
96 | 'http://mydomain.foo/rdm/issues/1', |
|
96 | 'http://mydomain.foo/rdm/issues/1', | |
97 | 'Can\'t print recipes (New)', |
|
97 | 'Can\'t print recipes (New)', | |
98 | :text => '#1' |
|
98 | :text => '#1' | |
99 | # link to a changeset |
|
99 | # link to a changeset | |
100 | assert_select 'a[href=?][title=?]', |
|
100 | assert_select 'a[href=?][title=?]', | |
101 | 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2', |
|
101 | 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2', | |
102 | 'This commit fixes #1, #2 and references #1 & #3', |
|
102 | 'This commit fixes #1, #2 and references #1 & #3', | |
103 | :text => 'r2' |
|
103 | :text => 'r2' | |
104 | # link to a description diff |
|
104 | # link to a description diff | |
105 | assert_select 'a[href=?][title=?]', |
|
105 | assert_select 'a[href=?][title=?]', | |
106 | 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4', |
|
106 | 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4', | |
107 | 'View differences', |
|
107 | 'View differences', | |
108 | :text => 'diff' |
|
108 | :text => 'diff' | |
109 | # link to an attachment |
|
109 | # link to an attachment | |
110 | assert_select 'a[href=?]', |
|
110 | assert_select 'a[href=?]', | |
111 | 'http://mydomain.foo/rdm/attachments/download/4/source.rb', |
|
111 | 'http://mydomain.foo/rdm/attachments/download/4/source.rb', | |
112 | :text => 'source.rb' |
|
112 | :text => 'source.rb' | |
113 | end |
|
113 | end | |
114 | end |
|
114 | end | |
115 |
|
115 | |||
116 | def test_generated_links_with_prefix_and_no_relative_url_root |
|
116 | def test_generated_links_with_prefix_and_no_relative_url_root | |
117 | Setting.default_language = 'en' |
|
117 | Setting.default_language = 'en' | |
118 | relative_url_root = Redmine::Utils.relative_url_root |
|
118 | relative_url_root = Redmine::Utils.relative_url_root | |
119 | Setting.host_name = 'mydomain.foo/rdm' |
|
119 | Setting.host_name = 'mydomain.foo/rdm' | |
120 | Setting.protocol = 'http' |
|
120 | Setting.protocol = 'http' | |
121 | Redmine::Utils.relative_url_root = nil |
|
121 | Redmine::Utils.relative_url_root = nil | |
122 |
|
122 | |||
123 | journal = Journal.find(3) |
|
123 | journal = Journal.find(3) | |
124 |
assert Mailer. |
|
124 | assert Mailer.issue_edit(journal).deliver | |
125 |
|
125 | |||
126 | mail = last_email |
|
126 | mail = last_email | |
127 | assert_not_nil mail |
|
127 | assert_not_nil mail | |
128 |
|
128 | |||
129 | assert_select_email do |
|
129 | assert_select_email do | |
130 | # link to the main ticket |
|
130 | # link to the main ticket | |
131 | assert_select 'a[href=?]', |
|
131 | assert_select 'a[href=?]', | |
132 | 'http://mydomain.foo/rdm/issues/2#change-3', |
|
132 | 'http://mydomain.foo/rdm/issues/2#change-3', | |
133 | :text => 'Feature request #2: Add ingredients categories' |
|
133 | :text => 'Feature request #2: Add ingredients categories' | |
134 | # link to a referenced ticket |
|
134 | # link to a referenced ticket | |
135 | assert_select 'a[href=?][title=?]', |
|
135 | assert_select 'a[href=?][title=?]', | |
136 | 'http://mydomain.foo/rdm/issues/1', |
|
136 | 'http://mydomain.foo/rdm/issues/1', | |
137 | 'Can\'t print recipes (New)', |
|
137 | 'Can\'t print recipes (New)', | |
138 | :text => '#1' |
|
138 | :text => '#1' | |
139 | # link to a changeset |
|
139 | # link to a changeset | |
140 | assert_select 'a[href=?][title=?]', |
|
140 | assert_select 'a[href=?][title=?]', | |
141 | 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2', |
|
141 | 'http://mydomain.foo/rdm/projects/ecookbook/repository/revisions/2', | |
142 | 'This commit fixes #1, #2 and references #1 & #3', |
|
142 | 'This commit fixes #1, #2 and references #1 & #3', | |
143 | :text => 'r2' |
|
143 | :text => 'r2' | |
144 | # link to a description diff |
|
144 | # link to a description diff | |
145 | assert_select 'a[href=?][title=?]', |
|
145 | assert_select 'a[href=?][title=?]', | |
146 | 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4', |
|
146 | 'http://mydomain.foo/rdm/journals/diff/3?detail_id=4', | |
147 | 'View differences', |
|
147 | 'View differences', | |
148 | :text => 'diff' |
|
148 | :text => 'diff' | |
149 | # link to an attachment |
|
149 | # link to an attachment | |
150 | assert_select 'a[href=?]', |
|
150 | assert_select 'a[href=?]', | |
151 | 'http://mydomain.foo/rdm/attachments/download/4/source.rb', |
|
151 | 'http://mydomain.foo/rdm/attachments/download/4/source.rb', | |
152 | :text => 'source.rb' |
|
152 | :text => 'source.rb' | |
153 | end |
|
153 | end | |
154 | ensure |
|
154 | ensure | |
155 | # restore it |
|
155 | # restore it | |
156 | Redmine::Utils.relative_url_root = relative_url_root |
|
156 | Redmine::Utils.relative_url_root = relative_url_root | |
157 | end |
|
157 | end | |
158 |
|
158 | |||
159 | def test_email_headers |
|
159 | def test_email_headers | |
160 | issue = Issue.find(1) |
|
160 | issue = Issue.find(1) | |
161 |
Mailer. |
|
161 | Mailer.issue_add(issue).deliver | |
162 | mail = last_email |
|
162 | mail = last_email | |
163 | assert_not_nil mail |
|
163 | assert_not_nil mail | |
164 | assert_equal 'OOF', mail.header['X-Auto-Response-Suppress'].to_s |
|
164 | assert_equal 'OOF', mail.header['X-Auto-Response-Suppress'].to_s | |
165 | assert_equal 'auto-generated', mail.header['Auto-Submitted'].to_s |
|
165 | assert_equal 'auto-generated', mail.header['Auto-Submitted'].to_s | |
166 | end |
|
166 | end | |
167 |
|
167 | |||
168 | def test_email_headers_should_include_sender |
|
168 | def test_email_headers_should_include_sender | |
169 | issue = Issue.find(1) |
|
169 | issue = Issue.find(1) | |
170 |
Mailer. |
|
170 | Mailer.issue_add(issue).deliver | |
171 | mail = last_email |
|
171 | mail = last_email | |
172 | assert_equal issue.author.login, mail.header['X-Redmine-Sender'].to_s |
|
172 | assert_equal issue.author.login, mail.header['X-Redmine-Sender'].to_s | |
173 | end |
|
173 | end | |
174 |
|
174 | |||
175 | def test_plain_text_mail |
|
175 | def test_plain_text_mail | |
176 | Setting.plain_text_mail = 1 |
|
176 | Setting.plain_text_mail = 1 | |
177 | journal = Journal.find(2) |
|
177 | journal = Journal.find(2) | |
178 |
Mailer. |
|
178 | Mailer.issue_edit(journal).deliver | |
179 | mail = last_email |
|
179 | mail = last_email | |
180 | assert_equal "text/plain; charset=UTF-8", mail.content_type |
|
180 | assert_equal "text/plain; charset=UTF-8", mail.content_type | |
181 | assert_equal 0, mail.parts.size |
|
181 | assert_equal 0, mail.parts.size | |
182 | assert !mail.encoded.include?('href') |
|
182 | assert !mail.encoded.include?('href') | |
183 | end |
|
183 | end | |
184 |
|
184 | |||
185 | def test_html_mail |
|
185 | def test_html_mail | |
186 | Setting.plain_text_mail = 0 |
|
186 | Setting.plain_text_mail = 0 | |
187 | journal = Journal.find(2) |
|
187 | journal = Journal.find(2) | |
188 |
Mailer. |
|
188 | Mailer.issue_edit(journal).deliver | |
189 | mail = last_email |
|
189 | mail = last_email | |
190 | assert_equal 2, mail.parts.size |
|
190 | assert_equal 2, mail.parts.size | |
191 | assert mail.encoded.include?('href') |
|
191 | assert mail.encoded.include?('href') | |
192 | end |
|
192 | end | |
193 |
|
193 | |||
194 | def test_from_header |
|
194 | def test_from_header | |
195 | with_settings :mail_from => 'redmine@example.net' do |
|
195 | with_settings :mail_from => 'redmine@example.net' do | |
196 |
Mailer. |
|
196 | Mailer.test_email(User.find(1)).deliver | |
197 | end |
|
197 | end | |
198 | mail = last_email |
|
198 | mail = last_email | |
199 | assert_equal 'redmine@example.net', mail.from_addrs.first |
|
199 | assert_equal 'redmine@example.net', mail.from_addrs.first | |
200 | end |
|
200 | end | |
201 |
|
201 | |||
202 | def test_from_header_with_phrase |
|
202 | def test_from_header_with_phrase | |
203 | with_settings :mail_from => 'Redmine app <redmine@example.net>' do |
|
203 | with_settings :mail_from => 'Redmine app <redmine@example.net>' do | |
204 |
Mailer. |
|
204 | Mailer.test_email(User.find(1)).deliver | |
205 | end |
|
205 | end | |
206 | mail = last_email |
|
206 | mail = last_email | |
207 | assert_equal 'redmine@example.net', mail.from_addrs.first |
|
207 | assert_equal 'redmine@example.net', mail.from_addrs.first | |
208 | assert_equal 'Redmine app <redmine@example.net>', mail.header['From'].to_s |
|
208 | assert_equal 'Redmine app <redmine@example.net>', mail.header['From'].to_s | |
209 | end |
|
209 | end | |
210 |
|
210 | |||
211 | def test_should_not_send_email_without_recipient |
|
211 | def test_should_not_send_email_without_recipient | |
212 | news = News.find(:first) |
|
212 | news = News.find(:first) | |
213 | user = news.author |
|
213 | user = news.author | |
214 | # Remove members except news author |
|
214 | # Remove members except news author | |
215 | news.project.memberships.each {|m| m.destroy unless m.user == user} |
|
215 | news.project.memberships.each {|m| m.destroy unless m.user == user} | |
216 |
|
216 | |||
217 | user.pref[:no_self_notified] = false |
|
217 | user.pref[:no_self_notified] = false | |
218 | user.pref.save |
|
218 | user.pref.save | |
219 | User.current = user |
|
219 | User.current = user | |
220 |
Mailer. |
|
220 | Mailer.news_added(news.reload).deliver | |
221 | assert_equal 1, last_email.bcc.size |
|
221 | assert_equal 1, last_email.bcc.size | |
222 |
|
222 | |||
223 | # nobody to notify |
|
223 | # nobody to notify | |
224 | user.pref[:no_self_notified] = true |
|
224 | user.pref[:no_self_notified] = true | |
225 | user.pref.save |
|
225 | user.pref.save | |
226 | User.current = user |
|
226 | User.current = user | |
227 | ActionMailer::Base.deliveries.clear |
|
227 | ActionMailer::Base.deliveries.clear | |
228 |
Mailer. |
|
228 | Mailer.news_added(news.reload).deliver | |
229 | assert ActionMailer::Base.deliveries.empty? |
|
229 | assert ActionMailer::Base.deliveries.empty? | |
230 | end |
|
230 | end | |
231 |
|
231 | |||
232 | def test_issue_add_message_id |
|
232 | def test_issue_add_message_id | |
233 | issue = Issue.find(1) |
|
233 | issue = Issue.find(1) | |
234 |
Mailer. |
|
234 | Mailer.issue_add(issue).deliver | |
235 | mail = last_email |
|
235 | mail = last_email | |
236 | assert_equal Mailer.message_id_for(issue), mail.message_id |
|
236 | assert_equal Mailer.message_id_for(issue), mail.message_id | |
237 | assert_nil mail.references |
|
237 | assert_nil mail.references | |
238 | end |
|
238 | end | |
239 |
|
239 | |||
240 | def test_issue_edit_message_id |
|
240 | def test_issue_edit_message_id | |
241 | journal = Journal.find(1) |
|
241 | journal = Journal.find(1) | |
242 |
Mailer. |
|
242 | Mailer.issue_edit(journal).deliver | |
243 | mail = last_email |
|
243 | mail = last_email | |
244 | assert_equal Mailer.message_id_for(journal), mail.message_id |
|
244 | assert_equal Mailer.message_id_for(journal), mail.message_id | |
245 | assert_include Mailer.message_id_for(journal.issue), mail.references |
|
245 | assert_include Mailer.message_id_for(journal.issue), mail.references | |
246 | assert_select_email do |
|
246 | assert_select_email do | |
247 | # link to the update |
|
247 | # link to the update | |
248 | assert_select "a[href=?]", |
|
248 | assert_select "a[href=?]", | |
249 | "http://mydomain.foo/issues/#{journal.journalized_id}#change-#{journal.id}" |
|
249 | "http://mydomain.foo/issues/#{journal.journalized_id}#change-#{journal.id}" | |
250 | end |
|
250 | end | |
251 | end |
|
251 | end | |
252 |
|
252 | |||
253 | def test_message_posted_message_id |
|
253 | def test_message_posted_message_id | |
254 | message = Message.find(1) |
|
254 | message = Message.find(1) | |
255 |
Mailer. |
|
255 | Mailer.message_posted(message).deliver | |
256 | mail = last_email |
|
256 | mail = last_email | |
257 | assert_equal Mailer.message_id_for(message), mail.message_id |
|
257 | assert_equal Mailer.message_id_for(message), mail.message_id | |
258 | assert_nil mail.references |
|
258 | assert_nil mail.references | |
259 | assert_select_email do |
|
259 | assert_select_email do | |
260 | # link to the message |
|
260 | # link to the message | |
261 | assert_select "a[href=?]", |
|
261 | assert_select "a[href=?]", | |
262 | "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}", |
|
262 | "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.id}", | |
263 | :text => message.subject |
|
263 | :text => message.subject | |
264 | end |
|
264 | end | |
265 | end |
|
265 | end | |
266 |
|
266 | |||
267 | def test_reply_posted_message_id |
|
267 | def test_reply_posted_message_id | |
268 | message = Message.find(3) |
|
268 | message = Message.find(3) | |
269 |
Mailer. |
|
269 | Mailer.message_posted(message).deliver | |
270 | mail = last_email |
|
270 | mail = last_email | |
271 | assert_equal Mailer.message_id_for(message), mail.message_id |
|
271 | assert_equal Mailer.message_id_for(message), mail.message_id | |
272 | assert_include Mailer.message_id_for(message.parent), mail.references |
|
272 | assert_include Mailer.message_id_for(message.parent), mail.references | |
273 | assert_select_email do |
|
273 | assert_select_email do | |
274 | # link to the reply |
|
274 | # link to the reply | |
275 | assert_select "a[href=?]", |
|
275 | assert_select "a[href=?]", | |
276 | "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}", |
|
276 | "http://mydomain.foo/boards/#{message.board.id}/topics/#{message.root.id}?r=#{message.id}#message-#{message.id}", | |
277 | :text => message.subject |
|
277 | :text => message.subject | |
278 | end |
|
278 | end | |
279 | end |
|
279 | end | |
280 |
|
280 | |||
281 | context("#issue_add") do |
|
281 | context("#issue_add") do | |
282 | setup do |
|
282 | setup do | |
283 | ActionMailer::Base.deliveries.clear |
|
283 | ActionMailer::Base.deliveries.clear | |
284 | Setting.bcc_recipients = '1' |
|
284 | Setting.bcc_recipients = '1' | |
285 | @issue = Issue.find(1) |
|
285 | @issue = Issue.find(1) | |
286 | end |
|
286 | end | |
287 |
|
287 | |||
288 | should "notify project members" do |
|
288 | should "notify project members" do | |
289 |
assert Mailer. |
|
289 | assert Mailer.issue_add(@issue).deliver | |
290 | assert last_email.bcc.include?('dlopper@somenet.foo') |
|
290 | assert last_email.bcc.include?('dlopper@somenet.foo') | |
291 | end |
|
291 | end | |
292 |
|
292 | |||
293 | should "not notify project members that are not allow to view the issue" do |
|
293 | should "not notify project members that are not allow to view the issue" do | |
294 | Role.find(2).remove_permission!(:view_issues) |
|
294 | Role.find(2).remove_permission!(:view_issues) | |
295 |
assert Mailer. |
|
295 | assert Mailer.issue_add(@issue).deliver | |
296 | assert !last_email.bcc.include?('dlopper@somenet.foo') |
|
296 | assert !last_email.bcc.include?('dlopper@somenet.foo') | |
297 | end |
|
297 | end | |
298 |
|
298 | |||
299 | should "notify issue watchers" do |
|
299 | should "notify issue watchers" do | |
300 | user = User.find(9) |
|
300 | user = User.find(9) | |
301 | # minimal email notification options |
|
301 | # minimal email notification options | |
302 | user.pref[:no_self_notified] = '1' |
|
302 | user.pref[:no_self_notified] = '1' | |
303 | user.pref.save |
|
303 | user.pref.save | |
304 | user.mail_notification = false |
|
304 | user.mail_notification = false | |
305 | user.save |
|
305 | user.save | |
306 |
|
306 | |||
307 | Watcher.create!(:watchable => @issue, :user => user) |
|
307 | Watcher.create!(:watchable => @issue, :user => user) | |
308 |
assert Mailer. |
|
308 | assert Mailer.issue_add(@issue).deliver | |
309 | assert last_email.bcc.include?(user.mail) |
|
309 | assert last_email.bcc.include?(user.mail) | |
310 | end |
|
310 | end | |
311 |
|
311 | |||
312 | should "not notify watchers not allowed to view the issue" do |
|
312 | should "not notify watchers not allowed to view the issue" do | |
313 | user = User.find(9) |
|
313 | user = User.find(9) | |
314 | Watcher.create!(:watchable => @issue, :user => user) |
|
314 | Watcher.create!(:watchable => @issue, :user => user) | |
315 | Role.non_member.remove_permission!(:view_issues) |
|
315 | Role.non_member.remove_permission!(:view_issues) | |
316 |
assert Mailer. |
|
316 | assert Mailer.issue_add(@issue).deliver | |
317 | assert !last_email.bcc.include?(user.mail) |
|
317 | assert !last_email.bcc.include?(user.mail) | |
318 | end |
|
318 | end | |
319 | end |
|
319 | end | |
320 |
|
320 | |||
321 | # test mailer methods for each language |
|
321 | # test mailer methods for each language | |
322 | def test_issue_add |
|
322 | def test_issue_add | |
323 | issue = Issue.find(1) |
|
323 | issue = Issue.find(1) | |
324 | valid_languages.each do |lang| |
|
324 | valid_languages.each do |lang| | |
325 | Setting.default_language = lang.to_s |
|
325 | Setting.default_language = lang.to_s | |
326 |
assert Mailer. |
|
326 | assert Mailer.issue_add(issue).deliver | |
327 | end |
|
327 | end | |
328 | end |
|
328 | end | |
329 |
|
329 | |||
330 | def test_issue_edit |
|
330 | def test_issue_edit | |
331 | journal = Journal.find(1) |
|
331 | journal = Journal.find(1) | |
332 | valid_languages.each do |lang| |
|
332 | valid_languages.each do |lang| | |
333 | Setting.default_language = lang.to_s |
|
333 | Setting.default_language = lang.to_s | |
334 |
assert Mailer. |
|
334 | assert Mailer.issue_edit(journal).deliver | |
335 | end |
|
335 | end | |
336 | end |
|
336 | end | |
337 |
|
337 | |||
338 | def test_document_added |
|
338 | def test_document_added | |
339 | document = Document.find(1) |
|
339 | document = Document.find(1) | |
340 | valid_languages.each do |lang| |
|
340 | valid_languages.each do |lang| | |
341 | Setting.default_language = lang.to_s |
|
341 | Setting.default_language = lang.to_s | |
342 |
assert Mailer. |
|
342 | assert Mailer.document_added(document).deliver | |
343 | end |
|
343 | end | |
344 | end |
|
344 | end | |
345 |
|
345 | |||
346 | def test_attachments_added |
|
346 | def test_attachments_added | |
347 | attachements = [ Attachment.find_by_container_type('Document') ] |
|
347 | attachements = [ Attachment.find_by_container_type('Document') ] | |
348 | valid_languages.each do |lang| |
|
348 | valid_languages.each do |lang| | |
349 | Setting.default_language = lang.to_s |
|
349 | Setting.default_language = lang.to_s | |
350 |
assert Mailer. |
|
350 | assert Mailer.attachments_added(attachements).deliver | |
351 | end |
|
351 | end | |
352 | end |
|
352 | end | |
353 |
|
353 | |||
354 | def test_version_file_added |
|
354 | def test_version_file_added | |
355 | attachements = [ Attachment.find_by_container_type('Version') ] |
|
355 | attachements = [ Attachment.find_by_container_type('Version') ] | |
356 |
assert Mailer. |
|
356 | assert Mailer.attachments_added(attachements).deliver | |
357 | assert_not_nil last_email.bcc |
|
357 | assert_not_nil last_email.bcc | |
358 | assert last_email.bcc.any? |
|
358 | assert last_email.bcc.any? | |
359 | assert_select_email do |
|
359 | assert_select_email do | |
360 | assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files" |
|
360 | assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files" | |
361 | end |
|
361 | end | |
362 | end |
|
362 | end | |
363 |
|
363 | |||
364 | def test_project_file_added |
|
364 | def test_project_file_added | |
365 | attachements = [ Attachment.find_by_container_type('Project') ] |
|
365 | attachements = [ Attachment.find_by_container_type('Project') ] | |
366 |
assert Mailer. |
|
366 | assert Mailer.attachments_added(attachements).deliver | |
367 | assert_not_nil last_email.bcc |
|
367 | assert_not_nil last_email.bcc | |
368 | assert last_email.bcc.any? |
|
368 | assert last_email.bcc.any? | |
369 | assert_select_email do |
|
369 | assert_select_email do | |
370 | assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files" |
|
370 | assert_select "a[href=?]", "http://mydomain.foo/projects/ecookbook/files" | |
371 | end |
|
371 | end | |
372 | end |
|
372 | end | |
373 |
|
373 | |||
374 | def test_news_added |
|
374 | def test_news_added | |
375 | news = News.find(:first) |
|
375 | news = News.find(:first) | |
376 | valid_languages.each do |lang| |
|
376 | valid_languages.each do |lang| | |
377 | Setting.default_language = lang.to_s |
|
377 | Setting.default_language = lang.to_s | |
378 |
assert Mailer. |
|
378 | assert Mailer.news_added(news).deliver | |
379 | end |
|
379 | end | |
380 | end |
|
380 | end | |
381 |
|
381 | |||
382 | def test_news_comment_added |
|
382 | def test_news_comment_added | |
383 | comment = Comment.find(2) |
|
383 | comment = Comment.find(2) | |
384 | valid_languages.each do |lang| |
|
384 | valid_languages.each do |lang| | |
385 | Setting.default_language = lang.to_s |
|
385 | Setting.default_language = lang.to_s | |
386 |
assert Mailer. |
|
386 | assert Mailer.news_comment_added(comment).deliver | |
387 | end |
|
387 | end | |
388 | end |
|
388 | end | |
389 |
|
389 | |||
390 | def test_message_posted |
|
390 | def test_message_posted | |
391 | message = Message.find(:first) |
|
391 | message = Message.find(:first) | |
392 | recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author} |
|
392 | recipients = ([message.root] + message.root.children).collect {|m| m.author.mail if m.author} | |
393 | recipients = recipients.compact.uniq |
|
393 | recipients = recipients.compact.uniq | |
394 | valid_languages.each do |lang| |
|
394 | valid_languages.each do |lang| | |
395 | Setting.default_language = lang.to_s |
|
395 | Setting.default_language = lang.to_s | |
396 |
assert Mailer. |
|
396 | assert Mailer.message_posted(message).deliver | |
397 | end |
|
397 | end | |
398 | end |
|
398 | end | |
399 |
|
399 | |||
400 | def test_wiki_content_added |
|
400 | def test_wiki_content_added | |
401 | content = WikiContent.find(1) |
|
401 | content = WikiContent.find(1) | |
402 | valid_languages.each do |lang| |
|
402 | valid_languages.each do |lang| | |
403 | Setting.default_language = lang.to_s |
|
403 | Setting.default_language = lang.to_s | |
404 | assert_difference 'ActionMailer::Base.deliveries.size' do |
|
404 | assert_difference 'ActionMailer::Base.deliveries.size' do | |
405 |
assert Mailer. |
|
405 | assert Mailer.wiki_content_added(content).deliver | |
406 | end |
|
406 | end | |
407 | end |
|
407 | end | |
408 | end |
|
408 | end | |
409 |
|
409 | |||
410 | def test_wiki_content_updated |
|
410 | def test_wiki_content_updated | |
411 | content = WikiContent.find(1) |
|
411 | content = WikiContent.find(1) | |
412 | valid_languages.each do |lang| |
|
412 | valid_languages.each do |lang| | |
413 | Setting.default_language = lang.to_s |
|
413 | Setting.default_language = lang.to_s | |
414 | assert_difference 'ActionMailer::Base.deliveries.size' do |
|
414 | assert_difference 'ActionMailer::Base.deliveries.size' do | |
415 |
assert Mailer. |
|
415 | assert Mailer.wiki_content_updated(content).deliver | |
416 | end |
|
416 | end | |
417 | end |
|
417 | end | |
418 | end |
|
418 | end | |
419 |
|
419 | |||
420 | def test_account_information |
|
420 | def test_account_information | |
421 | user = User.find(2) |
|
421 | user = User.find(2) | |
422 | valid_languages.each do |lang| |
|
422 | valid_languages.each do |lang| | |
423 | user.update_attribute :language, lang.to_s |
|
423 | user.update_attribute :language, lang.to_s | |
424 | user.reload |
|
424 | user.reload | |
425 |
assert Mailer. |
|
425 | assert Mailer.account_information(user, 'pAsswORd').deliver | |
426 | end |
|
426 | end | |
427 | end |
|
427 | end | |
428 |
|
428 | |||
429 | def test_lost_password |
|
429 | def test_lost_password | |
430 | token = Token.find(2) |
|
430 | token = Token.find(2) | |
431 | valid_languages.each do |lang| |
|
431 | valid_languages.each do |lang| | |
432 | token.user.update_attribute :language, lang.to_s |
|
432 | token.user.update_attribute :language, lang.to_s | |
433 | token.reload |
|
433 | token.reload | |
434 |
assert Mailer. |
|
434 | assert Mailer.lost_password(token).deliver | |
435 | end |
|
435 | end | |
436 | end |
|
436 | end | |
437 |
|
437 | |||
438 | def test_register |
|
438 | def test_register | |
439 | token = Token.find(1) |
|
439 | token = Token.find(1) | |
440 | Setting.host_name = 'redmine.foo' |
|
440 | Setting.host_name = 'redmine.foo' | |
441 | Setting.protocol = 'https' |
|
441 | Setting.protocol = 'https' | |
442 |
|
442 | |||
443 | valid_languages.each do |lang| |
|
443 | valid_languages.each do |lang| | |
444 | token.user.update_attribute :language, lang.to_s |
|
444 | token.user.update_attribute :language, lang.to_s | |
445 | token.reload |
|
445 | token.reload | |
446 | ActionMailer::Base.deliveries.clear |
|
446 | ActionMailer::Base.deliveries.clear | |
447 |
assert Mailer. |
|
447 | assert Mailer.register(token).deliver | |
448 | mail = last_email |
|
448 | mail = last_email | |
449 | assert_select_email do |
|
449 | assert_select_email do | |
450 | assert_select "a[href=?]", |
|
450 | assert_select "a[href=?]", | |
451 | "https://redmine.foo/account/activate?token=#{token.value}", |
|
451 | "https://redmine.foo/account/activate?token=#{token.value}", | |
452 | :text => "https://redmine.foo/account/activate?token=#{token.value}" |
|
452 | :text => "https://redmine.foo/account/activate?token=#{token.value}" | |
453 | end |
|
453 | end | |
454 | end |
|
454 | end | |
455 | end |
|
455 | end | |
456 |
|
456 | |||
457 | def test_test |
|
457 | def test_test | |
458 | user = User.find(1) |
|
458 | user = User.find(1) | |
459 | valid_languages.each do |lang| |
|
459 | valid_languages.each do |lang| | |
460 | user.update_attribute :language, lang.to_s |
|
460 | user.update_attribute :language, lang.to_s | |
461 |
assert Mailer. |
|
461 | assert Mailer.test_email(user).deliver | |
462 | end |
|
462 | end | |
463 | end |
|
463 | end | |
464 |
|
464 | |||
465 | def test_reminders |
|
465 | def test_reminders | |
466 | Mailer.reminders(:days => 42) |
|
466 | Mailer.reminders(:days => 42) | |
467 | assert_equal 1, ActionMailer::Base.deliveries.size |
|
467 | assert_equal 1, ActionMailer::Base.deliveries.size | |
468 | mail = last_email |
|
468 | mail = last_email | |
469 | assert mail.bcc.include?('dlopper@somenet.foo') |
|
469 | assert mail.bcc.include?('dlopper@somenet.foo') | |
470 | assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail |
|
470 | assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail | |
471 | assert_equal '1 issue(s) due in the next 42 days', mail.subject |
|
471 | assert_equal '1 issue(s) due in the next 42 days', mail.subject | |
472 | end |
|
472 | end | |
473 |
|
473 | |||
474 | def test_reminders_should_not_include_closed_issues |
|
474 | def test_reminders_should_not_include_closed_issues | |
475 | with_settings :default_language => 'en' do |
|
475 | with_settings :default_language => 'en' do | |
476 | Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5, |
|
476 | Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 5, | |
477 | :subject => 'Closed issue', :assigned_to_id => 3, |
|
477 | :subject => 'Closed issue', :assigned_to_id => 3, | |
478 | :due_date => 5.days.from_now, |
|
478 | :due_date => 5.days.from_now, | |
479 | :author_id => 2) |
|
479 | :author_id => 2) | |
480 | ActionMailer::Base.deliveries.clear |
|
480 | ActionMailer::Base.deliveries.clear | |
481 |
|
481 | |||
482 | Mailer.reminders(:days => 42) |
|
482 | Mailer.reminders(:days => 42) | |
483 | assert_equal 1, ActionMailer::Base.deliveries.size |
|
483 | assert_equal 1, ActionMailer::Base.deliveries.size | |
484 | mail = last_email |
|
484 | mail = last_email | |
485 | assert mail.bcc.include?('dlopper@somenet.foo') |
|
485 | assert mail.bcc.include?('dlopper@somenet.foo') | |
486 | assert_mail_body_no_match 'Closed issue', mail |
|
486 | assert_mail_body_no_match 'Closed issue', mail | |
487 | end |
|
487 | end | |
488 | end |
|
488 | end | |
489 |
|
489 | |||
490 | def test_reminders_for_users |
|
490 | def test_reminders_for_users | |
491 | Mailer.reminders(:days => 42, :users => ['5']) |
|
491 | Mailer.reminders(:days => 42, :users => ['5']) | |
492 | assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper |
|
492 | assert_equal 0, ActionMailer::Base.deliveries.size # No mail for dlopper | |
493 | Mailer.reminders(:days => 42, :users => ['3']) |
|
493 | Mailer.reminders(:days => 42, :users => ['3']) | |
494 | assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper |
|
494 | assert_equal 1, ActionMailer::Base.deliveries.size # No mail for dlopper | |
495 | mail = last_email |
|
495 | mail = last_email | |
496 | assert mail.bcc.include?('dlopper@somenet.foo') |
|
496 | assert mail.bcc.include?('dlopper@somenet.foo') | |
497 | assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail |
|
497 | assert_mail_body_match 'Bug #3: Error 281 when updating a recipe', mail | |
498 | end |
|
498 | end | |
499 |
|
499 | |||
500 | def test_mailer_should_not_change_locale |
|
500 | def test_mailer_should_not_change_locale | |
501 | Setting.default_language = 'en' |
|
501 | Setting.default_language = 'en' | |
502 | # Set current language to italian |
|
502 | # Set current language to italian | |
503 | set_language_if_valid 'it' |
|
503 | set_language_if_valid 'it' | |
504 | # Send an email to a french user |
|
504 | # Send an email to a french user | |
505 | user = User.find(1) |
|
505 | user = User.find(1) | |
506 | user.language = 'fr' |
|
506 | user.language = 'fr' | |
507 |
Mailer. |
|
507 | Mailer.account_activated(user).deliver | |
508 | mail = last_email |
|
508 | mail = last_email | |
509 | assert_mail_body_match 'Votre compte', mail |
|
509 | assert_mail_body_match 'Votre compte', mail | |
510 |
|
510 | |||
511 | assert_equal :it, current_language |
|
511 | assert_equal :it, current_language | |
512 | end |
|
512 | end | |
513 |
|
513 | |||
514 | def test_with_deliveries_off |
|
514 | def test_with_deliveries_off | |
515 | Mailer.with_deliveries false do |
|
515 | Mailer.with_deliveries false do | |
516 |
Mailer. |
|
516 | Mailer.test_email(User.find(1)).deliver | |
517 | end |
|
517 | end | |
518 | assert ActionMailer::Base.deliveries.empty? |
|
518 | assert ActionMailer::Base.deliveries.empty? | |
519 | # should restore perform_deliveries |
|
519 | # should restore perform_deliveries | |
520 | assert ActionMailer::Base.perform_deliveries |
|
520 | assert ActionMailer::Base.perform_deliveries | |
521 | end |
|
521 | end | |
522 |
|
522 | |||
523 | def test_layout_should_include_the_emails_header |
|
523 | def test_layout_should_include_the_emails_header | |
524 | with_settings :emails_header => "*Header content*" do |
|
524 | with_settings :emails_header => "*Header content*" do | |
525 |
assert Mailer. |
|
525 | assert Mailer.test_email(User.find(1)).deliver | |
526 | assert_select_email do |
|
526 | assert_select_email do | |
527 | assert_select ".header" do |
|
527 | assert_select ".header" do | |
528 | assert_select "strong", :text => "Header content" |
|
528 | assert_select "strong", :text => "Header content" | |
529 | end |
|
529 | end | |
530 | end |
|
530 | end | |
531 | end |
|
531 | end | |
532 | end |
|
532 | end | |
533 |
|
533 | |||
534 | private |
|
534 | private | |
535 | def last_email |
|
535 | def last_email | |
536 | mail = ActionMailer::Base.deliveries.last |
|
536 | mail = ActionMailer::Base.deliveries.last | |
537 | assert_not_nil mail |
|
537 | assert_not_nil mail | |
538 |
|
538 | |||
539 | end |
|
539 | end | |
540 | end |
|
540 | end |
General Comments 0
You need to be logged in to leave comments.
Login now