class UserMailer < ActionMailer::Base helper :application, # for textilizable :custom_fields # for show_value # wrap in a lambda to allow changing at run-time default :from => Proc.new { Setting.mail_from } def test_mail(user) @welcome_url = url_for(:controller => :welcome) headers['X-OpenProject-Type'] = 'Test' with_locale_for(user) do mail :to => "#{user.name} <#{user.mail}>", :subject => 'OpenProject Test' end end def issue_added(user, issue) @issue = issue open_project_headers 'Project' => @issue.project.identifier, 'Issue-Id' => @issue.id, 'Issue-Author' => @issue.author.login, 'Type' => 'Issue' open_project_headers 'Issue-Assignee' => @issue.assigned_to.login if @issue.assigned_to message_id @issue with_locale_for(user) do subject = "[#{@issue.project.name} - #{@issue.tracker.name} ##{@issue.id}] (#{@issue.status.name}) #{@issue.subject}" mail :to => user.mail, :subject => subject end end def issue_updated(user, journal) @journal = journal @issue = journal.journaled.reload open_project_headers 'Project' => @issue.project.identifier, 'Issue-Id' => @issue.id, 'Issue-Author' => @issue.author.login, 'Type' => 'Issue' open_project_headers 'Issue-Assignee' => @issue.assigned_to.login if @issue.assigned_to message_id @journal references @issue with_locale_for(user) do subject = "[#{@issue.project.name} - #{@issue.tracker.name} ##{@issue.id}] " subject << "(#{@issue.status.name}) " if @journal.details['status_id'] subject << @issue.subject mail :to => user.mail, :subject => subject end end def password_lost(token) return unless token.user # token's can have no user @token = token @reset_password_url = url_for(:controller => :account, :action => :lost_password, :token => @token.value) open_project_headers 'Type' => 'Account' user = @token.user with_locale_for(user) do subject = t(:mail_subject_lost_password, :value => Setting.app_title) mail :to => user.mail, :subject => subject end end def news_added(user, news) @news = news open_project_headers 'Type' => 'News' open_project_headers 'Project' => @news.project.identifier if @news.project message_id @news with_locale_for(user) do subject = "#{t(:label_news)}: #{@news.title}" subject = "[#{@news.project.name}] #{subject}" if @news.project mail :to => user.mail, :subject => subject end end def user_signed_up(token) return unless token.user @token = token @activation_url = url_for(:controller => :account, :action => :activate, :token => @token.value) open_project_headers 'Type' => 'Account' user = token.user with_locale_for(user) do subject = t(:mail_subject_register, :value => Setting.app_title) mail :to => user.mail, :subject => subject end end def news_comment_added(user, comment) @comment = comment @news = @comment.commented open_project_headers 'Project' => @news.project.identifier if @news.project message_id @comment references @news with_locale_for(user) do subject = "#{t(:label_news)}: #{@news.title}" subject = "Re: [#{@news.project.name}] #{subject}" if @news.project mail :to => user.mail, :subject => subject end end def wiki_content_added(user, wiki_content) @wiki_content = wiki_content open_project_headers 'Project' => @wiki_content.project.identifier, 'Wiki-Page-Id' => @wiki_content.page.id, 'Type' => 'Wiki' message_id @wiki_content with_locale_for(user) do subject = "[#{@wiki_content.project.name}] #{t(:mail_subject_wiki_content_added, :id => @wiki_content.page.pretty_title)}" mail :to => user.mail, :subject => subject end end def wiki_content_updated(user, wiki_content) @wiki_content = wiki_content @wiki_diff_url = url_for(:controller => :wiki, :action => :diff, :project_id => wiki_content.project, :id => wiki_content.page.title, :version => wiki_content.version) open_project_headers 'Project' => @wiki_content.project.identifier, 'Wiki-Page-Id' => @wiki_content.page.id, 'Type' => 'Wiki' message_id @wiki_content with_locale_for(user) do subject = "[#{@wiki_content.project.name}] #{t(:mail_subject_wiki_content_updated, :id => @wiki_content.page.pretty_title)}" mail :to => user.mail, :subject => subject end end def message_posted(user, message) @message = message @message_url = url_for(:controller => :messages, :action => :show, :board_id => @message.board, :id => @message.root, :r => @message, :anchor => "message-#{@message.id}") open_project_headers 'Project' => @message.project.identifier, 'Wiki-Page-Id' => @message.parent_id || @message.id, 'Type' => 'Forum' message_id @message references @message.parent if @message.parent with_locale_for(user) do subject = "[#{@message.board.project.name} - #{@message.board.name} - msg#{@message.root.id}] #{@message.subject}" mail :to => user.mail, :subject => subject end end def document_added(user, document) @document = document open_project_headers 'Project' => @document.project.identifier, 'Type' => 'Document' with_locale_for(user) do subject = "[#{@document.project.name}] #{t(:label_document_new)}: #{@document.title}" mail :to => user.mail, :subject => subject end end def account_activated(user) @user = user open_project_headers 'Type' => 'Account' with_locale_for(user) do subject = t(:mail_subject_register, :value => Setting.app_title) mail :to => user.mail, :subject => subject end end def account_information(user, password) @user = user @password = password open_project_headers 'Type' => 'Account' with_locale_for(user) do subject = t(:mail_subject_register, :value => Setting.app_title) mail :to => user.mail, :subject => subject end end def account_activation_requested(admin, user) @user = user @activation_url = url_for(:controller => :users, :action => :index, :status => User::STATUS_REGISTERED, :sort_key => :created_on, :sort_order => :desc) open_project_headers 'Type' => 'Account' with_locale_for(admin) do subject = t(:mail_subject_account_activation_request, :value => Setting.app_title) mail :to => admin.mail, :subject => subject end end def attachments_added(user, attachments) @attachments = attachments container = attachments.first.container open_project_headers 'Type' => 'Attachment' open_project_headers 'Project' => container.project.identifier if container.project case container.class.name when 'Project' @added_to = "#{t(:label_project)}: #{container}" @added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container) when 'Version' @added_to = "#{t(:label_version)}: #{container.name}" @added_to_url = url_for(:controller => 'files', :action => 'index', :project_id => container.project) when 'Document' @added_to = "#{t(:label_document)}: #{container.title}" @added_to_url = url_for(:controller => 'documents', :action => 'show', :id => container.id) end with_locale_for(user) do subject = t(:label_attachment_new) subject = "[#{container.project.name}] #{subject}" if container.project mail :to => user.mail, :subject => subject end end def reminder_mail(user, issues, days) @issues = issues @days = days @assigned_issues_url = url_for(:controller => :issues, :action => :index, :set_filter => 1, :assigned_to_id => user.id, :sort => 'due_date:asc') open_project_headers 'Type' => 'Issue' with_locale_for(user) do subject = t(:mail_subject_reminder, :count => @issues.size, :days => @days) mail :to => user.mail, :subject => subject end end # Activates/desactivates email deliveries during +block+ def self.with_deliveries(temporary_state = true, &block) old_state = ActionMailer::Base.perform_deliveries ActionMailer::Base.perform_deliveries = temporary_state yield ensure ActionMailer::Base.perform_deliveries = old_state end def self.generate_message_id(object) # id + timestamp should reduce the odds of a collision # as far as we don't send multiple emails for the same object timestamp = object.send(object.respond_to?(:created_on) ? :created_on : :updated_on) hash = "openproject.#{object.class.name.demodulize.underscore}-#{object.id}.#{timestamp.strftime("%Y%m%d%H%M%S")}" host = Setting.mail_from.to_s.gsub(%r{^.*@}, '') host = "#{::Socket.gethostname}.openproject" if host.empty? "#{hash}@#{host}" end private def self.host if Redmine::Utils.relative_url_root.blank? Setting.host_name else Setting.host_name.to_s.gsub(%r{\/.*$}, '') end end def self.protocol Setting.protocol end def self.default_url_options super.merge :host => host, :protocol => protocol end def mail(headers = {}) super(headers) do |format| format.text format.html unless Setting.plain_text_mail? end end def message_id(object) headers['Message-ID'] = "<#{self.class.generate_message_id(object)}>" end def references(object) headers['References'] = "<#{self.class.generate_message_id(object)}>" end def with_locale_for(user, &block) locale = user.language.presence || Setting.default_language.presence || I18n.default_locale I18n.with_locale(locale, &block) end # Prepends given fields with 'X-OpenProject-' to save some duplication def open_project_headers(hash) hash.each { |key, value| headers["X-OpenProject-#{key}"] = value.to_s } end end # interceptors class DefaultHeadersInterceptor def delivering_email(mail) mail.headers(default_headers) end def default_headers { 'X-Mailer' => 'OpenProject', 'X-OpenProject-Host' => Setting.host_name, 'X-OpenProject-Site' => Setting.app_title, 'Precedence' => 'bulk', 'Auto-Submitted' => 'auto-generated' } end end class RemoveSelfNotificationsInterceptor def delivering_email(mail) current_user = User.current if current_user.pref[:no_self_notified].present? mail.to = mail.to.reject {|address| address == current_user.mail} if mail.to.present? end end end class DoNotSendMailsWithoutReceiverInterceptor def delivering_email(mail) mail.perform_deliveries = false if mail.to.blank? end end # register interceptors UserMailer.register_interceptor(DefaultHeadersInterceptor.new) UserMailer.register_interceptor(RemoveSelfNotificationsInterceptor.new) # following needs to be the last interceptor UserMailer.register_interceptor(DoNotSendMailsWithoutReceiverInterceptor.new) # helper object for `rake redmine:send_reminders` class DueIssuesReminder def initialize(days = nil, project_id = nil, tracker_id = nil, user_ids = []) @days = days ? days.to_i : 7 @project = Project.find_by_id(project_id) @tracker = Tracker.find_by_id(tracker_id) @user_ids = user_ids end def remind_users s = ARCondition.new ["#{IssueStatus.table_name}.is_closed = ? AND #{Issue.table_name}.due_date <= ?", false, @days.days.from_now.to_date] s << "#{Issue.table_name}.assigned_to_id IS NOT NULL" s << ["#{Issue.table_name}.assigned_to_id IN (?)", @user_ids] if @user_ids.any? s << "#{Project.table_name}.status = #{Project::STATUS_ACTIVE}" s << "#{Issue.table_name}.project_id = #{@project.id}" if @project s << "#{Issue.table_name}.tracker_id = #{@tracker.id}" if @tracker issues_by_assignee = Issue.find(:all, :include => [:status, :assigned_to, :project, :tracker], :conditions => s.conditions ).group_by(&:assigned_to) issues_by_assignee.each do |assignee, issues| UserMailer.reminder_mail(assignee, issues, @days).deliver if assignee && assignee.active? end end end