=begin rdoc Author:: Chris Hauboldt (mailto:biz@lnstar.com) Copyright:: 2009 Lone Star Internet Inc. Message is used in sending a message from an Mailing and keeping state of the message. Generates a "GUID" which is sent in the email and used to identify it in bounces. Statuses: pending - MailingJob has created the message but it has not yet been sent processing - MailingJob is in process of sending the message sent - MailingJob has successfully sent the message to the Email Server failed - either the message couldn't be handed to the Email Server or it has been bounced as a permanent failure =end module MailManager class Message < ActiveRecord::Base self.table_name = "#{MailManager.table_prefix}messages" belongs_to :mailing, :class_name => 'MailManager::Mailing', counter_cache: true belongs_to :subscription, :class_name => 'MailManager::Subscription' has_many :bounces, :class_name => 'MailManager::Bounce' belongs_to :contact, :class_name => 'MailManager::Contact' #FIXME: if we add more types ... change the base message to be MailingMessage or something and don't use #me as a message type default_scope :conditions => {:type => "MailManager::#{(self.class.name.eql?('Class') ? self.name : self.class.name).gsub(/^MailManager::/,'')}" } scope :pending, {:conditions => {:status => 'pending'}} scope :ready, :conditions => ["status=?", 'ready'] scope :sent, :conditions => ["status=?", 'sent'] scope :processing, :conditions => ["status=?", 'processing'] before_save :fix_counter_cache, if: lambda {|message| !message.new_record? && message.mailing_id_changed? } attr_protected :id def initialize(*args) super set_type end scope :search, lambda{|params| conditions = ["1=1"] if params[:mailing_id] conditions[0] += " AND #{MailManager.table_prefix}messages.mailing_id=?" conditions << params[:mailing_id] end if params[:status] conditions[0] += " AND #{MailManager.table_prefix}messages.status=?" conditions << params[:status] end { :conditions => conditions, :order => "#{MailManager.table_prefix}contacts.last_name, #{MailManager.table_prefix}contacts.first_name, #{MailManager.table_prefix}contacts.email_address", :joins => " INNER JOIN #{MailManager.table_prefix}contacts on #{MailManager.table_prefix}messages.contact_id=#{MailManager.table_prefix}contacts.id" }} include StatusHistory override_statuses(['pending','processing','sent','failed','ready'], 'pending') before_create :set_default_status after_create :generate_guid # returns a string in the form of "contact name" <email@example.com> if # the contact's full name returns anything ... or simply email@example.com if # there is no name at all def email_address_with_name return %Q|"#{full_name}" <#{email_address}>|.gsub(/\s+/,' ') unless full_name.eql?('') email_address end # sends the message through Mailer def deliver # lock only needed until status is updated Lock.with_lock("deliver_message_#{self.id}") do reload if can_deliver? change_status(:processing) else Rails.logger.warn "Message(#{self.id})'s is no longer suitable to deliver.. staus: #{status}" end end MailManager::Mailer.deliver_message(self) change_status(:sent) # allow other errors to bubble up rescue MailManager::LockException => e Rails.logger.warn "Locking error while trying to send MailManager::Message(#{id}) leaving in #{status} status" end # whether or not you can deliver a message def can_deliver? ['ready','pending'].include?(status) end # return a contact whether its deleted or not def active_or_deleted_contact @active_or_deleted_contact ||= self.contact || MailManager::Contact.unscoped. where(id: self.contact_id).first end # returns the contact's full name def full_name active_or_deleted_contact.try(:full_name) end # returns the contact's email address def email_address active_or_deleted_contact.try(:email_address) end # returns the mailings subject def subject mailing.subject end # the "From: " email address for the email # lazy sets the from email addres if not present from the mailing def from_email_address return self[:from_email_address] if self[:from_email_address].present? self.update_attribute(:from_email_address,mailing.from_email_address) self[:from_email_address] end # returns the separate mime parts of the message's Mailable def parts @parts ||= mailing.parts(substitutions) end # returns the contact's 'contactable' object tied to the contact def contactable active_or_deleted_contact.try(:contactable) end # returns a hash of substitutions to be used to modify the mailable's html/plaing text def substitutions substitutions_hash = {} MailManager::ContactableRegistry.registered_methods.each do |method| method_key = method.to_s.upcase if active_or_deleted_contact.respond_to?(method) substitutions_hash[method_key] = active_or_deleted_contact.send(method) elsif contactable.respond_to?(method) substitutions_hash[method_key] = contactable.send(method) else substitutions_hash[method_key] = '' end end substitutions_hash.merge('UNSUBSCRIBE_URL' => unsubscribe_url) end # the full url to unsubscribe based on this message; including site url & guid def unsubscribe_url "#{MailManager.site_url}#{MailManager.unsubscribe_path}/#{guid}" end # generated the guid for which the message is identified by in transit def generate_guid update_attribute(:guid, "#{active_or_deleted_contact.try(:id)}-#{subscription.try(:id)}-#{self.id}-#{Digest::SHA1.hexdigest("#{active_or_deleted_contact.try(:id)}-#{subscription.try(:id)}-#{self.id}-#{MailManager.secret}")}") end protected # nodoc: set the type on create def set_type self[:type] = self.class.name end def fix_counter_cache MailManager::Mailing.decrement_counter(:messages_count, self.mailing_id_was) if self.mailing_id_was.present? MailManager::Mailing.increment_counter(:messages_count, self.mailing_id) end end end