diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/about_me_validator.rb | 8 | ||||
-rw-r--r-- | app/models/application_mailer.rb | 14 | ||||
-rw-r--r-- | app/models/change_email_validator.rb | 5 | ||||
-rw-r--r-- | app/models/comment.rb | 39 | ||||
-rw-r--r-- | app/models/contact_mailer.rb | 2 | ||||
-rw-r--r-- | app/models/foi_attachment.rb | 3 | ||||
-rw-r--r-- | app/models/incoming_message.rb | 2 | ||||
-rw-r--r-- | app/models/info_request.rb | 147 | ||||
-rw-r--r-- | app/models/info_request_event.rb | 34 | ||||
-rw-r--r-- | app/models/outgoing_message.rb | 54 | ||||
-rw-r--r-- | app/models/profile_photo.rb | 6 | ||||
-rw-r--r-- | app/models/public_body.rb | 72 | ||||
-rw-r--r-- | app/models/request_mailer.rb | 42 | ||||
-rw-r--r-- | app/models/track_mailer.rb | 9 | ||||
-rw-r--r-- | app/models/track_thing.rb | 11 | ||||
-rw-r--r-- | app/models/user.rb | 30 |
16 files changed, 238 insertions, 240 deletions
diff --git a/app/models/about_me_validator.rb b/app/models/about_me_validator.rb index 5e04c2761..8ee505ac8 100644 --- a/app/models/about_me_validator.rb +++ b/app/models/about_me_validator.rb @@ -17,10 +17,14 @@ class AboutMeValidator < ActiveRecord::BaseWithoutTable column :about_me, :text, "I...", false - def validate + # TODO: Switch to built in validations + validate :length_of_about_me + + private + + def length_of_about_me if !self.about_me.blank? && self.about_me.size > 500 errors.add(:about_me, _("Please keep it shorter than 500 characters")) end end - end diff --git a/app/models/application_mailer.rb b/app/models/application_mailer.rb index cdb279c3c..1a97a4bf9 100644 --- a/app/models/application_mailer.rb +++ b/app/models/application_mailer.rb @@ -77,6 +77,17 @@ class ApplicationMailer < ActionMailer::Base # 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 @@ -139,6 +150,9 @@ class ApplicationMailer < ActionMailer::Base # 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 diff --git a/app/models/change_email_validator.rb b/app/models/change_email_validator.rb index 9ef25217d..2ddebb177 100644 --- a/app/models/change_email_validator.rb +++ b/app/models/change_email_validator.rb @@ -28,12 +28,15 @@ class ChangeEmailValidator < ActiveRecord::BaseWithoutTable 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() self.user_circumstance == 'change_email' end - def validate + private + + def password_and_format_of_email if !self.old_email.blank? && !MySociety::Validate.is_valid_email(self.old_email) errors.add(:old_email, _("Old email doesn't look like a valid address")) end diff --git a/app/models/comment.rb b/app/models/comment.rb index bcd1efca8..70f3ba00d 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -24,13 +24,13 @@ class Comment < ActiveRecord::Base strip_attributes! belongs_to :user - #validates_presence_of :user # breaks during construction of new ones :( - - validates_inclusion_of :comment_type, :in => [ 'request' ] belongs_to :info_request - has_many :info_request_events # in practice only ever has one + #validates_presence_of :user # breaks during construction of new ones :( + validates_inclusion_of :comment_type, :in => [ 'request' ] + validate :body_of_comment + def body ret = read_attribute(:body) if ret.nil? @@ -40,6 +40,7 @@ class Comment < ActiveRecord::Base ret = ret.gsub(/(?:\n\s*){2,}/, "\n\n") # remove excess linebreaks that unnecessarily space it out ret end + def raw_body read_attribute(:body) end @@ -52,16 +53,6 @@ class Comment < ActiveRecord::Base end end - # Check have edited comment - def validate - if self.body.empty? || self.body =~ /^\s+$/ - errors.add(:body, _("Please enter your annotation")) - end - if !MySociety::Validate.uses_mixed_capitals(self.body) - errors.add(:body, _('Please write your annotation using a mixture of capital and lower case letters. This makes it easier for others to read.')) - end - end - # Return body for display as HTML def get_body_for_html_display text = self.body.strip @@ -82,9 +73,21 @@ class Comment < ActiveRecord::Base return Comment.find(:first, :conditions => [ "info_request_id = ? and body = ?", info_request_id, body ]) end 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) + + 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 body_of_comment + if self.body.empty? || self.body =~ /^\s+$/ + errors.add(:body, _("Please enter your annotation")) + end + if !MySociety::Validate.uses_mixed_capitals(self.body) + errors.add(:body, _('Please write your annotation using a mixture of capital and lower case letters. This makes it easier for others to read.')) + end end - end end diff --git a/app/models/contact_mailer.rb b/app/models/contact_mailer.rb index 16aae2f15..abde64928 100644 --- a/app/models/contact_mailer.rb +++ b/app/models/contact_mailer.rb @@ -7,7 +7,7 @@ class ContactMailer < ApplicationMailer # Send message to administrator - def message(name, email, subject, message, logged_in_user, last_request, last_body) + 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 diff --git a/app/models/foi_attachment.rb b/app/models/foi_attachment.rb index 723bc4abb..bba0b6a8d 100644 --- a/app/models/foi_attachment.rb +++ b/app/models/foi_attachment.rb @@ -317,8 +317,7 @@ class FoiAttachment < ActiveRecord::Base text = CGI.escapeHTML(text) text = MySociety::Format.make_clickable(text) html = text.gsub(/\n/, '<br>') - return '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" - "http://www.w3.org/TR/html4/loose.dtd"><html><head><title></title></head><body>' + html + "</body></html>", wrapper_id + return '<!DOCTYPE html><html><head><title></title></head><body>' + html + "</body></html>", wrapper_id end # the extractions will also produce image files, which go in the diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb index 3f551f420..5c845ead3 100644 --- a/app/models/incoming_message.rb +++ b/app/models/incoming_message.rb @@ -535,7 +535,7 @@ class IncomingMessage < ActiveRecord::Base 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) - rescue Iconv::InvalidEncoding, Iconv::IllegalSequence + rescue Iconv::InvalidEncoding, Iconv::IllegalSequence, Iconv::InvalidCharacter if source_charset != "utf-8" source_charset = "utf-8" retry diff --git a/app/models/info_request.rb b/app/models/info_request.rb index cee9eb959..237364f56 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -543,20 +543,16 @@ 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? - return true if InfoRequest.requires_admin_states.include?(described_state) - return false + ['requires_admin', 'error_message', 'attention_requested'].include?(described_state) end # change status, including for last event for later historical purposes - def set_described_state(new_state, set_by = nil) + def set_described_state(new_state, set_by = nil, message = "") + old_described_state = described_state ActiveRecord::Base.transaction do self.awaiting_description = false - last_event = self.get_last_event + last_event = self.info_request_events.last last_event.described_state = new_state self.described_state = new_state last_event.save! @@ -568,9 +564,23 @@ 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) + RequestMailer.deliver_requires_admin(self, set_by, message) end end + + unless set_by.nil? || is_actual_owning_user?(set_by) || described_state == 'attention_requested' + # Log the status change by someone other than the requester + event = log_event("status_update", + { :user_id => set_by.id, + :old_described_state => old_described_state, + :described_state => described_state, + }) + # Create a classification event for league tables + RequestClassification.create!(:user_id => set_by.id, + :info_request_event_id => event.id) + + RequestMailer.deliver_old_unclassified_updated(self) if !is_external? + end end # Work out what the situation of the request is. In addition to values of @@ -719,41 +729,28 @@ public self.info_request_events.create!(:event_type => type, :params => params) end + def response_events + self.info_request_events.select{|e| e.response?} + end + # The last response is the default one people might want to reply to def get_last_response_event_id - for e in self.info_request_events.reverse - if e.event_type == 'response' - return e.id - end - end - return nil - + get_last_response_event.id if get_last_response_event end def get_last_response_event - for e in self.info_request_events.reverse - if e.event_type == 'response' - return e - end - end - return nil + response_events.last end def get_last_response - last_response_event = self.get_last_response_event - if last_response_event.nil? - return nil - else - return last_response_event.incoming_message - end + get_last_response_event.incoming_message if get_last_response_event + end + + def outgoing_events + info_request_events.select{|e| e.outgoing? } end # The last outgoing message def get_last_outgoing_event - for e in self.info_request_events.reverse - if [ 'sent', 'followup_sent' ].include?(e.event_type) - return e - end - end - return nil + outgoing_events.last end # Text from the the initial request, for use in summary display @@ -794,16 +791,6 @@ 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 @@ -821,46 +808,31 @@ public # Display version of status def InfoRequest.get_status_description(status) - if status == 'waiting_classification' - _("Awaiting classification.") - elsif status == 'waiting_response' - _("Awaiting response.") - elsif status == 'waiting_response_overdue' - _("Delayed.") - elsif status == 'waiting_response_very_overdue' - _("Long overdue.") - elsif status == 'not_held' - _("Information not held.") - elsif status == 'rejected' - _("Refused.") - elsif status == 'partially_successful' - _("Partially successful.") - elsif status == 'successful' - _("Successful.") - elsif status == 'waiting_clarification' - _("Waiting clarification.") - elsif status == 'gone_postal' - _("Handled by post.") - elsif status == 'internal_review' - _("Awaiting internal review.") - elsif status == 'error_message' - _("Delivery error") - elsif status == 'requires_admin' - _("Unusual response.") - elsif status == 'attention_requested' - _("Reported for administrator attention.") - elsif status == 'user_withdrawn' - _("Withdrawn by the requester.") - elsif status == 'vexatious' - _("Considered by administrators as vexatious and hidden from site.") - elsif status == 'not_foi' - _("Considered by administrators as not an FOI request and hidden from site.") + descriptions = { + 'waiting_classification' => _("Awaiting classification."), + 'waiting_response' => _("Awaiting response."), + 'waiting_response_overdue' => _("Delayed."), + 'waiting_response_very_overdue' => _("Long overdue."), + 'not_held' => _("Information not held."), + 'rejected' => _("Refused."), + 'partially_successful' => _("Partially successful."), + 'successful' => _("Successful."), + 'waiting_clarification' => _("Waiting clarification."), + 'gone_postal' => _("Handled by post."), + 'internal_review' => _("Awaiting internal review."), + 'error_message' => _("Delivery error"), + 'requires_admin' => _("Unusual response."), + 'attention_requested' => _("Reported for administrator attention."), + 'user_withdrawn' => _("Withdrawn by the requester."), + 'vexatious' => _("Considered by administrators as vexatious and hidden from site."), + 'not_foi' => _("Considered by administrators as not an FOI request and hidden from site."), + } + if descriptions[status] + descriptions[status] + elsif respond_to?(:theme_display_status) + theme_display_status(status) else - begin - return self.theme_display_status(status) - rescue NoMethodError - raise _("unknown status ") + status - end + raise _("unknown status ") + status end end @@ -987,13 +959,8 @@ public end def is_old_unclassified? - return false if is_external? - return false if !awaiting_description - return false if url_title == 'holding_pen' - last_response_event = get_last_response_event - return false unless last_response_event - return false if last_response_event.created_at >= Time.now - OLD_AGE_IN_DAYS - return true + !is_external? && awaiting_description && url_title != 'holding_pen' && get_last_response_event && + Time.now > get_last_response_event.created_at + OLD_AGE_IN_DAYS end # List of incoming messages to followup, by unique email diff --git a/app/models/info_request_event.rb b/app/models/info_request_event.rb index 09eba31ab..871b81b1f 100644 --- a/app/models/info_request_event.rb +++ b/app/models/info_request_event.rb @@ -77,17 +77,18 @@ class InfoRequestEvent < ActiveRecord::Base end def user_can_view?(user) - if !self.info_request.user_can_view?(user) + unless info_request.user_can_view?(user) raise "internal error, called user_can_view? on event when there is not permission to view entire request" end - if self.prominence == 'hidden' - return User.view_hidden_requests?(user) - end - if self.prominence == 'requester_only' - return self.info_request.is_owning_user?(user) + case prominence + when 'hidden' + User.view_hidden_requests?(user) + when 'requester_only' + info_request.is_owning_user?(user) + else + true end - return true end @@ -363,16 +364,19 @@ class InfoRequestEvent < ActiveRecord::Base end def is_sent_sort? - if [ 'sent', 'resent'].include?(self.event_type) - return true - end - return false + ['sent', 'resent'].include?(event_type) end + def is_followup_sort? - if [ 'followup_sent', 'followup_resent'].include?(self.event_type) - return true - end - return false + ['followup_sent', 'followup_resent'].include?(event_type) + end + + def outgoing? + ['sent', 'followup_sent'].include?(event_type) + end + + def response? + event_type == 'response' end def same_email_as_previous_send? diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb index 3cb0ebf49..c75894e6a 100644 --- a/app/models/outgoing_message.rb +++ b/app/models/outgoing_message.rb @@ -30,6 +30,7 @@ class OutgoingMessage < ActiveRecord::Base 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' @@ -136,32 +137,6 @@ class OutgoingMessage < ActiveRecord::Base end end - # Check have edited letter - def validate - 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' - 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 - # Deliver outgoing message # Note: You can test this from script/console with, say: # InfoRequest.find(1).outgoing_messages[0].send_message @@ -275,6 +250,33 @@ class OutgoingMessage < ActiveRecord::Base yield(column.human_name, self.send(column.name), column.type.to_s, column.name) end end + + private + + 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' + 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 diff --git a/app/models/profile_photo.rb b/app/models/profile_photo.rb index bcb0390b5..47ce221b2 100644 --- a/app/models/profile_photo.rb +++ b/app/models/profile_photo.rb @@ -23,6 +23,8 @@ class ProfilePhoto < ActiveRecord::Base belongs_to :user + validate :data_and_draft_checks + # deliberately don't strip_attributes, so keeps raw photo properly attr_accessor :x, :y, :w, :h @@ -81,7 +83,9 @@ class ProfilePhoto < ActiveRecord::Base end end - def validate + private + + def data_and_draft_checks if self.data.nil? errors.add(:data, N_("Please choose a file containing your photo.")) return diff --git a/app/models/public_body.rb b/app/models/public_body.rb index f71520ee6..b4d36fe91 100644 --- a/app/models/public_body.rb +++ b/app/models/public_body.rb @@ -35,6 +35,8 @@ class PublicBody < ActiveRecord::Base validates_uniqueness_of :short_name, :message => N_("Short name is already taken"), :if => Proc.new { |pb| pb.short_name != "" } validates_uniqueness_of :name, :message => N_("Name is already taken") + validate :request_email_if_requestable + has_many :info_requests, :order => 'created_at desc' has_many :track_things, :order => 'created_at desc' has_many :censor_rules, :order => 'created_at desc' @@ -104,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 @@ -135,15 +134,6 @@ class PublicBody < ActiveRecord::Base self.first_letter = self.name.scan(/./mu)[0].upcase end - def validate - # Request_email can be blank, meaning we don't have details - if self.is_requestable? - unless MySociety::Validate.is_valid_email(self.request_email) - errors.add(:request_email, "Request email doesn't look like a valid email address") - end - end - end - # If tagged "not_apply", then FOI/EIR no longer applies to authority at all def not_apply? return self.has_tag?('not_apply') @@ -250,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 @@ -336,7 +326,7 @@ 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( @@ -374,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 @@ -417,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}" @@ -452,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}" @@ -642,4 +632,12 @@ class PublicBody < ActiveRecord::Base end end + def request_email_if_requestable + # Request_email can be blank, meaning we don't have details + if self.is_requestable? + unless MySociety::Validate.is_valid_email(self.request_email) + errors.add(:request_email, "Request email doesn't look like a valid email address") + end + end + end end diff --git a/app/models/request_mailer.rb b/app/models/request_mailer.rb index 493d6961c..955f73ef4 100644 --- a/app/models/request_mailer.rb +++ b/app/models/request_mailer.rb @@ -5,7 +5,11 @@ # 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 @@ -58,32 +62,28 @@ class RequestMailer < ApplicationMailer end # An FOI response is outside the scope of the system, and needs admin attention - def requires_admin(info_request, set_by = nil) - if !set_by.nil? - user = set_by - else - user = info_request.user - end + 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 = main_url(request_url(info_request)) - admin_url = request_admin_url(info_request) - @body = {:reported_by => user, :info_request => info_request, :url => url, :admin_url => admin_url } + 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 = main_url(incoming_message_url(incoming_message)) + 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 + @subject = (_("New response to your FOI request - ") + info_request.title).html_safe @body = { :incoming_message => incoming_message, :info_request => info_request, :url => url } end @@ -102,7 +102,7 @@ class RequestMailer < ApplicationMailer '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 + @subject = (_("Delayed response to your FOI request - ") + info_request.title).html_safe @body = { :info_request => info_request, :url => url } end @@ -121,7 +121,7 @@ class RequestMailer < ApplicationMailer '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 + @subject = (_("You're long overdue a response to your FOI request - ") + info_request.title).html_safe @body = { :info_request => info_request, :url => url } end @@ -131,7 +131,7 @@ class RequestMailer < ApplicationMailer # Make a link going to the form to describe state, and which logs the # user in. post_redirect = PostRedirect.new( - :uri => main_url(request_url(info_request)) + "#describe_state_form_1", + :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) @@ -153,7 +153,7 @@ class RequestMailer < ApplicationMailer 'X-Auto-Response-Suppress' => 'OOF' @recipients = info_request.user.name_and_email @subject = _("Someone has updated the status of your request") - url = main_url(request_url(info_request)) + url = request_url(info_request) @body = {:info_request => info_request, :url => url} end @@ -173,7 +173,7 @@ class RequestMailer < ApplicationMailer '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 + @subject = (_("Clarify your FOI request - ") + info_request.title).html_safe @body = { :incoming_message => incoming_message, :info_request => info_request, :url => url } end @@ -184,8 +184,8 @@ class RequestMailer < ApplicationMailer '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 - @body = { :comment => comment, :info_request => info_request, :url => main_url(comment_url(comment)) } + @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 @@ -193,8 +193,8 @@ class RequestMailer < ApplicationMailer '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 - @body = { :count => count, :info_request => info_request, :url => main_url(comment_url(earliest_unalerted_comment)) } + @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. diff --git a/app/models/track_mailer.rb b/app/models/track_mailer.rb index 7dfa87f52..7262c82f3 100644 --- a/app/models/track_mailer.rb +++ b/app/models/track_mailer.rb @@ -7,7 +7,7 @@ class TrackMailer < ApplicationMailer def event_digest(user, email_about_things) post_redirect = PostRedirect.new( - :uri => main_url(user_url(user)) + "#email_subscriptions", + :uri => user_url(user) + "#email_subscriptions", :user_id => user.id) post_redirect.save! unsubscribe_url = confirm_url(:email_token => post_redirect.email_token) @@ -91,10 +91,9 @@ class TrackMailer < ApplicationMailer 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 + I18n.with_locale(user.get_locale) do + TrackMailer.deliver_event_digest(user, email_about_things) + end end # Record that we've now sent those alerts to that user diff --git a/app/models/track_thing.rb b/app/models/track_thing.rb index 81800f0ae..a0c74bdb6 100644 --- a/app/models/track_thing.rb +++ b/app/models/track_thing.rb @@ -23,6 +23,8 @@ require 'set' +# TODO: TrackThing looks like a good candidate for single table inheritance + class TrackThing < ActiveRecord::Base belongs_to :tracking_user, :class_name => 'User' validates_presence_of :track_query @@ -199,7 +201,8 @@ class TrackThing < ActiveRecord::Base if self.track_type == 'request_updates' @params = { # Website - :list_description => _("'{{link_to_request}}', a request", :link_to_request => "<a href=\"/request/" + CGI.escapeHTML(self.info_request.url_title) + "\">" + CGI.escapeHTML(self.info_request.title) + "</a>"), # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how + :list_description => _("'{{link_to_request}}', a request", + :link_to_request => ("<a href=\"/request/" + CGI.escapeHTML(self.info_request.url_title) + "\">" + CGI.escapeHTML(self.info_request.title) + "</a>").html_safe), # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how :verb_on_page => _("Follow this request"), :verb_on_page_already => _("You are already following this request"), # Email @@ -250,7 +253,7 @@ class TrackThing < ActiveRecord::Base elsif self.track_type == 'public_body_updates' @params = { # Website - :list_description => _("'{{link_to_authority}}', a public authority", :link_to_authority => "<a href=\"/body/" + CGI.escapeHTML(self.public_body.url_name) + "\">" + CGI.escapeHTML(self.public_body.name) + "</a>"), # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how + :list_description => _("'{{link_to_authority}}', a public authority", :link_to_authority => ("<a href=\"/body/" + CGI.escapeHTML(self.public_body.url_name) + "\">" + CGI.escapeHTML(self.public_body.name) + "</a>").html_safe), # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how :verb_on_page => _("Follow requests to {{public_body_name}}",:public_body_name=>CGI.escapeHTML(self.public_body.name)), :verb_on_page_already => _("You are already following requests to {{public_body_name}}", :public_body_name=>CGI.escapeHTML(self.public_body.name)), # Email @@ -266,7 +269,7 @@ class TrackThing < ActiveRecord::Base elsif self.track_type == 'user_updates' @params = { # Website - :list_description => _("'{{link_to_user}}', a person", :link_to_user => "<a href=\"/user/" + CGI.escapeHTML(self.tracked_user.url_name) + "\">" + CGI.escapeHTML(self.tracked_user.name) + "</a>"), # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how + :list_description => _("'{{link_to_user}}', a person", :link_to_user => ("<a href=\"/user/" + CGI.escapeHTML(self.tracked_user.url_name) + "\">" + CGI.escapeHTML(self.tracked_user.name) + "</a>").html_safe), # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how :verb_on_page => _("Follow this person"), :verb_on_page_already => _("You are already following this person"), # Email @@ -282,7 +285,7 @@ class TrackThing < ActiveRecord::Base elsif self.track_type == 'search_query' @params = { # Website - :list_description => "<a href=\"/search/" + CGI.escapeHTML(self.track_query) + "/newest/advanced\">" + self.track_query_description + "</a>", # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how + :list_description => ("<a href=\"/search/" + CGI.escapeHTML(self.track_query) + "/newest/advanced\">" + CGI.escapeHTML(self.track_query_description) + "</a>").html_safe, # XXX yeuch, sometimes I just want to call view helpers from the model, sorry! can't work out how :verb_on_page => _("Follow things matching this search"), :verb_on_page_already => _("You are already following things matching this search"), # Email diff --git a/app/models/user.rb b/app/models/user.rb index 6e1e21481..612ac7fa2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -50,6 +50,8 @@ class User < ActiveRecord::Base 'super', ], :message => N_('Admin level is not included in list') + validate :email_and_name_are_valid + acts_as_xapian :texts => [ :name, :about_me ], :values => [ [ :created_at_numeric, 1, "created_at", :number ] # for sorting @@ -96,27 +98,13 @@ 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 self.comments.find(:all, :conditions => 'visible') end - def validate - if self.email != "" && !MySociety::Validate.is_valid_email(self.email) - errors.add(:email, _("Please enter a valid email address")) - end - if MySociety::Validate.is_valid_email(self.name) - errors.add(:name, _("Please enter your name, not your email address, in the name field.")) - end - end - # Don't display any leading/trailing spaces # XXX we have strip_attributes! now, so perhaps this can be removed (might # be still needed for existing cases) @@ -361,12 +349,13 @@ class User < ActiveRecord::Base end # Return about me text for display as HTML + # TODO: Move this to a view helper def get_about_me_for_html_display text = self.about_me.strip text = CGI.escapeHTML(text) text = MySociety::Format.make_clickable(text, :contract => 1) text = text.gsub(/\n/, '<br>') - return text + return text.html_safe end def json_for_api @@ -413,6 +402,15 @@ class User < ActiveRecord::Base self.salt = self.object_id.to_s + rand.to_s 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")) + end + if MySociety::Validate.is_valid_email(self.name) + errors.add(:name, _("Please enter your name, not your email address, in the name field.")) + end + end + ## Class methods def User.encrypted_password(password, salt) string_to_hash = password + salt # XXX need to add a secret here too? |