aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock17
-rw-r--r--app/controllers/public_body_controller.rb33
-rw-r--r--app/controllers/request_controller.rb29
-rw-r--r--app/controllers/track_controller.rb2
-rw-r--r--app/models/incoming_message.rb6
-rw-r--r--app/models/info_request.rb13
-rw-r--r--app/models/public_body.rb41
-rw-r--r--app/models/request_mailer.rb37
-rw-r--r--app/models/track_thing.rb7
-rw-r--r--app/views/admin_public_body/_form.rhtml3
-rw-r--r--app/views/admin_public_body/import_csv.rhtml2
-rw-r--r--app/views/admin_public_body/show.rhtml4
-rw-r--r--app/views/public_body/show.rhtml3
-rw-r--r--app/views/request/_request_listing_via_event.rhtml10
-rw-r--r--config/.gitignore1
-rw-r--r--config/environment.rb6
-rw-r--r--config/general.yml-example4
-rw-r--r--config/httpd.conf2
-rw-r--r--config/newrelic.yml-example54
-rw-r--r--db/migrate/20121022031914_add_disclosure_log.rb13
-rw-r--r--doc/INSTALL.md6
-rw-r--r--lib/configuration.rb1
-rw-r--r--lib/timezone_fixes.rb3
-rw-r--r--spec/controllers/request_controller_spec.rb63
-rw-r--r--spec/fixtures/files/forward-quoting-example-2.email318
-rw-r--r--spec/fixtures/files/forward-quoting-example.email73
-rw-r--r--spec/fixtures/public_body_translations.yml7
-rw-r--r--spec/lib/timezone_fixes_spec.rb68
-rw-r--r--spec/models/incoming_message_spec.rb17
-rw-r--r--spec/models/track_thing_spec.rb9
-rw-r--r--spec/views/public_body/show.rhtml_spec.rb1
32 files changed, 713 insertions, 144 deletions
diff --git a/Gemfile b/Gemfile
index 334c9aa07..03385e057 100644
--- a/Gemfile
+++ b/Gemfile
@@ -12,8 +12,8 @@ gem 'pg'
gem 'fast_gettext', '>= 0.6.0'
gem 'fastercsv', '>=1.5.5'
-gem 'gettext_i18n_rails', '>= 0.6.0', :git => "git://github.com/sebbacon/gettext_i18n_rails.git"
-gem 'gettext', '>= 1.9.3'
+gem 'gettext_i18n_rails', '>= 0.7.1'
+gem 'gettext', '~> 2.3.3'
gem 'json', '~> 1.5.1'
gem 'mahoro'
gem 'memcache-client', :require => 'memcache'
diff --git a/Gemfile.lock b/Gemfile.lock
index 58c5bcbe0..0969c5794 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,10 +1,3 @@
-GIT
- remote: git://github.com/sebbacon/gettext_i18n_rails.git
- revision: 2616d7e76872942d6e2351974a7e3fa4c2f933e2
- specs:
- gettext_i18n_rails (0.6.2)
- fast_gettext
-
GEM
remote: http://rubygems.org/
specs:
@@ -20,7 +13,7 @@ GEM
activesupport (2.3.14)
annotate (2.4.0)
archive-tar-minitar (0.5.2)
- capistrano (2.13.3)
+ capistrano (2.13.4)
highline
net-scp (>= 1.0.0)
net-sftp (>= 2.0.0)
@@ -30,8 +23,10 @@ GEM
fakeweb (1.3.0)
fast_gettext (0.6.8)
fastercsv (1.5.5)
- gettext (2.2.1)
+ gettext (2.3.3)
locale
+ gettext_i18n_rails (0.7.1)
+ fast_gettext (>= 0.4.8)
highline (1.6.13)
hoe (3.0.8)
rake (~> 0.8)
@@ -110,8 +105,8 @@ DEPENDENCIES
fakeweb
fast_gettext (>= 0.6.0)
fastercsv (>= 1.5.5)
- gettext (>= 1.9.3)
- gettext_i18n_rails (>= 0.6.0)!
+ gettext (~> 2.3.3)
+ gettext_i18n_rails (>= 0.7.1)
json (~> 1.5.1)
locale (>= 2.0.5)
mahoro
diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb
index b34e89b8d..8a4a65820 100644
--- a/app/controllers/public_body_controller.rb
+++ b/app/controllers/public_body_controller.rb
@@ -146,38 +146,7 @@ class PublicBodyController < ApplicationController
end
def list_all_csv
- public_bodies = PublicBody.find(:all, :order => 'url_name',
- :include => [:translations, :tags])
- report = FasterCSV.generate() do |csv|
- csv << [
- 'Name',
- 'Short name',
- # deliberately not including 'Request email'
- 'URL name',
- 'Tags',
- 'Home page',
- 'Publication scheme',
- 'Created at',
- 'Updated at',
- 'Version',
- ]
- public_bodies.each do |public_body|
- csv << [
- public_body.name,
- public_body.short_name,
- # DO NOT include request_email (we don't want to make it
- # easy to spam all authorities with requests)
- public_body.url_name,
- public_body.tag_string,
- public_body.calculated_home_page,
- public_body.publication_scheme,
- public_body.created_at,
- public_body.updated_at,
- public_body.version,
- ]
- end
- end
- send_data(report, :type=> 'text/csv; charset=utf-8; header=present',
+ send_data(PublicBody.export_csv, :type=> 'text/csv; charset=utf-8; header=present',
:filename => 'all-authorities.csv',
:disposition =>'attachment', :encoding => 'utf8')
end
diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb
index 396e6593a..3296db6b7 100644
--- a/app/controllers/request_controller.rb
+++ b/app/controllers/request_controller.rb
@@ -446,18 +446,19 @@ class RequestController < ApplicationController
return
end
+ calculated_status = @info_request.calculate_status
# Display advice for requester on what to do next, as appropriate
- if @info_request.calculate_status == 'waiting_response'
+ if calculated_status == 'waiting_response'
flash[:notice] = _("<p>Thank you! Hopefully your wait isn't too long.</p> <p>By law, you should get a response promptly, and normally before the end of <strong>
{{date_response_required_by}}</strong>.</p>",:date_response_required_by=>simple_date(@info_request.date_response_required_by))
redirect_to request_url(@info_request)
- elsif @info_request.calculate_status == 'waiting_response_overdue'
+ elsif calculated_status == 'waiting_response_overdue'
flash[:notice] = _("<p>Thank you! Hope you don't have to wait much longer.</p> <p>By law, you should have got a response promptly, and normally before the end of <strong>{{date_response_required_by}}</strong>.</p>",:date_response_required_by=>simple_date(@info_request.date_response_required_by))
redirect_to request_url(@info_request)
- elsif @info_request.calculate_status == 'waiting_response_very_overdue'
+ elsif calculated_status == 'waiting_response_very_overdue'
flash[:notice] = _("<p>Thank you! Your request is long overdue, by more than {{very_late_number_of_days}} working days. Most requests should be answered within {{late_number_of_days}} working days. You might like to complain about this, see below.</p>", :very_late_number_of_days => Configuration::reply_very_late_after_days, :late_number_of_days => Configuration::reply_late_after_days)
redirect_to unhappy_url(@info_request)
- elsif @info_request.calculate_status == 'not_held'
+ elsif calculated_status == 'not_held'
flash[:notice] = _("<p>Thank you! Here are some ideas on what to do next:</p>
<ul>
<li>To send your request to another authority, first copy the text of your request below, then <a href=\"{{find_authority_url}}\">find the other authority</a>.</li>
@@ -472,37 +473,37 @@ class RequestController < ApplicationController
:complain_url => CGI.escapeHTML(unhappy_url(@info_request)),
:other_means_url => CGI.escapeHTML(unhappy_url(@info_request)) + "#other_means")
redirect_to request_url(@info_request)
- elsif @info_request.calculate_status == 'rejected'
+ elsif calculated_status == 'rejected'
flash[:notice] = _("Oh no! Sorry to hear that your request was refused. Here is what to do now.")
redirect_to unhappy_url(@info_request)
- elsif @info_request.calculate_status == 'successful'
+ elsif calculated_status == 'successful'
flash[:notice] = _("<p>We're glad you got all the information that you wanted. If you write about or make use of the information, please come back and add an annotation below saying what you did.</p><p>If you found {{site_name}} useful, <a href=\"{{donation_url}}\">make a donation</a> to the charity which runs it.</p>", :site_name=>site_name, :donation_url => "http://www.mysociety.org/donate/")
redirect_to request_url(@info_request)
- elsif @info_request.calculate_status == 'partially_successful'
+ elsif calculated_status == 'partially_successful'
flash[:notice] = _("<p>We're glad you got some of the information that you wanted. If you found {{site_name}} useful, <a href=\"{{donation_url}}\">make a donation</a> to the charity which runs it.</p><p>If you want to try and get the rest of the information, here's what to do now.</p>", :site_name=>site_name, :donation_url=>"http://www.mysociety.org/donate/")
redirect_to unhappy_url(@info_request)
- elsif @info_request.calculate_status == 'waiting_clarification'
+ elsif calculated_status == 'waiting_clarification'
flash[:notice] = _("Please write your follow up message containing the necessary clarifications below.")
redirect_to respond_to_last_url(@info_request)
- elsif @info_request.calculate_status == 'gone_postal'
+ elsif calculated_status == 'gone_postal'
redirect_to respond_to_last_url(@info_request) + "?gone_postal=1"
- elsif @info_request.calculate_status == 'internal_review'
+ elsif calculated_status == 'internal_review'
flash[:notice] = _("<p>Thank you! Hopefully your wait isn't too long.</p><p>You should get a response within {{late_number_of_days}} days, or be told if it will take longer (<a href=\"{{review_url}}\">details</a>).</p>",:late_number_of_days => Configuration.reply_late_after_days, :review_url => unhappy_url(@info_request) + "#internal_review")
redirect_to request_url(@info_request)
- elsif @info_request.calculate_status == 'error_message'
+ elsif calculated_status == 'error_message'
flash[:notice] = _("<p>Thank you! We'll look into what happened and try and fix it up.</p><p>If the error was a delivery failure, and you can find an up to date FOI email address for the authority, please tell us using the form below.</p>")
redirect_to help_general_url(:action => 'contact')
- elsif @info_request.calculate_status == 'requires_admin'
+ elsif calculated_status == 'requires_admin'
flash[:notice] = _("Please use the form below to tell us more.")
redirect_to help_general_url(:action => 'contact')
- elsif @info_request.calculate_status == 'user_withdrawn'
+ elsif calculated_status == 'user_withdrawn'
flash[:notice] = _("If you have not done so already, please write a message below telling the authority that you have withdrawn your request. Otherwise they will not know it has been withdrawn.")
redirect_to respond_to_last_url(@info_request)
else
if @@custom_states_loaded
return self.theme_describe_state(@info_request)
else
- raise "unknown calculate_status " + @info_request.calculate_status
+ raise "unknown calculate_status " + calculated_status
end
end
end
diff --git a/app/controllers/track_controller.rb b/app/controllers/track_controller.rb
index 8e76b4c9f..51e081c88 100644
--- a/app/controllers/track_controller.rb
+++ b/app/controllers/track_controller.rb
@@ -180,7 +180,7 @@ class TrackController < ApplicationController
new_medium = params[:track_medium]
if new_medium == 'delete'
track_thing.destroy
- flash[:notice] = _("You are no longer following {{track_description}}", :track_description => track_thing.params[:list_description])
+ flash[:notice] = _("You are no longer following {{track_description}}.", :track_description => track_thing.params[:list_description])
redirect_to params[:r]
# Reuse code like this if we let medium change again.
#elsif new_medium == 'email_daily'
diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb
index 367909f57..48fbfe73a 100644
--- a/app/models/incoming_message.rb
+++ b/app/models/incoming_message.rb
@@ -347,12 +347,10 @@ class IncomingMessage < ActiveRecord::Base
text.gsub!(/^\s?#{name}[^\n]+\n([^\n]+\n)?\s?Sent by:[^\n]+\n.*/ims, "\n\n" + replacement)
# Some other sort of forwarding quoting
- # http://www.whatdotheyknow.com/request/224/response/326
- text.gsub!(/^#{name}[^\n]+\n[0-9\/:\s]+\s+To\s+FOI requests at.*/ims, "\n\n" + replacement)
+ text.gsub!(/^\s?#{name}\s+To\s+FOI requests at.*/ims, "\n\n" + replacement)
- # http://www.whatdotheyknow.com/request/how_do_the_pct_deal_with_retirin_33#incoming-930
# http://www.whatdotheyknow.com/request/229/response/809
- text.gsub!(/^From: [^\n]+\nSent: [^\n]+\nTo:\s+['"?]#{name}['"]?\nSubject:.*/ims, "\n\n" + replacement)
+ text.gsub!(/^\s?From: [^\n]+\n\s?Sent: [^\n]+\n\s?To:\s+['"]?#{name}['"]?\n\s?Subject:.*/ims, "\n\n" + replacement)
return text
diff --git a/app/models/info_request.rb b/app/models/info_request.rb
index 74a67b9d9..47424e573 100644
--- a/app/models/info_request.rb
+++ b/app/models/info_request.rb
@@ -584,12 +584,11 @@ public
# waiting_classification
# waiting_response_overdue
# waiting_response_very_overdue
- def calculate_status
- if @@custom_states_loaded
- return self.theme_calculate_status
- else
- self.base_calculate_status
+ def calculate_status(cached_value_ok=false)
+ if cached_value_ok && @cached_calculated_status
+ return @cached_calculated_status
end
+ @cached_calculated_status = @@custom_states_loaded ? self.theme_calculate_status : self.base_calculate_status
end
def base_calculate_status
@@ -871,8 +870,8 @@ public
end
end
- def display_status
- InfoRequest.get_status_description(self.calculate_status)
+ def display_status(cached_value_ok=false)
+ InfoRequest.get_status_description(self.calculate_status(cached_value_ok))
end
# Completely delete this request and all objects depending on it
diff --git a/app/models/public_body.rb b/app/models/public_body.rb
index c3bc060a4..57fe27767 100644
--- a/app/models/public_body.rb
+++ b/app/models/public_body.rb
@@ -413,7 +413,7 @@ class PublicBody < ActiveRecord::Base
next
end
- field_list = ['name', 'short_name', 'request_email', 'notes', 'publication_scheme', 'home_page', 'tag_string']
+ field_list = ['name', 'short_name', 'request_email', 'notes', 'publication_scheme', 'disclosure_log', 'home_page', 'tag_string']
if public_body = bodies_by_name[name] # Existing public body
available_locales.each do |locale|
@@ -500,6 +500,45 @@ class PublicBody < ActiveRecord::Base
return [errors, notes]
end
+ # Returns all public bodies (except for the internal admin authority) as csv
+ def self.export_csv
+ public_bodies = PublicBody.visible.find(:all, :order => 'url_name',
+ :include => [:translations, :tags])
+ FasterCSV.generate() do |csv|
+ csv << [
+ 'Name',
+ 'Short name',
+ # deliberately not including 'Request email'
+ 'URL name',
+ 'Tags',
+ 'Home page',
+ 'Publication scheme',
+ 'Disclosure log',
+ 'Notes',
+ 'Created at',
+ 'Updated at',
+ 'Version',
+ ]
+ public_bodies.each do |public_body|
+ csv << [
+ public_body.name,
+ public_body.short_name,
+ # DO NOT include request_email (we don't want to make it
+ # easy to spam all authorities with requests)
+ public_body.url_name,
+ public_body.tag_string,
+ public_body.calculated_home_page,
+ public_body.publication_scheme,
+ public_body.disclosure_log,
+ public_body.notes,
+ public_body.created_at,
+ public_body.updated_at,
+ public_body.version,
+ ]
+ end
+ end
+ end
+
# Does this user have the power of FOI officer for this body?
def is_foi_officer?(user)
user_domain = user.email_domain
diff --git a/app/models/request_mailer.rb b/app/models/request_mailer.rb
index 413e93e25..90c4c6b53 100644
--- a/app/models/request_mailer.rb
+++ b/app/models/request_mailer.rb
@@ -256,24 +256,47 @@ class RequestMailer < ApplicationMailer
def self.alert_overdue_requests()
info_requests = InfoRequest.find(:all,
:conditions => [
- "described_state = 'waiting_response' and awaiting_description = ? and user_id is not null", false
+ "described_state = 'waiting_response'
+ AND awaiting_description = ?
+ AND user_id is not null
+ AND (SELECT id
+ FROM user_info_request_sent_alerts
+ WHERE alert_type = 'very_overdue_1'
+ AND info_request_id = info_requests.id
+ AND user_id = info_requests.user_id
+ AND info_request_event_id = (SELECT max(id)
+ FROM info_request_events
+ WHERE event_type in ('sent',
+ 'followup_sent',
+ 'resent',
+ 'followup_resent')
+ AND info_request_id = info_requests.id)
+ ) IS NULL", false
],
:include => [ :user ]
)
for info_request in info_requests
alert_event_id = info_request.last_event_forming_initial_request.id
# Only overdue requests
- if ['waiting_response_overdue', 'waiting_response_very_overdue'].include?(info_request.calculate_status)
- if info_request.calculate_status == 'waiting_response_overdue'
+ calculated_status = info_request.calculate_status
+ if ['waiting_response_overdue', 'waiting_response_very_overdue'].include?(calculated_status)
+ if calculated_status == 'waiting_response_overdue'
alert_type = 'overdue_1'
- elsif info_request.calculate_status == 'waiting_response_very_overdue'
+ elsif calculated_status == 'waiting_response_very_overdue'
alert_type = 'very_overdue_1'
else
raise "unknown request status"
end
# For now, just to the user who created the request
- sent_already = UserInfoRequestSentAlert.find(:first, :conditions => [ "alert_type = ? and user_id = ? and info_request_id = ? and info_request_event_id = ?", alert_type, info_request.user_id, info_request.id, alert_event_id])
+ sent_already = UserInfoRequestSentAlert.find(:first, :conditions => [ "alert_type = ?
+ AND user_id = ?
+ AND info_request_id = ?
+ AND info_request_event_id = ?",
+ alert_type,
+ info_request.user_id,
+ info_request.id,
+ alert_event_id])
if sent_already.nil?
# Alert not yet sent for this user, so send it
store_sent = UserInfoRequestSentAlert.new
@@ -284,9 +307,9 @@ class RequestMailer < ApplicationMailer
# Only send the alert if the user can act on it by making a followup
# (otherwise they are banned, and there is no point sending it)
if info_request.user.can_make_followup?
- if info_request.calculate_status == 'waiting_response_overdue'
+ if calculated_status == 'waiting_response_overdue'
RequestMailer.deliver_overdue_alert(info_request, info_request.user)
- elsif info_request.calculate_status == 'waiting_response_very_overdue'
+ elsif calculated_status == 'waiting_response_very_overdue'
RequestMailer.deliver_very_overdue_alert(info_request, info_request.user)
else
raise "unknown request status"
diff --git a/app/models/track_thing.rb b/app/models/track_thing.rb
index d1cef4d4d..2a61eb858 100644
--- a/app/models/track_thing.rb
+++ b/app/models/track_thing.rb
@@ -107,7 +107,12 @@ class TrackThing < ActiveRecord::Base
end
descriptions = []
if varieties.include? _("requests")
- descriptions << _("requests which are {{list_of_statuses}}", :list_of_statuses => Array(statuses).sort.join(_(' or ')))
+ if statuses.empty?
+ # HACK: Relies on the 'descriptions.sort' below to luckily put this first
+ descriptions << _("all requests")
+ else
+ descriptions << _("requests which are {{list_of_statuses}}", :list_of_statuses => Array(statuses).sort.join(_(' or ')))
+ end
varieties -= [_("requests")]
end
if descriptions.empty? and varieties.empty?
diff --git a/app/views/admin_public_body/_form.rhtml b/app/views/admin_public_body/_form.rhtml
index d854b53f5..0d6ae51e2 100644
--- a/app/views/admin_public_body/_form.rhtml
+++ b/app/views/admin_public_body/_form.rhtml
@@ -38,6 +38,9 @@
<p><label for="<%= form_tag_id(t.object_name, :publication_scheme, locale) %>">Publication scheme URL</label><br/>
<%= t.text_field :publication_scheme, :size => 60, :id => form_tag_id(t.object_name, :publication_scheme, locale) %></p>
+ <p><label for="<%= form_tag_id(t.object_name, :disclosure_log, locale) %>">Disclosure log URL</label><br/>
+ <%= t.text_field :disclosure_log, :size => 60, :id => form_tag_id(t.object_name, :disclosure_log, locale) %></p>
+
<p><label for="<%= form_tag_id(t.object_name, :notes, locale) %>">Public notes</label> <small>(HTML, for users to consider when making FOI requests to the authority)</small><br/>
<%= t.text_area :notes, :rows => 3, :cols => 60, :id => form_tag_id(t.object_name, :notes, locale) %></p>
</div>
diff --git a/app/views/admin_public_body/import_csv.rhtml b/app/views/admin_public_body/import_csv.rhtml
index 4a03d0665..62908ba52 100644
--- a/app/views/admin_public_body/import_csv.rhtml
+++ b/app/views/admin_public_body/import_csv.rhtml
@@ -51,7 +51,7 @@
</blockquote>
<p>Supported fields: name (i18n), short_name (i18n), request_email (i18n), notes (i18n),
- publication_scheme (i18n), home_page, tag_string (tags separated by spaces).</p>
+ publication_scheme (i18n), disclosure_log (i18n), home_page, tag_string (tags separated by spaces).</p>
<p><strong>Note:</strong> Choose <strong>dry run</strong> to test, without
actually altering the database. Choose <strong>upload</strong> to actually
diff --git a/app/views/admin_public_body/show.rhtml b/app/views/admin_public_body/show.rhtml
index fa17d4027..094007c02 100644
--- a/app/views/admin_public_body/show.rhtml
+++ b/app/views/admin_public_body/show.rhtml
@@ -9,9 +9,9 @@
for column in columns %>
<b><%= column.human_name %>:</b>
- <% if column.name == 'home_page' and !column.name.empty? %>
+ <% if ['home_page', 'publication_scheme', 'disclosure_log'].include? column.name %>
<%= link_to(h(@public_body.send(column.name)), @public_body.send(column.name)) %>
- <% elsif column.name == 'request_email' and !column.name.empty? %>
+ <% elsif column.name == 'request_email' %>
<%= link_to(h(@public_body.send(column.name)), "mailto:#{@public_body.send(column.name)}") %>
<% if !@public_body.is_requestable? %>
(not requestable due to: <%=h @public_body.not_requestable_reason %><% if @public_body.is_followupable? %>; but followupable<% end %>)
diff --git a/app/views/public_body/show.rhtml b/app/views/public_body/show.rhtml
index b56556d5d..e13f9d1c0 100644
--- a/app/views/public_body/show.rhtml
+++ b/app/views/public_body/show.rhtml
@@ -14,6 +14,9 @@
<% if !@public_body.publication_scheme.empty? %>
<%= link_to _('Publication scheme'), @public_body.publication_scheme %><br>
<% end %>
+ <% unless @public_body.disclosure_log.empty? %>
+ <%= link_to _('Disclosure log'), @public_body.disclosure_log %><br>
+ <% end %>
<% if @public_body.has_tag?("charity") %>
<% for tag_value in @public_body.get_tag_values("charity") %>
<% if tag_value.match(/^SC/) %>
diff --git a/app/views/request/_request_listing_via_event.rhtml b/app/views/request/_request_listing_via_event.rhtml
index e3abfe393..ee1cc079a 100644
--- a/app/views/request/_request_listing_via_event.rhtml
+++ b/app/views/request/_request_listing_via_event.rhtml
@@ -17,13 +17,13 @@ end %>
</span>
<div class="requester">
<% if event.event_type == 'sent' %>
- <%= _('Request sent to {{public_body_name}} by {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at )) %>
+ <%= _('Request sent to {{public_body_name}} by {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at )) %>
<% elsif event.event_type == 'followup_sent' %>
<%=event.display_status %>
- <%= _('sent to {{public_body_name}} by {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at )) %>
+ <%= _('sent to {{public_body_name}} by {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at )) %>
<% elsif event.event_type == 'response' %>
<%=event.display_status %>
- <%= _('by {{public_body_name}} to {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at )) %>
+ <%= _('by {{public_body_name}} to {{info_request_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>request_user_link_absolute(info_request),:date=>simple_date(event.created_at )) %>
<% elsif event.event_type == 'comment' %>
<%= _('Request to {{public_body_name}} by {{info_request_user}}. Annotated by {{event_comment_user}} on {{date}}.',:public_body_name=>public_body_link_absolute(info_request.public_body),:info_request_user=>user_link_absolute(info_request.user),:event_comment_user=>user_link_absolute(event.comment.user),:date=>simple_date(event.created_at)) %>
<% else %>
@@ -35,12 +35,12 @@ end %>
</div>
<span class="bottomline icon_<%= info_request.calculate_status %>">
<strong>
- <%= info_request.display_status %>
+ <%= info_request.display_status(cached_value_ok=true) %>
</strong><br>
</span>
</div>
<div class="request_right">
- <span class="desc">
+ <span class="desc">
<%= highlight_and_excerpt(event.search_text_main(true), @highlight_words, 150) %>
</span>
</div>
diff --git a/config/.gitignore b/config/.gitignore
index 78d586ea8..5ad2de008 100644
--- a/config/.gitignore
+++ b/config/.gitignore
@@ -7,3 +7,4 @@ logrotate
memcached.yml
*.deployed
deploy.yml
+newrelic.yml
diff --git a/config/environment.rb b/config/environment.rb
index 3ce887c03..8076464a5 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -77,6 +77,12 @@ Rails::Initializer.run do |config|
# Make Active Record use UTC-base instead of local time
config.active_record.default_timezone = :utc
+ # This is the timezone that times and dates are displayed in
+ # Note that having set a zone, the Active Record
+ # time_zone_aware_attributes flag is on, so times from models
+ # will be in this time zone
+ config.time_zone = Configuration::time_zone
+
config.after_initialize do
require 'routing_filters.rb'
end
diff --git a/config/general.yml-example b/config/general.yml-example
index 1b30ffce6..0d6ecfc11 100644
--- a/config/general.yml-example
+++ b/config/general.yml-example
@@ -16,6 +16,10 @@ DOMAIN: '127.0.0.1:3000'
# (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
ISO_COUNTRY_CODE: GB
+# This is the timezone that times and dates are displayed in
+# If not set defaults to UTC.
+#TIME_ZONE: Australia/Sydney
+
# These feeds are displayed accordingly on the Alaveteli "blog" page:
BLOG_FEED: 'http://www.mysociety.org/category/projects/whatdotheyknow/feed/'
TWITTER_USERNAME: 'whatdotheyknow'
diff --git a/config/httpd.conf b/config/httpd.conf
index 440da0d87..acf37d97c 100644
--- a/config/httpd.conf
+++ b/config/httpd.conf
@@ -46,7 +46,7 @@ RewriteRule ^/request/((\d{1,3})\d*)/(response/\d+/attach/(html/)?\d+/.+) /views
<IfModule mod_passenger.c>
# Set this to something like 100 if you have memory leak issues
- PassengerMaxRequests 20
+ PassengerMaxRequests 500
PassengerResolveSymlinksInDocumentRoot on
# Recommend setting this to 3 or less on servers with 512MB RAM
PassengerMaxPoolSize 6
diff --git a/config/newrelic.yml-example b/config/newrelic.yml-example
index 8da7e26bd..ce19cca54 100644
--- a/config/newrelic.yml-example
+++ b/config/newrelic.yml-example
@@ -15,7 +15,7 @@ common: &default_settings
# account. This key binds your Agent's data to your account in the
# New Relic service.
#license_key: ''
-
+
# Agent Enabled (Ruby/Rails Only)
# Use this setting to force the agent to run or not run.
# Default is 'auto' which means the agent will install and run only
@@ -23,21 +23,21 @@ common: &default_settings
# it from running with Rake or the console. Set to false to
# completely turn the agent off regardless of the other settings.
# Valid values are true, false and auto.
- # agent_enabled: auto
-
+ agent_enabled: false
+
# Application Name
# Set this to be the name of your application as you'd like it show
# up in New Relic. New Relic will then auto-map instances of your application
# into a New Relic "application" on your home dashboard page. If you want
# to map this instance into multiple apps, like "AJAX Requests" and
# "All UI" then specify a semicolon-separated list of up to three
- # distinct names. If you comment this out, it defaults to the
+ # distinct names. If you comment this out, it defaults to the
# capitalized RAILS_ENV (i.e., Production, Staging, etc)
app_name: My Application
- # When "true", the agent collects performance data about your
+ # When "true", the agent collects performance data about your
# application and reports this data to the New Relic service at
- # newrelic.com. This global switch is normally overridden for each
+ # newrelic.com. This global switch is normally overridden for each
# environment below. (formerly called 'enabled')
monitor_mode: true
@@ -49,7 +49,7 @@ common: &default_settings
# information separate from that of your application. Specify its
# log level here.
log_level: info
-
+
# The newrelic agent communicates with the New Relic service via http by
# default. If you want to communicate via https to increase
# security, then turn on SSL by setting this value to true. Note,
@@ -86,7 +86,7 @@ common: &default_settings
# Rails: the RoR filter_parameter_logging excludes parameters
# Java: create a config setting called "ignored_params" and set it to
# a comma separated list of HTTP parameter names.
- # ex: ignored_params: credit_card, ssn, password
+ # ex: ignored_params: credit_card, ssn, password
capture_params: false
# Transaction tracer captures deep information about slow
@@ -94,12 +94,12 @@ common: &default_settings
# minute. Included in the transaction is the exact call sequence of
# the transactions including any SQL statements issued.
transaction_tracer:
-
+
# Transaction tracer is enabled by default. Set this to false to
# turn it off. This feature is only available at the Professional
# product level.
enabled: true
-
+
# Threshold in seconds for when to collect a transaction
# trace. When the response time of a controller action exceeds
# this threshold, a transaction trace will be recorded and sent to
@@ -107,13 +107,13 @@ common: &default_settings
# which will use the threshold for an dissatisfying Apdex
# controller action - four times the Apdex T value.
transaction_threshold: apdex_f
-
+
# When transaction tracer is on, SQL statements can optionally be
# recorded. The recorder has three modes, "off" which sends no
# SQL, "raw" which sends the SQL statement in its original form,
# and "obfuscated", which strips out numeric and string literals.
record_sql: obfuscated
-
+
# Threshold in seconds for when to collect stack trace for a SQL
# call. In other words, when SQL statements exceed this threshold,
# then capture and send to New Relic the current stack trace. This is
@@ -125,24 +125,24 @@ common: &default_settings
# set to false when using other adapters.
# explain_enabled: true
- # Threshold for query execution time below which query plans will not
+ # Threshold for query execution time below which query plans will not
# not be captured. Relevant only when `explain_enabled` is true.
# explain_threshold: 0.5
-
+
# Error collector captures information about uncaught exceptions and
# sends them to New Relic for viewing
error_collector:
-
+
# Error collector is enabled by default. Set this to false to turn
# it off. This feature is only available at the Professional
# product level.
enabled: true
-
- # Rails Only - tells error collector whether or not to capture a
- # source snippet around the place of the error when errors are View
+
+ # Rails Only - tells error collector whether or not to capture a
+ # source snippet around the place of the error when errors are View
# related.
- capture_source: true
-
+ capture_source: true
+
# To stop specific errors from reporting to New Relic, set this property
# to comma-separated values. Default is to ignore routing errors,
# which are how 404's get triggered.
@@ -152,7 +152,7 @@ common: &default_settings
# won't run. Useful when you are using the agent to monitor an
# external resource
# disable_samplers: true
-
+
# If you aren't interested in visibility in these areas, you can
# disable the instrumentation to reduce overhead.
#
@@ -160,8 +160,8 @@ common: &default_settings
# disable_activerecord_instrumentation: true
# disable_memcache_instrumentation: true
# disable_dj: true
-
- # Certain types of instrumentation such as GC stats will not work if
+
+ # Certain types of instrumentation such as GC stats will not work if
# you are running multi-threaded. Please let us know.
# multi_threaded = false
@@ -179,17 +179,17 @@ development:
<<: *default_settings
# Turn off communication to New Relic service in development mode (also
# 'enabled').
- # NOTE: for initial evaluation purposes, you may want to temporarily
+ # NOTE: for initial evaluation purposes, you may want to temporarily
# turn agent communication on in development mode.
monitor_mode: false
- # Rails Only - when running in Developer Mode, the New Relic Agent will
+ # Rails Only - when running in Developer Mode, the New Relic Agent will
# present performance information on the last 100 transactions you have
# executed since starting the app server.
# NOTE: There is substantial overhead when running in developer mode.
- # Do not use for production or load testing.
+ # Do not use for production or load testing.
developer_mode: true
-
+
# Enable textmate links
# textmate: true
diff --git a/db/migrate/20121022031914_add_disclosure_log.rb b/db/migrate/20121022031914_add_disclosure_log.rb
new file mode 100644
index 000000000..82ea7dbcd
--- /dev/null
+++ b/db/migrate/20121022031914_add_disclosure_log.rb
@@ -0,0 +1,13 @@
+class AddDisclosureLog < ActiveRecord::Migration
+ def self.up
+ add_column :public_bodies, :disclosure_log, :text, :null => false, :default => ""
+ add_column :public_body_versions, :disclosure_log, :text, :null => false, :default => ""
+ add_column :public_body_translations, :disclosure_log, :text
+ end
+
+ def self.down
+ remove_column :public_bodies, :disclosure_log
+ remove_column :public_body_versions, :disclosure_log
+ remove_column :public_body_translations, :disclosure_log
+ end
+end
diff --git a/doc/INSTALL.md b/doc/INSTALL.md
index b805ee0c5..e36d10e2b 100644
--- a/doc/INSTALL.md
+++ b/doc/INSTALL.md
@@ -222,6 +222,12 @@ Interlock Rails plugin, to cache content using memcached. You
probably don't want this in your development profile; the example
`memcached.yml` file disables this behaviour.
+Finally, copy `config/newrelic.yml-example` to `config/newrelic.yml`.
+This file contains configuration information for the New Relic
+performance management system. By default, monitoring is switched off
+by the `agent_enabled: false` setting. See https://github.com/newrelic/rpm
+for instructions on switching on local and remote performance analysis.
+
# Deployment
In the 'alaveteli' directory, run:
diff --git a/lib/configuration.rb b/lib/configuration.rb
index d679e1c33..0266d05a4 100644
--- a/lib/configuration.rb
+++ b/lib/configuration.rb
@@ -44,6 +44,7 @@ module Configuration
:SPECIAL_REPLY_VERY_LATE_AFTER_DAYS => 60,
:THEME_URL => "",
:THEME_URLS => [],
+ :TIME_ZONE => "UTC",
:TRACK_SENDER_EMAIL => 'contact@localhost',
:TRACK_SENDER_NAME => 'Alaveteli',
:TWITTER_USERNAME => '',
diff --git a/lib/timezone_fixes.rb b/lib/timezone_fixes.rb
index b830ded4e..e6d2f9470 100644
--- a/lib/timezone_fixes.rb
+++ b/lib/timezone_fixes.rb
@@ -1,4 +1,4 @@
-# Taken from
+# Taken from
# https://rails.lighthouseapp.com/projects/8994/tickets/2946
# http://github.com/rails/rails/commit/6f97ad07ded847f29159baf71050c63f04282170
@@ -11,7 +11,6 @@ module ActiveRecord
module ConnectionAdapters # :nodoc:
module Quoting
def quoted_date(value)
- value.to_s(:db)
if value.acts_like?(:time)
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb
index 77f43b618..b0223588e 100644
--- a/spec/controllers/request_controller_spec.rb
+++ b/spec/controllers/request_controller_spec.rb
@@ -245,7 +245,7 @@ describe RequestController, "when showing one request" do
response.should have_tag('#anyone_actions', /Add an annotation/)
end
end
-
+
describe 'when the request does not allow comments' do
it 'should not have a comment link' do
get :show, { :url_title => 'spam_1' },
@@ -253,7 +253,7 @@ describe RequestController, "when showing one request" do
response.should_not have_tag('#anyone_actions', /Add an annotation/)
end
end
-
+
describe 'when the request is being viewed by an admin' do
describe 'if the request is awaiting description' do
@@ -1709,15 +1709,17 @@ describe RequestController, "sending overdue request alerts" do
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
+ it "should send not actually send the overdue alert if the user is banned but should
+ record it as sent" do
user = info_requests(:naughty_chicken_request).user
user.ban_text = 'Banned'
user.save!
-
+ UserInfoRequestSentAlert.find_all_by_user_id(user.id).count.should == 0
RequestMailer.alert_overdue_requests
deliveries = ActionMailer::Base.deliveries
deliveries.size.should == 0
+ UserInfoRequestSentAlert.find_all_by_user_id(user.id).count.should > 0
end
it "should send a very overdue alert mail to creators of very overdue requests" do
@@ -1746,6 +1748,59 @@ describe RequestController, "sending overdue request alerts" do
assigns[:info_request].should == info_requests(:naughty_chicken_request)
end
+ it "should not resend alerts to people who've already received them" do
+ chicken_request = info_requests(:naughty_chicken_request)
+ chicken_request.outgoing_messages[0].last_sent_at = Time.now() - 60.days
+ chicken_request.outgoing_messages[0].save!
+ RequestMailer.alert_overdue_requests
+ chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/}
+ chicken_mails.size.should == 1
+ RequestMailer.alert_overdue_requests
+ chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/}
+ chicken_mails.size.should == 1
+ end
+
+ it 'should send alerts for requests where the last event forming the initial request is a followup
+ being sent following a request for clarification' do
+ chicken_request = info_requests(:naughty_chicken_request)
+ chicken_request.outgoing_messages[0].last_sent_at = Time.now() - 60.days
+ chicken_request.outgoing_messages[0].save!
+ RequestMailer.alert_overdue_requests
+ chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/}
+ chicken_mails.size.should == 1
+
+ # Request is waiting clarification
+ chicken_request.set_described_state('waiting_clarification')
+
+ # Followup message is sent
+ outgoing_message = OutgoingMessage.new(:status => 'ready',
+ :message_type => 'followup',
+ :info_request_id => chicken_request.id,
+ :body => 'Some text',
+ :what_doing => 'normal_sort')
+ outgoing_message.send_message
+ outgoing_message.save!
+
+ chicken_request = InfoRequest.find(chicken_request.id)
+
+ # Last event forming the request is now the followup
+ chicken_request.last_event_forming_initial_request.event_type.should == 'followup_sent'
+
+ # This isn't overdue, so no email
+ RequestMailer.alert_overdue_requests
+ chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/}
+ chicken_mails.size.should == 1
+
+ # Make the followup older
+ outgoing_message.last_sent_at = Time.now() - 60.days
+ outgoing_message.save!
+
+ # Now it should be alerted on
+ RequestMailer.alert_overdue_requests
+ chicken_mails = ActionMailer::Base.deliveries.select{|x| x.body =~ /chickens/}
+ chicken_mails.size.should == 2
+ end
+
end
describe RequestController, "sending unclassified new response reminder alerts" do
diff --git a/spec/fixtures/files/forward-quoting-example-2.email b/spec/fixtures/files/forward-quoting-example-2.email
new file mode 100644
index 000000000..936de3ddb
--- /dev/null
+++ b/spec/fixtures/files/forward-quoting-example-2.email
@@ -0,0 +1,318 @@
+From foi@example.com Wed May 28 14:14:39 2008
+To: FOI Person <EMAIL_TO>
+Subject: RE: Freedom of Information request
+Date: Wed, 28 May 2008 14:16:55 +0100
+MIME-Version: 1.0
+Content-Type: multipart/alternative;
+ boundary="----=_NextPart_000_008E_01C8C0CD.7BC69660"
+
+This is a multi-part message in MIME format.
+
+------=_NextPart_000_008E_01C8C0CD.7BC69660
+Content-Type: text/plain;
+ charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+
+Dear Mr Smith
+
+Some information
+
+Yours sincerely
+
+Foi Officer
+
+Please note that my email address has changed to foi@example.com
+
+
+From: Foi Officer [mailto:foi@example.com] On Behalf Of Foi Officer
+Sent: 19 May 2008 12:24
+To: Bob Smith
+Subject: RE: Freedom of Information request
+
+Dear Mr Smith
+
+Some information
+
+Please note that my email address has changed to foi@example.com
+
+
+-----Original Message-----
+From: Bob Smith [mailto:request-xxx-xxxxx@whatdotheyknow.com]
+Sent: 18 May 2008 14:22
+To: FOI requests at Example
+Subject: Freedom of Information request
+ Dear Sir or Madam,
+
+ Some information?
+
+ Yours faithfully,
+ Bob Smith
+ -------------------------------------------------------------------
+ Is this the wrong address for Freedom of Information
+ requests to Example? If so please let us know
+ by emailing xxx@whatdotheyknow.com - we'll make sure future ones go
+ to the right place.
+ -------------------------------------------------------------------
+
+
+------=_NextPart_000_008E_01C8C0CD.7BC69660
+Content-Type: text/html;
+ charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+
+<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:st1="urn:schemas-microsoft-com:office:smarttags" xmlns="http://www.w3.org/TR/REC-html40">
+
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=us-ascii">
+<meta name=ProgId content=Word.Document>
+<meta name=Generator content="Microsoft Word 11">
+<meta name=Originator content="Microsoft Word 11">
+<link rel=File-List href="cid:filelist.xml@01C8C0CD.7B3EA1B0">
+<link rel=Edit-Time-Data href="cid:editdata.mso">
+<!--[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]-->
+<title>RE: Freedom of Information request </title>
+<o:SmartTagType namespaceuri="urn:schemas-microsoft-com:office:smarttags"
+ name="Street"/>
+<o:SmartTagType namespaceuri="urn:schemas-microsoft-com:office:smarttags"
+ name="address"/>
+<!--[if gte mso 9]><xml>
+ <o:OfficeDocumentSettings>
+ <o:DoNotRelyOnCSS/>
+ </o:OfficeDocumentSettings>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:WordDocument>
+ <w:DisplayBackgroundShape/>
+ <w:SpellingState>Clean</w:SpellingState>
+ <w:GrammarState>Clean</w:GrammarState>
+ <w:DocumentKind>DocumentEmail</w:DocumentKind>
+ <w:EnvelopeVis/>
+ <w:ValidateAgainstSchemas/>
+ <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid>
+ <w:IgnoreMixedContent>false</w:IgnoreMixedContent>
+ <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText>
+ <w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel>
+ </w:WordDocument>
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <w:LatentStyles DefLockedState="false" LatentStyleCount="156">
+ </w:LatentStyles>
+</xml><![endif]--><!--[if !mso]>
+<style>
+st1\:*{behavior:url(#default#ieooui) }
+</style>
+<![endif]-->
+<style>
+<!--
+ /* Font Definitions */
+ @font-face
+ {font-family:Tahoma;
+ panose-1:2 11 6 4 3 5 4 4 2 4;
+ mso-font-charset:0;
+ mso-generic-font-family:swiss;
+ mso-font-pitch:variable;
+ mso-font-signature:1627421319 -2147483648 8 0 66047 0;}
+ /* Style Definitions */
+ p.MsoNormal, li.MsoNormal, div.MsoNormal
+ {mso-style-parent:"";
+ margin:0in;
+ margin-bottom:.0001pt;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";}
+a:link, span.MsoHyperlink
+ {color:blue;
+ text-decoration:underline;
+ text-underline:single;}
+a:visited, span.MsoHyperlinkFollowed
+ {color:blue;
+ text-decoration:underline;
+ text-underline:single;}
+p
+ {mso-margin-top-alt:auto;
+ margin-right:0in;
+ mso-margin-bottom-alt:auto;
+ margin-left:0in;
+ mso-pagination:widow-orphan;
+ font-size:12.0pt;
+ font-family:"Times New Roman";
+ mso-fareast-font-family:"Times New Roman";}
+span.EmailStyle18
+ {mso-style-type:personal-reply;
+ mso-style-noshow:yes;
+ mso-ansi-font-size:10.0pt;
+ mso-bidi-font-size:10.0pt;
+ font-family:Arial;
+ mso-ascii-font-family:Arial;
+ mso-hansi-font-family:Arial;
+ mso-bidi-font-family:Arial;
+ color:navy;}
+span.SpellE
+ {mso-style-name:"";
+ mso-spl-e:yes;}
+span.GramE
+ {mso-style-name:"";
+ mso-gram-e:yes;}
+@page Section1
+ {size:595.3pt 841.9pt;
+ margin:1.0in 1.0in 1.0in 1.0in;
+ mso-header-margin:35.3pt;
+ mso-footer-margin:35.3pt;
+ mso-paper-source:0;}
+div.Section1
+ {page:Section1;}
+-->
+</style>
+<!--[if gte mso 10]>
+<style>
+ /* Style Definitions */
+ table.MsoNormalTable
+ {mso-style-name:"Table Normal";
+ mso-tstyle-rowband-size:0;
+ mso-tstyle-colband-size:0;
+ mso-style-noshow: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";
+ mso-ansi-language:#0400;
+ mso-fareast-language:#0400;
+ mso-bidi-language:#0400;}
+</style>
+<![endif]--><!--[if gte mso 9]><xml>
+ <o:shapedefaults v:ext="edit" spidmax="1026" />
+</xml><![endif]--><!--[if gte mso 9]><xml>
+ <o:shapelayout v:ext="edit">
+ <o:idmap v:ext="edit" data="1" />
+ </o:shapelayout></xml><![endif]-->
+</head>
+
+<body lang=EN-GB link=blue vlink=blue style='tab-interval:.5in'>
+
+<div class=Section1>
+
+<p class=MsoNormal><font size=2 color=navy face=Arial><span style='font-size:
+10.0pt;font-family:Arial;color:navy'>Dear Mr Smith<o:p></o:p></span></font></p>
+
+<p class=MsoNormal><font size=2 color=navy face=Arial><span style='font-size:
+10.0pt;font-family:Arial;color:navy'><o:p>&nbsp;</o:p></span></font></p>
+
+<p class=MsoNormal><font size=2 color=navy face=Arial><span style='font-size:
+10.0pt;font-family:Arial;color:navy'>Some information.</p>
+
+<div>
+
+<p class=MsoNormal><strong><b><font size=2 color=navy face=Arial><span
+style='font-size:10.0pt;font-family:Arial;color:navy;mso-no-proof:yes'>Please
+note that my email address has changed to <a href="mailto:foi@example.com">foi@example.com</a></span></font></b></strong><font
+color=navy><span style='color:navy;mso-no-proof:yes'><o:p></o:p></span></font></p>
+
+<p class=MsoNormal><font size=3 color=navy face="Times New Roman"><span
+style='font-size:12.0pt;color:navy;mso-no-proof:yes'>&nbsp;<o:p></o:p></span></font></p>
+
+</div>
+
+<div>
+
+<div class=MsoNormal align=center style='text-align:center'><font size=3
+face="Times New Roman"><span lang=EN-US style='font-size:12.0pt;mso-ansi-language:
+EN-US'>
+
+<hr size=2 width="100%" align=center tabindex=-1>
+
+</span></font></div>
+
+<p class=MsoNormal><b><font size=2 face=Tahoma><span lang=EN-US
+style='font-size:10.0pt;font-family:Tahoma;mso-ansi-language:EN-US;font-weight:
+bold'>From:</span></font></b><font size=2 face=Tahoma><span lang=EN-US
+style='font-size:10.0pt;font-family:Tahoma;mso-ansi-language:EN-US'> Foi Officer
+[mailto:foi@example.com] <b><span style='font-weight:bold'>On Behalf Of </span></b>Foi
+Officer<br>
+<b><span style='font-weight:bold'>Sent:</span></b> 19 May 2008 12:24<br>
+<b><span style='font-weight:bold'>To:</span></b> Bob Smith<br>
+<b><span style='font-weight:bold'>Subject:</span></b> RE: Freedom of
+Information request </span></font><span lang=EN-US style='mso-ansi-language:
+EN-US'><o:p></o:p></span></p>
+
+</div>
+
+<p class=MsoNormal><font size=3 face="Times New Roman"><span style='font-size:
+12.0pt'><o:p>&nbsp;</o:p></span></font></p>
+
+<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>Dear Mr
+Smith</span></font> <o:p></o:p></p>
+
+<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>Some information. </span></font><o:p></o:p></p>
+
+
+<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>Yours
+sincerely</span></font> <o:p></o:p></p>
+
+<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>Foi Officer</span></font> <o:p></o:p></p>
+
+
+<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>-----Original
+Message-----</span></font> <br>
+<font size=2><span style='font-size:10.0pt'>From: Bob Smith [<a
+href="mailto:request-xxx-xxxx@whatdotheyknow.com">mailto:request-xxx-xxxxx@whatdotheyknow.com</a>]
+</span></font><br>
+<font size=2><span style='font-size:10.0pt'>Sent: 18 May 2008 14:22</span></font>
+<br>
+<font size=2><span style='font-size:10.0pt'>To: FOI requests at Example</span></font> <br>
+<font size=2><span style='font-size:10.0pt'>Subject: Freedom of Information
+request</span></font>
+<o:p></o:p></p>
+
+<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp;
+Dear Sir or Madam,</span></font> <o:p></o:p></p>
+
+<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp;
+Some information?</span></font> <font
+color=navy><span style='color:navy'><span
+style='mso-spacerun:yes'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></span></font><o:p></o:p></p>
+
+p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp;
+Yours faithfully,</span></font> <o:p></o:p></p>
+
+<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp;
+Bob Smith</span></font> <o:p></o:p></p>
+
+<p><font size=2 face="Times New Roman"><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp;
+-------------------------------------------------------------------</span></font>
+<br>
+<font size=2><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp; Is
+this the wrong address for Freedom of Information</span></font> <br>
+<font size=2><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp; requests
+to Example? If so please let us know</span></font> <br>
+<font size=2><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp; by
+emailing xxxx@whatdotheyknow.com - we'll make sure future ones go</span></font>
+<br>
+<font size=2><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp; to the
+right place.</span></font> <br>
+<font size=2><span style='font-size:10.0pt'>&nbsp;&nbsp;&nbsp;&nbsp;
+-------------------------------------------------------------------</span></font>
+<o:p></o:p></p>
+
+<p class=MsoNormal><font size=3 face="Times New Roman"><span style='font-size:
+12.0pt'><o:p>&nbsp;</o:p></span></font></p>
+
+</div>
+
+
+
+
+</FONT></DIV></body></html>
+
+------=_NextPart_000_008E_01C8C0CD.7BC69660--
+
+
+
diff --git a/spec/fixtures/files/forward-quoting-example.email b/spec/fixtures/files/forward-quoting-example.email
new file mode 100644
index 000000000..80b29c533
--- /dev/null
+++ b/spec/fixtures/files/forward-quoting-example.email
@@ -0,0 +1,73 @@
+From foi@example.com Thu Apr 17 16:45:16 2008
+Return-path: <foi@example.com>
+Envelope-to: request-xxx-xxxxx@whatdotheyknow.com
+Delivery-date: Thu, 17 Apr 2008 16:45:16 +0100
+To: FOI Person <EMAIL_TO>
+Subject: Re: Freedom of Information request
+MIME-Version: 1.0
+From: FOI Officer <foi@example.com>
+Date: Thu, 17 Apr 2008 16:44:53 +0100
+Content-Type: multipart/alternative;
+ boundary="=_alternative 0056816D8025742E_="
+
+This is a multipart message in MIME format.
+
+--=_alternative 0056816D8025742E_=
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+
+17/04/08
+
+Dear Mr Requester
+
+Some text.
+
+Bob Smith <request-xxx-xxxxx@whatdotheyknow.com>
+17/04/08 15:57
+
+To
+FOI requests <foi@example.com>
+cc
+
+Subject
+Freedom of Information request
+
+
+
+--=_alternative 0056816D8025742E_=
+Content-Type: text/html; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+
+
+<br><font size=2 face="sans-serif">17/04/08</font>
+<br>
+<br><font size=2 face="sans-serif">Dear Mr Requester</font>
+<br>
+<br><font size=2 face="Arial"><b>Some text.</font>
+<br>
+<br>
+<br>
+<table width=100%>
+<tr valign=top>
+<td width=40%><font size=1 face="sans-serif"><b> Bob Smith &lt;request-xxx-xxxxx@whatdotheyknow.com&gt;</b>
+</font>
+<p><font size=1 face="sans-serif">17/04/08 15:57</font>
+<td width=59%>
+<table width=100%>
+<tr>
+<td>
+<div align=right><font size=1 face="sans-serif">To</font></div>
+<td valign=top><font size=1 face="sans-serif">FOI requests at &lt;foi@example.com&gt;</font>
+<tr>
+<td>
+<div align=right><font size=1 face="sans-serif">cc</font></div>
+<td valign=top>
+<tr>
+<td>
+<div align=right><font size=1 face="sans-serif">Subject</font></div>
+<td valign=top><font size=1 face="sans-serif">Freedom of Information request</font></table>
+<br>
+
+
+--=_alternative 0056816D8025742E_=--
+
diff --git a/spec/fixtures/public_body_translations.yml b/spec/fixtures/public_body_translations.yml
index f3453e853..24b14c470 100644
--- a/spec/fixtures/public_body_translations.yml
+++ b/spec/fixtures/public_body_translations.yml
@@ -9,6 +9,7 @@ geraldine_es_public_body_translation:
locale: es
notes: ""
publication_scheme: ""
+ disclosure_log: ""
geraldine_en_public_body_translation:
name: Geraldine Quango
@@ -21,6 +22,7 @@ geraldine_en_public_body_translation:
locale: en
notes: ""
publication_scheme: ""
+ disclosure_log: ""
humpadink_es_public_body_translation:
name: "El Department for Humpadinking"
@@ -33,6 +35,7 @@ humpadink_es_public_body_translation:
locale: es
notes: Baguette
publication_scheme: ""
+ disclosure_log: ""
humpadink_en_public_body_translation:
name: "Department for Humpadinking"
@@ -45,6 +48,7 @@ humpadink_en_public_body_translation:
locale: en
notes: An albatross told me!!!
publication_scheme: ""
+ disclosure_log: ""
forlorn_en_public_body_translation:
name: "Department of Loneliness"
@@ -57,6 +61,7 @@ forlorn_en_public_body_translation:
locale: en
notes: A very lonely public body that no one has corresponded with
publication_scheme: ""
+ disclosure_log: ""
silly_walks_en_public_body_translation:
id: 6
@@ -69,6 +74,7 @@ silly_walks_en_public_body_translation:
url_name: msw
notes: You know the one.
publication_scheme: ""
+ disclosure_log: ""
sensible_walks_en_public_body_translation:
id: 7
@@ -81,3 +87,4 @@ sensible_walks_en_public_body_translation:
url_name: sensible_walks
notes: I bet you’ve never heard of it.
publication_scheme: ""
+ disclosure_log: ""
diff --git a/spec/lib/timezone_fixes_spec.rb b/spec/lib/timezone_fixes_spec.rb
index 8dd1ff480..a2bea5f64 100644
--- a/spec/lib/timezone_fixes_spec.rb
+++ b/spec/lib/timezone_fixes_spec.rb
@@ -1,68 +1,100 @@
# This is a test of the monkey patches in timezone_fixes.rb
# We use MailServerLogDone here just as a totally random model that has a datetime type.
-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe "when doing things with timezones" do
- it "should preserve time objects with local time conversion to default timezone UTC" do
+ it "should preserve time objects with local time conversion to default timezone UTC
+ and return them as UTC times when config.time_zone is UTC" do
with_env_tz 'America/New_York' do
with_active_record_default_timezone :utc do
time = Time.local(2000)
mail_server_log_done = MailServerLogDone.create('last_stat' => time, 'filename' => 'dummy')
+ raw_saved_time = MailServerLogDone.find(mail_server_log_done.id).read_attribute(:last_stat)
saved_time = MailServerLogDone.find(mail_server_log_done.id).last_stat
assert_equal time, saved_time
+ assert_equal saved_time, raw_saved_time
+ # Time is created in EST by local method (using ENV['TZ'])
assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a
+ # Due to :utc active_record_default_timezone, everything saved as UTC
+ assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], raw_saved_time.to_a
+ # As config.time_zone is UTC (from config default), times returned in UTC
assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
end
end
end
- it "should preserve time objects with time with zone conversion to default timezone UTC" do
+ it "should preserve time objects with time with zone conversion to default timezone UTC
+ and return them as local times in the zone set by Time.use_zone" do
+
with_env_tz 'America/New_York' do
with_active_record_default_timezone :utc do
Time.use_zone 'Central Time (US & Canada)' do
time = Time.zone.local(2000)
mail_server_log_done = MailServerLogDone.create('last_stat' => time, 'filename' => 'dummy')
+ raw_saved_time = MailServerLogDone.find(mail_server_log_done.id).read_attribute(:last_stat)
saved_time = MailServerLogDone.find(mail_server_log_done.id).last_stat
assert_equal time, saved_time
+ assert_equal saved_time, raw_saved_time
+ # Time is created in CST by Time.local (as Time.zone has been set)
assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
- assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
+ # Due to :utc active_record_default_timezone, everything saved as UTC
+ assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], raw_saved_time.to_a
+ # Times returned in CST due to Time.use_zone and ActiveRecord::time_zone_aware_attributes
+ # being true
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], saved_time.to_a
end
end
end
end
- # XXX Couldn't get this test to work - but the other tests seem to detect presence of
- # the monkey patch, so they will do for now.
- #it "should preserve time objects with UTC time conversion to default timezone local" do
- # with_env_tz 'America/New_York' do
- # time = Time.utc(2000)
- # mail_server_log_done = MailServerLogDone.create('last_stat' => time, 'filename' => 'dummy')
- # saved_time = MailServerLogDone.find(mail_server_log_done.id).last_stat
- # assert_equal time, saved_time
- # assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
- # assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a
- # end
- #end
+ it "should preserve time objects with UTC time conversion to default timezone local
+ and return then as UTC times when config.time_zone is UTC" do
+ with_env_tz 'America/New_York' do
+ with_active_record_default_timezone :local do
+ time = Time.utc(2000)
+ mail_server_log_done = MailServerLogDone.create('last_stat' => time, 'filename' => 'dummy')
+ raw_saved_time = MailServerLogDone.find(mail_server_log_done.id).read_attribute(:last_stat)
+ saved_time = MailServerLogDone.find(mail_server_log_done.id).last_stat
+ assert_equal time, saved_time
+ assert_equal saved_time, raw_saved_time
+ # Time is created in UTC by Time.utc method
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
+ # Due to :local active_record_default_timezone, saved as EST
+ assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], raw_saved_time.to_a
+ # As config.time_zone is UTC (from config default), times returned in UTC
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
+ end
+ end
+ end
- it "should preserve time objects with time with zone conversion to default timezone local" do
+ it "should preserve time objects with time with zone conversion to default timezone local
+ and return them as local times in the zone set by Time.use_zone" do
with_env_tz 'America/New_York' do
with_active_record_default_timezone :local do
Time.use_zone 'Central Time (US & Canada)' do
time = Time.zone.local(2000)
mail_server_log_done = MailServerLogDone.create('last_stat' => time, 'filename' => 'dummy')
+ raw_saved_time = MailServerLogDone.find(mail_server_log_done.id).read_attribute(:last_stat)
saved_time = MailServerLogDone.find(mail_server_log_done.id).last_stat
assert_equal time, saved_time
+ assert_equal saved_time, raw_saved_time
+ # Time is created in CST by Time.zone.local
assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
- assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], saved_time.to_a
+ # Due to :local active_record_default_timezone, saved as EST
+ assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], raw_saved_time.to_a
+ # Due to Time.use_zone, and ActiveRecord::time_zone_aware_attributes
+ # being true, time returned in CST
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], saved_time.to_a
end
end
end
end
+
protected
+
def with_env_tz(new_tz = 'US/Eastern')
old_tz, ENV['TZ'] = ENV['TZ'], new_tz
yield
diff --git a/spec/models/incoming_message_spec.rb b/spec/models/incoming_message_spec.rb
index 69a5da0e9..b038c43d9 100644
--- a/spec/models/incoming_message_spec.rb
+++ b/spec/models/incoming_message_spec.rb
@@ -154,6 +154,23 @@ describe IncomingMessage, " folding quoted parts of emails" do
text.should == "\n\nFOLDED_QUOTED_SECTION"
end
+ it 'should fold an example of another kind of forward quoting' do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('forward-quoting-example.email', ir.incoming_email)
+ message = ir.incoming_messages[1]
+ message.get_main_body_text_folded.should match(/FOLDED_QUOTED_SECTION/)
+ end
+
+ it 'should fold a further example of forward quoting' do
+ ir = info_requests(:fancy_dog_request)
+ receive_incoming_mail('forward-quoting-example-2.email', ir.incoming_email)
+ message = ir.incoming_messages[1]
+ body_text = message.get_main_body_text_folded
+ body_text.should match(/FOLDED_QUOTED_SECTION/)
+ # check that the quoted section incorporates both quoted messages
+ body_text.should_not match('Subject: RE: Freedom of Information request')
+ end
+
end
describe IncomingMessage, " checking validity to reply to" do
diff --git a/spec/models/track_thing_spec.rb b/spec/models/track_thing_spec.rb
index 345629bd6..c42eb5e8b 100644
--- a/spec/models/track_thing_spec.rb
+++ b/spec/models/track_thing_spec.rb
@@ -35,10 +35,11 @@ describe TrackThing, "when tracking changes" do
end
it "will make some sane descriptions of search-based tracks" do
- tests = [['bob variety:user', "users matching text 'bob'"],
- ['bob (variety:sent OR variety:followup_sent OR variety:response OR variety:comment) (latest_status:successful OR latest_status:partially_successful OR latest_status:rejected OR latest_status:not_held)', "comments or requests which are successful or unsuccessful matching text 'bob'"],
- ['(latest_status:waiting_response OR latest_status:waiting_clarification OR waiting_classification:true)', 'requests which are awaiting a response']]
- for query, description in tests
+ tests = { 'bob variety:user' => "users matching text 'bob'",
+ 'bob (variety:sent OR variety:followup_sent OR variety:response OR variety:comment) (latest_status:successful OR latest_status:partially_successful OR latest_status:rejected OR latest_status:not_held)' => "comments or requests which are successful or unsuccessful matching text 'bob'",
+ '(latest_status:waiting_response OR latest_status:waiting_clarification OR waiting_classification:true)' => 'requests which are awaiting a response',
+ ' (variety:sent OR variety:followup_sent OR variety:response OR variety:comment)' => 'all requests or comments' }
+ tests.each do |query, description|
track_thing = TrackThing.create_track_for_search_query(query)
track_thing.track_query_description.should == description
end
diff --git a/spec/views/public_body/show.rhtml_spec.rb b/spec/views/public_body/show.rhtml_spec.rb
index a42516d72..b68b3f43b 100644
--- a/spec/views/public_body/show.rhtml_spec.rb
+++ b/spec/views/public_body/show.rhtml_spec.rb
@@ -11,6 +11,7 @@ describe "when viewing a body" do
:eir_only? => nil,
:info_requests => [1, 2, 3, 4], # out of sync with Xapian
:publication_scheme => '',
+ :disclosure_log => '',
:calculated_home_page => '')
@pb.stub!(:override_request_email).and_return(nil)
@pb.stub!(:is_requestable?).and_return(true)