Initial Redmine tooling and local plugin forks
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
# This file is a part of Redmine CRM (redmine_contacts) plugin,
|
||||
# customer relationship management plugin for Redmine
|
||||
#
|
||||
# Copyright (C) 2010-2018 RedmineUP
|
||||
# http://www.redmineup.com/
|
||||
#
|
||||
# redmine_contacts is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# redmine_contacts is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with redmine_contacts. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
class ContactsMailer < ActionMailer::Base
|
||||
include Redmine::I18n
|
||||
|
||||
class UnauthorizedAction < StandardError; end
|
||||
class MissingInformation < StandardError; end
|
||||
|
||||
helper :application
|
||||
|
||||
attr_reader :email, :user
|
||||
|
||||
def self.default_url_options
|
||||
h = Setting.host_name
|
||||
h = h.to_s.gsub(%r{\/.*$}, '') unless Redmine::Utils.relative_url_root.blank?
|
||||
{ :host => h, :protocol => Setting.protocol }
|
||||
end
|
||||
|
||||
def bulk_mail(contact, params = {})
|
||||
raise l(:error_empty_email) if (contact.emails.empty? || params[:message].blank?)
|
||||
|
||||
@contact = contact
|
||||
@params = params
|
||||
|
||||
params[:attachments].each_value do |mail_attachment|
|
||||
if file = mail_attachment['file']
|
||||
file.rewind if file
|
||||
attachments[file.original_filename] = file.binread
|
||||
file.rewind if file
|
||||
elsif token = mail_attachment['token']
|
||||
if token.to_s =~ /^(\d+)\.([0-9a-f]+)$/
|
||||
attachment_id, attachment_digest = $1, $2
|
||||
if a = Attachment.where(:id => attachment_id, :digest => attachment_digest).first
|
||||
attachments[a.filename] = File.binread(a.diskfile)
|
||||
end
|
||||
end
|
||||
end
|
||||
end unless params[:attachments].blank?
|
||||
|
||||
mail(:from => params[:from] || User.current.mail,
|
||||
:to => contact.emails.first,
|
||||
:cc => params[:cc],
|
||||
:bcc => params[:bcc],
|
||||
:subject => params[:subject]) do |format|
|
||||
format.text
|
||||
format.html
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def self.receive(email, options={})
|
||||
@@contacts_mailer_options = options.dup
|
||||
super email
|
||||
end
|
||||
|
||||
# Processes incoming emails
|
||||
# Returns the created object (eg. an issue, a message) or false
|
||||
def receive(email)
|
||||
# debugger
|
||||
@email = email
|
||||
sender_email = email.from.to_a.first.to_s.strip
|
||||
# Ignore emails received from the application emission address to avoid hell cycles
|
||||
if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
|
||||
logger.info "ContactsMailHandler: ignoring email from Redmine emission address [#{sender_email}]" if logger && logger.info
|
||||
return false
|
||||
end
|
||||
@user = User.find_by_mail(sender_email) if sender_email.present?
|
||||
if @user.nil? || (@user && !@user.active?)
|
||||
logger.info "ContactsMailHandler: user not found [#{sender_email}]" if logger && logger.info
|
||||
end
|
||||
dispatch
|
||||
end
|
||||
|
||||
def dispatch
|
||||
deal_id = email.to.to_s.match(/.+\+d([0-9]*)/).to_a[1]
|
||||
deal_id ||= email.bcc.to_s.match(/.+\+d([0-9]*)/).to_a[1]
|
||||
deal_id ||= email.cc.to_s.match(/.+\+d([0-9]*)/).to_a[1]
|
||||
|
||||
if deal_id
|
||||
deal = Deal.find_by_id(deal_id)
|
||||
if deal
|
||||
return [*receive_deal_note(deal_id)]
|
||||
end
|
||||
end
|
||||
|
||||
contacts = []
|
||||
|
||||
if contacts.blank?
|
||||
contact_id = email.to.to_s.match(/.+\+c([0-9]*)/).to_a[1]
|
||||
contact_id ||= email.bcc.to_s.match(/.+\+c([0-9]*)/).to_a[1]
|
||||
contact_id ||= email.cc.to_s.match(/.+\+c([0-9]*)/).to_a[1]
|
||||
contacts = Contact.where(:id => contact_id)
|
||||
end
|
||||
|
||||
if contacts.blank?
|
||||
contacts = Contact.find_by_emails(email.to.to_a)
|
||||
end
|
||||
|
||||
if contacts.blank?
|
||||
from_key_words = get_keyword_locales(:label_crm_mail_from)
|
||||
@plain_text_body = plain_text_body.gsub(/^>\s*/, '').gsub('> ','').gsub('"', '"')
|
||||
full_address = plain_text_body.match(/^(#{from_key_words.join('|')})[ \s]*:[ \s]*(.+)\s*$/).to_a[2]
|
||||
|
||||
email_address = full_address.match(/[\w,\.,\-,\+]+@.+\.\w{2,}/) if full_address
|
||||
contacts = Contact.find_by_emails([email_address.to_s.strip]) if email_address
|
||||
end
|
||||
|
||||
if contacts.blank?
|
||||
return false
|
||||
end
|
||||
|
||||
raise MissingInformation if contacts.blank?
|
||||
|
||||
result = []
|
||||
contacts.each do |contact|
|
||||
result << receive_contact_note(contact.id)
|
||||
end
|
||||
result
|
||||
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
# TODO: send a email to the user
|
||||
logger.error e.message if logger
|
||||
false
|
||||
rescue MissingInformation => e
|
||||
logger.error "ContactsMailHandler: missing information from #{user}: #{e.message}" if logger
|
||||
false
|
||||
rescue UnauthorizedAction => e
|
||||
logger.error "ContactsMailHandler: unauthorized attempt from #{user}" if logger
|
||||
false
|
||||
end
|
||||
|
||||
# Receives a reply to a forum message
|
||||
def receive_contact_note(contact_id)
|
||||
contact = Contact.find_by_id(contact_id)
|
||||
note = nil
|
||||
# logger.error "ContactsMailHandler: receive_contact_note user: #{user},
|
||||
# contact: #{contact.name},
|
||||
# editable: #{contact.editable?(self.user)},
|
||||
# current: #{User.current}"
|
||||
raise UnauthorizedAction unless contact.editable?(self.user)
|
||||
if contact
|
||||
note = ContactNote.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
|
||||
:type_id => Note.note_types[:email],
|
||||
:content => plain_text_body,
|
||||
:created_on => email.date)
|
||||
note.author = self.user
|
||||
contact.notes << note
|
||||
add_attachments(note)
|
||||
logger.info note
|
||||
note.save
|
||||
contact.save
|
||||
end
|
||||
note
|
||||
end
|
||||
|
||||
def receive_deal_note(deal_id)
|
||||
deal = Deal.find_by_id(deal_id)
|
||||
note = nil
|
||||
# logger.error "ContactsMailHandler: receive_contact_note user: #{user},
|
||||
# contact: #{contact.name},
|
||||
# editable: #{contact.editable?(self.user)},
|
||||
# current: #{User.current}"
|
||||
raise UnauthorizedAction unless deal.editable?(self.user)
|
||||
if deal
|
||||
note = DealNote.new(:subject => email.subject.gsub(%r{^.*msg\d+\]}, '').strip,
|
||||
:type_id => Note.note_types[:email],
|
||||
:content => plain_text_body,
|
||||
:created_on => email.date)
|
||||
note.author = self.user
|
||||
deal.notes << note
|
||||
add_attachments(note)
|
||||
logger.info note
|
||||
note.save
|
||||
deal.save
|
||||
end
|
||||
note
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Destructively extracts the value for +attr+ in +text+
|
||||
# Returns nil if no matching keyword found
|
||||
def extract_keyword!(text, attr, format=nil)
|
||||
keys = [attr.to_s.humanize]
|
||||
if attr.is_a?(Symbol)
|
||||
keys << l("field_#{attr}", :default => '', :locale => user.language) if user && user.language.present?
|
||||
keys << l("field_#{attr}", :default => '', :locale => Setting.default_language) if Setting.default_language.present?
|
||||
end
|
||||
keys.reject! {|k| k.blank?}
|
||||
keys.collect! {|k| Regexp.escape(k)}
|
||||
format ||= '.+'
|
||||
text.gsub!(/^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i, '') # /^(От:)[ \t]*:[ \t]*(.+)\s*$/i
|
||||
$2 && $2.strip
|
||||
end
|
||||
|
||||
def add_attachments(obj)
|
||||
if email.attachments && email.attachments.any?
|
||||
email.attachments.each do |attachment|
|
||||
obj.attachments << Attachment.create(:container => obj,
|
||||
:file => attachment.decoded,
|
||||
:filename => attachment.filename,
|
||||
:author => user,
|
||||
:content_type => attachment.mime_type)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the text/plain part of the email
|
||||
# If not found (eg. HTML-only email), returns the body with tags removed
|
||||
def plain_text_body
|
||||
|
||||
return @plain_text_body unless @plain_text_body.nil?
|
||||
|
||||
part = email.text_part || email.html_part || email
|
||||
@plain_text_body = Redmine::CodesetUtil.to_utf8(part.body.decoded, part.charset)
|
||||
|
||||
# strip html tags and remove doctype directive
|
||||
@plain_text_body = ActionController::Base.helpers.strip_tags(@plain_text_body.strip) unless email.text_part
|
||||
@plain_text_body.sub! %r{^<!DOCTYPE .*$}, ''
|
||||
@plain_text_body
|
||||
|
||||
end
|
||||
|
||||
def get_keyword_locales(keyword)
|
||||
I18n.available_locales.collect{|lc| l(keyword, :locale => lc)}.uniq
|
||||
end
|
||||
|
||||
# Appends a Redmine header field (name is prepended with 'X-Redmine-')
|
||||
def redmine_headers(h)
|
||||
h.each { |k,v| headers["X-Redmine-#{k}"] = v }
|
||||
end
|
||||
|
||||
def initialize_defaults(method_name)
|
||||
super
|
||||
# Common headers
|
||||
headers 'X-Mailer' => 'Redmine Contacts',
|
||||
'X-Redmine-Host' => Setting.host_name,
|
||||
'X-Redmine-Site' => Setting.app_title
|
||||
end
|
||||
|
||||
def logger
|
||||
Rails.logger
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user