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