Index: app/controllers/settings_controller.rb =================================================================== --- app/controllers/settings_controller.rb (revision 2679) +++ app/controllers/settings_controller.rb (working copy) @@ -40,6 +40,9 @@ @options[:user_format] = User::USER_FORMATS.keys.collect {|f| [User.current.name(f), f.to_s] } @deliveries = ActionMailer::Base.perform_deliveries + @users = User.active.find(:all, :order => :login) + @roles = Role.find(:all, :order => :name) + @guessed_host_and_path = request.host_with_port.dup @guessed_host_and_path << ('/'+ Redmine::Utils.relative_url_root.gsub(%r{^\/}, '')) unless Redmine::Utils.relative_url_root.blank? end Index: app/models/mail_handler.rb =================================================================== --- app/models/mail_handler.rb (revision 2679) +++ app/models/mail_handler.rb (working copy) @@ -41,16 +41,98 @@ def receive(email) @email = email @user = User.active.find_by_mail(email.from.to_a.first.to_s.strip) - unless @user - # Unknown user => the email is ignored - # TODO: ability to create the user's account - logger.info "MailHandler: email submitted by unknown user [#{email.from.first}]" if logger && logger.info - return false + unless @user # create user + if Setting.mail_handler_create_users_from_incoming_mails.to_i != 0 # creates a user and assigns him to a project + @user = create_user(email) + return false unless @user + return false unless create_role(@user) + elsif Setting.mail_handler_assign_unknown_addresses_to_user.to_i != 0 # use default users, needs :add_issue right on project + unless @user = User.find_by_id(Setting.mail_handler_assign_unknown_addresses_to_user.to_i) + 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 + return false + end + else + # unknown user and no values specified to handle unknown mails + 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 + return false + end end User.current = @user dispatch end - + + + protected + def target_project + # TODO: other ways to specify project: + # * parse the email To field + # * specific project (eg. Setting.mail_handler_target_project) + target = Project.find_by_identifier(get_keyword(:project)) + raise MissingInformation.new('Unable to determine target project') if target.nil? + target + end + + # creates a user from email address and sets the status depending on the configured actions + def create_user(email) + new_user = User.new + new_user.mail = email.from.first.to_s + new_user.admin = false + email_user_name = email.from.first.to_s.split('@').first + 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 + email_user_name + else + email.from.first.to_s + end + new_user.status = User::STATUS_REGISTERED + new_user.firstname = email_user_name.split('.').first + new_user.lastname = email_user_name.split('.').last + password = Token.generate_token_value(8) + new_user.password = password + new_user.password_confirmation = password + + case Setting.mail_handler_create_users_from_incoming_mails + when '1' || '2' + token = Token.new(:user => new_user, :action => "register") + Mailer.deliver_register(token) if new_user.save and token.save + Mailer.deliver_account_information(new_user, password) + when '3' + new_user.status = User::STATUS_ACTIVE + new_user.last_login_on = Time.now + if new_user.save + Mailer.deliver_account_information(new_user, password, :notice_account_register_done) + else + logger.info "could not create user #{new_user}" if logger and logger.info + return false + end + else + Mailer.deliver_account_activation_request(new_user) + Mailer.deliver_account_information(new_user, password, :notice_account_pending) + end + return new_user + end + + # creates a member on the target_project for the given user with the configured role + def create_role(user) + # Roles need right for add_issue + if Setting.mail_handler_role_to_add_users_to_project.to_i != 0 + if Role.find_by_id(Setting.mail_handler_role_to_add_users_to_project.to_i) + member = Member.new( + :user_id => user.id, + :project_id => self.target_project.id, + :role_id => Setting.mail_handler_role_to_add_users_to_project.to_i + ) + unless member.save + logger.info "could not create Role for user #{user.login} and project #{self.target_project.name}" if logger and logger.info + return false + end + else + 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 + return false + end + end + return true + end + private MESSAGE_ID_RE = %r{^ user, :project => project, :tracker => tracker, :category => category, :priority => priority) # check workflow @@ -118,15 +200,6 @@ issue end - def target_project - # TODO: other ways to specify project: - # * parse the email To field - # * specific project (eg. Setting.mail_handler_target_project) - target = Project.find_by_identifier(get_keyword(:project)) - raise MissingInformation.new('Unable to determine target project') if target.nil? - target - end - # Adds a note to an existing issue def receive_issue_reply(issue_id) status = (get_keyword(:status) && IssueStatus.find_by_name(get_keyword(:status))) @@ -164,7 +237,7 @@ message = message.root if user.allowed_to?(:add_messages, message.project) && !message.locked? reply = Message.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip, - :content => plain_text_body) + :content => plain_text_body) reply.author = user reply.board = message.board message.children << reply @@ -180,9 +253,9 @@ if email.has_attachments? email.attachments.each do |attachment| Attachment.create(:container => obj, - :file => attachment, - :author => user, - :content_type => attachment.content_type) + :file => attachment, + :author => user, + :content_type => attachment.content_type) end end end Index: app/models/mailer.rb =================================================================== --- app/models/mailer.rb (revision 2679) +++ app/models/mailer.rb (working copy) @@ -158,12 +158,13 @@ # Example: # account_information(user, password) => tmail object # Mailer.deliver_account_information(user, password) => sends account information to the user - def account_information(user, password) + def account_information(user, password, information = '') set_language_if_valid user.language recipients user.mail subject l(:mail_subject_register, Setting.app_title) body :user => user, :password => password, + :information => information, :login_url => url_for(:controller => 'account', :action => 'login') end Index: app/models/token.rb =================================================================== --- app/models/token.rb (revision 2679) +++ app/models/token.rb (working copy) @@ -35,10 +35,10 @@ end private - def self.generate_token_value + def self.generate_token_value(length = 40) chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a token_value = '' - 40.times { |i| token_value << chars[rand(chars.size-1)] } + length.times { |i| token_value << chars[rand(chars.size-1)] } token_value end end Index: app/views/mailer/account_information.text.html.rhtml =================================================================== --- app/views/mailer/account_information.text.html.rhtml (revision 2679) +++ app/views/mailer/account_information.text.html.rhtml (working copy) @@ -1,11 +1,15 @@ <% if @user.auth_source %> -

