# == Schema Information # # Table name: outgoing_messages # # id :integer not null, primary key # info_request_id :integer not null # body :text not null # status :string(255) not null # message_type :string(255) not null # created_at :datetime not null # updated_at :datetime not null # last_sent_at :datetime # incoming_message_followup_id :integer # what_doing :string(255) not null # prominence :string(255) default("normal"), not null # prominence_reason :text # # models/outgoing_message.rb: # A message, associated with a request, from the user of the site to somebody # else. e.g. An initial request for information, or a complaint. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. # Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class OutgoingMessage < ActiveRecord::Base extend MessageProminence include Rails.application.routes.url_helpers include LinkToHelper self.default_url_options[:host] = AlaveteliConfiguration::domain # https links in emails if forcing SSL if AlaveteliConfiguration::force_ssl self.default_url_options[:protocol] = "https" end strip_attributes! has_prominence belongs_to :info_request validates_presence_of :info_request validates_inclusion_of :status, :in => ['ready', 'sent', 'failed'] validates_inclusion_of :message_type, :in => ['initial_request', 'followup' ] #, 'complaint'] validate :format_of_body belongs_to :incoming_message_followup, :foreign_key => 'incoming_message_followup_id', :class_name => 'IncomingMessage' # can have many events, for items which were resent by site admin e.g. if # contact address changed has_many :info_request_events # To override the default letter attr_accessor :default_letter # reindex if body text is edited (e.g. by admin interface) after_update :xapian_reindex_after_update def xapian_reindex_after_update if self.changes.include?('body') for info_request_event in self.info_request_events info_request_event.xapian_mark_needs_index end end end after_initialize :set_default_letter # How the default letter starts and ends def get_salutation ret = "" if self.message_type == 'followup' && !self.incoming_message_followup.nil? && !self.incoming_message_followup.safe_mail_from.nil? && self.incoming_message_followup.valid_to_reply_to? ret = ret + OutgoingMailer.name_for_followup(self.info_request, self.incoming_message_followup) else ret = ret + self.info_request.public_body.name end salutation = _("Dear {{public_body_name}},", :public_body_name => ret) end def get_signoff if self.message_type == 'followup' && !self.incoming_message_followup.nil? && !self.incoming_message_followup.safe_mail_from.nil? && self.incoming_message_followup.valid_to_reply_to? return _("Yours sincerely,") else return _("Yours faithfully,") end end def get_internal_review_insert_here_note return _("GIVE DETAILS ABOUT YOUR COMPLAINT HERE") end def get_default_letter if self.default_letter return self.default_letter end if self.what_doing == 'internal_review' _("Please pass this on to the person who conducts Freedom of Information reviews.") + "\n\n" + _("I am writing to request an internal review of {{public_body_name}}'s handling of my FOI request '{{info_request_title}}'.", :public_body_name => self.info_request.public_body.name, :info_request_title => self.info_request.title) + "\n\n\n\n [ " + self.get_internal_review_insert_here_note + " ] \n\n\n\n" + _("A full history of my FOI request and all correspondence is available on the Internet at this address: {{url}}", :url => request_url(self.info_request)) + "\n" else "" end end def get_default_message get_salutation + "\n\n" + get_default_letter + "\n\n" + get_signoff + "\n\n" end def set_signature_name(name) # XXX We use raw_body here to get unstripped one if self.raw_body == self.get_default_message self.body = self.raw_body + name end end def body ret = read_attribute(:body) if ret.nil? return ret end ret = ret.dup ret.strip! ret.gsub!(/(?:\n\s*){2,}/, "\n\n") # remove excess linebreaks that unnecessarily space it out # Remove things from censor rules if !self.info_request.nil? self.info_request.apply_censor_rules_to_text!(ret) end ret end def raw_body read_attribute(:body) end # Used to give warnings when writing new messages def contains_email? MySociety::Validate.email_find_regexp.match(self.body) end def contains_postcode? MySociety::Validate.contains_postcode?(self.body) end # Deliver outgoing message # Note: You can test this from script/console with, say: # InfoRequest.find(1).outgoing_messages[0].send_message def send_message(log_event_type = 'sent') if self.status == 'ready' if self.message_type == 'initial_request' self.last_sent_at = Time.now self.status = 'sent' self.save! mail_message = OutgoingMailer.initial_request(self.info_request, self).deliver self.info_request.log_event(log_event_type, { :email => mail_message.to_addrs.join(", "), :outgoing_message_id => self.id, :smtp_message_id => mail_message.message_id }) self.info_request.set_described_state('waiting_response') elsif self.message_type == 'followup' self.last_sent_at = Time.now self.status = 'sent' self.save! mail_message = OutgoingMailer.followup(self.info_request, self, self.incoming_message_followup).deliver self.info_request.log_event('followup_' + log_event_type, { :email => mail_message.to_addrs.join(", "), :outgoing_message_id => self.id, :smtp_message_id => mail_message.message_id }) if self.info_request.described_state == 'waiting_clarification' self.info_request.set_described_state('waiting_response') end if self.what_doing == 'internal_review' self.info_request.set_described_state('internal_review') end else raise "Message id #{self.id} has type '#{self.message_type}' which send_message can't handle" end elsif self.status == 'sent' raise "Message id #{self.id} has already been sent" else raise "Message id #{self.id} not in state for send_message" end end # An admin function def resend_message if ['initial_request', 'followup'].include?(self.message_type) and self.status == 'sent' self.status = 'ready' send_message('resent') else raise "Message id #{self.id} has type '#{self.message_type}' status '#{self.status}' which resend_message can't handle" end end # Returns the text to quote the original message when sending this one def quoted_part_to_append_to_email if self.message_type == 'followup' && !self.incoming_message_followup.nil? return "\n\n-----Original Message-----\n\n" + self.incoming_message_followup.get_body_for_quoting + "\n" else return "" end end # We hide emails from display in outgoing messages. def remove_privacy_sensitive_things!(text) text.gsub!(MySociety::Validate.email_find_regexp, "[email address]") end # Returns text for indexing / text display def get_text_for_indexing(strip_salutation=true) text = self.body.strip # Remove salutation text.sub!(/Dear .+,/, "") if strip_salutation # Remove email addresses from display/index etc. self.remove_privacy_sensitive_things!(text) return text end # Return body for display as HTML def get_body_for_html_display text = self.body.strip self.remove_privacy_sensitive_things!(text) text = MySociety::Format.wrap_email_body_by_lines(text) # reparagraph and wrap it so is good preview of emails text = CGI.escapeHTML(text) text = MySociety::Format.make_clickable(text, :contract => 1) text.gsub!(/\[(email address|mobile number)\]/, '[\1]') text = text.gsub(/\n/, '
') return text.html_safe end # Return body for display as text def get_body_for_text_display get_text_for_indexing(strip_salutation=false) end def fully_destroy ActiveRecord::Base.transaction do info_request_event = InfoRequestEvent.find_by_outgoing_message_id(self.id) info_request_event.track_things_sent_emails.each { |a| a.destroy } info_request_event.user_info_request_sent_alerts.each { |a| a.destroy } info_request_event.destroy self.destroy end end after_save(:purge_in_cache) def purge_in_cache self.info_request.purge_in_cache end def for_admin_column self.class.content_columns.each do |column| yield(column.human_name, self.send(column.name), column.type.to_s, column.name) end end private def set_default_letter if self.body.nil? self.body = get_default_message end end def format_of_body if self.body.empty? || self.body =~ /\A#{Regexp.escape(get_salutation)}\s+#{Regexp.escape(get_signoff)}/ || self.body =~ /#{Regexp.escape(get_internal_review_insert_here_note)}/ if self.message_type == 'followup' if self.what_doing == 'internal_review' errors.add(:body, _("Please give details explaining why you want a review")) else errors.add(:body, _("Please enter your follow up message")) end elsif errors.add(:body, _("Please enter your letter requesting information")) else raise "Message id #{self.id} has type '#{self.message_type}' which validate can't handle" end end if self.body =~ /#{get_signoff}\s*\Z/m errors.add(:body, _("Please sign at the bottom with your name, or alter the \"{{signoff}}\" signature", :signoff => get_signoff)) end if !MySociety::Validate.uses_mixed_capitals(self.body) errors.add(:body, _('Please write your message using a mixture of capital and lower case letters. This makes it easier for others to read.')) end if self.what_doing.nil? || !['new_information', 'internal_review', 'normal_sort'].include?(self.what_doing) errors.add(:what_doing_dummy, _('Please choose what sort of reply you are making.')) end end end