diff options
Diffstat (limited to 'app/models')
28 files changed, 264 insertions, 1192 deletions
diff --git a/app/models/about_me_validator.rb b/app/models/about_me_validator.rb index 8ee505ac8..7df70fb61 100644 --- a/app/models/about_me_validator.rb +++ b/app/models/about_me_validator.rb @@ -1,25 +1,23 @@ -# == Schema Information -# Schema version: 114 -# -# Table name: about_me_validators -# -# about_me :text default("I..."), not null -# - # models/about_me_validator.rb: # Validates editing about me text on user profile pages. # # Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ -class AboutMeValidator < ActiveRecord::BaseWithoutTable - strip_attributes! +class AboutMeValidator + include ActiveModel::Validations - column :about_me, :text, "I...", false + attr_accessor :about_me # TODO: Switch to built in validations validate :length_of_about_me + def initialize(attributes = {}) + attributes.each do |name, value| + send("#{name}=", value) + end + end + private def length_of_about_me diff --git a/app/models/application_mailer.rb b/app/models/application_mailer.rb deleted file mode 100644 index 1a97a4bf9..000000000 --- a/app/models/application_mailer.rb +++ /dev/null @@ -1,164 +0,0 @@ -# models/application_mailer.rb: -# Shared code between different mailers. -# -# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -require 'action_mailer/version' -class ApplicationMailer < ActionMailer::Base - # Include all the functions views get, as emails call similar things. - helper :application - include MailerHelper - - # This really should be the default - otherwise you lose any information - # about the errors, and have to do error checking on return codes. - self.raise_delivery_errors = true - - def blackhole_email - Configuration::blackhole_prefix+"@"+Configuration::incoming_email_domain - end - - # URL generating functions are needed by all controllers (for redirects), - # views (for links) and mailers (for use in emails), so include them into - # all of all. - include LinkToHelper - - # Site-wide access to configuration settings - include ConfigHelper - - # Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer - # will be initialized according to the named method. If not, the mailer will - # remain uninitialized (useful when you only need to invoke the "receive" - # method, for instance). - def initialize(method_name=nil, *parameters) #:nodoc: - create!(method_name, *parameters) if method_name - end - - # For each multipart template (e.g. "the_template_file.text.html.erb") available, - # add the one from the view path with the highest priority as a part to the mail - def render_multipart_templates - added_content_types = {} - self.view_paths.each do |view_path| - Dir.glob("#{view_path}/#{mailer_name}/#{@template}.*").each do |path| - template = view_path["#{mailer_name}/#{File.basename(path)}"] - - # Skip unless template has a multipart format - next unless template && template.multipart? - next if added_content_types[template.content_type] == true - @parts << Part.new( - :content_type => template.content_type, - :disposition => "inline", - :charset => charset, - :body => render_message(template, @body) - ) - added_content_types[template.content_type] = true - end - end - end - - # Look for the current template in each element of view_paths in order, - # return the first - def find_template - self.view_paths.each do |view_path| - if template = view_path["#{mailer_name}/#{@template}"] - return template - end - end - return nil - end - - if ActionMailer::VERSION::MAJOR == 2 - - # This method is a customised version of ActionMailer::Base.create! - # modified to allow templates to be selected correctly for multipart - # mails when themes have added to the view_paths. The problem from our - # point of view with ActionMailer::Base is that it sets template_root to - # the first element of the view_paths array and then uses only that (directly - # and via template_path, which is created from it) in the create! method when - # looking for templates. Our modified version looks for templates in the view_paths - # in order. - - # It also has a line converting the mail subject to a string. This is because we - # use translated strings in the subject lines, sometimes in conjunction with - # user input, like request titles. The _() function used for translation - # returns an instance of SafeBuffer, which doesn't handle gsub calls in the block form - # with $ variables - https://github.com/rails/rails/issues/1555. - # Unfortunately ActionMailer uses that form in quoted_printable(), which will be - # called if any part of the subject requires quoting. So we convert the subject - # back to a string via to_str() before passing in to create_mail. There is a test - # for this in spec/models/request_mailer_spec.rb - - # Changed lines marked with *** - - # Initialize the mailer via the given +method_name+. The body will be - # rendered and a new TMail::Mail object created. - def create!(method_name, *parameters) #:nodoc: - initialize_defaults(method_name) - __send__(method_name, *parameters) - - # If an explicit, textual body has not been set, we check assumptions. - unless String === @body - # First, we look to see if there are any likely templates that match, - # which include the content-type in their file name (i.e., - # "the_template_file.text.html.erb", etc.). Only do this if parts - # have not already been specified manually. - if @parts.empty? - # *** render_multipart_templates replaces the following code - # Dir.glob("#{template_path}/#{@template}.*").each do |path| - # template = template_root["#{mailer_name}/#{File.basename(path)}"] - # - # # Skip unless template has a multipart format - # next unless template && template.multipart? - # - # @parts << Part.new( - # :content_type => template.content_type, - # :disposition => "inline", - # :charset => charset, - # :body => render_message(template, @body) - # ) - # end - render_multipart_templates - - unless @parts.empty? - @content_type = "multipart/alternative" if @content_type !~ /^multipart/ - @parts = sort_parts(@parts, @implicit_parts_order) - end - end - - # Then, if there were such templates, we check to see if we ought to - # also render a "normal" template (without the content type). If a - # normal template exists (or if there were no implicit parts) we render - # it. - template_exists = @parts.empty? - - # *** find_template replaces template_root call - # template_exists ||= template_root["#{mailer_name}/#{@template}"] - template_exists ||= find_template - - @body = render_message(@template, @body) if template_exists - - # Finally, if there are other message parts and a textual body exists, - # we shift it onto the front of the parts and set the body to nil (so - # that create_mail doesn't try to render it in addition to the parts). - if !@parts.empty? && String === @body - @parts.unshift ActionMailer::Part.new(:charset => charset, :body => @body) - @body = nil - end - end - - # If this is a multipart e-mail add the mime_version if it is not - # already set. - @mime_version ||= "1.0" if !@parts.empty? - - # *** Convert into a string - @subject = @subject.to_str if @subject - - # build the mail object itself - @mail = create_mail - end - else - raise "ApplicationMailer.create! is obsolete - find another way to ensure that themes can override mail templates for multipart mails" - end - -end - diff --git a/app/models/censor_rule.rb b/app/models/censor_rule.rb index f40ab6fbb..f0d06e088 100644 --- a/app/models/censor_rule.rb +++ b/app/models/censor_rule.rb @@ -20,7 +20,7 @@ # Stores alterations to remove specific data from requests. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class CensorRule < ActiveRecord::Base belongs_to :info_request @@ -33,13 +33,15 @@ class CensorRule < ActiveRecord::Base validate :require_valid_regexp, :if => proc{ |rule| rule.regexp? == true } validates_presence_of :text - named_scope :global, {:conditions => {:info_request_id => nil, - :user_id => nil, - :public_body_id => nil}} + scope :global, {:conditions => {:info_request_id => nil, + :user_id => nil, + :public_body_id => nil}} def require_user_request_or_public_body if self.info_request.nil? && self.user.nil? && self.public_body.nil? - errors.add("Censor must apply to an info request a user or a body; ") + [:info_request, :user, :public_body].each do |a| + errors.add(a, "Rule must apply to an info request, a user or a body") + end end end diff --git a/app/models/change_email_validator.rb b/app/models/change_email_validator.rb index 2ddebb177..5cc13d4c2 100644 --- a/app/models/change_email_validator.rb +++ b/app/models/change_email_validator.rb @@ -1,36 +1,27 @@ -# == Schema Information -# Schema version: 114 -# -# Table name: change_email_validators -# -# old_email :string -# new_email :string -# password :string -# user_circumstance :string -# - # models/changeemail_validator.rb: # Validates email change form submissions. # # Copyright (c) 2010 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -class ChangeEmailValidator < ActiveRecord::BaseWithoutTable - strip_attributes! +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ - column :old_email, :string - column :new_email, :string - column :password, :string - column :user_circumstance, :string +class ChangeEmailValidator + include ActiveModel::Validations - attr_accessor :logged_in_user + attr_accessor :old_email, :new_email, :password, :user_circumstance, :logged_in_user validates_presence_of :old_email, :message => N_("Please enter your old email address") validates_presence_of :new_email, :message => N_("Please enter your new email address") validates_presence_of :password, :message => N_("Please enter your password"), :unless => :changing_email validate :password_and_format_of_email - def changing_email() + def initialize(attributes = {}) + attributes.each do |name, value| + send("#{name}=", value) + end + end + + + def changing_email self.user_circumstance == 'change_email' end @@ -41,11 +32,11 @@ class ChangeEmailValidator < ActiveRecord::BaseWithoutTable errors.add(:old_email, _("Old email doesn't look like a valid address")) end - if !errors[:old_email] + if errors[:old_email].blank? if self.old_email.downcase != self.logged_in_user.email.downcase errors.add(:old_email, _("Old email address isn't the same as the address of the account you are logged in with")) elsif (!self.changing_email) && (!self.logged_in_user.has_this_password?(self.password)) - if !errors[:password] + if errors[:password].blank? errors.add(:password, _("Password is not correct")) end end @@ -55,5 +46,4 @@ class ChangeEmailValidator < ActiveRecord::BaseWithoutTable errors.add(:new_email, _("New email doesn't look like a valid address")) end end - end diff --git a/app/models/comment.rb b/app/models/comment.rb index 70f3ba00d..9527030a9 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -18,7 +18,7 @@ # A comment by a user upon something. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class Comment < ActiveRecord::Base strip_attributes! diff --git a/app/models/contact_mailer.rb b/app/models/contact_mailer.rb deleted file mode 100644 index abde64928..000000000 --- a/app/models/contact_mailer.rb +++ /dev/null @@ -1,56 +0,0 @@ -# models/contact_mailer.rb: -# Sends contact form mails. -# -# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -class ContactMailer < ApplicationMailer - - # Send message to administrator - def to_admin_message(name, email, subject, message, logged_in_user, last_request, last_body) - @from = name + " <" + email + ">" - @recipients = contact_from_name_and_email - @subject = subject - @body = { :message => message, - :logged_in_user => logged_in_user , - :last_request => last_request, - :last_body => last_body - } - end - - # We always set Reply-To when we set Return-Path to be different from From, - # since some email clients seem to erroneously use the envelope from when - # they shouldn't, and this might help. (Have had mysterious cases of a - # reply coming in duplicate from a public body to both From and envelope - # from) - - # Send message to another user - def user_message(from_user, recipient_user, from_user_url, subject, message) - @from = from_user.name_and_email - # Do not set envelope from address to the from_user, so they can't get - # someone's email addresses from transitory bounce messages. - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from - @recipients = recipient_user.name_and_email - @subject = subject - @body = { - :message => message, - :from_user => from_user, - :recipient_user => recipient_user, - :from_user_url => from_user_url - } - end - - # Send message to a user from the administrator - def from_admin_message(recipient_user, subject, message) - @from = contact_from_name_and_email - @recipients = recipient_user.name_and_email - @subject = subject - @body = { - :message => message, - :from_user => @from, - :recipient_user => recipient_user, - } - bcc Configuration::contact_email - end - -end diff --git a/app/models/contact_validator.rb b/app/models/contact_validator.rb index d277161f9..65e539669 100644 --- a/app/models/contact_validator.rb +++ b/app/models/contact_validator.rb @@ -1,35 +1,29 @@ -# == Schema Information -# Schema version: 114 -# -# Table name: contact_validators -# -# name :string -# email :string -# subject :text -# message :text -# - # models/contact_validator.rb: # Validates contact form submissions. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ -class ContactValidator < ActiveRecord::BaseWithoutTable - strip_attributes! +class ContactValidator + include ActiveModel::Validations - column :name, :string - column :email, :string - column :subject, :text - column :message, :text + attr_accessor :name, :email, :subject, :message validates_presence_of :name, :message => N_("Please enter your name") validates_presence_of :email, :message => N_("Please enter your email address") validates_presence_of :subject, :message => N_("Please enter a subject") validates_presence_of :message, :message => N_("Please enter the message you want to send") + validate :email_format - def validate - errors.add(:email, _("Email doesn't look like a valid address")) unless MySociety::Validate.is_valid_email(self.email) + def initialize(attributes = {}) + attributes.each do |name, value| + send("#{name}=", value) + end end + private + + def email_format + errors.add(:email, _("Email doesn't look like a valid address")) unless MySociety::Validate.is_valid_email(self.email) + end end diff --git a/app/models/foi_attachment.rb b/app/models/foi_attachment.rb index bba0b6a8d..fcde379e0 100644 --- a/app/models/foi_attachment.rb +++ b/app/models/foi_attachment.rb @@ -20,7 +20,7 @@ # An attachment to an email (IncomingMessage) # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ # This is the type which is used to send data about attachments to the view require 'digest' @@ -38,11 +38,7 @@ class FoiAttachment < ActiveRecord::Base BODY_MAX_DELAY = 5 def directory - rails_env = Rails.env - if rails_env.nil? || rails_env.empty? - raise "$RAILS_ENV is not set" - end - base_dir = File.expand_path(File.join(File.dirname(__FILE__), "../../cache", "attachments_#{rails_env}")) + base_dir = File.expand_path(File.join(File.dirname(__FILE__), "../../cache", "attachments_#{Rails.env}")) return File.join(base_dir, self.hexdigest[0..2]) end @@ -67,22 +63,9 @@ class FoiAttachment < ActiveRecord::Base file.write d } update_display_size! - encode_cached_body! @cached_body = d end - # If the original mail part had a charset, it's some kind of string, so assume that - # it should be handled as a string in the stated charset, not a bytearray, and then - # convert it our default encoding. For ruby 1.8 this is a noop. - def encode_cached_body! - if RUBY_VERSION.to_f >= 1.9 - if charset - @cached_body.force_encoding(charset) - @cached_body = @cached_body.encode(Encoding.default_internal, charset) - end - end - end - def body if @cached_body.nil? tries = 0 @@ -103,7 +86,6 @@ class FoiAttachment < ActiveRecord::Base self.incoming_message.parse_raw_email!(force) retry end - encode_cached_body! end return @cached_body end diff --git a/app/models/holiday.rb b/app/models/holiday.rb index 13258396a..98f73e963 100644 --- a/app/models/holiday.rb +++ b/app/models/holiday.rb @@ -19,7 +19,7 @@ # -- Freedom of Information Act 2000 section 10 # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class Holiday < ActiveRecord::Base diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb index 5c845ead3..c914edb7e 100644 --- a/app/models/incoming_message.rb +++ b/app/models/incoming_message.rb @@ -25,7 +25,7 @@ # response from the public body. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ # TODO # Move some of the (e.g. quoting) functions here into rblib, as they feel @@ -37,7 +37,7 @@ require 'rexml/document' require 'zip/zip' require 'mapi/msg' require 'mapi/convert' - +require 'iconv' unless RUBY_VERSION >= '1.9' class IncomingMessage < ActiveRecord::Base belongs_to :info_request @@ -193,7 +193,7 @@ class IncomingMessage < ActiveRecord::Base text.gsub!(self.info_request.public_body.request_email, _("[{{public_body}} request email]", :public_body => self.info_request.public_body.short_or_long_name)) end text.gsub!(self.info_request.incoming_email, _('[FOI #{{request}} email]', :request => self.info_request.id.to_s) ) - text.gsub!(Configuration::contact_email, _("[{{site_name}} contact email]", :site_name => Configuration::site_name) ) + text.gsub!(AlaveteliConfiguration::contact_email, _("[{{site_name}} contact email]", :site_name => AlaveteliConfiguration::site_name) ) end # Replaces all email addresses in (possibly binary data) with equal length alternative ones. @@ -219,7 +219,7 @@ class IncomingMessage < ActiveRecord::Base if censored_uncompressed_text != uncompressed_text # then use the altered file (recompressed) recompressed_text = nil - if Configuration::use_ghostscript_compression == true + if AlaveteliConfiguration::use_ghostscript_compression == true command = ["gs", "-sDEVICE=pdfwrite", "-dCompatibilityLevel=1.4", "-dPDFSETTINGS=/screen", "-dNOPAUSE", "-dQUIET", "-dBATCH", "-sOutputFile=-", "-"] else command = ["pdftk", "-", "output", "-", "compress"] @@ -258,11 +258,21 @@ class IncomingMessage < ActiveRecord::Base # equivalents to the UCS-2 ascii_chars = text.gsub(/\0/, "") emails = ascii_chars.scan(MySociety::Validate.email_find_regexp) + # Convert back to UCS-2, making a mask at the same time - emails.map! {|email| [ - Iconv.conv('ucs-2le', 'ascii', email[0]), - Iconv.conv('ucs-2le', 'ascii', email[0].gsub(/[^@.]/, 'x')) - ] } + if RUBY_VERSION >= '1.9' + emails.map! do |email| + # We want the ASCII representation of UCS-2 + [email[0].encode('UTF-16LE').force_encoding('US-ASCII'), + email[0].gsub(/[^@.]/, 'x').encode('UTF-16LE').force_encoding('US-ASCII')] + end + else + emails.map! {|email| [ + Iconv.conv('ucs-2le', 'ascii', email[0]), + Iconv.conv('ucs-2le', 'ascii', email[0].gsub(/[^@.]/, 'x')) + ] } + end + # Now search and replace the UCS-2 email with the UCS-2 mask for email, mask in emails text.gsub!(email, mask) @@ -316,7 +326,7 @@ class IncomingMessage < ActiveRecord::Base text.gsub!(/(Mobile|Mob)([\s\/]*(Fax|Tel))*\s*:?[\s\d]*\d/, "[mobile number]") # Remove WhatDoTheyKnow signup links - text.gsub!(/http:\/\/#{Configuration::domain}\/c\/[^\s]+/, "[WDTK login link]") + text.gsub!(/http:\/\/#{AlaveteliConfiguration::domain}\/c\/[^\s]+/, "[WDTK login link]") # Remove things from censor rules self.info_request.apply_censor_rules_to_text!(text) @@ -534,7 +544,7 @@ class IncomingMessage < ActiveRecord::Base source_charset = 'utf-8' if source_charset.nil? text = Iconv.conv('utf-8//IGNORE', source_charset, text) + _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]", - :site_name => Configuration::site_name) + :site_name => AlaveteliConfiguration::site_name) rescue Iconv::InvalidEncoding, Iconv::IllegalSequence, Iconv::InvalidCharacter if source_charset != "utf-8" source_charset = "utf-8" @@ -605,7 +615,7 @@ class IncomingMessage < ActiveRecord::Base content_type = 'application/octet-stream' end hexdigest = Digest::MD5.hexdigest(content) - attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => hexdigest) + attachment = self.foi_attachments.find_or_create_by_hexdigest(hexdigest) attachment.update_attributes(:filename => filename, :content_type => content_type, :body => content, @@ -632,14 +642,14 @@ class IncomingMessage < ActiveRecord::Base attachment_attributes = MailHandler.get_attachment_attributes(self.mail(force)) attachments = [] attachment_attributes.each do |attrs| - attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => attrs[:hexdigest]) - body = attrs.delete(:body) + attachment = self.foi_attachments.find_or_create_by_hexdigest(attrs[:hexdigest]) attachment.update_attributes(attrs) - # Set the body separately as its handling can depend on the value of charset - attachment.body = body attachment.save! attachments << attachment.id end + # Reload to refresh newly created foi_attachments + self.reload + main_part = get_main_body_text_part # we don't use get_main_body_text_internal, as we want to avoid charset # conversions, since /usr/bin/uudecode needs to deal with those. @@ -748,9 +758,15 @@ class IncomingMessage < ActiveRecord::Base attachment.body, attachment.charset) end + # Remove any bad characters - text = Iconv.conv('utf-8//IGNORE', 'utf-8', text) - return text + if RUBY_VERSION >= '1.9' + text.encode("utf-8", :invalid => :replace, + :undef => :replace, + :replace => "") + else + Iconv.conv('utf-8//IGNORE', 'utf-8', text) + end end diff --git a/app/models/info_request.rb b/app/models/info_request.rb index 237364f56..156399b99 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -26,8 +26,7 @@ require 'digest/sha1' class InfoRequest < ActiveRecord::Base - include ActionView::Helpers::UrlHelper - include ActionController::UrlWriter + include Rails.application.routes.url_helpers strip_attributes! @@ -51,7 +50,7 @@ class InfoRequest < ActiveRecord::Base has_tag_string - named_scope :visible, :conditions => {:prominence => "normal"} + scope :visible, :conditions => {:prominence => "normal"} # user described state (also update in info_request_event, admin_request/edit.rhtml) validate :must_be_valid_state @@ -81,6 +80,11 @@ class InfoRequest < ActiveRecord::Base 'blackhole' # just dump them ] + # only check on create, so existing models with mixed case are allowed + validate :title_formatting, :on => :create + + after_initialize :set_defaults + def self.enumerate_states states = [ 'waiting_response', @@ -148,7 +152,7 @@ class InfoRequest < ActiveRecord::Base @@custom_states_loaded = false begin - if ENV["RAILS_ENV"] != "test" + if !Rails.env.test? require 'customstates' include InfoRequestCustomStates @@custom_states_loaded = true @@ -456,7 +460,7 @@ public if !allow if self.handle_rejected_responses == 'bounce' - RequestMailer.deliver_stopped_responses(self, email, raw_email_data) if !is_external? + RequestMailer.stopped_responses(self, email, raw_email_data).deliver if !is_external? elsif self.handle_rejected_responses == 'holding_pen' InfoRequest.holding_pen_request.receive(email, raw_email_data, false, reason) elsif self.handle_rejected_responses == 'blackhole' @@ -484,13 +488,13 @@ public self.awaiting_description = true params = { :incoming_message_id => incoming_message.id } if !rejected_reason.empty? - params[:rejected_reason] = rejected_reason + params[:rejected_reason] = rejected_reason.to_str end self.log_event("response", params) self.save! end self.info_request_events.each { |event| event.xapian_mark_needs_index } # for the "waiting_classification" index - RequestMailer.deliver_new_response(self, incoming_message) if !is_external? + RequestMailer.new_response(self, incoming_message).deliver if !is_external? end @@ -543,6 +547,10 @@ public # states which require administrator action (hence email administrators # when they are entered, and offer state change dialog to them) + def InfoRequest.requires_admin_states + return ['requires_admin', 'error_message', 'attention_requested'] + end + def requires_admin? ['requires_admin', 'error_message', 'attention_requested'].include?(described_state) end @@ -564,7 +572,7 @@ public if self.requires_admin? # Check there is someone to send the message "from" if !set_by.nil? || !self.user.nil? - RequestMailer.deliver_requires_admin(self, set_by, message) + RequestMailer.requires_admin(self, set_by, message).deliver end end @@ -579,7 +587,7 @@ public RequestClassification.create!(:user_id => set_by.id, :info_request_event_id => event.id) - RequestMailer.deliver_old_unclassified_updated(self) if !is_external? + RequestMailer.old_unclassified_updated(self).deliver if !is_external? end end @@ -694,7 +702,7 @@ public # last_event_forming_initial_request. There may be more obscure # things, e.g. fees, not properly covered. def date_response_required_by - Holiday.due_date_from(self.date_initial_request_last_sent_at, Configuration::reply_late_after_days, Configuration::working_or_calendar_days) + Holiday.due_date_from(self.date_initial_request_last_sent_at, AlaveteliConfiguration::reply_late_after_days, AlaveteliConfiguration::working_or_calendar_days) end # This is a long stop - even with UK public interest test extensions, 40 # days is a very long time. @@ -702,10 +710,10 @@ public last_sent = last_event_forming_initial_request if self.public_body.is_school? # schools have 60 working days maximum (even over a long holiday) - Holiday.due_date_from(self.date_initial_request_last_sent_at, Configuration::special_reply_very_late_after_days, Configuration::working_or_calendar_days) + Holiday.due_date_from(self.date_initial_request_last_sent_at, AlaveteliConfiguration::special_reply_very_late_after_days, AlaveteliConfiguration::working_or_calendar_days) else # public interest test ICO guidance gives 40 working maximum - Holiday.due_date_from(self.date_initial_request_last_sent_at, Configuration::reply_very_late_after_days, Configuration::working_or_calendar_days) + Holiday.due_date_from(self.date_initial_request_last_sent_at, AlaveteliConfiguration::reply_very_late_after_days, AlaveteliConfiguration::working_or_calendar_days) end end @@ -791,6 +799,16 @@ public end end + # Returns last event + def get_last_event + events = self.info_request_events + if events.size == 0 + return nil + else + return events[-1] + end + end + # Get previous email sent to def get_previous_email_sent_to(info_request_event) last_email = nil @@ -867,10 +885,10 @@ public end def InfoRequest.magic_email_for_id(prefix_part, id) - magic_email = Configuration::incoming_email_prefix + magic_email = AlaveteliConfiguration::incoming_email_prefix magic_email += prefix_part + id.to_s magic_email += "-" + InfoRequest.hash_from_id(id) - magic_email += "@" + Configuration::incoming_email_domain + magic_email += "@" + AlaveteliConfiguration::incoming_email_domain return magic_email end @@ -881,7 +899,7 @@ public end def InfoRequest.hash_from_id(id) - return Digest::SHA1.hexdigest(id.to_s + Configuration::incoming_email_secret)[0,8] + return Digest::SHA1.hexdigest(id.to_s + AlaveteliConfiguration::incoming_email_secret)[0,8] end # Called by find_by_incoming_email - and used to be called by separate @@ -1104,7 +1122,7 @@ public before_save :purge_in_cache def purge_in_cache - if !Configuration::varnish_host.blank? && !self.id.nil? + if !AlaveteliConfiguration::varnish_host.blank? && !self.id.nil? # we only do this for existing info_requests (new ones have a nil id) path = url_for(:controller => 'request', :action => 'show', :url_title => self.url_title, :only_path => true, :locale => :none) req = PurgeRequest.find_by_url(path) @@ -1122,5 +1140,34 @@ public yield(column.human_name, self.send(column.name), column.type.to_s, column.name) end end + + private + + def set_defaults + begin + if self.described_state.nil? + self.described_state = 'waiting_response' + end + rescue ActiveModel::MissingAttributeError + # this should only happen on Model.exists?() call. It can be safely ignored. + # See http://www.tatvartha.com/2011/03/activerecordmissingattributeerror-missing-attribute-a-bug-or-a-features/ + end + # FOI or EIR? + if !self.public_body.nil? && self.public_body.eir_only? + self.law_used = 'eir' + end + end + + def title_formatting + if !self.title.nil? && !MySociety::Validate.uses_mixed_capitals(self.title, 10) + errors.add(:title, _('Please write the summary using a mixture of capital and lower case letters. This makes it easier for others to read.')) + end + if !self.title.nil? && title.size > 200 + errors.add(:title, _('Please keep the summary short, like in the subject of an email. You can use a phrase, rather than a full sentence.')) + end + if !self.title.nil? && self.title =~ /^(FOI|Freedom of Information)\s*requests?$/i + errors.add(:title, _('Please describe more what the request is about in the subject. There is no need to say it is an FOI request, we add that on anyway.')) + end + end end diff --git a/app/models/info_request_event.rb b/app/models/info_request_event.rb index 871b81b1f..469aabc4a 100644 --- a/app/models/info_request_event.rb +++ b/app/models/info_request_event.rb @@ -20,7 +20,7 @@ # models/info_request_event.rb: # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class InfoRequestEvent < ActiveRecord::Base belongs_to :info_request @@ -405,7 +405,7 @@ class InfoRequestEvent < ActiveRecord::Base :comment_id => self.comment_id, # XXX would be nice to add links here, but alas the - # code to make them is in views only. See views/request/details.rhtml + # code to make them is in views only. See views/request/details.html.erb # perhaps can call with @template somehow } diff --git a/app/models/mail_server_log.rb b/app/models/mail_server_log.rb index 755584b90..7f61377ce 100644 --- a/app/models/mail_server_log.rb +++ b/app/models/mail_server_log.rb @@ -15,9 +15,7 @@ # We load log file lines for requests in here, for display in the admin interface. # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ -# -# $Id: exim_log.rb,v 1.14 2009-09-17 21:10:05 francis Exp $ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class MailServerLog < ActiveRecord::Base belongs_to :info_request @@ -53,7 +51,7 @@ class MailServerLog < ActiveRecord::Base done.save! f = is_gz ? Zlib::GzipReader.open(file_name) : File.open(file_name, 'r') - case(Configuration::mta_log_type.to_sym) + case(AlaveteliConfiguration::mta_log_type.to_sym) when :exim load_exim_log_data(f, done) when :postfix @@ -123,13 +121,13 @@ class MailServerLog < ActiveRecord::Base # We also check the email prefix so that we could, for instance, separately handle a staging and production # instance running on the same server with different email prefixes. def MailServerLog.email_addresses_on_line(line) - prefix = Regexp::quote(Configuration::incoming_email_prefix) - domain = Regexp::quote(Configuration::incoming_email_domain) + prefix = Regexp::quote(AlaveteliConfiguration::incoming_email_prefix) + domain = Regexp::quote(AlaveteliConfiguration::incoming_email_domain) line.scan(/#{prefix}request-[^\s]+@#{domain}/).sort.uniq end def MailServerLog.request_sent?(ir) - case(Configuration::mta_log_type.to_sym) + case(AlaveteliConfiguration::mta_log_type.to_sym) when :exim request_exim_sent?(ir) when :postfix diff --git a/app/models/mail_server_log_done.rb b/app/models/mail_server_log_done.rb index 3fb20f0b3..0e7e9eec3 100644 --- a/app/models/mail_server_log_done.rb +++ b/app/models/mail_server_log_done.rb @@ -13,7 +13,7 @@ # Stores that a particular mail server log file has been loaded in, see mail_server_log.rb # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class MailServerLogDone < ActiveRecord::Base has_many :mail_server_logs diff --git a/app/models/outgoing_mailer.rb b/app/models/outgoing_mailer.rb deleted file mode 100644 index 503166b8a..000000000 --- a/app/models/outgoing_mailer.rb +++ /dev/null @@ -1,98 +0,0 @@ -# models/outgoing_mailer.rb: -# Emails which go to public bodies on behalf of users. -# -# Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -# Note: The layout for this wraps messages by lines rather than (blank line -# separated) paragraphs, as is the convention for all the other mailers. This -# turned out to fit better with user exepectations when formatting messages. -# -# XXX The other mail templates are written to use blank line separated -# paragraphs. They could be rewritten, and the wrapping method made uniform -# throughout the application. - -class OutgoingMailer < ApplicationMailer - - # Email to public body requesting info - def initial_request(info_request, outgoing_message) - @wrap_lines_as_paragraphs = true - @from = info_request.incoming_name_and_email - @recipients = info_request.recipient_name_and_email - @subject = info_request.email_subject_request - @headers["message-id"] = OutgoingMailer.id_for_message(outgoing_message) - @body = {:info_request => info_request, :outgoing_message => outgoing_message, - :contact_email => Configuration::contact_email } - end - - # Later message to public body regarding existing request - def followup(info_request, outgoing_message, incoming_message_followup) - @wrap_lines_as_paragraphs = true - @from = info_request.incoming_name_and_email - @recipients = OutgoingMailer.name_and_email_for_followup(info_request, incoming_message_followup) - @subject = OutgoingMailer.subject_for_followup(info_request, outgoing_message) - @headers["message-id"] = OutgoingMailer.id_for_message(outgoing_message) - @body = {:info_request => info_request, :outgoing_message => outgoing_message, - :incoming_message_followup => incoming_message_followup, - :contact_email => Configuration::contact_email } - end - - # XXX the condition checking valid_to_reply_to? also appears in views/request/_followup.rhtml, - # it shouldn't really, should call something here. - # XXX also OutgoingMessage.get_salutation - # XXX these look like they should be members of IncomingMessage, but logically they - # need to work even when IncomingMessage is nil - def OutgoingMailer.name_and_email_for_followup(info_request, incoming_message_followup) - if incoming_message_followup.nil? || !incoming_message_followup.valid_to_reply_to? - return info_request.recipient_name_and_email - else - # calling safe_mail_from from so censor rules are run - return MailHandler.address_from_name_and_email(incoming_message_followup.safe_mail_from, - incoming_message_followup.from_email) - end - end - # Used in the preview of followup - def OutgoingMailer.name_for_followup(info_request, incoming_message_followup) - if incoming_message_followup.nil? || !incoming_message_followup.valid_to_reply_to? - return info_request.public_body.name - else - # calling safe_mail_from from so censor rules are run - return incoming_message_followup.safe_mail_from || info_request.public_body.name - end - end - # Used when making list of followup places to remove duplicates - def OutgoingMailer.email_for_followup(info_request, incoming_message_followup) - if incoming_message_followup.nil? || !incoming_message_followup.valid_to_reply_to? - return info_request.recipient_email - else - return incoming_message_followup.from_email - end - end - # Subject to use for followup - def OutgoingMailer.subject_for_followup(info_request, outgoing_message) - if outgoing_message.what_doing == 'internal_review' - return "Internal review of " + info_request.email_subject_request - else - return info_request.email_subject_followup(outgoing_message.incoming_message_followup) - end - end - # Whether we have a valid email address for a followup - def OutgoingMailer.is_followupable?(info_request, incoming_message_followup) - if incoming_message_followup.nil? || !incoming_message_followup.valid_to_reply_to? - return info_request.recipient_email_valid_for_followup? - else - # email has been checked in incoming_message_followup.valid_to_reply_to? above - return true - end - end - # Message-ID to use - def OutgoingMailer.id_for_message(outgoing_message) - message_id = "ogm-" + outgoing_message.id.to_s - t = Time.now - message_id += "+" + '%08x%05x-%04x' % [t.to_i, t.tv_usec, rand(0xffff)] - message_id += "@" + Configuration::incoming_email_domain - return "<" + message_id + ">" - end - -end - diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb index c75894e6a..11711090e 100644 --- a/app/models/outgoing_message.rb +++ b/app/models/outgoing_message.rb @@ -20,7 +20,7 @@ # else. e.g. An initial request for information, or a complaint. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class OutgoingMessage < ActiveRecord::Base strip_attributes! @@ -51,6 +51,8 @@ class OutgoingMessage < ActiveRecord::Base end end + after_initialize :set_default_letter + # How the default letter starts and ends def get_salutation ret = "" @@ -86,7 +88,7 @@ class OutgoingMessage < ActiveRecord::Base "'" + 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:\n" + - "http://" + Configuration::domain + "/request/" + self.info_request.url_title + "http://" + AlaveteliConfiguration::domain + "/request/" + self.info_request.url_title else "" end @@ -130,13 +132,6 @@ class OutgoingMessage < ActiveRecord::Base MySociety::Validate.contains_postcode?(self.body) end - # Set default letter - def after_initialize - if self.body.nil? - self.body = get_default_message - end - end - # Deliver outgoing message # Note: You can test this from script/console with, say: # InfoRequest.find(1).outgoing_messages[0].send_message @@ -147,7 +142,7 @@ class OutgoingMessage < ActiveRecord::Base self.status = 'sent' self.save! - mail_message = OutgoingMailer.deliver_initial_request(self.info_request, self) + 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, @@ -159,7 +154,7 @@ class OutgoingMessage < ActiveRecord::Base self.status = 'sent' self.save! - mail_message = OutgoingMailer.deliver_followup(self.info_request, self, self.incoming_message_followup) + 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, @@ -253,6 +248,12 @@ class OutgoingMessage < ActiveRecord::Base 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#{get_salutation}\s+#{get_signoff}/ || self.body =~ /#{get_internal_review_insert_here_note}/ if self.message_type == 'followup' diff --git a/app/models/post_redirect.rb b/app/models/post_redirect.rb index 31f08c21a..409069cb6 100644 --- a/app/models/post_redirect.rb +++ b/app/models/post_redirect.rb @@ -24,7 +24,7 @@ # fakes the redirect to include POST parameters in request later. # # Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'openssl' # for random bytes function @@ -32,6 +32,8 @@ class PostRedirect < ActiveRecord::Base # Optional, does a login confirm before redirect for use in email links. belongs_to :user + after_initialize :generate_token + # We store YAML version of POST parameters in the database def post_params=(params) self.post_params_yaml = params.to_yaml @@ -62,18 +64,6 @@ class PostRedirect < ActiveRecord::Base MySociety::Util.generate_token end - # Make the token - def after_initialize - # The token is used to return you to what you are doing after the login form. - if not self.token - self.token = PostRedirect.generate_random_token - end - # There is a separate token to use in the URL if we send a confirmation email. - if not self.email_token - self.email_token = PostRedirect.generate_random_token - end - end - # Used by (rspec) test code only def self.get_last_post_redirect # XXX yeuch - no other easy way of getting the token so we can check @@ -89,6 +79,18 @@ class PostRedirect < ActiveRecord::Base PostRedirect.delete_all "updated_at < (now() - interval '2 months')" end + private + + def generate_token + # The token is used to return you to what you are doing after the login form. + if not self.token + self.token = PostRedirect.generate_random_token + end + # There is a separate token to use in the URL if we send a confirmation email. + if not self.email_token + self.email_token = PostRedirect.generate_random_token + end + end end diff --git a/app/models/profile_photo.rb b/app/models/profile_photo.rb index 73d7ca12b..8a6fe1636 100644 --- a/app/models/profile_photo.rb +++ b/app/models/profile_photo.rb @@ -13,7 +13,7 @@ # Image of user that goes on their profile. # # Copyright (c) 2009 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class ProfilePhoto < ActiveRecord::Base WIDTH = 96 @@ -29,25 +29,9 @@ class ProfilePhoto < ActiveRecord::Base attr_accessor :x, :y, :w, :h - # convert binary data blob into ImageMagick image when assigned attr_accessor :image - def after_initialize - if data.nil? - self.image = nil - return - end - - image_list = Magick::ImageList.new - begin - image_list.from_blob(data) - rescue Magick::ImageMagickError - self.image = nil - return - end - self.image = image_list[0] # XXX perhaps take largest image or somesuch if there were multiple in the file? - self.convert_image - end + after_initialize :convert_data_to_image # make image valid format and size def convert_image @@ -112,6 +96,25 @@ class ProfilePhoto < ActiveRecord::Base raise "Internal error, real pictures must have a user" end end + + # Convert binary data blob into ImageMagick image when assigned + def convert_data_to_image + if data.nil? + self.image = nil + return + end + + image_list = Magick::ImageList.new + begin + image_list.from_blob(data) + rescue Magick::ImageMagickError + self.image = nil + return + end + + self.image = image_list[0] # XXX perhaps take largest image or somesuch if there were multiple in the file? + self.convert_image + end end diff --git a/app/models/public_body.rb b/app/models/public_body.rb index 168b9f4c7..a76aeb189 100644 --- a/app/models/public_body.rb +++ b/app/models/public_body.rb @@ -45,7 +45,7 @@ class PublicBody < ActiveRecord::Base before_save :set_api_key, :set_default_publication_scheme # Every public body except for the internal admin one is visible - named_scope :visible, lambda { + scope :visible, lambda { { :conditions => "public_bodies.id <> #{PublicBody.internal_admin_body.id}" } @@ -54,7 +54,7 @@ class PublicBody < ActiveRecord::Base translates :name, :short_name, :request_email, :url_name, :notes, :first_letter, :publication_scheme # Convenience methods for creating/editing translations via forms - def translation(locale) + def find_translation_by_locale(locale) self.translations.find_by_locale(locale) end @@ -79,7 +79,7 @@ class PublicBody < ActiveRecord::Base if translation_attrs.respond_to? :each_value # Hash => updating translation_attrs.each_value do |attrs| next if skip?(attrs) - t = translation(attrs[:locale]) || PublicBody::Translation.new + t = translation_for(attrs[:locale]) || PublicBody::Translation.new t.attributes = attrs calculate_cached_fields(t) t.save! @@ -106,28 +106,25 @@ class PublicBody < ActiveRecord::Base # like find_by_url_name but also search historic url_name if none found def self.find_by_url_name_with_historic(name) - locale = self.locale || I18n.locale - PublicBody.with_locale(locale) do - found = PublicBody.find(:all, - :conditions => ["public_body_translations.url_name=?", name], - :joins => :translations, - :readonly => false) - # If many bodies are found (usually because the url_name is the same across - # locales) return any of them - return found.first if found.size >= 1 - - # If none found, then search the history of short names - old = PublicBody::Version.find_all_by_url_name(name) - # Find unique public bodies in it - old = old.map { |x| x.public_body_id } - old = old.uniq - # Maybe return the first one, so we show something relevant, - # rather than throwing an error? - raise "Two bodies with the same historical URL name: #{name}" if old.size > 1 - return unless old.size == 1 - # does acts_as_versioned provide a method that returns the current version? - return PublicBody.find(old.first) - end + found = PublicBody.find(:all, + :conditions => ["public_body_translations.url_name=?", name], + :joins => :translations, + :readonly => false) + # If many bodies are found (usually because the url_name is the same across + # locales) return any of them + return found.first if found.size >= 1 + + # If none found, then search the history of short names + old = PublicBody::Version.find_all_by_url_name(name) + # Find unique public bodies in it + old = old.map { |x| x.public_body_id } + old = old.uniq + # Maybe return the first one, so we show something relevant, + # rather than throwing an error? + raise "Two bodies with the same historical URL name: #{name}" if old.size > 1 + return unless old.size == 1 + # does acts_as_versioned provide a method that returns the current version? + return PublicBody.find(old.first) end # Set the first letter, which is used for faster queries @@ -243,13 +240,13 @@ class PublicBody < ActiveRecord::Base # When name or short name is changed, also change the url name def short_name=(short_name) - globalize.write(self.class.locale || I18n.locale, :short_name, short_name) + globalize.write(I18n.locale, :short_name, short_name) self[:short_name] = short_name self.update_url_name end def name=(name) - globalize.write(self.class.locale || I18n.locale, :name, name) + globalize.write(I18n.locale, :name, name) self[:name] = name self.update_url_name end @@ -329,13 +326,13 @@ class PublicBody < ActiveRecord::Base # The "internal admin" is a special body for internal use. def PublicBody.internal_admin_body - PublicBody.with_locale(I18n.default_locale) do + I18n.with_locale(I18n.default_locale) do pb = PublicBody.find_by_url_name("internal_admin_authority") if pb.nil? pb = PublicBody.new( :name => 'Internal admin authority', :short_name => "", - :request_email => Configuration::contact_email, + :request_email => AlaveteliConfiguration::contact_email, :home_page => "", :notes => "", :publication_scheme => "", @@ -367,7 +364,7 @@ class PublicBody < ActiveRecord::Base # of updating them bodies_by_name = {} set_of_existing = Set.new() - PublicBody.with_locale(I18n.default_locale) do + I18n.with_locale(I18n.default_locale) do bodies = (tag.nil? || tag.empty?) ? PublicBody.find(:all) : PublicBody.find_by_tag(tag) for existing_body in bodies # Hide InternalAdminBody from import notes @@ -410,7 +407,7 @@ class PublicBody < ActiveRecord::Base if public_body = bodies_by_name[name] # Existing public body available_locales.each do |locale| - PublicBody.with_locale(locale) do + I18n.with_locale(locale) do changed = ActiveSupport::OrderedHash.new field_list.each do |field_name| localized_field_name = (locale.to_s == I18n.default_locale.to_s) ? field_name : "#{field_name}.#{locale}" @@ -445,7 +442,7 @@ class PublicBody < ActiveRecord::Base else # New public body public_body = PublicBody.new(:name=>"", :short_name=>"", :request_email=>"") available_locales.each do |locale| - PublicBody.with_locale(locale) do + I18n.with_locale(locale) do changed = ActiveSupport::OrderedHash.new field_list.each do |field_name| localized_field_name = (locale.to_s == I18n.default_locale.to_s) ? field_name : "#{field_name}.#{locale}" @@ -551,7 +548,7 @@ class PublicBody < ActiveRecord::Base # Returns nil if configuration variable not set def override_request_email - e = Configuration::override_all_public_body_request_emails + e = AlaveteliConfiguration::override_all_public_body_request_emails e if e != "" end @@ -635,6 +632,8 @@ class PublicBody < ActiveRecord::Base end end + private + def request_email_if_requestable # Request_email can be blank, meaning we don't have details if self.is_requestable? diff --git a/app/models/purge_request.rb b/app/models/purge_request.rb index 48a16f9e6..e48f3cc6f 100644 --- a/app/models/purge_request.rb +++ b/app/models/purge_request.rb @@ -14,7 +14,7 @@ # A queue of URLs to purge # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ # class PurgeRequest < ActiveRecord::Base diff --git a/app/models/raw_email.rb b/app/models/raw_email.rb index de7978b82..6bf01bc74 100644 --- a/app/models/raw_email.rb +++ b/app/models/raw_email.rb @@ -10,7 +10,7 @@ # The fat part of models/incoming_message.rb # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class RawEmail < ActiveRecord::Base # deliberately don't strip_attributes, so keeps raw email properly @@ -23,10 +23,10 @@ class RawEmail < ActiveRecord::Base raise "Failed to find the id number of the associated request: has it been saved?" end - if ENV["RAILS_ENV"] == "test" + if Rails.env.test? return File.join(Rails.root, 'files/raw_email_test') else - return File.join(Configuration::raw_emails_location, + return File.join(AlaveteliConfiguration::raw_emails_location, request_id[0..2], request_id) end end diff --git a/app/models/request_mailer.rb b/app/models/request_mailer.rb deleted file mode 100644 index 955f73ef4..000000000 --- a/app/models/request_mailer.rb +++ /dev/null @@ -1,457 +0,0 @@ -# models/request_mailer.rb: -# Alerts relating to requests. -# -# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -require 'alaveteli_file_types' -if Rails.env == 'test' && RUBY_VERSION.to_f >= 1.9 - # Avoid spec/script/mailin_spec.rb running script/runner as a test suite - # http://stackoverflow.com/questions/1899009/why-are-tests-running-in-production-mode-and-causing-my-script-runners-to-fail - Test::Unit.run = true -end -class RequestMailer < ApplicationMailer - - - # Used when an FOI officer uploads a response from their web browser - this is - # the "fake" email used to store in the same format in the database as if they - # had emailed it. - def fake_response(info_request, from_user, body, attachment_name, attachment_content) - @from = from_user.name_and_email - @recipients = info_request.incoming_name_and_email - @body = { - :body => body - } - if !attachment_name.nil? && !attachment_content.nil? - content_type = AlaveteliFileTypes.filename_to_mimetype(attachment_name) || 'application/octet-stream' - - attachment :content_type => content_type, - :body => attachment_content, - :filename => attachment_name - end - end - - # Used when a response is uploaded using the API - def external_response(info_request, body, sent_at, attachments) - @from = blackhole_email - @recipients = info_request.incoming_name_and_email - @body = { :body => body } - - # ActionMailer only works properly when the time is in the local timezone: - # see https://rails.lighthouseapp.com/projects/8994/tickets/3113-actionmailer-only-works-correctly-with-sent_on-times-that-are-in-the-local-time-zone - @sent_on = sent_at.dup.localtime - - attachments.each do |attachment_hash| - attachment attachment_hash - end - end - - # Incoming message arrived for a request, but new responses have been stopped. - def stopped_responses(info_request, email, raw_email_data) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # we don't care about bounces, likely from spammers - 'Auto-Submitted' => 'auto-replied' # http://tools.ietf.org/html/rfc3834 - @recipients = email.from_addrs[0].to_s - @subject = _("Your response to an FOI request was not delivered") - attachment :content_type => 'message/rfc822', :body => raw_email_data, - :filename => "original.eml", :transfer_encoding => '7bit', :content_disposition => 'inline' - @body = { - :info_request => info_request, - :contact_email => Configuration::contact_email - } - end - - # An FOI response is outside the scope of the system, and needs admin attention - def requires_admin(info_request, set_by = nil, message = "") - user = set_by || info_request.user - @from = user.name_and_email - @recipients = contact_from_name_and_email - @subject = _("FOI response requires admin ({{reason}}) - {{title}}", :reason => info_request.described_state, :title => info_request.title) - url = request_url(info_request) - admin_url = admin_request_show_url(info_request) - @body = {:reported_by => user, :message => message, :info_request => info_request, :url => url, :admin_url => admin_url } - end - - # Tell the requester that a new response has arrived - def new_response(info_request, incoming_message) - # Don't use login link here, just send actual URL. This is - # because people tend to forward these emails amongst themselves. - url = incoming_message_url(incoming_message) - - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken - 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = (_("New response to your FOI request - ") + info_request.title).html_safe - @body = { :incoming_message => incoming_message, :info_request => info_request, :url => url } - end - - # Tell the requester that the public body is late in replying - def overdue_alert(info_request, user) - respond_url = respond_to_last_url(info_request) + "#followup" - - post_redirect = PostRedirect.new( - :uri => respond_url, - :user_id => user.id) - post_redirect.save! - url = confirm_url(:email_token => post_redirect.email_token) - - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken - 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = user.name_and_email - @subject = (_("Delayed response to your FOI request - ") + info_request.title).html_safe - @body = { :info_request => info_request, :url => url } - end - - # Tell the requester that the public body is very late in replying - def very_overdue_alert(info_request, user) - respond_url = respond_to_last_url(info_request) + "#followup" - - post_redirect = PostRedirect.new( - :uri => respond_url, - :user_id => user.id) - post_redirect.save! - url = confirm_url(:email_token => post_redirect.email_token) - - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken - 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = user.name_and_email - @subject = (_("You're long overdue a response to your FOI request - ") + info_request.title).html_safe - @body = { :info_request => info_request, :url => url } - end - - # Tell the requester that they need to say if the new response - # contains info or not - def new_response_reminder_alert(info_request, incoming_message) - # Make a link going to the form to describe state, and which logs the - # user in. - post_redirect = PostRedirect.new( - :uri => request_url(info_request) + "#describe_state_form_1", - :user_id => info_request.user.id) - post_redirect.save! - url = confirm_url(:email_token => post_redirect.email_token) - - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken - 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = _("Was the response you got to your FOI request any good?") - @body = { :incoming_message => incoming_message, :info_request => info_request, :url => url } - end - - # Tell the requester that someone updated their old unclassified request - def old_unclassified_updated(info_request) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken - 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = _("Someone has updated the status of your request") - url = request_url(info_request) - @body = {:info_request => info_request, :url => url} - end - - # Tell the requester that they need to clarify their request - def not_clarified_alert(info_request, incoming_message) - respond_url = show_response_url(:id => info_request.id, :incoming_message_id => incoming_message.id) - respond_url = respond_url + "#followup" - - post_redirect = PostRedirect.new( - :uri => respond_url, - :user_id => info_request.user.id) - post_redirect.save! - url = confirm_url(:email_token => post_redirect.email_token) - - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken - 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = (_("Clarify your FOI request - ") + info_request.title).html_safe - @body = { :incoming_message => incoming_message, :info_request => info_request, :url => url } - end - - # Tell requester that somebody add an annotation to their request - def comment_on_alert(info_request, comment) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken - 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = (_("Somebody added a note to your FOI request - ") + info_request.title).html_safe - @body = { :comment => comment, :info_request => info_request, :url => comment_url(comment) } - end - def comment_on_alert_plural(info_request, count, earliest_unalerted_comment) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from, # not much we can do if the user's email is broken - 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'X-Auto-Response-Suppress' => 'OOF' - @recipients = info_request.user.name_and_email - @subject = (_("Some notes have been added to your FOI request - ") + info_request.title).html_safe - @body = { :count => count, :info_request => info_request, :url => comment_url(earliest_unalerted_comment) } - end - - # Class function, called by script/mailin with all incoming responses. - # [ This is a copy (Monkeypatch!) of function from action_mailer/base.rb, - # but which additionally passes the raw_email to the member function, as we - # want to record it. - # - # That is because we want to be sure we properly record the actual message - # received in its raw form - so any information won't be lost in a round - # trip via the mail handler, or by bugs in it, and so we can use something - # other than TMail at a later date. And so we can offer an option to download the - # actual original mail sent by the authority in the admin interface (so - # can check that attachment decoding failures are problems in the message, - # not in our code). ] - def self.receive(raw_email) - logger.info "Received mail:\n #{raw_email}" unless logger.nil? - mail = MailHandler.mail_from_raw_email(raw_email) - new.receive(mail, raw_email) - end - - # Find which info requests the email is for - def requests_matching_email(email) - # We deliberately don't use Envelope-to here, so ones that are BCC - # drop into the holding pen for checking. - reply_info_requests = [] # XXX should be set? - for address in (email.to || []) + (email.cc || []) - reply_info_request = InfoRequest.find_by_incoming_email(address) - reply_info_requests.push(reply_info_request) if reply_info_request - end - return reply_info_requests - end - - # Member function, called on the new class made in self.receive above - def receive(email, raw_email) - # Find which info requests the email is for - reply_info_requests = self.requests_matching_email(email) - # Nothing found, so save in holding pen - if reply_info_requests.size == 0 - reason = _("Could not identify the request from the email address") - request = InfoRequest.holding_pen_request - request.receive(email, raw_email, false, reason) - return - end - - # Send the message to each request, to be archived with it - for reply_info_request in reply_info_requests - # If environment variable STOP_DUPLICATES is set, don't send message with same id again - if ENV['STOP_DUPLICATES'] - if reply_info_request.already_received?(email, raw_email) - raise "message " + email.message_id + " already received by request" - end - end - reply_info_request.receive(email, raw_email) - end - end - - # Send email alerts for overdue requests - def self.alert_overdue_requests() - info_requests = InfoRequest.find(:all, - :conditions => [ - "described_state = 'waiting_response' - AND awaiting_description = ? - AND user_id is not null - AND (SELECT id - FROM user_info_request_sent_alerts - WHERE alert_type = 'very_overdue_1' - AND info_request_id = info_requests.id - AND user_id = info_requests.user_id - AND info_request_event_id = (SELECT max(id) - FROM info_request_events - WHERE event_type in ('sent', - 'followup_sent', - 'resent', - 'followup_resent') - AND info_request_id = info_requests.id) - ) IS NULL", false - ], - :include => [ :user ] - ) - for info_request in info_requests - alert_event_id = info_request.last_event_forming_initial_request.id - # Only overdue requests - calculated_status = info_request.calculate_status - if ['waiting_response_overdue', 'waiting_response_very_overdue'].include?(calculated_status) - if calculated_status == 'waiting_response_overdue' - alert_type = 'overdue_1' - elsif calculated_status == 'waiting_response_very_overdue' - alert_type = 'very_overdue_1' - else - raise "unknown request status" - end - - # For now, just to the user who created the request - sent_already = UserInfoRequestSentAlert.find(:first, :conditions => [ "alert_type = ? - AND user_id = ? - AND info_request_id = ? - AND info_request_event_id = ?", - alert_type, - info_request.user_id, - info_request.id, - alert_event_id]) - if sent_already.nil? - # Alert not yet sent for this user, so send it - store_sent = UserInfoRequestSentAlert.new - store_sent.info_request = info_request - store_sent.user = info_request.user - store_sent.alert_type = alert_type - store_sent.info_request_event_id = alert_event_id - # Only send the alert if the user can act on it by making a followup - # (otherwise they are banned, and there is no point sending it) - if info_request.user.can_make_followup? - if calculated_status == 'waiting_response_overdue' - RequestMailer.deliver_overdue_alert(info_request, info_request.user) - elsif calculated_status == 'waiting_response_very_overdue' - RequestMailer.deliver_very_overdue_alert(info_request, info_request.user) - else - raise "unknown request status" - end - end - store_sent.save! - end - end - end - end - - # Send email alerts for new responses which haven't been classified. By default, - # it goes out 3 days after last update of event, then after 10, then after 24. - def self.alert_new_response_reminders - Configuration::new_response_reminder_after_days.each_with_index do |days, i| - self.alert_new_response_reminders_internal(days, "new_response_reminder_#{i+1}") - end - end - def self.alert_new_response_reminders_internal(days_since, type_code) - info_requests = InfoRequest.find_old_unclassified(:order => 'info_requests.id', - :include => [:user], - :age_in_days => days_since) - - for info_request in info_requests - alert_event_id = info_request.get_last_response_event_id - last_response_message = info_request.get_last_response - if alert_event_id.nil? - raise "internal error, no last response while making alert new response reminder, request id " + info_request.id.to_s - end - # To the user who created the request - sent_already = UserInfoRequestSentAlert.find(:first, :conditions => [ "alert_type = ? and user_id = ? and info_request_id = ? and info_request_event_id = ?", type_code, info_request.user_id, info_request.id, alert_event_id]) - if sent_already.nil? - # Alert not yet sent for this user - store_sent = UserInfoRequestSentAlert.new - store_sent.info_request = info_request - store_sent.user = info_request.user - store_sent.alert_type = type_code - store_sent.info_request_event_id = alert_event_id - # XXX uses same template for reminder 1 and reminder 2 right now. - RequestMailer.deliver_new_response_reminder_alert(info_request, last_response_message) - store_sent.save! - end - end - end - - # Send email alerts for requests which need clarification. Goes out 3 days - # after last update of event. - def self.alert_not_clarified_request() - info_requests = InfoRequest.find(:all, :conditions => [ "awaiting_description = ? and described_state = 'waiting_clarification' and info_requests.updated_at < ?", false, Time.now() - 3.days ], :include => [ :user ], :order => "info_requests.id" ) - for info_request in info_requests - alert_event_id = info_request.get_last_response_event_id - last_response_message = info_request.get_last_response - if alert_event_id.nil? - raise "internal error, no last response while making alert not clarified reminder, request id " + info_request.id.to_s - end - # To the user who created the request - sent_already = UserInfoRequestSentAlert.find(:first, :conditions => [ "alert_type = 'not_clarified_1' and user_id = ? and info_request_id = ? and info_request_event_id = ?", info_request.user_id, info_request.id, alert_event_id]) - if sent_already.nil? - # Alert not yet sent for this user - store_sent = UserInfoRequestSentAlert.new - store_sent.info_request = info_request - store_sent.user = info_request.user - store_sent.alert_type = 'not_clarified_1' - store_sent.info_request_event_id = alert_event_id - # Only send the alert if the user can act on it by making a followup - # (otherwise they are banned, and there is no point sending it) - if info_request.user.can_make_followup? - RequestMailer.deliver_not_clarified_alert(info_request, last_response_message) - end - store_sent.save! - end - end - end - - # Send email alert to request submitter for new comments on the request. - def self.alert_comment_on_request() - - # We only check comments made in the last month - this means if the - # cron jobs broke for more than a month events would be lost, but no - # matter. I suspect the performance gain will be needed (with an index on updated_at) - - # XXX the :order part info_request_events.created_at is a work around - # for a very old Rails bug which means eager loading does not respect - # association orders. - # http://dev.rubyonrails.org/ticket/3438 - # http://lists.rubyonrails.org/pipermail/rails-core/2006-July/001798.html - # That that patch has not been applied, despite bribes of beer, is - # typical of the lack of quality of Rails. - - info_requests = InfoRequest.find(:all, - :conditions => [ - "info_requests.id in ( - select info_request_id - from info_request_events - where event_type = 'comment' - and created_at > (now() - '1 month'::interval) - )" - ], - :include => [ { :info_request_events => :user_info_request_sent_alerts } ], - :order => "info_requests.id, info_request_events.created_at" - ) - for info_request in info_requests - - next if info_request.is_external? - # Count number of new comments to alert on - earliest_unalerted_comment_event = nil - last_comment_event = nil - count = 0 - for e in info_request.info_request_events.reverse - # alert on comments, which were not made by the user who originally made the request - if e.event_type == 'comment' && e.comment.user_id != info_request.user_id - last_comment_event = e if last_comment_event.nil? - - alerted_for = e.user_info_request_sent_alerts.find(:first, :conditions => [ "alert_type = 'comment_1' and user_id = ?", info_request.user_id]) - if alerted_for.nil? - count = count + 1 - earliest_unalerted_comment_event = e - else - break - end - end - end - - # Alert needs sending if there are new comments - if count > 0 - store_sent = UserInfoRequestSentAlert.new - store_sent.info_request = info_request - store_sent.user = info_request.user - store_sent.alert_type = 'comment_1' - store_sent.info_request_event_id = last_comment_event.id - if count > 1 - RequestMailer.deliver_comment_on_alert_plural(info_request, count, earliest_unalerted_comment_event.comment) - elsif count == 1 - RequestMailer.deliver_comment_on_alert(info_request, last_comment_event.comment) - else - raise "internal error" - end - store_sent.save! - end - end - end - - -end - - diff --git a/app/models/track_mailer.rb b/app/models/track_mailer.rb deleted file mode 100644 index 51440e4d7..000000000 --- a/app/models/track_mailer.rb +++ /dev/null @@ -1,135 +0,0 @@ -# models/track_mailer.rb: -# Emails which go to users who are tracking things. -# -# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -class TrackMailer < ApplicationMailer - def event_digest(user, email_about_things) - post_redirect = PostRedirect.new( - :uri => user_url(user) + "#email_subscriptions", - :user_id => user.id) - post_redirect.save! - unsubscribe_url = confirm_url(:email_token => post_redirect.email_token) - - @from = contact_from_name_and_email - headers 'Auto-Submitted' => 'auto-generated', # http://tools.ietf.org/html/rfc3834 - 'Precedence' => 'bulk' # http://www.vbulletin.com/forum/project.php?issueid=27687 (Exchange hack) - # 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces for tracks - # (We let it return bounces for now, so we can manually kill the tracks that bounce so Yahoo - # etc. don't decide we are spammers.) - - @recipients = user.name_and_email - @subject = _("Your {{site_name}} email alert", :site_name => site_name) - @body = { :user => user, :email_about_things => email_about_things, :unsubscribe_url => unsubscribe_url } - end - - def contact_from_name_and_email - "#{Configuration::track_sender_name} <#{Configuration::track_sender_email}>" - end - - # Send email alerts for tracked things. Never more than one email - # a day, nor about events which are more than a week old, nor - # events about which emails have been sent within the last two - # weeks. - - # Useful query to run by hand to see how many alerts are due: - # User.find(:all, :conditions => [ "last_daily_track_email < ?", Time.now - 1.day ]).size - def self.alert_tracks - done_something = false - now = Time.now() - users = User.find(:all, :conditions => [ "last_daily_track_email < ?", now - 1.day ]) - if users.empty? - return done_something - end - for user in users - next if !user.should_be_emailed? || !user.receive_email_alerts - - email_about_things = [] - track_things = TrackThing.find(:all, :conditions => [ "tracking_user_id = ? and track_medium = ?", user.id, 'email_daily' ]) - for track_thing in track_things - # What have we alerted on already? - # - # We only use track_things_sent_emails records which are less than 14 days old. - # In the search query loop below, we also only use items described in last 7 days. - # An item described that recently definitely can't appear in track_things_sent_emails - # earlier, so this is safe (with a week long margin of error). If the alerts break - # for a whole week, then they will miss some items. Tough. - done_info_request_events = {} - tt_sent = track_thing.track_things_sent_emails.find(:all, :conditions => ['created_at > ?', now - 14.days]) - for t in tt_sent - if not t.info_request_event_id.nil? - done_info_request_events[t.info_request_event_id] = 1 - end - end - - # Query for things in this track. We use described_at for the - # ordering, so we catch anything new (before described), or - # anything whose new status has been described. - xapian_object = InfoRequest.full_search([InfoRequestEvent], track_thing.track_query, 'described_at', true, nil, 100, 1) - # Go through looking for unalerted things - alert_results = [] - for result in xapian_object.results - if result[:model].class.to_s != "InfoRequestEvent" - raise "need to add other types to TrackMailer.alert_tracks (unalerted)" - end - - next if track_thing.created_at >= result[:model].described_at # made before the track was created - next if result[:model].described_at < now - 7.days # older than 1 week (see 14 days / 7 days in comment above) - next if done_info_request_events.include?(result[:model].id) # definitely already done - - # OK alert this one - alert_results.push(result) - end - # If there were more alerts for this track, then store them - if alert_results.size > 0 - email_about_things.push([track_thing, alert_results, xapian_object]) - end - end - - # If we have anything to send, then send everything for the user in one mail - if email_about_things.size > 0 - # Send the email - - previous_locale = I18n.locale - I18n.locale = user.get_locale - TrackMailer.deliver_event_digest(user, email_about_things) - I18n.locale = previous_locale - end - - # Record that we've now sent those alerts to that user - for track_thing, alert_results in email_about_things - for result in alert_results - track_things_sent_email = TrackThingsSentEmail.new - track_things_sent_email.track_thing_id = track_thing.id - if result[:model].class.to_s == "InfoRequestEvent" - track_things_sent_email.info_request_event_id = result[:model].id - else - raise "need to add other types to TrackMailer.alert_tracks (mark alerted)" - end - track_things_sent_email.save! - end - end - user.last_daily_track_email = now - user.no_xapian_reindex = true - user.save! - done_something = true - end - return done_something - end - - def self.alert_tracks_loop - # Run alert_tracks in an endless loop, sleeping when there is nothing to do - while true - sleep_seconds = 1 - while !alert_tracks - sleep sleep_seconds - sleep_seconds *= 2 - sleep_seconds = 300 if sleep_seconds > 300 - end - end - end - -end - - diff --git a/app/models/track_thing.rb b/app/models/track_thing.rb index a0c74bdb6..66b8a5c47 100644 --- a/app/models/track_thing.rb +++ b/app/models/track_thing.rb @@ -19,7 +19,7 @@ # When somebody is getting alerts for something. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ require 'set' @@ -260,7 +260,7 @@ class TrackThing < ActiveRecord::Base :title_in_email => self.public_body.law_only_short + " requests to '" + self.public_body.name + "'", :title_in_rss => self.public_body.law_only_short + " requests to '" + self.public_body.name + "'", # Authentication - :web => _("To follow requests made using {{site_name}} to the public authority '{{public_body_name}}'", :site_name=>Configuration::site_name, :public_body_name=>CGI.escapeHTML(self.public_body.name)), + :web => _("To follow requests made using {{site_name}} to the public authority '{{public_body_name}}'", :site_name=>AlaveteliConfiguration::site_name, :public_body_name=>CGI.escapeHTML(self.public_body.name)), :email => _("Then you will be notified whenever someone requests something or gets a response from '{{public_body_name}}'.", :public_body_name=>CGI.escapeHTML(self.public_body.name)), :email_subject => _("Confirm you want to follow requests to '{{public_body_name}}'", :public_body_name=>self.public_body.name), # RSS sorting diff --git a/app/models/track_things_sent_email.rb b/app/models/track_things_sent_email.rb index a0a4c0f0c..a9ea2520e 100644 --- a/app/models/track_things_sent_email.rb +++ b/app/models/track_things_sent_email.rb @@ -16,7 +16,7 @@ # Record that alert has arrived. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class TrackThingsSentEmail < ActiveRecord::Base belongs_to :info_request_event diff --git a/app/models/user.rb b/app/models/user.rb index e6c666e47..63dd5b1dd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -58,6 +58,9 @@ class User < ActiveRecord::Base ], :terms => [ [ :variety, 'V', "variety" ] ], :if => :indexed_by_search? + + after_initialize :set_defaults + def created_at_numeric # format it here as no datetime support in Xapian's value ranges return self.created_at.strftime("%Y%m%d%H%M%S") @@ -67,17 +70,6 @@ class User < ActiveRecord::Base "user" end - def after_initialize - if self.admin_level.nil? - self.admin_level = 'none' - end - if self.new_record? - # make alert emails go out at a random time for each new user, so - # overall they are spread out throughout the day. - self.last_daily_track_email = User.random_time_in_last_day - end - end - # requested_by: and commented_by: search queries also need updating after save after_update :reindex_referencing_models def reindex_referencing_models @@ -98,12 +90,7 @@ class User < ActiveRecord::Base end def get_locale - if !self.locale.nil? - locale = self.locale - else - locale = I18n.locale - end - return locale.to_s + (self.locale || I18n.locale).to_s end def visible_comments @@ -141,14 +128,14 @@ class User < ActiveRecord::Base if user # There is user with email, check password if !user.has_this_password?(params[:password]) - user.errors.add_to_base(auth_fail_message) + user.errors.add(:base, auth_fail_message) end else # No user of same email, make one (that we don't save in the database) # for the forms code to use. user = User.new(params) # deliberately same message as above so as not to leak whether registered - user.errors.add_to_base(auth_fail_message) + user.errors.add(:base, auth_fail_message) end user end @@ -201,12 +188,12 @@ class User < ActiveRecord::Base # The "internal admin" is a special user for internal use. def User.internal_admin_user - u = User.find_by_email(Configuration::contact_email) + u = User.find_by_email(AlaveteliConfiguration::contact_email) if u.nil? password = PostRedirect.generate_random_token u = User.new( :name => 'Internal admin user', - :email => Configuration::contact_email, + :email => AlaveteliConfiguration::contact_email, :password => password, :password_confirmation => password ) @@ -279,16 +266,16 @@ class User < ActiveRecord::Base return false if self.no_limit # Has the user issued as many as MAX_REQUESTS_PER_USER_PER_DAY requests in the past 24 hours? - return false if Configuration::max_requests_per_user_per_day.blank? + return false if AlaveteliConfiguration::max_requests_per_user_per_day.blank? recent_requests = InfoRequest.count(:conditions => ["user_id = ? and created_at > now() - '1 day'::interval", self.id]) - return (recent_requests >= Configuration::max_requests_per_user_per_day) + return (recent_requests >= AlaveteliConfiguration::max_requests_per_user_per_day) end def next_request_permitted_at return nil if self.no_limit - n_most_recent_requests = InfoRequest.all(:conditions => ["user_id = ? and created_at > now() - '1 day'::interval", self.id], :order => "created_at DESC", :limit => Configuration::max_requests_per_user_per_day) - return nil if n_most_recent_requests.size < Configuration::max_requests_per_user_per_day + n_most_recent_requests = InfoRequest.all(:conditions => ["user_id = ? and created_at > now() - '1 day'::interval", self.id], :order => "created_at DESC", :limit => AlaveteliConfiguration::max_requests_per_user_per_day) + return nil if n_most_recent_requests.size < AlaveteliConfiguration::max_requests_per_user_per_day nth_most_recent_request = n_most_recent_requests[-1] return nth_most_recent_request.created_at + 1.day @@ -407,6 +394,17 @@ class User < ActiveRecord::Base self.salt = self.object_id.to_s + rand.to_s end + def set_defaults + if self.admin_level.nil? + self.admin_level = 'none' + end + if self.new_record? + # make alert emails go out at a random time for each new user, so + # overall they are spread out throughout the day. + self.last_daily_track_email = User.random_time_in_last_day + end + end + def email_and_name_are_valid if self.email != "" && !MySociety::Validate.is_valid_email(self.email) errors.add(:email, _("Please enter a valid email address")) diff --git a/app/models/user_info_request_sent_alert.rb b/app/models/user_info_request_sent_alert.rb index cf20bcbf5..449a4c237 100644 --- a/app/models/user_info_request_sent_alert.rb +++ b/app/models/user_info_request_sent_alert.rb @@ -15,7 +15,7 @@ # given type of alert. # # Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class UserInfoRequestSentAlert < ActiveRecord::Base belongs_to :user diff --git a/app/models/user_mailer.rb b/app/models/user_mailer.rb deleted file mode 100644 index 1be4f8aa3..000000000 --- a/app/models/user_mailer.rb +++ /dev/null @@ -1,48 +0,0 @@ -# models/user_mailer.rb: -# Emails relating to user accounts. e.g. Confirming a new account -# -# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved. -# Email: francis@mysociety.org; WWW: http://www.mysociety.org/ - -class UserMailer < ApplicationMailer - def confirm_login(user, reasons, url) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces when people are fiddling with their account - @recipients = user.name_and_email - @subject = reasons[:email_subject] - @body[:reasons] = reasons - @body[:name] = user.name - @body[:url] = url - end - - def already_registered(user, reasons, url) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces when people are fiddling with their account - @recipients = user.name_and_email - @subject = reasons[:email_subject] - @body[:reasons] = reasons - @body[:name] = user.name - @body[:url] = url - end - - def changeemail_confirm(user, new_email, url) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces when people are fiddling with their account - @recipients = new_email - @subject = _("Confirm your new email address on {{site_name}}", :site_name=>site_name) - @body[:name] = user.name - @body[:url] = url - @body[:old_email] = user.email - @body[:new_email] = new_email - end - - def changeemail_already_used(old_email, new_email) - @from = contact_from_name_and_email - headers 'Return-Path' => blackhole_email, 'Reply-To' => @from # we don't care about bounces when people are fiddling with their account - @recipients = new_email - @subject = _("Unable to change email address on {{site_name}}", :site_name=>site_name) - @body[:old_email] = old_email - @body[:new_email] = new_email - end -end - |