<%= l(:mail_body_account_information_external, @user.auth_source.name) %>

+

<%= l(:mail_body_account_information_external, @user.auth_source.name) %>

<% else %> -

<%= l(:mail_body_account_information) %>:

- <% end %> +<% if @information -%> +

<%= l(@information) %>

+<% end -%> +

<%= l(:label_login) %>: <%= auto_link(@login_url) %>

Index: app/views/mailer/account_information.text.plain.rhtml =================================================================== --- app/views/mailer/account_information.text.plain.rhtml (revision 2679) +++ app/views/mailer/account_information.text.plain.rhtml (working copy) @@ -3,4 +3,7 @@ * <%= l(:field_login) %>: <%= @user.login %> * <%= l(:field_password) %>: <%= @password %> <% end %> +<% if @information -%> + <%= l(@information) %> +<% end -%> <%= l(:label_login) %>: <%= @login_url %> Index: app/views/settings/_mail_handler.rhtml =================================================================== --- app/views/settings/_mail_handler.rhtml (revision 2679) +++ app/views/settings/_mail_handler.rhtml (working copy) @@ -1,18 +1,47 @@ <% form_tag({:action => 'edit', :tab => 'mail_handler'}) do %> -
-

-<%= check_box_tag 'settings[mail_handler_api_enabled]', 1, Setting.mail_handler_api_enabled?, - :onclick => "if (this.checked) { Form.Element.enable('settings_mail_handler_api_key'); } else { Form.Element.disable('settings_mail_handler_api_key'); }" %> -<%= hidden_field_tag 'settings[mail_handler_api_enabled]', 0 %>

+
+

+ <%= check_box_tag 'settings[mail_handler_api_enabled]', 1, Setting.mail_handler_api_enabled?, + :onclick => "if (this.checked) { Form.Element.enable('settings_mail_handler_api_key'); } else { Form.Element.disable('settings_mail_handler_api_key'); }" %> + <%= hidden_field_tag 'settings[mail_handler_api_enabled]', 0 %>

-

-<%= text_field_tag 'settings[mail_handler_api_key]', Setting.mail_handler_api_key, - :size => 30, - :id => 'settings_mail_handler_api_key', - :disabled => !Setting.mail_handler_api_enabled? %> -<%= link_to_function l(:label_generate_key), "if ($('settings_mail_handler_api_key').disabled == false) { $('settings_mail_handler_api_key').value = randomKey(20) }" %>

-
+

+ <%= text_field_tag 'settings[mail_handler_api_key]', Setting.mail_handler_api_key, + :size => 30, + :id => 'settings_mail_handler_api_key', + :disabled => !Setting.mail_handler_api_enabled? %> + <%= link_to_function l(:label_generate_key), "if ($('settings_mail_handler_api_key').disabled == false) { $('settings_mail_handler_api_key').value = randomKey(20) }" %>

