aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Rakefile2
-rw-r--r--app/controllers/admin_controller.rb1
-rw-r--r--app/controllers/public_body_controller.rb25
-rw-r--r--app/controllers/request_controller.rb18
-rw-r--r--app/controllers/user_controller.rb58
-rwxr-xr-xapp/helpers/link_to_helper.rb11
-rw-r--r--app/models/foi_attachment.rb321
-rw-r--r--app/models/incoming_message.rb710
-rw-r--r--app/models/info_request.rb27
-rw-r--r--app/models/public_body.rb48
-rw-r--r--app/models/request_mailer.rb1
-rw-r--r--app/views/layouts/default.rhtml11
-rw-r--r--app/views/public_body/_search_ahead.rhtml2
-rw-r--r--app/views/request/_search_ahead.rhtml2
-rw-r--r--app/views/request/upload_response.rhtml2
-rw-r--r--app/views/user/bad_token.rhtml2
-rw-r--r--app/views/user/show.rhtml36
-rw-r--r--config/boot.rb2
-rw-r--r--config/environment.rb19
-rw-r--r--config/general.yml-example2
-rw-r--r--config/initializers/fast_gettext.rb2
-rw-r--r--config/routes.rb4
-rw-r--r--config/test.yml118
-rw-r--r--db/.gitignore1
-rw-r--r--db/migrate/104_create_foi_attachments.rb18
-rw-r--r--db/migrate/105_extend_incoming_message.rb17
-rw-r--r--db/migrate/106_add_hex_digest_to_foi_attachment.rb9
-rw-r--r--db/migrate/107_add_date_parsed_field_to_incoming_message.rb9
-rw-r--r--db/migrate/108_change_safe_mail_from_to_mail_from.rb11
-rw-r--r--doc/CHANGES.md22
-rw-r--r--doc/INSTALL.md72
-rw-r--r--doc/THEMES.md33
-rw-r--r--doc/todo.txt559
-rw-r--r--lib/languages.rb1
-rw-r--r--lib/old_rubygems_patch.rb6
-rw-r--r--lib/ruby19.rb8
-rw-r--r--lib/sendmail_return_path.rb2
-rw-r--r--lib/tasks/rspec.rake3
-rw-r--r--lib/tmail_extensions.rb2
-rw-r--r--lib/tnef.rb2
-rw-r--r--public/images/logo.pngbin1064 -> 1348 bytes
-rw-r--r--public/images/quote-marks.pngbin470 -> 464 bytes
-rw-r--r--public/stylesheets/main.css2612
-rw-r--r--public/stylesheets/print.css6
-rw-r--r--public/stylesheets/theme.css1141
-rwxr-xr-xscript/about2
-rwxr-xr-xscript/breakpointer2
-rw-r--r--script/cache-incoming-emails9
-rwxr-xr-xscript/clear-caches2
-rwxr-xr-xscript/console2
-rwxr-xr-xscript/destroy2
-rwxr-xr-xscript/fill-database-caches11
-rwxr-xr-xscript/generate2
-rwxr-xr-xscript/handle-mail-replies21
-rwxr-xr-xscript/performance/benchmarker2
-rwxr-xr-xscript/performance/profiler2
-rwxr-xr-xscript/plugin2
-rwxr-xr-xscript/process/inspector2
-rwxr-xr-xscript/process/reaper2
-rwxr-xr-xscript/process/spawner2
-rwxr-xr-xscript/runner2
-rwxr-xr-xscript/server2
-rwxr-xr-xscript/wraptest2
-rw-r--r--spec/controllers/admin_public_body_controller_spec.rb8
-rw-r--r--spec/controllers/admin_request_controller_spec.rb4
-rw-r--r--spec/controllers/admin_track_controller_spec.rb2
-rw-r--r--spec/controllers/admin_user_controller_spec.rb2
-rw-r--r--spec/controllers/comment_controller_spec.rb2
-rw-r--r--spec/controllers/general_controller_spec.rb26
-rw-r--r--spec/controllers/public_body_controller_spec.rb34
-rw-r--r--spec/controllers/request_controller_spec.rb144
-rw-r--r--spec/controllers/request_game_controller_spec.rb2
-rw-r--r--spec/controllers/track_controller_spec.rb8
-rw-r--r--spec/controllers/user_controller_spec.rb17
-rw-r--r--spec/fixtures/files/email-folding-example-1.txt32
-rw-r--r--spec/fixtures/files/email-folding-example-1.txt.expected10
-rw-r--r--spec/fixtures/files/email-folding-example-10.txt52
-rw-r--r--spec/fixtures/files/email-folding-example-10.txt.expected25
-rw-r--r--spec/fixtures/files/email-folding-example-2.txt7
-rw-r--r--spec/fixtures/files/email-folding-example-2.txt.expected4
-rw-r--r--spec/fixtures/files/email-folding-example-3.txt18
-rw-r--r--spec/fixtures/files/email-folding-example-3.txt.expected5
-rw-r--r--spec/fixtures/files/email-folding-example-4.txt37
-rw-r--r--spec/fixtures/files/email-folding-example-4.txt.expected15
-rw-r--r--spec/fixtures/files/email-folding-example-5.txt35
-rw-r--r--spec/fixtures/files/email-folding-example-5.txt.expected24
-rw-r--r--spec/fixtures/files/email-folding-example-6.txt30
-rw-r--r--spec/fixtures/files/email-folding-example-6.txt.expected15
-rw-r--r--spec/fixtures/files/email-folding-example-7.txt30
-rw-r--r--spec/fixtures/files/email-folding-example-7.txt.expected16
-rw-r--r--spec/fixtures/files/email-folding-example-8.txt18
-rw-r--r--spec/fixtures/files/email-folding-example-8.txt.expected6
-rw-r--r--spec/fixtures/files/email-folding-example-9.txt29
-rw-r--r--spec/fixtures/files/email-folding-example-9.txt.expected9
-rw-r--r--spec/fixtures/files/iso8859_2_email.html18
-rw-r--r--spec/fixtures/files/iso8859_2_raw_email.email50
-rw-r--r--spec/fixtures/files/quoted-subject-iso8859-1.email462
-rw-r--r--spec/fixtures/files/track-response-abcmail-oof.email80
-rw-r--r--spec/fixtures/files/track-response-outlook-oof.email587
-rw-r--r--spec/fixtures/foi_attachments.yml1
-rw-r--r--spec/integration/errors_spec.rb22
-rw-r--r--spec/integration/search_request_spec.rb22
-rw-r--r--spec/integration/view_request_spec.rb32
-rw-r--r--spec/lib/tmail_extensions_spec.rb10
-rw-r--r--spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb2
-rw-r--r--spec/models/has_tag_string_tag_spec.rb2
-rw-r--r--spec/models/incoming_message_spec.rb167
-rw-r--r--spec/models/info_request_event_spec.rb25
-rw-r--r--spec/models/info_request_spec.rb16
-rw-r--r--spec/models/outgoing_mailer_spec.rb4
-rw-r--r--spec/models/outgoing_message_spec.rb4
-rw-r--r--spec/models/public_body_spec.rb40
-rw-r--r--spec/models/request_mailer_spec.rb2
-rw-r--r--spec/models/track_thing_spec.rb2
-rw-r--r--spec/models/user_spec.rb14
-rw-r--r--spec/models/xapian_spec.rb81
-rw-r--r--spec/script/handle-mail-replies_spec.rb10
-rw-r--r--spec/spec_helper.rb44
-rw-r--r--spec/views/public_body/show.rhtml_spec.rb2
-rw-r--r--spec/views/request/_after_actions.rhtml_spec.rb2
-rw-r--r--spec/views/request/_describe_state.rhtml_spec.rb2
-rw-r--r--spec/views/request/list.rhtml_spec.rb2
-rw-r--r--spec/views/request/show.rhtml_spec.rb2
-rw-r--r--spec/views/request_game/play.rhtml_spec.rb2
-rw-r--r--vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb184
-rw-r--r--vendor/plugins/acts_as_xapian/lib/tasks/xapian.rake23
-rw-r--r--vendor/plugins/has_tag_string/lib/has_tag_string.rb2
m---------vendor/rails-locales0
129 files changed, 4866 insertions, 3748 deletions
diff --git a/.gitignore b/.gitignore
index 7d06aa9e9..efb25b8d8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,6 @@
.DS_Store
.autotest
/db/test_structure.sql
-moo.txt
*#*#
TAGS
/vendor/plugins/*theme
diff --git a/Rakefile b/Rakefile
index 7d7ac5bc7..d4ebade51 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,7 +5,7 @@ require(File.join(File.dirname(__FILE__), 'config', 'boot'))
require 'rake'
require 'rake/testtask'
-require 'rake/rdoctask'
+require 'rdoc/task'
require 'tasks/rails'
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
index 0bfbcd3d1..adb506b91 100644
--- a/app/controllers/admin_controller.rb
+++ b/app/controllers/admin_controller.rb
@@ -53,6 +53,7 @@ class AdminController < ApplicationController
authenticate_or_request_with_http_basic do |user_name, password|
if user_name == config_username && password == config_password
session[:using_admin] = 1
+ request.env['REMOTE_USER'] = user_name
else
request_http_basic_authentication
end
diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb
index 251ab5efe..62229a441 100644
--- a/app/controllers/public_body_controller.rb
+++ b/app/controllers/public_body_controller.rb
@@ -91,31 +91,31 @@ class PublicBodyController < ApplicationController
@query = "%#{params[:public_body_query].nil? ? "" : params[:public_body_query]}%"
@tag = params[:tag]
@locale = self.locale_from_params()
-
+ default_locale = I18n.default_locale.to_s
locale_condition = "(upper(public_body_translations.name) LIKE upper(?)
OR upper(public_body_translations.notes) LIKE upper (?))
AND public_body_translations.locale = ?
AND public_bodies.id <> #{PublicBody.internal_admin_body.id}"
if @tag.nil? or @tag == "all"
@tag = "all"
- conditions = [locale_condition, @query, @query, @locale]
+ conditions = [locale_condition, @query, @query, default_locale]
elsif @tag == 'other'
category_list = PublicBodyCategories::get().tags().map{|c| "'"+c+"'"}.join(",")
conditions = [locale_condition + ' AND (select count(*) from has_tag_string_tags where has_tag_string_tags.model_id = public_bodies.id
and has_tag_string_tags.model = \'PublicBody\'
- and has_tag_string_tags.name in (' + category_list + ')) = 0', @query, @query, @locale]
+ and has_tag_string_tags.name in (' + category_list + ')) = 0', @query, @query, default_locale]
elsif @tag.size == 1
@tag.upcase!
- conditions = [locale_condition + ' AND public_body_translations.first_letter = ?', @query, @query, @locale, @tag]
+ conditions = [locale_condition + ' AND public_body_translations.first_letter = ?', @query, @query, default_locale, @tag]
elsif @tag.include?(":")
name, value = HasTagString::HasTagStringTag.split_tag_into_name_value(@tag)
conditions = [locale_condition + ' AND (select count(*) from has_tag_string_tags where has_tag_string_tags.model_id = public_bodies.id
and has_tag_string_tags.model = \'PublicBody\'
- and has_tag_string_tags.name = ? and has_tag_string_tags.value = ?) > 0', @query, @query, @locale, name, value]
+ and has_tag_string_tags.name = ? and has_tag_string_tags.value = ?) > 0', @query, @query, default_locale, name, value]
else
conditions = [locale_condition + ' AND (select count(*) from has_tag_string_tags where has_tag_string_tags.model_id = public_bodies.id
and has_tag_string_tags.model = \'PublicBody\'
- and has_tag_string_tags.name = ?) > 0', @query, @query, @locale, @tag]
+ and has_tag_string_tags.name = ?) > 0', @query, @query, default_locale, @tag]
end
if @tag == "all"
@description = ""
@@ -185,11 +185,14 @@ class PublicBodyController < ApplicationController
def search_typeahead
# Since acts_as_xapian doesn't support the Partial match flag, we work around it
# by making the last work a wildcard, which is quite the same
- query = params[:q] + '*'
-
- query = query.split(' ').join(' OR ') # XXX: HACK for OR instead of default AND!
- @xapian_requests = perform_search([PublicBody], query, 'relevant', nil, 5)
-
+ query = params[:q]
+ query = query.split(' ')
+ if query.last.nil? || query.last.strip.length < 3
+ @xapian_requests = nil
+ else
+ query = query.join(' OR ') # XXX: HACK for OR instead of default AND!
+ @xapian_requests = perform_search([PublicBody], query, 'relevant', nil, 5)
+ end
render :partial => "public_body/search_ahead"
end
end
diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb
index 4b7884065..dad5e81cd 100644
--- a/app/controllers/request_controller.rb
+++ b/app/controllers/request_controller.rb
@@ -676,6 +676,7 @@ class RequestController < ApplicationController
# Internal function
def get_attachment_internal(html_conversion)
@incoming_message = IncomingMessage.find(params[:incoming_message_id])
+ @incoming_message.parse_raw_email!
@info_request = @incoming_message.info_request
if @incoming_message.info_request_id != params[:id].to_i
raise sprintf("Incoming message %d does not belong to request %d", @incoming_message.info_request_id, params[:id])
@@ -690,7 +691,6 @@ class RequestController < ApplicationController
# check permissions
raise "internal error, pre-auth filter should have caught this" if !@info_request.user_can_view?(authenticated_user)
-
@attachment = IncomingMessage.get_attachment_by_url_part_number(@incoming_message.get_attachments_for_display, @part_number)
raise ActiveRecord::RecordNotFound.new("attachment not found part number " + @part_number.to_s + " incoming_message " + @incoming_message.id.to_s) if @attachment.nil?
@@ -713,6 +713,7 @@ class RequestController < ApplicationController
:email => _("Then you can upload an FOI response. "),
:email_subject => _("Confirm your account on {{site_name}}",:site_name=>site_name)
}
+
if !authenticated?(@reason_params)
return
end
@@ -754,11 +755,14 @@ class RequestController < ApplicationController
def search_typeahead
# Since acts_as_xapian doesn't support the Partial match flag, we work around it
# by making the last work a wildcard, which is quite the same
- query = params[:q] + '*'
-
- query = query.split(' ').join(' OR ') # XXX: HACK for OR instead of default AND!
- @xapian_requests = perform_search([InfoRequestEvent], query, 'relevant', 'request_collapse', 5)
-
+ query = params[:q]
+ query = query.split(' ')
+ if query.last.nil? || query.last.strip.length < 3
+ @xapian_requests = nil
+ else
+ query = query.join(' OR ') # XXX: HACK for OR instead of default AND!
+ @xapian_requests = perform_search([InfoRequestEvent], query, 'relevant', 'request_collapse', 5)
+ end
render :partial => "request/search_ahead.rhtml"
end
@@ -774,7 +778,7 @@ class RequestController < ApplicationController
:email => _("Then you can download a zip file of {{info_request_title}}.",:info_request_title=>info_request.title),
:email_subject => _("Log in to download a zip file of {{info_request_title}}",:info_request_title=>info_request.title)
)
- updated = Digest::SHA1.hexdigest(info_request.get_last_event.created_at.to_s + info_request.updated_at.to_s)
+ updated = Digest::SHA1.hexdigest(info_request.get_last_event.created_at.to_i.to_s + info_request.updated_at.to_i.to_s)
@url_path = "/download/#{updated[0..1]}/#{updated}/#{params[:url_title]}.zip"
file_path = File.join(File.dirname(__FILE__), '../../cache/zips', @url_path)
if !File.exists?(file_path)
diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb
index 96dbfba74..fc29a847c 100644
--- a/app/controllers/user_controller.rb
+++ b/app/controllers/user_controller.rb
@@ -23,7 +23,17 @@ class UserController < ApplicationController
redirect_to :url_name => MySociety::Format.simplify_url_part(params[:url_name], 'user', 32), :status => :moved_permanently
return
end
-
+ if params[:view].nil?
+ @show_requests = true
+ @show_profile = true
+ elsif params[:view] == 'profile'
+ @show_profile = true
+ @show_requests = false
+ elsif params[:view] == 'requests'
+ @show_profile = false
+ @show_requests = true
+ end
+
@display_user = User.find(:first, :conditions => [ "url_name = ? and email_confirmed = ?", params[:url_name], true ])
if not @display_user
raise ActiveRecord::RecordNotFound.new("user not found, url_name=" + params[:url_name])
@@ -34,31 +44,33 @@ class UserController < ApplicationController
# Use search query for this so can collapse and paginate easily
# XXX really should just use SQL query here rather than Xapian.
- begin
- requests_query = 'requested_by:' + @display_user.url_name
- comments_query = 'commented_by:' + @display_user.url_name
- if !params[:user_query].nil?
- requests_query += " " + params[:user_query]
- comments_query += " " + params[:user_query]
- @match_phrase = _("{{search_results}} matching '{{query}}'", :search_results => "", :query => params[:user_query])
- end
- @xapian_requests = perform_search([InfoRequestEvent], requests_query, 'newest', 'request_collapse')
- @xapian_comments = perform_search([InfoRequestEvent], comments_query, 'newest', nil)
-
- if (@page > 1)
- @page_desc = " (page " + @page.to_s + ")"
- else
- @page_desc = ""
+ if @show_requests
+ begin
+ requests_query = 'requested_by:' + @display_user.url_name
+ comments_query = 'commented_by:' + @display_user.url_name
+ if !params[:user_query].nil?
+ requests_query += " " + params[:user_query]
+ comments_query += " " + params[:user_query]
+ @match_phrase = _("{{search_results}} matching '{{query}}'", :search_results => "", :query => params[:user_query])
+ end
+ @xapian_requests = perform_search([InfoRequestEvent], requests_query, 'newest', 'request_collapse')
+ @xapian_comments = perform_search([InfoRequestEvent], comments_query, 'newest', nil)
+
+ if (@page > 1)
+ @page_desc = " (page " + @page.to_s + ")"
+ else
+ @page_desc = ""
+ end
+ rescue
+ @xapian_requests = nil
+ @xapian_comments = nil
end
- rescue
- @xapian_requests = nil
- @xapian_comments = nil
- end
- # Track corresponding to this page
- @track_thing = TrackThing.create_track_for_user(@display_user)
- @feed_autodetect = [ { :url => do_track_url(@track_thing, 'feed'), :title => @track_thing.params[:title_in_rss], :has_json => true } ]
+ # Track corresponding to this page
+ @track_thing = TrackThing.create_track_for_user(@display_user)
+ @feed_autodetect = [ { :url => do_track_url(@track_thing, 'feed'), :title => @track_thing.params[:title_in_rss], :has_json => true } ]
+ end
# All tracks for the user
if @is_you
@track_things = TrackThing.find(:all, :conditions => ["tracking_user_id = ? and track_medium = ?", @display_user.id, 'email_daily'], :order => 'created_at desc')
diff --git a/app/helpers/link_to_helper.rb b/app/helpers/link_to_helper.rb
index 54b8d69d0..5866c31f0 100755
--- a/app/helpers/link_to_helper.rb
+++ b/app/helpers/link_to_helper.rb
@@ -185,9 +185,16 @@ module LinkToHelper
end
- def main_url(relative_path)
+ def main_url(relative_path, append = nil)
url_prefix = "http://" + MySociety::Config.get("DOMAIN", '127.0.0.1:3000')
- return url_prefix + relative_path
+ url = url_prefix + relative_path
+ if !append.nil?
+ env = Rack::MockRequest.env_for(url)
+ req = Rack::Request.new(env)
+ req.path_info += append
+ url = req.url
+ end
+ return url
end
# Basic date format
diff --git a/app/models/foi_attachment.rb b/app/models/foi_attachment.rb
new file mode 100644
index 000000000..057dcdb69
--- /dev/null
+++ b/app/models/foi_attachment.rb
@@ -0,0 +1,321 @@
+# encoding: UTF-8
+
+# models/foi_attachment.rb:
+# 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/
+# This is the type which is used to send data about attachments to the view
+
+require 'digest'
+
+class FoiAttachment < ActiveRecord::Base
+ belongs_to :incoming_message
+ validates_presence_of :content_type
+ validates_presence_of :filename
+ validates_presence_of :display_size
+
+ before_validation :ensure_filename!, :only => [:filename]
+ before_destroy :delete_cached_file!
+
+ def directory
+ base_dir = File.join("cache", "attachments_#{ENV['RAILS_ENV']}")
+ return File.join(base_dir, self.hexdigest[0..2])
+ end
+
+ def filepath
+ File.join(self.directory, self.hexdigest)
+ end
+
+ def delete_cached_file!
+ begin
+ File.delete(self.filepath)
+ rescue
+ end
+ end
+
+ def body=(d)
+ self.hexdigest = Digest::MD5.hexdigest(d)
+ if !File.exists?(self.directory)
+ FileUtils.mkdir_p self.directory
+ end
+ File.open(self.filepath, "wb") { |file|
+ file.write d
+ }
+ update_display_size!
+ end
+
+ def body
+ if @cached_body.nil?
+ @cached_body = File.open(self.filepath, "rb" ).read
+ end
+ return @cached_body
+ end
+
+ # List of DSN codes taken from RFC 3463
+ # http://tools.ietf.org/html/rfc3463
+ DsnToMessage = {
+ 'X.1.0' => 'Other address status',
+ 'X.1.1' => 'Bad destination mailbox address',
+ 'X.1.2' => 'Bad destination system address',
+ 'X.1.3' => 'Bad destination mailbox address syntax',
+ 'X.1.4' => 'Destination mailbox address ambiguous',
+ 'X.1.5' => 'Destination mailbox address valid',
+ 'X.1.6' => 'Mailbox has moved',
+ 'X.1.7' => 'Bad sender\'s mailbox address syntax',
+ 'X.1.8' => 'Bad sender\'s system address',
+ 'X.2.0' => 'Other or undefined mailbox status',
+ 'X.2.1' => 'Mailbox disabled, not accepting messages',
+ 'X.2.2' => 'Mailbox full',
+ 'X.2.3' => 'Message length exceeds administrative limit.',
+ 'X.2.4' => 'Mailing list expansion problem',
+ 'X.3.0' => 'Other or undefined mail system status',
+ 'X.3.1' => 'Mail system full',
+ 'X.3.2' => 'System not accepting network messages',
+ 'X.3.3' => 'System not capable of selected features',
+ 'X.3.4' => 'Message too big for system',
+ 'X.4.0' => 'Other or undefined network or routing status',
+ 'X.4.1' => 'No answer from host',
+ 'X.4.2' => 'Bad connection',
+ 'X.4.3' => 'Routing server failure',
+ 'X.4.4' => 'Unable to route',
+ 'X.4.5' => 'Network congestion',
+ 'X.4.6' => 'Routing loop detected',
+ 'X.4.7' => 'Delivery time expired',
+ 'X.5.0' => 'Other or undefined protocol status',
+ 'X.5.1' => 'Invalid command',
+ 'X.5.2' => 'Syntax error',
+ 'X.5.3' => 'Too many recipients',
+ 'X.5.4' => 'Invalid command arguments',
+ 'X.5.5' => 'Wrong protocol version',
+ 'X.6.0' => 'Other or undefined media error',
+ 'X.6.1' => 'Media not supported',
+ 'X.6.2' => 'Conversion required and prohibited',
+ 'X.6.3' => 'Conversion required but not supported',
+ 'X.6.4' => 'Conversion with loss performed',
+ 'X.6.5' => 'Conversion failed',
+ 'X.7.0' => 'Other or undefined security status',
+ 'X.7.1' => 'Delivery not authorized, message refused',
+ 'X.7.2' => 'Mailing list expansion prohibited',
+ 'X.7.3' => 'Security conversion required but not possible',
+ 'X.7.4' => 'Security features not supported',
+ 'X.7.5' => 'Cryptographic failure',
+ 'X.7.6' => 'Cryptographic algorithm not supported',
+ 'X.7.7' => 'Message integrity failure'
+ }
+
+ # Returns HTML, of extra comment to put by attachment
+ def extra_note
+ # For delivery status notification attachments, extract the status and
+ # look up what it means in the DSN table.
+ if @content_type == 'message/delivery-status'
+ if !@body.match(/Status:\s+([0-9]+\.([0-9]+\.[0-9]+))\s+/)
+ return ""
+ end
+ dsn = $1
+ dsn_part = 'X.' + $2
+
+ dsn_message = ""
+ if DsnToMessage.include?(dsn_part)
+ dsn_message = " (" + DsnToMessage[dsn_part] + ")"
+ end
+
+ return "<br><em>DSN: " + dsn + dsn_message + "</em>"
+ end
+ return ""
+ end
+
+ # Called by controller so old filenames still work
+ def old_display_filename
+ filename = self.filename
+
+ # Convert weird spaces (e.g. \n) to normal ones
+ filename = filename.gsub(/\s/, " ")
+ # Remove slashes, they mess with URLs
+ filename = filename.gsub(/\//, "-")
+
+ return filename
+ end
+
+ # XXX changing this will break existing URLs, so have a care - maybe
+ # make another old_display_filename see above
+ def display_filename
+ filename = self.filename
+ if !self.incoming_message.nil?
+ self.incoming_message.info_request.apply_censor_rules_to_text!(filename)
+ end
+ # Sometimes filenames have e.g. %20 in - no point butchering that
+ # (without unescaping it, this would remove the % and leave 20s in there)
+ filename = CGI.unescape(filename)
+
+ # Remove weird spaces
+ filename = filename.gsub(/\s+/, " ")
+ # Remove non-alphabetic characters
+ filename = filename.gsub(/[^A-Za-z0-9.]/, " ")
+ # Remove spaces near dots
+ filename = filename.gsub(/\s*\.\s*/, ".")
+ # Compress adjacent spaces down to a single one
+ filename = filename.gsub(/\s+/, " ")
+ filename = filename.strip
+
+ return filename
+ end
+
+
+ def ensure_filename!
+ if self.filename.nil?
+ calc_ext = AlaveteliFileTypes.mimetype_to_extension(self.content_type)
+ if !calc_ext
+ calc_ext = "bin"
+ end
+ if !self.within_rfc822_subject.nil?
+ computed = self.within_rfc822_subject + "." + calc_ext
+ else
+ computed = "attachment." + calc_ext
+ end
+ self.filename = computed
+ end
+ end
+
+ def filename=(filename)
+ calc_ext = AlaveteliFileTypes.mimetype_to_extension(self.content_type)
+ # Put right extension on if missing
+ if !filename.nil? && !filename.match(/\.#{calc_ext}$/) && calc_ext
+ computed = filename + "." + calc_ext
+ else
+ computed = filename
+ end
+ write_attribute('filename', computed)
+ end
+
+ # Size to show next to the download link for the attachment
+ def update_display_size!
+ s = self.body.size
+
+ if s > 1024 * 1024
+ self.display_size = sprintf("%.1f", s.to_f / 1024 / 1024) + 'M'
+ else
+ self.display_size = (s / 1024).to_s + 'K'
+ end
+ end
+
+ # Whether this type can be shown in the Google Docs Viewer.
+ # The full list of supported types can be found at
+ # https://docs.google.com/support/bin/answer.py?hl=en&answer=1189935
+ def has_google_docs_viewer?
+ return !! {
+ "application/pdf" => true, # .pdf
+ "image/tiff" => true, # .tiff
+
+ "application/vnd.ms-word" => true, # .doc
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => true, # .docx
+
+ "application/vnd.ms-powerpoint" => true, # .ppt
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation" => true, # .pptx
+
+ "application/vnd.ms-excel" => true, # .xls
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => true, # .xlsx
+
+ } [self.content_type]
+ end
+
+ # Whether this type has a "View as HTML"
+ def has_body_as_html?
+ return (
+ !!{
+ "text/plain" => true,
+ "application/rtf" => true,
+ }[self.content_type] or
+ self.has_google_docs_viewer?
+ )
+ end
+
+ # Name of type of attachment type - only valid for things that has_body_as_html?
+ def name_of_content_type
+ return {
+ "text/plain" => "Text file",
+ 'application/rtf' => "RTF file",
+
+ 'application/pdf' => "PDF file",
+ 'image/tiff' => "TIFF image",
+
+ 'application/vnd.ms-word' => "Word document",
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => "Word document",
+
+ 'application/vnd.ms-powerpoint' => "PowerPoint presentation",
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => "PowerPoint presentation",
+
+ 'application/vnd.ms-excel' => "Excel spreadsheet",
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => "Excel spreadsheet",
+ }[self.content_type]
+ end
+
+ # For "View as HTML" of attachment
+ def body_as_html(dir)
+ html = nil
+ wrapper_id = "wrapper"
+
+ # simple cases, can never fail
+ if self.content_type == 'text/plain'
+ text = self.body.strip
+ 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
+ end
+
+ # the extractions will also produce image files, which go in the
+ # current directory, so change to the directory the function caller
+ # wants everything in
+ Dir.chdir(dir) do
+ tempfile = Tempfile.new('foiextract', '.')
+ tempfile.print self.body
+ tempfile.flush
+
+ if self.content_type == 'application/pdf'
+ IO.popen("/usr/bin/pdftohtml -nodrm -zoom 1.0 -stdout -enc UTF-8 -noframes " + tempfile.path + "", "r") do |child|
+ html = child.read()
+ end
+ elsif self.content_type == 'application/rtf'
+ IO.popen("/usr/bin/unrtf --html " + tempfile.path + "", "r") do |child|
+ html = child.read()
+ end
+ elsif self.has_google_docs_viewer?
+ html = '' # force error and using Google docs viewer
+ else
+ raise "No HTML conversion available for type " + self.content_type
+ end
+
+ tempfile.close
+ tempfile.delete
+ end
+
+ # We need to look at:
+ # a) Any error code
+ # b) The output size, as pdftohtml does not return an error code upon error.
+ # c) For cases when there is no text in the body of the HTML, or
+ # images, so nothing will be rendered. This is to detect some bug in
+ # pdftohtml, which sometimes makes it return just <hr>s and no other
+ # content.
+ html.match(/(\<body[^>]*\>.*)/mi)
+ body = $1.to_s
+ body_without_tags = body.gsub(/\s+/,"").gsub(/\<[^\>]*\>/, "")
+ contains_images = html.match(/<img/mi) ? true : false
+ if !$?.success? || html.size == 0 || (body_without_tags.size == 0 && !contains_images)
+ ret = "<html><head></head><body>";
+ if self.has_google_docs_viewer?
+ wrapper_id = "wrapper_google_embed"
+ ret = ret + "<iframe src='http://docs.google.com/viewer?url=<attachment-url-here>&embedded=true' width='100%' height='100%' style='border: none;'></iframe>";
+ else
+ ret = ret + "<p>Sorry, we were unable to convert this file to HTML. Please use the download link at the top right.</p>"
+ end
+ ret = ret + "</body></html>"
+ return ret, wrapper_id
+ end
+
+ return html, wrapper_id
+ end
+
+end
+
diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb
index 2b795ddf5..a8498b6e8 100644
--- a/app/models/incoming_message.rb
+++ b/app/models/incoming_message.rb
@@ -44,275 +44,6 @@ module TMail
end
end
-# This is the type which is used to send data about attachments to the view
-class FOIAttachment
- attr_accessor :body
- attr_accessor :content_type
- attr_accessor :filename
- attr_accessor :url_part_number
- attr_accessor :within_rfc822_subject # we use the subject as the filename for email attachments
-
- # List of DSN codes taken from RFC 3463
- # http://tools.ietf.org/html/rfc3463
- DsnToMessage = {
- 'X.1.0' => 'Other address status',
- 'X.1.1' => 'Bad destination mailbox address',
- 'X.1.2' => 'Bad destination system address',
- 'X.1.3' => 'Bad destination mailbox address syntax',
- 'X.1.4' => 'Destination mailbox address ambiguous',
- 'X.1.5' => 'Destination mailbox address valid',
- 'X.1.6' => 'Mailbox has moved',
- 'X.1.7' => 'Bad sender\'s mailbox address syntax',
- 'X.1.8' => 'Bad sender\'s system address',
- 'X.2.0' => 'Other or undefined mailbox status',
- 'X.2.1' => 'Mailbox disabled, not accepting messages',
- 'X.2.2' => 'Mailbox full',
- 'X.2.3' => 'Message length exceeds administrative limit.',
- 'X.2.4' => 'Mailing list expansion problem',
- 'X.3.0' => 'Other or undefined mail system status',
- 'X.3.1' => 'Mail system full',
- 'X.3.2' => 'System not accepting network messages',
- 'X.3.3' => 'System not capable of selected features',
- 'X.3.4' => 'Message too big for system',
- 'X.4.0' => 'Other or undefined network or routing status',
- 'X.4.1' => 'No answer from host',
- 'X.4.2' => 'Bad connection',
- 'X.4.3' => 'Routing server failure',
- 'X.4.4' => 'Unable to route',
- 'X.4.5' => 'Network congestion',
- 'X.4.6' => 'Routing loop detected',
- 'X.4.7' => 'Delivery time expired',
- 'X.5.0' => 'Other or undefined protocol status',
- 'X.5.1' => 'Invalid command',
- 'X.5.2' => 'Syntax error',
- 'X.5.3' => 'Too many recipients',
- 'X.5.4' => 'Invalid command arguments',
- 'X.5.5' => 'Wrong protocol version',
- 'X.6.0' => 'Other or undefined media error',
- 'X.6.1' => 'Media not supported',
- 'X.6.2' => 'Conversion required and prohibited',
- 'X.6.3' => 'Conversion required but not supported',
- 'X.6.4' => 'Conversion with loss performed',
- 'X.6.5' => 'Conversion failed',
- 'X.7.0' => 'Other or undefined security status',
- 'X.7.1' => 'Delivery not authorized, message refused',
- 'X.7.2' => 'Mailing list expansion prohibited',
- 'X.7.3' => 'Security conversion required but not possible',
- 'X.7.4' => 'Security features not supported',
- 'X.7.5' => 'Cryptographic failure',
- 'X.7.6' => 'Cryptographic algorithm not supported',
- 'X.7.7' => 'Message integrity failure'
- }
-
- # Returns HTML, of extra comment to put by attachment
- def extra_note
- # For delivery status notification attachments, extract the status and
- # look up what it means in the DSN table.
- if @content_type == 'message/delivery-status'
- if !@body.match(/Status:\s+([0-9]+\.([0-9]+\.[0-9]+))\s+/)
- return ""
- end
- dsn = $1
- dsn_part = 'X.' + $2
-
- dsn_message = ""
- if DsnToMessage.include?(dsn_part)
- dsn_message = " (" + DsnToMessage[dsn_part] + ")"
- end
-
- return "<br><em>DSN: " + dsn + dsn_message + "</em>"
- end
- return ""
- end
-
- # Called by controller so old filenames still work
- def old_display_filename
- filename = self._internal_display_filename
-
- # Convert weird spaces (e.g. \n) to normal ones
- filename = filename.gsub(/\s/, " ")
- # Remove slashes, they mess with URLs
- filename = filename.gsub(/\//, "-")
-
- return filename
- end
-
- # XXX changing this will break existing URLs, so have a care - maybe
- # make another old_display_filename see above
- def display_filename
- filename = self._internal_display_filename
-
- # Sometimes filenames have e.g. %20 in - no point butchering that
- # (without unescaping it, this would remove the % and leave 20s in there)
- filename = CGI.unescape(filename)
-
- # Remove weird spaces
- filename = filename.gsub(/\s+/, " ")
- # Remove non-alphabetic characters
- filename = filename.gsub(/[^A-Za-z0-9.]/, " ")
- # Remove spaces near dots
- filename = filename.gsub(/\s*\.\s*/, ".")
- # Compress adjacent spaces down to a single one
- filename = filename.gsub(/\s+/, " ")
- filename = filename.strip
-
- return filename
- end
-
- def _internal_display_filename
- calc_ext = AlaveteliFileTypes.mimetype_to_extension(@content_type)
-
- if @filename
- # Put right extension on if missing
- if !filename.match(/\.#{calc_ext}$/) && calc_ext
- filename + "." + calc_ext
- else
- filename
- end
- else
- if !calc_ext
- calc_ext = "bin"
- end
- if @within_rfc822_subject
- @within_rfc822_subject + "." + calc_ext
- else
- "attachment." + calc_ext
- end
- end
- end
-
- # Size to show next to the download link for the attachment
- def display_size
- s = self.body.size
-
- if s > 1024 * 1024
- return sprintf("%.1f", s.to_f / 1024 / 1024) + 'M'
- else
- return (s / 1024).to_s + 'K'
- end
- end
-
- # Whether this type can be shown in the Google Docs Viewer.
- # The full list of supported types can be found at
- # https://docs.google.com/support/bin/answer.py?hl=en&answer=1189935
- def has_google_docs_viewer?
- return !! {
- "application/pdf" => true, # .pdf
- "image/tiff" => true, # .tiff
-
- "application/vnd.ms-word" => true, # .doc
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => true, # .docx
-
- "application/vnd.ms-powerpoint" => true, # .ppt
- "application/vnd.openxmlformats-officedocument.presentationml.presentation" => true, # .pptx
-
- "application/vnd.ms-excel" => true, # .xls
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => true, # .xlsx
-
- } [self.content_type]
- end
-
- # Whether this type has a "View as HTML"
- def has_body_as_html?
- return (
- !!{
- "text/plain" => true,
- "application/rtf" => true,
- }[self.content_type] or
- self.has_google_docs_viewer?
- )
- end
-
- # Name of type of attachment type - only valid for things that has_body_as_html?
- def name_of_content_type
- return {
- "text/plain" => "Text file",
- 'application/rtf' => "RTF file",
-
- 'application/pdf' => "PDF file",
- 'image/tiff' => "TIFF image",
-
- 'application/vnd.ms-word' => "Word document",
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => "Word document",
-
- 'application/vnd.ms-powerpoint' => "PowerPoint presentation",
- 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => "PowerPoint presentation",
-
- 'application/vnd.ms-excel' => "Excel spreadsheet",
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => "Excel spreadsheet",
- }[self.content_type]
- end
-
- # For "View as HTML" of attachment
- def body_as_html(dir)
- html = nil
- wrapper_id = "wrapper"
-
- # simple cases, can never fail
- if self.content_type == 'text/plain'
- text = self.body.strip
- 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
- end
-
- # the extractions will also produce image files, which go in the
- # current directory, so change to the directory the function caller
- # wants everything in
- Dir.chdir(dir) do
- tempfile = Tempfile.new('foiextract', '.')
- tempfile.print self.body
- tempfile.flush
-
- if self.content_type == 'application/pdf'
- IO.popen("/usr/bin/pdftohtml -nodrm -zoom 1.0 -stdout -enc UTF-8 -noframes " + tempfile.path + "", "r") do |child|
- html = child.read()
- end
- elsif self.content_type == 'application/rtf'
- IO.popen("/usr/bin/unrtf --html " + tempfile.path + "", "r") do |child|
- html = child.read()
- end
- elsif self.has_google_docs_viewer?
- html = '' # force error and using Google docs viewer
- else
- raise "No HTML conversion available for type " + self.content_type
- end
-
- tempfile.close
- tempfile.delete
- end
-
- # We need to look at:
- # a) Any error code
- # b) The output size, as pdftohtml does not return an error code upon error.
- # c) For cases when there is no text in the body of the HTML, or
- # images, so nothing will be rendered. This is to detect some bug in
- # pdftohtml, which sometimes makes it return just <hr>s and no other
- # content.
- html.match(/(\<body[^>]*\>.*)/mi)
- body = $1.to_s
- body_without_tags = body.gsub(/\s+/,"").gsub(/\<[^\>]*\>/, "")
- contains_images = html.match(/<img/mi) ? true : false
- if !$?.success? || html.size == 0 || (body_without_tags.size == 0 && !contains_images)
- ret = "<html><head></head><body>";
- if self.has_google_docs_viewer?
- wrapper_id = "wrapper_google_embed"
- ret = ret + "<iframe src='http://docs.google.com/viewer?url=<attachment-url-here>&embedded=true' width='100%' height='100%' style='border: none;'></iframe>";
- else
- ret = ret + "<p>Sorry, we were unable to convert this file to HTML. Please use the download link at the top right.</p>"
- end
- ret = ret + "</body></html>"
- return ret, wrapper_id
- end
-
- return html, wrapper_id
- end
-
-end
-
-
class IncomingMessage < ActiveRecord::Base
belongs_to :info_request
validates_presence_of :info_request
@@ -320,7 +51,7 @@ class IncomingMessage < ActiveRecord::Base
validates_presence_of :raw_email
has_many :outgoing_message_followups, :foreign_key => 'incoming_message_followup_id', :class_name => 'OutgoingMessage'
-
+ has_many :foi_attachments
has_many :info_request_events # never really has many, but could in theory
belongs_to :raw_email
@@ -338,8 +69,8 @@ class IncomingMessage < ActiveRecord::Base
# Return the structured TMail::Mail object
# Documentation at http://i.loveruby.net/en/projects/tmail/doc/
- def mail
- if @mail.nil? && !self.raw_email.nil?
+ def mail(force = nil)
+ if (!force.nil? || @mail.nil?) && !self.raw_email.nil?
# Hack round bug in TMail's MIME decoding. Example request which provokes it:
# http://www.whatdotheyknow.com/request/reviews_of_unduly_lenient_senten#incoming-4830
# Report of TMail bug:
@@ -352,23 +83,109 @@ class IncomingMessage < ActiveRecord::Base
@mail
end
+ # Returns the name of the person the incoming message is from, or nil if
+ # there isn't one or if there is only an email address. XXX can probably
+ # remove from_name_if_present (which is a monkey patch) by just calling
+ # .from_addrs[0].name here instead?
+
+ # Return false if for some reason this is a message that we shouldn't let them reply to
+ def _calculate_valid_to_reply_to
+ # check validity of email
+ if self.mail.from_addrs.nil? || self.mail.from_addrs.size == 0
+ return false
+ end
+ email = self.mail.from_addrs[0].spec
+ if !MySociety::Validate.is_valid_email(email)
+ return false
+ end
+
+ # reject postmaster - authorities seem to nearly always not respond to
+ # email to postmaster, and it tends to only happen after delivery failure.
+ # likewise Mailer-Daemon, Auto_Reply...
+ prefix = email
+ prefix =~ /^(.*)@/
+ prefix = $1
+ if !prefix.nil? && prefix.downcase.match(/^(postmaster|mailer-daemon|auto_reply|donotreply|no.reply)$/)
+ return false
+ end
+ if !self.mail['return-path'].nil? && self.mail['return-path'].addr == "<>"
+ return false
+ end
+ if !self.mail['auto-submitted'].nil?
+ return false
+ end
+ return true
+ end
+
+ def parse_raw_email!(force = nil)
+ # The following fields may be absent; we treat them as cached
+ # values in case we want to regenerate them (due to mail
+ # parsing bugs, etc).
+ if (!force.nil? || self.last_parsed.nil?)
+ self.extract_attachments!
+ self.sent_at = self.mail.date || self.created_at
+ self.subject = self.mail.subject
+ # XXX can probably remove from_name_if_present (which is a
+ # monkey patch) by just calling .from_addrs[0].name here
+ # instead?
+ self.mail_from = self.mail.from_name_if_present
+ begin
+ self.mail_from_domain = PublicBody.extract_domain_from_email(self.mail.from_addrs[0].spec)
+ rescue NoMethodError
+ self.mail_from_domain = ""
+ end
+ self.valid_to_reply_to = self._calculate_valid_to_reply_to
+ self.last_parsed = Time.now
+ self.save!
+ end
+ end
+
+ def valid_to_reply_to?
+ return self.valid_to_reply_to
+ end
+
+ # The cached fields mentioned in the previous comment
+ # XXX there must be a nicer way to do this without all that
+ # repetition. I tried overriding method_missing but got some
+ # unpredictable results.
+ def valid_to_reply_to
+ parse_raw_email!
+ super
+ end
+ def sent_at
+ parse_raw_email!
+ super
+ end
+ def subject
+ parse_raw_email!
+ super
+ end
+ def mail_from
+ parse_raw_email!
+ super
+ end
+ def safe_mail_from
+ if !self.mail_from.nil?
+ mail_from = self.mail_from.dup
+ self.info_request.apply_censor_rules_to_text!(mail_from)
+ return mail_from
+ end
+ end
+ def mail_from_domain
+ parse_raw_email!
+ super
+ end
+
# Number the attachments in depth first tree order, for use in URLs.
# XXX This fills in part.rfc822_attachment and part.url_part_number within
# all the parts of the email (see TMail monkeypatch above for how these
# attributes are added). ensure_parts_counted must be called before using
- # the attributes. This calculation is done only when required to avoid
- # having to load and parse the email unnecessarily.
- def after_initialize
- @parts_counted = false
- end
+ # the attributes.
def ensure_parts_counted
- if not @parts_counted
- @count_parts_count = 0
- _count_parts_recursive(self.mail)
- # we carry on using these numeric ids for attachments uudecoded from within text parts
- @count_first_uudecode_count = @count_parts_count
- @parts_counted = true
- end
+ @count_parts_count = 0
+ _count_parts_recursive(self.mail)
+ # we carry on using these numeric ids for attachments uudecoded from within text parts
+ @count_first_uudecode_count = @count_parts_count
end
def _count_parts_recursive(part)
if part.multipart?
@@ -406,7 +223,7 @@ class IncomingMessage < ActiveRecord::Base
end
end
# And look up by URL part number to get an attachment
- # XXX relies on get_attachments_for_display calling ensure_parts_counted
+ # XXX relies on extract_attachments calling ensure_parts_counted
def self.get_attachment_by_url_part_number(attachments, found_url_part_number)
attachments.each do |a|
if a.url_part_number == found_url_part_number
@@ -416,12 +233,6 @@ class IncomingMessage < ActiveRecord::Base
return nil
end
- # Return date mail was sent
- def sent_at
- # Use date it arrived (created_at) if mail itself doesn't have Date: header
- self.mail.date || self.created_at
- end
-
# Converts email addresses we know about into textual descriptions of them
def mask_special_emails!(text)
# XXX can later display some of these special emails as actual emails,
@@ -447,7 +258,7 @@ class IncomingMessage < ActiveRecord::Base
# Special cases for some content types
if content_type == 'application/pdf'
uncompressed_text = nil
- IO.popen("/usr/bin/pdftk - output - uncompress", "r+") do |child|
+ IO.popen("#{`which pdftk`.chomp} - output - uncompress", "r+") do |child|
child.write(text)
child.close_write()
uncompressed_text = child.read()
@@ -464,7 +275,7 @@ class IncomingMessage < ActiveRecord::Base
if MySociety::Config.get('USE_GHOSTSCRIPT_COMPRESSION') == true
command = "gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen -dNOPAUSE -dQUIET -dBATCH -sOutputFile=- -"
else
- command = "/usr/bin/pdftk - output - compress"
+ command = "#{`which pdftk`.chomp} - output - compress"
end
IO.popen(command, "r+") do |child|
child.write(censored_uncompressed_text)
@@ -518,6 +329,7 @@ class IncomingMessage < ActiveRecord::Base
self.info_request.apply_censor_rules_to_binary!(text)
raise "internal error in binary_mask_stuff" if text.size != orig_size
+ return text
end
# Removes censored stuff from from HTML conversion of downloaded binaries
@@ -606,21 +418,13 @@ class IncomingMessage < ActiveRecord::Base
text.gsub!(/^(>.*\n)/, replacement)
text.gsub!(/^(On .+ (wrote|said):\n)/, replacement)
- # Multiple line sections
- # http://www.whatdotheyknow.com/request/identity_card_scheme_expenditure
- # http://www.whatdotheyknow.com/request/parliament_protest_actions
- # http://www.whatdotheyknow.com/request/64/response/102
- # http://www.whatdotheyknow.com/request/47/response/283
- # http://www.whatdotheyknow.com/request/30/response/166
- # http://www.whatdotheyknow.com/request/52/response/238
- # http://www.whatdotheyknow.com/request/224/response/328 # example with * * * * *
- # http://www.whatdotheyknow.com/request/297/response/506
- ['-', '_', '*', '#'].each do |score|
+ ['-', '_', '*', '#'].each do |scorechar|
+ score = /(?:[#{scorechar}]\s*){8,}/
text.sub!(/(Disclaimer\s+)? # appears just before
(
- \s*(?:[#{score}]\s*){8,}\s*\n.*? # top line
+ \s*#{score}\n(?:(?!#{score}\n).)*? # top line
(disclaimer:\n|confidential|received\sthis\semail\sin\serror|virus|intended\s+recipient|monitored\s+centrally|intended\s+(for\s+|only\s+for\s+use\s+by\s+)the\s+addressee|routinely\s+monitored|MessageLabs|unauthorised\s+use)
- .*?((?:[#{score}]\s*){8,}\s*\n|\z) # bottom line OR end of whole string (for ones with no terminator XXX risky)
+ .*?(?:#{score}|\z) # bottom line OR end of whole string (for ones with no terminator XXX risky)
)
/imx, replacement)
end
@@ -666,20 +470,20 @@ class IncomingMessage < ActiveRecord::Base
end
# Internal function
- def _get_censored_part_file_name(mail)
+ def _get_part_file_name(mail)
part_file_name = TMail::Mail.get_part_file_name(mail)
if part_file_name.nil?
return nil
end
part_file_name = part_file_name.dup
- self.info_request.apply_censor_rules_to_text!(part_file_name)
return part_file_name
end
# (This risks losing info if the unchosen alternative is the only one to contain
# useful info, but let's worry about that another time)
def get_attachment_leaves
- return _get_attachment_leaves_recursive(self.mail)
+ force = true
+ return _get_attachment_leaves_recursive(self.mail(force))
end
def _get_attachment_leaves_recursive(curr_mail, within_rfc822_attachment = nil)
leaves_found = []
@@ -719,14 +523,14 @@ class IncomingMessage < ActiveRecord::Base
# it into conflict with ensure_parts_counted which it has to be
# called both before and after. It will fail with cases of
# attachments of attachments etc.
-
+ charset = curr_mail.charset # save this, because overwriting content_type also resets charset
# Don't allow nil content_types
if curr_mail.content_type.nil?
curr_mail.content_type = 'application/octet-stream'
end
# PDFs often come with this mime type, fix it up for view code
if curr_mail.content_type == 'application/octet-stream'
- part_file_name = self._get_censored_part_file_name(curr_mail)
+ part_file_name = self._get_part_file_name(curr_mail)
calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(part_file_name, curr_mail.body)
if calc_mime
curr_mail.content_type = calc_mime
@@ -749,7 +553,6 @@ class IncomingMessage < ActiveRecord::Base
curr_mail.content_type = 'application/octet-stream'
end
end
-
# If the part is an attachment of email
if curr_mail.content_type == 'message/rfc822' || curr_mail.content_type == 'application/vnd.ms-outlook' || curr_mail.content_type == 'application/ms-tnef'
ensure_parts_counted # fills in rfc822_attachment variable
@@ -759,6 +562,8 @@ class IncomingMessage < ActiveRecord::Base
curr_mail.within_rfc822_attachment = within_rfc822_attachment
leaves_found += [curr_mail]
end
+ # restore original charset
+ curr_mail.charset = charset
end
return leaves_found
end
@@ -776,7 +581,6 @@ class IncomingMessage < ActiveRecord::Base
# search results
def _cache_main_body_text
text = self.get_main_body_text_internal
-
# Strip the uudecode parts from main text
# - this also effectively does a .dup as well, so text mods don't alter original
text = text.split(/^begin.+^`\n^end\n/sm).join(" ")
@@ -818,61 +622,54 @@ class IncomingMessage < ActiveRecord::Base
main_part = get_main_body_text_part
return _convert_part_body_to_text(main_part)
end
+
# Given a main text part, converts it to text
def _convert_part_body_to_text(part)
if part.nil?
text = "[ Email has no body, please see attachments ]"
- text_charset = "utf-8"
+ source_charset = "utf-8"
else
- text = part.body
- text_charset = part.charset
+ text = part.body # by default, TMail converts to UT8 in this call
+ source_charset = part.charset
if part.content_type == 'text/html'
# e.g. http://www.whatdotheyknow.com/request/35/response/177
- # XXX This is a bit of a hack as it is calling a convert to text routine.
- # Could instead call a sanitize HTML one.
- text = self.class._get_attachment_text_internal_one_file(part.content_type, text)
- end
- end
-
- # Charset conversion, turn everything into UTF-8
- if not text_charset.nil?
- begin
- # XXX specially convert unicode pound signs, was needed here
- # http://www.whatdotheyknow.com/request/88/response/352
- text = text.gsub("£", Iconv.conv(text_charset, 'utf-8', '£'))
- # Try proper conversion
- text = Iconv.conv('utf-8', text_charset, text)
- rescue Iconv::IllegalSequence, Iconv::InvalidEncoding
- # Clearly specified charset was nonsense
- text_charset = nil
+ # XXX This is a bit of a hack as it is calling a
+ # convert to text routine. Could instead call a
+ # sanitize HTML one.
+
+ # If the text isn't UTF8, it means TMail had a problem
+ # converting it (invalid characters, etc), and we
+ # should instead tell elinks to respect the source
+ # charset
+ use_charset = "utf-8"
+ begin
+ text = Iconv.conv('utf-8', 'utf-8', text)
+ rescue Iconv::IllegalSequence
+ use_charset = source_charset
+ end
+ text = self.class._get_attachment_text_internal_one_file(part.content_type, text, use_charset)
end
end
- if text_charset.nil?
- # No specified charset, so guess
-
- # Could use rchardet here, but it had trouble with
- # http://www.whatdotheyknow.com/request/107/response/144
- # So I gave up - most likely in UK we'll only get windows-1252 anyway.
+ # If TMail can't convert text, it just returns it, so we sanitise it.
+ begin
+ # Test if it's good UTF-8
+ text = Iconv.conv('utf-8', 'utf-8', text)
+ rescue Iconv::IllegalSequence
+ # Text looks like unlabelled nonsense,
+ # strip out anything that isn't UTF-8
begin
- # See if it is good UTF-8 anyway
- text = Iconv.conv('utf-8', 'utf-8', text)
- rescue Iconv::IllegalSequence
- begin
- # Or is it good windows-1252, most likely
- text = Iconv.conv('utf-8', 'windows-1252', text)
- rescue Iconv::IllegalSequence
- # Text looks like unlabelled nonsense, strip out anything that isn't UTF-8
- text = Iconv.conv('utf-8//IGNORE', 'utf-8', text) +
- _("\n\n[ {{site_name}} note: The above text was badly encoded, and has had strange characters removed. ]",
- :site_name => MySociety::Config.get('SITE_NAME', 'Alaveteli'))
+ 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 => MySociety::Config.get('SITE_NAME', 'Alaveteli'))
+ rescue Iconv::InvalidEncoding, Iconv::IllegalSequence
+ if source_charset != "utf-8"
+ source_charset = "utf-8"
+ retry
end
end
end
- # An assertion that we have ended up with UTF-8 XXX can remove as this should
- # always be fine if code above is
- Iconv.conv('utf-8', 'utf-8', text)
# Fix DOS style linefeeds to Unix style ones (or other later regexps won't work)
# Needed for e.g. http://www.whatdotheyknow.com/request/60/response/98
@@ -887,8 +684,8 @@ class IncomingMessage < ActiveRecord::Base
end
# Returns part which contains main body text, or nil if there isn't one
def get_main_body_text_part
- leaves = get_attachment_leaves
-
+ leaves = self.foi_attachments
+
# Find first part which is text/plain or text/html
# (We have to include HTML, as increasingly there are mail clients that
# include no text alternative for the main part, and we don't want to
@@ -902,7 +699,7 @@ class IncomingMessage < ActiveRecord::Base
# Otherwise first part which is any sort of text
leaves.each do |p|
- if p.main_type == 'text'
+ if p.content_type.match(/^text/)
return p
end
end
@@ -910,7 +707,7 @@ class IncomingMessage < ActiveRecord::Base
# ... or if none, consider first part
p = leaves[0]
# if it is a known type then don't use it, return no body (nil)
- if AlaveteliFileTypes.mimetype_to_extension(p.content_type)
+ if !p.nil? && AlaveteliFileTypes.mimetype_to_extension(p.content_type)
# this is guess of case where there are only attachments, no body text
# e.g. http://www.whatdotheyknow.com/request/cost_benefit_analysis_for_real_n
return nil
@@ -922,16 +719,7 @@ class IncomingMessage < ActiveRecord::Base
return p
end
# Returns attachments that are uuencoded in main body part
- def get_main_body_text_uudecode_attachments
- # 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.
- # e.g. for https://secure.mysociety.org/admin/foi/request/show_raw_email/24550
- main_part = get_main_body_text_part
- if main_part.nil?
- return []
- end
- text = main_part.body
-
+ def _uudecode_and_save_attachments(text)
# Find any uudecoded things buried in it, yeuchly
uus = text.scan(/^begin.+^`\n^end\n/sm)
attachments = []
@@ -946,91 +734,109 @@ class IncomingMessage < ActiveRecord::Base
end
tempfile.close
# Make attachment type from it, working out filename and mime type
- attachment = FOIAttachment.new()
- attachment.body = content
- attachment.filename = uu.match(/^begin\s+[0-9]+\s+(.*)$/)[1]
- self.info_request.apply_censor_rules_to_text!(attachment.filename)
- calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(attachment.filename, attachment.body)
+ filename = uu.match(/^begin\s+[0-9]+\s+(.*)$/)[1]
+ calc_mime = AlaveteliFileTypes.filename_and_content_to_mimetype(filename, content)
if calc_mime
calc_mime = normalise_content_type(calc_mime)
- attachment.content_type = calc_mime
+ content_type = calc_mime
else
- attachment.content_type = 'application/octet-stream'
+ content_type = 'application/octet-stream'
end
- attachments += [attachment]
- end
-
+ hexdigest = Digest::MD5.hexdigest(content)
+ attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => hexdigest)
+ attachment.update_attributes(:filename => filename,
+ :content_type => content_type,
+ :body => content,
+ :display_size => "0K")
+ attachment.save!
+ attachments << attachment
+ end
return attachments
end
- # Returns all attachments for use in display code
- # XXX is this called multiple times and should be cached?
def get_attachments_for_display
+ parse_raw_email!
+ # return what user would consider attachments, i.e. not the main body
main_part = get_main_body_text_part
- leaves = get_attachment_leaves
+ attachments = []
+ for attachment in self.foi_attachments
+ attachments << attachment if attachment != main_part
+ end
+ return attachments
+ end
+ def extract_attachments!
+ leaves = get_attachment_leaves # XXX check where else this is called from
# XXX we have to call ensure_parts_counted after get_attachment_leaves
# which is really messy.
ensure_parts_counted
-
attachments = []
- for leaf in leaves
- if leaf != main_part
- attachment = FOIAttachment.new
-
- attachment.body = leaf.body
- # As leaf.body causes MIME decoding which uses lots of RAM, do garbage collection here
- # to prevent excess memory use. XXX not really sure if this helps reduce
- # peak RAM use overall. Anyway, maybe there is something better to do than this.
- GC.start
-
- attachment.filename = _get_censored_part_file_name(leaf)
- if leaf.within_rfc822_attachment
- attachment.within_rfc822_subject = leaf.within_rfc822_attachment.subject
- # Test to see if we are in the first part of the attached
- # RFC822 message and it is text, if so add headers.
- # XXX should probably use hunting algorithm to find main text part, rather than
- # just expect it to be first. This will do for now though.
- # Example request that needs this:
- # http://www.whatdotheyknow.com/request/2923/response/7013/attach/2/Cycle%20Path%20Bank.txt
- if leaf.within_rfc822_attachment == leaf && leaf.content_type == 'text/plain'
- headers = ""
- for header in [ 'Date', 'Subject', 'From', 'To', 'Cc' ]
- if leaf.within_rfc822_attachment.header.include?(header.downcase)
- header_value = leaf.within_rfc822_attachment.header[header.downcase]
- # Example message which has a blank Date header:
- # http://www.whatdotheyknow.com/request/30747/response/80253/attach/html/17/Common%20Purpose%20Advisory%20Group%20Meeting%20Tuesday%202nd%20March.txt.html
- if !header_value.blank?
- headers = headers + header + ": " + header_value.to_s + "\n"
- end
+ for leaf in leaves
+ body = leaf.body
+ # As leaf.body causes MIME decoding which uses lots of RAM, do garbage collection here
+ # to prevent excess memory use. XXX not really sure if this helps reduce
+ # peak RAM use overall. Anyway, maybe there is something better to do than this.
+ GC.start
+ if leaf.within_rfc822_attachment
+ within_rfc822_subject = leaf.within_rfc822_attachment.subject
+ # Test to see if we are in the first part of the attached
+ # RFC822 message and it is text, if so add headers.
+ # XXX should probably use hunting algorithm to find main text part, rather than
+ # just expect it to be first. This will do for now though.
+ # Example request that needs this:
+ # http://www.whatdotheyknow.com/request/2923/response/7013/attach/2/Cycle%20Path%20Bank.txt
+ if leaf.within_rfc822_attachment == leaf && leaf.content_type == 'text/plain'
+ headers = ""
+ for header in [ 'Date', 'Subject', 'From', 'To', 'Cc' ]
+ if leaf.within_rfc822_attachment.header.include?(header.downcase)
+ header_value = leaf.within_rfc822_attachment.header[header.downcase]
+ # Example message which has a blank Date header:
+ # http://www.whatdotheyknow.com/request/30747/response/80253/attach/html/17/Common%20Purpose%20Advisory%20Group%20Meeting%20Tuesday%202nd%20March.txt.html
+ if !header_value.blank?
+ headers = headers + header + ": " + header_value.to_s + "\n"
end
end
- # XXX call _convert_part_body_to_text here, but need to get charset somehow
- # e.g. http://www.whatdotheyknow.com/request/1593/response/3088/attach/4/Freedom%20of%20Information%20request%20-%20car%20oval%20sticker:%20Article%2020,%20Convention%20on%20Road%20Traffic%201949.txt
- attachment.body = headers + "\n" + attachment.body
-
- # This is quick way of getting all headers, but instead we only add some a) to
- # make it more usable, b) as at least one authority accidentally leaked security
- # information into a header.
- #attachment.body = leaf.within_rfc822_attachment.port.to_s
end
+ # XXX call _convert_part_body_to_text here, but need to get charset somehow
+ # e.g. http://www.whatdotheyknow.com/request/1593/response/3088/attach/4/Freedom%20of%20Information%20request%20-%20car%20oval%20sticker:%20Article%2020,%20Convention%20on%20Road%20Traffic%201949.txt
+ body = headers + "\n" + body
+
+ # This is quick way of getting all headers, but instead we only add some a) to
+ # make it more usable, b) as at least one authority accidentally leaked security
+ # information into a header.
+ #attachment.body = leaf.within_rfc822_attachment.port.to_s
end
- attachment.content_type = leaf.content_type
- attachment.url_part_number = leaf.url_part_number
- attachments += [attachment]
end
+ hexdigest = Digest::MD5.hexdigest(body)
+ attachment = self.foi_attachments.find_or_create_by_hexdigest(:hexdigest => hexdigest)
+ attachment.update_attributes(:url_part_number => leaf.url_part_number,
+ :content_type => leaf.content_type,
+ :filename => _get_part_file_name(leaf),
+ :charset => leaf.charset,
+ :within_rfc822_subject => within_rfc822_subject,
+ :display_size => "0K",
+ :body => body)
+ attachment.save!
+ attachments << attachment.id
end
-
- uudecode_attachments = get_main_body_text_uudecode_attachments
- c = @count_first_uudecode_count
- for uudecode_attachment in uudecode_attachments
- c += 1
- uudecode_attachment.url_part_number = c
- attachments += [uudecode_attachment]
+ 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.
+ # e.g. for https://secure.mysociety.org/admin/foi/request/show_raw_email/24550
+ if !main_part.nil?
+ uudecoded_attachments = _uudecode_and_save_attachments(main_part.body)
+ c = @count_first_uudecode_count
+ for uudecode_attachment in uudecoded_attachments
+ c += 1
+ uudecode_attachment.url_part_number = c
+ uudecode_attachment.save!
+ attachments << uudecode_attachment.id
+ end
end
- return attachments
- end
+ # now get rid of any attachments we no longer have
+ FoiAttachment.destroy_all("id NOT IN (#{attachments.join(',')}) AND incoming_message_id = #{self.id}")
+ end
# Returns body text as HTML with quotes flattened, and emails removed.
def get_body_for_html_display(collapse_quoted_sections = true)
@@ -1055,7 +861,7 @@ class IncomingMessage < ActiveRecord::Base
text.strip!
# if there is nothing but quoted stuff, then show the subject
if text == "FOLDED_QUOTED_SECTION"
- text = "[Subject only] " + CGI.escapeHTML(self.mail.subject) + text
+ text = "[Subject only] " + CGI.escapeHTML(self.subject) + text
end
# and display link for quoted stuff
text = text.gsub(/FOLDED_QUOTED_SECTION/, "\n\n" + '<span class="unfold_link"><a href="?unfold=1#incoming-'+self.id.to_s+'">show quoted sections</a></span>' + "\n\n")
@@ -1071,6 +877,7 @@ class IncomingMessage < ActiveRecord::Base
return text
end
+
# Returns text of email for using in quoted section when replying
def get_body_for_quoting
# Get the body text with emails and quoted sections removed
@@ -1110,7 +917,9 @@ class IncomingMessage < ActiveRecord::Base
return self.cached_attachment_text_clipped
end
- def IncomingMessage._get_attachment_text_internal_one_file(content_type, body)
+ def IncomingMessage._get_attachment_text_internal_one_file(content_type, body, charset = 'utf-8')
+ # note re. charset: TMail always tries to convert email bodies
+ # to UTF8 by default, so normally it should already be that.
text = ''
# XXX - tell all these command line tools to return utf-8
if content_type == 'text/plain'
@@ -1120,21 +929,22 @@ class IncomingMessage < ActiveRecord::Base
tempfile.print body
tempfile.flush
if content_type == 'application/vnd.ms-word'
- AlaveteliExternalCommand.run("/usr/bin/wvText", tempfile.path, tempfile.path + ".txt")
+ AlaveteliExternalCommand.run(`which wvText`.chomp, tempfile.path, tempfile.path + ".txt")
# Try catdoc if we get into trouble (e.g. for InfoRequestEvent 2701)
if not File.exists?(tempfile.path + ".txt")
- AlaveteliExternalCommand.run("/usr/bin/catdoc", tempfile.path, :append_to => text)
+ AlaveteliExternalCommand.run(`which catdoc`.chomp, tempfile.path, :append_to => text)
else
text += File.read(tempfile.path + ".txt") + "\n\n"
File.unlink(tempfile.path + ".txt")
end
elsif content_type == 'application/rtf'
# catdoc on RTF prodcues less comments and extra bumf than --text option to unrtf
- AlaveteliExternalCommand.run("/usr/bin/catdoc", tempfile.path, :append_to => text)
+ AlaveteliExternalCommand.run(`which catdoc`.chomp, tempfile.path, :append_to => text)
elsif content_type == 'text/html'
- # lynx wordwraps links in its output, which then don't get formatted properly
- # by Alaveteli. We use elinks instead, which doesn't do that.
- AlaveteliExternalCommand.run("/usr/bin/elinks", "-eval", "'set document.codepage.assume = \"utf-8\"'", "-dump-charset", "utf-8", "-force-html", "-dump",
+ # lynx wordwraps links in its output, which then don't
+ # get formatted properly by Alaveteli. We use elinks
+ # instead, which doesn't do that.
+ AlaveteliExternalCommand.run(`which elinks`.chomp, "-eval", "'set document.codepage.assume = \"#{charset}\"'", "-eval", "'set document.codepage.force_assumed = 1'", "-dump-charset", "utf-8", "-force-html", "-dump",
tempfile.path, :append_to => text)
elsif content_type == 'application/vnd.ms-excel'
# Bit crazy using /usr/bin/strings - but xls2csv, xlhtml and
@@ -1145,9 +955,9 @@ class IncomingMessage < ActiveRecord::Base
elsif content_type == 'application/vnd.ms-powerpoint'
# ppthtml seems to catch more text, but only outputs HTML when
# we want text, so just use catppt for now
- AlaveteliExternalCommand.run("/usr/bin/catppt", tempfile.path, :append_to => text)
+ AlaveteliExternalCommand.run(`which catppt`.chomp, tempfile.path, :append_to => text)
elsif content_type == 'application/pdf'
- AlaveteliExternalCommand.run("/usr/bin/pdftotext", tempfile.path, "-", :append_to => text)
+ AlaveteliExternalCommand.run(`which pdftotext`.chomp, tempfile.path, "-", :append_to => text)
elsif content_type == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
# This is Microsoft's XML office document format.
# Just pull out the main XML file, and strip it of text.
@@ -1201,13 +1011,14 @@ class IncomingMessage < ActiveRecord::Base
text = ''
attachments = self.get_attachments_for_display
for attachment in attachments
- text += IncomingMessage._get_attachment_text_internal_one_file(attachment.content_type, attachment.body)
+ text += IncomingMessage._get_attachment_text_internal_one_file(attachment.content_type, attachment.body, attachment.charset)
end
# Remove any bad characters
text = Iconv.conv('utf-8//IGNORE', 'utf-8', text)
return text
end
+
# Returns text for indexing
def get_text_for_indexing_full
return get_body_for_quoting + "\n\n" + get_attachment_text_full
@@ -1217,23 +1028,6 @@ class IncomingMessage < ActiveRecord::Base
return get_body_for_quoting + "\n\n" + get_attachment_text_clipped
end
- # Returns the name of the person the incoming message is from, or nil if
- # there isn't one or if there is only an email address. XXX can probably
- # remove from_name_if_present (which is a monkey patch) by just calling
- # .from_addrs[0].name here instead?
- def safe_mail_from
- name = self.mail.from_name_if_present
- if name.nil?
- return nil
- end
- name = name.dup
- self.info_request.apply_censor_rules_to_text!(name)
- return name
- end
-
- def mail_from_domain
- return PublicBody.extract_domain_from_email(self.mail.from_addrs[0].spec)
- end
# Has message arrived "recently"?
@@ -1310,7 +1104,7 @@ class IncomingMessage < ActiveRecord::Base
if !self.mail['return-path'].nil? && self.mail['return-path'].addr == "<>"
return false
end
- if !self.mail['auto-submitted'].nil? && !self.mail['auto-submitted'].keys.empty?
+ if !self.mail['auto-submitted'].nil?
return false
end
return true
diff --git a/app/models/info_request.rb b/app/models/info_request.rb
index 92322f74f..cfef6ebd8 100644
--- a/app/models/info_request.rb
+++ b/app/models/info_request.rb
@@ -1,3 +1,4 @@
+
# == Schema Information
# Schema version: 95
#
@@ -240,19 +241,19 @@ public
# into some sort of separate jurisdiction dependent file
if self.public_body.url_name == 'general_register_office'
# without GQ in the subject, you just get an auto response
- self.law_used_full + ' request GQ - ' + self.title
+ _('{{law_used_full}} request GQ - {{title}}',:law_used_full=>self.law_used_full,:title=>self.title)
else
- self.law_used_full + ' request - ' + self.title
+ _('{{law_used_full}} request - {{title}}',:law_used_full=>self.law_used_full,:title=>self.title)
end
end
def email_subject_followup(incoming_message = nil)
if incoming_message.nil? || !incoming_message.valid_to_reply_to?
'Re: ' + self.email_subject_request
else
- if incoming_message.mail.subject.match(/^Re:/i)
- incoming_message.mail.subject
+ if incoming_message.subject.match(/^Re:/i)
+ incoming_message.subject
else
- 'Re: ' + incoming_message.mail.subject
+ 'Re: ' + incoming_message.subject
end
end
end
@@ -260,36 +261,36 @@ public
# Two sorts of laws for requests, FOI or EIR
def law_used_full
if self.law_used == 'foi'
- return "Freedom of Information"
+ return _("Freedom of Information")
elsif self.law_used == 'eir'
- return "Environmental Information Regulations"
+ return _("Environmental Information Regulations")
else
raise "Unknown law used '" + self.law_used + "'"
end
end
def law_used_short
if self.law_used == 'foi'
- return "FOI"
+ return _("FOI")
elsif self.law_used == 'eir'
- return "EIR"
+ return _("EIR")
else
raise "Unknown law used '" + self.law_used + "'"
end
end
def law_used_act
if self.law_used == 'foi'
- return "Freedom of Information Act"
+ return _("Freedom of Information Act")
elsif self.law_used == 'eir'
- return "Environmental Information Regulations"
+ return _("Environmental Information Regulations")
else
raise "Unknown law used '" + self.law_used + "'"
end
end
def law_used_with_a
if self.law_used == 'foi'
- return "A Freedom of Information request"
+ return _("A Freedom of Information request")
elsif self.law_used == 'eir'
- return "An Environmental Information Regulations request"
+ return _("An Environmental Information Regulations request")
else
raise "Unknown law used '" + self.law_used + "'"
end
diff --git a/app/models/public_body.rb b/app/models/public_body.rb
index ab836657b..453e3a6cf 100644
--- a/app/models/public_body.rb
+++ b/app/models/public_body.rb
@@ -64,8 +64,14 @@ class PublicBody < ActiveRecord::Base
end
def translated_versions=(translation_attrs)
+ def skip?(attrs)
+ valueless = attrs.inject({}) { |h, (k, v)| h[k] = v if v != '' and k != 'locale'; h } # because we want to fall back to alternative translations where there are empty values
+ return valueless.length == 0
+ end
+
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.attributes = attrs
calculate_cached_fields(t)
@@ -73,6 +79,7 @@ class PublicBody < ActiveRecord::Base
end
else # Array => creating
translation_attrs.each do |attrs|
+ next if skip?(attrs)
new_translation = PublicBody::Translation.new(attrs)
calculate_cached_fields(new_translation)
translations << new_translation
@@ -309,22 +316,23 @@ class PublicBody < ActiveRecord::Base
# The "internal admin" is a special body for internal use.
def PublicBody.internal_admin_body
- pb = PublicBody.find_by_url_name("internal_admin_authority")
- if pb.nil?
- pb = PublicBody.new(
- :name => 'Internal admin authority',
- :short_name => "",
- :request_email => MySociety::Config.get("CONTACT_EMAIL", 'contact@localhost'),
- :home_page => "",
- :notes => "",
- :publication_scheme => "",
- :last_edit_editor => "internal_admin",
- :last_edit_comment => "Made by PublicBody.internal_admin_body"
- )
- pb.save!
+ PublicBody.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 => MySociety::Config.get("CONTACT_EMAIL", 'contact@localhost'),
+ :home_page => "",
+ :notes => "",
+ :publication_scheme => "",
+ :last_edit_editor => "internal_admin",
+ :last_edit_comment => "Made by PublicBody.internal_admin_body"
+ )
+ pb.save!
+ end
+ return pb
end
-
- return pb
end
@@ -360,11 +368,11 @@ class PublicBody < ActiveRecord::Base
set_of_importing = Set.new()
field_names = { 'name'=>1, 'request_email'=>2 } # Default values in case no field list is given
line = 0
- CSV::Reader.parse(csv) do |row|
+ CSV.parse(csv) do |row|
line = line + 1
# Parse the first line as a field list if it starts with '#'
- if line==1 and row.to_s =~ /^#(.*)$/
+ if line==1 and row.first.to_s =~ /^#(.*)$/
row[0] = row[0][1..-1] # Remove the # sign on first field
row.each_with_index {|field, i| field_names[field] = i}
next
@@ -390,7 +398,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
- changed = {}
+ 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}"
localized_value = field_names[localized_field_name] && row[field_names[localized_field_name]]
@@ -425,7 +433,7 @@ class PublicBody < ActiveRecord::Base
public_body = PublicBody.new(:name=>"", :short_name=>"", :request_email=>"")
available_locales.each do |locale|
PublicBody.with_locale(locale) do
- changed = {}
+ 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}"
localized_value = field_names[localized_field_name] && row[field_names[localized_field_name]]
@@ -457,7 +465,7 @@ class PublicBody < ActiveRecord::Base
# Give an error listing ones that are to be deleted
deleted_ones = set_of_existing - set_of_importing
if deleted_ones.size > 0
- notes.push "Notes: Some " + tag + " bodies are in database, but not in CSV file:\n " + Array(deleted_ones).join("\n ") + "\nYou may want to delete them manually.\n"
+ notes.push "Notes: Some " + tag + " bodies are in database, but not in CSV file:\n " + Array(deleted_ones).sort.join("\n ") + "\nYou may want to delete them manually.\n"
end
# Rollback if a dry run, or we had errors
diff --git a/app/models/request_mailer.rb b/app/models/request_mailer.rb
index 75dc58447..272f2ea83 100644
--- a/app/models/request_mailer.rb
+++ b/app/models/request_mailer.rb
@@ -10,6 +10,7 @@ require 'alaveteli_file_types'
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.
diff --git a/app/views/layouts/default.rhtml b/app/views/layouts/default.rhtml
index 2af13f342..ad0560baa 100644
--- a/app/views/layouts/default.rhtml
+++ b/app/views/layouts/default.rhtml
@@ -12,7 +12,6 @@
<link rel="shortcut icon" href="/favicon.ico">
<%= stylesheet_link_tag 'main', :title => "Main", :rel => "stylesheet", :media => "all" %>
<%= stylesheet_link_tag 'fonts', :rel => "stylesheet", :media => "all" %>
- <%= stylesheet_link_tag 'theme', :rel => "stylesheet", :media => "all" %>
<%= stylesheet_link_tag 'print', :rel => "stylesheet", :media => "print" %>
<% if !params[:print_stylesheet].nil? %>
<%= stylesheet_link_tag 'print', :rel => "stylesheet", :media => "all" %>
@@ -35,11 +34,12 @@
<!--[if LT IE 8]>
<style type="text/css">@import url("/stylesheets/ie7.css");</style>
<![endif]-->
- <%= stylesheet_link_tag 'custom', :title => "Main", :rel => "stylesheet" %>
+ <!-- the following method for customising CSS is deprecated; see `doc/THEMES.md` for detail -->
+ <%= stylesheet_link_tag 'custom', :title => "Main", :rel => "stylesheet" %>
<% if force_registration_on_new_request %>
<%= stylesheet_link_tag 'jquery.fancybox-1.3.4', :rel => "stylesheet" %>
<% end %>
-
+
<% if @feed_autodetect %>
<% for feed in @feed_autodetect %>
<link rel="alternate" type="application/atom+xml" title="<%=h feed[:title] %>" href="<%=h feed[:url]%>">
@@ -49,7 +49,7 @@
<% end %>
<% end %>
<% if @has_json %>
- <link rel="alternate" type="application/json" title="JSON version of this page" href="<%=h main_url(request.request_uri) %>.json">
+ <link rel="alternate" type="application/json" title="JSON version of this page" href="<%=h main_url(request.request_uri, '.json') %>">
<% end %>
<% if @no_crawl %>
@@ -102,7 +102,8 @@
<%= _('Hello, {{username}}!', :username => h(@user.name))%>
<% if @user %>
- <%=link_to _("My profile"), user_url(@user) %>
+ <%=link_to _("My requests"), show_user_requests_path(:url_name => @user.url_name) %>
+ <%=link_to _("My profile"), show_user_profile_path(:url_name => @user.url_name) %>
<% end %>
diff --git a/app/views/public_body/_search_ahead.rhtml b/app/views/public_body/_search_ahead.rhtml
index 19c7eb4e8..436471544 100644
--- a/app/views/public_body/_search_ahead.rhtml
+++ b/app/views/public_body/_search_ahead.rhtml
@@ -1,4 +1,5 @@
<p>
+ <% if !@xapian_requests.nil? %>
<% if @xapian_requests.results.size > 0 %>
<h3><%= _('Top search results:') %></h3>
<p>
@@ -12,6 +13,7 @@
<%= render :partial => 'body_listing_single', :locals => { :public_body => result[:model] } %>
<% end %>
</div>
+ <% end %>
</p>
diff --git a/app/views/request/_search_ahead.rhtml b/app/views/request/_search_ahead.rhtml
index 9c49680c3..d0b19de7d 100644
--- a/app/views/request/_search_ahead.rhtml
+++ b/app/views/request/_search_ahead.rhtml
@@ -1,4 +1,5 @@
<div id="request_search_ahead_results">
+ <% if !@xapian_requests.nil? %>
<% if @xapian_requests.results.size > 0 %>
<h3><%= _("Possibly related requests:") %></h3>
<% end %>
@@ -9,4 +10,5 @@
<p>
<a id="body-site-search-link" target="_blank"><%= _("Or search in their website for this information.") %></a>
</p>
+ <% end %>
</div>
diff --git a/app/views/request/upload_response.rhtml b/app/views/request/upload_response.rhtml
index eaa6602a1..38c3db268 100644
--- a/app/views/request/upload_response.rhtml
+++ b/app/views/request/upload_response.rhtml
@@ -23,7 +23,7 @@ file too large for email, use the form below.')%>
<p><%= _('Enter your response below. You may attach one file (use email, or
<a href="%s">contact us</a> if you need more).')% [help_contact_path] %></p>
-<% form_tag '', :html => { :id => 'upload_response_form' }, :multipart => true do %>
+<% form_tag '', :id => 'upload_response_form', :multipart => true do %>
<p>
<label class="form_label" for="body"><% _('Response:')%></label>
<%= text_area_tag :body, "", :rows => 10, :cols => 55 %>
diff --git a/app/views/user/bad_token.rhtml b/app/views/user/bad_token.rhtml
index 443835382..538bc5606 100644
--- a/app/views/user/bad_token.rhtml
+++ b/app/views/user/bad_token.rhtml
@@ -1,6 +1,6 @@
<h1>
<%= _('Please check the URL (i.e. the long code of letters and numbers) is copied
-correctly from your email.'%>
+correctly from your email.')%>
</h1>
<p>
diff --git a/app/views/user/show.rhtml b/app/views/user/show.rhtml
index baf6621df..9ac203541 100644
--- a/app/views/user/show.rhtml
+++ b/app/views/user/show.rhtml
@@ -1,4 +1,8 @@
-<% @title = h(@display_user.name) + " - Freedom of Information requests" %>
+<% if @show_requests %>
+ <% @title = h(@display_user.name) + " - Freedom of Information requests" %>
+<% else %>
+ <% @title = h(@display_user.name) + " - user profile" %>
+<% end %>
<% if (@same_name_users.size >= 1) %>
<p><%= _('There is <strong>more than one person</strong> who uses this site and has this name.
@@ -7,7 +11,7 @@
<% end %>
<% end%>
-<% if @is_you && @undescribed_requests.size > 0 %>
+<% if @show_profile && @is_you && @undescribed_requests.size > 0 %>
<div class="undescribed_requests">
<p><%= _('Please <strong>go to the following requests</strong>, and let us
know if there was information in the recent responses to them.')%></p>
@@ -24,17 +28,18 @@
</div>
<% end %>
+<% if @show_profile %>
<div id="user_profile_header">
<div id="header_right">
- <h2><%= _('Track this person')%></h2>
- <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => false, :location => 'sidebar' } %>
-
- <h2><%= _('On this page')%></h2>
- <a href="#foi_requests"><%= _('FOI requests')%></a>
- <br><a href="#annotations"><%= _('Annotations')%></a>
- <% if @is_you %>
- <br><a href="#email_subscriptions"><%= _('Email subscriptions')%></a>
- <% end %>
+ <% if !@track_thing.nil? %>
+ <h2><%= _('Track this person')%></h2>
+ <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => false, :location => 'sidebar' } %>
+ <% end %>
+ <% if !@xapian_requests.nil? %>
+ <h2><%= _('On this page')%></h2>
+ <a href="#foi_requests"><%= _('FOI requests')%></a>
+ <br><a href="#annotations"><%= _('Annotations')%></a>
+ <% end %>
</div>
<div class="header_left">
@@ -116,7 +121,9 @@
</div>
</div>
<div style="clear:both"></div>
+<% end %>
+<% if @show_requests %>
<div id="user_profile_search">
<% form_tag(show_user_url, :method => "get", :id=>"search_form") do %>
<div>
@@ -154,8 +161,10 @@
<%= will_paginate WillPaginate::Collection.new(@page, @per_page, @display_user.info_requests.size) %>
<% end %>
<% else %>
+ <% if @show_requests %>
<h2 class="foi_results" id="foi_requests"><%= @is_you ? _('Freedom of Information requests made by you') : _('Freedom of Information requests made by this person') %> </h2>
<p><%= _('The search index is currently offline, so we can\'t show the Freedom of Information requests this person has made.')%></p>
+ <% end %>
<% end %>
<% if !@xapian_comments.nil? %>
@@ -181,7 +190,7 @@
<% end %>
<% end %>
- <% if @is_you %>
+ <% if @show_profile && @is_you %>
<% if @track_things.empty? %>
<h2 id="email_subscriptions"> <%= _('Your email subscriptions')%></h2>
<p><%= _('None made.')%></p>
@@ -232,4 +241,5 @@
<% end %>
<% end %>
<% end %>
-</div> \ No newline at end of file
+</div>
+<% end %>
diff --git a/config/boot.rb b/config/boot.rb
index 90fd9cd4d..0f5e661ed 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -31,7 +31,7 @@ module Rails
end
def vendor_rails?
- File.exist?("#{RAILS_ROOT}/vendor/rails")
+ File.exist?("#{RAILS_ROOT}/vendor/rails/Rakefile")
end
def preinitialize
diff --git a/config/environment.rb b/config/environment.rb
index 0af465049..d15ee9a0e 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -6,7 +6,7 @@
# ENV['RAILS_ENV'] ||= 'production'
# Specifies gem version of Rails to use when vendor/rails is not present
-RAILS_GEM_VERSION = '2.3.11' unless defined? RAILS_GEM_VERSION
+RAILS_GEM_VERSION = '2.3.14' unless defined? RAILS_GEM_VERSION
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
@@ -37,7 +37,11 @@ require File.join(File.dirname(__FILE__), '../lib/old_rubygems_patch')
Rails::Initializer.run do |config|
# Load intial mySociety config
- MySociety::Config.set_file(File.join(config.root_path, 'config', 'general'), true)
+ if ENV["RAILS_ENV"] == "test"
+ MySociety::Config.set_file(File.join(config.root_path, 'config', 'test'), true)
+ else
+ MySociety::Config.set_file(File.join(config.root_path, 'config', 'general'), true)
+ end
MySociety::Config.load_default
# Settings in config/environments/* take precedence over those specified here
@@ -119,14 +123,8 @@ if (MySociety::Config.get("DOMAIN", "") != "")
end
# fallback locale and available locales
-if ENV["RAILS_ENV"] == "test"
- # The tests assume that the "en" and "es" locales are available
- available_locales = ["en", "es"]
- default_locale = "en"
-else
- available_locales = MySociety::Config.get('AVAILABLE_LOCALES', 'en es').split(/ /)
- default_locale = MySociety::Config.get('DEFAULT_LOCALE', 'en')
-end
+available_locales = MySociety::Config.get('AVAILABLE_LOCALES', '').split(/ /)
+default_locale = MySociety::Config.get('DEFAULT_LOCALE', '')
FastGettext.default_available_locales = available_locales
I18n.locale = default_locale
@@ -134,6 +132,7 @@ I18n.available_locales = available_locales.map {|locale_name| locale_name.to_sym
I18n.default_locale = default_locale
# Load monkey patches and other things from lib/
+require 'ruby19.rb'
require 'tmail_extensions.rb'
require 'activesupport_cache_extensions.rb'
require 'timezone_fixes.rb'
diff --git a/config/general.yml-example b/config/general.yml-example
index dee393941..be39e5b3c 100644
--- a/config/general.yml-example
+++ b/config/general.yml-example
@@ -35,7 +35,7 @@ SPECIAL_REPLY_VERY_LATE_AFTER_DAYS: 60
FRONTPAGE_PUBLICBODY_EXAMPLES: 'tgq'
# URL of theme to install (when running rails-post-deploy script)
-THEME_URL: 'git://github.com/mysociety/whatdotheyknow-theme.git'
+THEME_URL: 'git://github.com/sebbacon/alavetelitheme.git'
# Whether a user needs to sign in to start the New Request process
FORCE_REGISTRATION_ON_NEW_REQUEST: false
diff --git a/config/initializers/fast_gettext.rb b/config/initializers/fast_gettext.rb
index 026c4111c..63cf6b50d 100644
--- a/config/initializers/fast_gettext.rb
+++ b/config/initializers/fast_gettext.rb
@@ -1,2 +1,2 @@
-FastGettext.add_text_domain 'app', :path => 'locale', :type => :po
+FastGettext.add_text_domain 'app', :path => File.join(Rails.root, 'locale'), :type => :po
FastGettext.default_text_domain = 'app'
diff --git a/config/routes.rb b/config/routes.rb
index 48bf92e75..39c6ba70f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -15,7 +15,7 @@ ActionController::Routing::Routes.draw do |map|
# Keep in mind you can assign values other than :controller and :action
# Allow easy extension from themes. Note these will have the highest priority.
- require 'config/custom-routes'
+ require File.join(Rails.root, 'config', 'custom-routes')
map.with_options :controller => 'general' do |general|
general.frontpage '/', :action => 'frontpage'
@@ -78,6 +78,8 @@ ActionController::Routing::Routes.draw do |map|
user.confirm '/c/:email_token', :action => 'confirm'
user.show_user '/user/:url_name.:format', :action => 'show'
+ user.show_user_profile '/user/:url_name/profile.:format', :action => 'show', :view => 'profile'
+ user.show_user_requests '/user/:url_name/requests.:format', :action => 'show', :view => 'requests'
user.contact_user '/user/contact/:id', :action => 'contact'
user.signchangepassword '/profile/change_password', :action => 'signchangepassword'
diff --git a/config/test.yml b/config/test.yml
new file mode 100644
index 000000000..c13b9c9db
--- /dev/null
+++ b/config/test.yml
@@ -0,0 +1,118 @@
+# test.yml
+# Test values for the "general" config file.
+#
+# Configuration parameters, in YAML syntax.
+#
+# These may be values expected by the test suite; changing them may
+# break tests.
+
+# Site name appears in various places throughout the site
+SITE_NAME: 'Alaveteli'
+
+# Domain used in URLs generated by scripts (e.g. for going in some emails)
+DOMAIN: 'localhost:3000'
+
+# ISO country code of country currrently deployed in
+# (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
+ISO_COUNTRY_CODE: DE
+
+# These feeds are displayed accordingly on the Alaveteli "blog" page:
+BLOG_FEED: 'http://www.mysociety.org/category/projects/whatdotheyknow/feed/'
+TWITTER_USERNAME: 'alaveteli_foi'
+
+# Locales we wish to support in this app, space-delimited
+AVAILABLE_LOCALES: 'en es'
+DEFAULT_LOCALE: 'en'
+
+# if 'true', respect the user's choice of language in the browser
+USE_DEFAULT_BROWSER_LANGUAGE: true
+
+# How many days should have passed before an answer to a request is officially late?
+REPLY_LATE_AFTER_DAYS: 20
+REPLY_VERY_LATE_AFTER_DAYS: 40
+# We give some types of authority like schools a bit longer than everyone else
+SPECIAL_REPLY_VERY_LATE_AFTER_DAYS: 60
+
+# example searches for the home page, semicolon delimited.
+FRONTPAGE_SEARCH_EXAMPLES: 'Geraldine Quango; Department for Humpadinking'
+
+# example public bodies for the home page, semicolon delimited - short_names
+FRONTPAGE_PUBLICBODY_EXAMPLES: 'tgq'
+
+# URL of theme to install (when running rails-post-deploy script)
+THEME_URL: 'git://github.com/sebbacon/alavetelitheme.git'
+
+
+## Incoming email
+# Your email domain, e.g. 'foifa.com'
+INCOMING_EMAIL_DOMAIN: 'localhost'
+
+# An optional prefix to help you distinguish FOI requests, e.g. 'foi+'
+INCOMING_EMAIL_PREFIX: ''
+
+# used for hash in request email address
+INCOMING_EMAIL_SECRET: 'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx'
+
+# used as envelope from at the incoming email domain for cases where we don't care about failure
+BLACKHOLE_PREFIX: 'do-not-reply-to-this-address'
+
+## Administration
+
+# Leave these two blank to skip admin authorisation
+ADMIN_USERNAME: 'asd'
+ADMIN_PASSWORD: 'qwe'
+
+# Email "from" details
+CONTACT_EMAIL: 'postmaster@localhost'
+CONTACT_NAME: 'Alaveteli Webmaster'
+
+# Where the raw incoming email data gets stored; make sure you back
+# this up!
+RAW_EMAILS_LOCATION: 'files/raw_emails'
+
+# The base URL for admin pages. You probably don't want to change this.
+ADMIN_BASE_URL: '/admin/'
+
+# Where /stylesheets sits under for admin pages. See asset_host in
+# config/environment.rb. Can be full domain or relative path (not an
+# absolute path beginning with /). Again, unlikely to want to change
+# this.
+ADMIN_PUBLIC_URL: ''
+
+# Secret key for signing cookie_store sessions
+COOKIE_STORE_SESSION_SECRET: 'your secret key here, make it long and random'
+
+# If present, puts the site in read only mode, and uses the text as reason
+# (whole paragraph). Please use a read-only database user as well, as it only
+# checks in a few obvious places.
+READ_ONLY: ''
+
+# Doesn't do anything right now.
+STAGING_SITE: 1
+
+# Recaptcha, for detecting humans. Get keys here: http://recaptcha.net/whyrecaptcha.html
+RECAPTCHA_PUBLIC_KEY: 'xxx'
+RECAPTCHA_PRIVATE_KEY: 'xxx'
+
+# For debugging memory problems. If true, the app logs
+# the memory use increase of the Ruby process due to the
+# request (Linux only). Since Ruby never returns memory to the OS, if the
+# existing process previously served a larger request, this won't
+# show any consumption for the later request.
+DEBUG_RECORD_MEMORY: false
+
+# If you have Alaveteli set up behind an HTTP caching proxy
+# (accelerator) like Varnish or Squid, you can cause the application
+# to purge selected URLs by setting these two variables (see
+# `../doc/CACHING.md` for details)
+ACCELERATOR_HOST: 'localhost'
+ACCELERATOR_PORT: '6081'
+
+# mySociety's gazeteer service. Shouldn't change.
+GAZE_URL: http://gaze.mysociety.org
+
+# Path to a program that converts a page at a URL to HTML. It should
+# take two arguments: the URL, and a path to an output file. A static
+# binary of wkhtmltopdf is recommended:
+# http://code.google.com/p/wkhtmltopdf/downloads/list
+HTML_TO_PDF_COMMAND: /usr/local/bin/wkhtmltopdf-amd64 \ No newline at end of file
diff --git a/db/.gitignore b/db/.gitignore
index 7b6eceda6..f3aee50a1 100644
--- a/db/.gitignore
+++ b/db/.gitignore
@@ -1 +1,2 @@
+schema.rb
development_structure.sql
diff --git a/db/migrate/104_create_foi_attachments.rb b/db/migrate/104_create_foi_attachments.rb
new file mode 100644
index 000000000..c53cd4f64
--- /dev/null
+++ b/db/migrate/104_create_foi_attachments.rb
@@ -0,0 +1,18 @@
+
+class CreateFoiAttachments < ActiveRecord::Migration
+ def self.up
+ create_table :foi_attachments do |t|
+ t.column :content_type, :text
+ t.column :filename, :text
+ t.column :charset, :text
+ t.column :display_size, :text
+ t.column :url_part_number, :integer
+ t.column :within_rfc822_subject, :text
+ t.column :incoming_message_id, :integer
+ end
+ end
+
+ def self.down
+ drop_table :foi_attachments
+ end
+end
diff --git a/db/migrate/105_extend_incoming_message.rb b/db/migrate/105_extend_incoming_message.rb
new file mode 100644
index 000000000..9db8649a0
--- /dev/null
+++ b/db/migrate/105_extend_incoming_message.rb
@@ -0,0 +1,17 @@
+class ExtendIncomingMessage < ActiveRecord::Migration
+ def self.up
+ add_column :incoming_messages, :sent_at, :time
+ add_column :incoming_messages, :subject, :text
+ add_column :incoming_messages, :safe_mail_from, :text
+ add_column :incoming_messages, :mail_from_domain, :text
+ add_column :incoming_messages, :valid_to_reply_to, :boolean
+ end
+
+ def self.down
+ remove_column :incoming_messages, :sent_at
+ remove_column :incoming_messages, :subject
+ remove_column :incoming_messages, :safe_mail_from
+ remove_column :incoming_messages, :mail_from_domain
+ remove_column :incoming_messages, :valid_to_reply_to
+ end
+end
diff --git a/db/migrate/106_add_hex_digest_to_foi_attachment.rb b/db/migrate/106_add_hex_digest_to_foi_attachment.rb
new file mode 100644
index 000000000..d9520a934
--- /dev/null
+++ b/db/migrate/106_add_hex_digest_to_foi_attachment.rb
@@ -0,0 +1,9 @@
+class AddHexDigestToFoiAttachment < ActiveRecord::Migration
+ def self.up
+ add_column :foi_attachments, :hexdigest, :string, :limit => 32
+ end
+
+ def self.down
+ remove_column :foi_attachments, :hexdigest
+ end
+end
diff --git a/db/migrate/107_add_date_parsed_field_to_incoming_message.rb b/db/migrate/107_add_date_parsed_field_to_incoming_message.rb
new file mode 100644
index 000000000..fbc017134
--- /dev/null
+++ b/db/migrate/107_add_date_parsed_field_to_incoming_message.rb
@@ -0,0 +1,9 @@
+class AddDateParsedFieldToIncomingMessage < ActiveRecord::Migration
+ def self.up
+ add_column :incoming_messages, :last_parsed, :datetime
+ end
+
+ def self.down
+ remove_column :incoming_messages, :last_parsed
+ end
+end
diff --git a/db/migrate/108_change_safe_mail_from_to_mail_from.rb b/db/migrate/108_change_safe_mail_from_to_mail_from.rb
new file mode 100644
index 000000000..57997f674
--- /dev/null
+++ b/db/migrate/108_change_safe_mail_from_to_mail_from.rb
@@ -0,0 +1,11 @@
+class ChangeSafeMailFromToMailFrom < ActiveRecord::Migration
+ def self.up
+ remove_column :incoming_messages, :safe_mail_from
+ add_column :incoming_messages, :mail_from, :text
+ end
+
+ def self.down
+ remove_column :incoming_messages, :mail_from
+ add_column :incoming_messages, :safe_mail_from, :text
+ end
+end
diff --git a/doc/CHANGES.md b/doc/CHANGES.md
index d4b8ca379..b6e901fa9 100644
--- a/doc/CHANGES.md
+++ b/doc/CHANGES.md
@@ -1,3 +1,22 @@
+# Version 0.5
+
+## Highlighted features
+* It should now be possible to develop the software on OSX
+* Base design refactored: CSS simplified and reduced, base design colours removed, now provided in example Alaveteli theme override
+* It is now possible to rebuild the xapian index for specific terms, rather than having to drop and rebuild the entire database every time (as previously). See rake xapian:rebuild_index for more info.
+* When listing authorities, show all authorities in default locale, rather than only those in the currently selected locale.
+* Ensure incoming emails are only ever parsed once (should give a performance boost)
+* [Full list of changes on github](https://github.com/sebbacon/alaveteli/issues?state=closed&milestone=9)
+
+## Upgrade notes
+* **IMPORTANT! We now depend on Xapian 1.2**, which means you may need to install Xapian from backports. See [issue #159](https://github.com/sebbacon/alaveteli/issues/159) for more info.
+* Themes created for 0.4 and below should be changed to match the new format (although the old way should continue to work):
+ * You should create a resources folder at `<yourtheme>/public/` and symlink to it from the main rails app. See the `install.rb` in `alaveteli-theme` example theme for details.
+ * Your styles should be moved from `general/custom_css.rhtml` to a standalone stylesheet in `<yourtheme>/public/stylesheets/`
+ * The partial at `general/_before_head_end.rhtml` should be changed in the theme to include this stylesheet
+* [issue #281](https://github.com/sebbacon/alaveteli/issues/281) fixes some bugs relating to display of internationalised emails. To fix any wrongly displayed emails, you'll need to run the script at `script/clear-caches` so that the caches can be regenerated
+* During this release, a bug was discovered in pdftk 1.44 which caused it to loop forever. Until it's incorporated into an official release, you'll need to patch it yourself or use the Debian package compiled by mySociety (see link in [issue 305](https://github.com/sebbacon/alaveteli/issues/305))
+
# Version 0.4
## Highlighted features
@@ -16,6 +35,9 @@
* TRACK_SENDER_EMAIL
* TRACK_SENDER_NAME
* HTML_TO_PDF_COMMAND
+ * NEW_RESPONSE_REMINDER_AFTER_DAYS
+ * FORCE_REGISTRATION_ON_NEW_REQUEST
+* The config variable `FRONTPAGE_SEARCH_EXAMPLES` is no longer used, so you should remove it to avoid confusion.
* Execute `script/rebuild-xapian-index` to create new xapian index
terms used in latest version of search (can take a long time)
* Install wkhtmltopdf to enable PDFs in downloadable zipfiles. A
diff --git a/doc/INSTALL.md b/doc/INSTALL.md
index 656b96467..963d0b6f0 100644
--- a/doc/INSTALL.md
+++ b/doc/INSTALL.md
@@ -1,8 +1,13 @@
-These instructions are based on getting the FOI site up and running on
-Ubuntu and/or Debian.
+These instructions assume Debian Squeeze or Ubuntu 11.04, or later
+(probably, though we won't necessarily have tested in later versions
+yet!)
+[Install instructions for OS X](https://github.com/sebbacon/alaveteli/wiki/OS-X-Quickstart)
+are under development.
-It was last run using the Lucid Lynx version of Ubuntu and on the
-Parallels debian instance (2.6.18-4-686).
+It is possible to install on Ubuntus as old as 10.04, but you must use
+[Xapian backports](https://launchpad.net/~xapian-backports/+archive/xapian-1.2)
+(see [issue #158](https://github.com/sebbacon/alaveteli/issues/159)
+for discussion).
Commands are intended to be run via the terminal or over ssh.
@@ -46,6 +51,12 @@ graphical interface running) on Linux. If you do install
`wkhtmltopdf`, you need to edit a setting in the config file to point
to it (see below).
+Version 1.44 of `pdftk` contains a bug which makes it to loop forever
+in certain edge conditions. Until it's incorporated into an official
+release, you can either hope you don't encounter the bug (it ties up a
+rails process until you kill it) you'll need to patch it yourself or
+use the Debian package compiled by mySociety (see link in
+[issue 305](https://github.com/sebbacon/alaveteli/issues/305))
# Configure Database
@@ -72,8 +83,8 @@ constraints whilst running the tests they also need to be a superuser.
The following command will set up a user 'foi' with password 'foi':
- echo "CREATE DATABASE foi_development encoding = 'UTF8';
- CREATE DATABASE foi_test encoding = 'UTF8';
+ echo "CREATE DATABASE foi_development encoding = 'SQL_ASCII';
+ CREATE DATABASE foi_test encoding = 'SQL_ASCII';
CREATE USER foi WITH CREATEUSER;
ALTER USER foi WITH PASSWORD 'foi';
ALTER USER foi WITH CREATEDB;
@@ -81,6 +92,24 @@ The following command will set up a user 'foi' with password 'foi':
GRANT ALL PRIVILEGES ON DATABASE foi_test TO foi;
ALTER DATABASE foi_development OWNER TO foi;
ALTER DATABASE foi_test OWNER TO foi;" | psql
+
+We create using the ``SQL_ASCII`` encoding, because in postgres this
+is means "no encoding"; and because we handle and store all kinds of
+data that may not be valid UTF (for example, data originating from
+various broken email clients that's not 8-bit clean), it's safer to be
+able to store *anything*, than reject data at runtime.
+
+# Configure email
+
+You will need to set up an email server (MTA) to send and receive
+emails. Full configuration for an MTA is beyond the scope of this
+document. However, just to get the tests to pass, you will at a
+minimum need to allow sending emails via a `sendmail` command (a
+requirement met, for example, with `sudo apt-get install exim4`).
+
+To receive email in a production setup, you will also need to
+configure your MTA to forward incoming emails to Alaveteli. An
+example configuration is described in `INSTALL-exim4.md`.
# Set up configs
@@ -266,3 +295,34 @@ is supplied in `../conf/varnish-alaveteli.vcl`.
Did you remember to remove the file `alaveteli/config/rails_env.rb`
as described above? It's created every time you run
`script/rails-post-deploy`
+
+* **Non-ASCII characters are being displayed as asterisks in my incoming messages**
+
+ We rely on `elinks` to convert HTML email to plain text.
+ Normally, the encoding should just work, but under some
+ circumstances it appears that `elinks` ignores the parameters
+ passed to it from Alaveteli.
+
+ To force `elinks` always to treat input as UTF8, add the following
+ to `/etc/elinks/elinks.conf`:
+
+ set document.codepage.assume = "utf-8"
+ set document.codepage.force_assumed = 1
+
+ You should also check that your locale is set up correctly. See
+ [https://github.com/sebbacon/alaveteli/issues/128#issuecomment-1814845](this issue followup)
+ for further discussion.
+
+* **I'm getting lots of `SourceIndex.new(hash) is deprecated` errors when running the tests**
+
+ The latest versions of rubygems contain a large number of noisy
+ deprecation warnings that you can't turn off individually. Rails
+ 2.x isn't under active development so isn't going to get fixed (in
+ the sense of using a non-deprecated API). So the only vaguely
+ sensible way to avoid this noisy output is to downgrade rubygems.
+
+ For example, you might do this by uninstalling your
+ system-packaged rubygems, and then installing the latest rubygems
+ from source, and finally executing `sudo gem update --system
+ 1.6.2`.
+
diff --git a/doc/THEMES.md b/doc/THEMES.md
index a4793a6fd..c2381b61f 100644
--- a/doc/THEMES.md
+++ b/doc/THEMES.md
@@ -50,24 +50,21 @@ These means that a file at
`vendor/plugins/alavetelitheme/lib/help/about.rhml` will appear in
place of the core "about us" file.
-There's a special file at
-`vendor/plugins/alavetelitheme/lib/views/general/custom_css.rhtml`.
-Its contents are automatically included as the last CSS file in the
-header of the site. In theory, it should be possible to do quite a
-lot of layout customisation by only changing this file.
-
-Your CSS is likely to reference various images such as a logo,
-background images, etc. We don't have a nice way of supporting these
-at the moment. The current practice is to place them in
-`vendor/plugins/alavetelitheme/public/images/` and then symlink this
-directory to somewhere within the `public/` folder in the main
-Alaveteli Rails app, e.g.:
-
- ln -s $ALAVETELI_SITE/vendor/plugins/alavetelitheme/public/images/ public/images/my_images
-
-...and then refer to these in your custom_css.rhtml like so:
-
- background-image: url("../my_images/navimg/my-logo.png");
+Rails expects all its stylesheets to live at `<railshome>/public`,
+which presents a problem for plugins. Here's how we solve it: the
+stylesheet and associated resources for your theme live (by
+convention) in at `alavatelitheme/public/`. This is symlinked from
+the main Rails app -- see `alavetelitheme/install.rb` to see how this
+happens.
+
+The partial at
+`alavetelitheme/lib/views/general/_before_head_end.rhtml` includes the
+custom CSS in your theme's stylesheet folder (by convention, in
+`alavetelitheme/public/stylesheets/`), with:
+
+ <%= stylesheet_link_tag "/alavetelitheme/stylesheets/custom" %>
+
+...which will, of course, need changing for your theme.
# Customising the request states
diff --git a/doc/todo.txt b/doc/todo.txt
deleted file mode 100644
index e78429960..000000000
--- a/doc/todo.txt
+++ /dev/null
@@ -1,559 +0,0 @@
-
-
-Next (things that will reduce admin time mainly)
-====
-
-- "Can I help out" a bit invisible
-
-Destroy request - does it remove the tags?
-Richard says he wants the internationalisation to be so it could be one site
-with combined search. Why obey the notion of a country? I'm not sure, but
-it might be prudent to write it so it can run multiple jurisdictions in
-one deploy, if only for administrative reasoins.
- - path maybe: lib/juris/uk, lib/juris/eu etc.
- - consider Single Table Inheritance (harder to back out of though)
- - http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html
- - use mixins with explicit include otherwise
-
-Add links to these tags where possible:
- ch:* - Bodies that appear on the Register of Companies. '*' is replaced by the company number, which is eight characters long and consists of optional upper-case letters followed by digits.
- coins:* Bodies appearing in COINS database followed by reference code e.g. coins:BRL048 (British Library)
- dpr:* - Bodies that appear on the Register of Data Controllers. '*' is replaced by the registration number.
- urn:* - Bodies that appear on EduBase. '*' is replaced by the institution's Unique Reference Number.
- VAT:* - Bodies with a VAT (UK Value Added Tax) registration number. '*' is replaced by the VAT registration number (no spaces) e.g. VAT:895108987
-http://foiwiki.com/foiwiki/index.php/WhatDoTheyKnow.com_Tags
-
-Merge with New Zealand code base properly
-
-Handle bounce messages from alerts automatically
-
-Make it so when you make followups the whole request is shown on the page.
-Remove all show_response URLs, and replace with a special version of the
-request URL with a new input box at the bottom and a hash link to it?
-<< when following links such as "I'm about to send clarification", a
-form appears into which the reply can be typed. However, the
-previous correspondence in that thread is not shown.
-I usually open a new tab to see what was written previously before
-writing in the form. It might be useful if the previous
-correspondence were instead shown on the page in which the form
-appears. >>
-
-Make profile photo on comments slightly larger
-
-Ask people for annotation immediately after they have submitted their request
-Ask for annotation about what they learnt from request?
-
-Private request premium feature
-http://www.activemerchant.org/
-
-Froze during reindex, is the doc files inside the .zip here:
- http://www.whatdotheyknow.com/request/last_collection_times#incoming-8405
- ActsAsXapian.rebuild_index InfoRequestEvent 16061
- foi 23175 0.0 0.0 5176 1472 pts/1 S+ 14:16 0:00 /bin/sh /usr/bin/wvText /tmp/foiextract20100619-20578-1gcbuqz-0 /tmp/foiextract20100619-20578-1gcbuqz-0.txt
- foi 23180 0.0 0.0 4664 1220 pts/1 S+ 14:16 0:00 /bin/sh /usr/bin/wvHtml -1 /tmp/foiextract20100619-20578-1gcbuqz-0 --targetdir=/tmp wv-XeJwGT
-Also freezes Abiword, but not catdoc
-
-
-Performance
-===========
-
-Reduce storing the number of bogus post redirects that aren't people
-
-Receiving email can be resource drain starting app instance each time - use daemon instead
-
-Cache /feed/list/successful
-Cache /body/list/a
-
-Cache parts of /body/xxxxx
-Cache parts of /user/xxxxx
-
-Finish migration to Rails 1.9 - for uncached requests, seems to be twice as fast.
-
-Regular expression library - change to faster one. Oniguruma isn't enough.
-This shows slowness:
- e = InfoRequestEvent.find(213700)
- text = e.incoming_message.get_main_body_text (XXX alter to call internal not cache)
- IncomingMessage.remove_quoted_sections(text, "")
-
-This is slow:
-http://www.whatdotheyknow.com/request/renumeration_committee
-
-Varnish config
-http://www.varnish-cache.org/wiki/VCLExampleCachingLoggedInUsers
-
-Some requests to lower memory use of still:
-PID: 676 CONSUME MEMORY: 16968 KB Now: 102604 KB http://www.whatdotheyknow.com/request/parking_ticket_data_81
-PID: 2036 CONSUME MEMORY: 129368 KB Now: 179652 KB http://www.whatdotheyknow.com/request/14186/response/33740
-- search engines shouldn't be going for those URLs. and do they really need to
- unpack so much? could use snippet cache.
-
-Things to make bots not crawl somehow:
- /request/13683/response?internal_review=1
- /request/febrile_neutropenia_154?unfold=1
-
-Renaming of a body, or changing its domain, should clear the cached bubbles of
-all requests to that body.
-
-Change it to store emails as files in the filesystem? For speed of backup if nothing else.
-Should have simpler system for us to upload files sent to us via CD etc.
-Currently we have to manually put them in the files directory on the vhost.
-Make it so web upload interface copes gracefully with arbitarily large messages
-(it causes speed trouble having them in the database right now)
-Maybe have flag saying "don't try to make snippet for this it is just too big".
-
-Compress the emails in the database, or on disk
-
-Keep cached columns in database in their own table, might give performance benefits
-
-
-
-
-Letting you hide individual events (incoming/outgoing messages, annotations)
-==================================
-
-*** this needs either removing or finishing, it is half done. Has the
-database entries but doesn't use them yet.
-
-Move comments to use new system first
-Show message to signed in user that others can't see this part
-Make sure hidden things don't show in search snippets
- in models/comment.rb: # So when made invisble it vanishes
-Remove comments visible
-
-
-Later
-=====
-
-JSON API:
- Pagination on the Atom feed JSON, so you can get later pages, and/or choose > 25 items
- Information about attachments in event JSON
- Allow Javascript callbacks (JSONP)
-
-Spelling correction not working if you search for "comission", as described here:
- http://comments.gmane.org/gmane.comp.search.xapian.general/8384
-When this patch from Xapian is in stable version, check that it all works.
- http://trac.xapian.org/changeset/14859
-
-Make outgoing requests and follow ups get CCed to our backup mailbox, so that
-can do data recovery more easily
-
-Admin button to resend request one off to particular address
-
-Stop search for users working on unconfirmed accounts
-
-Make zip file contents browseable, so each document in them "appears in Google".
-http://www.whatdotheyknow.com/request/transport_direct_cycle_journey_p#incoming-78421
-
-Ability to move requests to other bodies. Useful in these two places - anywhere else?
-http://www.whatdotheyknow.com/body/suffolk_primary_care_trust_pct_duplicate
-http://www.whatdotheyknow.com/body/colchester_hospital_university_nhs_foundation_trust_duplicate
-
-In admin interface, move a response from the holding pen to a request which is
-closed to new responses. The message disappears into the ether. Should either
-stop or allow such moves.
-
-PDF that gets corrupted by email censoring - have only seen this once, watch for it again
- http://www.whatdotheyknow.com/request/information_on_traffic_flows_in
-The image in a "stream" section get corrupted:
- _#p!/DB]eER4cPAPm&W7;-]L!e(*U=7"h^X7hYXqSI][9UZJV+>hr2:&c@S.lRr.ndm)2]b$-lU+#lg
- _#p!/DB]eER4cPAPm&W7;-]L!e(*U=7"h^X7hYXqSI][9UZJV+>hr2:&x@x.xxx.xxx)2]b$-lU+#lg
-Needs a fancy PDF library (which doesn't exist yet) that can tell when it is
-binary or text stream within the file. See thread in email "corrupted pdf" for
-more details. Maybe have option in admin to turn off censoring on a particular file.
-Maybe just do an MX check to see if it is really an email :)
-
-check-recent-requests-sent probably doesn't work, as exim log lines wouldn't
-be load in case where the envelope from gets broken?
-
-Point all MX records to one server, so can see incoming messages in exim logs also.
-Hmmm, but less robust. Run the exim log grabber across all mail servers?
-
-When on a small screen, the actual form when making a new request is below
-the fold, and it isn't obvious what you need to do. (Seen while watching
-a new user try to make a request)
-
-Put public body name in search text for each info request, so that people
-typing in a word and a body name in the search (have seen people new
-to the site doing that) do find things
-
-Completely remove the "feed" option in the database from tracks (it is only
-there for historical reasons, as feeds used to have to be explicitly in the
-database)
-
-Show the Subject: lines on request pages. Perhaps only show them where they are
-substantively different (modulo Re: and Fwd:) from the title and other subjects
-- so you can see any FOI code number the authority has put in the subject.
-
-For Scotland, don't need to say "normally" equivocally when it is taking more than 20 days
-(as there is no public interest test).
-Arguably, and to simplify things with Alaveteli, we should just be campaigning about this whole public interest test thing anyway, and not pussy foot about it. Not sure what form of wording to use that does that that is true though.
-
-Add explicit option for user to select "misdelivered to the wrong request"
-and let people move them to the right place.
-(Julian wants that too)
-
-Perhaps fold up request pages more by default - don't show known acknowledgements until
-you click and some (javascript) expands them.
-
-Some people want the fold/unfold of quoted sections to be javascripty as well.
-Esp. when filling in a form on the same page.
-
-Somehow make clear that a "rejection because referring to info already
-in public domain" should really be marked sucessful.
-
-Emails sent to stopped requests should follow RFC: http://tools.ietf.org/html/rfc3834
- Shouldn't bounce message back to Auto-Submitted
- Should check from address being replied to is valid
- Should set In-Reply-To and References fields
- Reconsider message content given that section in RFC
-
-When registering a new user, give a warning if they only enter one name. Link
-to the help about pseudonymous requests, that you need at least initial
-as well.
-
-Let requesters view the uncensored versions of their correspondence (e.g. with
-emails in it). Let other people do so with a CAPTCHA?
-Perhaps easy thing to do is just to do it for email addresses (and not the request email address)
-
-For followups, have radio button to say is it a new request or followup
-Do by uncommenting the "new information" option when writing a followup, so
-that it makes a new request
-
-When a user says that a response is an error message, it prompts them to say
-what the problem is to administrators. It could at that time show them the
-email of the authority that their request was sent to for them to check
-then and there, and tell the administrators.
-
-If you've already conducted an internal review, at all places
- - when on unhappy/url
- - when on not held link
- - on the page for the request
-don't offer it again, as they've already done it.
-Example of completed review:
-http://www.whatdotheyknow.com/request/request_for_full_disclosure_of_b#incoming-9267
-
-Don't allow sending internal review boiler plate text twice (although make sure they
-can write followups to internal review)
-e.g. http://www.whatdotheyknow.com/request/reply_to_letter_from_historic_ro
-
-Clock for internal review
- The Information Commissioner has issued a "Good Practice Guidance" document: http://www.ico.gov.uk/upload/documents/library/freedom_of_information/detailed_specialist_guides/foi_good_practice_guidance_5.pdf
- 20 days is late
- 40 days max.
-Fix up the text: "The internal review should take 2-3 weeks for simple cases,
-and up to 6 weeks even for complex reviews."
-Awaiting internal review overdue state?
-
-Sort requests on user page by status.
- "For sorting I was just thinking of a generic sort/filter by clicking
- on the header or some such -- I'd probably want to sort open requests
- in order of 'last action'... to quickly see what was most overdue."
-Group list on user page by authority
-
-I have several email alerts set up. Is there any chance they could include part
-(or, preferably, all) of the search criterion in the Subject: line? :o)
-(Perhaps do it in the case when only one search criterion makes the mail)
-
-Search for text "internal review" in followups and add warning if they aren't
-using the internal review mode.
-
-CSS / design things
- - The stepwise instruction boxes "Next, select the public authority ... "
- need to look better, and have icons associated with them etc.
- - CSS error on "all councils" page on some browsers
- https://bugzilla.mozilla.org/show_bug.cgi?id=424194
- - Spacing on error boxes round form elements. Matthew says:
- Well, the correct thing to do is have the class="fieldWithErrors" on the
- <p> containing the Summary: label and text input box, not have the
- pointless <span> at all, and then it all looks perfect and as you'd
- expect. But I had a look at the code and haven't got the slightest clue
- how you'd do that, sorry, given it appears new.rhtml is printing the <p>
- but some magic Ruby thing is printing the error span.
- - Improve CSS on IE7 for large images in docs
- http://www.whatdotheyknow.com/request/3289/response/7810/attach/html/3/20081023ReplyLetter.pdf.html
- - Get Atom feed of search results to include stylesheet for highlighting words in
- yellow somehow
-
-When doing search, people often just want it to show the whole page. Perhaps
-all listing should just link to top of page, rather than # links for outgoing
-incoming, or perhaps just some of them.
-
-Some more traditional help (in a new section in the help) such as:
- * Information about how to track requests and RSS feeds
- * Information about how to contacting other users
- * How to change your email address
-
-Show similar requests after you have filed yours - maybe on preview too.
-
-Test code for rendering lots of different attachments and filetypes
-Test code for internal review submitting
-
-Look at quote_address_if_necessary in actionmailer's quoting.rb - why did it
-not work for the email address with "@" in its name part?
-
-Something to check which tags are used but aren't in PublicBody category lists
-
-Other references to title field changes don't get search index updated when title is altered
- (e.g. when a public body is renamed)
-Maybe just reindex all once a week, but it is a bit slow now, so perhaps do it properly.
-$ ./script/rebuild-xapian-index
-
-Renaming public authorities will break alerts on them. For basic alerts the
-structured info is there so this should just be fixed. For searches, perhaps
-Xapian index should search url_name history as well?
-We have a policy of not renaming them in some cases, which helps a bit.
-
-Never updates cached attachment text unless cache is explicitly cleared (which
- might matter with software updates, or code changes). Should we clear the
- cache automatically every month in the middle of the night or something?
-$ ./script/clear-caches
-Alternative - embed some identifier in the code and the cache directory, and
-detect it changes and automatically clear the cache.
-
-
-Display and indexing of response emails/attachments
----------------------------------------------------
-
-Install more recent poppler-utils
- e.g. 0.12.0 can definitely convert this to HTML, extacting the images:
- http://www.whatdotheyknow.com/request/13903/response/36117/attach/html/4/FOI%20beaver%20site%20species%20audit%20SNH%20review%20of%20proposal%20redact.pdf.html
-Really need a "pdftk -nodrm" to remove compression from encrypted PDFs, so strips emails from e.g.:
- http://www.whatdotheyknow.com/request/14414/response/38590/attach/html/3/090807%20FOI.pdf.html
- ... this misses a whole page out (someone emailed us)
- http://www.whatdotheyknow.com/request/unredacted_expense_claims_for_jo#incoming-49674
-
-Worth doing View as HTML ourselves for .docx, .ppt, .tif (covered now by Google Docs)
-View as HTML for .txt requested
-
-Failed to detect attachments are emails and decode them:
- http://www.whatdotheyknow.com/request/malicious_communication_act#incoming-12964
-
-When indexing .docx do you need to index docProps/custom.xml and docProps/app.xml
-as well as word/document.xml ? (thread on xapian-discuss does so)
-
-Consider using odt2txt or unoconv
-http://www-verimag.imag.fr/~moy/opendocument/
-
-Mime type / extension wrong on these .docx's
-http://www.whatdotheyknow.com/request/bridleway_classifications
-http://www.whatdotheyknow.com/request/19976/response/51468/attach/3/TU%20MembershipTeachers%20SolidarityTU%20231009.docx.doc (thinks it is doc when it is docx)
-Search for "OIC" for some more examples
-
-VSD files vsdump - example in zip file
- http://www.whatdotheyknow.com/request/dog_control_orders#incoming-3510
- doing file RESPONSE/Internal documents/Briefing with Contact Islington/Contact Islington Flowchart Jul 08.vsd content type
-
-Search for other file extensions that we have now and look for ones we could
-and should be indexing
- (call IncomingMessage.find_all_unknown_mime_types to find them - needs
- updating to do it in clumps as all requests won't load in RAM now )
-
-Render HTML alternative rather than text (so tables look good) e.g.:
- http://www.whatdotheyknow.com/request/parking_policy
-
-These attachment.bin files should come out as winmail.dat and be parsed
-by existing TNEF code. For some reason though TMail doesn't get the right
-content-type out of them. Not sure why.
- http://www.whatdotheyknow.com/request/acting_up_in_a_higher_rank
-
-Make HTML attachments have view as HTML :)
-http://www.whatdotheyknow.com/request/enforced_medication#incoming-7395
-
-Knackered view as HTML:
- http://www.whatdotheyknow.com/request/1385/response/5483/attach/html/3/Response%20465.2008.pdf.html
-Some other pdftohtml bugs (fix them or file about them)
- http://www.whatdotheyknow.com/request/sale_of_public_land#incoming-8146
- http://www.whatdotheyknow.com/request/childrens_database_compliance_wi#incoming-8088
- http://www.whatdotheyknow.com/request/3326/response/7701/attach/html/2/Scan001.PDF.pdf.html
- http://www.whatdotheyknow.com/request/risk_log#incoming-8090 (bad tables)
- http://www.whatdotheyknow.com/request/4635/response/11248/attach/html/4/FOI%20request.pdf.html (bad table)
-Orientation wrong:
- http://www.whatdotheyknow.com/request/3153/response/7726/attach/html/2/258850.pdf.html
-Bug in wvHtml, segfaults when converting this:
- http://www.whatdotheyknow.com/request/subject_access_request_guide_sar#incoming-10242
-
-Images aren't coming out here
- http://www.whatdotheyknow.com/request/33682/response/83455/attach/html/3/100428%20Reply%201519%2010.doc.html
-
-Doesn't detect doc type of a few garbage results in this list right:
- http://www.whatdotheyknow.com/search/UWE
-
-Quoting fixing TODO:
- http://www.whatdotheyknow.com/request/35/response/191 # Funny disclaimer
- http://www.whatdotheyknow.com/request/40/response/163 # funny disclaimer
- http://www.whatdotheyknow.com/request/35/response/191 # funny disclaimer "- - Disclaimer - -"
-
- http://www.whatdotheyknow.com/request/m3_junction_2_eastbound_speed_re # cut here
-
- http://www.whatdotheyknow.com/request/123/response/184 # nasty nasty formatted quoting
- http://www.whatdotheyknow.com/request/155/response/552 # nasty nasty formatted quoting
- http://www.whatdotheyknow.com/request/how_do_the_pct_deal_with_retirin_87#incoming-1847
- http://www.whatdotheyknow.com/request/complaints_about_jobcentres#incoming-688 # word wrapping of <
-
- http://www.whatdotheyknow.com/request/224/response/589 # have knackered the apostrophes here
-
- http://www.whatdotheyknow.com/request/operation_oasis_protester_databa#incoming-20922
-
- http://www.whatdotheyknow.com/request/new_bristol_city_stadium_plansci#incoming-44114 # funny forward not detected
- http://www.whatdotheyknow.com/request/the_facts_about_side_effects_cau#incoming-6754 # "Communications via the GSi" should be stripped, so then subject would get shown
-
- Unclassified:
- http://www.whatdotheyknow.com/request/666/response/1020
- http://www.whatdotheyknow.com/request/364/response/1100
- http://www.whatdotheyknow.com/request/council_housing_accommodation # over zealous half cuts
- http://www.whatdotheyknow.com/request/621/response/1131 # virus footer
- http://www.whatdotheyknow.com/request/231/response/338
- http://www.whatdotheyknow.com/request/930/response/1609
- http://www.whatdotheyknow.com/request/1102/response/2067
- http://www.whatdotheyknow.com/request/list_of_public_space_cctv_instal#incoming-2164
- http://www.whatdotheyknow.com/request/errors_in_list_of_postbox_locati#incoming-2272
- http://localhost:3000/request/cctv_data_retention_and_use#incoming-2093
- http://www.whatdotheyknow.com/request/stasi_activity_at_climate_camp#incoming-3362
- http://www.whatdotheyknow.com/request/total_remuneration_and_benefits#incoming-2436
- http://www.whatdotheyknow.com/request/dual_british_and_israeli_nationa#incoming-3461
- http://www.whatdotheyknow.com/request/council_functions_55#incoming-4099
- http://www.whatdotheyknow.com/request/public_safety_consequential_to_c#incoming-1586
- http://www.whatdotheyknow.com/request/functions_council_43#incoming-4509
- http://www.whatdotheyknow.com/request/york_road_tube_re_opening_feasib#incoming-3509
- http://www.whatdotheyknow.com/request/controlled_drinking_zones_5#incoming-4210
- http://www.whatdotheyknow.com/request/road_and_junction_specifications#incoming-3598
- http://www.whatdotheyknow.com/request/disused_live_stations#incoming-4898
- http://www.whatdotheyknow.com/request/errors_in_list_of_postbox_locati#incoming-3577
-
- http://www.whatdotheyknow.com/request/public_inspection_periods_for_lo_2#outgoing-1707 # square bracket in link
- http://www.whatdotheyknow.com/request/digital_tv_switchover_in_local_a#incoming-4931
- http://www.whatdotheyknow.com/request/local_government_ombudsman_58#incoming-5763
-
- http://www.whatdotheyknow.com/request/415/response/1041/attach/3/CONF%20FOI%209508%20Ian%20Holton.doc
-
- http://www.whatdotheyknow.com/request/function_council_88#incoming-6258
- http://www.whatdotheyknow.com/request/please_submit_the_surveyors_repo#incoming-6334 # charset
- http://www.whatdotheyknow.com/request/archive_record#incoming-7514 # charset
-
- http://www.whatdotheyknow.com/request/enforcement_forders_for_replacin#incoming-6277 # over zealous quoting
- http://www.whatdotheyknow.com/request/renewable_energy_consumption_by # over zealous
-
- http://www.whatdotheyknow.com/request/can_my_mp_ask_questions_on_my_be#incoming-33112 # hyperlink broken
- http://www.whatdotheyknow.com/request/clarification_of_the_igs_to_psw # wrapped link
- http://www.whatdotheyknow.com/request/request_for_details_from_consult # wrapped link
-
- http://www.whatdotheyknow.com/request/independent_psychological_assess#incoming-52956 # shows text as attachment when could be inline due to multipart nature?
-
- http://www.whatdotheyknow.com/request/bnp_membership_list_43#incoming-59204 # not detecting original message
- http://www.whatdotheyknow.com/request/maximum_pedestrian_crossing_wait#incoming-34094 # not detecting original message
-
-
-Display pasted quotes in annotations better:
- http://www.whatdotheyknow.com/request/scientology_incidents#comment-3352
-
-
-Totally new features
---------------------
-
-Publish statistics (stats) on how long it takes bodies to respond. And other
-things (like the WriteToThem pages).
-
-Add interface for editing tags on your own requests so you can keep track of them more easily.
-Lisa asked for this - is definitely only whole requests needed. Tony says
-anyone should be able to edit the tags, but requester should have last say (so
-can prevent a tag being added that they removed).
-
-Read reply - ask for Microsoft Exchange "read receipts", and show if mail was read.
-Or maybe use a webbug.
-
-Telephone numbers. Add advice in workflow to call authority first to check
-form they have info in. Store telephone numbers in database.
-
-Give authorities interface for editing their request email address
-and resend messages to them
-
-Make search know about uncategorised requests and timed out requests.
-And make search able to do *current* status in general as operator.
-
-Test data dumper that removes sensitive data, but lets trusted people play with
-whole database on their own machine without risk of compromise (for Tony)
-- can avoid rebuilding emails, attachments etc. sanitized provided we don't
-mind leaking out email address ot requests etc. to the trusted person (in contrast
-can easily totally remove private emails in the user table)
-
-Have an interface for users to be able to suggest new authorities and give
-their email address (perhaps just have admins validate / approve it)
-
-Detect councils that always send automatic acknowledgements, and notice if they
-do not for a particular request? (e.g. Leicestershire County Council)
-
-Add tips on using the law, e.g.:
-- You can go up and down between local and national - ask local places what
- their policy is, and hwo they are implementing it. Ask national things what
- odcuments set local policies.
-
-Add note by any exemption to the page on FOI Wiki Add note on mention of
-"Re-Use of Public Sector Information Regulations 2005"
-to the appropriate FAQ.
-
-Hyperlink Section 1(3) to the act (or have an automatic tip appear)
- http://www.whatdotheyknow.com/request/university_investment_in_the_arm#incoming-86
-and to guidance notes
- http://www.ico.gov.uk/what_we_cover/freedom_of_information/guidance.aspx
-
-Link to /random jump to a random request somewhere
-
-Do conversion tracking on endpoints in WDTK, advertise perhaps TWFY, or perhaps
- donations to mySociety.
-Advertise WDTK search queries on TWFY
-Advertise alerts on end pages with WDTK
-
-Forms to search this user, this request, and this authority on their pages
-
-Search FAQ and other help pages with normal search
-
-Make text boxes autogrow as you type into them.
-(10:32:14) richard: you just need to count the number of rows of text and compare it to the number of rows in the textbox
-(10:32:29) richard: then increase the height of the textbox by 1em-ish
-(10:32:52) Matthew: their function is called autogrow_textarea() by the way, if you just want to look at it...
- thanks :) I won't do it now as there are more important things, I was just accidentally impressed
-
-"Why not fill in your profile?" not that many people fill in their profile, we could have special
-advert (on page when they are on site) trying to force them to (maybe only if active). Give
-points for doing this.
-Also could offer option of Gravatar during signup, or later (as explicit option, so not leaking private info)
-
-Scoreboards and gamification. Put all admin functions in the main site, and let anyone get
-power to do them by accumulating points doing other useful things. There's lots of stuff!
-
-.tif files are hard for people to view as multi page, consider automatically
-separating out the pages as separate links (to .png files or whatever)
- http://www.whatdotheyknow.com/request/windsor_maidenhead_council_commo#incoming-1910
-Heck, may as well give thumbnails of all images, indeed all docs while you're at it :)
-
-Add geographical location of councils, PCTs etc.
-Have a single button to sign up to alerts on authorities for your postcode
-NHS postcode database:
- http://www.ons.gov.uk/about-statistics/geography/products/geog-products-postcode/nhspd/index.html
-
-Make request preview have a URL so you can show it to someone else before sending it :)
-Proposed request submission queue with comments - new requests don't get sent straight
- away, but are delayed while people help improve them.
-
-Screen scrape ICO's decision notices list and add link to it on the relevant public authority pages
-http://www.ico.gov.uk/Home/tools_and_resources/decision_notices.aspx
-
-Description for each body as to what info it holds
-Link to:
- Company number
- Aliases (not just short name, but multiple real names e.g. for museums)
- Disclosure logs
- Publication schemes (http://www.ico.gov.uk/what_we_cover/freedom_of_information/publication_schemes.aspx)
- TWFY department search
- Complaint email
- Phone number for advice and assistence (House of Lords give one http://www.parliament.uk/parliamentary_publications_and_archives/freedom_of_information_in_the_house_of_lords/lords__foi___how_to_obtain_information.cfm )
-e.g.
- http://www.ordnancesurvey.co.uk/oswebsite/aboutus/foi/index.html
- http://www.ordnancesurvey.co.uk/oswebsite/aboutus/foi/coiindex.html
-Maybe gather this data by letting authorities input it
-
-EU regulation 1049/2001 requests
-US requests (with Sunlight)
-
-OCR all images automatically, even if badly (check for tiffs!)
-
diff --git a/lib/languages.rb b/lib/languages.rb
index 43212a777..474c0e0cb 100644
--- a/lib/languages.rb
+++ b/lib/languages.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
class LanguageNames
def self.get_language_name(locale)
language_names = {
diff --git a/lib/old_rubygems_patch.rb b/lib/old_rubygems_patch.rb
index 5601a5e90..3001a7381 100644
--- a/lib/old_rubygems_patch.rb
+++ b/lib/old_rubygems_patch.rb
@@ -1,4 +1,8 @@
-require File.join(File.dirname(__FILE__),'..','vendor','rails','railties','lib','rails','gem_dependency.rb')
+if File.exist? File.join(File.dirname(__FILE__),'..','vendor','rails','railties','lib','rails','gem_dependency.rb')
+ require File.join(File.dirname(__FILE__),'..','vendor','rails','railties','lib','rails','gem_dependency.rb')
+else
+ require 'rails/gem_dependency'
+end
module Rails
class GemDependency < Gem::Dependency
diff --git a/lib/ruby19.rb b/lib/ruby19.rb
new file mode 100644
index 000000000..39f48d74e
--- /dev/null
+++ b/lib/ruby19.rb
@@ -0,0 +1,8 @@
+if RUBY_VERSION.to_f == 1.9
+ class String
+ # @see syck/lib/syck/rubytypes.rb
+ def is_binary_data?
+ self.count("\x00-\x7F", "^ -~\t\r\n").fdiv(self.size) > 0.3 || self.index("\x00") unless self.empty?
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/sendmail_return_path.rb b/lib/sendmail_return_path.rb
index d8922f78b..23c4d4376 100644
--- a/lib/sendmail_return_path.rb
+++ b/lib/sendmail_return_path.rb
@@ -6,7 +6,7 @@
module ActionMailer
class Base
def perform_delivery_sendmail(mail)
- sender = (mail['return-path'] && mail['return-path'].spec) || mail.from
+ sender = (mail['return-path'] && mail['return-path'].spec) || mail.from.first
sendmail_args = sendmail_settings[:arguments].dup
sendmail_args += " -f \"#{sender}\""
diff --git a/lib/tasks/rspec.rake b/lib/tasks/rspec.rake
index 588c26378..4024a6a6d 100644
--- a/lib/tasks/rspec.rake
+++ b/lib/tasks/rspec.rake
@@ -53,7 +53,8 @@ end
task :default => :spec
task :stats => "spec:statsetup"
-task :spec => ['spec:commonlib']
+# XXX commonlib tests are not Ruby 1.9 compatible
+#task :spec => ['spec:commonlib']
task :test => ['spec']
task :cruise => ['spec']
diff --git a/lib/tmail_extensions.rb b/lib/tmail_extensions.rb
index 6a5044cdb..f35565352 100644
--- a/lib/tmail_extensions.rb
+++ b/lib/tmail_extensions.rb
@@ -30,7 +30,7 @@ module TMail
# Monkeypatch! Return the name part of from address, or nil if there isn't one
def from_name_if_present
if self.from && self.from_addrs[0].name
- return self.from_addrs[0].name
+ return TMail::Unquoter.unquote_and_convert_to(self.from_addrs[0].name, "utf-8")
else
return nil
end
diff --git a/lib/tnef.rb b/lib/tnef.rb
index ff88b0005..1c941f8b0 100644
--- a/lib/tnef.rb
+++ b/lib/tnef.rb
@@ -9,7 +9,7 @@ class TNEF
main = TMail::Mail.new
main.set_content_type 'multipart', 'mixed', { 'boundary' => TMail.new_boundary }
Dir.mktmpdir do |dir|
- IO.popen("/usr/bin/tnef -K -C #{dir}", "w") do |f|
+ IO.popen("#{`which tnef`.chomp} -K -C #{dir}", "w") do |f|
f.write(content)
f.close
if $?.signaled?
diff --git a/public/images/logo.png b/public/images/logo.png
index 1a865124f..d5e87912c 100644
--- a/public/images/logo.png
+++ b/public/images/logo.png
Binary files differ
diff --git a/public/images/quote-marks.png b/public/images/quote-marks.png
index 752b7d4cf..e2bdfb06d 100644
--- a/public/images/quote-marks.png
+++ b/public/images/quote-marks.png
Binary files differ
diff --git a/public/stylesheets/main.css b/public/stylesheets/main.css
index 35f456e72..1166975d7 100644
--- a/public/stylesheets/main.css
+++ b/public/stylesheets/main.css
@@ -1,1296 +1,1652 @@
-/*------------------------------------------------ global */
-body
-{
- padding: 0px;
- margin: 0px;
- text-align: center;
- font-family: Tahoma, Geneva, sans-serif;
-}
-
-/*------------------------------------------------ banner */
-
-#banner
-{
- position: absolute;
- top: 0px;
- left: 0px;
- width: 100%;
- margin: 0px;
- background-color: #F0F0F0;
- border-color: #993233;
- border-width: 0 0 3px 0;
- border-style: solid;
- height: 100px;
- background-image: url(../images/navimg/bnnr-temp-100pxd.jpg);
- background-repeat: no-repeat;
- background-position: center top;
-
-}
-
-/*------------------------------------------------ header */
-#header
-{
- height: 55px;
- position: absolute;
- top: 0px;
- left: 14px;
- height: 55px;
- width: 500px;
- z-index: 200;
- text-align: left;
-}
- #header h1
- {
- font-size: 0.8em;
- line-height: 0em;
- margin: 0;
- }
-
- #header h1 a
- {
- color: #f0f0f0;
- display: block;
- height: 55px;
- background-image: url(../images/navimg/alaveteli-logo.png);
- background-repeat: no-repeat;
- }
-
- #header #tagline
- {
- font-size: 0.8em;
- font-style: italic;
- text-align: left;
- margin: 2px 0 0 0;
- color: #626262;
- }
-
- #header #user_locale_switcher
- {
- font-size: 0.8em;
- font-style: italic;
- text-align: left;
- margin: 2px 0 0 0;
- color: #626262;
- position: absolute;
- }
-/*------------------------------------------------ temp stuff */
-#staging, #alpha_notice, #beta
-{ }
-
-
-#header h1 #beta, #header h1 #beta a
-{
- display: inline;
- height: 1em;
- background-image: none;
- font-size: 12px;
- color: #A4A4A4;
-}
-
-
-#staging
-{
- visibility: hidden;
- overflow: default;
-}
-
-#alpha_notice
-{
- padding: 0 20px 0 20px;
- margin: 0 0 1em 0;
- border-color: #FF201D;
- border-width: 1px;
- border-style: solid;
- background-color: #f0f0f0;
-}
-
-#downtime {
- border: 2px dashed #993233;
- background-color: #ffcc99;
- padding: 4px;
- margin: 0 auto;
- width: 80%;
- text-align: center;
-}
-
-/*------------------------------------------------ org logo */
- #orglogo
- {
- position: absolute;
- width: 100%;
- left: 0px;
- top: 0;
- z-index: 150;
- text-align: right;
- font-size: 0.8em;
- height: 40px;
- z-index: 100;
- }
- #orglogo a
- {
- color: #f0f0f0;
- display: block;
- float: right;
- clear: none;
- height: 55px;
- width: 265px;
- background-image: url(../images/navimg/alaveteli-logo-header.png);
- background-repeat: no-repeat;
- background-position: 125px 15px;
- }
-/*------------------------------------------------ search */
-
-#navigation_search
-{
- position: absolute;
- width: 100%;
- left: 0px;
- top: 70px;
- z-index: 150;
- text-align: right;
- -moz-opacity: 0.7!important;
- filter: alpha(opacity= 70)!important;
- opacity: 0.7!important;
-}
- #navigation_search input
- {
- border-color: #010101;
- border-width: 1px;
- border-style: solid;
- background-color: #fff;
- color: #000;
- }
- #navigation_search input#navigation_search_query
- {
- width: 14em;
- }
-
-#navigation_search p { margin: 0 0.6em 0 0; }
-
-/*------------------------------------------------ topnav */
-#topnav
-{
- position: relative;
- top: 103px;
- left: 0px;
- width: 100%;
- height: auto;
- overflow: auto;
- padding: 0px 0px 0px 0px;
- z-index: 100;
- background-color: #000;
- font-size: 0.9em;
-}
-
- #topnav ul
- {
- list-style: none;
- margin: 0px;
- padding: 0px;
- }
-
- #topnav li
- {
- float: left;
- }
-
- #topnav li a, #topnav li a:visited
- {
- display: block;
- margin: 0px;
- padding: 0.15em 0.6em 0.25em 0.8em;
- color: #ADADAD;
- text-decoration: none;
- }
-
- #topnav li a:hover
- { color: #fff; }
-
- #topnav li a:active { }
-
- #topnav li a.on, #topnav li a.on:visited
- {
- font-weight: bold;
- color: #000;
- }
-
- #topnav li a.on:hover {}
-
- #topnav li a.on:active {}
-
-/*-------------------------- login/signup */
-#logged_in_bar
-{
- float: right;
- clear: none;
- font-size: 0.9em;
- z-index: 200;
- padding: 0.20em 10px 0.25em 1em;
- color: #444;
-}
-
-#logged_in_bar a, #logged_in_bar a:visited
-{
- color: #92B3FF;
-}
-
-
-/*------------------------------------------------ wrapper round content */
-
-#wrapper
-{
- position: relative;
- clear: both;
- top: 0px;
- padding-top: 100px;
- width: 58em;
- margin: 0px auto 1.2em auto;
- /* = 800px at default size? so 1em = 16px*/
- text-align: left;
- overflow: visible;
-}
-
-/*------------------------------------------------ view as HTML */
-/* XXX this copies lines from #wrapper above, as didn't want to break wrapper
- * right now */
-#wrapper_xlhtml {
- position: relative;
- clear: both;
- top: 0px;
- padding-top: 100px;
- width: 90%;
- margin: 0px auto 1.2em auto;
- /* = 800px at default size? so 1em = 16px*/
- text-align: left;
- overflow: visible;
+body {
+text-align:center;
+color:#444;
+font-size:12px;
+font-family:Arial, sans-serif;
+margin:0;
+padding:0;
+}
+
+#banner {
+position:absolute;
+top:0;
+background-color:#EEE;
+left:0;
+width:100%;
+border:none;
+height:160px;
+border:none;
+margin:0;
+}
+
+#navigation_search {
+position:absolute;
+left:0;
+z-index:150;
+text-align:right;
+-moz-opacity:0.7px;
+filter:alpha(opacity= 70) !important;
+opacity:0.7px;
+width:auto;
+right:0;
+top:10px;
+}
+
+#navigation_search input {
+background-color:#fff;
+color:#000;
+border-color:#010101;
+border-style:solid;
+border-width:1px;
+}
+
+#navigation_search input#navigation_search_query {
+width:14em;
+}
+
+#navigation_search p {
+margin:0 0.6em 0 0;
+}
+
+#topnav {
+position:relative;
+left:0;
+height:auto;
+overflow:auto;
+z-index:100;
+background-color:#000;
+background:transparent;
+top:120px;
+margin-left:115px;
+width:auto;
+font-family:Arial, sans-serif;
+font-size:18px;
+padding:0;
+}
+
+#topnav ul {
+list-style:none;
+margin:0;
+padding:0;
+}
+
+#topnav li a,#topnav li a:visited {
+display:block;
+color:#444;
+text-decoration:none;
+margin:0;
+padding:0;
+}
+
+#topnav li a:hover {
+color:#000;
+}
+
+#logged_in_bar {
+clear:none;
+font-size:0.9em;
+z-index:200;
+color:#444;
+top:18px;
+right:210px;
+float:none;
+position:absolute;
+padding:0.2em 10px 0.25em 1em;
+}
+
+#logged_in_bar a,#logged_in_bar a:visited {
+color:#444;
+}
+
+#wrapper {
+position:relative;
+clear:both;
+top:0;
+text-align:left;
+overflow:visible;
+padding-top:160px;
+width:900px;
+margin:0 auto 1.2em;
}
+
#wrapper_google_embed {
- position: relative;
- clear: both;
- width: 100%;
- height: 90%;
- margin: 0 0 0 0;
- text-align: left;
- overflow: visible;
+position:relative;
+clear:both;
+width:100%;
+height:90%;
+text-align:left;
+overflow:visible;
+margin:0;
}
-#view-html-content table {
- border-collapse: collapse;
- margin-bottom: 1em;
+
+#content {
+position:relative;
+width:875px;
+padding:1em;
}
-#view-html-content td, th {
- border: solid 1px #000000;
+
+h1,h2,h3 {
+font-family:sans-serif;
+font-weight:700;
+line-height:1em;
+letter-spacing:0;
+color:#222;
+clear:left;
}
-#view-html-content td {
- vertical-align: top
+
+h1 {
+font-size:42px;
+margin-bottom:15px;
+margin-top:10px;
}
-#view-html-content td {
- max-width: 30em;
- overflow: auto;
+
+h2 {
+font-size:1.4em;
}
-#view-html-content tr:nth-child(odd) {
- background-color: #bbbbbb;
+
+.highlight {
+background:#FF0;
+border-color:#A3A3A3;
+border-style:dotted;
+border-width:0;
}
-#view-html-content tr:nth-child(even) {
- background-color: #dddddd;
+
+dl {
+margin-top:24px;
+line-height:160%;
}
+dt {
+font-weight:700;
+}
+dd {
+width:auto;
+margin:18px 0 36px;
+}
-/*------------------------------------------------ content */
+#stepwise_make_request {
+background-color:#BBB;
+border:1px solid #222;
+border-radius:5px;
+-moz-border-radius:5px;
+color:#222;
+font-size:18px;
+text-align:left;
+width:412px;
+margin:0 14em 40px 0;
+padding:10px 12px;
+}
-#content
-{
- position: relative;
- padding: 1em 1em 1em 1em;
+#stepwise_make_request_view_email {
+text-align:center;
+background-color:#d0d0d0;
+margin:0;
+padding:1em 0;
}
-h1, h2, h3
-{
- font-family: Trebuchet, Trebuchet MS, Helvetica, sans-serif;
- /*Arial Black, Gadget, sans-serif*/
- font-weight: bold;
- line-height: 1em;
- letter-spacing: 0em;
- color: #555;
- clear: left;
+#frontpage_examples div#examples_0 {
+float:left;
+margin-left:0;
+width:49%;
}
- h1 { font-size: 1.8em;}
- h2 { font-size: 1.4em;}
- h3 { font-size: 1.2em;}
-h4, h5, h6
-{}
+#frontpage_examples div#examples_1 {
+float:right;
+margin-right:0;
+width:49%;
+}
-/* XXX Francis put this here so he could check highlighting worked */
-.highlight
-{
- background-color: #F0F0F0;
- border-color: #A3A3A3;
- border-width: 1px;
- border-style: dotted;
+#frontpage_examples ul {
+text-align:center;
+list-style:none;
+margin:0;
+padding:1em 0;
}
-dl { line-height: 1.2em; }
+div#twitter {
+float:right;
+clear:none;
+width:18em;
+background-color:#EAEAEA;
+background:#FFF;
+margin:30px 0 1em 1em;
+padding:0.5em;
+}
-dt
-{
- font-weight: bold;
+img.twitter-icon {
+vertical-align:middle;
+}
+.blog_post {
+margin-bottom:2em;
}
-dd { margin: 0.6em 0 2em 4em; width: 33em; }
-/*---------------- content : recent requests sidebar */
-#frontpage_search
-{
- text-align: center;
- margin: 3em 0em 1em 0em;
- padding: 1em;
- background-color: #d0d0d0;
+.request_listing,.body_listing,.user_listing {
+font-size:0.8em;
+margin-top:1.5em;
+border-bottom:#9C9C9C;
+overflow:hidden;
+border-style:none none solid;
+border-width:0 0 1px;
+padding:0 0 1.5em;
}
-#stepwise_instructions
-{
- text-align: center;
- margin: 0em 0em 0em 0em;
- padding: 0.2em 0em 0.2em 0em;
- background-color: #d0d0d0;
+
+.body_listing {
+_width:47em;
+padding-bottom:16px;
}
-#stepwise_instructions p
-{
- margin: 0.5em 0em 0em 0em;
- padding: 0em 0em 0em 0em;
+
+span.head {
+display:block;
+font-size:1.4em;
+font-weight:700;
+padding:12px 0 0;
}
-#stepwise_make_request
-{
- text-align: center;
- margin: 0em 14em 0em 0em;
- padding: 1em 1em 1em 1em;
- background-color: #d0d0d0;
+
+.request_listing span.head /* full page request list only */ {
+min-height:32px;
+background-image:url(../images/navimg/request-icon.png);
+background-repeat:no-repeat;
+background-position:4px 0;
+margin:0 0 0.3em;
+padding:8px 0 0 42px;
}
-#stepwise_make_request_view_email
-{
- text-align: center;
- margin: 0em 0em 0em 0em;
- padding: 1em 0em 1em 0em;
- background-color: #d0d0d0;
+
+.body_listing span.head /* full page request list only */ {
+min-height:32px;
+background-image:url(../images/navimg/auth-icon.png);
+background-repeat:no-repeat;
+background-position:4px 0;
+margin:0 0 0.3em;
+padding:8px 0 0 42px;
}
-#frontpage_examples div#examples_0 {
- float: left;
- margin-left: 0%;
- width: 49%;
+
+.user_listing span.head /* full page request list only */ {
+min-height:32px;
+background-image:url(../images/navimg/user-icon.png);
+background-repeat:no-repeat;
+background-position:4px 0;
+margin:0 0 0.3em;
+padding:8px 0 0 42px;
}
-#frontpage_examples div#examples_1 {
- float: right;
- margin-right: 0%;
- width: 49%;
+
+.user_listing span.no_icon /* full page request list only */ {
+background-image:none;
}
-#frontpage_examples p
-{
- text-align: center;
- clear: both;
+
+span.bottomline {
+clear:left;
+display:block;
+padding:10px 0 0 42px;
}
-#frontpage_examples h2 {
- text-align: center;
- clear: both;
+
+.request_listing span.bottomline
+/* full page request list only */ {
+width:35em;
+background-repeat:no-repeat;
+background-position:left center;
+min-height:42px;
+margin:0 0 0.6em;
}
-#frontpage_examples li
-{
+
+.request_icon_line {
+background-repeat:no-repeat;
+background-position:left center;
+min-height:24px;
+clear:left;
+padding:8px 0 10px 42px;
}
-#frontpage_examples ul
-{
- margin: 0 0 0 0;
- padding: 1em 0em 1em 0em;
- text-align: center;
- list-style: none;
+
+.icon_waiting_response,.icon_waiting_classification,.icon_waiting_clarification {
+background-image:url(/images/status-pending.png);
+color:#A68C2E;
}
-/*-----------------------------blog----------------*/
+.icon_rejected {
+background-image:url(../images/navimg/status-icons-fail.png);
+}
-div#twitter
-{
- float: right;
- clear: none;
- width: 18em;
- background-color: #EAEAEA;
- padding: 0.5em;
- margin: 0 0 1em 1em;
+.icon_not_held {
+background-image:url(/images/status-not-held.png);
+color:#A68C2E;
}
-img.twitter-icon {
- vertical-align: middle;
+.icon_successful,.icon_partially_successful {
+background-image:url(/images/status-complete.png);
+color:#69952F;
}
-.blog_post {
- margin-bottom: 2em;
-}
-
-/*-----------------------------list sidebars----------------*/
-#list_sidebar
-{
- width: 14em;
- float: right;
- font-size: 0.85em;
- border-color: #AEAEAE;
- border-width: 1px;
- border-style: solid;
- background-color: #EAEAEA;
- padding: 0.5em;
- margin: 0 0 0 1em;
-}
- #list_sidebar h1, #list_sidebar h2
- {
- font-size: 1.2em;
- line-height: 1em;
- margin: 0px 0px 0.3em 0px;
- }
- #list_sidebar ul
- {
- list-style: none;
- margin: 0px;
- padding: 0px;
- }
-
-/*-----------------------------request sidebars----------------*/
-
- #request_sidebar span.head
- {
- padding: 12px 0 0 0;
- font-weight: bold;
- font-size: 1.2em;
- padding: 8px 0 0 0px;
- background-image: none;
- }
-
- #request_sidebar .request_listing span.desc
- /* sidebar request list only */
- {
- float: left;
- clear: both;
- width: 16.9em;
- background-image: url(../images/navimg/quote-open-small.png);
- background-repeat: no-repeat;
- background-position: 0 0;
- padding: 2px 2px 1em 15px;
- }
-
- #request_sidebar span.bottomline
- {
- float: left;
- clear: both;
- width: 100%;
- margin: 0 0 0.6em 0;
- background-image: none;
- padding: 0px;
- }
-
-
-
-/*--------------------------------- content : full lists */
-
-.request_listing,
-.body_listing,
-.user_listing
-{
- font-size: 0.8em;
- margin-top: 1.5em;
- padding: 0 0 1.5em 0;
- border-bottom: #9C9C9C;
- border-width: 0 0 1px 0;
- border-style: none none solid none;
- overflow: hidden;
-}
- .body_listing
- {
- _width: 47em;
- }
-/*.body_listing,
-.user_listing
-{ float: left; clear: none; width: 50%;}*/
-
- .request_listing a,
- .body_listing a,
- .user_listing a
- { text-decoration: none; }
-
- span.head
- {
- display: block;
- font-size: 1.4em;
- padding: 12px 0 0 0;
- font-weight: bold;
- }
- .request_listing span.head /* full page request list only */
- {
- min-height: 32px;
- margin: 0 0 0.3em 0;
- padding: 8px 0 0 42px;
- background-image: url(../images/navimg/request-icon.png);
- background-repeat: no-repeat;
- background-position: 4px 0px;
- }
- .body_listing span.head /* full page request list only */
- {
- min-height: 32px;
- margin: 0 0 0.3em 0;
- padding: 8px 0 0 42px;
- background-image: url(../images/navimg/auth-icon.png);
- background-repeat: no-repeat;
- background-position: 4px 0px;
- }
- .user_listing span.head /* full page request list only */
- {
- min-height: 32px;
- margin: 0 0 0.3em 0;
- padding: 8px 0 0 42px;
- background-image: url(../images/navimg/user-icon.png);
- background-repeat: no-repeat;
- background-position: 4px 0px;
- }
- .user_listing span.no_icon /* full page request list only */
- {
- background-image: none;
- }
-
- span.bottomline
- {
- clear: left;
- display: block;
- padding: 10px 0 0 42px;
-
- }
- .request_listing span.bottomline
- /* full page request list only */
- {
- width: 35em;
- margin: 0 0 0.6em 0;
- background-repeat: no-repeat;
- background-position: left center;
- min-height: 42px;
- }
-
- span.bottomline a
- {}
-
- .request_icon_line
- {
- background-repeat: no-repeat;
- background-position: left center;
- min-height: 24px;
- padding: 8px 0 10px 42px;
- clear: left;
- }
-
- /* Waiting */
- .icon_waiting_response,
- .icon_waiting_classification,
- .icon_waiting_clarification
- { background-image: url(../images/navimg/status-icons-wait.png);}
- /* Failed */
- .icon_rejected
- { background-image: url(../images/navimg/status-icons-fail.png);}
- /* Not held */
- .icon_not_held
- { background-image: url(../images/navimg/status-icons-not-held.png);}
- /* Successful */
- .icon_successful,
- .icon_partially_successful
- { background-image: url(../images/navimg/status-icons-succeed.png);}
- /* Alert */
- .icon_requires_admin,
- .icon_waiting_response_overdue,
- .icon_waiting_response_very_overdue
- { background-image: url(../images/navimg/status-icons-attn.png);}
- /* Postal */
- .icon_gone_postal
- { background-image: url(../images/navimg/status-icons-post.png);}
- /* Error */
- .icon_error_message
- { background-image: url(../images/navimg/status-icons-error-message.png);}
- /* Interal review */
- .icon_internal_review
- { background-image: url(../images/navimg/status-icons-internal-review.png);}
- /* User withdrawn */
- .icon_user_withdrawn
- { background-image: url(../images/navimg/status-icons-user-withdrawn.png);}
-
- /* span.bottomline.icon_requires_admin,
- span.bottomline.icon_user_withdrawn, */
-
- span.desc
- {
- display: block;
- float: right;
- clear: none;
- font-style: italic;
- color: #3F3F3F;
- overflow: hidden;
- }
- .request_listing span.desc
- /* full page request list only */
- {
- width: 25em;
- background-image: url(../images/navimg/quote-open.png);
- background-repeat: no-repeat;
- background-position: 0 0;
- padding: 8px 0 0 25px;
- }
-
- .body_listing span.desc
- { background-image: none; float: left; clear: both; padding: 0px 0 0 42px;}
-
-/*--------------------------------- content : short lists */
-
-.request_short_listing
-{
- margin-top: 1em;
-}
-
-.request_short_listing h3
-{
- font-size: 1.2em;
-}
-.request_short_listing p
-{
- font-size: 0.8em;
- margin-top: -0.8em;
- margin-bottom: 0;
-}
-
-
-.request_short_listing a
-{ text-decoration: none; }
-
-.request_short_listing
-{
-}
-
-/*---------------- content : lists back/next */
-
-div.pagination { text-align: center; padding-top: 0.3em;}
-
- div.pagination span.current
- {
- padding: 0 0.6em 0.1em 0.6em;
- background-color: #000;
- color: #FFF;
- }
- span.disabled { color: #B2B2B2; padding: 0 0.6em 0.1em 0.6em; }
- div.pagination a, div.pagination a:visited
- {
- text-decoration: none;
- padding: 0 0.6em 0.1em 0.6em;
- }
- div.pagination a:hover
- {
- background-color: #626262;
- color: #FFF;
- }
-
-
-/*---------------- content : find authority (home page) */
-#make_requests
-{
- float: left;
- clear: none;
- width: 26em;
- text-align: center;
-}
-
-.auto_complete
-{
- text-align: left;
- background-color: #FFF;
- border-color: #454545 #282828 #000 #454545;
- border-width: 1px 1px 2px 1px;
- border-style: solid;
-}
-
- .auto_complete ul
- {
- list-style: none;
- margin: 0px;
- padding: 0px;
- }
- .auto_complete ul li
- {
- margin: 0px 0px 0.4em 0px;
- padding: 0.1em 1em 0.3em 1em;
- color: #595959;
- }
- .auto_complete ul li:hover
- {
- color: #000;
- background-color: #D7D7D7;
- cursor: default;
- }
-
-
-
-/*-------------------- Content : form errors */
-#error, .errorExplanation, #hidden_request
-{
- color: #FF0606;
- font-size: 1.4em;
- font-weight: bold;
- border-color: #FF0C11;
- border-width: 1px;
- border-style: solid;
- background-color: #ffeeee;
-}
-#error, #hidden_request {
- padding: 0.5em;
-}
-.fieldWithErrors
-{
- display: block;
- padding: 0.2em;
- border-color: #FF0C11;
- border-width: 1px;
- border-style: solid;
- background-color: #ffeeee;
-}
-
-/*-------------------- Content : action notice */
-#notice, .describe_state_form, .undescribed_requests, .gone_postal_help
-{
- color: #16C132;
- font-size: 1.4em;
- font-weight: bold;
- border-color: #1EFF38;
- border-width: 1px;
- border-style: solid;
- background-color: #D5FFD8;
- padding: 0.5em;
-}
-
-.form_explanation{
- font-size: 0.8em;
-}
-/* This doesn't seem to work, not sure why (if you turn it on, check all
- * flashes with <strong> in them work OK)
-#notice * strong
-{
- font-weight: bolder;
-} */
-.describe_state_form, .undescribed_requests, .gone_postal_help {
- font-weight: normal;
- margin-bottom: 1em;
- font-size: 1.0em;
- color: #454545;
- float: left;
- width: 39em;
-}
-.undescribed_requests {
- clear: both;
+.icon_requires_admin,.icon_waiting_response_overdue,.icon_waiting_response_very_overdue {
+background-image:url(/images/status-overdue.png);
+color:#C1272D;
+}
+
+.icon_gone_postal {
+background-image:url(/images/status-gone-postal.png);
+color:#A68C2E;
+}
+
+.icon_error_message {
+background-image:url(/images/status-error.png);
+color:#C1272D;
+}
+
+.icon_internal_review {
+background-image:url(/images/status-internal-review.png);
+color:#A68C2E;
+}
+
+.icon_user_withdrawn {
+background-image:url(/images/status-withdrawn.png);
+color:#A68C2E;
+}
+
+span.desc {
+display:block;
+float:right;
+clear:none;
+font-style:italic;
+color:#3F3F3F;
+overflow:hidden;
+}
+
+.request_listing span.desc
+/* full page request list only */ {
+width:25em;
+background-image:url(../images/navimg/quote-open.png);
+background-repeat:no-repeat;
+background-position:0 0;
+padding:8px 0 0 25px;
}
+
+.body_listing span.desc {
+background-image:none;
+float:left;
+clear:both;
+padding:0 0 0 42px;
+}
+
+.request_short_listing p {
+font-size:0.8em;
+margin-top:-0.8em;
+margin-bottom:0;
+}
+
+div.pagination {
+text-align:center;
+padding-top:0.3em;
+}
+
+div.pagination span.current {
+background-color:#000;
+color:#FFF;
+padding:0 0.6em 0.1em;
+}
+
+span.disabled {
+color:#B2B2B2;
+padding:0 0.6em 0.1em;
+}
+
+div.pagination a,div.pagination a:visited {
+text-decoration:none;
+padding:0 0.6em 0.1em;
+}
+
+div.pagination a:hover {
+background-color:#626262;
+color:#FFF;
+}
+
+#error,.errorExplanation,#hidden_request {
+color:#FF0606;
+font-size:1.4em;
+font-weight:700;
+background-color:#fee;
+border-color:#FF0C11;
+border-style:solid;
+border-width:1px;
+}
+
+#error,#hidden_request {
+padding:0.5em;
+}
+
+.fieldWithErrors {
+display:block;
+background-color:#fee;
+background:none;
+border:solid 0 #FFF;
+border-color:#FF0C11;
+border-style:solid;
+border-width:1px;
+padding:0.2em;
+}
+
+#notice,.describe_state_form,.undescribed_requests,.gone_postal_help {
+color:#16C132;
+font-size:1.4em;
+font-weight:700;
+background-color:#D5FFD8;
+border-color:#1EFF38;
+border-style:solid;
+border-width:1px;
+padding:0.5em;
+}
+
+.describe_state_form,.undescribed_requests,.gone_postal_help {
+font-weight:400;
+margin-bottom:1em;
+font-size:1em;
+color:#454545;
+float:left;
+width:39em;
+}
+
.requires_admin_details {
- margin-left: 1.8em;
- width: 37em;
-}
-.single_user {
- clear: left;
-}
-
-/*---------------- content : request detail pages */
-
-#request_sidebar
-{
- float: right;
- clear: none;
- width: 14em;
- margin: 0 0 0 0.6em;
- font-size: 0.9em;
-}
-#request_main, #show_response_view
-{
- width: 35em;
-}
-
-#preview_form p {clear: both;}
-
-div.correspondence
-{
- width: 40em;
- float: left;
- padding: 0em 0.5em 0em 0.5em;
- margin: 0 0 1em 0;
- border-color: #5F5F5F;
- border-width: 1px;
- border-style: solid;
- overflow: auto;
-}
-div.correspondence h2
-{ text-align: right; font-size: 1em; }
-
-.event_actions
-{ text-align: right}
-
-div.comment_in_request
-{
- width: 39em;
- float: left;
- padding: 0em 0.5em 0em 0.5em;
- margin: 0 0 1em 0;
- border-color: #5F5F5F;
- border-width: 1px;
- border-style: dotted;
- overflow: auto;
-}
-
-div#after_actions
-{
- float: left;
- margin-bottom: 1em;
- width: 39em;
-}
-
-div#anyone_actions
-{
- margin-bottom: 1em;
-}
-
-/* id starts with... */
-div[id|="outgoing"] { }
- div[id|="outgoing"] p { font-size: 0.95em;}
-
-div[id|="incoming"] { background-color: #E7E7E7; font-family: Times New Roman, Times, serif; }
- div[id|="incoming"] p { font-size: 1.08em;}
-
-div[id|="comment"] { }
- div[id|="comment"] { margin-left: 2em; width: 35em; }
- div[id|="comment"] p { font-size: 0.8em;}
- div[id|="comment"] h2 { font-size: 0.8em; text-align: left; margin-left: 1em; }
-.comment_quote { float: left; margin-right: 0.6em; }
-
-.correspondence_text { margin: 0 1.2em 0 0.8em; }
-.comment_in_request_text { margin: 0 1.2em 0 0.8em; }
-.preview_subject { margin: 1em 1.2em 0 0.8em; }
-
-/*.event_bubble { margin: 3em 0 0 0; font-size: 0.9em;}*/
-
-.attachments {
- border-color: #010101;
- border-width: 1px;
- margin: 0 0 1em 0;
-}
-.attachment {
- clear: both;
+margin-left:1.8em;
+width:37em;
+}
+
+#show_response_view {
+width:35em;
+}
+
+div.correspondence {
+width:40em;
+float:left;
+overflow:auto;
+border-color:#5F5F5F;
+border-style:solid;
+border-width:1px;
+margin:0 0 1em;
+padding:0 0.5em;
+}
+
+div.correspondence h2 {
+text-align:right;
+font-size:1em;
+}
+
+.event_actions {
+text-align:right;
+}
+
+div.comment_in_request {
+float:left;
+overflow:auto;
+width:550px;
+border-color:#5F5F5F;
+border-style:dotted;
+border-width:1px;
+margin:0 0 1em 50px;
+padding:0 0.5em;
+}
+
+div#after_actions {
+float:left;
+margin-bottom:1em;
+width:39em;
+}
+
+div#anyone_actions {
+margin-bottom:1em;
+}
+
+div[id|="outgoing"] p {
+}
+
+div[id|="incoming"] {
+background-color:#DEDEDE;
+}
+
+div[id|="incoming"] p {
+}
+
+div[id|="comment"] {
+width:35em;
+margin-left:50px;
+}
+
+div[id|="comment"] h2 {
+margin-left:1em;
+font-size:1em;
+text-align:right;
+}
+
+.comment_quote {
+float:left;
+margin-right:0.6em;
+}
+
+.preview_subject {
+margin:1em 1.2em 0 0.8em;
+}
+
+.attachments {
+border-color:#010101;
+border-width:1px;
+margin:0 0 1em;
}
+
a img.attachment_image {
- float: left;
- border: 0px;
- vertical-align: middle;
- margin: 0 0.2em 0.2em 0;
+float:left;
+border:0;
+vertical-align:middle;
+margin:0 0.2em 0.2em 0;
}
+
.attachments hr.top {
- margin: 0 0 1em 0;
- clear: both;
+clear:both;
+margin:0 0 1em;
}
+
.attachments hr.bottom {
- margin: 1em 0 0 0;
- clear: both;
+clear:both;
+margin:1em 0 0;
}
-#followup
-{ clear: both; float: left; }
-
-/*------------------------------------------------ view attachment as HTML */
+#followup {
+clear:both;
+float:left;
+}
-.view-html-content {
- margin-left: 1em;
- margin-right: 1em;
+#view-html-content {
+margin-left:1em;
+margin-right:1em;
}
-.view-html-content, img {
- max-width: 50em;
+
+#view-html-content img {
+max-width:50em;
}
.view_html_prefix {
- text-align: left;
- background-color: #E7E7E7;
- border-bottom: 1px solid #5f5f5f;
- padding: 0.5em 1em 0.5em 1em;
- height: 6%;
+text-align:left;
+background-color:#E7E7E7;
+border-bottom:1px solid #5f5f5f;
+height:6%;
+padding:0.5em 1em;
}
+
.view_html_logo {
- float: left;
- margin-right: 1em;
+float:left;
+margin-right:1em;
}
+
.view_html_logo img {
- border: 0;
+border:0;
}
+
.view_html_download_link {
- float: right;
- margin-left: 1em;
+float:right;
+margin-left:1em;
+}
+
+#authority_selection {
+float:left;
+width:40%;
}
-/*------------------------------------------------ authority listings */
-#body_sidebar {
- font-size: 0.85em;
- width: 16em;
- float: right;
- clear:none;
+#authority_search_ahead_results {
+width:26em;
}
-#body_sidebar a { text-decoration: none; }
+#authority_preview {
+width:45%;
+float:right;
+background-color:#FFFFE0;
+padding-left:1em;
+padding-right:1em;
+overflow:hidden;
+margin-top:-67px;
+}
+
+#authority_preview #header_left,#authority_preview.request_left,#authority_preview #stepwise_make_request {
+width:95%;
+}
-/*------------------------------------------------ selecting an authority */
+#request_advice {
+float:right;
+width:250px;
+margin-top:1em;
+}
-#authority_selection
-{
- float: left;
- width: 40%;
+#request_advice ul {
+margin:0 auto;
}
-#authority_search_ahead_results
-{
- width: 26em;
+#request_advice ul li {
+margin:0 0 1em;
}
-#authority_preview
-{
- width: 45%;
- float: right;
- background-color: #FFFFE0;
- padding-left: 1em;
- padding-right: 1em;
- overflow: hidden;
- margin-top: -67px;
+#request_header {
+background-color:#FFFFE0;
+padding-top:0.5em;
+padding-bottom:1em;
}
- #authority_preview #header_left,
- #authority_preview.request_left,
- #authority_preview #stepwise_make_request
- {
- width: 95%;
- }
+#request_form label,label.form_label {
+display:block;
+float:left;
+clear:none;
+width:100px;
+text-align:left;
+margin:2px 0 0;
+padding:0 10px 0 0;
+}
-/*------------------------------------------------ making a request / sign up / sign in */
+.form_item_note,.form_note {
+width:34em;
+margin-left:110px;
+font-size:1em;
+}
-#request_advice
-{
- float: right;
- width: 250px;
- margin-top: 1em;
+.form_item_note {
+margin-top:-1em;
}
- #request_advice ul
- {
- margin: 0 auto 0 auto;
- }
- #request_advice ul li { margin: 0 0 1em 0; }
+.form_button {
+margin:0 0 0 9em;
+}
+
+p#sign_in_reason {
+text-align:center;
+font-size:1.4em;
+font-weight:700;
+line-height:1em;
+}
+
+#signup,#signin {
+clear:none;
+margin-bottom:1em;
+float:none;
+margin-top:20px;
+width:auto;
+}
-#request_header
-{
- background-color: #FFFFE0;
- padding-top: 0.5em;
- padding-bottom: 1em;
+#signup h2,#signin h2 {
+font-size:1.1em;
}
-#request_header_text
-{
- font-size: 0.8em;
- margin-left: 11em;
+#signup {
+float:right;
}
-#request_search_ahead_results
-{
- font-size: 0.8em;
- margin-left: 11em;
+#sign_alone #signin {
+margin-left:25%;
}
-#request_form
-{ margin-top: 1em;}
+#signup .form_item_note,#signin .form_note {
+font-size:0.8em;
+margin-left:11.5em;
+width:24em;
+}
-#request_form label,
-label.form_label
-{
- display: block;
- /*width: 128px;*/ width: 8em;
- float: left;
- clear: none;
- text-align: right;
- padding: 0 10px 0 0;
- margin: 0 0 0 0;
+div.controller_help dt a,div.controller_help h1 a,div#help_unhappy h1 a.hover_a {
+text-decoration:none;
+font-size:0.8em;
+color:#fff;
+background-color:#fff;
}
-.form_item_note, .form_note
-{
- font-size: 0.8em;
- /*width: 432px;*/ width: 34em;
- /*margin-left: 138px;*/ margin-left: 11em;
+div.controller_help dt:hover > a,div.controller_help h1:hover > a,div#help_unhappy h1:hover > a.hover_a {
+color:#777;
}
-.form_item_note
-{
- margin-top: -1.0em;
+
+#hash_link_padding {
+margin-bottom:10em;
}
+#contact_preamble {
+width:auto;
+margin:0 0 30px;
+}
-.form_button
-{
- margin: 0 0 0 9em;
+div.feed_link_main {
+display:inline;
}
-p#sign_in_reason
-{
- text-align: center;
- font-size: 1.4em;
- font-weight: bold;
- line-height: 1em;
+#footer {
+position:relative;
+clear:both;
+float:left;
+width:100%;
+height:2em;
+font-size:0.85em;
+background-color:#F0F0F0;
+border-color:#FFF;
+border-style:solid;
+border-width:3px 0 0;
+margin:60px 0 0;
+padding:0.5em 0;
}
-#signup,
-#signin
-{
- clear: none;
- width: 23em;
- margin-bottom: 1em;
+#everypage {
+background-color:#fc9;
+border:solid 2px #f60;
+border-top:none;
+opacity:0.97px;
+-moz-border-radius-bottomleft:10px;
+-moz-border-radius-bottomright:10px;
+position:fixed;
+width:70%;
+left:15%;
+z-index:200;
+max-height:95%;
+overflow:auto;
+padding:4px;
+}
+
+#everypage h2,#everypage h3 {
+margin:0.5em 0;
+}
+
+#game_sidebar {
+float:right;
+clear:none;
+width:20em;
+font-size:0.8em;
+margin:0 0 2em 2em;
+}
+
+#user_photo_on_profile img,#user_photo_on_profile #set_photo {
+width:96px;
+height:96px;
+float:left;
+vertical-align:middle;
+text-align:center;
+border:1px solid #ddd;
+margin-right:5px;
+padding:2px;
+}
+
+.user_photo_on_request img {
+width:48px;
+height:48px;
+float:left;
+vertical-align:middle;
+border:1px solid #ddd;
+margin-right:5px;
+padding:2px;
+}
+
+.user_photo_on_comment img {
+width:36px;
+height:36px;
+float:left;
+vertical-align:middle;
+border:1px solid #ddd;
+margin-right:5px;
+margin-top:5px;
+padding:2px;
+}
+
+.user_photo_on_search img {
+width:48px;
+height:48px;
+vertical-align:middle;
+border:1px solid #ddd;
+margin-right:5px;
+padding:2px;
+}
+
+div.user_about_me {
+overflow:auto;
+margin:1em 1.5em;
+padding:0 0.5em;
+}
+
+#user_public_banned {
+background-color:#d0d0d0;
+margin:0 14em 0 0;
+padding:0.5em 1em;
+}
+
+#user_public_banned .details {
+margin-left:4em;
+margin-right:4em;
+font-size:0.8em;
+font-style:italic;
}
- #signup h2,
- #signin h2
- { font-size: 1.1em; }
- #signup
- {
- float: right;
- }
+div.lang {
+text-align:right;
+font-size:0.8em;
+right:0;
+z-index:200;
+top:40px;
+position:absolute;
+padding:0;
+}
- #signin
- {
- float: left;
- }
+div#user_locale_switcher {
+margin:5px;
+}
-#sign_alone #signin {
- margin-left: 25%;
+#topnav li,#signin,.user_photo_on_search {
+float:left;
}
-
-#signup .form_item_note,
-#signin .form_note
-{
- font-size: 0.8em;
- width: 16em;
- margin-left: 11em;
+
+#view-html-content table,#request_details table {
+border-collapse:collapse;
+margin-bottom:1em;
}
-/*--------------------------------- content : about pages */
+#view-html-content td,th,#request_details td,th {
+border:solid 1px #000;
+}
-div#about_sidebar
-{
- float: right;
- clear: none;
- width: 14em;
- background-color: #EAEAEA;
- padding: 0.5em;
- margin: 0 0 0 1em;
+#view-html-content td,#request_details td {
+vertical-align:top;
+max-width:30em;
+overflow:auto;
+}
- border-color: #AEAEAE;
- border-width: 1px;
- border-style: solid;
+#view-html-content tr:nth-child(odd),#request_details tr.odd {
+background-color:#bbb;
}
-div.controller_help dt a, div.controller_help h1 a, div#help_unhappy h1 a.hover_a
-{
- text-decoration: none;
- font-size: 0.80em;
- color: #fff;
- background-color: #fff;
+#view-html-content tr:nth-child(even),#request_details tr.even {
+background-color:#ddd;
}
-div.controller_help dt:hover > a, div.controller_help h1:hover > a, div#help_unhappy h1:hover > a.hover_a {
- color: #0000ee;
+
+h3,.request_short_listing h3 {
+font-size:1.2em;
}
-div.controller_help dt:hover > a:hover, div.controller_help h1:hover > a:hover, div#help_unhappy h1:hover > a.hover_a:hover {
- text-decoration: underline;
+
+#frontpage_examples p,#frontpage_examples h2 {
+text-align:center;
+clear:both;
}
-#hash_link_padding
-{
- margin-bottom: 10em;
+.request_short_listing,#request_form {
+margin-top:1em;
}
-#contact_preamble
-{
- /*width: 224px;*/ width: 33em;
- /*margin-left: 142px;*/ margin-left: 8em;
+.form_explanation,div[id|="comment"] p {
+font-size:0.8em;
}
+.undescribed_requests,#preview_form p,.attachment {
+clear:both;
+}
+.single_user,#user_change_password_email,#user_not_logged_in {
+clear:left;
+}
-/*---------------------------------- RSS and email alerts */
+.correspondence_text,.comment_in_request_text {
+margin:0 1.2em 0 0.8em;
+}
-div.feed_link img {
- border: none;
- vertical-align: middle;
- text-decoration: none;
+#request_header_text,#request_search_ahead_results {
+font-size:0.8em;
+margin-left:11em;
}
-div.feed_link_main {
- display: inline;
+
+div.feed_link img,div.act_link img {
+border:none;
+vertical-align:middle;
+text-decoration:none;
+}
+
+h2,dt {
+font-size:21px;
}
-form.feed_form_main {
- display: inline;
+
+h3 {
+text-decoration:none;
+font-size:20px;
+margin-top:3px;
+margin-bottom:10px;
}
-div.act_link img {
- border: none;
- vertical-align: middle;
- text-decoration: none;
+a {
+text-decoration:underline;
}
-/*------------------------------------------------ footer */
+#banner_inner {
+width:890px;
+position:relative;
+margin:auto;
+}
-#footer
-{
- position: relative;
- clear: both;
- float: left;
- width: 100%;
- height: 2em;
- margin: 0px 0 0px 0;
- padding: 0.5em 0 0.5em 0;
- font-size: 0.85em;
- background-color: #F0F0F0;
- border-color: #993233;
- border-width: 3px 0 0 0;
- border-style: solid;
+#banner_inner a#logo {
+position:absolute;
+left:0;
+top:70px;
+z-index:100;
}
-/*------------------------------------------------ interstitial advert */
+a img {
+border:none;
+}
-#everypage {
- background-color: #ffcc99;
- border: solid 2px #ff6600; border-top: none; /* No top border, so looks like coming out of top */
- opacity: 0.97;
- -moz-border-radius-bottomleft: 10px; -moz-border-radius-bottomright: 10px; /* Rounded bottom corners */
- padding: 4px;
- position: fixed;
- width: 70%; left: 15%; /* So centered on screen, always visible */
- z-index: 200; /* So on top */
- max-height: 95%; overflow: auto; /* So on e.g. 800x600 scrollbar appears */
+#navigation_search input[type=image] {
+border:0;
+margin-bottom:-9px;
+margin-left:-4px;
+}
+
+#navigation_search input[type=text] {
+font-size:12px;
+border-radius:5px 0 0 5px;
+-moz-border-radius:5px 0 0 5px;
+border-color:#222;
+padding:5px 5px 4px;
+}
+
+#topnav ul li {
+margin:0 3px;
+padding:10px 15px;
+}
+
+#topnav ul li.selected {
+background:#FFF;
}
-#everypage h2, #everypage h3, #foi2009 h2, #foi2009 h3 {
- margin: 0.5em 0;
+
+.request_right {
+padding-top:5px;
+width:245px;
+float:left;
+}
+
+#request_header_text {
+margin-left:110px;
}
+#stepwise_make_request a img {
+margin-bottom:-10px;
+margin-top:-10px;
+margin-left:6px;
+}
-/*------------------------------------------------ request categorisation game */
+p.subtitle {
+margin-top:10px;
+margin-bottom:20px;
+font-size:18px;
+font-style:normal;
+color:#222;
+}
-#game_buttons {
+.results_section {
+margin-bottom:40px;
}
-#game_sidebar
-{
- float: right;
- clear: none;
- width: 20em;
- margin: 0 0 2em 2em;
- font-size: 0.8em;
+.results_section div:last-child {
+border-bottom-width:0;
+padding-bottom:0;
}
-/*------------------------------------------------ request details */
+.request_listing,.user_listing,.body_listing {
+border-bottom:1px solid #DDD;
+margin:0 0 -1px;
+padding:12px 0 6px;
+}
-#request_details table {
- border-collapse: collapse;
- margin-bottom: 1em;
+.request_listing span.head,.user_listing span.head,.body_listing span.head {
+background:none;
+font-size:21px;
+margin-bottom:6px;
+padding:0;
}
-#request_details td, th {
- border: solid 1px #000000;
+
+.request_listing span.head a,.user_listing span.head a,.body_listing span.head a {
+text-decoration:none;
+font-size:20px;
+margin-top:3px;
+display:block;
+margin-bottom:-6px;
+}
+
+.request_listing .requester {
+font-size:12px;
+padding-bottom:0;
+}
+
+.body_listing span.desc,.body_listing span.bottomline,.user_listing span.bottomline {
+font-style:normal;
+font-size:12px;
+font-weight:400;
+margin:0;
+padding:0;
}
-#request_details td {
- vertical-align: top
+
+.request_listing span.bottomline {
+font-style:normal;
+margin-bottom:0;
+margin-top:12px;
+background-position:top left;
+font-size:14px;
+font-weight:400;
+min-height:36px;
+padding:3px 0 0 27px;
}
-#request_details td {
- max-width: 30em;
- overflow: auto;
+
+.user_listing {
+padding-top:10px;
+padding-bottom:0;
}
-#request_details tr.odd {
- background-color: #bbbbbb;
+
+.icon_failed,.icon_rejected {
+background-image:url(/images/status-denied.png);
+color:#C1272D;
}
-#request_details tr.even {
- background-color: #dddddd;
+
+#request_sidebar {
+width:212px;
+font-size:12px;
}
+.feed_link {
+padding:4px 0;
+}
-/*------------------------------------------------ users */
+.request_listing span.desc {
+background:url(/images/quote-marks.png) no-repeat;
+min-height:60px;
+font-size:12px;
+width:auto;
+color:#444;
+line-height:18px;
+padding:0 0 0 40px;
+}
-#user_photo_on_profile {
+#search_form {
+margin:0 -6px 20px 0;
}
-#user_photo_on_profile img, #user_photo_on_profile #set_photo {
- width: 96px;
- height: 96px;
- float: left;
- vertical-align: middle;
- text-align: center;
- border: 1px solid #dddddd;
- margin-right: 5px;
- padding: 2px;
+#advanced-search input[type=text] {
+width:auto;
}
-.user_photo_on_request img {
- width: 48px;
- height: 48px;
- float: left;
- vertical-align: middle;
- border: 1px solid #dddddd;
- margin-right: 5px;
- padding: 2px;
+#search_form input[type=submit] {
+border-radius:0 2px 2px 0;
+-moz-border-radius:0 2px 2px 0;
}
-.user_photo_on_comment img {
- width: 36px;
- height: 36px;
- float: left;
- vertical-align: middle;
- border: 1px solid #dddddd;
- margin-right: 5px;
- margin-top: 5px;
- padding: 2px;
+#header_right {
+float:right;
+width:230px;
+padding-top:27px;
}
-.user_photo_on_search {
- float: left;
+.feed_link,.act_link {
+display:block !important;
+margin-bottom:10px;
}
-.user_photo_on_search img {
- width: 48px;
- height: 48px;
- vertical-align: middle;
- border: 1px solid #dddddd;
- margin-right: 5px;
- padding: 2px;
+
+.feed_link a,.act_link a,#header_right > a {
+text-decoration:none;
}
-#user_change_password_email, #user_not_logged_in {
- clear: left;
+.feed_link a img,.act_link img,.act_link a img {
+padding-right:2px;
}
-div.user_about_me
-{
- padding: 0em 0.5em 0em 0.5em;
- margin: 1em 1.5em 1em 1.5em;
- overflow: auto;
+form.feed_form input[type="submit"] {
+font-size:12px;
+line-height:12px;
+padding:2px 4px;
}
-#user_public_banned
-{
- margin: 0em 14em 0em 0em;
- padding: 0.5em 1em 0.5em 1em;
- background-color: #d0d0d0;
+#header_right > br {
+line-height:200%;
}
-#user_public_banned .details
-{
- margin-left: 4em;
- margin-right: 4em;
- font-size: 0.8em;
- font-style: italic;
+
+#general_search h2 {
+clear:both;
+margin-top:20px;
}
-div.lang {
- position: absolute;
- text-align: right;
- font-size: 0.8em;
- top: 45px;
- right: 0px;
- padding-right: 5px;
- z-index: 200;
+h2.foi_results,h2.person_results,h2.publicbody_results {
+padding-top:0;
+padding-bottom:15px;
+margin-bottom:0;
+margin-top:0;
}
-div#user_locale_switcher {
- margin: 5px;
+h2.foi_results {
+width:600px;
+}
+
+.list_toggle_controls {
+padding-bottom:20px;
+}
+
+#request_advice ol {
+margin-left:18px;
+margin-top:20px;
+display:block;
+padding:0;
+}
+
+#request_advice ol li {
+padding-bottom:5px;
+}
+
+#request_form label,label.form_label,span#to_public_body {
+font-size:18px;
+}
+
+#date_range label,#filter_requests_form label {
+display:inline;
+float:none;
+padding-right:5px;
+}
+
+#date_range label.title,#filter_requests_form label.title,h3.title {
+display:inline-block;
+float:none;
+width:110px;
+}
+
+h3.title {
+width:114px;
+margin-bottom:5px;
+}
+
+#requests-subfilters div {
+margin-top:10px;
+}
+
+#requests-subfilters #latest_status_0,#requests-subfilters #request_variety_0 {
+margin-left:0;
+}
+
+#requests-subfilters input[type=checkbox] {
+margin-left:117px;
+}
+
+span#to_public_body {
+}
+
+#left_column {
+width:600px;
+float:left;
+}
+
+#right_column_flip {
+width:220px;
+float:left;
+margin-top:20px;
+}
+
+#left_column_flip {
+width:620px;
+float:right;
+margin-top:10px;
+}
+
+#right_column {
+width:220px;
+float:right;
+margin-top:30px;
+}
+
+#left_half {
+width:45%;
+float:left;
+}
+
+#right_half {
+width:45%;
+float:right;
+}
+
+#middle_strip {
+float:left;
+width:10%;
+height:100px;
+text-align:center;
+margin-top:45px;
+font-size:16px;
+font-family:Georgia;
+font-style:italic;
+color:#444;
+}
+
+#sign_together h1 {
+width:320px;
+text-align:center;
+}
+
+#sign_together .form_button {
+margin-left:10.5em;
+}
+
+form input[type=text],form input[type=password] {
+font-size:14px;
+width:200px;
+color:#555;
+border-radius:3px;
+-moz-border-radius:3px;
+border-color:#BBB;
+border-style:solid;
+border-width:1px;
+padding:5px;
+}
+
+form input.use-datepicker[type=text] {
+width:130px !important;
+background:url(/images/calendar.png) no-repeat 115px 3px;
+border-radius:3px !important;
+-moz-border-radius:3px !important;
+font-size:14px !important;
+margin:0 !important;
+}
+
+form input[type=submit],a.link_button_green,a.link_button_green_large {
+background:url(/images/button-gradient.png);
+color:#FFF;
+text-decoration:none;
+display:inline-block;
+line-height:18px;
+border:solid 1px #69952F;
+border-radius:2px;
+-moz-border-radius:2px;
+text-shadow:1px 1px 0 #5B841D;
+font-size:18px;
+cursor:hand;
+padding:5px 11px;
+}
+
+a.link_button_green_large {
+background:url(/images/button-gradient-large.png);
+font-size:22px;
+line-height:22px;
+padding-bottom:7px;
+}
+
+form input[type=submit].small {
+font-size:15px;
+line-height:10px;
+padding:4px 9px;
+}
+
+ul.no_bullets {
+list-style-type:none;
+margin:0 0 30px;
+padding:0;
+}
+
+ul.no_bullets li {
+margin-bottom:6px;
+}
+
+#frontpage_search {
+background:transparent;
+}
+
+#public_body_list #right_column ul {
+list-style:none;
+margin:0;
+padding:0 0 20px;
+}
+
+div.blog_post p {
+line-height:180%;
+}
+
+div.frontpage-box {
+background:url(/images/stripes.png);
+border:1px solid #DEBEDD;
+border-radius:5px;
+-moz-border-radius:5px;
+color:#DDD;
+font-size:18px;
+text-align:center;
+width:255px;
+height:210px;
+float:left;
+padding:15px 12px 0;
+}
+
+#frontpage-box-1 {
+margin-right:10px;
+vertical-align:middle;
+}
+
+#frontpage-box-2 {
+margin-right:10px;
+}
+
+#frontpage-box-3 #search_form input[type=text] {
+width:10em;
+}
+
+#frontpage_examples p,#frontpage_examples ul {
+text-align:left;
+}
+
+#examples_1 ul li {
+border-bottom:1px solid #ddd;
+padding:5px 0;
+}
+
+#frontpage_examples .excerpt {
+cursor:pointer;
+background:url(/images/quote-marks.png) no-repeat;
+font-size:12px;
+color:#444;
+line-height:18px;
+min-height:30px;
+font-style:italic;
+padding:0 0 0 40px;
+}
+
+#set_photo {
+background:url(/images/defaultprofilepic.png);
+}
+
+div.correspondence,div.comment_in_request {
+width:600px;
+font-size:13px;
+border-radius:6px;
+-moz-border-radius:6px;
+border-width:0;
+padding:4px 20px 0 9px;
+}
+
+div.outgoing.correspondence {
+ background: #EFEFEF;
+}
+
+div[id|="comment"] p {
+font-size:13px;
+}
+
+.comment_in_request_text {
+margin:0 0 0 10px;
+}
+
+#frontpage_splash {
+background:url(/images/flying-computer.png) no-repeat 175px bottom;
+height:375px;
+margin-top:-12px;
+margin-bottom:20px;
+width:100%;
+}
+
+#frontpage_splash #left_column {
+line-height:40px;
+margin-top:66px;
+}
+
+#frontpage_splash h1 {
+font-size:39px;
+color:#222;
+font-weight:400;
+margin:0 0 20px;
+}
+
+#frontpage_splash h1 strong {
+font-size:54px;
+color:#222;
+font-weight:400;
+}
+
+#frontpage_splash h1 span {
+font-family:Georgia;
+font-style:italic;
+font-weight:400;
+font-size:25px;
+color:#444;
+}
+
+#frontpage_splash h2 {
+font-size:26px;
+font-weight:400;
+color:#222;
+margin-bottom:10px;
+line-height:28px;
+}
+
+#frontpage_splash h2 strong {
+font-size:31px;
+color:#222;
+}
+
+#frontpage_splash h2 span {
+color:#333;
+font-style:italic;
+font-size:19px;
+font-family:Georgia;
+}
+
+#frontpage_splash #right_column {
+width:265px;
+}
+
+#frontpage_splash #right_column input[type=text] {
+width:180px;
+}
+
+#frontpage_splash #frontpage_search_box {
+margin-bottom:30px;
+margin-top:-10px;
+}
+
+#frontpage_splash #frontpage_right_to_know p {
+line-height:20px;
+}
+
+body.front h3 {
+font-size:28px;
+}
+
+#ui-datepicker-div.ui-widget {
+font-family:Arial, sans-serif;
+color:#93278F;
+}
+
+#ui-datepicker-div .ui-datepicker-header,#ui-datepicker-div .ui-widget-header {
+background:none;
+border:solid 0 #FFF;
+color:#444;
+font-size:17px;
+font-weight:400;
+line-height:1.5em !important;
+}
+
+#ui-datepicker-div .ui-state-default {
+background:#F2F2F2;
+border:solid 0 #FFF;
+border-radius:2px;
+-moz-border-radius:2px;
+}
+
+#ui-datepicker-div .ui-state-default:hover {
+background:#222;
+color:#FFF;
+}
+
+#ui-datepicker-div .ui-state-active {
+background:#222;
+color:#FFF;
+}
+
+#ui-datepicker-div .ui-icon-circle-triangle-w,#ui-datepicker-div .ui-icon-circle-triangle-e {
+background-image:url(/images/ui-icons-theme.png);
+}
+
+#ui-datepicker-div .ui-datepicker-prev-hover {
+left:2px;
+top:2px;
+border:none;
+background:#FFF;
+cursor:pointer;
+opacity:1px;
+}
+
+#ui-datepicker-div .ui-datepicker-next-hover {
+right:2px;
+top:2px;
+border:none;
+background:#FFF;
+cursor:pointer;
+opacity:1px;
+}
+
+#other-country-notice {
+background:#222;
+color:#FFF;
+font-size:16px;
+width:100%;
+z-index:999;
+display:block;
+position:absolute;
+top:0;
+opacity:0.9px;
+}
+
+p.public-body-name-prefix {
+color:#888;
+margin-top:15px;
+margin-bottom:-15px;
+}
+
+#other-country-notice a {
+color:#FFF;
+}
+
+.close-button {
+color:#FFF;
+text-decoration:none;
+display:inline-block;
+border-radius:2px;
+-moz-border-radius:2px;
+cursor:hand;
+background:url(/images/small-white-cross.png) no-repeat;
+width:15px;
+height:15px;
+border:solid 0 #FFF;
+text-indent:-999px;
+overflow:hidden;
+float:right;
+padding:10px 0;
+}
+
+#link_box {
+position:absolute;
+text-align:left;
+background-color:#FFF;
+z-index:999;
+opacity:0.9px;
+border-radius:6px;
+-moz-border-radius:6px;
+border:1px solid #444;
+display:none;
+padding:5px;
+}
+
+#link_box .close-button {
+background-color:#444;
+margin-left:15px;
+padding:0;
+}
+
+a.link_to_this {
+display:inline-block;
+width:20px;
+letter-spacing:-1000em;
+overflow:hidden;
+background:url(/images/link-icon.png) no-repeat;
+}
+
+#to_public_body {
+display:block;
+margin-bottom:15px;
+}
+
+.fieldWithErrors textarea,.fieldWithErrors input {
+border:solid 1px Red !important;
+}
+
+.errorExplanation {
+border-radius:6px;
+-moz-border-radius:6px;
+font-size:12px;
+font-weight:400;
+width:554px;
+margin:20px 0 30px;
+}
+
+#notice,.describe_state_form,#other_recipients {
+font-size:12px;
+font-weight:400;
+background:#E9FDD3 !important;
+color:#517704;
+border-radius:6px;
+-moz-border-radius:6px;
+border-color:#B0CA86;
+margin:15px 0;
+padding:10px 20px;
+}
+
+.describe_state_form hr {
+border-top:0;
+border-color:#B0CA86;
+border-style:dotted;
+margin:20px 0;
+}
+
+.describe_state_form {
+color:#333;
+}
+
+#notice p:first-child {
+margin-top:0;
+}
+
+#notice p:last-child {
+margin-bottom:0;
+}
+
+div.correspondence p.preview_subject {
+font-size:18px !important;
+margin-left:10px;
+line-height:25px;
+}
+
+div.correspondence p.preview_subject strong {
+}
+
+#preview_form ul {
+margin:0;
+padding:1px 32px 10px;
+}
+
+#preview_form ul li {
+margin:10px 0;
+}
+
+div.controller_help h1 a,#logged_in_bar a,#logged_in_bar a:visited,#stepwise_make_request strong {
}
+
+.request_left,#header_left {
+width:625px;
+float:left;
+}
+
+#request_sidebar h2,.list-filter-item {
+margin-bottom:10px;
+}
+
+div.ff-icon-printfix,.comment_in_request_text img.comment_quote,body.front #other-country-notice,#other-country-notice,#authority_preview .public-body-name-prefix,#authority_preview #list-filter,#authority_preview h2.foi_results,div#show_response_view p.event_actions {
+display:none;
+}
+
+#ui-datepicker-div .ui-datepicker-prev,#ui-datepicker-div .ui-datepicker-next {
+margin-top:2px;
+opacity:0.5px;
+}
+
+div.controller_help dt:hover > a:hover,div.controller_help h1:hover > a:hover,div#help_unhappy h1:hover > a.hover_a:hover,h2 a:hover,.request_listing span.head a:hover,.user_listing span.head a:hover,.body_listing span.head a:hover,.request_listing .requester a,.feed_link a:hover,.act_link a:hover,#header_right > a:hover {
+text-decoration:underline;
+}
+
+.request_listing a,.body_listing a,.user_listing a,.request_short_listing a,h2 a,.feed_link a {
+text-decoration:none;
+} \ No newline at end of file
diff --git a/public/stylesheets/print.css b/public/stylesheets/print.css
index 02e0e98c0..43d7a1807 100644
--- a/public/stylesheets/print.css
+++ b/public/stylesheets/print.css
@@ -1,4 +1,4 @@
-div#content, div#left_column, div.entirebodym div#wrapper {
+div#content, div#left_column, div.entirebody div#wrapper {
width: 100%;
margin: 0;
float: none;
@@ -35,4 +35,8 @@ p#request_status {
}
div.correspondence {
page-break-before: avoid;
+}
+
+#other-country-notice {
+ display: none;
} \ No newline at end of file
diff --git a/public/stylesheets/theme.css b/public/stylesheets/theme.css
deleted file mode 100644
index d00ccdf3d..000000000
--- a/public/stylesheets/theme.css
+++ /dev/null
@@ -1,1141 +0,0 @@
-
-h1 {
- color: #93278F;
- font-size: 42px;
- font-family: 'DeliciousBold', Arial, sans-serif;
- margin-bottom:15px;
- margin-top:10px;
-}
-
-h2 a {
- text-decoration: none;
-}
-
-h2 a:hover {
- text-decoration: underline;
-}
-
-h2, dt {
- color: #93278F;
- font-size: 21px;
- font-family: 'DeliciousBold', Arial, sans-serif;
- font-weight:normal;
-}
-
-h3 {
- color: #6B3C6A;
- font-family: 'DeliciousRoman', Arial, sans-serif;
- font-weight: normal;
- text-decoration: none;
- font-size: 20px;
- margin-top: 3px;
- margin-bottom:10px;
-}
-
-dd {
- margin:18px 0px 36px 0px;
- width:auto;
-}
-
-dl {
- margin-top:24px;
- line-height:160%;
-}
-
-div.controller_help dt:hover > a, div.controller_help h1:hover > a, div#help_unhappy h1:hover > a.hover_a {
- color: #777;
- font-family:Arial, sans-serif;
-}
-
-a {
- color: #93278F;
- text-decoration: underline;
-}
-
-body {
- font-family: Arial, sans-serif;
- color: #444444;
- font-size: 12px;
-}
-
-body.front {
- background: url(/images/home-grad.png) repeat-x 0px 160px;
-}
-
-div.controller_help h1 a
-{
- color: #93278F
-}
-
-#wrapper {
- padding-top:160px;
-}
-
-#banner {
- background: url(/images/stripes.png);
- border: none;
- height:160px;
-}
-
-#banner_inner {
- width: 890px;
- margin: auto;
- position:relative;
-}
-
-#banner_inner a#logo {
- position:absolute;
- left:0px;
- top:70px;
- z-index: 100;
-}
-
-a img {
- border: none
-}
-
-#navigation_search {
- width:auto;
- right:0px;
- top:10px;
-}
-
-#navigation_search input[type=image] {
- border: 0px;
- margin-bottom: -9px;
- margin-left: -4px;
-}
-
-#navigation_search input[type=text] {
- font-size: 12px;
- padding:5px 5px 4px 5px;
- border-color:#BE7DBC;
- border-radius:5px 0px 0px 5px;
- -moz-border-radius:5px 0px 0px 5px;
-}
-
-#logged_in_bar {
- top: 18px;
- right:210px;
- float:none;
- position:absolute;
-}
-
-#logged_in_bar a,
-#logged_in_bar a:visited {
- color: #93278F;
-}
-
-div.lang {
- top:40px;
- position:absolute;
- padding:0px;
-}
-
-#topnav {
- background: transparent;
- top: 120px;
- margin-left: 115px;
- width: auto;
- font-family:'DeliciousRoman', Arial, sans-serif;
- font-size: 18px;
-}
-
-#topnav ul li a {
- color: #6B3C6A !important;
-}
-
-#topnav ul li {
- padding:10px 15px;
- margin: 0px 3px;
-}
-
-#topnav ul li.selected {
- background:#FFF;
-}
-
-#topnav li a, #topnav li a:visited {
- padding:0px;
-}
-
-.request_left {
- width: 625px;
- float:left;
-}
-
-.request_right {
- padding-top:5px;
- width: 245px;
- float:left;
-}
-
-#request_header_text {
- margin-left: 110px;
-}
-
-#wrapper {
- width: 900px;
-}
-
-#content {
- width: 875px;
-}
-
-#stepwise_make_request {
- background: url(/images/stripes.png);
- border: 1px solid #DEBEDD;
- border-radius:5px;
- -moz-border-radius:5px;
- font-family:'DeliciousRoman', Arial, sans-serif;
- color: #6B3C6A;
- font-size:18px;
- padding:10px 12px;
- text-align:left;
- width: 412px;
- margin-bottom:40px;
-}
-
-#stepwise_make_request a img {
- margin-bottom:-10px;
- margin-top: -10px;
- margin-left:6px;
-}
-
-#stepwise_make_request strong {
- color: #93278F;
-}
-
-p.subtitle {
- margin-top:10px;
- margin-bottom:20px;
- font-style: italic;
-}
-
-.results_section {
- margin-bottom:40px;
-}
-
-.results_section div:last-child {
- border-bottom-width:0px;
- padding-bottom:0px;
-}
-
-.request_listing,
-.user_listing,
-.body_listing {
- border-bottom:1px solid #DDD;
- padding:12px 0px 6px;
- margin:0px;
- margin-bottom:-1px;
-}
-
-.body_listing {
- padding-bottom:16px;
-}
-
-.request_listing span.head,
-.user_listing span.head,
-.body_listing span.head {
- background:none;
- padding:0px;
- font-size:21px;
- margin-bottom:6px;
-}
-
-.request_listing span.head a,
-.user_listing span.head a,
-.body_listing span.head a {
- color: #6B3C6A;
- font-family: 'DeliciousRoman', Arial, sans-serif;
- font-weight: normal;
- text-decoration: none;
- font-size: 20px;
- margin-top: 3px;
- display: block;
- margin-bottom: -6px;
-}
-
-.request_listing span.head a:hover,
-.user_listing span.head a:hover,
-.body_listing span.head a:hover {
- text-decoration: underline;
-}
-
-.request_listing .requester {
- font-size: 12px;
- padding-bottom:0px;
-}
-
-.request_listing .requester a {
- text-decoration: underline;
-}
-
-.body_listing span.desc,
-.body_listing span.bottomline,
-.user_listing span.bottomline {
- padding:0px;
- margin:0px;
- font-style: normal;
- font-size: 12px;
- font-weight: normal !important;
-}
-
-.request_listing span.bottomline {
- padding: 3px 0px 0px 27px;
- font-style: normal;
- margin-bottom: 0px;
- margin-top:12px;
- background-position: top left;
- font-size: 14px;
- font-weight: normal !important;
- min-height: 36px;
-}
-
-.user_listing {
- padding-top:10px;
- padding-bottom:0px;
-}
-
-.icon_requires_admin,
-.icon_waiting_response_overdue,
-.icon_waiting_response_very_overdue {
- background-image: url(/images/status-overdue.png);
- color: #C1272D;
-}
-
-.icon_successful,
-.icon_partially_successful {
- background-image: url(/images/status-complete.png);
- color: #69952F;
-}
-
-.icon_waiting_response,
-.icon_waiting_classification,
-.icon_waiting_clarification {
- background-image: url(/images/status-pending.png);
- color: #A68C2E;
-}
-
-.icon_failed,
-.icon_rejected {
- background-image: url(/images/status-denied.png);
- color: #C1272D;
-}
-
-.icon_not_held {
- background-image: url(/images/status-not-held.png);
- color: #A68C2E;
-}
-
-.icon_gone_postal {
- background-image: url(/images/status-gone-postal.png);
- color: #A68C2E;
-}
-
-.icon_error_message {
- background-image: url(/images/status-error.png);
- color: #C1272D;
-}
-
-.icon_internal_review {
- background-image: url(/images/status-internal-review.png);
- color: #A68C2E;
-}
-
-.icon_user_withdrawn {
- background-image: url(/images/status-withdrawn.png);
- color: #A68C2E;
-}
-
-#request_sidebar {
- width: 212px;
- font-size: 12px;
-}
-
-#request_sidebar h2 {
- margin-bottom:10px;
-}
-
-.feed_link {
- padding: 4px 0px;
-}
-
-.feed_link a {
- text-decoration: none;
-}
-
-.request_listing span.desc {
- background: url(/images/quote-marks.png) no-repeat;
- padding: 0px 0px 0px 40px;
- min-height:60px;
- font-size:12px;
- width:auto;
- color:#444;
- line-height:18px;
-}
-
-#footer {
- border-color: #FFF;
- margin-top:60px;
-}
-
-div.ff-icon-printfix {
- display: none;
-}
-#search_form {
- margin:0px -6px 20px 0px;
-}
-
-#advanced-search input[type=text] {
- width: auto;
-}
-
-#search_form input[type=submit] {
- border-radius: 0px 2px 2px 0px;
- -moz-border-radius: 0px 2px 2px 0px;
-}
-
-#header_left {
- width:625px;
- float:left;
-}
-
-#header_right {
- float:right;
- width:230px;
- padding-top:27px;
-}
-
-.feed_link,
-.act_link {
- display:block !important;
- margin-bottom:10px;
-}
-
-.feed_link a,
-.act_link a,
-#header_right > a {
- text-decoration: none;
- color: #6B3C6A;
-}
-
-.feed_link a:hover,
-.act_link a:hover,
-#header_right > a:hover {
- text-decoration: underline;
-}
-
-.feed_link a img,
-.act_link img,
-.act_link a img {
- padding-right:2px;
-}
-
-form.feed_form input[type="submit"] {
- font-size: 12px;
- line-height: 12px;
- padding: 2px 4px;
-}
-
-#header_right > br {
- line-height:200%;
-}
-
-#general_search h2 {
- clear:both;
- margin-top:20px;
-}
-
-.highlight {
- background:#FFFF00;
- border-width:0px;
-}
-
-h2.foi_results,
-h2.person_results,
-h2.publicbody_results {
- padding-top:0px;
- padding-bottom:15px;
- margin-bottom:0px;
- margin-top: 0px;
-}
-
-h2.foi_results {
- width: 600px;
-}
-
-.list_toggle_controls {
- padding-bottom:20px;
-}
-
-#request_advice ol {
- margin-left:18px;
- margin-top:20px;
- display:block;
- padding:0px;
-}
-
-#request_advice ol li {
- padding-bottom:5px;
-}
-
-#request_form label,
-label.form_label {
- width: 100px;
- font-family: 'DeliciousRoman', Arial, sans-serif;
-}
-
-#request_form label,
-label.form_label,
-span#to_public_body {
- color: #6B3C6A;
- font-size: 18px;
-}
-
-#date_range label,
-#filter_requests_form label {
- display: inline;
- float: none;
- padding-right: 5px;
-}
-
-#date_range label.title,
-#filter_requests_form label.title,
-h3.title {
- display: inline-block;
- float: none;
- width:110px;
-}
-
-h3.title {
- width: 114px;
- margin-bottom: 5px;
-}
-
-#requests-subfilters div {
- margin-top: 10px;
-}
-
-#requests-subfilters #latest_status_0,
-#requests-subfilters #request_variety_0 {
- margin-left: 0px;
-}
-
-#requests-subfilters input[type=checkbox] {
- margin-left: 117px;
-}
-
-.list-filter-item {
- margin-bottom: 10px;
-}
-
-span#to_public_body {
- font-family: 'DeliciousBold', Arial, sans-serif;
-}
-
-.form_item_note, .form_note {
- margin-left: 110px;
- font-size: 1em;
-}
-
-#left_column {
- width:600px;
- float:left;
-}
-
-#right_column_flip {
- width:220px;
- float:left;
- margin-top:20px;
-}
-
-#left_column_flip {
- width:620px;
- float:right;
- margin-top: 10px;
-}
-
-#right_column {
- width:220px;
- float:right;
- margin-top:30px;
-}
-
-#left_half {
- width:45%;
- float:left;
-}
-
-#right_half {
- width:45%;
- float:right;
-}
-
-#middle_strip {
- float: left;
- width: 10%;
- height: 100px;
- text-align: center;
- margin-top: 45px;
- font-size: 16px;
- font-family: Georgia;
- font-style: italic;
- color: #93278F;
-}
-
-#signup, #signin {
- float:none;
- margin-top:20px;
- width:auto;
-}
-
-#sign_together h1 {
- width: 320px;
- text-align: center;
-}
-
-#sign_together .form_button {
- margin-left: 10.5em;
-}
-
-#signup .form_item_note, #signin .form_note {
- margin-left:11.5em;
- width:24em;
-}
-
-#request_form label, label.form_label {
- margin-top:2px;
- text-align:left;
-}
-
-form input[type=text],
-form input[type=password] {
- font-size: 14px;
- padding: 5px;
- width:200px;
- color: #555;
- border-radius: 3px;
- -moz-border-radius: 3px;
- border-style: solid;
- border-color: #BBB;
- border-width: 1px;
-}
-
-form input.use-datepicker[type=text] {
- width: 130px !important;
- background: url(/images/calendar.png) no-repeat 115px 3px;
- margin: 0px !important;
- border-radius:3px !important;
- -moz-border-radius:3px !important;
- font-size: 14px !important;
-}
-
-form input[type=submit],
-a.link_button_green,
-a.link_button_green_large {
- background: url(/images/button-gradient.png);
- color: white;
- text-decoration: none;
- padding: 5px 11px;
- display: inline-block;
- line-height: 18px;
- border: solid 1px #69952F;
- border-radius: 2px;
- -moz-border-radius: 2px;
- text-shadow: 1px 1px 0px #5B841D;
- font-family: 'DeliciousRoman', Arial, sans-serif;
- font-size: 18px;
- cursor: pointer;
- cursor: hand;
-}
-
-a.link_button_green_large {
- background: url(/images/button-gradient-large.png);
- font-size: 22px;
- line-height: 22px;
- padding-bottom: 7px;
-}
-
-@-moz-document url-prefix() {
- form input[type=submit],
- a.link_button_green {
- padding: 2px 11px;
- margin-top:1px;
- }
-}
-
-form input[type=submit].small {
- font-size: 15px;
- padding: 4px 9px;
- line-height: 10px;
-}
-
-ul.no_bullets {
- list-style-type:none;
- margin:0px 0px 30px 0px;
- padding:0px;
-}
-
-ul.no_bullets li {
- margin-bottom:6px;
-}
-
-#contact_preamble {
- margin: 0px 0px 30px 0px;
- width:auto;
-}
-
-#frontpage_search {
- background:transparent;
-}
-
-#public_body_list #right_column ul {
- padding: 0px 0px 20px 0px;
- margin: 0px;
- list-style: none;
-}
-
-div#twitter {
- margin-top:30px;
- background:#FFF;
-}
-
-div.blog_post p {
- line-height:180%;
-}
-
-div.frontpage-box {
- background: url(/images/stripes.png);
- border: 1px solid #DEBEDD;
- border-radius:5px;
- -moz-border-radius:5px;
- font-family:'DeliciousRoman', Arial, sans-serif;
- color: #6B3C6A;
- font-size:18px;
- padding:15px 12px 0;
- text-align:center;
- width: 255px;
- height: 210px;
- float: left;
-}
-
-#frontpage-box-1 {
- margin-right: 10px;
- vertical-align: middle;
-}
-
-#frontpage-box-2 {
- margin-right: 10px;
-}
-
-
-#bighand {
- padding: 0;
- margin: 0;
- height: 210px;
- background: url(/images/bighand.png) no-repeat scroll 0 bottom transparent;
-}
-
-#littlehand {
- padding: 0;
- margin: 0;
- height: 210px;
- background: url(/images/littlehand.png) repeat-x scroll 10px bottom transparent;
-}
-
-#frontpage-box-3 {
-}
-
-#frontpage-box-3 #search_form input[type=text] {
- width: 10em;
-}
-
-#frontpage_examples p,
-#frontpage_examples ul {
- text-align: left;
-}
-#examples_1 ul li {
- border-bottom: 1px solid #ddd;
- padding: 5px 0 5px 0;
-}
-
-#frontpage_examples .excerpt {
- cursor: hand;
- cursor: pointer;
- background: url(/images/quote-marks.png) no-repeat;
- padding: 0px 0px 0px 40px;
- font-size:12px;
- color:#444;
- line-height:18px;
- min-height: 30px;
- font-style: italic;
-}
-
-#set_photo {
- background: url(/images/defaultprofilepic.png)
-}
-
-div.correspondence,
-div.comment_in_request {
- width: 600px;
- background: url(/images/stripes-70.png);
- border-width: 0px;
- font-size: 13px;
- border-radius: 6px;
- -moz-border-radius: 6px;
- padding: 4px 20px 0px 9px;
-}
-div.outgoing.correspondence {
- background: url(/images/stripes-70-light.png);
-}
-
-div.comment_in_request {
- background: url(/images/stripes-70-light2.png);
-}
-
-div.comment_in_request {
- margin-left: 50px;
- width: 550px;
-}
-
-div[id|="comment"] {
- margin-left: 50px;
-}
-
-div[id|="comment"] h2 {
- font-size: 1em;
- text-align:right;
-}
-
-div[id|="comment"] p {
- font-size: 13px;
-}
-
-.comment_in_request_text {
- margin: 0px 0px 0px 10px;
-}
-
-.comment_in_request_text img.comment_quote {
- display: none;
-}
-
-/* ---------- Frontpage ----------- */
-
-#frontpage_splash {
- height: 375px;
- margin-top: -12px;
- margin-bottom: 20px;
- width: 100%;
- background: url(/images/flying-computer.png) no-repeat 175px bottom;
-}
-
-#frontpage_splash #left_column {
- line-height: 40px;
- margin-top: 66px;
-}
-
-#frontpage_splash h1 {
- margin: 0px 0px 20px 0px;
- font-family: 'DeliciousRoman', Arial, sans-serif;
- font-size: 39px;
- color: #6B3C6A;
- font-weight:normal;
-}
-
-#frontpage_splash h1 strong {
- font-family: 'DeliciousHeavyRegular', Arial, sans-serif;
- font-size: 54px;
- color: #93278F;
- font-weight: normal;
-}
-
-#frontpage_splash h1 span {
- font-family: Georgia;
- font-style: italic;
- font-weight:normal;
- font-size: 25px;
- color: #6B3C6A;
-}
-
-#frontpage_splash #right_column {
- width: 265px;
-}
-
-#frontpage_splash h2 {
- font-size: 26px;
- font-weight:normal;
- color: #6B3C6A;
- font-family:'DeliciousRoman', Arial, sans-serif;
- margin-bottom: 10px;
- line-height: 28px;
-}
-
-#frontpage_splash h2 strong {
- font-size: 31px;
- color: #93278F;
- font-family:'DeliciousBold', Arial, sans-serif;
-}
-
-#frontpage_splash h2 span {
- color: #6B3C6A;
- font-style: italic;
- font-size: 19px;
- font-family: Georgia;
-}
-
-#frontpage_splash #right_column input[type=text] {
- width: 180px;
-}
-
-#frontpage_splash #frontpage_search_box {
- margin-bottom: 30px;
- margin-top: -10px;
-}
-
-#frontpage_splash #frontpage_right_to_know p {
- line-height: 20px;
-}
-
-body.front h3 {
- font-size: 28px;
-}
-
-/* ---------- Calendar theme ----------- */
-
-#ui-datepicker-div.ui-widget {
- font-family: Arial, sans-serif;
- color: #93278F;
-}
-
-#ui-datepicker-div .ui-datepicker-header,
-#ui-datepicker-div .ui-widget-header {
- background: none;
- border: solid 0px white;
- color: #93278F;
- font-family: 'DeliciousRoman';
- font-size: 17px;
- font-weight: normal;
- line-height: 1.5em !important;
-}
-
-#ui-datepicker-div .ui-state-default {
- background: #F2F2F2;
- border: solid 0px #FFF;
- border-radius: 2px;
- -moz-border-radius: 2px;
-}
-
-#ui-datepicker-div .ui-state-default:hover {
- background: #DEB4D8;
- color: #FFF;
-}
-
-#ui-datepicker-div .ui-state-active {
- background: #D093C7;
- color: #FFF;
-}
-
-#ui-datepicker-div .ui-icon-circle-triangle-w,
-#ui-datepicker-div .ui-icon-circle-triangle-e {
- background-image: url(/images/ui-icons-theme.png);
-}
-
-#ui-datepicker-div .ui-datepicker-prev {
- margin-top: 2px;
- opacity: 0.5;
-}
-
-#ui-datepicker-div .ui-datepicker-prev-hover {
- left: 2px;
- top: 2px;
- border: none;
- background:#FFF;
- cursor:pointer;
- opacity: 1;
-}
-
-#ui-datepicker-div .ui-datepicker-next {
- margin-top: 2px;
- opacity: 0.5;
-}
-
-#ui-datepicker-div .ui-datepicker-next-hover {
- right: 2px;
- top: 2px;
- border: none;
- background:#FFF;
- cursor:pointer;
- opacity: 1;
-}
-
-#other-country-notice {
- position: absolute;
-}
-
-body.front #other-country-notice,
-#other-country-notice {
- display: none;
-}
-
-p.public-body-name-prefix {
- color: #DEB4D8;
- margin-top: 15px;
- margin-bottom: -15px;
-}
-
-#authority_preview .public-body-name-prefix,
-#authority_preview #list-filter,
-#authority_preview h2.foi_results {
- display: none;
-}
-
-p.subtitle {
- font-size: 18px;
- font-family: 'DeliciousRoman';
- font-style: normal;
- color: #6B3C7F;
-}
-
-/*------------- Other countries notice ---------------*/
-#other-country-notice {
- background: #93278F;
- color: #FFF;
- font-size: 16px;
- width: 100%;
- z-index: 999;
- display: block;
- position: absolute;
- top: 0px;
- opacity: 0.9;
-}
-
-#other-country-notice a {
- color: #FFF;
-}
-
-.close-button {
- color: white;
- text-decoration: none;
- display: inline-block;
- border-radius: 2px;
- -moz-border-radius: 2px;
- cursor: pointer;
- cursor: hand;
- background: url(/images/small-white-cross.png) no-repeat;
- padding: 10px 0;
- width: 15px;
- height: 15px;
- border: solid 0px #FFF;
- text-indent: -999px;
- overflow:hidden;
- float:right;
-
-}
-/*------------- Link box ---------------*/
-#link_box {
- position: absolute;
- padding: 5px;
- text-align: left;
- background-color: white;
- z-index: 999;
- opacity: 0.9;
- border-radius: 6px;
- -moz-border-radius: 6px;
- border: 1px solid #93278F;
- display: none;
-}
-
-#link_box .close-button {
- background-color: #93278F;
- padding: 0;
- margin-left: 15px;
-}
-
-a.link_to_this {
- display: inline-block;
- width: 20px;
- letter-spacing: -1000em;
- overflow: hidden;
- background: url(/images/link-icon.png) no-repeat;
-}
-
-/*---------- From tweaks ----------*/
-#to_public_body {
- display: block;
- margin-bottom: 15px;
-}
-
-.fieldWithErrors {
- background: none;
- border: solid 0px #FFF;
-}
-
-.fieldWithErrors textarea,
-.fieldWithErrors input {
- border: solid 1px Red !important;
-}
-
-.errorExplanation {
- border-radius: 6px;
- -moz-border-radius: 6px;
- font-size: 12px;
- font-weight: normal;
- width: 554px;
- margin: 20px 0px 30px 0px;
-}
-
-#notice, .describe_state_form, #other_recipients {
- font-size: 12px;
- font-weight: normal;
- padding: 10px 20px 10px 20px;
- background: #E9FDD3 !important;
- color: #517704;
- border-color: #B0CA86;
- border-radius: 6px;
- -moz-border-radius: 6px;
- margin: 15px 0px;
-}
-
-.describe_state_form hr {
- border-color: #B0CA86;
- border-top: 0px;
- border-style: dotted;
- margin: 20px 0px;
-}
-
-.describe_state_form {
- color: #333;
-}
-
-#notice p:first-child {
- margin-top: 0px;
-}
-
-#notice p:last-child {
- margin-bottom: 0px;
-}
-
-div.correspondence p.preview_subject {
- font-family: 'DeliciousRoman';
- font-size: 18px !important;
- margin-left: 10px;
- color: #6B3C7F;
- line-height: 25px;
-}
-
-div.correspondence p.preview_subject strong {
- color: #6B3C7F;
-}
-
-#preview_form ul {
- margin: 0px;
- padding: 1px 32px 10px;
-}
-
-#preview_form ul li {
- margin: 10px 0px;
-}
-
-div#show_response_view p.event_actions {
- display: none;
-} \ No newline at end of file
diff --git a/script/about b/script/about
index 746e44659..f2b98742d 100755
--- a/script/about
+++ b/script/about
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot.rb'
require 'commands/about'
diff --git a/script/breakpointer b/script/breakpointer
index dfe58bf36..609564148 100755
--- a/script/breakpointer
+++ b/script/breakpointer
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot.rb'
require 'commands/breakpointer'
diff --git a/script/cache-incoming-emails b/script/cache-incoming-emails
new file mode 100644
index 000000000..a84a713d6
--- /dev/null
+++ b/script/cache-incoming-emails
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# Fill in all the database caches of text from body/attachments.
+# Will take a while to run! Can use after clear-caches to refresh the database
+# level caches if you like.
+
+LOC=`dirname $0`
+
+"$LOC/runner" 'IncomingMessage.find_each() { |im| print "info request " + im.info_request.id.to_s + ", incoming message " + im.id.to_s + ": " + im.extract_attachments!.count.to_s + " attachments extracted to " + im.foi_attachments[0].directory + "; main body folded: " + im.get_main_body_text_folded.size.to_s + " attachment clipped:" + im.get_attachment_text_clipped.size.to_s + "\n" }'
diff --git a/script/clear-caches b/script/clear-caches
index e9438f92d..be1d3d017 100755
--- a/script/clear-caches
+++ b/script/clear-caches
@@ -4,7 +4,7 @@
LOC=`dirname $0`
-"$LOC/runner" "ActiveRecord::Base.connection.execute(\"update incoming_messages set cached_attachment_text_clipped = null, cached_main_body_text_unfolded = null, cached_main_body_text_folded = null\")"
+"$LOC/runner" "ActiveRecord::Base.connection.execute(\"update incoming_messages set cached_attachment_text_clipped = null, cached_main_body_text_unfolded = null, cached_main_body_text_folded = null, sent_at = null, subject = null, mail_from = null, mail_from_domain = null, valid_to_reply_to = null, last_parsed = null\")"
# Remove page cache (do it in two stages so live site gets cache cleared faster)
rm -fr $LOC/../old-cache
diff --git a/script/console b/script/console
index 98f6702bb..83386647f 100755
--- a/script/console
+++ b/script/console
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.expand_path(File.dirname(__FILE__) + '/../config/boot.rb')
require 'commands/console'
diff --git a/script/destroy b/script/destroy
index 937962908..e63ac0ef5 100755
--- a/script/destroy
+++ b/script/destroy
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot.rb'
require 'commands/destroy'
diff --git a/script/fill-database-caches b/script/fill-database-caches
deleted file mode 100755
index e6b525144..000000000
--- a/script/fill-database-caches
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-
-# Fill in all the database caches of text from body/attachments.
-# Will take a while to run! Can use after clear-caches to refresh the database
-# level caches if you like.
-
-LOC=`dirname $0`
-
-"$LOC/runner" 'IncomingMessage.find_each() { |im| print im.id.to_s + " id: main body folded:" + im.get_main_body_text_folded.size.to_s + " attachment clipped:" + im.get_attachment_text_clipped.size.to_s + "\n" }'
-
-
diff --git a/script/generate b/script/generate
index a765ddcd4..8c0486a09 100755
--- a/script/generate
+++ b/script/generate
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot.rb'
require 'commands/generate'
diff --git a/script/handle-mail-replies b/script/handle-mail-replies
index 9b1fb5b29..68cab9035 100755
--- a/script/handle-mail-replies
+++ b/script/handle-mail-replies
@@ -1,4 +1,5 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
# Handle email responses sent to us.
#
@@ -17,8 +18,13 @@ load "config.rb"
MySociety::Config.set_file(File.join($alaveteli_dir, 'config', 'general'), true)
MySociety::Config.load_default
-$:.push(File.join($alaveteli_dir, "vendor", "rails", "actionmailer", "lib", "action_mailer", "vendor", "tmail-1.2.7"))
-require 'tmail'
+require 'rubygems'
+if File.exist? File.join($alaveteli_dir,'vendor','rails','Rakefile')
+ $:.push(File.join($alaveteli_dir, "vendor", "rails", "actionmailer", "lib", "action_mailer", "vendor", "tmail-1.2.7"))
+ require 'tmail'
+else
+ require 'action_mailer'
+end
def main(in_test_mode)
Dir.chdir($alaveteli_dir) do
@@ -114,12 +120,21 @@ def is_oof?(message)
end
end
+ if message.header_string("Auto-Submitted") == "auto-generated"
+ if subject =~ /out of( the)? office/
+ return true
+ end
+ end
+
if subject.start_with? "out of office autoreply:"
return true
end
if subject == "out of office"
return true
end
+ if subject == "out of office reply"
+ return true
+ end
if subject.end_with? "is out of the office"
return true
end
diff --git a/script/performance/benchmarker b/script/performance/benchmarker
index c2441c941..a94253aba 100755
--- a/script/performance/benchmarker
+++ b/script/performance/benchmarker
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../config/boot.rb'
require 'commands/performance/benchmarker'
diff --git a/script/performance/profiler b/script/performance/profiler
index a7ea37a9a..e9e5b071d 100755
--- a/script/performance/profiler
+++ b/script/performance/profiler
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../config/boot.rb'
require 'commands/performance/profiler'
diff --git a/script/plugin b/script/plugin
index bcc697802..18ae72620 100755
--- a/script/plugin
+++ b/script/plugin
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot.rb'
require 'commands/plugin'
diff --git a/script/process/inspector b/script/process/inspector
index 261317109..696551c6b 100755
--- a/script/process/inspector
+++ b/script/process/inspector
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../config/boot.rb'
require 'commands/process/inspector'
diff --git a/script/process/reaper b/script/process/reaper
index 309764a0d..a03da9387 100755
--- a/script/process/reaper
+++ b/script/process/reaper
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../config/boot.rb'
require 'commands/process/reaper'
diff --git a/script/process/spawner b/script/process/spawner
index 2768db7fd..6852fba27 100755
--- a/script/process/spawner
+++ b/script/process/spawner
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../../config/boot.rb'
require 'commands/process/spawner'
diff --git a/script/runner b/script/runner
index 5a5254c47..6b0bc0a08 100755
--- a/script/runner
+++ b/script/runner
@@ -1,4 +1,4 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
daemon_mode = !ARGV.empty? && ARGV[0] == "--daemon"
diff --git a/script/server b/script/server
index 9c6088a88..dc3edabd5 100755
--- a/script/server
+++ b/script/server
@@ -1,5 +1,3 @@
-#!/usr/bin/ruby
-
#!/usr/bin/env ruby
require File.expand_path(File.dirname(__FILE__) + '/../config/boot.rb')
require 'commands/server'
diff --git a/script/wraptest b/script/wraptest
index d62e4ce48..780c9b4a2 100755
--- a/script/wraptest
+++ b/script/wraptest
@@ -1,4 +1,4 @@
-#!/usr/bin/ruby
+#!/usr/bin/env ruby
#
# wraptest:
# Test email wrapping function
diff --git a/spec/controllers/admin_public_body_controller_spec.rb b/spec/controllers/admin_public_body_controller_spec.rb
index 0a90cd64b..22af3df80 100644
--- a/spec/controllers/admin_public_body_controller_spec.rb
+++ b/spec/controllers/admin_public_body_controller_spec.rb
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminPublicBodyController, "when administering public bodies" do
integrate_views
- fixtures :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
username = MySociety::Config.get('ADMIN_USERNAME', '')
@@ -57,7 +57,7 @@ end
describe AdminPublicBodyController, "when administering public bodies and paying attention to authentication" do
integrate_views
- fixtures :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "disallows non-authenticated users to do anything" do
@request.env["HTTP_AUTHORIZATION"] = ""
@@ -107,7 +107,7 @@ end
describe AdminPublicBodyController, "when administering public bodies with i18n" do
integrate_views
- fixtures :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
username = MySociety::Config.get('ADMIN_USERNAME', '')
@@ -176,7 +176,7 @@ end
describe AdminPublicBodyController, "when creating public bodies with i18n" do
integrate_views
- fixtures :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
username = MySociety::Config.get('ADMIN_USERNAME', '')
diff --git a/spec/controllers/admin_request_controller_spec.rb b/spec/controllers/admin_request_controller_spec.rb
index 6f9af0525..635d73b9e 100644
--- a/spec/controllers/admin_request_controller_spec.rb
+++ b/spec/controllers/admin_request_controller_spec.rb
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminRequestController, "when administering requests" do
integrate_views
- fixtures :info_requests, :outgoing_messages, :users, :info_request_events, :public_bodies, :public_body_translations
+ fixtures :users, :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before { basic_auth_login @request }
it "shows the index/list page" do
@@ -41,7 +41,7 @@ end
describe AdminRequestController, "when administering the holding pen" do
integrate_views
- fixtures :info_requests, :incoming_messages, :raw_emails, :users, :public_bodies, :public_body_translations
+ fixtures :users, :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
basic_auth_login @request
load_raw_emails_data(raw_emails)
diff --git a/spec/controllers/admin_track_controller_spec.rb b/spec/controllers/admin_track_controller_spec.rb
index 4d5b0ac5e..b87ee9f0e 100644
--- a/spec/controllers/admin_track_controller_spec.rb
+++ b/spec/controllers/admin_track_controller_spec.rb
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminTrackController, "when administering tracks" do
integrate_views
- fixtures :track_things, :users
+ fixtures :users, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "shows the list page" do
get :list
diff --git a/spec/controllers/admin_user_controller_spec.rb b/spec/controllers/admin_user_controller_spec.rb
index 313f3f328..b2b2d0626 100644
--- a/spec/controllers/admin_user_controller_spec.rb
+++ b/spec/controllers/admin_user_controller_spec.rb
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe AdminUserController, "when administering users" do
integrate_views
- fixtures :users
+ fixtures :users, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before { basic_auth_login @request }
it "shows the index/list page" do
diff --git a/spec/controllers/comment_controller_spec.rb b/spec/controllers/comment_controller_spec.rb
index 2b0f5eee2..4c14b8d24 100644
--- a/spec/controllers/comment_controller_spec.rb
+++ b/spec/controllers/comment_controller_spec.rb
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe CommentController, "when commenting on a request" do
integrate_views
- fixtures :info_requests, :outgoing_messages, :public_bodies, :public_body_translations, :users, :comments
+ fixtures :users, :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should give an error and render 'new' template when body text is just some whitespace" do
post :new, :url_title => info_requests(:naughty_chicken_request).url_title,
diff --git a/spec/controllers/general_controller_spec.rb b/spec/controllers/general_controller_spec.rb
index 1ffbda90d..40a676d61 100644
--- a/spec/controllers/general_controller_spec.rb
+++ b/spec/controllers/general_controller_spec.rb
@@ -2,15 +2,19 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe GeneralController, "when searching" do
integrate_views
- fixtures [ :info_requests,
- :info_request_events,
- :public_bodies,
- :public_body_translations,
- :users,
- :raw_emails,
- :outgoing_messages,
- :incoming_messages,
- :comments ]
+ fixtures [
+ :public_bodies,
+ :public_body_translations,
+ :public_body_versions,
+ :users,
+ :info_requests,
+ :raw_emails,
+ :incoming_messages,
+ :outgoing_messages,
+ :comments,
+ :info_request_events,
+ :track_things,
+ ]
before(:each) do
load_raw_emails_data(raw_emails)
@@ -70,13 +74,13 @@ describe GeneralController, "when searching" do
describe "when using different locale settings" do
home_link_regex = /href=".*\/en"/
it "should generate URLs with a locale prepended when there's more than one locale set" do
- ActionController::Routing::Routes.add_filters(['conditionallyprependlocale'])
+ ActionController::Routing::Routes.add_filters('conditionallyprependlocale')
get :frontpage
response.should have_text(home_link_regex)
end
it "should generate URLs without a locale prepended when there's only one locale set" do
- ActionController::Routing::Routes.add_filters(['conditionallyprependlocale'])
+ ActionController::Routing::Routes.add_filters('conditionallyprependlocale')
old_available_locales = FastGettext.default_available_locales
available_locales = ['en']
FastGettext.default_available_locales = available_locales
diff --git a/spec/controllers/public_body_controller_spec.rb b/spec/controllers/public_body_controller_spec.rb
index c5c9d60e1..8182e1331 100644
--- a/spec/controllers/public_body_controller_spec.rb
+++ b/spec/controllers/public_body_controller_spec.rb
@@ -4,7 +4,7 @@ require 'json'
describe PublicBodyController, "when showing a body" do
integrate_views
- fixtures :public_bodies, :public_body_translations, :public_body_versions
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should be successful" do
get :show, :url_name => "dfh", :view => 'all'
@@ -61,13 +61,28 @@ end
describe PublicBodyController, "when listing bodies" do
integrate_views
- fixtures :public_bodies, :public_body_translations, :public_body_versions
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should be successful" do
get :list
response.should be_success
end
+ it "should list all bodies from default locale, even when there are no translations for selected locale" do
+ PublicBody.with_locale(:en) do
+ english_only = PublicBody.new(:name => 'English only',
+ :short_name => 'EO',
+ :request_email => 'english@flourish.org',
+ :last_edit_editor => 'test',
+ :last_edit_comment => '')
+ english_only.save
+ end
+ PublicBody.with_locale(:es) do
+ get :list
+ assigns[:public_bodies].length.should == 3
+ end
+ end
+
it "should list bodies in alphabetical order" do
get :list
@@ -110,7 +125,7 @@ describe PublicBodyController, "when listing bodies" do
get :list, :tag => "other"
response.should render_template('list')
assigns[:public_bodies].should == [ public_bodies(:geraldine_public_body) ]
-
+
get :list
response.should render_template('list')
assigns[:public_bodies].count.should == 2
@@ -142,7 +157,7 @@ end
describe PublicBodyController, "when showing JSON version for API" do
- fixtures :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should be successful" do
get :show, :url_name => "dfh", :format => "json", :view => 'all'
@@ -157,12 +172,12 @@ describe PublicBodyController, "when showing JSON version for API" do
end
describe PublicBodyController, "when doing type ahead searches" do
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should return nothing for the empty query string" do
get :search_typeahead, :q => ""
response.should render_template('public_body/_search_ahead')
- assigns[:xapian_requests].results.size.should == 0
+ assigns[:xapian_requests].should be_nil
end
it "should return a body matching the given keyword, but not users with a matching description" do
@@ -187,10 +202,9 @@ describe PublicBodyController, "when doing type ahead searches" do
assigns[:xapian_requests].results[0][:model].name.should == public_bodies(:humpadink_public_body).name
end
- it "should return partial matches" do
- get :search_typeahead, :q => "geral" # 'geral' for 'Geraldine'
+ it "should not return matches for short words" do
+ get :search_typeahead, :q => "b"
response.should render_template('public_body/_search_ahead')
- assigns[:xapian_requests].results.size.should == 1
- assigns[:xapian_requests].results[0][:model].name.should == public_bodies(:geraldine_public_body).name
+ assigns[:xapian_requests].should be_nil
end
end
diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb
index 494713a4a..4994c2a8f 100644
--- a/spec/controllers/request_controller_spec.rb
+++ b/spec/controllers/request_controller_spec.rb
@@ -1,9 +1,10 @@
+# -*- coding: utf-8 -*-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
require 'json'
describe RequestController, "when listing recent requests" do
- fixtures :info_requests, :outgoing_messages, :users, :info_request_events, :public_bodies, :public_body_translations, :incoming_messages, :raw_emails, :comments
+ fixtures :users, :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
@@ -51,7 +52,7 @@ end
describe RequestController, "when showing one request" do
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things # all needed as integrating views
before(:each) do
load_raw_emails_data(raw_emails)
@@ -104,10 +105,12 @@ describe RequestController, "when showing one request" do
integrate_views
it "should receive incoming messages, send email to creator, and show them" do
+ ir = info_requests(:fancy_dog_request)
+ ir.incoming_messages.each { |x| x.parse_raw_email! }
+
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
size_before = assigns[:info_request_events].size
- ir = info_requests(:fancy_dog_request)
receive_incoming_mail('incoming-request-plain.email', ir.incoming_email)
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 1
@@ -119,6 +122,8 @@ describe RequestController, "when showing one request" do
end
it "should download attachments" do
+ ir = info_requests(:fancy_dog_request)
+ ir.incoming_messages.each { |x| x.parse_raw_email! }
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
response.content_type.should == "text/html"
size_before = assigns[:info_request_events].size
@@ -128,7 +133,7 @@ describe RequestController, "when showing one request" do
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
(assigns[:info_request_events].size - size_before).should == 1
-
+ ir.reload
get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt']
response.content_type.should == "text/plain"
response.should have_text(/Second hello/)
@@ -137,19 +142,60 @@ describe RequestController, "when showing one request" do
response.should have_text(/First hello/)
end
+ it "should convert message body to UTF8" do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('iso8859_2_raw_email.email', ir.incoming_email)
+ get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
+ response.should have_text(/tënde/u)
+ end
+
it "should generate valid HTML verson of plain text attachments " do
ir = info_requests(:fancy_dog_request)
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
+ ir.reload
get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
response.content_type.should == "text/html"
response.should have_text(/Second hello/)
end
+ it "should not cause a reparsing of the raw email, even when the result would be a 404 " do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
+ ir.reload
+ attachment = IncomingMessage.get_attachment_by_url_part_number(ir.incoming_messages[1].get_attachments_for_display, 2)
+ attachment.body.should have_text(/Second hello/)
+
+ # change the raw_email associated with the message; this only be reparsed when explicitly asked for
+ ir.incoming_messages[1].raw_email.data = ir.incoming_messages[1].raw_email.data.sub("Second", "Third")
+ # asking for an attachment by the wrong filename results
+ # in a 404 for browsing users. This shouldn't cause a
+ # re-parse...
+ lambda {
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt.baz.html'], :skip_cache => 1
+ }.should raise_error(ActiveRecord::RecordNotFound)
+
+ attachment = IncomingMessage.get_attachment_by_url_part_number(ir.incoming_messages[1].get_attachments_for_display, 2)
+ attachment.body.should have_text(/Second hello/)
+
+ # ...nor should asking for it by its correct filename...
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
+ response.should_not have_text(/Third hello/)
+
+ # ...but if we explicitly ask for attachments to be extracted, then they should be
+ force = true
+ ir.incoming_messages[1].parse_raw_email!(force)
+ attachment = IncomingMessage.get_attachment_by_url_part_number(ir.incoming_messages[1].get_attachments_for_display, 2)
+ attachment.body.should have_text(/Second hello/)
+ get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt.html'], :skip_cache => 1
+ response.should have_text(/Third hello/)
+ end
+
it "should treat attachments with unknown extensions as binary" do
ir = info_requests(:fancy_dog_request)
receive_incoming_mail('incoming-request-attachment-unknown-extension.email', ir.incoming_email)
+ ir.reload
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.qwglhm']
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.qwglhm'], :skip_cache => 1
response.content_type.should == "application/octet-stream"
response.should have_text(/an unusual sort of file/)
end
@@ -192,8 +238,9 @@ describe RequestController, "when showing one request" do
ir.user.censor_rules << censor_rule
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
+ ir.reload
- get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt']
+ get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => ['hello.txt'], :skip_cache => 1
response.content_type.should == "text/plain"
response.should have_text(/xxxxxx hello/)
end
@@ -202,7 +249,22 @@ describe RequestController, "when showing one request" do
ir = info_requests(:fancy_dog_request)
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
+ # XXX this is horrid, but don't know a better way. If we
+ # don't do this, the info_request_event to which the
+ # info_request is attached still uses the unmodified
+ # version from the fixture.
+ #event = info_request_events(:useless_incoming_message_event)
+ ir.reload
+ assert ir.info_request_events[3].incoming_message.get_attachments_for_display.count == 2
+ ir.save!
+ ir.incoming_messages.last.save!
get :show, :url_title => 'why_do_you_have_such_a_fancy_dog'
+ assert assigns[:info_request].info_request_events[3].incoming_message.get_attachments_for_display.count == 2
+ # the issue is that the info_request_events have got cached on them the old info_requests.
+ # where i'm at: trying to replace those fields that got re-read from the raw email. however tests are failing in very strange ways. currently I don't appear to be getting any attachments parsed in at all when in the template (see "*****" in _correspondence.rhtml) but do when I'm in the code.
+
+ # so at this point, assigns[:info_request].incoming_messages[1].get_attachments_for_display is returning stuff, but the equivalent thing in the template isn't.
+ # but something odd is that the above is return a whole load of attachments which aren't there in the controller
response.body.should have_tag("p.attachment strong", /hello.txt/m)
censor_rule = CensorRule.new()
@@ -217,10 +279,17 @@ describe RequestController, "when showing one request" do
end
it "should make a zipfile available, which has a different URL when it changes" do
+ title = 'why_do_you_have_such_a_fancy_dog'
ir = info_requests(:fancy_dog_request)
session[:user_id] = ir.user.id # bob_smith_user
+ get :download_entire_request, :url_title => title
+ assigns[:url_path].should have_text(/#{title}.zip$/)
+ old_path = assigns[:url_path]
+ response.location.should have_text(/#{assigns[:url_path]}$/)
+ zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", old_path)) { |zipfile|
+ zipfile.count.should == 2
+ }
receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email)
- title = 'why_do_you_have_such_a_fancy_dog'
get :download_entire_request, :url_title => title
assigns[:url_path].should have_text(/#{title}.zip$/)
old_path = assigns[:url_path]
@@ -232,17 +301,16 @@ describe RequestController, "when showing one request" do
get :download_entire_request, :url_title => title
assigns[:url_path].should have_text(/#{title}.zip$/)
response.location.should have_text(/#{assigns[:url_path]}/)
- assigns[:url_path].should_not == old_path
zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", assigns[:url_path])) { |zipfile|
zipfile.count.should == 4
-zipfile.entries.each {|x| puts x.name}
}
+ assigns[:url_path].should_not == old_path
end
end
end
describe RequestController, "when changing prominence of a request" do
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :info_request_events, :track_things # all needed as integrating views
before(:each) do
load_raw_emails_data(raw_emails)
@@ -358,7 +426,7 @@ end
describe RequestController, "when creating a new request" do
integrate_views
- fixtures :info_requests, :outgoing_messages, :public_bodies, :public_body_translations, :users
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@user = users(:bob_smith_user)
@@ -542,7 +610,7 @@ end
describe RequestController, "when viewing an individual response for reply/followup" do
integrate_views
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things # all needed as integrating views
before(:each) do
load_raw_emails_data(raw_emails)
@@ -589,7 +657,7 @@ end
describe RequestController, "when classifying an information request" do
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things # all needed as integrating views
before(:each) do
@dog_request = info_requests(:fancy_dog_request)
@@ -800,7 +868,7 @@ describe RequestController, "when classifying an information request" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /as needing admin/
- mail.from_addrs.to_s.should == @request_owner.name_and_email
+ mail.from_addrs.first.to_s.should == @request_owner.name_and_email
end
it 'should say it is showing advice as to what to do next' do
@@ -925,7 +993,7 @@ end
describe RequestController, "when sending a followup message" do
integrate_views
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things # all needed as integrating views
before(:each) do
load_raw_emails_data(raw_emails)
@@ -978,7 +1046,7 @@ describe RequestController, "when sending a followup message" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /What a useless response! You suck./
- mail.to_addrs.to_s.should == "FOI Person <foiperson@localhost>"
+ mail.to_addrs.first.to_s.should == "FOI Person <foiperson@localhost>"
response.should redirect_to(:action => 'show', :url_title => info_requests(:fancy_dog_request).url_title)
@@ -1008,7 +1076,7 @@ end
describe RequestController, "sending overdue request alerts" do
integrate_views
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things # all needed as integrating views
before(:each) do
load_raw_emails_data(raw_emails)
@@ -1025,7 +1093,7 @@ describe RequestController, "sending overdue request alerts" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /promptly, as normally/
- mail.to_addrs.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email
mail.body =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
@@ -1053,7 +1121,7 @@ describe RequestController, "sending overdue request alerts" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /promptly, as normally/
- mail.to_addrs.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email
end
it "should send not actually send the overdue alert if the user is banned" do
@@ -1078,7 +1146,7 @@ describe RequestController, "sending overdue request alerts" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /required by law/
- mail.to_addrs.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:naughty_chicken_request).user.name_and_email
mail.body =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
@@ -1096,7 +1164,7 @@ end
describe RequestController, "sending unclassified new response reminder alerts" do
integrate_views
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things # all needed as integrating views
before(:each) do
load_raw_emails_data(raw_emails)
@@ -1109,7 +1177,7 @@ describe RequestController, "sending unclassified new response reminder alerts"
deliveries.size.should == 3 # sufficiently late it sends reminders too
mail = deliveries[0]
mail.body.should =~ /To let us know/
- mail.to_addrs.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
mail.body =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
mail_token = $2
@@ -1127,7 +1195,7 @@ end
describe RequestController, "clarification required alerts" do
integrate_views
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things # all needed as integrating views
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -1146,7 +1214,7 @@ describe RequestController, "clarification required alerts" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /asked you to explain/
- mail.to_addrs.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
mail.body =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
mail_token = $2
@@ -1181,7 +1249,7 @@ end
describe RequestController, "comment alerts" do
integrate_views
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things # all needed as integrating views
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -1200,7 +1268,7 @@ describe RequestController, "comment alerts" do
deliveries = ActionMailer::Base.deliveries
mail = deliveries[0]
mail.body.should =~ /has annotated your/
- mail.to_addrs.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
mail.body =~ /(http:\/\/.*)/
mail_url = $1
@@ -1243,7 +1311,7 @@ describe RequestController, "comment alerts" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /There are 2 new annotations/
- mail.to_addrs.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
+ mail.to_addrs.first.to_s.should == info_requests(:fancy_dog_request).user.name_and_email
mail.body =~ /(http:\/\/.*)/
mail_url = $1
@@ -1256,7 +1324,7 @@ end
describe RequestController, "when viewing comments" do
integrate_views
- fixtures :users, :raw_emails, :incoming_messages, :info_requests
+ fixtures :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -1279,9 +1347,10 @@ end
describe RequestController, "authority uploads a response from the web interface" do
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users
+ integrate_views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
- before(:all) do
+ before(:each) do
# domain after the @ is used for authentication of FOI officers, so to test it
# we need a user which isn't at localhost.
@normal_user = User.new(:name => "Mr. Normal", :email => "normal-user@flourish.org",
@@ -1335,7 +1404,7 @@ describe RequestController, "authority uploads a response from the web interface
# How do I test a file upload in rails?
# http://stackoverflow.com/questions/1178587/how-do-i-test-a-file-upload-in-rails
- it "should let the requester upload a file" do
+ it "should let the authority upload a file" do
@ir = info_requests(:fancy_dog_request)
incoming_before = @ir.incoming_messages.size
session[:user_id] = @foi_officer_user.id
@@ -1366,7 +1435,7 @@ end
describe RequestController, "when showing JSON version for API" do
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
@@ -1386,12 +1455,12 @@ describe RequestController, "when showing JSON version for API" do
end
describe RequestController, "when doing type ahead searches" do
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages, :comments
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should return nothing for the empty query string" do
get :search_typeahead, :q => ""
response.should render_template('request/_search_ahead.rhtml')
- assigns[:xapian_requests].results.size.should == 0
+ assigns[:xapian_requests].should be_nil
end
it "should return a request matching the given keyword, but not users with a matching description" do
@@ -1409,11 +1478,10 @@ describe RequestController, "when doing type ahead searches" do
assigns[:xapian_requests].results[1][:model].title.should == info_requests(:naughty_chicken_request).title
end
- it "should return partial matches" do
- get :search_typeahead, :q => "chick" # 'chick' for 'chicken'
+ it "should not return matches for short words" do
+ get :search_typeahead, :q => "a"
response.should render_template('request/_search_ahead.rhtml')
- assigns[:xapian_requests].results.size.should == 1
- assigns[:xapian_requests].results[0][:model].title.should == info_requests(:naughty_chicken_request).title
+ assigns[:xapian_requests].should be_nil
end
end
diff --git a/spec/controllers/request_game_controller_spec.rb b/spec/controllers/request_game_controller_spec.rb
index 4883bfdd6..cc0808ef3 100644
--- a/spec/controllers/request_game_controller_spec.rb
+++ b/spec/controllers/request_game_controller_spec.rb
@@ -2,7 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe RequestGameController, "when playing the game" do
- fixtures :info_requests, :info_request_events, :public_bodies, :public_body_translations, :users, :incoming_messages, :raw_emails, :outgoing_messages # all needed as integrating views
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things # all needed as integrating views
before(:each) do
load_raw_emails_data(raw_emails)
end
diff --git a/spec/controllers/track_controller_spec.rb b/spec/controllers/track_controller_spec.rb
index 435d9a0d3..90d13495f 100644
--- a/spec/controllers/track_controller_spec.rb
+++ b/spec/controllers/track_controller_spec.rb
@@ -36,7 +36,7 @@ end
describe TrackController, "when sending alerts for a track" do
integrate_views
- fixtures :comments, :info_requests, :outgoing_messages, :incoming_messages, :raw_emails, :info_request_events, :users, :track_things, :track_things_sent_emails, :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things, :track_things_sent_emails
include LinkToHelper # for main_url
before(:each) do
@@ -58,7 +58,7 @@ describe TrackController, "when sending alerts for a track" do
deliveries.size.should == 1
mail = deliveries[0]
mail.body.should =~ /Alter your subscription/
- mail.to_addrs.to_s.should include(users(:silly_name_user).email)
+ mail.to_addrs.first.to_s.should include(users(:silly_name_user).email)
mail.body =~ /(http:\/\/.*\/c\/(.*))/
mail_url = $1
mail_token = $2
@@ -110,7 +110,7 @@ end
describe TrackController, "when viewing RSS feed for a track" do
integrate_views
- fixtures :info_requests, :outgoing_messages, :incoming_messages, :raw_emails, :info_request_events, :users, :track_things, :comments, :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
@@ -136,7 +136,7 @@ end
describe TrackController, "when viewing JSON version of a track feed" do
integrate_views
- fixtures :info_requests, :outgoing_messages, :incoming_messages, :raw_emails, :info_request_events, :users, :track_things, :comments, :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
diff --git a/spec/controllers/user_controller_spec.rb b/spec/controllers/user_controller_spec.rb
index b4cc0d6e3..cf50bcc7a 100644
--- a/spec/controllers/user_controller_spec.rb
+++ b/spec/controllers/user_controller_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
require 'json'
@@ -7,7 +8,7 @@ require 'json'
describe UserController, "when showing a user" do
integrate_views
- fixtures :users, :outgoing_messages, :incoming_messages, :raw_emails, :info_requests, :info_request_events, :comments, :public_bodies, :public_body_translations
+ fixtures :users, :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -27,6 +28,16 @@ describe UserController, "when showing a user" do
response.should render_template('show')
end
+ it "should distinguish between 'my profile' and 'my requests' for logged in users" do
+ session[:user_id] = users(:bob_smith_user).id
+ get :show, :url_name => "bob_smith", :view => 'requests'
+ response.body.should_not include("Change your password")
+ response.body.should include("Freedom of Information requests")
+ get :show, :url_name => "bob_smith", :view => 'profile'
+ response.body.should include("Change your password")
+ response.body.should_not include("Freedom of Information requests")
+ end
+
it "should assign the user" do
get :show, :url_name => "bob_smith"
assigns[:display_user].should == users(:bob_smith_user)
@@ -260,8 +271,8 @@ describe UserController, "when sending another user a message" do
mail = deliveries[0]
mail.body.should include("Bob Smith has used #{MySociety::Config.get('SITE_NAME')} to send you the message below")
mail.body.should include("Just a test!")
- #mail.to_addrs.to_s.should == users(:silly_name_user).name_and_email # XXX fix some nastiness with quoting name_and_email
- mail.from_addrs.to_s.should == users(:bob_smith_user).name_and_email
+ #mail.to_addrs.first.to_s.should == users(:silly_name_user).name_and_email # XXX fix some nastiness with quoting name_and_email
+ mail.from_addrs.first.to_s.should == users(:bob_smith_user).name_and_email
end
end
diff --git a/spec/fixtures/files/email-folding-example-1.txt b/spec/fixtures/files/email-folding-example-1.txt
new file mode 100644
index 000000000..9d0810a36
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-1.txt
@@ -0,0 +1,32 @@
+Dear Mr Pollard,
+
+Thank you for your email of 26 February. Please find a response attached.
+
+Yours faithfully,
+
+On behalf of James Hall, Chief Executive
+Identity and Passport Service
+
+<<9032 C Pollard final response.doc>>
+
+**********************************************************************
+
+This email and any files transmitted with it are confidential and
+
+intended solely for the use of the individual or entity to whom they
+
+are addressed. If you have received this email in error please notify
+
+the system manager.
+
+This footnote also confirms that this email message has been swept for the
+presence of computer viruses.
+
+**********************************************************************
+
+The original of this email was scanned for viruses by the Government
+Secure Intranet virus scanning service supplied by Cable&Wireless in
+partnership with MessageLabs. (CCTM Certificate Number 2007/11/0032.) On
+leaving the GSi this email was certified virus free.
+Communications via the GSi may be automatically logged, monitored and/or
+recorded for legal purposes.
diff --git a/spec/fixtures/files/email-folding-example-1.txt.expected b/spec/fixtures/files/email-folding-example-1.txt.expected
new file mode 100644
index 000000000..801542288
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-1.txt.expected
@@ -0,0 +1,10 @@
+Dear Mr Pollard,
+
+Thank you for your email of 26 February. Please find a response attached.
+
+Yours faithfully,
+
+On behalf of James Hall, Chief Executive
+Identity and Passport Service
+
+<<9032 C Pollard final response.doc>>
diff --git a/spec/fixtures/files/email-folding-example-10.txt b/spec/fixtures/files/email-folding-example-10.txt
new file mode 100644
index 000000000..0fabb7f9c
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-10.txt
@@ -0,0 +1,52 @@
+Please note: it is not possible to reply to this email. To contact the
+Department of Health, please visit the 'Contact us' page on the
+Department’s website.
+
+-----------------------------------------------------------------------------------------
+
+ Apologies that you were not able to read our previous response of 4
+ October. Please find the text of that email below.
+
+Our ref: DE00000642471
+
+Dear Ms Peters Rock,
+
+You requested your correspondence to be treated under the Freedom of
+Information Act.  However, as your correspondence asked for general
+information, rather than requesting recorded information or documentation,
+I should advise you that on this occasion we have not considered your
+correspondence under the provisions of the Act.
+
+I am sorry I cannot be more helpful.
+
+Yours sincerely,
+Simon Dove
+Customer Service Centre
+Department of Health
+
+
+
+
+-------------------------------------------------------------------------------------------------------------------------
+
+
+Please do not reply to this email. To contact the Department of Health,
+please visit the 'Contact us' page on the Department’s website, where you
+can also view our performance against quarterly service targets.
+
+
+- - Disclaimer - -
+This e-mail and any files transmitted with it are confidential. If you are
+not the intended recipient, any reading, printing, storage, disclosure,
+copying or any other action taken in respect of this e-mail is prohibited
+and may be unlawful. If you are not the intended recipient, please notify
+the sender immediately by using the reply function and then permanently
+delete what you have received.
+
+Incoming and outgoing e-mail messages are routinely monitored for
+compliance with the Department of Health's policy on the use of electronic
+communications. For more information on the Department of Health's e-mail
+policy click here http://www.dh.gov.uk/terms
+
+The original of this email was scanned for viruses by the Government Secure Intranet virus scanning service supplied by Cable&Wireless Worldwide in partnership with MessageLabs. (CCTM Certificate Number 2009/09/0052.) On leaving the GSi this email was certified virus free.
+Communications via the GSi may be automatically logged, monitored and/or recorded for legal purposes.
diff --git a/spec/fixtures/files/email-folding-example-10.txt.expected b/spec/fixtures/files/email-folding-example-10.txt.expected
new file mode 100644
index 000000000..e4f704c0e
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-10.txt.expected
@@ -0,0 +1,25 @@
+Please note: it is not possible to reply to this email. To contact the
+Department of Health, please visit the 'Contact us' page on the
+Department’s website.
+
+-----------------------------------------------------------------------------------------
+
+ Apologies that you were not able to read our previous response of 4
+ October. Please find the text of that email below.
+
+Our ref: DE00000642471
+
+Dear Ms Peters Rock,
+
+You requested your correspondence to be treated under the Freedom of
+Information Act.  However, as your correspondence asked for general
+information, rather than requesting recorded information or documentation,
+I should advise you that on this occasion we have not considered your
+correspondence under the provisions of the Act.
+
+I am sorry I cannot be more helpful.
+
+Yours sincerely,
+Simon Dove
+Customer Service Centre
+Department of Health
diff --git a/spec/fixtures/files/email-folding-example-2.txt b/spec/fixtures/files/email-folding-example-2.txt
new file mode 100644
index 000000000..13dd39a69
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-2.txt
@@ -0,0 +1,7 @@
+Preface to the message which we are not interested in
+
+-----------------------------------------------------------------------------------------
+Important message about cheese
+-----------------------------------------------------------------------------------------
+
+Actual footer that contains the word confidential
diff --git a/spec/fixtures/files/email-folding-example-2.txt.expected b/spec/fixtures/files/email-folding-example-2.txt.expected
new file mode 100644
index 000000000..e52fbe443
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-2.txt.expected
@@ -0,0 +1,4 @@
+Preface to the message which we are not interested in
+
+-----------------------------------------------------------------------------------------
+Important message about cheese
diff --git a/spec/fixtures/files/email-folding-example-3.txt b/spec/fixtures/files/email-folding-example-3.txt
new file mode 100644
index 000000000..28a3861f6
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-3.txt
@@ -0,0 +1,18 @@
+Reference : T3241/8
+
+Thank you for your e-mail enquiry of 12th February.
+
+A reply is attached.
+
+**********************************************************************
+This email and any files transmitted with it are private and intended
+solely for the use of the individual or entity to whom they are addressed.
+If you have received this email in error please return it to the address
+it came from telling them it is not for you and then delete it from your system.
+
+This email message has been swept for computer viruses.
+
+**********************************************************************
+
+The original of this email was scanned for viruses by the Government Secure Intranet virus scanning service supplied by Cable&Wireless in partnership with MessageLabs. (CCTM Certificate Number 2007/11/0032.) On leaving the GSi this email was certified virus free.
+Communications via the GSi may be automatically logged, monitored and/or recorded for legal purposes.
diff --git a/spec/fixtures/files/email-folding-example-3.txt.expected b/spec/fixtures/files/email-folding-example-3.txt.expected
new file mode 100644
index 000000000..e2cca4933
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-3.txt.expected
@@ -0,0 +1,5 @@
+Reference : T3241/8
+
+Thank you for your e-mail enquiry of 12th February.
+
+A reply is attached.
diff --git a/spec/fixtures/files/email-folding-example-4.txt b/spec/fixtures/files/email-folding-example-4.txt
new file mode 100644
index 000000000..63b94a35c
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-4.txt
@@ -0,0 +1,37 @@
+<<Freedom of Information request - Contracts or options with Kimberley
+Developments or Waitrose>>
+
+Arthur Pritchard
+Property & Assets Manager
+Tel: 01625 504234
+Fax: 01625 504268
+e-mail: [1][email address]
+
+***********************************************************************************
+The information in this Email and any attachments is personal to the
+sender and the views of the author may not necessarily reflect those
+of Macclesfield Borough Council. The information is strictly confidential
+and is intended only for the named person or organisation to whom it is
+addressed as it may contain privileged and confidential information. If
+you are not the intended recipient do not copy, distribute or use this
+Email, and please notify the sender. Please note that we cannot
+guarantee that this message or any attachment is virus free or has not
+been intercepted and amended.
+***********************************************************************************
+
+Disclaimer
+
+--------------------------------------------------------------------------
+
+This email message has been scanned for viruses by Mimecast.
+Mimecast delivers a complete managed email solution from a single web
+based platform.
+For more information please visit [2]http://www.mimecast.com
+
+--------------------------------------------------------------------------
+
+References
+
+Visible links
+1. mailto:[email address]
+2. http://www.mimecast.com/
diff --git a/spec/fixtures/files/email-folding-example-4.txt.expected b/spec/fixtures/files/email-folding-example-4.txt.expected
new file mode 100644
index 000000000..42334a290
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-4.txt.expected
@@ -0,0 +1,15 @@
+<<Freedom of Information request - Contracts or options with Kimberley
+Developments or Waitrose>>
+
+Arthur Pritchard
+Property & Assets Manager
+Tel: 01625 504234
+Fax: 01625 504268
+e-mail: [1][email address]
+FOLDED_QUOTED_SECTION
+FOLDED_QUOTED_SECTION
+References
+
+Visible links
+1. mailto:[email address]
+2. http://www.mimecast.com/
diff --git a/spec/fixtures/files/email-folding-example-5.txt b/spec/fixtures/files/email-folding-example-5.txt
new file mode 100644
index 000000000..3d0964722
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-5.txt
@@ -0,0 +1,35 @@
+Hi Simon
+
+My apologies for timescale of response. The data forwarded is a public
+register, and is updated on a frequent and regular basis; your request
+unfortunately coincided with annual leave and a monthly update of the
+spreadsheet. As the definition of an HMO under the Housing Act 2004
+differs to that under planning legislation, I have forwarded this and
+your original request on to Andy England, Development Control Manager to
+respond independantly.
+
+If I can be of further assistance please contact me
+
+Barry Turnbull
+Environmental Health Co-ordinator (Housing)
+Housing, Health and Community Safety
+
+--
+
+****************************************************************
+Any opinions expressed are not necessarily those of Penwith
+District Council. This e-mail and any attachments, replies
+and forwarded copies are confidential and are strictly for
+the use of named recipient(s) only. If you have received
+it in error you may not make use of it. Please e-mail us,
+including a copy of the message to, [email address].
+Then delete the e-mail and any copies.
+****************************************************************
+
+**********************************************************************
+This footnote confirms that this message, and any
+attachments, have been screened by McAffee
+Webshield for the presence of virus code.
+
+Penwith District Council
+**********************************************************************
diff --git a/spec/fixtures/files/email-folding-example-5.txt.expected b/spec/fixtures/files/email-folding-example-5.txt.expected
new file mode 100644
index 000000000..fbb0f0f50
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-5.txt.expected
@@ -0,0 +1,24 @@
+Hi Simon
+
+My apologies for timescale of response. The data forwarded is a public
+register, and is updated on a frequent and regular basis; your request
+unfortunately coincided with annual leave and a monthly update of the
+spreadsheet. As the definition of an HMO under the Housing Act 2004
+differs to that under planning legislation, I have forwarded this and
+your original request on to Andy England, Development Control Manager to
+respond independantly.
+
+If I can be of further assistance please contact me
+
+Barry Turnbull
+Environmental Health Co-ordinator (Housing)
+Housing, Health and Community Safety
+
+--
+FOLDED_QUOTED_SECTION
+This footnote confirms that this message, and any
+attachments, have been screened by McAffee
+Webshield for the presence of virus code.
+
+Penwith District Council
+**********************************************************************
diff --git a/spec/fixtures/files/email-folding-example-6.txt b/spec/fixtures/files/email-folding-example-6.txt
new file mode 100644
index 000000000..272d6c9da
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-6.txt
@@ -0,0 +1,30 @@
+Dear Mr. Brown,
+
+Please find attached a reply to your FOI request.
+
+Yours ever,
+
+Adetokunbo Ighodaro
+
+<<FOI 0169-08 final.doc>>
+
+***********************************************************************************
+Visit [1]http://www.fco.gov.uk for British foreign policy news and travel
+advice; and [2]http://www.i-uk.com - the essential guide to the UK.
+
+We keep and use information in line with the Data Protection Act 1998. We
+may release this personal information to other UK government departments
+and public authorities.
+
+Please note that all messages sent and received by members of the Foreign
+& Commonwealth Office and its
+missions overseas may be monitored centrally. This is done to ensure the
+integrity of the system.
+
+***********************************************************************************
+
+References
+
+Visible links
+1. http://www.fco.gov.uk/
+2. http://www.i-uk.com/
diff --git a/spec/fixtures/files/email-folding-example-6.txt.expected b/spec/fixtures/files/email-folding-example-6.txt.expected
new file mode 100644
index 000000000..58021ce12
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-6.txt.expected
@@ -0,0 +1,15 @@
+Dear Mr. Brown,
+
+Please find attached a reply to your FOI request.
+
+Yours ever,
+
+Adetokunbo Ighodaro
+
+<<FOI 0169-08 final.doc>>
+FOLDED_QUOTED_SECTION
+References
+
+Visible links
+1. http://www.fco.gov.uk/
+2. http://www.i-uk.com/ \ No newline at end of file
diff --git a/spec/fixtures/files/email-folding-example-7.txt b/spec/fixtures/files/email-folding-example-7.txt
new file mode 100644
index 000000000..e10fe4657
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-7.txt
@@ -0,0 +1,30 @@
+Mr Hearne,
+Please see attached our response to your request dated 06 March 2008.
+Kind Regards,
+Linda Dempsey
+
+Information Assistant DP/FOI
+Data Protection/Information Security
+Professional Standards
+Leicestershire Constabulary
+http://www.leics.police.uk
+mailto [Leicestershire Constabulary request email]
+Telephone +44 (0) 116 2222222
+Extn 5221 VM No. 8035
+Fax + 44 (0) 116 2485217
+
+<<0001_00035908_Resp_12RESPONSE LETTER_20080408_112311_01.TIF>>
+
+**********
+
+Internet email is not to be treated as a secure means of communication.
+
+Leicestershire Constabulary monitors all internet email activity and content.
+
+This communication is intended for the addressee(s) only. Please notify the sender if received in error. Unauthorised use or disclosure of the content may be unlawful. Opinions expressed in this document may not be official policy.
+
+Thank you for your co-operation.
+
+© Leicestershire Constabulary
+
+**********
diff --git a/spec/fixtures/files/email-folding-example-7.txt.expected b/spec/fixtures/files/email-folding-example-7.txt.expected
new file mode 100644
index 000000000..0ef8fd82b
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-7.txt.expected
@@ -0,0 +1,16 @@
+Mr Hearne,
+Please see attached our response to your request dated 06 March 2008.
+Kind Regards,
+Linda Dempsey
+
+Information Assistant DP/FOI
+Data Protection/Information Security
+Professional Standards
+Leicestershire Constabulary
+http://www.leics.police.uk
+mailto [Leicestershire Constabulary request email]
+Telephone +44 (0) 116 2222222
+Extn 5221 VM No. 8035
+Fax + 44 (0) 116 2485217
+
+<<0001_00035908_Resp_12RESPONSE LETTER_20080408_112311_01.TIF>> \ No newline at end of file
diff --git a/spec/fixtures/files/email-folding-example-8.txt b/spec/fixtures/files/email-folding-example-8.txt
new file mode 100644
index 000000000..c1899e7c8
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-8.txt
@@ -0,0 +1,18 @@
+I will be out of the office starting 11/04/2008 and will not return until
+22/04/2008.
+
+I will respond to your message when I return. If you have any urgent
+queries please ring 02085419088 for Legal Business Support queries or
+contact Eileen Perren for FOI or DP queries
+
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+This email and any attachments with it are intended for the addressee only.
+It may be confidential and may be the subject of legal and/or professional privilege.
+If you have received this email in error please notify the sender or [email address]
+The content may be personal or contain personal opinions and cannot be taken as an expression of the County Council's position.
+Surrey County Council reserves the right to monitor all incoming and outgoing mail.
+Whilst every care has been taken to check this outgoing e-mail for viruses, it is your responsibility to carry out any checks upon receipt.
+
+Visit the Surrey County Council website - http://www.surreycc.gov.uk
+
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
diff --git a/spec/fixtures/files/email-folding-example-8.txt.expected b/spec/fixtures/files/email-folding-example-8.txt.expected
new file mode 100644
index 000000000..b5dc10c0d
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-8.txt.expected
@@ -0,0 +1,6 @@
+I will be out of the office starting 11/04/2008 and will not return until
+22/04/2008.
+
+I will respond to your message when I return. If you have any urgent
+queries please ring 02085419088 for Legal Business Support queries or
+contact Eileen Perren for FOI or DP queries \ No newline at end of file
diff --git a/spec/fixtures/files/email-folding-example-9.txt b/spec/fixtures/files/email-folding-example-9.txt
new file mode 100644
index 000000000..1f3d4c34a
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-9.txt
@@ -0,0 +1,29 @@
+Dear Mr Cross
+
+Freedom of Information Request Reference No: 2008040590
+
+Yours sincerely
+
+MICHAEL HEGARTY
+
+FOI Officer
+
+**********************************************************************************************
+Please Note: Incoming and Outgoing E-mail messages are routinely monitored
+for compliance with our policy on the use of electronic communications.
+
+Interested in Occupational Health & Safety information?
+Please visit the HSE website at the following address to keep yourself up
+to date
+
+www.hse.gov.uk
+
+Or contact HSE Infoline on 0845 345 0055 or email [HSE request email]
+
+**********************************************************************************************
+The original of this email was scanned for viruses by the Government
+Secure Intranet virus scanning service supplied by Cable&Wireless in
+partnership with MessageLabs. (CCTM Certificate Number 2007/11/0032.) On
+leaving the GSi this email was certified virus free.
+Communications via the GSi may be automatically logged, monitored and/or
+recorded for legal purposes.
diff --git a/spec/fixtures/files/email-folding-example-9.txt.expected b/spec/fixtures/files/email-folding-example-9.txt.expected
new file mode 100644
index 000000000..2d2381a34
--- /dev/null
+++ b/spec/fixtures/files/email-folding-example-9.txt.expected
@@ -0,0 +1,9 @@
+Dear Mr Cross
+
+Freedom of Information Request Reference No: 2008040590
+
+Yours sincerely
+
+MICHAEL HEGARTY
+
+FOI Officer
diff --git a/spec/fixtures/files/iso8859_2_email.html b/spec/fixtures/files/iso8859_2_email.html
new file mode 100644
index 000000000..c7384a831
--- /dev/null
+++ b/spec/fixtures/files/iso8859_2_email.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<style><!--
+.hmmessage P
+{
+margin:0px;
+padding:0px
+}
+body.hmmessage
+{
+font-size: 10pt;
+font-family:Tahoma
+}
+--></style>
+</head>
+<body class='hmmessage'><div dir='ltr'>
+<div>Faleminderit per kerkesen tënde.</div> </div></body>
+</html>
diff --git a/spec/fixtures/files/iso8859_2_raw_email.email b/spec/fixtures/files/iso8859_2_raw_email.email
new file mode 100644
index 000000000..2ac3b2533
--- /dev/null
+++ b/spec/fixtures/files/iso8859_2_raw_email.email
@@ -0,0 +1,50 @@
+From: EMAIL_FROM
+To: FOI Person <EMAIL_TO>
+Bcc:
+Reply-To:
+In-Reply-To: <471f1eae5d1cb_7347..fdbe67386163@cat.tmail>
+Content-Type: multipart/alternative;
+ boundary="_d47fc84f-c9cd-4fb3-ab16-15de158c8eef_"
+Subject: =?iso-8859-2?Q?RE:_Freedo?= =?iso-8859-2?Q?m_of_Infor?=
+ =?iso-8859-2?Q?mation_req?= =?iso-8859-2?Q?uest_-_Sas?=
+ =?iso-8859-2?Q?ia_e_pulav?= =?iso-8859-2?Q?e_t=EB_impor?=
+ =?iso-8859-2?Q?tuara_gjat?= =?iso-8859-2?Q?=EB_vitit_20?= =?iso-8859-2?Q?10?=
+Date: Fri, 30 Sep 2011 11:06:39 +0200
+
+--_d47fc84f-c9cd-4fb3-ab16-15de158c8eef_
+Content-Type: text/plain; charset="iso-8859-2"
+Content-Transfer-Encoding: quoted-printable
+
+
+I nderuari Besnik=2C=20
+Faleminderit per kerkesen t=EBnde.Numri i puleve te importuara ne vitin 201=
+0 ka qene 5 milion e treqind mije sosh (me numra 3=2C500=2C000)
+
+
+--_d47fc84f-c9cd-4fb3-ab16-15de158c8eef_
+Content-Type: text/html; charset="iso-8859-2"
+Content-Transfer-Encoding: quoted-printable
+
+<html>
+<head>
+<style><!--
+.hmmessage P
+{
+margin:0px=3B
+padding:0px
+}
+body.hmmessage
+{
+font-size: 10pt=3B
+font-family:Tahoma
+}
+--></style>
+</head>
+<body class=3D'hmmessage'><div dir=3D'ltr'>
+<div>I nderuari Besnik=2C&nbsp=3B</div><div><br></div><div>Faleminderit per=
+ kerkesen t=EBnde.</div>Numri i puleve te importuara ne vitin 2010 ka qene =
+5 milion e treqind mije sosh (me numra 3=2C500=2C000)<br><br></body>
+</html>=
+
+--_d47fc84f-c9cd-4fb3-ab16-15de158c8eef_--
+
diff --git a/spec/fixtures/files/quoted-subject-iso8859-1.email b/spec/fixtures/files/quoted-subject-iso8859-1.email
new file mode 100644
index 000000000..6ada69905
--- /dev/null
+++ b/spec/fixtures/files/quoted-subject-iso8859-1.email
@@ -0,0 +1,462 @@
+From: =?iso-8859-1?Q?Coordena=E7=E3o_de_Relacionamento=2C_Pesquisa_e_Informa=E7?=
+ =?iso-8859-1?Q?=E3o/CEDI?= <geraldinequango@localhost>
+To: FOI Person <EMAIL_TO>
+MIME-Version: 1.0
+Content-Type: multipart/related;
+ type="multipart/alternative";
+ boundary="----_=_NextPart_001_01CCB66F.F38B15FC"
+Subject: =?iso-8859-1?Q?C=E2mara_Responde=3A__Banco_de_ideias?=
+Date: Fri, 9 Dec 2011 10:42:02 -0200
+
+This is a multi-part message in MIME format.
+
+------_=_NextPart_001_01CCB66F.F38B15FC
+Content-Type: multipart/alternative;
+ boundary="----_=_NextPart_002_01CCB66F.F38B15FC"
+
+
+------_=_NextPart_002_01CCB66F.F38B15FC
+Content-Type: text/plain;
+ charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+=20
+
+Senhor Benedito,
+
+=20
+
+O Centro de Documenta=E7=E3o e Informa=E7=E3o (Cedi) da C=E2mara dos =
+Deputados agradece o seu contato.
+
+=20
+
+Em aten=E7=E3o ao solicitado, informamos que a C=E2mara dos Deputados, =
+por iniciativa da Comiss=E3o de Legisla=E7=E3o Participativa - CLP, =
+criou um "Banco de Id=E9ias" com o objetivo de registrar e reunir =
+id=E9ias de interesse da popula=E7=E3o. As sugest=F5es s=E3o organizadas =
+por temas e ficam =E0s disposi=E7=E3o para consulta de entidades da =
+sociedade civil e parlamentares, que poder=E3o adot=E1-las, =
+aprimorando-as ou n=E3o, para serem transformadas em sugest=E3o de =
+iniciativa legislativa, no caso das entidades da sociedade civil, ou em =
+proposi=E7=E3o legislativa, no caso dos parlamentares. Cabe ressaltar =
+que a Comiss=E3o reserva-se o direito de editar ou resumir os textos =
+recebidos.
+
+A seguir, o endere=E7o eletr=F4nico do Banco de Id=E9ias:=20
+
+=20
+
+http://www2.camara.gov.br/atividade-legislativa/comissoes/comissoes-perma=
+nentes/clp/banideias.htm/banco-de-ideias
+
+=20
+
+Atenciosamente,
+*****************************************************
+Coordena=E7=E3o de Relacionamento, Pesquisa e Informa=E7=E3o - Corpi
+Centro de Documenta=E7=E3o e Informa=E7=E3o - Cedi
+C=E2mara dos Deputados - Anexo II
+Pra=E7a dos Tr=EAs Poderes - Bras=EDlia - DF=20
+70160-900=20
+Tel.: 0-XX-61- 3216-5777; fax: 0-XX-61- 3216-5757=20
+informa.cedi@camara.gov.br <mailto:informa.cedi@camara.gov.br>=20
+*****************************************************
+
+mbb
+
+=20
+
+Solicita=E7=E3o:=20
+
+=20
+
+ Prezado(a) C=E2mara dos Deputados,
+
+ =20
+
+ Gostaria de sugerir que o sal=E1rio de quem trabalha com pol=EDtica
+
+ seja vinculado ao sal=E1rio m=EDnimo.
+
+ =20
+
+ Atenciosamente,
+
+ Benedito P.B.Neto
+
+ =20
+
+ -------------------------------------------------------------------
+
+ =20
+
+ Por favor use esse endere=E7o de email em todas as repostas para =
+este
+
+ pedido:
+
+ leideacesso+request-120-9702221c@queremossaber.org.br
+
+ =20
+
+ Caso este email - informa.cedi@camara.gov.br - seja o endere=E7o
+
+ errado para fazer acesso a informa=E7=E3o por favor nos contate e
+
+ aponte o endere=E7o correto atrav=E9s desse formul=E1rio:
+
+ http://queremossaber.org.br/pt/help/contact
+
+ =20
+
+ Aviso: Esta mensagem e todas as respostas ser=E3o publicadas na
+
+ internet. Leia sobre nossa pol=EDtica de privacidade:
+
+ http://queremossaber.org.br/pt/help/officers
+
+ =20
+
+ Caso voc=EA ache esse servi=E7o =FAtil, por favor entre em contato.
+
+ =20
+
+ =20
+
+ -------------------------------------------------------------------
+
+
+------_=_NextPart_002_01CCB66F.F38B15FC
+Content-Type: text/html;
+ charset="iso-8859-1"
+Content-Transfer-Encoding: quoted-printable
+
+<html xmlns:v=3D"urn:schemas-microsoft-com:vml" =
+xmlns:o=3D"urn:schemas-microsoft-com:office:office" =
+xmlns:w=3D"urn:schemas-microsoft-com:office:word" =
+xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml" =
+xmlns=3D"http://www.w3.org/TR/REC-html40"><head><meta =
+http-equiv=3DContent-Type content=3D"text/html; =
+charset=3Diso-8859-1"><meta name=3DGenerator content=3D"Microsoft Word =
+14 (filtered medium)"><!--[if !mso]><style>v\:* =
+{behavior:url(#default#VML);}
+o\:* {behavior:url(#default#VML);}
+w\:* {behavior:url(#default#VML);}
+.shape {behavior:url(#default#VML);}
+</style><![endif]--><style><!--
+/* Font Definitions */
+@font-face
+ {font-family:Calibri;
+ panose-1:2 15 5 2 2 2 4 3 2 4;}
+@font-face
+ {font-family:Tahoma;
+ panose-1:2 11 6 4 3 5 4 4 2 4;}
+/* Style Definitions */
+p.MsoNormal, li.MsoNormal, div.MsoNormal
+ {margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:11.0pt;
+ font-family:"Calibri","sans-serif";
+ mso-fareast-language:EN-US;}
+a:link, span.MsoHyperlink
+ {mso-style-priority:99;
+ color:blue;
+ text-decoration:underline;}
+a:visited, span.MsoHyperlinkFollowed
+ {mso-style-priority:99;
+ color:purple;
+ text-decoration:underline;}
+p.MsoPlainText, li.MsoPlainText, div.MsoPlainText
+ {mso-style-priority:99;
+ mso-style-link:"Texto sem Formata=E7=E3o Char";
+ margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:11.0pt;
+ font-family:"Calibri","sans-serif";
+ mso-fareast-language:EN-US;}
+p.MsoAcetate, li.MsoAcetate, div.MsoAcetate
+ {mso-style-priority:99;
+ mso-style-link:"Texto de bal=E3o Char";
+ margin:0cm;
+ margin-bottom:.0001pt;
+ font-size:8.0pt;
+ font-family:"Tahoma","sans-serif";
+ mso-fareast-language:EN-US;}
+span.TextosemFormataoChar
+ {mso-style-name:"Texto sem Formata=E7=E3o Char";
+ mso-style-priority:99;
+ mso-style-link:"Texto sem Formata=E7=E3o";
+ font-family:"Calibri","sans-serif";}
+span.TextodebaloChar
+ {mso-style-name:"Texto de bal=E3o Char";
+ mso-style-priority:99;
+ mso-style-link:"Texto de bal=E3o";
+ font-family:"Tahoma","sans-serif";}
+.MsoChpDefault
+ {mso-style-type:export-only;
+ font-family:"Calibri","sans-serif";
+ mso-fareast-language:EN-US;}
+@page WordSection1
+ {size:612.0pt 792.0pt;
+ margin:70.85pt 3.0cm 70.85pt 3.0cm;}
+div.WordSection1
+ {page:WordSection1;}
+--></style><!--[if gte mso 9]><xml>
+<o:shapedefaults v:ext=3D"edit" spidmax=3D"1026" />
+</xml><![endif]--><!--[if gte mso 9]><xml>
+<o:shapelayout v:ext=3D"edit">
+<o:idmap v:ext=3D"edit" data=3D"1" />
+</o:shapelayout></xml><![endif]--></head><body lang=3DPT-BR link=3Dblue =
+vlink=3Dpurple><div class=3DWordSection1><p class=3DMsoNormal><span =
+style=3D'mso-fareast-language:PT-BR'><img width=3D566 height=3D58 =
+id=3D"Imagem_x0020_1" src=3D"cid:image001.png@01CCB65F.2FD5C970" =
+alt=3D"Descri=E7=E3o: Logo Corpi_html_1cd70d8a"><o:p></o:p></span></p><p =
+class=3DMsoPlainText> <o:p></o:p></p><p class=3DMsoPlainText>Senhor =
+Benedito,<o:p></o:p></p><p class=3DMsoPlainText><o:p>&nbsp;</o:p></p><p =
+class=3DMsoPlainText>O Centro de Documenta=E7=E3o e Informa=E7=E3o =
+(Cedi) da C=E2mara dos Deputados agradece o seu =
+contato.<o:p></o:p></p><p class=3DMsoPlainText><o:p>&nbsp;</o:p></p><p =
+class=3DMsoPlainText>Em aten=E7=E3o ao solicitado, informamos que a =
+C=E2mara dos Deputados, por iniciativa da Comiss=E3o de Legisla=E7=E3o =
+Participativa &#8211; CLP, criou um &#8220;Banco de Id=E9ias&#8221; com =
+o objetivo de registrar e reunir id=E9ias de interesse da popula=E7=E3o. =
+As sugest=F5es s=E3o organizadas por temas e ficam =E0s disposi=E7=E3o =
+para consulta de entidades da sociedade civil e parlamentares, que =
+poder=E3o adot=E1-las, aprimorando-as ou n=E3o, para serem transformadas =
+em sugest=E3o de iniciativa legislativa, no caso das entidades da =
+sociedade civil, ou em proposi=E7=E3o legislativa, no caso dos =
+parlamentares. Cabe ressaltar que a Comiss=E3o reserva-se o direito de =
+editar ou resumir os textos recebidos.<o:p></o:p></p><p =
+class=3DMsoPlainText>A seguir, o endere=E7o eletr=F4nico do Banco de =
+Id=E9ias: <o:p></o:p></p><p class=3DMsoPlainText><o:p>&nbsp;</o:p></p><p =
+class=3DMsoPlainText>http://www2.camara.gov.br/atividade-legislativa/comi=
+ssoes/comissoes-permanentes/clp/banideias.htm/banco-de-ideias<o:p></o:p><=
+/p><p class=3DMsoPlainText><o:p>&nbsp;</o:p></p><p =
+class=3DMsoNormal><span =
+style=3D'font-size:10.0pt;font-family:"Arial","sans-serif";mso-fareast-la=
+nguage:PT-BR'>Atenciosamente,<br>****************************************=
+*************<br>Coordena=E7=E3o de Relacionamento, Pesquisa e =
+Informa=E7=E3o &#8211; Corpi<br>Centro de Documenta=E7=E3o e =
+Informa=E7=E3o &#8211; Cedi<br>C=E2mara dos Deputados &#8211; Anexo =
+II<br>Pra=E7a dos Tr=EAs Poderes &#8211; Bras=EDlia &#8211; DF =
+<br>70160-900 <br>Tel.: 0-XX-61- 3216-5777; fax: 0-XX-61- 3216-5757 =
+<br><a href=3D"mailto:informa.cedi@camara.gov.br"><span =
+style=3D'color:windowtext'>informa.cedi@camara.gov.br</span></a><br>*****=
+************************************************<o:p></o:p></span></p><p =
+class=3DMsoNormal><span =
+style=3D'font-size:8.0pt;font-family:"Arial","sans-serif";mso-fareast-lan=
+guage:PT-BR'>mbb<o:p></o:p></span></p><p =
+class=3DMsoPlainText><o:p>&nbsp;</o:p></p><p =
+class=3DMsoPlainText>Solicita=E7=E3o: <o:p></o:p></p><p =
+class=3DMsoPlainText><o:p>&nbsp;</o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 Prezado(a) C=E2mara dos =
+Deputados,<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0 =
+<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0=A0Gostaria de =
+sugerir que o sal=E1rio de quem trabalha com pol=EDtica<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 seja vinculado ao sal=E1rio =
+m=EDnimo.<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0 =
+<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0=A0Atenciosamente,<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 Benedito P.B.Neto<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0=A0-------------------------------------=
+------------------------------<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0=A0Por favor use esse endere=E7o de =
+email em todas as repostas para este<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 pedido:<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 =
+leideacesso+request-120-9702221c@queremossaber.org.br<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0=A0Caso este email - =
+informa.cedi@camara.gov.br - seja o endere=E7o<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 errado para fazer acesso a =
+informa=E7=E3o por favor nos contate e<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 aponte o endere=E7o correto atrav=E9s =
+desse formul=E1rio:<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0 =
+http://queremossaber.org.br/pt/help/contact<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0=A0Aviso: Esta mensagem e todas as =
+respostas ser=E3o publicadas na<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 internet. Leia sobre nossa pol=EDtica =
+de privacidade:<o:p></o:p></p><p class=3DMsoPlainText>=A0=A0=A0=A0 =
+http://queremossaber.org.br/pt/help/officers<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0=A0Caso voc=EA ache esse servi=E7o =
+=FAtil, por favor entre em contato.<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0 <o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0=A0<o:p></o:p></p><p =
+class=3DMsoPlainText>=A0=A0=A0=A0=A0-------------------------------------=
+------------------------------<o:p></o:p></p></div></body></html>
+------_=_NextPart_002_01CCB66F.F38B15FC--
+
+------_=_NextPart_001_01CCB66F.F38B15FC
+Content-Type: image/png;
+ name="image001.png"
+Content-Transfer-Encoding: base64
+Content-ID: <image001.png@01CCB65F.2FD5C970>
+Content-Description: image001.png
+Content-Location: image001.png
+
+iVBORw0KGgoAAAANSUhEUgAAAjYAAAA6CAIAAAAsiYQwAAAjEUlEQVR4nO2dB3gc1bn336nbm7pV
+rWZZkiU3ucmWcQMMGBsbguFiMARM5wMSQhIuaf4INyEkfOAvBG4IJU8uJU5CQlxkXHDFvajYlixZ
+stUsraTVStt3p9wzK1vI2tVom4rx/J71ejRzznvec2b2/OedOXOG5Hkerjd48LjdLp7v6eg4eKil
+utr21NMzdDpVxMt58slDb789O+JmJSQkJK4TyNF2YETgwNhqbbf0dHa7a+vOXjjTve1Ia6PLxtT2
+YOCeV5wql5dEvEye544f/xOAJFESEhISIUKyLEsQxGi7EXm6Ozvr66znu7u6WhpPNeDlZ6qbWxos
+1tgeM9B0N01NpDEdF63p5OpWr1spkw1HC2A0rRgGsxISEhLXC6TFYtFqtTiOj7Yn4dLGgOXc+bNV
+5hMnjp902C4Z7W2tqYzNbOjp6WJcGKVUEuPieZVbaSYZttN2AjxUjjJ+6dLxJZPVo+27hISEhIQf
+BImSyWQKxTV4vs+7zpg7vjrN7tlf4d5daoSoSme6zeHOtVpO82Q6rm/BPZMxeRUpswIFzl3gxFPj
+49MTYm4riC0snlyQNylaFaXRYAqVWN3dYG2zNtW26fft33fvzEnZubkjVj8JCQmJ6xxSLpezLDva
+bgRHp7E1fd1HCS5bS22djSKAYPPxJVWYy8Db1Tx2GtcU8vvLSRIIrhUfn8elFk5z3nrbf86YgSWM
+SyEppbhxj4exOaytra2fNTTtKd/ZRDXWwDmADoqO/WnKwZGpoISEhIQEglQqlWVlZcXFxaPtSRD8
+9jfvWE63OFXj4nQ3UsDlcthBRwtOGLioTlqrydfZp1E5zxWXTJmSmplDavXJIqbcLt7E28w95nZo
+O2w5e7Su63jTkfNdh4B0A0GBigYghEElLH17zApQC5dDOY7DvIxUdSUkJCSuU0iapnmedzgc18q1
+PjvAH76oK9Le2I67bOBKZvFOueu5NSkTycycfENGujo1c5zISEXGbW+zQQN/qb6pvsl5sbazu5xr
+rK6rMrOnAZcDgQvKpNYAXH1zjnGsnbIEvPrUO8BEkigJCQmJ4YakKCo2NtZoNKalpY22MwHxt7+d
+MssID8ZcBK4QCYnjQuGD+W88e69YHgcc7Gw/6yi/dPHSWeeZGkdXC9/e1NIAhAlwEjACaPQdN3h+
+Bhzxy9Nm9v6B9OlbMLpEQkJCYuwjRBtRUVEXzp9PTkwkKGq0/RkS+46NNTnAVAMkA3QDxXa1vLLq
+Md90RmisaTRt9Bw1VXeUmU42e6wWtsftcALuAcwjXL5TorrrAyrTbfn13B/0BmaSOElISEiMGEK/
+G20wnNRoqru782JiRtufQeB58F5Y++wvxz6r3eYm587CPBgQZbaq79wbn5OYIKTh3MAzQCj/q3br
+J9terCjHoAgD5srcGUJ2HGjs8r2lIIp2azxZD0xYFfE6SUhISEiII3TWGEFMUCguVVUxs2eTZG8n
+PsZgu4HUG43wyy3lbld+gZwGYM5xHiC45x54SLh7hMDRSvpM85lf7vq/NhTqzCSBQSu9d4ywy/+C
+hwc393zRwoQ4kcuAEhISEhLDwuV4Ii09/UhtbUxDc1ZG/FiUKFIP4Ppwx66KKnsRHVMHzRQkm2x1
+K1dkp+Xl9E/46xObbW470DSwWIiq1B/OlSYvWDPhobANSUhISEgEzTeXvObOnb/3zOaMlJU8Phan
+RGqub930wYEcJpakeQIYl8eeksz8dNWUKPk3QrSxav+nbZuA5EONmQbAggu/f8pt2fHxkbAWLhj2
+C57/2Wh7ISEhITFyfCNRiUrZzKTJn1V/eu8k0dFxo8TqX/3rQENDpsbAARfFpTaD46lbCqfMLkKb
+XC6XTCazdJne3vemm2kHYoiHcwOC54DBc9NueGLugghYCwwkQt+Uf7Ua9erTWFCpXifDd6N/ZSNi
+cLBSArccqapJSEhEiqsGDmSMyzxTdnZfx76SBSVjanrZ+95648BXddGaNAPYCd5Q7Xbrki8++sLT
+vVuRPqHv9Sff3O2sBFlE9IkH3iOjYt6Z+qNEMiECBgOgf/+Ilgf0rb3L377ec7D6RoQBKighIXHN
+MXBs27Kly35/9Peq88ppmdN7XyU16s+objhy8ONfnsKSFxMYR8I5K9sJxJmvX/mdQejg+F733jZ+
++PqJP4E2PkKX+FAIRXy46jfzE0dBn8BfCNW33LepL0vf1r7l/tl98wayZkgPxX0L0Gb/BANUKpAq
++673TQNXB1LiZoejahISEuHgZ/j1g9kPfv7lVgr0BRkZMFr6xLm9I/TgzW3nvv/8/0QlLUrBPD2A
+WZhxp7kz7z+zJq/IICRjWSDJ4+cOPPX5i2BIjVTZ4DA9n/niPYlFETIYFgN62AHRRt8FwP7LfWlE
+8vZfL2K/bz1c3e8PWD8gr3hEGEiVRbL7VtMvfpXMt6YjXDUJCYmg8CNRKr3qhhuX7NyzW+F2Z43W
+xN44Dx54/69HXt/w70nUJAVut0GjjSPqSetzD9320LrFvakwkvzk4rmndzwO+qQIFcyBTf7g5KW/
+W/RUhAyGi2+g47vVdzmQvIGn6Z848Ktnvd13xHvwkA0O2ZKjXjUJCYkB+H+INcWgWTVv9ptbdy5S
+9sxNmzXCPnmRPf3OV++9/1EGl0tTJAasBTijqf37jyx//albgbcBJrzH/V9njz+/d70JIwCL0KQP
+tu510xb/6ob/jIy1CDHYmX6k8oZj3y/9AzsIVVSG405S+DWNSNUkJCQCZLB5FnBtTMKLa+776L0P
+Ghacvzf7P4bdkStzQ/T+VfjdtypONEZDTBPFZQMcY6mUjoRn7pr7+svLOJbFvckOna1Yt+sn7Xgj
+EHQEbkHxLDg61xYseO2GH+kDnBhpRAi/lxfPO0xdbf+QJbSrYcM0eiJ8s+FXTUJCIkD8SxTvfbJI
+xtseXffQF5tLf3j4Lz9fdadCqeibiCjyeOeGQP//+eOKn7z4ywZdbLRsei7OHgVwOZ286sRr/7Vm
+9ZqFQkJCeO7pwZb3/rzpZWF8BBYJfUIC6SF/nvfqz5asCb8qIeB7DwnG5Bl6gMFHn/8Bxisi9fU7
+AER8TEdoDFPVJCQkwsG/RHEcB8KU3sLFtOW3LU07V7Oh9PObsibEjM/RsIxcqewd5x1BWLfn5Imz
+7/7lq/d3nU6JvSkO74nGuGqOZ60mfYb5r7/+3Xcm65BbgOP17S33nfjtwcqNoE0KY2YjL0JWDlwO
+nSr6g+wnVi6+L1LVCYE+ler70+/6cGyGn2ZAvxygzyJa6zeNeHbfcRBDutc3ZN+3psNXNQkJifDx
+L1EDnoiaPCE7Pln11cGvzBdborTRs2JjWjpa8/PzddERmHa2k4Hj24/8ed+R0s3nHR4iUT7VgbNa
+3sg5OlyE8pGbdY+/9Pzkcd5Z+FjXf1/8x893fHbJVQOa2HCDJ5S72wM2Ys6sWb+d9Mic5Hnh1yVM
+Buvv/K4fbKCEb4c+pLVA+tnBBsQH5XPgCYLdGqB74i0ZqapJSEhEisDm/OaZBGXCykV31ZVVbXac
+LD25a65m8c5N+/F2a1F+8c23JAFcHVSxNvBGYKJ4jtSe++Dfx45Ud9Udwwhrp1KZnk4ZyoCVuVxG
+d/fC4qRXVhWvXDqPoAQnN5858Zfa/7/xUjnLuYBWhn1xjwfGDjLdT6Y99b0Ft+vxMXTzSUJCQkKi
+lwBfS4GjLl2OyfKmTE52pX2kb9l5fmMSHZc/667N/7P7Fz99JTk19abFN5aUJCWPV6k0MYPok8fj
+sNpt3YePwFtf7SQrT5/yWKzGJAOrT6HVemVUPd9sdFd6zA554dT3X/zxPbPiFTIheLL0tK78/Hf7
+3AfcbhOQCiDCucbI984cAQ7L/fqFT9z/f+aQU8OwJjGiSOGLhMT1RmAS1W9It1amfyZX/0juCx8e
+eW/D18/e80hh0Zo3z+0/8oMvvjS/3ZjqcdPMDHYuFsfwE3CMw4Q3NO0GXEZgRDNVc7HGTRgBIzEs
+fyqG6bA5eozHif0nPdOB2B2FkwvTsz/53qLiG2YLJTGe3TV1rx78cHvPP4S3tBMkUENGZiLwwrR7
+LAsYn8rk/X3V94pGZzC9hISEhESgBPNyv34oQPXEzGefyOc2dO56ccOyWXPnvnjDfKWyZM8B07Gd
+5bgZ6hm+3ORwuO1gxYDrAtABkQV4XAI/TsWzaqLerZzcLnM5qZZxcRnfU9hm3Prs0rum6QFvc3Sd
+NFVv2Ltje837TbJ2QZYohXcYYchX9pAyMUjwoihDXtSk+8c/9OjsOaGakpAYSP9nra7nOE9qB4nh
+IESJuowKf0a15JnfLPnnmU2fHP2qprnVMDlh/t3x2nSV0pzgvOjs5rXq8yqcq7hEYZiTUmpoGcZa
+CXURvciSwidGcRnKhUVTFEatsvlizRcnPtnXVrGlvrLFXQm0DHRKgHBeJMgD5wHWA4wmNjZ5Ser8
+J5WF86YuBmyszI3rS6Qmf4tgHxHs8Hff8XIRcWO4CbPFhmkAeoCNP3aGF0oD8SUiTngSdYU78pbd
+kXdrVVNLhfH81srNm3bv7Sb1bKwzPio3LVWlwOkiKtvjpJVqWomzdkJJcJVOtulrjtjuNq3ffrHW
+w3a0XmAIs/AQLkGBMjr0mAkXBneA2wUMlqDOnZmZfot6Vn5mQUncxIjUdPiI1ORvY6GPuLamsAuz
+xfpGtEfIneAYO4/QjW47SHxbCVKimB4gtT4ru4HUIXGYmJyMPndOLW5sa67ounCk48BfT5/4ynIA
+SHILEQ0kDjgOHIEJ86dzPFgFHcJwb1iDgRJ9DzmsjvV+E0KE1Evv/yQPDO+dn8IN1XZKn1k8f+66
+6GlzUufHajQaKhKv5xhmfB/08d06YNOAuXwGTEA34Cng/nkDidUG6/iCivMGPJYknn0wP0WW+0cP
+fp/wHcygeIuJ+CmyIwJZ6Uvg7Rmm80O2hq9xEfeCqnKkrg1IXJ8EKVGExt/Kq0QLx6i0hPHoswwW
+rC8Bh9VR2rzr0wtHzTXnd1TXc4ntvBYHHikTdTlU6r3PhDRLUCDv29wHRlBIfsC7NQ3ADXDJu46/
+/I30aQ8Hc4g56iXzJ86fd9fCJXFR8uBqNaYRj66wq6f99ttZg08/Ih7l+HZegecNrSIhWO7/sO2A
+RhiyLPEWG5BY3KbfxAFWJ5xaizgvYtPvvh6sGcNvh9DqKCHRH38SxbOD3rDxO/uR6JRICrViZc5t
+6AM3I5FxdPRc+rD1rN1hP1txphEaGrAOYB12BuMxmwMIHHM67QxwWD+VQvqkkmswnAcZ30nRNF4W
+R0+i59Lz3Jw7KTVxXuzCuY8qErlkiNBEstcWw/qDH5VbCwHWSLznDce+b6wpYiSQxBEk4rtbpBnH
+cjtIXD/4kyjOGcCDt6FAgCJem/FDbYbwx2QAJ+fAcZujra4bc2DnGoCiiJa6OrPT2f81iizDTCgo
+JOUMpPBclCJWsUIRx8bh+qufjvq269PY+fGPHU+GJGRXB4sjw088WPaxyUi2g4SEX/xJ1PDokx/k
+uAKFWXR8jA6+GbyXOEKFX1uMncsjY8eTIQlnvEmAeYNK7Jcx254j3A4SEn4Z8l5U74CEMGcb6oX1
+jnQINpMTeJd3OMZVcF5IMjIjEkcdkVsp/RnhjkDkdDgQT0LzVnzgQwgMR6NF3OY12sVfo25LXEN8
+08WfONa69fM9u5w2wHGWxwmCxc4AY+JxGYbNAmAEhcm1ee76/rrFmYJimUzG997bb75k3k9SFNos
+QGDCwAakajgLWAqw6UB8DdgUljHGqTY8WrxtW90fD1UzNIUDzgA/HrBk4AjgDwDkAN4BWCaSHp7H
+eCJ7gnNSwe0zi8cBgaxpPE77S6++ddiaQhHQYzv04WPfzZ88Fcfxnp6esrKyadOmWq226urq8ePH
+YxiWlJRkNps7OjqMRmN0dBRBkCjZxIkT1Wr1qVOn9Ho9SoZ8Rem7urqmTZvW2dlZU1NjMBgKCgpG
+ZR/00qdSfX+Krx/Sjt+UgVgbcM88qLy9+E0jkt3vpsHcCISgGm1Aiw3IG6CTIZQe7J4NhAjaDL8d
+IuuPxPWJIFE95q4XX/h/724pB908wDQ4WNOwS/UwEQjPRJyosjrgC++booDYA+XvfHHPc9+55Y03
+HnS73Qf37flnVTZQJwGKvcp0GmCcMJEEtCIJGwf5rVDOQ9sOZnZCUoXpzhsry5nt/3KCEulZBUDK
+HrAAjPe6ge0Hkww6ScjqBoqFfbCJA9v2yVOK/rHh8Yx0hZMj39hvY5v+DeQ0MNn2P8LkA7S3t5eW
+lmZkZGzduqO+vmbGjBmxsXFr1tz3wgvfP3mybOHCBbt3750/f/6hQ18vW7bs5Zd/snr13RZLz549
+e5YvX56env7aa6899NBD27dv37lz18qVdzAMM6o7QkCkLxNfOdj4Pb95A+kmBtwGDzzvkGIQ1Ca/
+bgSy7Ndg4InFRUXclLiFoJKFUOvA/QnNeN/6ANtBZL2ERCAIEvXpriN/31Y7L3HBMdCSvMfqsOtY
+Qxw0tbP6KnBMhVN2KMRJppWOcuFF8RTzQdmRHzXdRispmYwcr+AS6JIuzqZw2Xk2vgZkTmjJhppW
+mB4HHVbQWSGbZ20xtD06pgePpaZocTlFV0NRjMfhYZouQA2APh1YK1Dt4AI4mwAxVjong0zUaxrL
+aw8/9rxh28ZHSQKTy2ilojCVGnfcWV3sdd1s7omNjUfK5HA4/vjH2paWlubmlscee+zQoUNmsy03
+N7ekpC0vb+KZM6f37duXnp7V2WmeN69YLpejGAtlz8zMnDdv3v79XyNxamlBKQdeS5SQ8EUaDtCL
+1A4SI4PwBtv9NV1WVTIPejd/MccRPftuxURtHMcxOCShWIWFFAfOnKl1nTxqNLNkN045HVBR0TV3
+rrYV4tuBNHAOULcvv60wJo5gXCqSNLqZDBLXsEBgnJEis1wuT3x8ulqt5Si2Dqez+XqKyOSnKOZk
+FS1wqyiy0+6Ji6JMbteEC+Bxy03E7todXY4sUpevnvTVoTM2t4uicRVoDKDuBg8Ik+4Jz0mlpCS1
+traUln5JUQSKilQqNU2TCoXigQfWvv766ygBTcs8Hndubt4NN5S8+eYb06dOPH78ZHv7pVtumYK2
+qtXqzZu3pKamzJkzOzU1qaOja3T3hMTYR7r10ovUDhIjBolxQBtxnECdPsY5L6TdWvzOz28A8L5C
+ULh2d3mgREer/bbHf1V1+GA6JecJS0NjU45iCkckZYPVwzmVGvvj3711XPoQj8wyTbzWdZZXRBmx
+inWLV76ytoRzA064WZYmCNbKEG6UiPa0fadt5tI/WeO0POhY2T6LoyeaNniAd4Dw9JMV2At8+mQA
+FA+VoECpzRgbG2O1Wu12O1Kd6OholUq1fv16ZAkFWDiOozVoee0DD+oNBqU6iqZnIRlDa9asWeNw
+OJOSEidOnOjN7oArrxtGuYTK83zfoAxM9NkviesEqVPuRWoHiRFDuNBH4leWHfiSe3VX9An6DeRj
+YhKwTR+96Oi2AM8ROBkbH2PpbjaxjPAiKUx+2j7+51v+lZ5OMAyKcmYDHPLmzcDAxEEHwVK3JqdO
+mTEH59lmSEsEFfAKGhfu/eA0+qK97/gl1JeH+1FReclTM+FkV3UqOfE8htttjhhDlBbA4b3H1Qp4
+PN4GgOI8buvWrfX1DU8//YRWq/3007/PnTs7Pz/38OHDlZWV999/f2dnZ1xcnMVirag4uGDBgp4e
+i8lkqqmpOX369KpVq5DCxcfHVVRUVFdXo/Xz589vbcWjopCG0R9//LHBYLjlllu2bNmi0+nQpt27
+d1+8eHHt2rUjt2euffo/KCN1ahIS4XDd/ppIJCnlgjqRLFQCdMVhk/wmQ59YJF46dd+qZkqWRmJb
+AKajOKjb8t9vHwKW9D5D+6VwKU7QORQU5QsPPDm/rF98zx9nAJZL8Lsrj3iKCkDp5jkRt+QLZjCf
+baVJHkU0bW1t6akpnV4nEgDqAGes7ShNQ0MDim/uumvlW2+9qVDoVt99x8lTZb/4xd+XLFm0YsWK
+Tz75BIVTc+fOPXXq1BtvvJGennHgwL59+75+9NF1zz13Y2dnx8MPP/Lss8/GxsYWFBSUlZWhWOqF
+F15AsoSkCGU/fvy42Wyura3NyMjYsuVLvV5z5513vvrqqy+99FKE90A/BgyfG74iRuwQD/OORbDe
++h1RFtkiws84woTTo4XQnhFhZNo28FLGyIjEYbr/N0zdTqR2IomUJN07FK/x8ox5/MAkPILhMQrH
+XP1fAB/rZlvYU6mQRmPkbF7rgFgMyCqIdQIrh+o8yGsApgOYSXxLpSxXXSjcQ4KzDFiKchRYFZhX
+sGJuZXOwHUxOMAKPabUa5AGSPjOSUGEjx6svP99LEER8fLzL5dZoOZ1ej+EIPjMzIyYmZtq0ourq
+s0J9eP6ee+4tLf1Sq1WjGOvYsWPl5WU33njjypXfQTo0c+bMcePGoZipsbEJWWtrM6JviqJmz57d
+3Nzs8XhQrFZbe3b16nvQglqtFnM6PL59s5n1DUcelXJhmJvxmtg7EenRRqY9/ZY4Fhgj5yLD9Gsa
+vm4nUnZQ8AQmYYHLgphmnPS0IyEYODU4z3lwkjpdYTzdZuIpHGf4otR4IkGmw6gLgLs4JkZpXH5T
+XlyCClgWE25hpbHA4t6QiuF5gsdmTssRDAlzmvNtgEVDD+BicxZVIzfw3EsoauJrZRTNeyeNRfLo
+vdnF2zjBQyQYPT09W7ZsQaGSTqdDIkTT1KpVq1BIdOzocYNeidTrwIEDWVlZKpXGYulWq3UorlKr
+NdHRBrvdlpc30eVKM5lMeXl5wgD6g4eeeuqpqqoaggCUq6WlBWW86aabjEbjtGnTKyrKkUoWFxdH
+pNF9GfAzGLB3RU5z/G7yXSn+LLDf7H4HFgdYXAiJfb3yuzXMM74hswdelwFORrAl+58s++YNvKC+
+lNjVc7yKt0DghHAw+PVQfDnYgob0UCSlSGtjVz9WP6QzIe/KYOsbzg4NodsZsqa+x2RQLvlCEt75
+w0uB64YYoAurdh+Em+4cUA+cFCThnxu3/XRLBy/Xk46uV1elrv3RigYMGBSHce5unePpJx9ITKX8
+F/INqLSabJhwAeNkctlgiTjOdmjfkWR6Mo/M87xaq+V7NeoKrHeWChQqzZ8/3263p6amoj+R/CQk
+JHjvMMWbu7qzsjPdHk9TY2NGhjAlIBIhmqY5DlpamiwWS2xsXGZmFoZhvYZRFJWRka5UqlJSUtxu
+149//NIPf/hDZAdtYhiGJMnOzg6TyZydnRVKG4eHyGmO+BmQSMcXSHbM3/ThvifUgxkJKrFfr3wj
+gDDP+IbM7tfnADOG35K+6X3zBlWQeK8R5imzX1MiBwOEep1qsIKGjO1CqKzvnhLX1MF+cb4GfY0H
+dbCFfGSGTMg1jaAPvZCoi/Z4UJnYKWCyZKk7d+6w/iHqjsxsT79HWSmCOGG8tPl4Zb4rq4Kt9Niy
+LLYeHcuqWL4eThZg02h797Yth9MzMnH8jNuT6Z01ibvyeifSyWPNCuaxhfEUhaQlHsfwNI969+fb
+zzV02TyeWQDHhEd/uQZgGgmgz9AO814D2Gw40ECQbkYnl/VKlAbpkLc1DFgXQBJSndOnTzc1XZo5
+05Wdna1UKuvr6xMTE3Ec1xt0n37695iYqFmzirZv3ymX0yUlJX/b+HFMtLzkhpV1dftyc3Pb29vN
+ZrNCoUDSFRcXd/bs2fLy8ptvvtlmsy1YsADpk9HYvmPHzsWLF6Ll6OgY9Ilgu48KQfURvseZ71mk
+SJYhEwfiQGjZQz6vDMTnEH5+wbakeF8QWd+Ggz6B9N0Uzj7tbzP8o8svEW9AkV0Z1ME2TPWNIMN3
+7JEYAYUoXPk3k6EgG/Hjarf7zbf/8jlAg6Az6NM7qI+LxZLdxLTx9BHg8yn10cL8h1sIsJEzUADG
+4PKD3UV7/rADuPeuTDnOeaeFzfZmPwqMKzFBuTR1nXdKPe1hjJ+MJ++saoEzGzN54h8Ak4D7J+j1
+kC/DmCYeK8Rz2mREEc+VOc9OL0yW0QoPzyFLvGAN2ccUYEdltLW1NTc3FxYWIAlhWfbkyZN79+5F
+EVVmZqZFwLRixa1bt27Nyso2Gts2bdrUabIkp2SUl5965plnysrK3n//z/n5OT09PSaT6fHHH//i
+iy8efviRxsbm7m4T2rp8+fJ3333nySefLC0tnTdvXlpa2jDtgFEnqOM+qNPhcM6dhzQrgvjvWTz7
+MPkc2YJGuKsSkfwBnvSPGHwTh8ZgNgNswLHcrY+FX9PYRxh0vuyW4rdLd1nLq1Px8RyOA8Zd4rFc
+1BqgBnB69YZngOMZo5UdDww7ZVrykqXj8S6LzN4KrvyjXIcgZrzG+4INbAJw5wQhQbnM8cKbdHOs
+HO7gPW4cGMYOzp5MvNwCOdnCAPLpLArdAG3ldBiYeVMqYJkYdLNcqhu3EF3T9d2/f+1ZUkY6bC5X
+l9vtMhlZClwec7cFvE8vNTU1O51Og8Gg0Wg8Hs/MmbN6err37v160aKSo0ePlZVVymQKuVy+aNGi
+9evXu1yu1tbW1atX33vvvRs2bFAqaRQwIdWsrj53/PiJnJychAQkdUxFRdm5c+fq6uqQzejoaBRj
+oYyjvZuGkcD7kaD6nQh2UgMI0OZglx1Esg+fz5EtaISjJZHixAPESF3z8bUZeAOOkcjSlxH7NfVJ
+2phtCnEEicpMSNn2yvqNG3fsudQY1W0Gq47ArDywnZDkgjpUNRno9JxDrVbRCdH/QU747sNrddGa
+7s62oiJDXEyNjAAGk7MYxWM8zVp6yJibGCQ/mIUgSY5xEPFxrktUvN4lN0yYcGlJSbeHnmgn9TJO
+xoGdwWQKtstGxiaxXTypUoCNF6RxXJzONkVXsGLVggkTxgle8thNaZrGqXYt69b2JEcZhKdxFQpF
+fv4klUqJQigMwxISErq7LUuXLn3ttV/r9Ya1ax9AqlNRUXHhQv3Ro0eefvrpysrKCRMmIJVCUdHR
+o0fvvPPO/fv3z549a+rU6Z2dHUiu3n33XaVSOXPmzAceeOCjjz4qKirasmVLbGxs77Szw4rvvQoY
+hkNK5PwrIiVG1u0Aw51wjIeQfcRkbCQ9iVRj9i0MdrIfQmg7pM2gPAwt+2gREbfFzzAi1e1E/BZU
+H5dnOh+fHf+Dl+77ntveYTLZLXoMs+DAd4DTKcybRyGJikYSpVGpxkUrruTUGmJef2GN3G0ieNaD
+I4mieYyQsRYradB5jCjI6SEJOcc6iRiVx9hDy5Plqgl3x9+6bCbKayNjUEqU3oPJlazJSsYomG6O
+UquRNArPSxlkV7+ySqGmPvrj3RZcr2NcBGNVaAxoZVRU1IIFJVarrXcs+NSpU0EYho6//PLLfaMr
+CgoKMjIyULyF9KykpIQgiLi4OLQVqRTaevfdd6M1SN6yszMnTy4sLS1duXJlb8Y1a9agTWazWavV
+4qKDDwcFibzLGUTy4Gc6F9nk1/KAH3mA2QdLH5TbgZc4mLfhONz/1rdIdr8+h5AxtJYMhOFugTA9
+GfJgGOwIFJGfQAoKPGOYDF8DihcR8pEZrBuBdDsidkLzQZyr3rdE0Mr4BKXwfKxwiQ9SRXNi4IlX
+8qCMunq1QSt8C+8n7Kcycd6VQBMKmhY0TgNXvgRivEu9s7gONpcrFq2Jib7s8DeGVSo1+lx2nrg8
+OwWGQf/5ilQqVf8E/V8x1X8Zadjtt9/e92dvYr1eP4g/Q8NjkHTr8uCyDK4xQWUZ7PKL76YhsweY
+3u/RGaBvImlEKiKecbA1ISQIJ2MILRnUcggFie9BEbMBbg32GPa7ryN+IA25NfwGDMG4+Dll4In9
+rg+coCwH3hdFSqvCeCUgJus3h9+3gci+IBHHsI9/tiyCBscyImfBEmOf4btKIxEa0g+qj3Ak6luk
+TsMDdT21kNTHXbuM5L6TjpMAuVYaarj9/Ja8WF1CQkJC4tuHJFESEhISEmMUSaIkJCQkJMYokkRJ
+SEhISIxR/heHXcIrMvkS1gAAAABJRU5ErkJggg==
+
+------_=_NextPart_001_01CCB66F.F38B15FC-- \ No newline at end of file
diff --git a/spec/fixtures/files/track-response-abcmail-oof.email b/spec/fixtures/files/track-response-abcmail-oof.email
new file mode 100644
index 000000000..5d1733143
--- /dev/null
+++ b/spec/fixtures/files/track-response-abcmail-oof.email
@@ -0,0 +1,80 @@
+Delivered-To: mysociety.robin@gmail.com
+Received: by 10.216.154.212 with SMTP id h62cs265517wek;
+ Fri, 30 Dec 2011 02:03:17 -0800 (PST)
+Received: by 10.227.208.129 with SMTP id gc1mr47630338wbb.4.1325239396543;
+ Fri, 30 Dec 2011 02:03:16 -0800 (PST)
+Return-Path: <Name.Removed@example.gov.uk>
+Received: from wildfire.ukcod.org.uk (wildfire.ukcod.org.uk. [89.238.145.74])
+ by mx.google.com with ESMTPS id ei10si9596065wbb.20.2011.12.30.02.03.16
+ (version=TLSv1/SSLv3 cipher=OTHER);
+ Fri, 30 Dec 2011 02:03:16 -0800 (PST)
+Received-SPF: neutral (google.com: 89.238.145.74 is neither permitted nor denied by best guess record for domain of Name.Removed@example.gov.uk) client-ip=89.238.145.74;
+Authentication-Results: mx.google.com; spf=neutral (google.com: 89.238.145.74 is neither permitted nor denied by best guess record for domain of Name.Removed@example.gov.uk) smtp.mail=Name.Removed@example.gov.uk
+Received: from foi by wildfire.ukcod.org.uk with local (Exim 4.72)
+ (envelope-from <Name.Removed@example.gov.uk>)
+ id 1RgZIs-0000ME-1T
+ for team_delivery@whatdotheyknow.com; Fri, 30 Dec 2011 10:03:10 +0000
+Received: from truro.icritical.com ([93.95.13.13]:51540)
+ by wildfire.ukcod.org.uk with smtp (Exim 4.72)
+ (envelope-from <Name.Removed@example.gov.uk>)
+ id 1RgZIq-0000M6-St
+ for track@whatdotheyknow.com; Fri, 30 Dec 2011 10:03:09 +0000
+Received: (qmail 19136 invoked from network); 30 Dec 2011 10:03:08 -0000
+Received: from localhost (127.0.0.1)
+ by truro.icritical.com with SMTP; 30 Dec 2011 10:03:08 -0000
+Received: from truro.icritical.com ([127.0.0.1])
+ by localhost (truro.icritical.com [127.0.0.1]) (amavisd-new, port 10024)
+ with SMTP id 19122-01 for <track@whatdotheyknow.com>;
+ Fri, 30 Dec 2011 10:03:07 +0000 (GMT)
+Received: (qmail 19112 invoked by uid 599); 30 Dec 2011 10:03:06 -0000
+Received: from unknown (HELO abcmail.example.gov.uk) (213.185.212.82)
+ by truro.icritical.com (qpsmtpd/0.28) with ESMTP; Fri, 30 Dec 2011 10:03:06 +0000
+Subject: AUTO: Name Removed is out of the office (returning 03/01/2012)
+Auto-Submitted: auto-generated
+From: Name.Removed@example.gov.uk
+To: track@whatdotheyknow.com
+Message-ID: <OFF4E36F18.ED02EFA3-ON80257976.00373794-80257976.00373794@example.gov.uk>
+Date: Fri, 30 Dec 2011 10:03:07 +0000
+X-MIMETrack: Serialize by Router on ABCMail/SVR/ABC(Release 8.5.2FP1|November 29, 2010) at
+ 30/12/2011 10:03:07
+MIME-Version: 1.0
+Content-type: text/plain; charset=US-ASCII
+X-Virus-Scanned: by iCritical at truro.icritical.com
+
+
+I am out of the office until 03/01/2012.
+
+I will be out of the office until 3rd January December 2012. I will deal
+with all emails upon my return. If your query is urgent please contact
+Colleague Name on colleague.name@example.gov.uk or 01234 567890.
+
+If you are requesting information under the Freedom of Information Act, the
+Environmental Information Regulations or the Data Protection Act, please
+forward your enquiry to colleague.name@example.gov.uk The Council
+will begin processing your request once it is received by that address.
+
+
+Thanks
+
+Name
+
+
+
+
+
+Note: This is an automated response to your message "Your WhatDoTheyKnow
+email alert" sent on 30/12/2011 06:54:19.
+
+This is the only notification you will receive while this person is away.
+
+
+This e-mail and any files transmitted with it are confidential and
+intended solely for the use of the individual or entity to whom
+they are addressed.
+If you have received this e-mail in error please notify the
+originator of the message. This footer also confirms that this
+e-mail message has been scanned for the presence of computer viruses.
+
+Any views expressed in this message are those of the individual
+sender, except where the sender specifies and with authority,
+states them to be the views of Organisation Name.
diff --git a/spec/fixtures/files/track-response-outlook-oof.email b/spec/fixtures/files/track-response-outlook-oof.email
new file mode 100644
index 000000000..ee5a28b15
--- /dev/null
+++ b/spec/fixtures/files/track-response-outlook-oof.email
@@ -0,0 +1,587 @@
+Delivered-To: mysociety.robin@gmail.com
+Received: by 10.152.24.138 with SMTP id u10cs341636laf;
+ Thu, 8 Dec 2011 02:39:53 -0800 (PST)
+Received: by 10.180.103.131 with SMTP id fw3mr4246912wib.57.1323340792168;
+ Thu, 08 Dec 2011 02:39:52 -0800 (PST)
+Return-Path: <peter@kentadvice.co.uk>
+Received: from wildfire.ukcod.org.uk (wildfire.ukcod.org.uk. [89.238.145.74])
+ by mx.google.com with ESMTPS id ft12si3357577wbb.14.2011.12.08.02.39.51
+ (version=TLSv1/SSLv3 cipher=OTHER);
+ Thu, 08 Dec 2011 02:39:52 -0800 (PST)
+Received-SPF: neutral (google.com: 89.238.145.74 is neither permitted nor denied by best guess record for domain of peter@kentadvice.co.uk) client-ip=89.238.145.74;
+Authentication-Results: mx.google.com; spf=neutral (google.com: 89.238.145.74 is neither permitted nor denied by best guess record for domain of peter@kentadvice.co.uk) smtp.mail=peter@kentadvice.co.uk
+Received: from foi by wildfire.ukcod.org.uk with local (Exim 4.72)
+ (envelope-from <peter@kentadvice.co.uk>)
+ id 1RYbOC-00034X-Vm
+ for team_delivery@whatdotheyknow.com; Thu, 08 Dec 2011 10:39:45 +0000
+Received: from mail-ey0-f173.google.com ([209.85.215.173]:38997)
+ by wildfire.ukcod.org.uk with esmtp (Exim 4.72)
+ (envelope-from <peter@kentadvice.co.uk>)
+ id 1RYbOC-00034L-GF
+ for track@whatdotheyknow.com; Thu, 08 Dec 2011 10:39:44 +0000
+Received: by eaai10 with SMTP id i10so1168752eaa.32
+ for <track@whatdotheyknow.com>; Thu, 08 Dec 2011 02:39:33 -0800 (PST)
+Received: by 10.213.21.148 with SMTP id j20mr131258ebb.87.1323340773446;
+ Thu, 08 Dec 2011 02:39:33 -0800 (PST)
+Received: from PRWin7 (cpc2-tilb7-2-0-cust982.basl.cable.virginmedia.com. [94.168.103.215])
+ by mx.google.com with ESMTPS id 49sm16411187eec.1.2011.12.08.02.39.31
+ (version=TLSv1/SSLv3 cipher=OTHER);
+ Thu, 08 Dec 2011 02:39:32 -0800 (PST)
+From: "Name Removed" <name-removed@example.co.uk>
+To: <track@whatdotheyknow.com>
+Subject: Out of Office reply
+Date: Thu, 8 Dec 2011 10:39:24 -0000
+Message-ID: <00ab01ccb595$aada0070$008e0150$@co.uk>
+MIME-Version: 1.0
+Content-Type: multipart/alternative;
+ boundary="----=_NextPart_000_00AC_01CCB595.AADA0070"
+X-Mailer: Microsoft Office Outlook 12.0
+Thread-Index: Acy1laTpNPAp9QuHRu2X59T70yzpQw==
+Content-Language: en-gb
+
+This is a multi-part message in MIME format.
+
+------=_NextPart_000_00AC_01CCB595.AADA0070
+Content-Type: text/plain;
+ charset="US-ASCII"
+Content-Transfer-Encoding: 7bit
+
+Thank you for your message. I am currently out of the office, with [limited]
+[no] access to e-mail.
+
+I will be returning on [day, date].
+
+If you need assistance before then, you may reach me at [phone number].
+For urgent issues, please contact [name] at [e-mail address] or [telephone
+number].
+
+[Signature]
+
+[Optional: Type your favorite quotation or saying here along with the author
+or source]
+
+------=_NextPart_000_00AC_01CCB595.AADA0070
+Content-Type: text/html;
+ charset="US-ASCII"
+Content-Transfer-Encoding: quoted-printable
+
+<html xmlns:v=3D"urn:schemas-microsoft-com:vml" =
+xmlns:o=3D"urn:schemas-microsoft-com:office:office" =
+xmlns:w=3D"urn:schemas-microsoft-com:office:word" =
+xmlns:x=3D"urn:schemas-microsoft-com:office:excel" =
+xmlns:p=3D"urn:schemas-microsoft-com:office:powerpoint" =
+xmlns:a=3D"urn:schemas-microsoft-com:office:access" =
+xmlns:dt=3D"uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" =
+xmlns:s=3D"uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" =
+xmlns:rs=3D"urn:schemas-microsoft-com:rowset" xmlns:z=3D"#RowsetSchema" =
+xmlns:b=3D"urn:schemas-microsoft-com:office:publisher" =
+xmlns:ss=3D"urn:schemas-microsoft-com:office:spreadsheet" =
+xmlns:c=3D"urn:schemas-microsoft-com:office:component:spreadsheet" =
+xmlns:oa=3D"urn:schemas-microsoft-com:office:activation" =
+xmlns:html=3D"http://www.w3.org/TR/REC-html40" =
+xmlns:q=3D"http://schemas.xmlsoap.org/soap/envelope/" xmlns:D=3D"DAV:" =
+xmlns:x2=3D"http://schemas.microsoft.com/office/excel/2003/xml" =
+xmlns:ois=3D"http://schemas.microsoft.com/sharepoint/soap/ois/" =
+xmlns:dir=3D"http://schemas.microsoft.com/sharepoint/soap/directory/" =
+xmlns:ds=3D"http://www.w3.org/2000/09/xmldsig#" =
+xmlns:dsp=3D"http://schemas.microsoft.com/sharepoint/dsp" =
+xmlns:udc=3D"http://schemas.microsoft.com/data/udc" =
+xmlns:xsd=3D"http://www.w3.org/2001/XMLSchema" =
+xmlns:sub=3D"http://schemas.microsoft.com/sharepoint/soap/2002/1/alerts/"=
+ xmlns:ec=3D"http://www.w3.org/2001/04/xmlenc#" =
+xmlns:sp=3D"http://schemas.microsoft.com/sharepoint/" =
+xmlns:sps=3D"http://schemas.microsoft.com/sharepoint/soap/" =
+xmlns:xsi=3D"http://www.w3.org/2001/XMLSchema-instance" =
+xmlns:udcxf=3D"http://schemas.microsoft.com/data/udc/xmlfile" =
+xmlns:wf=3D"http://schemas.microsoft.com/sharepoint/soap/workflow/" =
+xmlns:mver=3D"http://schemas.openxmlformats.org/markup-compatibility/2006=
+" xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml" =
+xmlns:mrels=3D"http://schemas.openxmlformats.org/package/2006/relationshi=
+ps" =
+xmlns:ex12t=3D"http://schemas.microsoft.com/exchange/services/2006/types"=
+ =
+xmlns:ex12m=3D"http://schemas.microsoft.com/exchange/services/2006/messag=
+es" xmlns=3D"http://www.w3.org/TR/REC-html40">
+
+<head>
+<META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
+charset=3Dus-ascii">
+
+
+<meta name=3DProgId content=3DWord.Document>
+<meta name=3DGenerator content=3D"Microsoft Word 12">
+<meta name=3DOriginator content=3D"Microsoft Word 12">
+<link rel=3DFile-List href=3D"cid:filelist.xml@01C895B2.35BC4F70">
+<!--[if gte mso 9]><xml>
+ <o:OfficeDocumentSettings>
+ <o:AllowPNG/>
+ <o:TargetScreenSize>1024x768</o:TargetScreenSize>
+ </o:OfficeDocumentSettings>
+</xml><![endif]-->
+<link rel=3DthemeData href=3D"~~themedata~~">
+<link rel=3DcolorSchemeMapping href=3D"~~colorschememapping~~">
+<!--[if gte mso 9]><xml>
+ <w:WordDocument>
+ <w:SpellingState>Clean</w:SpellingState>
+ <w:TrackMoves/>
+ <w:TrackFormatting/>
+ <w:EnvelopeVis/>
+ <w:ValidateAgainstSchemas/>
+ <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
+ <w:IgnoreMixedContent>false</w:IgnoreMixedContent>
+ <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
+ <w:DoNotPromoteQF/>
+ <w:LidThemeOther>EN-US</w:LidThemeOther>
+ <w:LidThemeAsian>X-NONE</w:LidThemeAsian>
+ <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript>
+ <w:Compatibility>
+ <w:DoNotExpandShiftReturn/>
+ <w:BreakWrappedTables/>
+ <w:SnapToGridInCell/>
+ <w:WrapTextWithPunct/>
+ <w:UseAsianBreakRules/>
+ <w:DontGrowAutofit/>
+ <w:SplitPgBreakAndParaMark/>
+ <w:DontVertAlignCellWithSp/>
+ <w:DontBreakConstrainedForcedTables/>
+ <w:DontVertAlignInTxbx/>
+ <w:Word11KerningPairs/>
+ <w:CachedColBalance/>
+ </w:Compatibility>
+ <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel>
+ <m:mathPr>
+ <m:mathFont m:val=3D"Cambria Math"/>
+ <m:brkBin m:val=3D"before"/>
+ <m:brkBinSub m:val=3D"&#45;-"/>
+ <m:smallFrac m:val=3D"off"/>
+ <m:dispDef/>
+ <m:lMargin m:val=3D"0"/>
+ <m:rMargin m:val=3D"0"/>
+ <m:defJc m:val=3D"centerGroup"/>
+ <m:wrapIndent m:val=3D"1440"/>
+ <m:intLim m:val=3D"subSup"/>
+ <m:naryLim m:val=3D"undOvr"/>
+ </m:mathPr></w:WordDocument>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:LatentStyles DefLockedState=3D"false" DefUnhideWhenUsed=3D"true"=20
+ DefSemiHidden=3D"true" DefQFormat=3D"false" DefPriority=3D"99"=20
+ LatentStyleCount=3D"267">
+ <w:LsdException Locked=3D"false" Priority=3D"0" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Normal"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"heading 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" =
+Name=3D"heading 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" =
+Name=3D"heading 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" =
+Name=3D"heading 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" =
+Name=3D"heading 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" =
+Name=3D"heading 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" =
+Name=3D"heading 7"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" =
+Name=3D"heading 8"/>
+ <w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" =
+Name=3D"heading 9"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 7"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 8"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" Name=3D"toc 9"/>
+ <w:LsdException Locked=3D"false" Priority=3D"35" QFormat=3D"true" =
+Name=3D"caption"/>
+ <w:LsdException Locked=3D"false" Priority=3D"10" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Title"/>
+ <w:LsdException Locked=3D"false" Priority=3D"1" Name=3D"Default =
+Paragraph Font"/>
+ <w:LsdException Locked=3D"false" Priority=3D"11" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtitle"/>
+ <w:LsdException Locked=3D"false" Priority=3D"22" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Strong"/>
+ <w:LsdException Locked=3D"false" Priority=3D"20" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Emphasis"/>
+ <w:LsdException Locked=3D"false" Priority=3D"59" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Table Grid"/>
+ <w:LsdException Locked=3D"false" UnhideWhenUsed=3D"false" =
+Name=3D"Placeholder Text"/>
+ <w:LsdException Locked=3D"false" Priority=3D"1" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"No Spacing"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light List"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Dark List"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 1"/>
+ <w:LsdException Locked=3D"false" UnhideWhenUsed=3D"false" =
+Name=3D"Revision"/>
+ <w:LsdException Locked=3D"false" Priority=3D"34" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"List Paragraph"/>
+ <w:LsdException Locked=3D"false" Priority=3D"29" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Quote"/>
+ <w:LsdException Locked=3D"false" Priority=3D"30" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Quote"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 1"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 2"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 3"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 4"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 5"/>
+ <w:LsdException Locked=3D"false" Priority=3D"60" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Shading Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"61" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light List Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"62" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Light Grid Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"63" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 1 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"64" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Shading 2 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"65" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 1 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"66" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium List 2 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"67" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 1 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"68" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 2 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"69" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Medium Grid 3 Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"70" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Dark List Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"71" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Shading Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"72" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful List Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"73" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" Name=3D"Colorful Grid Accent 6"/>
+ <w:LsdException Locked=3D"false" Priority=3D"19" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtle Emphasis"/>
+ <w:LsdException Locked=3D"false" Priority=3D"21" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense Emphasis"/>
+ <w:LsdException Locked=3D"false" Priority=3D"31" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Subtle Reference"/>
+ <w:LsdException Locked=3D"false" Priority=3D"32" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Intense =
+Reference"/>
+ <w:LsdException Locked=3D"false" Priority=3D"33" SemiHidden=3D"false"=20
+ UnhideWhenUsed=3D"false" QFormat=3D"true" Name=3D"Book Title"/>
+ <w:LsdException Locked=3D"false" Priority=3D"37" =
+Name=3D"Bibliography"/>
+ <w:LsdException Locked=3D"false" Priority=3D"39" QFormat=3D"true" =
+Name=3D"TOC Heading"/>
+ </w:LatentStyles>
+</xml><![endif]-->
+<style>
+<!--
+ /* Font Definitions */
+ @font-face
+ {font-family:"Cambria Math";
+ panose-1:2 4 5 3 5 4 6 3 2 4;
+ mso-font-charset:1;
+ mso-generic-font-family:roman;
+ mso-font-format:other;
+ mso-font-pitch:variable;
+ mso-font-signature:0 0 0 0 0 0;}
+@font-face
+ {font-family:Calibri;
+ panose-1:2 15 5 2 2 2 4 3 2 4;
+ mso-font-charset:0;
+ mso-generic-font-family:swiss;
+ mso-font-pitch:variable;
+ mso-font-signature:-1610611985 1073750139 0 0 159 0;}
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+ {mso-style-unhide:no;
+ mso-style-qformat:yes;
+ mso-style-parent:"";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:11.0pt;
+ font-family:"Calibri","sans-serif";
+ mso-fareast-font-family:Calibri;
+ mso-fareast-theme-font:minor-latin;
+ mso-bidi-font-family:"Times New Roman";}
+a:link, span.MsoHyperlink
+ {mso-style-noshow:yes;
+ mso-style-priority:99;
+ color:blue;
+ text-decoration:underline;
+ text-underline:single;}
+a:visited, span.MsoHyperlinkFollowed
+ {mso-style-noshow:yes;
+ mso-style-priority:99;
+ color:purple;
+ text-decoration:underline;
+ text-underline:single;}
+span.EmailStyle17
+ {mso-style-type:personal;
+ mso-style-noshow:yes;
+ mso-style-unhide:no;
+ font-family:"Calibri","sans-serif";
+ mso-ascii-font-family:Calibri;
+ mso-hansi-font-family:Calibri;
+ color:windowtext;}
+span.EmailStyle18
+ {mso-style-type:personal-reply;
+ mso-style-noshow:yes;
+ mso-style-unhide:no;
+ mso-ansi-font-size:11.0pt;
+ mso-bidi-font-size:11.0pt;
+ font-family:"Calibri","sans-serif";
+ mso-ascii-font-family:Calibri;
+ mso-ascii-theme-font:minor-latin;
+ mso-hansi-font-family:Calibri;
+ mso-hansi-theme-font:minor-latin;
+ mso-bidi-font-family:"Times New Roman";
+ mso-bidi-theme-font:minor-bidi;
+ color:#5F497A;
+ mso-themecolor:accent4;
+ mso-themeshade:191;}
+.MsoChpDefault
+ {mso-style-type:export-only;
+ mso-default-props:yes;
+ font-size:10.0pt;
+ mso-ansi-font-size:10.0pt;
+ mso-bidi-font-size:10.0pt;}
+@page Section1
+ {size:8.5in 11.0in;
+ margin:1.0in 1.0in 1.0in 1.0in;
+ mso-header-margin:.5in;
+ mso-footer-margin:.5in;
+ mso-paper-source:0;}
+div.Section1
+ {page:Section1;}
+-->
+</style>
+<!--[if gte mso 10]>
+<style>
+ /* Style Definitions */=20
+ table.MsoNormalTable
+ {mso-style-name:"Table Normal";
+ mso-tstyle-rowband-size:0;
+ mso-tstyle-colband-size:0;
+ mso-style-noshow:yes;
+ mso-style-priority:99;
+ mso-style-qformat:yes;
+ mso-style-parent:"";
+ mso-padding-alt:0in 5.4pt 0in 5.4pt;
+ mso-para-margin:0in;
+ mso-para-margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:10.0pt;
+ font-family:"Times New Roman","serif";}
+</style>
+<![endif]--><!--[if gte mso 9]><xml>
+ <o:shapedefaults v:ext=3D"edit" spidmax=3D"1026" />
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <o:shapelayout v:ext=3D"edit">
+ <o:idmap v:ext=3D"edit" data=3D"1" />
+ </o:shapelayout></xml><![endif]-->
+</head>
+
+<body lang=3DEN-US link=3Dblue vlink=3Dpurple =
+style=3D'tab-interval:.5in'>
+
+<div class=3DSection1>
+
+<p class=3DMsoNormal>Thank you for your message. I am currently out of =
+the
+office, with [limited] [no] access to e-mail.<o:p></o:p></p>
+
+<p class=3DMsoNormal><o:p>&nbsp;</o:p></p>
+
+<p class=3DMsoNormal>I will be returning on [day, date].<o:p></o:p></p>
+
+<p class=3DMsoNormal><o:p>&nbsp;</o:p></p>
+
+<p class=3DMsoNormal>If you need assistance before then, you may reach =
+me at
+[phone number].<o:p></o:p></p>
+
+<p class=3DMsoNormal>For urgent issues, please contact [name] at [e-mail =
+address]
+or [telephone number].<o:p></o:p></p>
+
+<p class=3DMsoNormal><o:p>&nbsp;</o:p></p>
+
+<p class=3DMsoNormal>[Signature]<o:p></o:p></p>
+
+<p class=3DMsoNormal><o:p>&nbsp;</o:p></p>
+
+<p class=3DMsoNormal><i style=3D'mso-bidi-font-style:normal'>[Optional: =
+Type your
+favorite quotation or saying here along with the author or =
+source]<o:p></o:p></i></p>
+
+</div>
+
+</body>
+
+</html>
+
+------=_NextPart_000_00AC_01CCB595.AADA0070--
+
diff --git a/spec/fixtures/foi_attachments.yml b/spec/fixtures/foi_attachments.yml
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/spec/fixtures/foi_attachments.yml
@@ -0,0 +1 @@
+
diff --git a/spec/integration/errors_spec.rb b/spec/integration/errors_spec.rb
index c64ca79e8..bfb7e5fb5 100644
--- a/spec/integration/errors_spec.rb
+++ b/spec/integration/errors_spec.rb
@@ -2,15 +2,19 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "When rendering errors" do
- fixtures [ :info_requests,
- :info_request_events,
- :public_bodies,
- :public_body_translations,
- :users,
- :raw_emails,
- :outgoing_messages,
- :incoming_messages,
- :comments ]
+ fixtures [
+ :users,
+ :public_bodies,
+ :public_body_translations,
+ :public_body_versions,
+ :info_requests,
+ :raw_emails,
+ :outgoing_messages,
+ :incoming_messages,
+ :comments,
+ :info_request_events,
+ :track_things,
+ ]
before(:each) do
load_raw_emails_data(raw_emails)
diff --git a/spec/integration/search_request_spec.rb b/spec/integration/search_request_spec.rb
index dcd20c7bd..07839af32 100644
--- a/spec/integration/search_request_spec.rb
+++ b/spec/integration/search_request_spec.rb
@@ -2,15 +2,19 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "When searching" do
- fixtures [ :info_requests,
- :info_request_events,
- :public_bodies,
- :public_body_translations,
- :users,
- :raw_emails,
- :outgoing_messages,
- :incoming_messages,
- :comments ]
+ fixtures [
+ :users,
+ :public_bodies,
+ :public_body_translations,
+ :public_body_versions,
+ :info_requests,
+ :raw_emails,
+ :outgoing_messages,
+ :incoming_messages,
+ :comments,
+ :info_request_events,
+ :track_things,
+ ]
before(:each) do
emails = raw_emails.clone
diff --git a/spec/integration/view_request_spec.rb b/spec/integration/view_request_spec.rb
new file mode 100644
index 000000000..cf1e4ca6c
--- /dev/null
+++ b/spec/integration/view_request_spec.rb
@@ -0,0 +1,32 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe "When viewing requests" do
+
+ fixtures [
+ :users,
+ :public_bodies,
+ :public_body_translations,
+ :public_body_versions,
+ :info_requests,
+ :raw_emails,
+ :outgoing_messages,
+ :incoming_messages,
+ :comments,
+ :info_request_events,
+ :track_things,
+ ]
+
+ before(:each) do
+ emails = raw_emails.clone
+ load_raw_emails_data(emails)
+ end
+
+ it "should not make endlessly recursive JSON <link>s" do
+ @dog_request = info_requests(:fancy_dog_request)
+ get "request/#{@dog_request.url_title}?unfold=1"
+ response.body.should_not include("dog?unfold=1.json")
+ response.body.should include("dog.json?unfold=1")
+ end
+
+end
+
diff --git a/spec/lib/tmail_extensions_spec.rb b/spec/lib/tmail_extensions_spec.rb
index c647fe522..6a55c34da 100644
--- a/spec/lib/tmail_extensions_spec.rb
+++ b/spec/lib/tmail_extensions_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# This is a test of the set_content_type monkey patch in
# lib/tmail_extensions.rb
@@ -27,5 +28,14 @@ describe "when using TMail" do
mail.to.should == ["request-66666-caa77777@whatdotheyknow.com", "foi@example.com"]
end
+ it 'should convert to utf8' do
+ # NB this isn't actually a TMail extension, but is core TMail;
+ # this was just a convenient place to assert the UTF8
+ # conversion is working
+ mail = TMail::Mail.parse(load_file_fixture('iso8859_2_raw_email.email'))
+ mail.subject.should have_text(/gjatë/u)
+ mail.body.is_utf8?.should == true
+ end
+
end
diff --git a/spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb b/spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb
index 1cf5e3d25..9bd5ccb93 100644
--- a/spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb
+++ b/spec/lib/whatdotheyknow/strip_empty_sessions_spec.rb
@@ -1,4 +1,4 @@
-require 'spec_helper'
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
describe WhatDoTheyKnow::StripEmptySessions do
def make_response(session_data, response_headers)
diff --git a/spec/models/has_tag_string_tag_spec.rb b/spec/models/has_tag_string_tag_spec.rb
index ba439acb8..1acd2e27d 100644
--- a/spec/models/has_tag_string_tag_spec.rb
+++ b/spec/models/has_tag_string_tag_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe HasTagString::HasTagStringTag, " when fiddling with tag strings " do
- fixtures :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should be able to make a new tag and save it" do
@tag = HasTagString::HasTagStringTag.new
diff --git a/spec/models/incoming_message_spec.rb b/spec/models/incoming_message_spec.rb
index d6923da21..08d3d89f7 100644
--- a/spec/models/incoming_message_spec.rb
+++ b/spec/models/incoming_message_spec.rb
@@ -1,13 +1,18 @@
+# coding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe IncomingMessage, " when dealing with incoming mail" do
- fixtures :incoming_messages, :raw_emails, :info_requests
+ fixtures :users, :raw_emails, :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
@im = incoming_messages(:useless_incoming_message)
load_raw_emails_data(raw_emails)
end
+ after(:all) do
+ ActionMailer::Base.deliveries.clear
+ end
+
it "should return the mail Date header date for sent at" do
@im.sent_at.should == @im.mail.date
end
@@ -17,6 +22,45 @@ describe IncomingMessage, " when dealing with incoming mail" do
TMail::Address.parse(em)
end
+ it "should correctly fold various types of footer" do
+ Dir.glob(File.join(Spec::Runner.configuration.fixture_path, "files", "email-folding-example-*.txt")).each do |file|
+ message = File.read(file)
+ parsed = IncomingMessage.remove_quoted_sections(message)
+ expected = File.read("#{file}.expected")
+ parsed.should include(expected)
+ end
+ end
+
+ it "should correctly convert HTML even when there's a meta tag asserting that it is iso-8859-1 which would normally confuse elinks" do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('quoted-subject-iso8859-1.email', ir.incoming_email)
+ message = ir.incoming_messages[1]
+ message.parse_raw_email!
+ message.get_main_body_text_part.charset.should == "iso-8859-1"
+ message.get_main_body_text_internal.should include("política")
+ end
+
+ it "should unquote RFC 2047 headers" do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('quoted-subject-iso8859-1.email', ir.incoming_email)
+ message = ir.incoming_messages[1]
+ message.mail_from.should == "Coordenação de Relacionamento, Pesquisa e Informação/CEDI"
+ message.subject.should == "Câmara Responde: Banco de ideias"
+ end
+
+
+ it "should fold multiline sections" do
+ {
+ "foo\n--------\nconfidential" => "foo\nFOLDED_QUOTED_SECTION\n", # basic test
+ "foo\n--------\nbar - confidential" => "foo\nFOLDED_QUOTED_SECTION\n", # allow scorechar inside folded section
+ "foo\n--------\nbar\n--------\nconfidential" => "foo\n--------\nbar\nFOLDED_QUOTED_SECTION\n", # don't assume that anything after a score is a folded section
+ "foo\n--------\nbar\n--------\nconfidential\n--------\nrest" => "foo\n--------\nbar\nFOLDED_QUOTED_SECTION\nrest", # don't assume that a folded section continues to the end of the message
+ "foo\n--------\nbar\n- - - - - - - -\nconfidential\n--------\nrest" => "foo\n--------\nbar\nFOLDED_QUOTED_SECTION\nrest", # allow spaces in the score
+ }.each do |input,output|
+ IncomingMessage.remove_quoted_sections(input).should == output
+ end
+ end
+
end
describe IncomingMessage, "when parsing HTML mail" do
@@ -42,7 +86,7 @@ end
describe IncomingMessage, " display attachments" do
it "should not show slashes in filenames" do
- foi_attachment = FOIAttachment.new()
+ foi_attachment = FoiAttachment.new()
# http://www.whatdotheyknow.com/request/post_commercial_manager_librarie#incoming-17233
foi_attachment.filename = "FOI/09/066 RESPONSE TO FOI REQUEST RECEIVED 21st JANUARY 2009.txt"
expected_display_filename = foi_attachment.filename.gsub(/\//, " ")
@@ -50,10 +94,11 @@ describe IncomingMessage, " display attachments" do
end
it "should not show slashes in subject generated filenames" do
- foi_attachment = FOIAttachment.new()
+ foi_attachment = FoiAttachment.new()
# http://www.whatdotheyknow.com/request/post_commercial_manager_librarie#incoming-17233
foi_attachment.within_rfc822_subject = "FOI/09/066 RESPONSE TO FOI REQUEST RECEIVED 21st JANUARY 2009"
foi_attachment.content_type = 'text/plain'
+ foi_attachment.ensure_filename!
expected_display_filename = foi_attachment.within_rfc822_subject.gsub(/\//, " ") + ".txt"
foi_attachment.display_filename.should == expected_display_filename
end
@@ -79,16 +124,15 @@ describe IncomingMessage, " folding quoted parts of emails" do
end
describe IncomingMessage, " checking validity to reply to" do
- def test_email(result, email, return_path, autosubmitted)
+ def test_email(result, email, return_path, autosubmitted = nil)
@address = mock(TMail::Address)
@address.stub!(:spec).and_return(email)
@return_path = mock(TMail::ReturnPathHeader)
@return_path.stub!(:addr).and_return(return_path)
-
- @autosubmitted = mock(TMail::KeywordsHeader)
- @autosubmitted.stub!(:keys).and_return(autosubmitted)
-
+ if !autosubmitted.nil?
+ @autosubmitted = TMail::UnstructuredHeader.new("auto-submitted", autosubmitted)
+ end
@mail = mock(TMail::Mail)
@mail.stub!(:from_addrs).and_return( [ @address ] )
@mail.stub!(:[]).with("return-path").and_return(@return_path)
@@ -96,50 +140,49 @@ describe IncomingMessage, " checking validity to reply to" do
@incoming_message = IncomingMessage.new()
@incoming_message.stub!(:mail).and_return(@mail)
-
- @incoming_message.valid_to_reply_to?.should == result
+ @incoming_message._calculate_valid_to_reply_to.should == result
end
it "says a valid email is fine" do
- test_email(true, "team@mysociety.org", nil, [])
+ test_email(true, "team@mysociety.org", nil)
end
it "says postmaster email is bad" do
- test_email(false, "postmaster@mysociety.org", nil, [])
+ test_email(false, "postmaster@mysociety.org", nil)
end
it "says Mailer-Daemon email is bad" do
- test_email(false, "Mailer-Daemon@mysociety.org", nil, [])
+ test_email(false, "Mailer-Daemon@mysociety.org", nil)
end
it "says case mangled MaIler-DaemOn email is bad" do
- test_email(false, "MaIler-DaemOn@mysociety.org", nil, [])
+ test_email(false, "MaIler-DaemOn@mysociety.org", nil)
end
it "says Auto_Reply email is bad" do
- test_email(false, "Auto_Reply@mysociety.org", nil, [])
+ test_email(false, "Auto_Reply@mysociety.org", nil)
end
it "says DoNotReply email is bad" do
- test_email(false, "DoNotReply@tube.tfl.gov.uk", nil, [])
+ test_email(false, "DoNotReply@tube.tfl.gov.uk", nil)
end
it "says a filled-out return-path is fine" do
- test_email(true, "team@mysociety.org", "Return-path: <foo@baz.com>", [])
+ test_email(true, "team@mysociety.org", "Return-path: <foo@baz.com>")
end
it "says an empty return-path is bad" do
- test_email(false, "team@mysociety.org", "<>", [])
+ test_email(false, "team@mysociety.org", "<>")
end
it "says an auto-submitted keyword is bad" do
- test_email(false, "team@mysociety.org", nil, ["auto-replied"])
+ test_email(false, "team@mysociety.org", nil, "auto-replied")
end
end
describe IncomingMessage, " checking validity to reply to with real emails" do
- fixtures :incoming_messages, :raw_emails, :public_bodies, :public_body_translations, :info_requests, :users
+ fixtures :users, :raw_emails, :public_bodies, :public_body_translations, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
after(:all) do
ActionMailer::Base.deliveries.clear
@@ -163,7 +206,7 @@ describe IncomingMessage, " checking validity to reply to with real emails" do
end
describe IncomingMessage, " when censoring data" do
- fixtures :incoming_messages, :raw_emails, :public_bodies, :public_body_translations, :info_requests, :users
+ fixtures :users, :raw_emails, :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
@test_data = "There was a mouse called Stilton, he wished that he was blue."
@@ -262,10 +305,8 @@ describe IncomingMessage, " when censoring data" do
end
it "should apply censor rules to From: addresses" do
- mock_mail = mock('Email object')
- mock_mail.stub!(:from_name_if_present).and_return("Stilton Mouse")
- @im.stub!(:mail).and_return(mock_mail)
-
+ @im.stub!(:mail_from).and_return("Stilton Mouse")
+ @im.stub!(:last_parsed).and_return(Time.now)
safe_mail_from = @im.safe_mail_from
safe_mail_from.should == "Jarlsberg Mouse"
end
@@ -273,7 +314,7 @@ describe IncomingMessage, " when censoring data" do
end
describe IncomingMessage, " when censoring whole users" do
- fixtures :incoming_messages, :raw_emails, :public_bodies, :public_body_translations, :info_requests, :users
+ fixtures :users, :raw_emails, :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
@test_data = "There was a mouse called Stilton, he wished that he was blue."
@@ -304,21 +345,23 @@ end
describe IncomingMessage, " when uudecoding bad messages" do
+ fixtures :incoming_messages, :raw_emails, :public_bodies, :public_body_translations, :info_requests, :users, :foi_attachments
+
+ before(:each) do
+ load_raw_emails_data(raw_emails)
+ end
+
it "should be able to do it at all" do
mail_body = load_file_fixture('incoming-request-bad-uuencoding.email')
mail = TMail::Mail.parse(mail_body)
mail.base64_decode
-
- im = IncomingMessage.new
+ im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
- ir = InfoRequest.new
- im.info_request = ir
- u = User.new
- ir.user = u
-
- attachments = im.get_main_body_text_uudecode_attachments
- attachments.size.should == 1
- attachments[0].filename.should == 'moo.txt'
+ im.extract_attachments!
+ attachments = im.foi_attachments
+ attachments.size.should == 2
+ attachments[1].filename.should == 'moo.txt'
+ im.get_attachments_for_display.size.should == 1
end
it "should apply censor rules" do
@@ -326,12 +369,9 @@ describe IncomingMessage, " when uudecoding bad messages" do
mail = TMail::Mail.parse(mail_body)
mail.base64_decode
- im = IncomingMessage.new
+ im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
- ir = InfoRequest.new
- im.info_request = ir
- u = User.new
- ir.user = u
+ ir = info_requests(:fancy_dog_request)
@censor_rule = CensorRule.new()
@censor_rule.text = "moo"
@@ -339,26 +379,31 @@ describe IncomingMessage, " when uudecoding bad messages" do
@censor_rule.last_edit_editor = "unknown"
@censor_rule.last_edit_comment = "none"
ir.censor_rules << @censor_rule
+ im.extract_attachments!
- attachments = im.get_main_body_text_uudecode_attachments
+ attachments = im.get_attachments_for_display
attachments.size.should == 1
- attachments[0].filename.should == 'bah.txt'
+ attachments[0].display_filename.should == 'bah.txt'
end
end
describe IncomingMessage, "when messages are attached to messages" do
+ fixtures :incoming_messages, :raw_emails, :public_bodies, :public_body_translations, :info_requests, :users, :foi_attachments
+
+ before(:each) do
+ load_raw_emails_data(raw_emails)
+ end
+
it "should flatten all the attachments out" do
mail_body = load_file_fixture('incoming-request-attach-attachments.email')
mail = TMail::Mail.parse(mail_body)
mail.base64_decode
- im = IncomingMessage.new
+ im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
- ir = InfoRequest.new
- im.info_request = ir
- u = User.new
- ir.user = u
+
+ im.extract_attachments!
attachments = im.get_attachments_for_display
attachments.size.should == 3
@@ -369,17 +414,20 @@ describe IncomingMessage, "when messages are attached to messages" do
end
describe IncomingMessage, "when Outlook messages are attached to messages" do
+ fixtures :incoming_messages, :raw_emails, :public_bodies, :public_body_translations, :info_requests, :users, :foi_attachments
+
+ before(:each) do
+ load_raw_emails_data(raw_emails)
+ end
+
it "should flatten all the attachments out" do
mail_body = load_file_fixture('incoming-request-oft-attachments.email')
mail = TMail::Mail.parse(mail_body)
mail.base64_decode
- im = IncomingMessage.new
+ im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
- ir = InfoRequest.new
- im.info_request = ir
- u = User.new
- ir.user = u
+ im.extract_attachments!
attachments = im.get_attachments_for_display
attachments.size.should == 2
@@ -389,17 +437,20 @@ describe IncomingMessage, "when Outlook messages are attached to messages" do
end
describe IncomingMessage, "when TNEF attachments are attached to messages" do
+ fixtures :incoming_messages, :raw_emails, :public_bodies, :public_body_translations, :info_requests, :users, :foi_attachments
+
+ before(:each) do
+ load_raw_emails_data(raw_emails)
+ end
+
it "should flatten all the attachments out" do
mail_body = load_file_fixture('incoming-request-tnef-attachments.email')
mail = TMail::Mail.parse(mail_body)
mail.base64_decode
- im = IncomingMessage.new
+ im = incoming_messages(:useless_incoming_message)
im.stub!(:mail).and_return(mail)
- ir = InfoRequest.new
- im.info_request = ir
- u = User.new
- ir.user = u
+ im.extract_attachments!
attachments = im.get_attachments_for_display
attachments.size.should == 2
diff --git a/spec/models/info_request_event_spec.rb b/spec/models/info_request_event_spec.rb
index 666f5cb1a..3229284cc 100644
--- a/spec/models/info_request_event_spec.rb
+++ b/spec/models/info_request_event_spec.rb
@@ -50,5 +50,30 @@ describe InfoRequestEvent do
end
end
+
+ describe "doing search/index stuff" do
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
+
+ before(:each) do
+ load_raw_emails_data(raw_emails)
+ parse_all_incoming_messages
+ end
+
+ it 'should get search text for outgoing messages' do
+ event = info_request_events(:useless_outgoing_message_event)
+ message = outgoing_messages(:useless_outgoing_message).body
+ event.search_text_main.should == message + "\n\n"
+ end
+
+ it 'should get search text for incoming messages' do
+ event = info_request_events(:useless_incoming_message_event)
+ event.search_text_main.strip.should == "No way! I'm not going to tell you that in a month of Thursdays.\n\nThe Geraldine Quango"
+ end
+
+
+ end
+
+
+
end
diff --git a/spec/models/info_request_spec.rb b/spec/models/info_request_spec.rb
index b82052a0f..b1baa66a2 100644
--- a/spec/models/info_request_spec.rb
+++ b/spec/models/info_request_spec.rb
@@ -3,7 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe InfoRequest do
describe "guessing a request from an email" do
- fixtures :info_requests, :public_bodies, :incoming_messages, :raw_emails
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
@im = incoming_messages(:useless_incoming_message)
@@ -74,7 +74,7 @@ describe InfoRequest do
describe " when emailing" do
- fixtures :info_requests, :info_request_events, :outgoing_messages, :public_bodies, :public_body_translations, :users, :comments
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@info_request = info_requests(:fancy_dog_request)
@@ -143,8 +143,8 @@ describe InfoRequest do
end
it "should cope with indexing after item is deleted" do
+ IncomingMessage.find(:all).each{|x| x.parse_raw_email!}
rebuild_xapian_index
-
# delete event from underneath indexing; shouldn't cause error
info_request_events(:useless_incoming_message_event).save!
info_request_events(:useless_incoming_message_event).destroy
@@ -154,7 +154,7 @@ describe InfoRequest do
end
describe "when calculating the status" do
- fixtures :info_requests, :info_request_events, :holidays, :public_bodies, :public_body_translations, :outgoing_messages
+ fixtures :holidays, :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@ir = info_requests(:naughty_chicken_request)
@@ -196,7 +196,7 @@ describe InfoRequest do
describe "when using a plugin and calculating the status" do
- fixtures :info_requests
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
InfoRequest.send(:require, File.expand_path(File.dirname(__FILE__) + '/customstates'))
@@ -231,7 +231,7 @@ describe InfoRequest do
describe "when calculating the status for a school" do
- fixtures :info_requests, :info_request_events, :holidays, :public_bodies, :public_body_translations
+ fixtures :holidays, :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@ir = info_requests(:naughty_chicken_request)
@@ -380,8 +380,8 @@ describe InfoRequest do
before do
Time.stub!(:now).and_return(Time.utc(2007, 11, 9, 23, 59))
- @mock_comment_event = mock_model(InfoRequestEvent, :created_at => Time.now - 23.days, :event_type => 'comment')
- @mock_response_event = mock_model(InfoRequestEvent, :created_at => Time.now - 22.days, :event_type => 'response')
+ @mock_comment_event = safe_mock_model(InfoRequestEvent, :created_at => Time.now - 23.days, :event_type => 'comment')
+ @mock_response_event = safe_mock_model(InfoRequestEvent, :created_at => Time.now - 22.days, :event_type => 'response')
@info_request = InfoRequest.new(:prominence => 'normal',
:awaiting_description => true,
:info_request_events => [@mock_response_event, @mock_comment_event])
diff --git a/spec/models/outgoing_mailer_spec.rb b/spec/models/outgoing_mailer_spec.rb
index c96a3fb74..75c8053b4 100644
--- a/spec/models/outgoing_mailer_spec.rb
+++ b/spec/models/outgoing_mailer_spec.rb
@@ -4,7 +4,7 @@ describe OutgoingMailer, " when working out follow up addresses" do
# This is done with fixtures as the code is a bit tangled with the way it
# calls TMail. XXX untangle it and make these tests spread out and using
# mocks. Put parts of the tests in spec/lib/tmail_extensions.rb
- fixtures :info_requests, :incoming_messages, :raw_emails, :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -70,7 +70,7 @@ describe OutgoingMailer, " when working out follow up addresses" do
end
describe OutgoingMailer, "when working out follow up subjects" do
- fixtures :info_requests, :incoming_messages, :outgoing_messages, :raw_emails
+ fixtures :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
diff --git a/spec/models/outgoing_message_spec.rb b/spec/models/outgoing_message_spec.rb
index 1956c4d73..58d9f398e 100644
--- a/spec/models/outgoing_message_spec.rb
+++ b/spec/models/outgoing_message_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe OutgoingMessage, " when making an outgoing message" do
- fixtures :outgoing_messages, :info_requests, :incoming_messages, :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@om = outgoing_messages(:useless_outgoing_message)
@@ -38,7 +38,7 @@ end
describe IncomingMessage, " when censoring data" do
- fixtures :outgoing_messages, :info_requests
+ fixtures :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@om = outgoing_messages(:useless_outgoing_message)
diff --git a/spec/models/public_body_spec.rb b/spec/models/public_body_spec.rb
index 33ab8ffdb..07e8f291d 100644
--- a/spec/models/public_body_spec.rb
+++ b/spec/models/public_body_spec.rb
@@ -95,7 +95,7 @@ describe PublicBody, " using machine tags" do
end
describe PublicBody, "when finding_by_tags" do
- fixtures :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@geraldine = public_bodies(:geraldine_public_body)
@@ -173,7 +173,7 @@ describe PublicBody, " when saving" do
end
describe PublicBody, "when searching" do
- fixtures :public_bodies, :public_body_translations, :public_body_versions
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should find by existing url name" do
body = PublicBody.find_by_url_name_with_historic('dfh')
@@ -226,6 +226,18 @@ describe PublicBody, "when searching" do
end
end
+describe PublicBody, " when dealing public body locales" do
+ it "shouldn't fail if it internal_admin_body was created in a locale other than the default" do
+ # first time, do it with the non-default locale
+ PublicBody.with_locale(:es) do
+ PublicBody.internal_admin_body
+ end
+
+ # second time
+ lambda {PublicBody.internal_admin_body }.should_not raise_error(ActiveRecord::RecordInvalid)
+ end
+end
+
describe PublicBody, " when loading CSV files" do
before(:each) do
# InternalBody is created the first time it's accessed, which happens sometimes during imports,
@@ -302,8 +314,8 @@ describe PublicBody, " when loading CSV files" do
notes.size.should == 4
notes.should == [
"line 2: creating new authority 'North West Fake Authority' (locale: en):\n\t\{\"name\":\"North West Fake Authority\",\"request_email\":\"north_west_foi@localhost\",\"home_page\":\"http://northwest.org\"\}",
- "line 3: creating new authority 'Scottish Fake Authority' (locale: en):\n\t\{\"tag_string\":\"scottish\",\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\",\"home_page\":\"http://scottish.org\"\}",
- "line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t\{\"tag_string\":\"fake aTag\",\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\"\}",
+ "line 3: creating new authority 'Scottish Fake Authority' (locale: en):\n\t\{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\",\"home_page\":\"http://scottish.org\",\"tag_string\":\"scottish\"\}",
+ "line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t\{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\",\"tag_string\":\"fake aTag\"\}",
"Notes: Some bodies are in database, but not in CSV file:\n Department for Humpadinking\n Geraldine Quango\nYou may want to delete them manually.\n"
]
@@ -316,7 +328,7 @@ describe PublicBody, " when loading CSV files" do
PublicBody.find_by_name('North West Fake Authority').tag_array_for_search.should == []
PublicBody.find_by_name('Scottish Fake Authority').tag_array_for_search.should == ['scottish']
- PublicBody.find_by_name('Fake Authority of Northern Ireland').tag_array_for_search.should == ['fake', 'aTag']
+ PublicBody.find_by_name('Fake Authority of Northern Ireland').tag_array_for_search.should == ['aTag', 'fake']
# Import again to check the 'add' tag functionality works
new_tags_file = load_file_fixture('fake-authority-add-tags.rb')
@@ -324,8 +336,8 @@ describe PublicBody, " when loading CSV files" do
# Check tags were added successfully
PublicBody.find_by_name('North West Fake Authority').tag_array_for_search.should == ['aTag']
- PublicBody.find_by_name('Scottish Fake Authority').tag_array_for_search.should == ['scottish', 'aTag']
- PublicBody.find_by_name('Fake Authority of Northern Ireland').tag_array_for_search.should == ['fake', 'aTag']
+ PublicBody.find_by_name('Scottish Fake Authority').tag_array_for_search.should == ['aTag', 'scottish']
+ PublicBody.find_by_name('Fake Authority of Northern Ireland').tag_array_for_search.should == ['aTag', 'fake']
end
it "should import tags successfully when the import tag is set" do
@@ -334,8 +346,8 @@ describe PublicBody, " when loading CSV files" do
# Check new bodies were imported successfully
PublicBody.find_by_name('North West Fake Authority').tag_array_for_search.should == ['fake']
- PublicBody.find_by_name('Scottish Fake Authority').tag_array_for_search.should == ['scottish', 'fake']
- PublicBody.find_by_name('Fake Authority of Northern Ireland').tag_array_for_search.should == ['fake', 'aTag']
+ PublicBody.find_by_name('Scottish Fake Authority').tag_array_for_search.should == ['fake', 'scottish']
+ PublicBody.find_by_name('Fake Authority of Northern Ireland').tag_array_for_search.should == ['aTag', 'fake']
# Import again to check the 'replace' tag functionality works
new_tags_file = load_file_fixture('fake-authority-add-tags.rb')
@@ -344,7 +356,7 @@ describe PublicBody, " when loading CSV files" do
# Check tags were added successfully
PublicBody.find_by_name('North West Fake Authority').tag_array_for_search.should == ['aTag']
PublicBody.find_by_name('Scottish Fake Authority').tag_array_for_search.should == ['aTag']
- PublicBody.find_by_name('Fake Authority of Northern Ireland').tag_array_for_search.should == ['fake', 'aTag']
+ PublicBody.find_by_name('Fake Authority of Northern Ireland').tag_array_for_search.should == ['aTag', 'fake']
end
it "should create bodies with names in multiple locales" do
@@ -357,9 +369,9 @@ describe PublicBody, " when loading CSV files" do
notes.should == [
"line 2: creating new authority 'North West Fake Authority' (locale: en):\n\t{\"name\":\"North West Fake Authority\",\"request_email\":\"north_west_foi@localhost\",\"home_page\":\"http://northwest.org\"}",
"line 2: creating new authority 'North West Fake Authority' (locale: es):\n\t{\"name\":\"Autoridad del Nordeste\"}",
- "line 3: creating new authority 'Scottish Fake Authority' (locale: en):\n\t{\"tag_string\":\"scottish\",\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\",\"home_page\":\"http://scottish.org\"}",
+ "line 3: creating new authority 'Scottish Fake Authority' (locale: en):\n\t{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\",\"home_page\":\"http://scottish.org\",\"tag_string\":\"scottish\"}",
"line 3: creating new authority 'Scottish Fake Authority' (locale: es):\n\t{\"name\":\"Autoridad Escocesa\"}",
- "line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t{\"tag_string\":\"fake aTag\",\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\"}",
+ "line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\",\"tag_string\":\"fake aTag\"}",
"line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: es):\n\t{\"name\":\"Autoridad Irlandesa\"}",
"Notes: Some bodies are in database, but not in CSV file:\n Department for Humpadinking\n Geraldine Quango\nYou may want to delete them manually.\n"
]
@@ -385,8 +397,8 @@ describe PublicBody, " when loading CSV files" do
notes.size.should == 4
notes.should == [
"line 2: creating new authority 'North West Fake Authority' (locale: en):\n\t{\"name\":\"North West Fake Authority\",\"request_email\":\"north_west_foi@localhost\",\"home_page\":\"http://northwest.org\"}",
- "line 3: creating new authority 'Scottish Fake Authority' (locale: en):\n\t{\"tag_string\":\"scottish\",\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\",\"home_page\":\"http://scottish.org\"}",
- "line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t{\"tag_string\":\"fake aTag\",\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\"}",
+ "line 3: creating new authority 'Scottish Fake Authority' (locale: en):\n\t{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\",\"home_page\":\"http://scottish.org\",\"tag_string\":\"scottish\"}",
+ "line 4: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\",\"tag_string\":\"fake aTag\"}",
"Notes: Some bodies are in database, but not in CSV file:\n Department for Humpadinking\n Geraldine Quango\nYou may want to delete them manually.\n"
]
diff --git a/spec/models/request_mailer_spec.rb b/spec/models/request_mailer_spec.rb
index fbe22c220..ef4ed8074 100644
--- a/spec/models/request_mailer_spec.rb
+++ b/spec/models/request_mailer_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe RequestMailer, " when receiving incoming mail" do
- fixtures :info_requests, :incoming_messages, :raw_emails, :users, :public_bodies, :public_body_translations
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
diff --git a/spec/models/track_thing_spec.rb b/spec/models/track_thing_spec.rb
index 1a0324a78..4922a96c7 100644
--- a/spec/models/track_thing_spec.rb
+++ b/spec/models/track_thing_spec.rb
@@ -1,7 +1,7 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe TrackThing, "when tracking changes" do
- fixtures :track_things, :users
+ fixtures :users, :info_requests, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@track_thing = track_things(:track_fancy_dog_search)
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 751a61060..e0a6c649e 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -152,10 +152,10 @@ end
describe User, "when reindexing referencing models" do
before do
- @request_event = mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
- @request = mock_model(InfoRequest, :info_request_events => [@request_event])
- @comment_event = mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
- @comment = mock_model(Comment, :info_request_events => [@comment_event])
+ @request_event = safe_mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
+ @request = safe_mock_model(InfoRequest, :info_request_events => [@request_event])
+ @comment_event = safe_mock_model(InfoRequestEvent, :xapian_mark_needs_index => true)
+ @comment = safe_mock_model(Comment, :info_request_events => [@comment_event])
@user = User.new(:comments => [@comment], :info_requests => [@request])
end
@@ -193,7 +193,7 @@ describe User, "when reindexing referencing models" do
end
describe User, "when checking abilities" do
- fixtures :users
+ fixtures :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@user = users(:bob_smith_user)
@@ -283,7 +283,7 @@ describe User, "when setting a profile photo" do
end
describe User, "when unconfirmed" do
- fixtures :users
+ fixtures :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before do
@user = users(:unconfirmed_user)
@@ -295,7 +295,7 @@ describe User, "when unconfirmed" do
end
describe User, "when emails have bounced" do
- fixtures :users
+ fixtures :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should record bounces" do
User.record_bounce_for_email("bob@localhost", "The reason we think the email bounced (e.g. a bounce message)")
diff --git a/spec/models/xapian_spec.rb b/spec/models/xapian_spec.rb
index 0c6fa6bb6..ec11c944b 100644
--- a/spec/models/xapian_spec.rb
+++ b/spec/models/xapian_spec.rb
@@ -1,9 +1,10 @@
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe User, " when indexing users with Xapian" do
- fixtures :users
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
it "should search by name" do
+ parse_all_incoming_messages
rebuild_xapian_index
# def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page)
xapian_object = InfoRequest.full_search([User], "Silly", 'created_at', true, nil, 100, 1)
@@ -12,10 +13,10 @@ describe User, " when indexing users with Xapian" do
end
it "should search by 'about me' text" do
+ rebuild_xapian_index
user = users(:bob_smith_user)
- rebuild_xapian_index
- # def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page)
+ # def InfoRequest.full_search(models, query, order, ascending, collapse, per_page, page)
xapian_object = InfoRequest.full_search([User], "stuff", 'created_at', true, nil, 100, 1)
xapian_object.results.size.should == 1
xapian_object.results[0][:model].should == user
@@ -34,7 +35,7 @@ describe User, " when indexing users with Xapian" do
end
describe PublicBody, " when indexing public bodies with Xapian" do
- fixtures :public_bodies, :public_body_translations, :incoming_messages, :outgoing_messages, :raw_emails, :comments, :info_requests
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -72,7 +73,7 @@ describe PublicBody, " when indexing public bodies with Xapian" do
end
describe PublicBody, " when indexing requests by body they are to" do
- fixtures :public_bodies, :public_body_translations, :info_request_events, :info_requests, :raw_emails, :comments
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
@@ -132,7 +133,7 @@ describe PublicBody, " when indexing requests by body they are to" do
end
describe User, " when indexing requests by user they are from" do
- fixtures :users, :info_request_events, :info_requests, :incoming_messages, :outgoing_messages, :raw_emails, :comments
+ fixtures :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -219,7 +220,7 @@ describe User, " when indexing requests by user they are from" do
end
describe User, " when indexing comments by user they are by" do
- fixtures :users, :info_request_events, :info_requests, :comments, :incoming_messages, :outgoing_messages, :raw_emails, :comments
+ fixtures :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -256,7 +257,7 @@ describe User, " when indexing comments by user they are by" do
end
describe InfoRequest, " when indexing requests by their title" do
- fixtures :info_request_events, :info_requests, :incoming_messages, :raw_emails, :comments
+ fixtures :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -287,7 +288,7 @@ describe InfoRequest, " when indexing requests by their title" do
end
describe InfoRequest, " when indexing requests by tag" do
- fixtures :info_request_events, :info_requests, :incoming_messages, :raw_emails, :comments
+ fixtures :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -309,7 +310,7 @@ describe InfoRequest, " when indexing requests by tag" do
end
describe PublicBody, " when indexing authorities by tag" do
- fixtures :public_bodies, :public_body_translations, :incoming_messages, :outgoing_messages, :raw_emails, :comments
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
before(:each) do
load_raw_emails_data(raw_emails)
end
@@ -333,6 +334,66 @@ describe PublicBody, " when indexing authorities by tag" do
end
end
+describe PublicBody, " when only indexing selected things on a rebuild" do
+ fixtures :public_bodies, :public_body_translations, :public_body_versions, :users, :info_requests, :raw_emails, :incoming_messages, :outgoing_messages, :comments, :info_request_events, :track_things
+ before(:each) do
+ load_raw_emails_data(raw_emails)
+ end
+
+ it "should only index what we ask it to" do
+ rebuild_xapian_index
+ body = public_bodies(:geraldine_public_body)
+ body.tag_string = 'mice:3'
+ body.name = 'frobzn'
+ body.save!
+ # only reindex 'variety' term
+ dropfirst = true
+ terms = "V"
+ values = false
+ texts = false
+ rebuild_xapian_index(terms, values, texts, dropfirst)
+ xapian_object = InfoRequest.full_search([PublicBody], "tag:mice", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 0
+ xapian_object = InfoRequest.full_search([PublicBody], "frobzn", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 0
+ xapian_object = InfoRequest.full_search([PublicBody], "variety:authority", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 2
+ # only reindex 'tag' and text
+ dropfirst = true
+ terms = "U"
+ values = false
+ texts = true
+ rebuild_xapian_index(terms, values, texts, dropfirst)
+ xapian_object = InfoRequest.full_search([PublicBody], "tag:mice", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 1
+ xapian_object = InfoRequest.full_search([PublicBody], "frobzn", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 1
+ xapian_object = InfoRequest.full_search([PublicBody], "variety:authority", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 0
+ # only reindex 'variety' term, but keeping the existing data in-place
+ dropfirst = false
+ terms = "V"
+ texts = false
+ rebuild_xapian_index(terms, values, texts, dropfirst)
+ xapian_object = InfoRequest.full_search([PublicBody], "tag:mice", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 1
+ xapian_object = InfoRequest.full_search([PublicBody], "frobzn", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 1
+ xapian_object = InfoRequest.full_search([PublicBody], "variety:authority", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 2
+ # only reindex 'variety' term, blowing away existing data
+ dropfirst = true
+ rebuild_xapian_index(terms, values, texts, dropfirst)
+ xapian_object = InfoRequest.full_search([PublicBody], "tag:mice", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 0
+ xapian_object = InfoRequest.full_search([PublicBody], "frobzn", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 0
+ xapian_object = InfoRequest.full_search([PublicBody], "variety:authority", 'created_at', true, nil, 100, 1)
+ xapian_object.results.size.should == 2
+ end
+end
+
+
diff --git a/spec/script/handle-mail-replies_spec.rb b/spec/script/handle-mail-replies_spec.rb
index eae0b516b..8ed83b31f 100644
--- a/spec/script/handle-mail-replies_spec.rb
+++ b/spec/script/handle-mail-replies_spec.rb
@@ -54,5 +54,15 @@ describe "When filtering" do
r = mail_reply_test("track-response-messageclass-oof.email")
r.status.should == 2
end
+
+ it "should detect an Outlook(?)-style out-of-office" do
+ r = mail_reply_test("track-response-outlook-oof.email")
+ r.status.should == 2
+ end
+
+ it "should detect an ABCMail-style out-of-office" do
+ r = mail_reply_test("track-response-abcmail-oof.email")
+ r.status.should == 2
+ end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index ffe48c731..5c5cd9a7f 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,7 +1,7 @@
# This file is copied to ~/spec when you run 'ruby script/generate rspec'
# from the project root directory.
ENV["RAILS_ENV"] ||= 'test'
-require File.expand_path(File.join(File.dirname(__FILE__),'..','config','environment'))
+require File.expand_path(File.join('..', '..', 'config', 'environment'), __FILE__)
require 'spec/autorun'
require 'spec/rails'
@@ -17,16 +17,13 @@ config['REPLY_LATE_AFTER_DAYS'] = 20
# Uncomment the next line to use webrat's matchers
#require 'webrat/integrations/rspec-rails'
-# Requires supporting files with custom matchers and macros, etc,
-# in ./support/ and its subdirectories.
-Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
-
Spec::Runner.configure do |config|
# If you're not using ActiveRecord you should remove these
# lines, delete config/database.yml and disable :active_record
# in your config/boot.rb
- config.fixture_path = RAILS_ROOT + '/spec/fixtures/'
+ # fixture_path must end in a separator
+ config.fixture_path = File.join(Rails.root, 'spec', 'fixtures') + File::SEPARATOR
# == Fixtures
#
@@ -80,12 +77,21 @@ def load_file_fixture(file_name)
return content
end
-def rebuild_xapian_index
- # XXX could for speed call ActsAsXapian.rebuild_index directly, but would
- # need model name list, and would need to fix acts_as_xapian so can call writes
- # and reads mixed up (it asserts where it thinks it can't do this)
- rebuild_name = File.dirname(__FILE__) + '/../script/rebuild-xapian-index'
- Kernel.system(rebuild_name) or raise "failed to launch #{rebuild_name}, error bitcode #{$?}, exit status: #{$?.exitstatus}"
+def rebuild_xapian_index(terms = true, values = true, texts = true, dropfirst = true)
+ parse_all_incoming_messages
+ if dropfirst
+ begin
+ ActsAsXapian.readable_init
+ FileUtils.rm_r(ActsAsXapian.db_path)
+ rescue RuntimeError
+ end
+ ActsAsXapian.writable_init
+ end
+ verbose = false
+ # safe_rebuild=true, which involves forking to avoid memory leaks, doesn't work well with rspec.
+ # unsafe is significantly faster, and we can afford possible memory leaks while testing.
+ safe_rebuild = false
+ ActsAsXapian.rebuild_index(["PublicBody", "User", "InfoRequestEvent"].map{|m| m.constantize}, verbose, terms, values, texts, safe_rebuild)
end
def update_xapian_index
@@ -114,7 +120,7 @@ def validate_as_body(html)
end
def basic_auth_login(request, username = nil, password = nil)
- username = MySociety::Config.get('ADMIN_USERNAME') if username.nil?
+ username = MySociety::Config.get('ADMIN_USERNAME') if username.nil?
password = MySociety::Config.get('ADMIN_PASSWORD') if password.nil?
request.env["HTTP_AUTHORIZATION"] = "Basic " + Base64::encode64("#{username}:#{password}")
end
@@ -147,6 +153,14 @@ if $tempfilecount.nil?
end
end
+# to_ary differs in Ruby 1.8 and 1.9
+# @see http://yehudakatz.com/2010/01/02/the-craziest-fing-bug-ive-ever-seen/
+def safe_mock_model(model, args = {})
+ mock = mock_model(model, args)
+ mock.should_receive(:to_ary).any_number_of_times
+ mock
+end
+
def load_raw_emails_data(raw_emails)
raw_email = raw_emails(:useless_raw_email)
begin
@@ -155,3 +169,7 @@ def load_raw_emails_data(raw_emails)
end
raw_email.data = load_file_fixture("useless_raw_email.email")
end
+
+def parse_all_incoming_messages
+ IncomingMessage.find(:all).each{|x| x.parse_raw_email!}
+end
diff --git a/spec/views/public_body/show.rhtml_spec.rb b/spec/views/public_body/show.rhtml_spec.rb
index cd81888eb..a37d8be0d 100644
--- a/spec/views/public_body/show.rhtml_spec.rb
+++ b/spec/views/public_body/show.rhtml_spec.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../../spec_helper'
+require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
describe "when viewing a body" do
before do
diff --git a/spec/views/request/_after_actions.rhtml_spec.rb b/spec/views/request/_after_actions.rhtml_spec.rb
index c73f35d33..6a56e7a71 100644
--- a/spec/views/request/_after_actions.rhtml_spec.rb
+++ b/spec/views/request/_after_actions.rhtml_spec.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../../spec_helper'
+require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
describe 'when displaying actions that can be taken with regard to a request' do
diff --git a/spec/views/request/_describe_state.rhtml_spec.rb b/spec/views/request/_describe_state.rhtml_spec.rb
index 9fb776db3..ccf653b1b 100644
--- a/spec/views/request/_describe_state.rhtml_spec.rb
+++ b/spec/views/request/_describe_state.rhtml_spec.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../../spec_helper'
+require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
describe 'when showing the form for describing the state of a request' do
diff --git a/spec/views/request/list.rhtml_spec.rb b/spec/views/request/list.rhtml_spec.rb
index 60a28eec5..1f86ec641 100644
--- a/spec/views/request/list.rhtml_spec.rb
+++ b/spec/views/request/list.rhtml_spec.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../../spec_helper'
+require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
describe "when listing recent requests" do
diff --git a/spec/views/request/show.rhtml_spec.rb b/spec/views/request/show.rhtml_spec.rb
index ca4663afc..adb244f47 100644
--- a/spec/views/request/show.rhtml_spec.rb
+++ b/spec/views/request/show.rhtml_spec.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../../spec_helper'
+require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
describe 'when viewing an information request' do
diff --git a/spec/views/request_game/play.rhtml_spec.rb b/spec/views/request_game/play.rhtml_spec.rb
index e90861e34..24fb6d75d 100644
--- a/spec/views/request_game/play.rhtml_spec.rb
+++ b/spec/views/request_game/play.rhtml_spec.rb
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/../../spec_helper'
+require File.expand_path(File.join('..', '..', '..', 'spec_helper'), __FILE__)
describe 'when viewing the request game' do
diff --git a/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb b/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb
index 4671b79da..fb6a08979 100644
--- a/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb
+++ b/vendor/plugins/acts_as_xapian/lib/acts_as_xapian.rb
@@ -35,7 +35,6 @@ module ActsAsXapian
@@db = nil
@@db_path = nil
@@writable_db = nil
- @@writable_suffix = nil
@@init_values = []
$acts_as_xapian_class_var_init = true
end
@@ -217,7 +216,6 @@ module ActsAsXapian
prepare_environment
full_path = @@db_path + suffix
- raise "writable_suffix/suffix inconsistency" if @@writable_suffix && @@writable_suffix != suffix
# for indexing
@@writable_db = Xapian::flint_open(full_path, Xapian::DB_CREATE_OR_OPEN)
@@ -225,7 +223,6 @@ module ActsAsXapian
@@term_generator.set_flags(Xapian::TermGenerator::FLAG_SPELLING, 0)
@@term_generator.database = @@writable_db
@@term_generator.stemmer = @@stemmer
- @@writable_suffix = suffix
end
######################################################################
@@ -580,16 +577,20 @@ module ActsAsXapian
STDERR.puts(detail.backtrace.join("\n") + "\nFAILED ActsAsXapian.update_index job #{id} #{$!} " + (job.nil? ? "" : "model " + job.model + " id " + job.model_id.to_s))
end
end
- end
+ # We close the database when we're finished to remove the lock file. Since writable_init
+ # reopens it and recreates the environment every time we don't need to do further cleanup
+ ActsAsXapian.writable_db.close
+ end
+
# You must specify *all* the models here, this totally rebuilds the Xapian
# database. You'll want any readers to reopen the database after this.
#
# Incremental update_index calls above are suspended while this rebuild
# happens (i.e. while the .new database is there) - any index update jobs
# are left in the database, and will run after the rebuild has finished.
- def ActsAsXapian.rebuild_index(model_classes, verbose = false)
- raise "when rebuilding all, please call as first and only thing done in process / task" if not ActsAsXapian.writable_db.nil?
+ def ActsAsXapian.rebuild_index(model_classes, verbose = false, terms = true, values = true, texts = true, safe_rebuild = true)
+ #raise "when rebuilding all, please call as first and only thing done in process / task" if not ActsAsXapian.writable_db.nil?
prepare_environment
@@ -600,7 +601,47 @@ module ActsAsXapian
FileUtils.rm_r(new_path)
end
- # Index everything
+ # Index everything
+ if safe_rebuild
+ _rebuild_index_safely(model_classes, verbose, terms, values, texts)
+ else
+ # Save time by running the indexing in one go and in-process
+ ActsAsXapian.writable_init(".new")
+ for model_class in model_classes
+ STDOUT.puts("ActsAsXapian.rebuild_index: Rebuilding #{model_class.to_s}") if verbose
+ model_class.find(:all).each do |model|
+ STDOUT.puts("ActsAsXapian.rebuild_index #{model_class} #{model.id}") if verbose
+ model.xapian_index(terms, values, texts)
+ end
+ end
+ # make sure everything is written and close
+ ActsAsXapian.writable_db.flush
+ ActsAsXapian.writable_db.close
+ end
+
+ # Rename into place
+ old_path = ActsAsXapian.db_path
+ temp_path = ActsAsXapian.db_path + ".tmp"
+ if File.exist?(temp_path)
+ raise "temporary database found " + temp_path + " which is not Xapian flint database, please delete for me" if not File.exist?(File.join(temp_path, "iamflint"))
+ FileUtils.rm_r(temp_path)
+ end
+ if File.exist?(old_path)
+ FileUtils.mv old_path, temp_path
+ end
+ FileUtils.mv new_path, old_path
+
+ # Delete old database
+ if File.exist?(temp_path)
+ raise "old database now at " + temp_path + " is not Xapian flint database, please delete for me" if not File.exist?(File.join(temp_path, "iamflint"))
+ FileUtils.rm_r(temp_path)
+ end
+
+ # You'll want to restart your FastCGI or Mongrel processes after this,
+ # so they get the new db
+ end
+
+ def ActsAsXapian._rebuild_index_safely(model_classes, verbose, terms, values, texts)
batch_size = 1000
for model_class in model_classes
model_class_count = model_class.count
@@ -621,13 +662,14 @@ module ActsAsXapian
# (so doc ids and so on aren't preserved across the fork)
ActsAsXapian.writable_init(".new")
STDOUT.puts("ActsAsXapian.rebuild_index: New batch. #{model_class.to_s} from #{i} to #{i + batch_size} of #{model_class_count} pid #{Process.pid.to_s}") if verbose
- models = model_class.find(:all, :limit => batch_size, :offset => i, :order => :id)
- for model in models
+ model_class.find(:all, :limit => batch_size, :offset => i, :order => :id).each do |model|
STDOUT.puts("ActsAsXapian.rebuild_index #{model_class} #{model.id}") if verbose
- model.xapian_index
+ model.xapian_index(terms, values, texts)
end
# make sure everything is written
ActsAsXapian.writable_db.flush
+ # close database
+ ActsAsXapian.writable_db.close
# database connection won't survive a fork, so shut it down
ActiveRecord::Base.connection.disconnect!
# brutal exit, so other shutdown code not run (for speed and safety)
@@ -636,27 +678,6 @@ module ActsAsXapian
end
end
-
- # Rename into place
- old_path = ActsAsXapian.db_path
- temp_path = ActsAsXapian.db_path + ".tmp"
- if File.exist?(temp_path)
- raise "temporary database found " + temp_path + " which is not Xapian flint database, please delete for me" if not File.exist?(File.join(temp_path, "iamflint"))
- FileUtils.rm_r(temp_path)
- end
- if File.exist?(old_path)
- FileUtils.mv old_path, temp_path
- end
- FileUtils.mv new_path, old_path
-
- # Delete old database
- if File.exist?(temp_path)
- raise "old database now at " + temp_path + " is not Xapian flint database, please delete for me" if not File.exist?(File.join(temp_path, "iamflint"))
- FileUtils.rm_r(temp_path)
- end
-
- # You'll want to restart your FastCGI or Mongrel processes after this,
- # so they get the new db
end
######################################################################
@@ -717,7 +738,7 @@ module ActsAsXapian
end
# Store record in the Xapian database
- def xapian_index
+ def xapian_index(terms = true, values = true, texts = true)
# if we have a conditional function for indexing, call it and destory object if failed
if self.class.xapian_options.include?(:if)
if_value = xapian_value(self.class.xapian_options[:if], :boolean)
@@ -727,37 +748,90 @@ module ActsAsXapian
end
end
+ if self.class.to_s == "PublicBody" and self.url_name == "tgq"
+
+#require 'ruby-debug'
+#debugger
+ end
# otherwise (re)write the Xapian record for the object
- doc = Xapian::Document.new
- ActsAsXapian.term_generator.document = doc
+ ActsAsXapian.readable_init
+ existing_query = Xapian::Query.new("I" + self.xapian_document_term)
+ ActsAsXapian.enquire.query = existing_query
+ match = ActsAsXapian.enquire.mset(0,1,1).matches[0]
- doc.data = self.xapian_document_term
+ if !match.nil?
+ doc = match.document
+ else
+ doc = Xapian::Document.new
+ doc.data = self.xapian_document_term
+ doc.add_term("M" + self.class.to_s)
+ doc.add_term("I" + doc.data)
+ end
+ ActsAsXapian.term_generator.document = doc
+ # work out what to index. XXX for now, this is only selective on "terms".
+ terms_to_index = []
+ drop_all_terms = false
+ if terms and self.xapian_options[:terms]
+ terms_to_index = self.xapian_options[:terms].dup
+ if terms.is_a?(String)
+ terms_to_index.reject!{|term| !terms.include?(term[1])}
+ if terms_to_index.length == self.xapian_options[:terms].length
+ drop_all_terms = true
+ end
+ else
+ drop_all_terms = true
+ end
+ end
+ texts_to_index = []
+ if texts and self.xapian_options[:texts]
+ texts_to_index = self.xapian_options[:texts]
+ end
+ values_to_index = []
+ if values and self.xapian_options[:values]
+ values_to_index = self.xapian_options[:values]
+ end
- doc.add_term("M" + self.class.to_s)
- doc.add_term("I" + doc.data)
- if self.xapian_options[:terms]
- for term in self.xapian_options[:terms]
- value = xapian_value(term[0])
- if value.kind_of?(Array)
+ # clear any existing values that we might want to replace
+ if drop_all_terms && texts
+ # as an optimisation, if we're reindexing all of both, we remove everything
+ doc.clear_terms
+ doc.add_term("M" + self.class.to_s)
+ doc.add_term("I" + doc.data)
+ else
+ term_prefixes_to_index = terms_to_index.map {|x| x[1]}
+ for existing_term in doc.terms
+ first_letter = existing_term.term[0...1]
+ if !"MI".include?(first_letter)
+ if first_letter.match("^[A-Z]+") && terms_to_index.include?(first_letter)
+ doc.remove_term(existing_term.term)
+ elsif texts
+ doc.remove_term(existing_term.term)
+ end
+ end
+ end
+ end
+ # for now, we always clear values
+ doc.clear_values
+
+ for term in terms_to_index
+ value = xapian_value(term[0])
+ if value.kind_of?(Array)
for v in value
- doc.add_term(term[1] + v)
+ doc.add_term(term[1] + v)
end
- else
+ else
doc.add_term(term[1] + value)
- end
- end
+ end
end
- if self.xapian_options[:values]
- for value in self.xapian_options[:values]
- doc.add_value(value[1], xapian_value(value[0], value[3]))
- end
+ # values
+ for value in values_to_index
+ doc.add_value(value[1], xapian_value(value[0], value[3]))
end
- if self.xapian_options[:texts]
- for text in self.xapian_options[:texts]
- ActsAsXapian.term_generator.increase_termpos # stop phrases spanning different text fields
- # XXX the "1" here is a weight that could be varied for a boost function
- ActsAsXapian.term_generator.index_text(xapian_value(text, nil, true), 1)
- end
+ # texts
+ for text in texts_to_index
+ ActsAsXapian.term_generator.increase_termpos # stop phrases spanning different text fields
+ # XXX the "1" here is a weight that could be varied for a boost function
+ ActsAsXapian.term_generator.index_text(xapian_value(text, nil, true), 1)
end
ActsAsXapian.writable_db.replace_document("I" + doc.data, doc)
diff --git a/vendor/plugins/acts_as_xapian/lib/tasks/xapian.rake b/vendor/plugins/acts_as_xapian/lib/tasks/xapian.rake
index 7168895f9..d18cd07d5 100644
--- a/vendor/plugins/acts_as_xapian/lib/tasks/xapian.rake
+++ b/vendor/plugins/acts_as_xapian/lib/tasks/xapian.rake
@@ -15,14 +15,27 @@ namespace :xapian do
# Parameters - specify 'models="PublicBody User"' to say which models
# you index with Xapian.
- # This totally rebuilds the database, so you will want to restart any
- # web server afterwards to make sure it gets the changes, rather than
- # still pointing to the old deleted database. Specify "verbose=true" to
- # print model name as it is run.
+
+ # This totally rebuilds the database, so you will want to restart
+ # any web server afterwards to make sure it gets the changes,
+ # rather than still pointing to the old deleted database. Specify
+ # "verbose=true" to print model name as it is run. By default,
+ # all of the terms, values and texts are reindexed. You can
+ # suppress any of these by specifying, for example, "texts=false".
+ # You can specify that only certain terms should be updated by
+ # specifying their prefix(es) as a string, e.g. "terms=IV" will
+ # index the two terms I and V (and "terms=false" will index none,
+ # and "terms=true", the default, will index all)
+
+
desc 'Completely rebuilds Xapian search index (must specify all models)'
task :rebuild_index => :environment do
raise "specify ALL your models with models=\"ModelName1 ModelName2\" as parameter" if ENV['models'].nil?
- ActsAsXapian.rebuild_index(ENV['models'].split(" ").map{|m| m.constantize}, ENV['verbose'] ? true : false)
+ ActsAsXapian.rebuild_index(ENV['models'].split(" ").map{|m| m.constantize},
+ ENV['verbose'] ? true : false,
+ ENV['terms'] == "false" ? false : ENV['terms'],
+ ENV['values'] == "false" ? false : ENV['values'],
+ ENV['texts'] == "false" ? false : true)
end
# Parameters - are models, query, offset, limit, sort_by_prefix,
diff --git a/vendor/plugins/has_tag_string/lib/has_tag_string.rb b/vendor/plugins/has_tag_string/lib/has_tag_string.rb
index 49b82ca0d..b982bc3a0 100644
--- a/vendor/plugins/has_tag_string/lib/has_tag_string.rb
+++ b/vendor/plugins/has_tag_string/lib/has_tag_string.rb
@@ -98,7 +98,7 @@ module HasTagString
ret[tag.name_and_value] = 1
end
- return ret.keys
+ return ret.keys.sort
end
# Test to see if class is tagged with the given tag
diff --git a/vendor/rails-locales b/vendor/rails-locales
-Subproject d0fb0563129001c6114e351ba5738655733b833
+Subproject 7b769690775e9705f82da75aee3435e8dadebec