Feature #3003 » create_users_from_emails_patch.patch
| app/controllers/settings_controller.rb (working copy) | ||
|---|---|---|
| 40 | 40 |
@options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] }
|
| 41 | 41 |
@deliveries = ActionMailer::Base.perform_deliveries |
| 42 | 42 | |
| 43 |
@users = User.active.find(:all, :order => :login) |
|
| 44 |
@roles = Role.find(:all, :order => :name) |
|
| 45 | ||
| 43 | 46 |
@guessed_host_and_path = request.host_with_port.dup |
| 44 | 47 |
@guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank?
|
| 45 | 48 |
end |
| app/models/mail_handler.rb (working copy) | ||
|---|---|---|
| 41 | 41 |
def receive(email) |
| 42 | 42 |
@email = email |
| 43 | 43 |
@user = User.active.find_by_mail(email.from.to_a.first.to_s.strip) |
| 44 |
unless @user |
|
| 45 |
# Unknown user => the email is ignored |
|
| 46 |
# TODO: ability to create the user's account |
|
| 47 |
logger.info "MailHandler: email submitted by unknown user [#{email.from.first}]" if logger && logger.info
|
|
| 48 |
return false |
|
| 44 |
unless @user # create user |
|
| 45 |
if Setting.mail_handler_create_users_from_incoming_mails.to_i != 0 # creates a user and assigns him to a project |
|
| 46 |
@user = create_user(email) |
|
| 47 |
return false unless @user |
|
| 48 |
return false unless create_role(@user) |
|
| 49 |
elsif Setting.mail_handler_assign_unknown_addresses_to_user.to_i != 0 # use default users, needs :add_issue right on project |
|
| 50 |
unless @user = User.find_by_id(Setting.mail_handler_assign_unknown_addresses_to_user.to_i) |
|
| 51 |
logger.info "MailHandler: could not find User with id [#{Setting.mail_handler_assign_unknown_addresses_to_user.to_i}] to assign sender #{email.from.first}" if logger && loggger.info
|
|
| 52 |
return false |
|
| 53 |
end |
|
| 54 |
else |
|
| 55 |
# unknown user and no values specified to handle unknown mails |
|
| 56 |
logger.info "MailHandler: email submitted by unknown user [#{email.from.first}] (change Settings in incoming E-Mail tab for default behaviour)" if logger && logger.info
|
|
| 57 |
return false |
|
| 58 |
end |
|
| 49 | 59 |
end |
| 50 | 60 |
User.current = @user |
| 51 | 61 |
dispatch |
| 52 | 62 |
end |
| 53 |
|
|
| 63 | ||
| 64 | ||
| 65 |
protected |
|
| 66 |
def target_project |
|
| 67 |
# TODO: other ways to specify project: |
|
| 68 |
# * parse the email To field |
|
| 69 |
# * specific project (eg. Setting.mail_handler_target_project) |
|
| 70 |
target = Project.find_by_identifier(get_keyword(:project)) |
|
| 71 |
raise MissingInformation.new('Unable to determine target project') if target.nil?
|
|
| 72 |
target |
|
| 73 |
end |
|
| 74 | ||
| 75 |
# creates a user from email address and sets the status depending on the configured actions |
|
| 76 |
def create_user(email) |
|
| 77 |
new_user = User.new |
|
| 78 |
new_user.mail = email.from.first.to_s |
|
| 79 |
new_user.admin = false |
|
| 80 |
email_user_name = email.from.first.to_s.split('@').first
|
|
| 81 |
new_user.login = if !User.find_by_login(email_user_name) # set login to the email address without domain or if it exists to the full email |
|
| 82 |
email_user_name |
|
| 83 |
else |
|
| 84 |
email.from.first.to_s |
|
| 85 |
end |
|
| 86 |
new_user.status = User::STATUS_REGISTERED |
|
| 87 |
new_user.firstname = email_user_name.split('.').first
|
|
| 88 |
new_user.lastname = email_user_name.split('.').last
|
|
| 89 |
password = Token.generate_token_value(8) |
|
| 90 |
new_user.password = password |
|
| 91 |
new_user.password_confirmation = password |
|
| 92 | ||
| 93 |
case Setting.mail_handler_create_users_from_incoming_mails |
|
| 94 |
when '1' || '2' |
|
| 95 |
token = Token.new(:user => new_user, :action => "register") |
|
| 96 |
Mailer.deliver_register(token) if new_user.save and token.save |
|
| 97 |
Mailer.deliver_account_information(new_user, password) |
|
| 98 |
when '3' |
|
| 99 |
new_user.status = User::STATUS_ACTIVE |
|
| 100 |
new_user.last_login_on = Time.now |
|
| 101 |
if new_user.save |
|
| 102 |
Mailer.deliver_account_information(new_user, password, :notice_account_register_done) |
|
| 103 |
else |
|
| 104 |
logger.info "could not create user #{new_user}" if logger and logger.info
|
|
| 105 |
return false |
|
| 106 |
end |
|
| 107 |
else |
|
| 108 |
Mailer.deliver_account_activation_request(new_user) |
|
| 109 |
Mailer.deliver_account_information(new_user, password, :notice_account_pending) |
|
| 110 |
end |
|
| 111 |
return new_user |
|
| 112 |
end |
|
| 113 | ||
| 114 |
# creates a member on the target_project for the given user with the configured role |
|
| 115 |
def create_role(user) |
|
| 116 |
# Roles need right for add_issue |
|
| 117 |
if Setting.mail_handler_role_to_add_users_to_project.to_i != 0 |
|
| 118 |
if Role.find_by_id(Setting.mail_handler_role_to_add_users_to_project.to_i) |
|
| 119 |
member = Member.new( |
|
| 120 |
:user_id => user.id, |
|
| 121 |
:project_id => self.target_project.id, |
|
| 122 |
:role_id => Setting.mail_handler_role_to_add_users_to_project.to_i |
|
| 123 |
) |
|
| 124 |
unless member.save |
|
| 125 |
logger.info "could not create Role for user #{user.login} and project #{self.target_project.name}" if logger and logger.info
|
|
| 126 |
return false |
|
| 127 |
end |
|
| 128 |
else |
|
| 129 |
logger.info "could not assing #{user} to #{self.target_project.name}, missing Role id: #{Setting.mail_handler_role_to_add_users_to_project}" if logger and logger.info
|
|
| 130 |
return false |
|
| 131 |
end |
|
| 132 |
end |
|
| 133 |
return true |
|
| 134 |
end |
|
| 135 | ||
| 54 | 136 |
private |
| 55 | 137 | |
| 56 | 138 |
MESSAGE_ID_RE = %r{^<redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
|
| ... | ... | |
| 94 | 176 |
priority = (get_keyword(:priority) && Enumeration.find_by_opt_and_name('IPRI', get_keyword(:priority)))
|
| 95 | 177 |
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status))) |
| 96 | 178 | |
| 97 |
# check permission |
|
| 179 |
# check permissions
|
|
| 98 | 180 |
raise UnauthorizedAction unless user.allowed_to?(:add_issues, project) |
| 99 | 181 |
issue = Issue.new(:author => user, :project => project, :tracker => tracker, :category => category, :priority => priority) |
| 100 | 182 |
# check workflow |
| ... | ... | |
| 118 | 200 |
issue |
| 119 | 201 |
end |
| 120 | 202 |
|
| 121 |
def target_project |
|
| 122 |
# TODO: other ways to specify project: |
|
| 123 |
# * parse the email To field |
|
| 124 |
# * specific project (eg. Setting.mail_handler_target_project) |
|
| 125 |
target = Project.find_by_identifier(get_keyword(:project)) |
|
| 126 |
raise MissingInformation.new('Unable to determine target project') if target.nil?
|
|
| 127 |
target |
|
| 128 |
end |
|
| 129 |
|
|
| 130 | 203 |
# Adds a note to an existing issue |
| 131 | 204 |
def receive_issue_reply(issue_id) |
| 132 | 205 |
status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status))) |
| ... | ... | |
| 164 | 237 |
message = message.root |
| 165 | 238 |
if user.allowed_to?(:add_messages, message.project) && !message.locked? |
| 166 | 239 |
reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
|
| 167 |
:content => plain_text_body)
|
|
| 240 |
:content => plain_text_body) |
|
| 168 | 241 |
reply.author = user |
| 169 | 242 |
reply.board = message.board |
| 170 | 243 |
message.children << reply |
| ... | ... | |
| 180 | 253 |
if email.has_attachments? |
| 181 | 254 |
email.attachments.each do |attachment| |
| 182 | 255 |
Attachment.create(:container => obj, |
| 183 |
:file => attachment,
|
|
| 184 |
:author => user,
|
|
| 185 |
:content_type => attachment.content_type)
|
|
| 256 |
:file => attachment, |
|
| 257 |
:author => user, |
|
| 258 |
:content_type => attachment.content_type) |
|
| 186 | 259 |
end |
| 187 | 260 |
end |
| 188 | 261 |
end |
| app/models/mailer.rb (working copy) | ||
|---|---|---|
| 158 | 158 |
# Example: |
| 159 | 159 |
# account_information(user, password) => tmail object |
| 160 | 160 |
# Mailer.deliver_account_information(user, password) => sends account information to the user |
| 161 |
def account_information(user, password) |
|
| 161 |
def account_information(user, password, information = '')
|
|
| 162 | 162 |
set_language_if_valid user.language |
| 163 | 163 |
recipients user.mail |
| 164 | 164 |
subject l(:mail_subject_register, Setting.app_title) |
| 165 | 165 |
body :user => user, |
| 166 | 166 |
:password => password, |
| 167 |
:information => information, |
|
| 167 | 168 |
:login_url => url_for(:controller => 'account', :action => 'login') |
| 168 | 169 |
end |
| 169 | 170 | |
| app/models/token.rb (working copy) | ||
|---|---|---|
| 35 | 35 |
end |
| 36 | 36 |
|
| 37 | 37 |
private |
| 38 |
def self.generate_token_value |
|
| 38 |
def self.generate_token_value(length = 40)
|
|
| 39 | 39 |
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
|
| 40 | 40 |
token_value = '' |
| 41 |
40.times { |i| token_value << chars[rand(chars.size-1)] }
|
|
| 41 |
length.times { |i| token_value << chars[rand(chars.size-1)] }
|
|
| 42 | 42 |
token_value |
| 43 | 43 |
end |
| 44 | 44 |
end |
| app/views/mailer/account_information.text.html.rhtml (working copy) | ||
|---|---|---|
| 1 | 1 |
<% if @user.auth_source %> |
| 2 |
<p><%= l(:mail_body_account_information_external, @user.auth_source.name) %></p> |
|
| 2 |
<p><%= l(:mail_body_account_information_external, @user.auth_source.name) %></p>
|
|
| 3 | 3 |
<% else %> |
| 4 |
<p><%= l(:mail_body_account_information) %>:</p> |
|
| 5 |
<ul> |
|
| 4 |
<p><%= l(:mail_body_account_information) %>:</p>
|
|
| 5 |
<ul>
|
|
| 6 | 6 |
<li><%= l(:field_login) %>: <%= @user.login %></li> |
| 7 | 7 |
<li><%= l(:field_password) %>: <%= @password %></li> |
| 8 |
</ul> |
|
| 8 |
</ul>
|
|
| 9 | 9 |
<% end %> |
| 10 | 10 | |
| 11 |
<% if @information -%> |
|
| 12 |
<p><%= l(@information) %></p> |
|
| 13 |
<% end -%> |
|
| 14 | ||
| 11 | 15 |
<p><%= l(:label_login) %>: <%= auto_link(@login_url) %></p> |
| app/views/mailer/account_information.text.plain.rhtml (working copy) | ||
|---|---|---|
| 3 | 3 |
* <%= l(:field_login) %>: <%= @user.login %> |
| 4 | 4 |
* <%= l(:field_password) %>: <%= @password %> |
| 5 | 5 |
<% end %> |
| 6 |
<% if @information -%> |
|
| 7 |
<%= l(@information) %> |
|
| 8 |
<% end -%> |
|
| 6 | 9 |
<%= l(:label_login) %>: <%= @login_url %> |
| app/views/settings/_mail_handler.rhtml (working copy) | ||
|---|---|---|
| 1 | 1 |
<% form_tag({:action => 'edit', :tab => 'mail_handler'}) do %>
|
| 2 | 2 | |
| 3 |
<div class="box tabular settings"> |
|
| 4 |
<p><label><%= l(:setting_mail_handler_api_enabled) %></label> |
|
| 5 |
<%= check_box_tag 'settings[mail_handler_api_enabled]', 1, Setting.mail_handler_api_enabled?, |
|
| 6 |
:onclick => "if (this.checked) { Form.Element.enable('settings_mail_handler_api_key'); } else { Form.Element.disable('settings_mail_handler_api_key'); }" %>
|
|
| 7 |
<%= hidden_field_tag 'settings[mail_handler_api_enabled]', 0 %></p> |
|
| 3 |
<div class="box tabular settings">
|
|
| 4 |
<p><label><%= l(:setting_mail_handler_api_enabled) %></label>
|
|
| 5 |
<%= check_box_tag 'settings[mail_handler_api_enabled]', 1, Setting.mail_handler_api_enabled?,
|
|
| 6 |
:onclick => "if (this.checked) { Form.Element.enable('settings_mail_handler_api_key'); } else { Form.Element.disable('settings_mail_handler_api_key'); }" %>
|
|
| 7 |
<%= hidden_field_tag 'settings[mail_handler_api_enabled]', 0 %></p>
|
|
| 8 | 8 | |
| 9 |
<p><label><%= l(:setting_mail_handler_api_key) %></label> |
|
| 10 |
<%= text_field_tag 'settings[mail_handler_api_key]', Setting.mail_handler_api_key, |
|
| 11 |
:size => 30, |
|
| 12 |
:id => 'settings_mail_handler_api_key', |
|
| 13 |
:disabled => !Setting.mail_handler_api_enabled? %> |
|
| 14 |
<%= link_to_function l(:label_generate_key), "if ($('settings_mail_handler_api_key').disabled == false) { $('settings_mail_handler_api_key').value = randomKey(20) }" %></p>
|
|
| 15 |
</div> |
|
| 9 |
<p><label><%= l(:setting_mail_handler_api_key) %></label> |
|
| 10 |
<%= text_field_tag 'settings[mail_handler_api_key]', Setting.mail_handler_api_key, |
|
| 11 |
:size => 30, |
|
| 12 |
:id => 'settings_mail_handler_api_key', |
|
| 13 |
:disabled => !Setting.mail_handler_api_enabled? %> |
|
| 14 |
<%= link_to_function l(:label_generate_key), "if ($('settings_mail_handler_api_key').disabled == false) { $('settings_mail_handler_api_key').value = randomKey(20) }" %></p>
|
|
| 16 | 15 | |
| 17 |
<%= submit_tag l(:button_save) %> |
|
| 16 |
<p> |
|
| 17 |
<label><%= l(:setting_mail_handler_create_users_from_incoming_mails) %></label> |
|
| 18 |
<%= select_tag 'settings[mail_handler_create_users_from_incoming_mails]', |
|
| 19 |
options_for_select( [[l(:label_disabled), "0"], |
|
| 20 |
[l(:label_registration_activation_by_email), "1"], |
|
| 21 |
[l(:label_registration_manual_activation), "2"], |
|
| 22 |
[l(:label_registration_automatic_activation), "3"] |
|
| 23 |
], Setting.mail_handler_create_users_from_incoming_mails ), |
|
| 24 |
:onChange => "if (this.options[this.selectedIndex].value == \"0\") { Form.Element.enable('settings_mail_handler_assign_unknown_addresses_to_user'); } else { Form.Element.disable('settings_mail_handler_assign_unknown_addresses_to_user'); }"
|
|
| 25 |
%> |
|
| 26 |
</p> |
|
| 27 | ||
| 28 |
<p> |
|
| 29 |
<label><%= l(:setting_mail_handler_role_to_add_users_to_project) %></label> |
|
| 30 |
<%= select(:settings, :mail_handler_role_to_add_users_to_project, @roles.collect {|p| [ p.name, p.id ] },
|
|
| 31 |
{ :include_blank => true, :selected => Setting.mail_handler_role_to_add_users_to_project.to_i }
|
|
| 32 |
) %> |
|
| 33 |
</p> |
|
| 34 | ||
| 35 |
|
|
| 36 |
<p> |
|
| 37 |
<label><%= l(:setting_mail_handler_assign_unknown_addresses_to_user) %></label> |
|
| 38 |
<%= select(:settings, :mail_handler_assign_unknown_addresses_to_user, @users.collect {|p| [ p.login, p.id ] },
|
|
| 39 |
{ :include_blank => true, :selected => Setting.mail_handler_assign_unknown_addresses_to_user.to_i },
|
|
| 40 |
{ :disabled => Setting.mail_handler_create_users_from_incoming_mails.to_i == 0}
|
|
| 41 |
) %> |
|
| 42 |
</p> |
|
| 43 | ||
| 44 |
</div> |
|
| 45 | ||
| 46 |
<%= submit_tag l(:button_save) %> |
|
| 18 | 47 |
<% end %> |
| config/locales/de.yml (working copy) | ||
|---|---|---|
| 304 | 304 |
setting_enabled_scm: Aktivierte Versionskontrollsysteme |
| 305 | 305 |
setting_mail_handler_api_enabled: Abruf eingehender E-Mails aktivieren |
| 306 | 306 |
setting_mail_handler_api_key: API-Schlüssel |
| 307 |
setting_mail_handler_create_users_from_incoming_mails: Erstelle Benutzer aus eingehenden E-Mails |
|
| 308 |
setting_mail_handler_assign_unknown_addresses_to_user: Unbekannte Absender folgendem Benutzer zuordnen |
|
| 309 |
setting_mail_handler_role_to_add_users_to_project: Rolle für neue Benutzer |
|
| 307 | 310 |
setting_sequential_project_identifiers: Fortlaufende Projektkennungen generieren |
| 308 | 311 |
setting_gravatar_enabled: Gravatar Benutzerbilder benutzen |
| 309 | 312 |
setting_diff_max_lines_displayed: Maximale Anzahl anzuzeigender Diff-Zeilen |
| config/locales/en.yml (working copy) | ||
|---|---|---|
| 279 | 279 |
setting_enabled_scm: Enabled SCM |
| 280 | 280 |
setting_mail_handler_api_enabled: Enable WS for incoming emails |
| 281 | 281 |
setting_mail_handler_api_key: API key |
| 282 |
setting_mail_handler_create_users_from_incoming_mails: Create users from incoming E-Mails |
|
| 283 |
setting_mail_handler_assign_unknown_addresses_to_user: Assign Unknown addresses to the following User |
|
| 284 |
setting_mail_handler_role_to_add_users_to_project: Role for new Users |
|
| 282 | 285 |
setting_sequential_project_identifiers: Generate sequential project identifiers |
| 283 | 286 |
setting_gravatar_enabled: Use Gravatar user icons |
| 284 | 287 |
setting_diff_max_lines_displayed: Max number of diff lines displayed |
| config/settings.yml (working copy) | ||
|---|---|---|
| 117 | 117 |
mail_handler_api_enabled: |
| 118 | 118 |
default: 0 |
| 119 | 119 |
mail_handler_api_key: |
| 120 |
default: |
|
| 120 |
default: |
|
| 121 |
mail_handler_create_users_from_incoming_mails: |
|
| 122 |
default: 0 |
|
| 123 |
mail_handler_assign_unknown_addresses_to_user: |
|
| 124 |
default: 0 |
|
| 125 |
mail_handler_role_to_add_users_to_project: |
|
| 126 |
default: 0 |
|
| 121 | 127 |
issue_list_default_columns: |
| 122 | 128 |
serialized: true |
| 123 | 129 |
default: |
| test/unit/mail_handler_test.rb (working copy) | ||
|---|---|---|
| 32 | 32 |
:custom_fields, |
| 33 | 33 |
:custom_fields_trackers, |
| 34 | 34 |
:boards, |
| 35 |
:messages |
|
| 35 |
:messages, |
|
| 36 |
:settings |
|
| 36 | 37 |
|
| 37 | 38 |
FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' |
| 38 | 39 |
|
| ... | ... | |
| 196 | 197 |
assert_equal 'This is a html-only email.', issue.description |
| 197 | 198 |
end |
| 198 | 199 | |
| 200 |
def test_should_create_new_user_and_assign_to_project |
|
| 201 |
issue = submit_email('ticket_with_new_user.eml')
|
|
| 202 |
assert issue.is_a?(Issue) |
|
| 203 |
assert !issue.new_record? |
|
| 204 |
assert_equal issue.subject, 'New ticker from new user' |
|
| 205 |
assert_equal 'onlinestore', issue.project.name.downcase |
|
| 206 |
assert_equal 'jjins', issue.author.login |
|
| 207 |
assert_equal 'jjins@somenet.foo', issue.author.mail |
|
| 208 |
creator = User.find_by_login('jjins')
|
|
| 209 |
assert_not_nil creator |
|
| 210 |
member = Member.find(:first, :conditions => {:project_id => issue.project.id, :user_id => creator.id})
|
|
| 211 |
assert_not_nil member |
|
| 212 |
assert_equal 'Reporter', member.role.name |
|
| 213 |
end |
|
| 214 | ||
| 199 | 215 |
private |
| 200 | 216 |
|
| 201 | 217 |
def submit_email(filename, options={})
|