-<%= submit_tag l(:button_save) %> +

+ + <%= select_tag 'settings[mail_handler_create_users_from_incoming_mails]', + options_for_select( [[l(:label_disabled), "0"], + [l(:label_registration_activation_by_email), "1"], + [l(:label_registration_manual_activation), "2"], + [l(:label_registration_automatic_activation), "3"] + ], Setting.mail_handler_create_users_from_incoming_mails ), + :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'); }" + %> +

+ +

+ + <%= select(:settings, :mail_handler_role_to_add_users_to_project, @roles.collect {|p| [ p.name, p.id ] }, + { :include_blank => true, :selected => Setting.mail_handler_role_to_add_users_to_project.to_i } + ) %> +

+ + +

+ + <%= select(:settings, :mail_handler_assign_unknown_addresses_to_user, @users.collect {|p| [ p.login, p.id ] }, + { :include_blank => true, :selected => Setting.mail_handler_assign_unknown_addresses_to_user.to_i }, + { :disabled => Setting.mail_handler_create_users_from_incoming_mails.to_i == 0} + ) %> +

+ +
+ + <%= submit_tag l(:button_save) %> <% end %> Index: config/locales/de.yml =================================================================== --- config/locales/de.yml (revision 2679) +++ config/locales/de.yml (working copy) @@ -304,6 +304,9 @@ setting_enabled_scm: Aktivierte Versionskontrollsysteme setting_mail_handler_api_enabled: Abruf eingehender E-Mails aktivieren setting_mail_handler_api_key: API-Schlüssel + setting_mail_handler_create_users_from_incoming_mails: Erstelle Benutzer aus eingehenden E-Mails + setting_mail_handler_assign_unknown_addresses_to_user: Unbekannte Absender folgendem Benutzer zuordnen + setting_mail_handler_role_to_add_users_to_project: Rolle für neue Benutzer setting_sequential_project_identifiers: Fortlaufende Projektkennungen generieren setting_gravatar_enabled: Gravatar Benutzerbilder benutzen setting_diff_max_lines_displayed: Maximale Anzahl anzuzeigender Diff-Zeilen Index: config/locales/en.yml =================================================================== --- config/locales/en.yml (revision 2679) +++ config/locales/en.yml (working copy) @@ -279,6 +279,9 @@ setting_enabled_scm: Enabled SCM setting_mail_handler_api_enabled: Enable WS for incoming emails setting_mail_handler_api_key: API key + setting_mail_handler_create_users_from_incoming_mails: Create users from incoming E-Mails + setting_mail_handler_assign_unknown_addresses_to_user: Assign Unknown addresses to the following User + setting_mail_handler_role_to_add_users_to_project: Role for new Users setting_sequential_project_identifiers: Generate sequential project identifiers setting_gravatar_enabled: Use Gravatar user icons setting_diff_max_lines_displayed: Max number of diff lines displayed Index: config/settings.yml =================================================================== --- config/settings.yml (revision 2679) +++ config/settings.yml (working copy) @@ -117,7 +117,13 @@ mail_handler_api_enabled: default: 0 mail_handler_api_key: - default: + default: +mail_handler_create_users_from_incoming_mails: + default: 0 +mail_handler_assign_unknown_addresses_to_user: + default: 0 +mail_handler_role_to_add_users_to_project: + default: 0 issue_list_default_columns: serialized: true default: Index: test/unit/mail_handler_test.rb =================================================================== --- test/unit/mail_handler_test.rb (revision 2679) +++ test/unit/mail_handler_test.rb (working copy) @@ -32,7 +32,8 @@ :custom_fields, :custom_fields_trackers, :boards, - :messages + :messages, + :settings FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler' @@ -196,6 +197,21 @@ assert_equal 'This is a html-only email.', issue.description end + def test_should_create_new_user_and_assign_to_project + issue = submit_email('ticket_with_new_user.eml') + assert issue.is_a?(Issue) + assert !issue.new_record? + assert_equal issue.subject, 'New ticker from new user' + assert_equal 'onlinestore', issue.project.name.downcase + assert_equal 'jjins', issue.author.login + assert_equal 'jjins@somenet.foo', issue.author.mail + creator = User.find_by_login('jjins') + assert_not_nil creator + member = Member.find(:first, :conditions => {:project_id => issue.project.id, :user_id => creator.id}) + assert_not_nil member + assert_equal 'Reporter', member.role.name + end + private def submit_email(filename, options={})