aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/admin_general_controller.rb4
-rw-r--r--app/controllers/admin_public_body_controller.rb10
-rw-r--r--app/controllers/admin_user_controller.rb1
-rw-r--r--app/controllers/application_controller.rb103
-rw-r--r--app/controllers/general_controller.rb74
-rw-r--r--app/controllers/help_controller.rb16
-rw-r--r--app/controllers/public_body_controller.rb24
-rw-r--r--app/controllers/request_controller.rb86
-rw-r--r--app/controllers/services_controller.rb10
-rw-r--r--app/controllers/track_controller.rb9
-rw-r--r--app/controllers/user_controller.rb67
-rwxr-xr-xapp/helpers/link_to_helper.rb39
-rw-r--r--app/models/about_me_validator.rb2
-rw-r--r--app/models/censor_rule.rb8
-rw-r--r--app/models/change_email_validator.rb9
-rw-r--r--app/models/comment.rb7
-rw-r--r--app/models/exim_log.rb6
-rw-r--r--app/models/foi_attachment.rb56
-rw-r--r--app/models/holiday.rb6
-rw-r--r--app/models/incoming_message.rb452
-rw-r--r--app/models/info_request.rb15
-rw-r--r--app/models/info_request_event.rb57
-rw-r--r--app/models/outgoing_message.rb6
-rw-r--r--app/models/post_redirect.rb8
-rw-r--r--app/models/profile_photo.rb6
-rw-r--r--app/models/public_body.rb4
-rw-r--r--app/models/raw_email.rb35
-rw-r--r--app/models/request_mailer.rb13
-rw-r--r--app/models/track_thing.rb27
-rw-r--r--app/models/track_things_sent_email.rb12
-rw-r--r--app/models/user.rb31
-rw-r--r--app/models/user_info_request_sent_alert.rb4
-rw-r--r--app/views/admin_general/debug.rhtml6
-rw-r--r--app/views/admin_general/timeline.rhtml10
-rw-r--r--app/views/admin_public_body/_form.rhtml7
-rw-r--r--app/views/admin_public_body/edit.rhtml6
-rw-r--r--app/views/admin_public_body/import_csv.rhtml2
-rw-r--r--app/views/admin_public_body/new.rhtml6
-rw-r--r--app/views/admin_user/_form.rhtml8
-rw-r--r--app/views/general/_topnav.rhtml4
-rw-r--r--app/views/general/exception_caught.rhtml4
-rw-r--r--app/views/general/frontpage.rhtml62
-rw-r--r--app/views/general/search.rhtml15
-rw-r--r--app/views/layouts/default.rhtml23
-rw-r--r--app/views/public_body/_search_ahead.rhtml5
-rw-r--r--app/views/public_body/list.rhtml2
-rw-r--r--app/views/public_body/show.rhtml192
-rw-r--r--app/views/request/_request_listing_via_event.rhtml25
-rw-r--r--app/views/request/_search_ahead.rhtml2
-rw-r--r--app/views/request/list.rhtml6
-rw-r--r--app/views/request/new.rhtml4
-rw-r--r--app/views/request/select_authority.rhtml10
-rw-r--r--app/views/user/rate_limited.rhtml17
-rw-r--r--app/views/user/show.rhtml151
-rw-r--r--app/views/user/sign.rhtml13
55 files changed, 860 insertions, 927 deletions
diff --git a/app/controllers/admin_general_controller.rb b/app/controllers/admin_general_controller.rb
index ae51e0923..0b7e9bec0 100644
--- a/app/controllers/admin_general_controller.rb
+++ b/app/controllers/admin_general_controller.rb
@@ -78,6 +78,10 @@ class AdminGeneralController < AdminController
end
def debug
+ @current_commit = `git log -1 --format="%H"`
+ @current_branch = `git branch | grep "\*" | awk '{print $2}'`
+ repo = `git remote show origin -n | grep Fetch | awk '{print $3}' | sed -re 's/.*:(.*).git/\\1/'`
+ @github_origin = "https://github.com/#{repo.strip}/tree/"
@request_env = request.env
end
end
diff --git a/app/controllers/admin_public_body_controller.rb b/app/controllers/admin_public_body_controller.rb
index e249cef11..bf7c07905 100644
--- a/app/controllers/admin_public_body_controller.rb
+++ b/app/controllers/admin_public_body_controller.rb
@@ -27,12 +27,12 @@ class AdminPublicBodyController < AdminController
end
@public_bodies = PublicBody.paginate :order => "public_body_translations.name", :page => @page, :per_page => 100,
:conditions => @query.nil? ? "public_body_translations.locale = '#{@locale}'" :
- ["(lower(public_body_translations.name) like lower('%'||?||'%') or
+ ["(lower(public_body_translations.name) like lower('%'||?||'%') or
lower(public_body_translations.short_name) like lower('%'||?||'%') or
lower(public_body_translations.request_email) like lower('%'||?||'%' )) AND (public_body_translations.locale = '#{@locale}')", @query, @query, @query],
:joins => :translations
- @public_bodies_by_tag = PublicBody::Translation.find_by_tag(@query)
end
+ @public_bodies_by_tag = PublicBody.find_by_tag(@query)
end
def list
@@ -56,7 +56,7 @@ class AdminPublicBodyController < AdminController
flash[:notice] = "Added tag to table of bodies."
end
- redirect_to admin_url('body/list') + "?query=" + @query + (@page.nil? ? "" : "&page=" + @page) # XXX construct this URL properly
+ redirect_to admin_body_list_url(:query => @query, :page => @page)
end
def missing_scheme
@@ -127,14 +127,14 @@ class AdminPublicBodyController < AdminController
if public_body.info_requests.size > 0
flash[:notice] = "There are requests associated with the authority, so can't destroy it"
- redirect_to admin_url('body/show/' + public_body.id.to_s)
+ redirect_to admin_body_show_url(public_body)
return
end
public_body.tag_string = ""
public_body.destroy
flash[:notice] = "PublicBody was successfully destroyed."
- redirect_to admin_url('body/list')
+ redirect_to admin_body_list_url
end
end
diff --git a/app/controllers/admin_user_controller.rb b/app/controllers/admin_user_controller.rb
index 5d90e74fe..12b4e553f 100644
--- a/app/controllers/admin_user_controller.rb
+++ b/app/controllers/admin_user_controller.rb
@@ -45,6 +45,7 @@ class AdminUserController < AdminController
@admin_user.admin_level = params[:admin_user][:admin_level]
@admin_user.ban_text = params[:admin_user][:ban_text]
@admin_user.about_me = params[:admin_user][:about_me]
+ @admin_user.no_limit = params[:admin_user][:no_limit]
if @admin_user.valid?
@admin_user.save!
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index b7457c48e..b681f455d 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -11,10 +11,15 @@
require 'open-uri'
class ApplicationController < ActionController::Base
+ class PermissionDenied < StandardError
+ end
# Standard headers, footers and navigation for whole site
layout "default"
include FastGettext::Translation # make functions like _, n_, N_ etc available)
-
+
+ # Send notification email on exceptions
+ include ExceptionNotification::Notifiable
+
# Note: a filter stops the chain if it redirects or renders something
before_filter :authentication_check
before_filter :set_gettext_locale
@@ -84,6 +89,7 @@ class ApplicationController < ActionController::Base
def record_memory
record_memory = MySociety::Config.get('DEBUG_RECORD_MEMORY', false)
if record_memory
+ logger.info "Processing request for #{request.url} with Rails process #{Process.pid}"
File.read("/proc/#{Process.pid}/status").match(/VmRSS:\s+(\d+)/)
rss_before_action = $1.to_i
yield
@@ -117,8 +123,11 @@ class ApplicationController < ActionController::Base
case exception
when ActiveRecord::RecordNotFound, ActionController::UnknownAction, ActionController::RoutingError
@status = 404
+ when PermissionDenied
+ @status = 403
else
@status = 500
+ notify_about_exception exception
end
# Display user appropriate error message
@exception_backtrace = exception.backtrace.join("\n")
@@ -169,11 +178,13 @@ class ApplicationController < ActionController::Base
end
def foi_fragment_cache_path(param)
- path = foi_fragment_cache_part_path(param)
- path = "/views" + path
- foi_cache_path = File.join(File.dirname(__FILE__), '../../cache')
- return File.join(foi_cache_path, path)
+ path = File.join(RAILS_ROOT, 'cache', 'views', foi_fragment_cache_part_path(param))
+ max_file_length = 255 - 35 # we subtract 35 because tempfile
+ # adds on a variable number of
+ # characters
+ return File.join(File.split(path).map{|x| x[0...max_file_length]})
end
+
def foi_fragment_cache_all_for_request(info_request)
# return stub path so admin can expire it
first_three_digits = info_request.id.to_s()[0..2]
@@ -185,10 +196,12 @@ class ApplicationController < ActionController::Base
return File.exists?(key_path)
end
def foi_fragment_cache_read(key_path)
- cached = File.read(key_path)
+ logger.info "Reading from fragment cache #{key_path}"
+ return File.read(key_path)
end
def foi_fragment_cache_write(key_path, content)
FileUtils.mkdir_p(File.dirname(key_path))
+ logger.info "Writing to fragment cache #{key_path}"
File.atomic_write(key_path) do |f|
f.write(content)
end
@@ -341,9 +354,7 @@ class ApplicationController < ActionController::Base
@sortby = sortby
# Work out sorting method
- order_pair = order_to_sort_by(@sortby)
- order = order_pair[0]
- ascending = order_pair[1]
+ order, ascending = order_to_sort_by(@sortby)
# Peform the search
@per_page = per_page
@@ -352,23 +363,71 @@ class ApplicationController < ActionController::Base
else
@page = this_page
end
- return InfoRequest.full_search(models, @query, order, ascending, collapse, @per_page, @page)
+ result = InfoRequest.full_search(models, @query, order, ascending, collapse, @per_page, @page)
+ result.results # Touch the results to load them, otherwise accessing them from the view
+ # might fail later if the database has subsequently been reopened.
+ return result
end
def get_search_page_from_params
return (params[:page] || "1").to_i
end
+ def perform_search_typeahead(query, model)
+ @page = get_search_page_from_params
+ @per_page = 10
+ query_words = query.split(/ +(?![-+]+)/)
+ if query_words.last.nil? || query_words.last.strip.length < 3
+ xapian_requests = nil
+ else
+ if model == PublicBody
+ collapse = nil
+ elsif model == InfoRequestEvent
+ collapse = 'request_collapse'
+ end
+ options = {
+ :offset => (@page - 1) * @per_page,
+ :limit => @per_page,
+ :sort_by_prefix => nil,
+ :sort_by_ascending => true,
+ :collapse_by_prefix => collapse,
+ }
+ ActsAsXapian.readable_init
+ old_default_op = ActsAsXapian.query_parser.default_op
+ ActsAsXapian.query_parser.default_op = Xapian::Query::OP_OR
+ begin
+ user_query = ActsAsXapian.query_parser.parse_query(
+ query.strip + '*',
+ Xapian::QueryParser::FLAG_LOVEHATE | Xapian::QueryParser::FLAG_WILDCARD |
+ Xapian::QueryParser::FLAG_SPELLING_CORRECTION)
+ xapian_requests = ActsAsXapian::Search.new([model], query, options, user_query)
+ rescue RuntimeError => e
+ if e.message =~ /^QueryParserError: Wildcard/
+ # Wildcard expands to too many terms
+ logger.info "Wildcard query '#{query.strip + '*'}' caused: #{e.message}"
+
+ user_query = ActsAsXapian.query_parser.parse_query(
+ query,
+ Xapian::QueryParser::FLAG_LOVEHATE |
+ Xapian::QueryParser::FLAG_SPELLING_CORRECTION)
+ xapian_requests = ActsAsXapian::Search.new([model], query, options, user_query)
+ end
+ end
+ ActsAsXapian.query_parser.default_op = old_default_op
+ end
+ return xapian_requests
+ end
+
# Store last visited pages, for contact form; but only for logged in users, as otherwise this breaks caching
def set_last_request(info_request)
if !session[:user_id].nil?
- session[:last_request_id] = info_request.id
- session[:last_body_id] = nil
+ cookies["last_request_id"] = info_request.id
+ cookies["last_body_id"] = nil
end
end
def set_last_body(public_body)
if !session[:user_id].nil?
- session[:last_request_id] = nil
- session[:last_body_id] = public_body.id
+ cookies["last_request_id"] = nil
+ cookies["last_body_id"] = public_body.id
end
end
@@ -406,7 +465,7 @@ class ApplicationController < ActionController::Base
params[:latest_status] = [params[:latest_status]]
end
if params[:latest_status].include?("recent") || params[:latest_status].include?("all")
- query += " variety:sent"
+ query += " (variety:sent OR variety:followup_sent OR variety:response OR variety:comment)"
end
if params[:latest_status].include? "successful"
statuses << ['latest_status:successful', 'latest_status:partially_successful']
@@ -415,7 +474,7 @@ class ApplicationController < ActionController::Base
statuses << ['latest_status:rejected', 'latest_status:not_held']
end
if params[:latest_status].include? "awaiting"
- statuses << ['latest_status:waiting_response', 'latest_status:waiting_clarification', 'waiting_classification:true']
+ statuses << ['latest_status:waiting_response', 'latest_status:waiting_clarification', 'waiting_classification:true', 'latest_status:internal_review','latest_status:gone_postal', 'latest_status:error_message', 'latest_status:requires_admin']
end
if params[:latest_status].include? "internal_review"
statuses << ['status:internal_review']
@@ -475,12 +534,22 @@ class ApplicationController < ActionController::Base
default = MySociety::Config.get('ISO_COUNTRY_CODE', '')
country = ""
if !gaze.empty?
- country = open("#{gaze}/gaze-rest?f=get_country_from_ip;ip=#{request.remote_ip}").read.strip
+ country = quietly_try_to_open("#{gaze}/gaze-rest?f=get_country_from_ip;ip=#{request.remote_ip}")
end
country = default if country.empty?
return country
end
+ def quietly_try_to_open(url)
+ begin
+ result = open(url).read.strip
+ rescue OpenURI::HTTPError, SocketError, Errno::ETIMEDOUT, Errno::ECONNREFUSED, Errno::EHOSTUNREACH
+ logger.warn("Unable to open third-party URL #{url}")
+ result = ""
+ end
+ return result
+ end
+
# URL generating functions are needed by all controllers (for redirects),
# views (for links) and mailers (for use in emails), so include them into
# all of all.
diff --git a/app/controllers/general_controller.rb b/app/controllers/general_controller.rb
index 194a1cec0..82b1b8629 100644
--- a/app/controllers/general_controller.rb
+++ b/app/controllers/general_controller.rb
@@ -32,12 +32,12 @@ class GeneralController < ApplicationController
if body_short_names.empty?
# This is too slow
@popular_bodies = PublicBody.find(:all,
- :select => "public_bodies.*, (select count(*) from info_requests where info_requests.public_body_id = public_bodies.id) as c",
- :order => "c desc",
- :limit => 32,
- :conditions => conditions,
- :joins => :translations
- )
+ :select => "public_bodies.*, (select count(*) from info_requests where info_requests.public_body_id = public_bodies.id) as c",
+ :order => "c desc",
+ :limit => 32,
+ :conditions => conditions,
+ :joins => :translations
+ )
else
conditions[0] += " and public_bodies.url_name in (" + body_short_names + ")"
@popular_bodies = PublicBody.find(:all,
@@ -45,20 +45,21 @@ class GeneralController < ApplicationController
:joins => :translations)
end
end
- # Get some successful requests #
+ # Get some successful requests
begin
query = 'variety:response (status:successful OR status:partially_successful)'
- # query = 'variety:response' # XXX debug
- sortby = "described"
+ sortby = "newest"
max_count = 5
xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_title_collapse', max_count)
@request_events = xapian_object.results.map { |r| r[:model] }
- @request_events = @request_events.sort_by { |e| e.described_at }.reverse
+
+ # If there are not yet enough successful requests, fill out the list with
+ # other requests
if @request_events.count < max_count
query = 'variety:sent'
xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_title_collapse', max_count-@request_events.count)
more_events = xapian_object.results.map { |r| r[:model] }
- @request_events += more_events.sort_by { |e| e.described_at }.reverse
+ @request_events += more_events
end
rescue
@request_events = []
@@ -71,48 +72,34 @@ class GeneralController < ApplicationController
medium_cache
@feed_autodetect = []
@feed_url = "#{MySociety::Config.get('BLOG_FEED', '')}?lang=#{self.locale_from_params()}"
+ @blog_items = []
if not @feed_url.empty?
- content = open(@feed_url).read
- @data = XmlSimple.xml_in(content)
- @channel = @data['channel'][0]
- @blog_items = @channel['item']
- @feed_autodetect = [{:url => @feed_url, :title => "#{site_name} blog"}]
- else
- @blog_items = []
+ content = quietly_try_to_open(@feed_url)
+ if !content.empty?
+ @data = XmlSimple.xml_in(content)
+ @channel = @data['channel'][0]
+ @blog_items = @channel['item']
+ @feed_autodetect = [{:url => @feed_url, :title => "#{site_name} blog"}]
+ end
end
@twitter_user = MySociety::Config.get('TWITTER_USERNAME', '')
end
# Just does a redirect from ?query= search to /query
def search_redirect
- if params[:advanced].nil?
- @query, _ = make_query_from_params
- else
- @query, _ = params[:query]
- end
- @sortby = params[:sortby]
- path = request.path.split("/")
- if path.size > 0 && (['newest', 'described', 'relevant'].include?(path[-1]))
- @sort_postfix = path.pop
- end
- if path.size > 0 && (['bodies', 'requests', 'users', 'all'].include?(path[-1]))
- @variety_postfix = path.pop
- end
- @variety_postfix = "bodies" if @variety_postfix.nil? && !params[:bodies].nil?
- @variety_postfix = "requests" if @variety_postfix.nil?
- if @variety_postfix != "users"
- @common_query = get_tags_from_params
- end
- [:latest_status, :request_variety, :request_date_after, :request_date_before, :query, :tags].each do |x|
- session[x] = params[x]
- end
+ @query = params.delete(:query)
if @query.nil? || @query.empty?
@query = nil
@page = 1
@advanced = !params[:advanced].nil?
render :action => "search"
else
- redirect_to search_url(@query, @variety_postfix, @sort_postfix, params[:advanced])
+ query_parts = @query.split("/")
+ if !['bodies', 'requests', 'users', 'all'].include?(query_parts[-1])
+ redirect_to search_url([@query, "all"], params)
+ else
+ redirect_to search_url(@query, params)
+ end
end
end
@@ -120,13 +107,6 @@ class GeneralController < ApplicationController
def search
# XXX Why is this so complicated with arrays and stuff? Look at the route
# in config/routes.rb for comments.
- if !params[:commit].nil?
- search_redirect
- return
- end
- [:latest_status, :request_variety, :request_date_after, :request_date_before, :query, :tags].each do |x|
- params[x] = session[x] if params[x].nil?
- end
combined = params[:combined]
@sortby = nil
@bodies = @requests = @users = true
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index c6d246b4c..b08438b52 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -26,20 +26,20 @@ class HelpController < ApplicationController
# if they clicked remove for link to request/body, remove it
if params[:remove]
@last_request = nil
- session[:last_request_id] = nil
- session[:last_body_id] = nil
+ cookies["last_request_id"] = nil
+ cookies["last_body_id"] = nil
end
# look up link to request/body
- @last_request_id = session[:last_request_id].to_i
- if @last_request_id > 0
- @last_request = InfoRequest.find(@last_request_id)
+ last_request_id = cookies["last_request_id"].to_i
+ if last_request_id > 0
+ @last_request = InfoRequest.find(last_request_id)
else
@last_request = nil
end
- @last_body_id = session[:last_body_id].to_i
- if @last_body_id > 0
- @last_body = PublicBody.find(@last_body_id)
+ last_body_id = cookies["last_body_id"].to_i
+ if last_body_id > 0
+ @last_body = PublicBody.find(last_body_id)
else
@last_body = nil
end
diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb
index 62229a441..00d1cc1e0 100644
--- a/app/controllers/public_body_controller.rb
+++ b/app/controllers/public_body_controller.rb
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# app/controllers/public_body_controller.rb:
# Show information about a public body.
#
@@ -117,19 +118,22 @@ class PublicBodyController < ApplicationController
and has_tag_string_tags.model = \'PublicBody\'
and has_tag_string_tags.name = ?) > 0', @query, @query, default_locale, @tag]
end
+
if @tag == "all"
@description = ""
elsif @tag.size == 1
- @description = _("beginning with") + " '" + @tag + "'"
+ @description = _("beginning with ‘{{first_letter}}’", :first_letter=>@tag)
else
- @description = PublicBodyCategories::get().by_tag()[@tag]
- if @description.nil?
- @description = @tag
+ category_name = PublicBodyCategories::get().by_tag()[@tag]
+ if category_name.nil?
+ @description = _("matching the tag ‘{{tag_name}}’", :tag_name=>@tag)
+ else
+ @description = _("in the category ‘{{category_name}}’", :category_name=>category_name)
end
end
PublicBody.with_locale(@locale) do
@public_bodies = PublicBody.paginate(
- :order => "public_body_translations.name", :page => params[:page], :per_page => 1000, # fit all councils on one page
+ :order => "public_body_translations.name", :page => params[:page], :per_page => 100,
:conditions => conditions,
:joins => :translations
)
@@ -185,14 +189,8 @@ 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(' ')
- 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
+ query = params[:query]
+ @xapian_requests = perform_search_typeahead(query, PublicBody)
render :partial => "public_body/search_ahead"
end
end
diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb
index dad5e81cd..2295d6718 100644
--- a/app/controllers/request_controller.rb
+++ b/app/controllers/request_controller.rb
@@ -13,6 +13,9 @@ require 'open-uri'
class RequestController < ApplicationController
before_filter :check_read_only, :only => [ :new, :show_response, :describe_state, :upload_response ]
protect_from_forgery :only => [ :new, :show_response, :describe_state, :upload_response ] # See ActionController::RequestForgeryProtection for details
+
+ MAX_RESULTS = 500
+ PER_PAGE = 25
@@custom_states_loaded = false
begin
@@ -35,11 +38,9 @@ class RequestController < ApplicationController
# do nothing - as "authenticated?" has done the redirect to signin page for us
return
end
-
if !params[:query].nil?
- query = params[:query] + '*'
- query = query.split(' ').join(' OR ') # XXX: HACK for OR instead of default AND!
- @xapian_requests = perform_search([PublicBody], query, 'relevant', nil, 5)
+ query = params[:query]
+ @xapian_requests = perform_search_typeahead(query, PublicBody)
end
medium_cache
end
@@ -73,8 +74,9 @@ class RequestController < ApplicationController
@info_request_events = @info_request.info_request_events
@status = @info_request.calculate_status
@collapse_quotes = params[:unfold] ? false : true
- @update_status = params[:update_status] ? true : false
+ @update_status = params[:update_status] ? true : false
@old_unclassified = @info_request.is_old_unclassified? && !authenticated_user.nil?
+ @is_owning_user = @info_request.is_owning_user?(authenticated_user)
if @update_status
return if !@is_owning_user && !authenticated_as_user?(@info_request.user,
@@ -107,7 +109,6 @@ class RequestController < ApplicationController
# For send followup link at bottom
@last_response = @info_request.get_last_response
- @is_owning_user = @info_request.is_owning_user?(authenticated_user)
respond_to do |format|
format.html { @has_json = true; render :template => 'request/show'}
format.json { render :json => @info_request.json_for_api(true) }
@@ -119,11 +120,14 @@ class RequestController < ApplicationController
def details
long_cache
@info_request = InfoRequest.find_by_url_title(params[:url_title])
- if !@info_request.user_can_view?(authenticated_user)
- render :template => 'request/hidden', :status => 410 # gone
- return
+ if @info_request.nil?
+ raise ActiveRecord::RecordNotFound.new("Request not found")
+ else
+ if !@info_request.user_can_view?(authenticated_user)
+ render :template => 'request/hidden', :status => 410 # gone
+ return
+ end
end
-
@columns = ['id', 'event_type', 'created_at', 'described_state', 'last_described_at', 'calculated_state' ]
end
@@ -150,15 +154,26 @@ class RequestController < ApplicationController
def list
medium_cache
@view = params[:view]
+ @page = get_search_page_from_params if !@page # used in cache case, as perform_search sets @page as side effect
+ if @view == "recent"
+ return redirect_to request_list_all_path(:action => "list", :view => "all", :page => @page), :status => :moved_permanently
+ end
+
+ # Later pages are very expensive to load
+ if @page > MAX_RESULTS / PER_PAGE
+ raise ActiveRecord::RecordNotFound.new("Sorry. No pages after #{MAX_RESULTS / PER_PAGE}.")
+ end
+
params[:latest_status] = @view
query = make_query_from_params
@title = _("View and search requests")
sortby = "newest"
- @page = get_search_page_from_params if !@page # used in cache case, as perform_search sets @page as side effect
- behavior_cache :tag => [@view, @page] do
+ @cache_tag = Digest::MD5.hexdigest(query + @page.to_s)
+ behavior_cache :tag => [@cache_tag] do
xapian_object = perform_search([InfoRequestEvent], query, sortby, 'request_collapse')
@list_results = xapian_object.results.map { |r| r[:model] }
@matches_estimated = xapian_object.matches_estimated
+ @show_no_more_than = (@matches_estimated > MAX_RESULTS) ? MAX_RESULTS : @matches_estimated
end
@title = @title + " (page " + @page.to_s + ")" if (@page > 1)
@@ -192,14 +207,28 @@ class RequestController < ApplicationController
end
# Banned from making new requests?
+ user_exceeded_limit = false
if !authenticated_user.nil? && !authenticated_user.can_file_requests?
- @details = authenticated_user.can_fail_html
- render :template => 'user/banned'
- return
+ # If the reason the user cannot make new requests is that they are
+ # rate-limited, it’s possible they composed a request before they
+ # logged in and we want to include the text of the request so they
+ # can squirrel it away for tomorrow, so we detect this later after
+ # we have constructed the InfoRequest.
+ user_exceeded_limit = authenticated_user.exceeded_limit?
+ if !user_exceeded_limit
+ @details = authenticated_user.can_fail_html
+ render :template => 'user/banned'
+ return
+ end
end
# First time we get to the page, just display it
if params[:submitted_new_request].nil? || params[:reedit]
+ if user_exceeded_limit
+ render :template => 'user/rate_limited'
+ return
+ end
+
params[:info_request] = { } if !params[:info_request]
# Read parameters in - first the public body (by URL name or id)
@@ -299,6 +328,11 @@ class RequestController < ApplicationController
return
end
+ if user_exceeded_limit
+ render :template => 'user/rate_limited'
+ return
+ end
+
if !authenticated?(
:web => _("To send your FOI request"),
:email => _("Then your FOI request to {{public_body_name}} will be sent.",:public_body_name=>@info_request.public_body.name),
@@ -602,7 +636,9 @@ class RequestController < ApplicationController
def authenticate_attachment
# Test for hidden
incoming_message = IncomingMessage.find(params[:incoming_message_id])
+ raise ActiveRecord::RecordNotFound.new("Message not found") if incoming_message.nil?
if !incoming_message.info_request.user_can_view?(authenticated_user)
+ @info_request = incoming_message.info_request # used by view
render :template => 'request/hidden', :status => 410 # gone
end
end
@@ -615,8 +651,8 @@ class RequestController < ApplicationController
else
key = params.merge(:only_path => true)
key_path = foi_fragment_cache_path(key)
-
if foi_fragment_cache_exists?(key_path)
+ raise PermissionDenied.new("Directory listing not allowed") if File.directory?(key_path)
cached = foi_fragment_cache_read(key_path)
response.content_type = AlaveteliFileTypes.filename_to_mimetype(params[:file_name].join("/")) || 'application/octet-stream'
render_for_text(cached)
@@ -676,10 +712,15 @@ class RequestController < ApplicationController
# Internal function
def get_attachment_internal(html_conversion)
@incoming_message = IncomingMessage.find(params[:incoming_message_id])
+ @requested_request = InfoRequest.find(params[: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])
+ # Note that params[:id] might not be an integer, though
+ # if we’ve got this far then it must begin with an integer
+ # and that integer must be the id number of an actual request.
+ message = "Incoming message %d does not belong to request '%s'" % [@incoming_message.info_request_id, params[:id]]
+ raise ActiveRecord::RecordNotFound.new(message)
end
@part_number = params[:part].to_i
@filename = params[:file_name].join("/")
@@ -756,13 +797,7 @@ class RequestController < ApplicationController
# 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(' ')
- 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
+ @xapian_requests = perform_search_typeahead(query, InfoRequestEvent)
render :partial => "request/search_ahead.rhtml"
end
@@ -815,7 +850,8 @@ class RequestController < ApplicationController
for message in info_request.incoming_messages
attachments = message.get_attachments_for_display
for attachment in attachments
- zipfile.get_output_stream(attachment.display_filename) { |f|
+ filename = "#{attachment.url_part_number}_#{attachment.display_filename}"
+ zipfile.get_output_stream(filename) { |f|
f.puts(attachment.body)
}
end
diff --git a/app/controllers/services_controller.rb b/app/controllers/services_controller.rb
index 6fb20336e..225790d71 100644
--- a/app/controllers/services_controller.rb
+++ b/app/controllers/services_controller.rb
@@ -1,12 +1,4 @@
-# controllers/application.rb:
-# Parent class of all controllers in FOI site. Filters added to this controller
-# apply to all controllers in the application. Likewise, all the methods added
-# will be available for all controllers.
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: francis@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: application.rb,v 1.59 2009-09-17 13:01:56 francis Exp $
+# controllers/services_controller.rb:
require 'open-uri'
diff --git a/app/controllers/track_controller.rb b/app/controllers/track_controller.rb
index e06701a5f..e39a0489d 100644
--- a/app/controllers/track_controller.rb
+++ b/app/controllers/track_controller.rb
@@ -46,7 +46,14 @@ class TrackController < ApplicationController
# Track all updates to a particular public body
def track_public_body
- @public_body = PublicBody.find_by_url_name(params[:url_name])
+ @public_body = PublicBody.find_by_url_name_with_historic(params[:url_name])
+ raise ActiveRecord::RecordNotFound.new("None found") if @public_body.nil?
+ # If found by historic name, or alternate locale name, redirect to new name
+ if @public_body.url_name != params[:url_name]
+ redirect_to track_public_body_url(:url_name => @public_body.url_name, :feed => params[:feed])
+ return
+ end
+
@track_thing = TrackThing.create_track_for_public_body(@public_body)
return atom_feed_internal if params[:feed] == 'feed'
diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb
index 96dbfba74..f49fc9165 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')
@@ -104,8 +116,10 @@ class UserController < ApplicationController
render :action => 'sign'
return
else
- @user_signin = User.authenticate_from_form(params[:user_signin], @post_redirect.reason_params[:user_name] ? true : false)
- if @user_signin.errors.size > 0
+ if !@post_redirect.nil?
+ @user_signin = User.authenticate_from_form(params[:user_signin], @post_redirect.reason_params[:user_name] ? true : false)
+ end
+ if @post_redirect.nil? || @user_signin.errors.size > 0
# Failed to authenticate
render :action => 'sign'
return
@@ -475,7 +489,8 @@ class UserController < ApplicationController
raise ActiveRecord::RecordNotFound.new("user not found, url_name=" + params[:url_name])
end
if !@display_user.profile_photo
- raise "user has no profile photo, url_name=" + params[:url_name]
+ raise ActiveRecord::RecordNotFound.new("user has no profile photo, url_name=" + params[:url_name])
+
end
response.content_type = "image/png"
diff --git a/app/helpers/link_to_helper.rb b/app/helpers/link_to_helper.rb
index 5866c31f0..56c33e512 100755
--- a/app/helpers/link_to_helper.rb
+++ b/app/helpers/link_to_helper.rb
@@ -136,9 +136,19 @@ module LinkToHelper
end
# General pages.
- def search_url(query, variety_postfix = nil, sort_postfix = nil, advanced = nil)
- query = query - ["", nil] if query.kind_of?(Array)
- url = search_general_url(:combined => query)
+ def search_url(query, params = nil)
+ if query.kind_of?(Array)
+ query = query - ["", nil]
+ query = query.join("/")
+ end
+ routing_info = {:controller => 'general',
+ :action => 'search',
+ :combined => query,
+ :view => nil}
+ if !params.nil?
+ routing_info = params.merge(routing_info)
+ end
+ url = url_for(routing_info)
# Here we can't escape the slashes, as RFC 2396 doesn't allow slashes
# within a path component. Rails is assuming when generating URLs that
# either there aren't slashes, or we are in a query part where you can
@@ -150,19 +160,10 @@ module LinkToHelper
# http://rails.lighthouseapp.com/projects/8994/tickets/144-patch-bug-in-rails-route-globbing
url = url.gsub("%2F", "/")
- if !variety_postfix.nil? && !variety_postfix.empty?
- url = url + "/" + variety_postfix
- end
- if !sort_postfix.nil? && !sort_postfix.empty?
- url = url + "/" + sort_postfix
- end
- if !advanced.nil? && (advanced)
- url = url + "/advanced"
- end
return url
end
def search_link(query, variety_postfix = nil, sort_postfix = nil, advanced = nil)
- link_to h(query), search_url(query, variety_postfix, sort_postfix, advanced)
+ link_to h(query), search_url(query)
end
# Admin pages
@@ -189,10 +190,14 @@ module LinkToHelper
url_prefix = "http://" + MySociety::Config.get("DOMAIN", '127.0.0.1:3000')
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
+ begin
+ env = Rack::MockRequest.env_for(url)
+ req = Rack::Request.new(env)
+ req.path_info += append
+ url = req.url
+ rescue URI::InvalidURIError
+ # don't append to it
+ end
end
return url
end
diff --git a/app/models/about_me_validator.rb b/app/models/about_me_validator.rb
index ec2b03201..e24c5512c 100644
--- a/app/models/about_me_validator.rb
+++ b/app/models/about_me_validator.rb
@@ -21,7 +21,7 @@ class AboutMeValidator < ActiveRecord::BaseWithoutTable
def validate
if !self.about_me.blank? && self.about_me.size > 500
- errors.add(_("Please keep it shorter than 500 characters"))
+ errors.add(:about_me, _("Please keep it shorter than 500 characters"))
end
end
diff --git a/app/models/censor_rule.rb b/app/models/censor_rule.rb
index e2dc12d6f..201e60746 100644
--- a/app/models/censor_rule.rb
+++ b/app/models/censor_rule.rb
@@ -1,12 +1,12 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: censor_rules
#
# id :integer not null, primary key
-# info_request_id :integer
-# user_id :integer
-# public_body_id :integer
+# info_request_id :integer
+# user_id :integer
+# public_body_id :integer
# text :text not null
# replacement :text not null
# last_edit_editor :string(255) not null
diff --git a/app/models/change_email_validator.rb b/app/models/change_email_validator.rb
index f7ec6d17e..e3f8fa892 100644
--- a/app/models/change_email_validator.rb
+++ b/app/models/change_email_validator.rb
@@ -1,11 +1,12 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: change_email_validators
#
-# old_email :string
-# new_email :string
-# password :string
+# old_email :string
+# new_email :string
+# password :string
+# user_circumstance :string
#
# models/changeemail_validator.rb:
diff --git a/app/models/comment.rb b/app/models/comment.rb
index b7ece9ba9..44a1079cd 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -1,16 +1,17 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: comments
#
# id :integer not null, primary key
# user_id :integer not null
# comment_type :string(255) default("internal_error"), not null
-# info_request_id :integer
+# info_request_id :integer
# body :text not null
-# visible :boolean default(true), not null
+# visible :boolean default(TRUE), not null
# created_at :datetime not null
# updated_at :datetime not null
+# locale :text default(""), not null
#
# models/comments.rb:
diff --git a/app/models/exim_log.rb b/app/models/exim_log.rb
index 83f031a92..77e5e2d21 100644
--- a/app/models/exim_log.rb
+++ b/app/models/exim_log.rb
@@ -1,11 +1,11 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: exim_logs
#
# id :integer not null, primary key
-# exim_log_done_id :integer
-# info_request_id :integer
+# exim_log_done_id :integer
+# info_request_id :integer
# order :integer not null
# line :text not null
# created_at :datetime not null
diff --git a/app/models/foi_attachment.rb b/app/models/foi_attachment.rb
index 057dcdb69..da92d1c2d 100644
--- a/app/models/foi_attachment.rb
+++ b/app/models/foi_attachment.rb
@@ -1,3 +1,19 @@
+# == Schema Information
+# Schema version: 108
+#
+# Table name: foi_attachments
+#
+# id :integer not null, primary key
+# content_type :text
+# filename :text
+# charset :text
+# display_size :text
+# url_part_number :integer
+# within_rfc822_subject :text
+# incoming_message_id :integer
+# hexdigest :string(32)
+#
+
# encoding: UTF-8
# models/foi_attachment.rb:
@@ -18,8 +34,15 @@ class FoiAttachment < ActiveRecord::Base
before_validation :ensure_filename!, :only => [:filename]
before_destroy :delete_cached_file!
+ BODY_MAX_TRIES = 3
+ BODY_MAX_DELAY = 5
+
def directory
- base_dir = File.join("cache", "attachments_#{ENV['RAILS_ENV']}")
+ rails_env = ENV['RAILS_ENV']
+ if rails_env.nil? || rails_env.empty?
+ raise "$RAILS_ENV is not set"
+ end
+ base_dir = File.join(File.dirname(__FILE__), "../../cache", "attachments_#{rails_env}")
return File.join(base_dir, self.hexdigest[0..2])
end
@@ -29,6 +52,7 @@ class FoiAttachment < ActiveRecord::Base
def delete_cached_file!
begin
+ @cached_body = nil
File.delete(self.filepath)
rescue
end
@@ -43,11 +67,29 @@ class FoiAttachment < ActiveRecord::Base
file.write d
}
update_display_size!
+ @cached_body = d
end
def body
if @cached_body.nil?
- @cached_body = File.open(self.filepath, "rb" ).read
+ tries = 0
+ delay = 1
+ begin
+ @cached_body = File.open(self.filepath, "rb" ).read
+ rescue Errno::ENOENT
+ # we've lost our cached attachments for some reason. Reparse them.
+ if tries > BODY_MAX_TRIES
+ raise
+ else
+ sleep delay
+ end
+ tries += 1
+ delay *= 2
+ delay = BODY_MAX_DELAY if delay > BODY_MAX_DELAY
+ force = true
+ self.incoming_message.parse_raw_email!(force)
+ retry
+ end
end
return @cached_body
end
@@ -274,13 +316,9 @@ class FoiAttachment < ActiveRecord::Base
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
+ html = AlaveteliExternalCommand.run("pdftohtml", "-nodrm", "-zoom", "1.0", "-stdout", "-enc", "UTF-8", "-noframes", tempfile.path)
elsif self.content_type == 'application/rtf'
- IO.popen("/usr/bin/unrtf --html " + tempfile.path + "", "r") do |child|
- html = child.read()
- end
+ html = AlaveteliExternalCommand.run("unrtf", "--html", tempfile.path)
elsif self.has_google_docs_viewer?
html = '' # force error and using Google docs viewer
else
@@ -302,7 +340,7 @@ class FoiAttachment < ActiveRecord::Base
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)
+ if html.size == 0 || !$?.success? || (body_without_tags.size == 0 && !contains_images)
ret = "<html><head></head><body>";
if self.has_google_docs_viewer?
wrapper_id = "wrapper_google_embed"
diff --git a/app/models/holiday.rb b/app/models/holiday.rb
index 4674d58f1..60b5ff443 100644
--- a/app/models/holiday.rb
+++ b/app/models/holiday.rb
@@ -1,11 +1,11 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: holidays
#
# id :integer not null, primary key
-# day :date
-# description :text
+# day :date
+# description :text
#
# models/holiday.rb:
diff --git a/app/models/incoming_message.rb b/app/models/incoming_message.rb
index a4519a17d..131970ba6 100644
--- a/app/models/incoming_message.rb
+++ b/app/models/incoming_message.rb
@@ -1,7 +1,5 @@
-# encoding: UTF-8
-
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: incoming_messages
#
@@ -10,11 +8,19 @@
# created_at :datetime not null
# updated_at :datetime not null
# raw_email_id :integer not null
-# cached_attachment_text_clipped :text
-# cached_main_body_text_folded :text
-# cached_main_body_text_unfolded :text
+# cached_attachment_text_clipped :text
+# cached_main_body_text_folded :text
+# cached_main_body_text_unfolded :text
+# sent_at :time
+# subject :text
+# mail_from_domain :text
+# valid_to_reply_to :boolean
+# last_parsed :datetime
+# mail_from :text
#
+# encoding: UTF-8
+
# models/incoming_message.rb:
# An (email) message from really anybody to be logged with a request. e.g. A
# response from the public body.
@@ -44,275 +50,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("#{`which pdftohtml`.chomp} -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
@@ -380,7 +117,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
@@ -390,22 +127,27 @@ class IncomingMessage < ActiveRecord::Base
# 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 self.raw_email.nil?
+ raise "Incoming message id=#{id} has no raw_email"
+ end
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 = ""
+ ActiveRecord::Base.transaction do
+ 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
- self.valid_to_reply_to = self._calculate_valid_to_reply_to
- self.last_parsed = Time.now
- self.save!
end
end
@@ -527,11 +269,7 @@ class IncomingMessage < ActiveRecord::Base
# Special cases for some content types
if content_type == 'application/pdf'
uncompressed_text = nil
- IO.popen("#{`which pdftk`.chomp} - output - uncompress", "r+") do |child|
- child.write(text)
- child.close_write()
- uncompressed_text = child.read()
- end
+ uncompressed_text = AlaveteliExternalCommand.run("pdftk", "-", "output", "-", "uncompress", :stdin_string => text)
# if we managed to uncompress the PDF...
if !uncompressed_text.nil? && !uncompressed_text.empty?
# then censor stuff (making a copy so can compare again in a bit)
@@ -542,15 +280,11 @@ class IncomingMessage < ActiveRecord::Base
# then use the altered file (recompressed)
recompressed_text = nil
if MySociety::Config.get('USE_GHOSTSCRIPT_COMPRESSION') == true
- command = "gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/screen -dNOPAUSE -dQUIET -dBATCH -sOutputFile=- -"
+ command = ["gs", "-sDEVICE=pdfwrite", "-dCompatibilityLevel=1.4", "-dPDFSETTINGS=/screen", "-dNOPAUSE", "-dQUIET", "-dBATCH", "-sOutputFile=-", "-"]
else
- command = "#{`which pdftk`.chomp} - output - compress"
- end
- IO.popen(command, "r+") do |child|
- child.write(censored_uncompressed_text)
- child.close_write()
- recompressed_text = child.read()
+ command = ["pdftk", "-", "output", "-", "compress"]
end
+ recompressed_text = AlaveteliExternalCommand.run(*(command + [{:stdin_string=>censored_uncompressed_text}]))
if recompressed_text.nil? || recompressed_text.empty?
# buggy versions of pdftk sometimes fail on
# compression, I don't see it's a disaster in
@@ -586,8 +320,8 @@ class IncomingMessage < ActiveRecord::Base
emails = ascii_chars.scan(MySociety::Validate.email_find_regexp)
# Convert back to UCS-2, making a mask at the same time
emails.map! {|email| [
- Iconv.conv('ucs-2', 'ascii', email[0]),
- Iconv.conv('ucs-2', 'ascii', email[0].gsub(/[^@.]/, 'x'))
+ Iconv.conv('ucs-2le', 'ascii', email[0]),
+ Iconv.conv('ucs-2le', 'ascii', email[0].gsub(/[^@.]/, 'x'))
] }
# Now search and replace the UCS-2 email with the UCS-2 mask
for email, mask in emails
@@ -792,7 +526,7 @@ 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'
@@ -822,7 +556,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
@@ -832,6 +565,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
@@ -887,64 +622,58 @@ class IncomingMessage < ActiveRecord::Base
end
# Returns body text from main text part of email, converted to UTF-8
def get_main_body_text_internal
+ parse_raw_email!
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 UTF8 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
@@ -1004,9 +733,7 @@ class IncomingMessage < ActiveRecord::Base
tempfile = Tempfile.new('foiuu')
tempfile.print uu
tempfile.flush
- IO.popen("/usr/bin/uudecode " + tempfile.path + " -o -", "r") do |child|
- content = child.read()
- end
+ content = AlaveteliExternalCommand.run("uudecode", "-o", "/dev/stdout", tempfile.path)
tempfile.close
# Make attachment type from it, working out filename and mime type
filename = uu.match(/^begin\s+[0-9]+\s+(.*)$/)[1]
@@ -1192,7 +919,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'
@@ -1202,22 +931,23 @@ class IncomingMessage < ActiveRecord::Base
tempfile.print body
tempfile.flush
if content_type == 'application/vnd.ms-word'
- AlaveteliExternalCommand.run(`which wvText`.chomp, tempfile.path, tempfile.path + ".txt")
+ AlaveteliExternalCommand.run("wvText", 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(`which catdoc`.chomp, tempfile.path, :append_to => text)
+ AlaveteliExternalCommand.run("catdoc", 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(`which catdoc`.chomp, tempfile.path, :append_to => text)
+ AlaveteliExternalCommand.run("catdoc", 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(`which elinks`.chomp, "-eval", "'set document.codepage.assume = \"utf-8\"'", "-dump-charset", "utf-8", "-force-html", "-dump",
- tempfile.path, :append_to => text)
+ # 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("elinks", "-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, :env => {"LANG" => "C"})
elsif content_type == 'application/vnd.ms-excel'
# Bit crazy using /usr/bin/strings - but xls2csv, xlhtml and
# py_xls2txt only extract text from cells, not from floating
@@ -1227,9 +957,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(`which catppt`.chomp, tempfile.path, :append_to => text)
+ AlaveteliExternalCommand.run("catppt", tempfile.path, :append_to => text)
elsif content_type == 'application/pdf'
- AlaveteliExternalCommand.run(`which pdftotext`.chomp, tempfile.path, "-", :append_to => text)
+ AlaveteliExternalCommand.run("pdftotext", 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.
@@ -1283,7 +1013,7 @@ 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)
@@ -1376,7 +1106,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 cfef6ebd8..b5a1cd833 100644
--- a/app/models/info_request.rb
+++ b/app/models/info_request.rb
@@ -1,6 +1,5 @@
-
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: info_requests
#
@@ -11,23 +10,17 @@
# created_at :datetime not null
# updated_at :datetime not null
# described_state :string(255) not null
-# awaiting_description :boolean default(false), not null
+# awaiting_description :boolean default(FALSE), not null
# prominence :string(255) default("normal"), not null
# url_title :text not null
# law_used :string(255) default("foi"), not null
# allow_new_responses_from :string(255) default("anybody"), not null
# handle_rejected_responses :string(255) default("bounce"), not null
+# idhash :string(255) not null
#
-# models/info_request.rb:
-# A Freedom of Information request.
-#
-# Copyright (c) 2007 UK Citizens Online Democracy. All rights reserved.
-# Email: francis@mysociety.org; WWW: http://www.mysociety.org/
-#
-# $Id: info_request.rb,v 1.217 2009-10-26 17:52:39 francis Exp $
+
require 'digest/sha1'
-require File.join(File.dirname(__FILE__),'../../vendor/plugins/acts_as_xapian/lib/acts_as_xapian')
class InfoRequest < ActiveRecord::Base
strip_attributes!
diff --git a/app/models/info_request_event.rb b/app/models/info_request_event.rb
index 4ea89bf81..99f34cf9e 100644
--- a/app/models/info_request_event.rb
+++ b/app/models/info_request_event.rb
@@ -1,5 +1,5 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: info_request_events
#
@@ -8,12 +8,12 @@
# event_type :text not null
# params_yaml :text not null
# created_at :datetime not null
-# described_state :string(255)
-# calculated_state :string(255)
-# last_described_at :datetime
-# incoming_message_id :integer
-# outgoing_message_id :integer
-# comment_id :integer
+# described_state :string(255)
+# calculated_state :string(255)
+# last_described_at :datetime
+# incoming_message_id :integer
+# outgoing_message_id :integer
+# comment_id :integer
# prominence :string(255) default("normal"), not null
#
@@ -109,7 +109,7 @@ class InfoRequestEvent < ActiveRecord::Base
[ :tags, 'U', "tag" ]
],
:if => :indexed_by_search?,
- :eager_load => [ :incoming_message, :outgoing_message, :comment, { :info_request => [ :user, :public_body, :censor_rules ] } ]
+ :eager_load => [ :outgoing_message, :comment, { :info_request => [ :user, :public_body, :censor_rules ] } ]
def requested_by
self.info_request.user.url_name
@@ -147,6 +147,7 @@ class InfoRequestEvent < ActiveRecord::Base
return event.calculated_state
end
end
+ return
end
def waiting_classification
@@ -175,7 +176,41 @@ class InfoRequestEvent < ActiveRecord::Base
# format it here as no datetime support in Xapian's value ranges
return self.created_at.strftime("%Y%m%d%H%M%S")
end
- # clipped = true - means return shorter text. It is used for snippets for
+
+ def incoming_message_selective_columns(fields)
+ message = IncomingMessage.find(:all,
+ :select => fields + ", incoming_messages.info_request_id",
+ :joins => "INNER JOIN info_request_events ON incoming_messages.id = incoming_message_id ",
+ :conditions => "info_request_events.id = #{self.id}"
+ )
+ message = message[0]
+ if !message.nil?
+ message.info_request = InfoRequest.find(message.info_request_id)
+ end
+ return message
+ end
+
+ def get_clipped_response_efficiently
+ # XXX this ugly code is an attempt to not always load all the
+ # columns for an incoming message, which can be *very* large
+ # (due to all the cached text). We care particularly in this
+ # case because it's called for every search result on a page
+ # (to show the search snippet). Actually, we should review if we
+ # need all this data to be cached in the database at all, and
+ # then we won't need this horrid workaround.
+ message = self.incoming_message_selective_columns("cached_attachment_text_clipped, cached_main_body_text_folded")
+ clipped_body = message.cached_main_body_text_folded
+ clipped_attachment = message.cached_attachment_text_clipped
+ if clipped_body.nil? || clipped_attachment.nil?
+ # we're going to have to load it anyway
+ text = self.incoming_message.get_text_for_indexing_clipped
+ else
+ text = clipped_body.gsub("FOLDED_QUOTED_SECTION", " ").strip + "\n\n" + clipped_attachment
+ end
+ return text + "\n\n"
+ end
+
+ # clipped = true - means return shorter text. It is used for snippets fore
# performance reasons. Xapian will take the full text.
def search_text_main(clipped = false)
text = ''
@@ -185,7 +220,7 @@ class InfoRequestEvent < ActiveRecord::Base
text = text + self.outgoing_message.get_text_for_indexing + "\n\n"
elsif self.event_type == 'response'
if clipped
- text = text + self.incoming_message.get_text_for_indexing_clipped + "\n\n"
+ text = text + self.get_clipped_response_efficiently
else
text = text + self.incoming_message.get_text_for_indexing_full + "\n\n"
end
@@ -295,7 +330,7 @@ class InfoRequestEvent < ActiveRecord::Base
end
- def is_incoming_message?() not self.incoming_message.nil? end
+ def is_incoming_message?() not self.incoming_message_selective_columns("incoming_messages.id").nil? end
def is_outgoing_message?() not self.outgoing_message.nil? end
def is_comment?() not self.comment.nil? end
diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb
index b7e310b1e..cc561b21d 100644
--- a/app/models/outgoing_message.rb
+++ b/app/models/outgoing_message.rb
@@ -1,5 +1,5 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: outgoing_messages
#
@@ -10,8 +10,8 @@
# message_type :string(255) not null
# created_at :datetime not null
# updated_at :datetime not null
-# last_sent_at :datetime
-# incoming_message_followup_id :integer
+# last_sent_at :datetime
+# incoming_message_followup_id :integer
# what_doing :string(255) not null
#
diff --git a/app/models/post_redirect.rb b/app/models/post_redirect.rb
index b111d019d..59cc86799 100644
--- a/app/models/post_redirect.rb
+++ b/app/models/post_redirect.rb
@@ -1,17 +1,17 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: post_redirects
#
# id :integer not null, primary key
# token :text not null
# uri :text not null
-# post_params_yaml :text
+# post_params_yaml :text
# created_at :datetime not null
# updated_at :datetime not null
# email_token :text not null
-# reason_params_yaml :text
-# user_id :integer
+# reason_params_yaml :text
+# user_id :integer
# circumstance :text default("normal"), not null
#
diff --git a/app/models/profile_photo.rb b/app/models/profile_photo.rb
index b15e3e4f4..43dbbbf0a 100644
--- a/app/models/profile_photo.rb
+++ b/app/models/profile_photo.rb
@@ -1,12 +1,12 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: profile_photos
#
# id :integer not null, primary key
# data :binary not null
-# user_id :integer
-# draft :boolean default(false), not null
+# user_id :integer
+# draft :boolean default(FALSE), not null
#
# models/profile_photo.rb:
diff --git a/app/models/public_body.rb b/app/models/public_body.rb
index 453e3a6cf..961fa3cbb 100644
--- a/app/models/public_body.rb
+++ b/app/models/public_body.rb
@@ -275,7 +275,7 @@ class PublicBody < ActiveRecord::Base
ret = ret + types[-1]
return ret
else
- return "A public authority"
+ return _("A public authority")
end
end
@@ -395,7 +395,7 @@ class PublicBody < ActiveRecord::Base
field_list = ['name', 'short_name', 'request_email', 'notes', 'publication_scheme', 'home_page', 'tag_string']
- if public_body = bodies_by_name[name] # Existing public body
+ if public_body = bodies_by_name[name] # Existing public body
available_locales.each do |locale|
PublicBody.with_locale(locale) do
changed = ActiveSupport::OrderedHash.new
diff --git a/app/models/raw_email.rb b/app/models/raw_email.rb
index c6066cbf4..3e12a6feb 100644
--- a/app/models/raw_email.rb
+++ b/app/models/raw_email.rb
@@ -1,11 +1,10 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: raw_emails
#
-# id :integer not null, primary key
-# data_text :text
-# data_binary :binary
+# id :integer not null, primary key
+#
# models/raw_email.rb:
# The fat part of models/incoming_message.rb
@@ -28,7 +27,7 @@ class RawEmail < ActiveRecord::Base
def directory
request_id = self.incoming_message.info_request.id.to_s
if ENV["RAILS_ENV"] == "test"
- return 'files/raw_email_test'
+ return File.join(RAILS_ROOT, 'files/raw_email_test')
else
return File.join(MySociety::Config.get('RAW_EMAILS_LOCATION',
'files/raw_emails'),
@@ -44,41 +43,19 @@ class RawEmail < ActiveRecord::Base
if !File.exists?(self.directory)
FileUtils.mkdir_p self.directory
end
- File.open(self.filepath, "wb") { |file|
+ File.atomic_write(self.filepath) { |file|
file.write d
}
end
def data
- if !File.exists?(self.filepath)
- dbdata
- else
- File.open(self.filepath, "rb" ).read
- end
+ File.open(self.filepath, "rb").read
end
def destroy_file_representation!
File.delete(self.filepath)
end
- def dbdata=(d)
- write_attribute(:data_binary, d)
- end
-
- def dbdata
- d = read_attribute(:data_binary)
- if !d.nil?
- return d
- end
-
- d = read_attribute(:data_text)
- if !d.nil?
- return d
- end
-
- raise "internal error, double nil value in RawEmail"
- end
-
end
diff --git a/app/models/request_mailer.rb b/app/models/request_mailer.rb
index 272f2ea83..83cce9045 100644
--- a/app/models/request_mailer.rb
+++ b/app/models/request_mailer.rb
@@ -353,7 +353,18 @@ class RequestMailer < ApplicationMailer
# That that patch has not been applied, despite bribes of beer, is
# typical of the lack of quality of Rails.
- info_requests = InfoRequest.find(:all, :conditions => [ "(select id from info_request_events where event_type = 'comment' and info_request_events.info_request_id = info_requests.id and created_at > ? limit 1) is not null", Time.now() - 1.month ], :include => [ { :info_request_events => :user_info_request_sent_alerts } ], :order => "info_requests.id, info_request_events.created_at" )
+ info_requests = InfoRequest.find(:all,
+ :conditions => [
+ "info_requests.id in (
+ select info_request_id
+ from info_request_events
+ where event_type = 'comment'
+ and created_at > (now() - '1 month'::interval)
+ )"
+ ],
+ :include => [ { :info_request_events => :user_info_request_sent_alerts } ],
+ :order => "info_requests.id, info_request_events.created_at"
+ )
for info_request in info_requests
# Count number of new comments to alert on
diff --git a/app/models/track_thing.rb b/app/models/track_thing.rb
index b74f7dad5..58d70ed86 100644
--- a/app/models/track_thing.rb
+++ b/app/models/track_thing.rb
@@ -1,18 +1,18 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: track_things
#
# id :integer not null, primary key
# tracking_user_id :integer not null
# track_query :string(255) not null
-# info_request_id :integer
-# tracked_user_id :integer
-# public_body_id :integer
+# info_request_id :integer
+# tracked_user_id :integer
+# public_body_id :integer
# track_medium :string(255) not null
# track_type :string(255) default("internal_error"), not null
-# created_at :datetime
-# updated_at :datetime
+# created_at :datetime
+# updated_at :datetime
#
# models/track_thing.rb:
@@ -71,14 +71,13 @@ class TrackThing < ActiveRecord::Base
def track_query_description
# XXX this is very brittle... we should probably ask users
# simply to name their tracks when they make them?
- self.track_query = self.track_query.gsub(/([()]|OR)/, "")
- filters = self.track_query.scan /\b\S+:\S+\b/
- text = self.track_query
+ original_text = parsed_text = self.track_query.gsub(/([()]|OR)/, "")
+ filters = parsed_text.scan /\b\S+:\S+\b/
varieties = Set.new
date = ""
statuses = Set.new
for filter in filters
- text = text.sub(filter, "")
+ parsed_text = parsed_text.sub(filter, "")
if filter =~ /variety:user/
varieties << _("users")
end
@@ -105,7 +104,7 @@ class TrackThing < ActiveRecord::Base
end
end
if filters.empty?
- text = self.track_query
+ parsed_text = original_text
end
descriptions = []
if varieties.include? _("requests")
@@ -116,10 +115,10 @@ class TrackThing < ActiveRecord::Base
varieties << _("anything")
end
descriptions += Array(varieties)
- text = text.strip
+ parsed_text = parsed_text.strip
descriptions = descriptions.join(_(" or "))
- if !text.empty?
- descriptions += _("{{list_of_things}} matching text '{{search_query}}'", :list_of_things => "", :search_query => text)
+ if !parsed_text.empty?
+ descriptions += _("{{list_of_things}} matching text '{{search_query}}'", :list_of_things => "", :search_query => parsed_text)
end
return descriptions
end
diff --git a/app/models/track_things_sent_email.rb b/app/models/track_things_sent_email.rb
index d83bf05ff..777339d75 100644
--- a/app/models/track_things_sent_email.rb
+++ b/app/models/track_things_sent_email.rb
@@ -1,15 +1,15 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: track_things_sent_emails
#
# id :integer not null, primary key
# track_thing_id :integer not null
-# info_request_event_id :integer
-# user_id :integer
-# public_body_id :integer
-# created_at :datetime
-# updated_at :datetime
+# info_request_event_id :integer
+# user_id :integer
+# public_body_id :integer
+# created_at :datetime
+# updated_at :datetime
#
# models/track_things_sent_email.rb:
diff --git a/app/models/user.rb b/app/models/user.rb
index e98d777b1..8c4b35fe6 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,5 +1,5 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: users
#
@@ -10,14 +10,16 @@
# salt :string(255) not null
# created_at :datetime not null
# updated_at :datetime not null
-# email_confirmed :boolean default(false), not null
+# email_confirmed :boolean default(FALSE), not null
# url_name :text not null
# last_daily_track_email :datetime default(Sat Jan 01 00:00:00 UTC 2000)
# admin_level :string(255) default("none"), not null
# ban_text :text default(""), not null
# about_me :text default(""), not null
-# email_bounced_at :datetime
+# locale :string(255)
+# email_bounced_at :datetime
# email_bounce_message :text default(""), not null
+# no_limit :boolean default(FALSE), not null
#
# models/user.rb:
@@ -130,7 +132,7 @@ class User < ActiveRecord::Base
name.strip!
end
if self.public_banned?
- name = _("{{user_name}} (Banned)", :user_name=>name)
+ name = _("{{user_name}} (Account suspended)", :user_name=>name)
end
name
end
@@ -255,7 +257,7 @@ class User < ActiveRecord::Base
end
def User.owns_every_request?(user)
- !user.nil? && user.owns_every_request?
+ !user.nil? && user.owns_every_request?
end
# Can the user see every request, even hidden ones?
@@ -273,7 +275,18 @@ class User < ActiveRecord::Base
end
# Various ways the user can be banned, and text to describe it if failed
def can_file_requests?
- self.ban_text.empty?
+ self.ban_text.empty? && !self.exceeded_limit?
+ end
+ def exceeded_limit?
+ # Some users have no limit
+ return false if self.no_limit
+
+ # Has the user issued as many as MAX_REQUESTS_PER_USER_PER_DAY requests in the past 24 hours?
+ daily_limit = MySociety::Config.get("MAX_REQUESTS_PER_USER_PER_DAY")
+ return false if daily_limit.nil?
+ recent_requests = InfoRequest.count(:conditions => ["user_id = ? and created_at > now() - '1 day'::interval", self.id])
+
+ return (recent_requests >= daily_limit)
end
def can_make_followup?
self.ban_text.empty?
@@ -285,7 +298,11 @@ class User < ActiveRecord::Base
self.ban_text.empty?
end
def can_fail_html
- text = self.ban_text.strip
+ if ban_text
+ text = self.ban_text.strip
+ else
+ raise "Unknown reason for ban"
+ end
text = CGI.escapeHTML(text)
text = MySociety::Format.make_clickable(text, :contract => 1)
text = text.gsub(/\n/, '<br>')
diff --git a/app/models/user_info_request_sent_alert.rb b/app/models/user_info_request_sent_alert.rb
index d07b4e553..5f23355bf 100644
--- a/app/models/user_info_request_sent_alert.rb
+++ b/app/models/user_info_request_sent_alert.rb
@@ -1,5 +1,5 @@
# == Schema Information
-# Schema version: 95
+# Schema version: 108
#
# Table name: user_info_request_sent_alerts
#
@@ -7,7 +7,7 @@
# user_id :integer not null
# info_request_id :integer not null
# alert_type :string(255) not null
-# info_request_event_id :integer
+# info_request_event_id :integer
#
# models/user_info_request_sent_alert.rb:
diff --git a/app/views/admin_general/debug.rhtml b/app/views/admin_general/debug.rhtml
index 422edea03..40fe33616 100644
--- a/app/views/admin_general/debug.rhtml
+++ b/app/views/admin_general/debug.rhtml
@@ -7,6 +7,10 @@
<h2>Version numbers</h2>
<p>
+Alaveteli branch: <%= link_to @current_branch, @github_origin + @current_branch %>
+<br>
+Alaveteli commit: <%= link_to @current_commit, @github_origin + @current_commit %>
+<br>
RUBY_VERSION <%=h RUBY_VERSION %>
<br>
Rails::VERSION::STRING <%=h Rails::VERSION::STRING%>
@@ -16,8 +20,6 @@ TMail::VERSION::STRING <%=h TMail::VERSION::STRING%>
Xapian::version_string <%=h Xapian::version_string%>
<br>
Spec::VERSION::STRING <%=h Spec::VERSION::STRING%>
-<br>
-Spec::Rails::VERSION::STRING <%=h Spec::Rails::VERSION::STRING%>
</p>
<h2>Configuration</h2>
diff --git a/app/views/admin_general/timeline.rhtml b/app/views/admin_general/timeline.rhtml
index dc72e46cd..39a4b3e36 100644
--- a/app/views/admin_general/timeline.rhtml
+++ b/app/views/admin_general/timeline.rhtml
@@ -36,10 +36,9 @@
end
%>
<% elsif event.event_type == 'edit_outgoing' %>
- <% outgoing_messages = OutgoingMessage.find(:all, event.params[:outgoing_message_id].to_i) %>
+ <% outgoing_message = OutgoingMessage.find(event.params[:outgoing_message_id].to_i) %>
had outgoing message edited by administrator <strong><%=h event.params[:editor] %></strong>.
- <% if outgoing_messages.size > 0 %>
- <% outgoing_message = outgoing_messages[0] %>
+ <% if outgoing_message %>
<% for p in ['body']
if event.params[p.to_sym] != event.params[('old_'+p).to_sym]
%> Changed <%=p%> from '<%=h event.params[('old_'+p).to_sym]%>' to '<%=h event.params[p.to_sym] %>'. <%
@@ -50,10 +49,9 @@
Missing outgoing message, internal error.
<% end %>
<% elsif event.event_type == 'edit_comment' %>
- <% comments = Comment.find(:all, event.params[:comment_id].to_i) %>
+ <% comment = Comment.find(event.params[:comment_id].to_i) %>
had annotation edited by administrator <strong><%=h event.params[:editor] %></strong>.
- <% if comments.size > 0 %>
- <% comment = comments[0] %>
+ <% if comment %>
<% for p in ['body']
if event.params[p.to_sym] != event.params[('old_'+p).to_sym]
%> Changed <%=p%> from '<%=h event.params[('old_'+p).to_sym]%>' to '<%=h event.params[p.to_sym] %>'. <%
diff --git a/app/views/admin_public_body/_form.rhtml b/app/views/admin_public_body/_form.rhtml
index 1cdc9b3fe..d854b53f5 100644
--- a/app/views/admin_public_body/_form.rhtml
+++ b/app/views/admin_public_body/_form.rhtml
@@ -50,12 +50,13 @@
<h3>Common Fields</h3>
<p><label for="public_body_tag_string">Tags <small>(space separated; see list of tags on the right; also <strong>not_apply</strong> if FOI and EIR no longer apply to authority, <strong>eir_only</strong> if EIR but not FOI applies to authority, <strong>defunct</strong> if the authority no longer exists; charity:NUMBER if a registered charity)</small></label><br/>
-<%= f.text_field :tag_string, :size => 60 %></p>
+
+<%= text_field :public_body, :tag_string, :size => 60, :id => 'public_body_tag_string' %></p>
<p><label for="public_body_home_page">Home page <small>(of whole authority, not just their FOI page; set to <strong>blank</strong> (empty string) to guess it from the email)</small></label><br/>
-<%= f.text_field :home_page, :size => 60 %></p>
+<%= text_field :public_body, :home_page, :size => 60, :id => 'public_body_home_page' %></p>
<p><label for="public_body_last_edit_comment"><strong>Comment</strong> for this edit</label> <small>(put URL or other source of new info)</small><br/>
-<%= f.text_area :last_edit_comment, :rows => 3, :cols => 60 %></p>
+<%= text_area :public_body, :last_edit_comment, :rows => 3, :cols => 60, :id => 'public_body_last_edit_comment' %></p>
<!--[eoform:public_body]-->
diff --git a/app/views/admin_public_body/edit.rhtml b/app/views/admin_public_body/edit.rhtml
index b91f15a2e..b19477a6b 100644
--- a/app/views/admin_public_body/edit.rhtml
+++ b/app/views/admin_public_body/edit.rhtml
@@ -9,9 +9,9 @@
<%= render :partial => 'tag_help' %>
<div id="public_body_form">
- <% form_for @public_body, :url => {:action => 'update'} do |f| %>
- <%= render :partial => 'form', :locals => {:f => f} %>
- <p><%= f.submit 'Save', :accesskey => 's' %></p>
+ <% form_tag '../update/' + @public_body.id.to_s do %>
+ <%= render :partial => 'form' %>
+ <p><%= submit_tag 'Save', :accesskey => 's' %></p>
<% end %>
<p>
diff --git a/app/views/admin_public_body/import_csv.rhtml b/app/views/admin_public_body/import_csv.rhtml
index ecd2c38b7..d5717de23 100644
--- a/app/views/admin_public_body/import_csv.rhtml
+++ b/app/views/admin_public_body/import_csv.rhtml
@@ -31,7 +31,7 @@
<p><strong>CSV file format:</strong> A first row with the list of fields,
starting with '#', is optional but highly recommended. The fields 'name'
- and 'request_email' are required; additionaly, translated values are supported by
+ and 'request_email' are required; additionally, translated values are supported by
adding the locale name to the field name, e.g. 'name.es', 'name.de'... Example:
</p>
diff --git a/app/views/admin_public_body/new.rhtml b/app/views/admin_public_body/new.rhtml
index b859fdf6a..047d5a5bb 100644
--- a/app/views/admin_public_body/new.rhtml
+++ b/app/views/admin_public_body/new.rhtml
@@ -11,9 +11,9 @@
<%= render :partial => 'tag_help' %>
<div id="public_body_form">
- <% form_for :public_body, @public_body, :url => {:action => "create"} do |f| %>
- <%= render :partial => 'form', :locals => {:f => f} %>
- <p><%= f.submit "Create" %></p>
+ <% form_tag './create/' + @public_body.id.to_s do %>
+ <%= render :partial => 'form' %>
+ <p><%= submit_tag "Create" %></p>
<% end %>
<p>
diff --git a/app/views/admin_user/_form.rhtml b/app/views/admin_user/_form.rhtml
index ba2bd8f8b..be69d9a80 100644
--- a/app/views/admin_user/_form.rhtml
+++ b/app/views/admin_user/_form.rhtml
@@ -8,10 +8,10 @@
<p><label for="admin_user_email">Email</label> (<strong>you must</strong> first validate this)<br/>
<%= text_field 'admin_user', 'email', :size => 60 %></p>
-<p><label for="admin_level">Admin level</label> (<strong>none</strong> or <strong>super</strong>; this is for admin features and links which are in the site proper)<br/>
+<p><label for="admin_user_admin_level">Admin level</label> (<strong>none</strong> or <strong>super</strong>; this is for admin features and links which are in the site proper)<br/>
<%= text_field 'admin_user', 'admin_level', :size => 60 %></p>
-<p><label for="ban_text">Ban text</label> <small>(if not blank will stop the
+<p><label for="admin_user_ban_text">Ban text</label> <small>(if not blank will stop the
user from filing new requests, making annotations or messaging other users;
the text is shown in public on the user's page and when they try to do a
forbidden action; write in the second person (you); see
@@ -19,7 +19,9 @@
<%= text_area 'admin_user', 'ban_text', :cols => 60, :rows => 3 %></p>
-<p><label for="about_me">About me</label> (user's own text on their profile, format like comments):<br/>
+<p><label for="admin_user_about_me">About me</label> (user's own text on their profile, format like comments):<br/>
<%= text_area 'admin_user', 'about_me', :cols => 60, :rows => 3 %></p>
+<p><%= check_box 'admin_user', 'no_limit' %>
+<label for="admin_user_no_limit">No rate limit</label> (disable the limit on daily requests)</p>
diff --git a/app/views/general/_topnav.rhtml b/app/views/general/_topnav.rhtml
index 619ff3593..8ef928bba 100644
--- a/app/views/general/_topnav.rhtml
+++ b/app/views/general/_topnav.rhtml
@@ -1,10 +1,10 @@
<div id="topnav">
<ul id="navigation">
- <li class="<%= 'selected' if params[:controller] == 'general' and params[:action] != 'blog' %>"><%= link_to _("Home"), frontpage_url %></li>
+ <li class="<%= 'selected' if params[:controller] == 'general' and params[:action] != 'blog' and params[:action] != 'search' %>"><%= link_to _("Home"), frontpage_url %></li>
<li class="<%= 'selected' if params[:controller] == 'request' and ['new', 'select_authority'].include?(params[:action]) %>"><%= link_to _("Make a request"), select_authority_url, :id => 'make-request-link' %></li>
<li class="<%= 'selected' if params[:controller] == 'request' and !['new', 'select_authority'].include?(params[:action]) %>"><%= link_to _("View requests"), request_list_successful_url %></li>
<li class="<%= 'selected' if params[:controller] == 'public_body' %>"><%= link_to _("View authorities"), list_public_bodies_default %></li>
<li class="<%= 'selected' if params[:controller] == 'general' and params[:action] == 'blog' %>"><%= link_to _("Read blog"), blog_url %></li>
<li class="<%= 'selected' if params[:controller] == 'help' %>"><%= link_to _("Help"), help_about_url %></li>
</ul>
-</div> \ No newline at end of file
+</div>
diff --git a/app/views/general/exception_caught.rhtml b/app/views/general/exception_caught.rhtml
index b266b53a1..5f0dfe13d 100644
--- a/app/views/general/exception_caught.rhtml
+++ b/app/views/general/exception_caught.rhtml
@@ -19,6 +19,6 @@
<% end %>
<h2><%= _('Technical details') %></h2>
- <p><strong><%=@exception_class ? @exception_class : _("Unknown")%></strong></p>
- <p><strong><%=@exception_message %></strong></p>
+ <p><strong><%= h(@exception_class ? @exception_class : _("Unknown")) %></strong></p>
+ <p><strong><%= h(@exception_message) %></strong></p>
</div>
diff --git a/app/views/general/frontpage.rhtml b/app/views/general/frontpage.rhtml
index 35751b6a4..38133e7ab 100644
--- a/app/views/general/frontpage.rhtml
+++ b/app/views/general/frontpage.rhtml
@@ -12,20 +12,20 @@
<div id="right_column">
<div id="frontpage_search_box">
<h2>
- <%= _("Search over<br/>
+ <%= _("Search over<br/>
<strong>{{number_of_requests}} requests</strong> <span>and</span><br/>
<strong>{{number_of_authorities}} authorities</strong>",
- :number_of_requests => InfoRequest.count, :number_of_authorities => PublicBody.count) %>
+ :number_of_requests => InfoRequest.count, :number_of_authorities => PublicBody.count) %>
</h2>
<% form_tag({:action => "search_redirect"}, {:id => "search_form"}) do %>
- <div>
- <%= text_field_tag 'query', params[:query], { :size => 30 } %>
- <%= submit_tag _('Search') %>
- </div>
+ <div>
+ <%= text_field_tag 'query', params[:query], { :size => 30 } %>
+ <%= submit_tag _('Search') %>
+ </div>
<% end %>
</div>
<div id="frontpage_right_to_know">
- <%= render :partial => 'frontpage_intro_sentence' %>
+ <%= render :partial => 'frontpage_intro_sentence' %>
</div>
</div>
<div style="clear:both"></div>
@@ -34,38 +34,38 @@
<div id="frontpage_examples">
<% if @popular_bodies.size > 0 %>
<div id="examples_0">
- <h3><%= _("Who can I request information from?") %></h3>
- <%= _("{{site_name}} covers requests to {{number_of_authorities}} authorities, including:",
- :site_name => site_name, :number_of_authorities => PublicBody.count) %>
- <ul>
+ <h3><%= _("Who can I request information from?") %></h3>
+ <%= _("{{site_name}} covers requests to {{number_of_authorities}} authorities, including:",
+ :site_name => site_name, :number_of_authorities => PublicBody.count) %>
+ <ul>
<% for popular_body in @popular_bodies %>
<li><%=public_body_link(popular_body)%>
<%= n_('%d request', '%d requests', popular_body.info_requests.count) % popular_body.info_requests.count %>
</li>
<% end%>
- </ul>
- <p><strong>
- <%= link_to _('Browse all authorities...'), list_public_bodies_default %>
- </strong></p>
- </div>
+ </ul>
+ <p><strong>
+ <%= link_to _('Browse all authorities...'), list_public_bodies_default %>
+ </strong></p>
+ </div>
<% end %>
- <div id="examples_1">
- <h3><%= _("What information has been released?") %></h3>
- <%= _("{{site_name}} users have made {{number_of_requests}} requests, including:",
- :site_name => site_name, :number_of_requests => InfoRequest.count) %>
- <ul>
- <% for event in @request_events %>
- <li>
- <%= public_body_link(event.info_request.public_body) %> <%= _('answered a request about') %>
- <%=link_to h(event.info_request.title), request_url(event.info_request)%>
+ <div id="examples_1">
+ <h3><%= _("What information has been released?") %></h3>
+ <%= _("{{site_name}} users have made {{number_of_requests}} requests, including:",
+ :site_name => site_name, :number_of_requests => InfoRequest.count) %>
+ <ul>
+ <% for event in @request_events %>
+ <li>
+ <%= public_body_link(event.info_request.public_body) %> <%= _('answered a request about') %>
+ <%=link_to h(event.info_request.title), request_url(event.info_request)%>
<%= _('{{length_of_time}} ago', :length_of_time => time_ago_in_words(event.described_at)) %>
- <p class="excerpt" onclick="document.location.href='<%=request_url(event.info_request)%>'"><%= excerpt(event.info_request.title, "", 200) %></p>
- </li>
- <% end %>
- </ul>
- <p><strong><%=link_to _('More successful requests...'), request_list_successful_url %></strong></p>
- </div>
+ <p class="excerpt" onclick="document.location.href='<%=request_url(event.info_request)%>'"><%= excerpt(event.search_text_main(true), "", 200) %></p>
+ </li>
+ <% end %>
+ </ul>
+ <p><strong><%=link_to _('More successful requests...'), request_list_successful_url %></strong></p>
+ </div>
</div>
diff --git a/app/views/general/search.rhtml b/app/views/general/search.rhtml
index 87a6ab446..90ace809e 100644
--- a/app/views/general/search.rhtml
+++ b/app/views/general/search.rhtml
@@ -7,7 +7,7 @@
<% if @query.nil? %>
<% @title = _("Search Freedom of Information requests, public authorities and users") %>
<% elsif @total_hits == 0 %>
- <% @title = _('There were no requests matching your query.') %>
+ <% @title = _('There were no results matching your query.') %>
<% else %>
<% @title = _("Results page {{page_number}}", :page_number => @page.to_s) %>
<% end%>
@@ -56,11 +56,7 @@
["all", _("everything")]]%>
<% for variety, label in labels %>
<% if @variety_postfix != variety %>
- <% if variety != "users" %>
- <%= link_to label, search_url([params[:query], @common_query], variety, @sort_postfix) %>
- <% else %>
- <%= link_to label, search_url(params[:query], variety, @sort_postfix) %>
- <% end %>
+ <%= link_to label, search_url([params[:query], variety, @sort_postfix]) %>
<% else %>
<%= label %>
<% end %>
@@ -126,9 +122,9 @@
<% if !@query.nil? %>
<p id="search_controls">
- <%=link_to_unless @sortby == 'relevant', _("Show most relevant results first"), search_url(@query, @variety_postfix, 'relevant') %>
+ <%=link_to_unless @sortby == 'relevant', _("Show most relevant results first"), search_url([params[:query], @variety_postfix, 'relevant'], params) %>
|
- <%=link_to_unless @sortby == 'newest', _("Newest results first"), search_url(@query, @variety_postfix, 'newest') %>
+ <%=link_to_unless @sortby == 'newest', _("Newest results first"), search_url([params[:query], @variety_postfix, 'newest'], params) %>
<% if @sortby == 'described' %>
| <%= _('Recently described results first') %>
<% end %>
@@ -166,7 +162,6 @@
<%= will_paginate WillPaginate::Collection.new(@page, @bodies_per_page, @xapian_bodies.matches_estimated) %>
<% elsif @bodies && !@query.nil? && @xapian_bodies.results.size == 0 && @page == 1 %>
- <h2 class="publicbody_results"><%= _('No public authorities found') %></h2>
<% if @spelling_correction %>
<p id="did_you_mean"><%= _('Did you mean: {{correction}}', :correction => search_link(@spelling_correction, @postfix)) %></p>
<% end %>
@@ -187,6 +182,8 @@
<%= render :partial => 'user/user_listing_single', :locals => { :display_user => result[:model] } %>
<% end %>
</div>
+ <%= will_paginate WillPaginate::Collection.new(@page, @users_per_page, @xapian_users.matches_estimated) %>
+
<% end %>
</div>
diff --git a/app/views/layouts/default.rhtml b/app/views/layouts/default.rhtml
index 2f8e0bf36..f439b27d2 100644
--- a/app/views/layouts/default.rhtml
+++ b/app/views/layouts/default.rhtml
@@ -10,13 +10,13 @@
</title>
<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 'print', :rel => "stylesheet", :media => "print" %>
- <% if !params[:print_stylesheet].nil? %>
+ <%= stylesheet_link_tag 'main', :title => "Main", :rel => "stylesheet", :media => "all" %>
+ <%= stylesheet_link_tag 'fonts', :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" %>
- <% end %>
- <%= javascript_include_tag 'jquery.js', 'jquery-ui.min','jquery.cookie.js', 'general.js' %>
+ <% end %>
+ <%= javascript_include_tag 'jquery.js', 'jquery-ui.min','jquery.cookie.js', 'general.js' %>
<% if @profile_photo_javascript %>
<script type="text/javascript" src="/javascripts/jquery.Jcrop.js"></script>
@@ -24,7 +24,7 @@
<link rel="stylesheet" href="/stylesheets/jquery.Jcrop.css" type="text/css" >
<% end %>
- <%= stylesheet_link_tag 'admin-theme/jquery-ui-1.8.15.custom.css', :rel => 'stylesheet'%>
+ <%= stylesheet_link_tag 'admin-theme/jquery-ui-1.8.15.custom.css', :rel => 'stylesheet'%>
<!--[if LT IE 7]>
<style type="text/css">@import url("/stylesheets/ie6.css");</style>
<![endif]-->
@@ -34,8 +34,8 @@
<!--[if LT IE 8]>
<style type="text/css">@import url("/stylesheets/ie7.css");</style>
<![endif]-->
- <!-- the following method for customising CSS is deprecated; see `doc/THEMES.md` for detail -->
- <%= 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 %>
@@ -56,7 +56,7 @@
<meta name="robots" content="noindex, nofollow">
<% end %>
- <%= render :partial => 'general/before_head_end' %>
+ <%= render :partial => 'general/before_head_end' %>
</head>
<body <%= "class='front'" if params[:action] == 'frontpage' %>>
@@ -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 436471544..7ade89b8e 100644
--- a/app/views/public_body/_search_ahead.rhtml
+++ b/app/views/public_body/_search_ahead.rhtml
@@ -1,4 +1,4 @@
-<p>
+<div>
<% if !@xapian_requests.nil? %>
<% if @xapian_requests.results.size > 0 %>
<h3><%= _('Top search results:') %></h3>
@@ -13,8 +13,9 @@
<%= render :partial => 'body_listing_single', :locals => { :public_body => result[:model] } %>
<% end %>
</div>
+ <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @xapian_requests.matches_estimated) %>
<% end %>
-</p>
+</div>
diff --git a/app/views/public_body/list.rhtml b/app/views/public_body/list.rhtml
index af91d8ed2..8cb207bd4 100644
--- a/app/views/public_body/list.rhtml
+++ b/app/views/public_body/list.rhtml
@@ -44,7 +44,7 @@
</div>
<% end %>
-<h2 class="publicbody_results"><%= _('Found {{count}} public bodies {{description}}', :count=>@public_bodies.size, :description=>@description) %></h2>
+<h2 class="publicbody_results"><%= _('Found {{count}} public bodies {{description}}', :count=>@public_bodies.total_entries, :description=>@description) %></h2>
<%= render :partial => 'body_listing', :locals => { :public_bodies => @public_bodies } %>
<%= will_paginate(@public_bodies) %><br/>
diff --git a/app/views/public_body/show.rhtml b/app/views/public_body/show.rhtml
index 7d37a35a7..5f20a9717 100644
--- a/app/views/public_body/show.rhtml
+++ b/app/views/public_body/show.rhtml
@@ -1,123 +1,123 @@
<% @title = h(@public_body.name) + _(" - view and make Freedom of Information requests") %>
<div id="main_content">
- <div id="header_right">
+ <div id="header_right">
<h2><%= _('Follow this authority')%></h2>
- <% follower_count = TrackThing.count(:all, :conditions => ["public_body_id = ?", @public_body.id]) %>
- <p><%= n_("There is %d person following this authority", "There are %d people following this authority", follower_count) % follower_count %></p>
+ <% follower_count = TrackThing.count(:all, :conditions => ["public_body_id = ?", @public_body.id]) %>
+ <p><%= n_("There is %d person following this authority", "There are %d people following this authority", follower_count) % follower_count %></p>
- <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => false, :location => 'sidebar' } %>
- <h2><%= _('More about this authority')%></h2>
- <% if !@public_body.calculated_home_page.nil? %>
- <%= link_to _('Home page of authority'), @public_body.calculated_home_page %><br>
- <% end %>
- <% if !@public_body.publication_scheme.empty? %>
- <%= link_to _('Publication scheme'), @public_body.publication_scheme %><br>
- <% end %>
- <% if @public_body.has_tag?("charity") %>
- <% for tag_value in @public_body.get_tag_values("charity") %>
- <% if tag_value.match(/^SC/) %>
- <%= link_to _('Charity registration'), "http://www.oscr.org.uk/CharityIndexDetails.aspx?id=" + tag_value %><br>
- <% else %>
- <%= link_to _('Charity registration'), "http://www.charity-commission.gov.uk/SHOWCHARITY/RegisterOfCharities/CharityFramework.aspx?RegisteredCharityNumber=" + tag_value %><br>
- <% end %>
- <% end %>
- <% end %>
- <%= link_to _('View FOI email address'), view_public_body_email_url(@public_body.url_name) %><br>
- </div>
+ <%= render :partial => 'track/tracking_links', :locals => { :track_thing => @track_thing, :own_request => false, :location => 'sidebar' } %>
+ <h2><%= _('More about this authority')%></h2>
+ <% if !@public_body.calculated_home_page.nil? %>
+ <%= link_to _('Home page of authority'), @public_body.calculated_home_page %><br>
+ <% end %>
+ <% if !@public_body.publication_scheme.empty? %>
+ <%= link_to _('Publication scheme'), @public_body.publication_scheme %><br>
+ <% end %>
+ <% if @public_body.has_tag?("charity") %>
+ <% for tag_value in @public_body.get_tag_values("charity") %>
+ <% if tag_value.match(/^SC/) %>
+ <%= link_to _('Charity registration'), "http://www.oscr.org.uk/CharityIndexDetails.aspx?id=" + tag_value %><br>
+ <% else %>
+ <%= link_to _('Charity registration'), "http://www.charity-commission.gov.uk/SHOWCHARITY/RegisterOfCharities/CharityFramework.aspx?RegisteredCharityNumber=" + tag_value %><br>
+ <% end %>
+ <% end %>
+ <% end %>
+ <%= link_to _('View FOI email address'), view_public_body_email_url(@public_body.url_name) %><br>
+ </div>
- <div id="header_left">
- <p class="public-body-name-prefix"><%= _("Freedom of information requests to") %></p>
- <h1><%=h(@public_body.name)%></h1>
+ <div id="header_left">
+ <p class="public-body-name-prefix"><%= _("Freedom of information requests to") %></p>
+ <h1><%=h(@public_body.name)%></h1>
- <p class="subtitle">
- <%=@public_body.type_of_authority(true)%><% if not @public_body.short_name.empty? %>,
- <%= _('also called {{public_body_short_name}}', :public_body_short_name => h(@public_body.short_name))%><% end %>
- <% if !@user.nil? && @user.admin_page_links? %>
- (<%= link_to _("admin"), public_body_admin_url(@public_body) %>)
- <% end %>
- </p>
+ <p class="subtitle">
+ <%=@public_body.type_of_authority(true)%><% if not @public_body.short_name.empty? %>,
+ <%= _('also called {{public_body_short_name}}', :public_body_short_name => h(@public_body.short_name))%><% end %>
+ <% if !@user.nil? && @user.admin_page_links? %>
+ (<%= link_to _("admin"), public_body_admin_url(@public_body) %>)
+ <% end %>
+ </p>
- <% if @public_body.has_notes? && (@public_body.is_requestable? || @public_body.not_requestable_reason == 'bad_contact') %>
- <p><%= @public_body.notes_as_html %></p>
- <% end %>
+ <% if @public_body.has_notes? && (@public_body.is_requestable? || @public_body.not_requestable_reason == 'bad_contact') %>
+ <p><%= @public_body.notes_as_html %></p>
+ <% end %>
- <% if @public_body.eir_only? %>
- <p><%= _('You can only request information about the environment from this authority.')%></p>
- <% end %>
+ <% if @public_body.eir_only? %>
+ <p><%= _('You can only request information about the environment from this authority.')%></p>
+ <% end %>
- <div id="stepwise_make_request">
- <% if @public_body.is_requestable? || @public_body.not_requestable_reason == 'bad_contact' %>
- <% if @public_body.eir_only? %>
- <%= _('Make a new <strong>Environmental Information</strong> request')%>
+ <div id="stepwise_make_request">
+ <% if @public_body.is_requestable? || @public_body.not_requestable_reason == 'bad_contact' %>
+ <% if @public_body.eir_only? %>
+ <%= _('Make a new <strong>Environmental Information</strong> request')%>
+ <% else %>
+ <%= _('Make a new <strong>Freedom of Information</strong> request to {{public_body}}', :public_body => h(@public_body.name))%>
+ <% end %>
+ &nbsp;<%= _('<a class="link_button_green" href="{{url}}">{{text}}</a>', :url=>new_request_to_body_url(:url_name => @public_body.url_name), :text=>_("Start"))%>
+ <% elsif @public_body.has_notes? %>
+ <%= @public_body.notes_as_html %>
+ <% elsif @public_body.not_requestable_reason == 'not_apply' %>
+ <%= _('Freedom of Information law does not apply to this authority, so you cannot make
+ a request to it.')%>
+ <% elsif @public_body.not_requestable_reason == 'defunct' %>
+ <%= _('This authority no longer exists, so you cannot make a request to it.')%>
<% else %>
- <%= _('Make a new <strong>Freedom of Information</strong> request to {{public_body}}', :public_body => h(@public_body.name))%>
+ <%= _('For an unknown reason, it is not possible to make a request to this authority.')%>
<% end %>
- &nbsp;<%= _('<a class="link_button_green" href="{{url}}">{{text}}</a>', :url=>new_request_to_body_url(:url_name => @public_body.url_name), :text=>_("Start"))%>
- <% elsif @public_body.has_notes? %>
- <%= @public_body.notes_as_html %>
- <% elsif @public_body.not_requestable_reason == 'not_apply' %>
- <%= _('Freedom of Information law does not apply to this authority, so you cannot make
- a request to it.')%>
- <% elsif @public_body.not_requestable_reason == 'defunct' %>
- <%= _('This authority no longer exists, so you cannot make a request to it.')%>
- <% else %>
- <%= _('For an unknown reason, it is not possible to make a request to this authority.')%>
- <% end %>
+ </div>
</div>
- </div>
- <div id="foi_results_section">
- <% if @public_body.info_requests.size == 0 %>
- <% if @public_body.eir_only? %>
- <h2><%= _('Environmental Information Regulations requests made using this site') %></h2>
- <p>Nobody has made any Environmental Information Regulations requests to <%=h(@public_body.name)%> using this site yet.</p>
- <% else %>
- <h2><%= _('Freedom of Information requests made using this site')%></h2>
- <p><%= _('Nobody has made any Freedom of Information requests to {{public_body_name}} using this site yet.', :public_body_name => h(@public_body.name))%></p>
- <% end %>
- <% else %>
- <h2 class="foi_results">
- <% if @public_body.eir_only? %>
- <%= pluralize(@public_body.info_requests.size, "Environmental Information Regulations request made using this site") %>
+ <div id="foi_results_section">
+ <% if @public_body.info_requests.size == 0 %>
+ <% if @public_body.eir_only? %>
+ <h2><%= _('Environmental Information Regulations requests made using this site') %></h2>
+ <p>Nobody has made any Environmental Information Regulations requests to <%=h(@public_body.name)%> using this site yet.</p>
+ <% else %>
+ <h2><%= _('Freedom of Information requests made using this site')%></h2>
+ <p><%= _('Nobody has made any Freedom of Information requests to {{public_body_name}} using this site yet.', :public_body_name => h(@public_body.name))%></p>
+ <% end %>
<% else %>
- <% if @public_body.info_requests.size > 4 %>
- <%= n_('Search within the %d Freedom of Information requests to %s', 'Search within the %d Freedom of Information requests made to %s', @public_body.info_requests.size) % [@public_body.info_requests.size, @public_body.name] %>
- <% else %>
- <%= n_('%d Freedom of Information request to %s', '%d Freedom of Information requests to %s', @public_body.info_requests.size) % [@public_body.info_requests.size, @public_body.name] %>
- <% end %>
+ <h2 class="foi_results">
+ <% if @public_body.eir_only? %>
+ <%= pluralize(@public_body.info_requests.size, "Environmental Information Regulations request made using this site") %>
+ <% else %>
+ <% if @public_body.info_requests.size > 4 %>
+ <%= n_('Search within the %d Freedom of Information requests to %s', 'Search within the %d Freedom of Information requests made to %s', @public_body.info_requests.size) % [@public_body.info_requests.size, @public_body.name] %>
+ <% else %>
+ <%= n_('%d Freedom of Information request to %s', '%d Freedom of Information requests to %s', @public_body.info_requests.size) % [@public_body.info_requests.size, @public_body.name] %>
+ <% end %>
+ <% end %>
+ <%= @page_desc %>
+ </h2>
+ <a name="results"></a>
+
+ <% if @public_body.info_requests.size > 4 %>
+ <%= render :partial => 'request/request_filter_form' %>
+ <% end %>
<% end %>
- <%= @page_desc %>
- </h2>
- <a name="results"></a>
-
- <% if @public_body.info_requests.size > 4 %>
- <%= render :partial => 'request/request_filter_form' %>
- <% end %>
- <% end %>
- <div style="clear:both">&nbsp;</div>
- <% if !@xapian_requests.nil? %>
+ <div style="clear:both">&nbsp;</div>
+ <% if !@xapian_requests.nil? %>
<% for result in @xapian_requests.results %>
<%= render :partial => 'request/request_listing_via_event', :locals => { :event => result[:model], :info_request => result[:model].info_request } %>
<% end %>
- <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @public_body.info_requests.size) %>
+ <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @xapian_requests.matches_estimated) %>
- <% if @xapian_requests.results.empty? %>
- <p><% _('There were no requests matching your query.') %></p>
- <% else %>
- <p> <%= _('Only requests made using {{site_name}} are shown.', :site_name => site_name) %></p>
- <% end %>
+ <% if @xapian_requests.results.empty? %>
+ <p><% _('There were no requests matching your query.') %></p>
+ <% else %>
+ <p> <%= _('Only requests made using {{site_name}} are shown.', :site_name => site_name) %></p>
+ <% end %>
- <% else %>
- <% if @public_body.eir_only? %>
- <h2><%= _('Environmental Information Regulations requests made') %></h2>
- <% else %>
- <h2> <%= _('Freedom of Information requests made')%></h2>
+ <% else %>
+ <% if @public_body.eir_only? %>
+ <h2><%= _('Environmental Information Regulations requests made') %></h2>
+ <% else %>
+ <h2> <%= _('Freedom of Information requests made')%></h2>
+ <% end %>
+ <p> <%= _('The search index is currently offline, so we can\'t show the Freedom of Information requests that have been made to this authority.')%></p>
<% end %>
- <p> <%= _('The search index is currently offline, so we can\'t show the Freedom of Information requests that have been made to this authority.')%></p>
- <% end %>
</div>
</div>
diff --git a/app/views/request/_request_listing_via_event.rhtml b/app/views/request/_request_listing_via_event.rhtml
index e7c378cec..7a211ed88 100644
--- a/app/views/request/_request_listing_via_event.rhtml
+++ b/app/views/request/_request_listing_via_event.rhtml
@@ -4,9 +4,9 @@ end %>
<div class="request_listing">
<div class="request_left">
- <span class="head">
+ <span class="head">
<% if event.is_incoming_message? %>
- <%= link_to highlight_words(info_request.title, @highlight_words), incoming_message_url(event.incoming_message) %>
+ <%= link_to highlight_words(info_request.title, @highlight_words), incoming_message_url(event.incoming_message_selective_columns("incoming_messages.id")) %>
<% elsif event.is_outgoing_message? and event.event_type == 'followup_sent' %>
<%= link_to highlight_words(info_request.title, @highlight_words), outgoing_message_url(event.outgoing_message) %>
<% elsif event.is_comment? %>
@@ -14,9 +14,9 @@ end %>
<% else %>
<%= link_to highlight_words(info_request.title, @highlight_words), request_url(info_request) %>
<% end %>
- </span>
- <div class="requester">
- <% if event.event_type == 'sent' %>
+ </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=>user_link_absolute(info_request.user),:date=>simple_date(event.created_at )) %>
<% elsif event.event_type == 'followup_sent' %>
<%=event.display_status %>
@@ -27,19 +27,22 @@ end %>
<% 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 %>
- <% raise _("unknown event type indexed ") + event.event_type %>
+ <%# Events of other types will not be indexed: see InfoRequestEvent#indexed_by_search?
+ However, it can happen that we see other types of event transiently here in the period
+ between a change being made and the update-xapian-index job being run. %>
+ <!-- Event of type '<%= event.event_type %>', id=<%= event.id %> -->
<% end %>
- </div>
- <span class="bottomline icon_<%= info_request.calculate_status %>">
+ </div>
+ <span class="bottomline icon_<%= info_request.calculate_status %>">
<strong>
<%= info_request.display_status %>
</strong><br>
- </span>
+ </span>
</div>
<div class="request_right">
<span class="desc">
<%= highlight_and_excerpt(event.search_text_main(true), @highlight_words, 150) %>
- </span>
- </div>
+ </span>
+ </div>
</div>
diff --git a/app/views/request/_search_ahead.rhtml b/app/views/request/_search_ahead.rhtml
index d0b19de7d..1e65a5458 100644
--- a/app/views/request/_search_ahead.rhtml
+++ b/app/views/request/_search_ahead.rhtml
@@ -8,7 +8,7 @@
<% end %>
<p>
- <a id="body-site-search-link" target="_blank"><%= _("Or search in their website for this information.") %></a>
+ <a id="body-site-search-link"><%= _("Or search in their website for this information.") %></a>
</p>
<% end %>
</div>
diff --git a/app/views/request/list.rhtml b/app/views/request/list.rhtml
index 3890fa28b..7cbd982f1 100644
--- a/app/views/request/list.rhtml
+++ b/app/views/request/list.rhtml
@@ -14,11 +14,11 @@
<div style="clear:both"></div>
<div class="results_section">
- <% view_cache :ttl => 5.minutes.to_i, :tag => [@view, @page, I18n.locale] do %>
+ <% view_cache :ttl => 5.minutes.to_i, :tag => [@cache_tag] do %>
<% if @list_results.empty? %>
<p> <%= _('No requests of this sort yet.')%></p>
<% else %>
- <h2 class="foi_results"><%= _('{{count}} FOI requests found', :count => @list_results.size) %></h2>
+ <h2 class="foi_results"><%= _('{{count}} FOI requests found', :count => @matches_estimated) %></h2>
<div class="results_block">
<% for result in @list_results%>
<% if result.class.to_s == 'InfoRequestEvent' %>
@@ -30,6 +30,6 @@
</div>
<% end %>
- <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @matches_estimated) %>
+ <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @show_no_more_than) %>
<% end %>
</div>
diff --git a/app/views/request/new.rhtml b/app/views/request/new.rhtml
index 2e554a20b..23212fc0b 100644
--- a/app/views/request/new.rhtml
+++ b/app/views/request/new.rhtml
@@ -47,12 +47,12 @@
<% end %>
</div>
- <div id="request_header_text">
<% if @info_request.public_body.has_notes? %>
+ <div id="request_header_text">
<h3><%= _('Special note for this authority!') %></h3>
<p><%= @info_request.public_body.notes_as_html %></p>
+ </div>
<% end %>
- </div>
<% if @info_request.public_body.eir_only? %>
<h3><%= _('Please ask for environmental information only') %></h3>
diff --git a/app/views/request/select_authority.rhtml b/app/views/request/select_authority.rhtml
index 55ebc40c4..521136f8e 100644
--- a/app/views/request/select_authority.rhtml
+++ b/app/views/request/select_authority.rhtml
@@ -7,16 +7,17 @@
// http://benalman.com/projects/jquery-throttle-debounce-plugin/
$("#query").keypress($.debounce( 300, function() {
// Do a type ahead search and display results
- $("#typeahead_response").load("<%=search_ahead_bodies_url%>?q="+encodeURI(this.value), function() {
+ $("#typeahead_response").load("<%=search_ahead_bodies_url%>?query="+encodeURI(this.value), function() {
$("#authority_preview").hide(); // Hide the preview, since results have changed
});
}));
// We're using the existing body list: we intercept the clicks on the titles to
// display a preview on the right hand side of the screen
- $("#typeahead_response a").live('click', function() {
+ $("#typeahead_response .head a").live('click', function() {
$("#authority_preview").load(this.href+" #public_body_show", function() {
$("#authority_preview").show();
+ $(window).scrollTop($("#banner").height());
$("#authority_preview #header_right").hide();
});
return false;
@@ -33,8 +34,8 @@
<p>
<p>
<%= _('First, type in the <strong>name of the UK public authority</strong> you\'d
- <br>like information from. <strong>By law, they have to respond</strong>
- (<a href="%s">why?</a>).') % help_about_url %>
+ like information from. <strong>By law, they have to respond</strong>
+ (<a href="%s#%s">why?</a>).') % [help_about_url, "whybother_them"] %>
</p>
<%= text_field_tag 'query', params[:query], { :size => 30 } %>
<%= hidden_field_tag 'bodies', 1 %>
@@ -56,6 +57,7 @@
<%= render :partial => 'public_body/body_listing_single', :locals => { :public_body => result[:model] } %>
<% end %>
</div>
+
<% end %>
diff --git a/app/views/user/rate_limited.rhtml b/app/views/user/rate_limited.rhtml
new file mode 100644
index 000000000..c1e8f360e
--- /dev/null
+++ b/app/views/user/rate_limited.rhtml
@@ -0,0 +1,17 @@
+<% @title = "Too many requests" %>
+
+<h1><%=@title%></h1>
+
+<p><%= _("There is a limit on the number of requests that you can make in any one day. You can make more requests tomorrow.")%></p>
+
+<!-- Insert explanation of why we have a limit -->
+
+<p><%= _("If you need to make more requests than this, <a href='%s'>get in touch</a> and we’ll consider it.") % [help_contact_path] %></p>
+
+<% if @info_request %>
+ <p><%= _("Here is the message you wrote, in case you would like to copy the text and save it for later.") %></p>
+
+ <div class="correspondence">
+ <div class="correspondence_text"><%= @info_request.outgoing_messages[0].get_body_for_html_display %></div>
+ </div>
+<% end %>
diff --git a/app/views/user/show.rhtml b/app/views/user/show.rhtml
index baf6621df..8f1803442 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">
@@ -76,7 +81,7 @@
<div id="user_public_banned">
<p>
<strong>
- <%= _('This user has been banned from {{site_name}} ', :site_name=>site_name)%>
+ <%= _('This user has been banned from {{site_name}} ', :site_name=>site_name)%>
</strong>
</p>
<p>
@@ -116,19 +121,20 @@
</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>
- <%= text_field_tag(:user_query, params[:user_query]) %>
- <% if @is_you %>
- <%= submit_tag(_("Search your contributions")) %>
- <% else %>
- <%= submit_tag(_("Search contributions by this person")) %>
- <% end %>
- </div>
- <% end %>
-
+ <% form_tag(show_user_url, :method => "get", :id=>"search_form") do %>
+ <div>
+ <%= text_field_tag(:user_query, params[:user_query]) %>
+ <% if @is_you %>
+ <%= submit_tag(_("Search your contributions")) %>
+ <% else %>
+ <%= submit_tag(_("Search contributions by this person")) %>
+ <% end %>
+ </div>
+ <% end %>
<% if !@xapian_requests.nil? %>
<% if @xapian_requests.results.empty? %>
@@ -136,16 +142,16 @@
<h2 class="foi_results" id="foi_requests"><%= @is_you ? 'Freedom of Information requests made by you' : 'Freedom of Information requests made by this person' %> <%= @match_phrase %>
</h2>
<p><%= @is_you ? _('You have made no Freedom of Information requests using this site.') : _('This person has made no Freedom of Information requests using this site.') %>
- <%= @page_desc %>
+ <%= @page_desc %>
<% end %>
<% else %>
<h2 class="foi_results" id="foi_requests">
- <%= @is_you ? n_('Your %d Freedom of Information request', 'Your %d Freedom of Information requests', @xapian_requests.results.size) % @xapian_requests.results.size : n_('This person\'s %d Freedom of Information request', 'This person\'s %d Freedom of Information requests', @xapian_requests.results.size) % @xapian_requests.results.size %>
+ <%= @is_you ? n_('Your %d Freedom of Information request', 'Your %d Freedom of Information requests', @xapian_requests.matches_estimated.to_s) % @xapian_requests.matches_estimated.to_s : n_('This person\'s %d Freedom of Information request', 'This person\'s %d Freedom of Information requests', @xapian_requests.matches_estimated.to_s) % @xapian_requests.matches_estimated %>
<!-- matches_estimated <%=@xapian_requests.matches_estimated%> -->
<%= @match_phrase %>
- <%= @page_desc %>
+ <%= @page_desc %>
</h2>
-
+
<% for result in @xapian_requests.results %>
<%= render :partial => 'request/request_listing_via_event', :locals => { :event => result[:model], :info_request => result[:model].info_request } %>
@@ -154,17 +160,19 @@
<%= 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? %>
<% if @xapian_comments.results.empty? %>
<% if @page == 1 %>
<h2><%= @is_you ? _('Your annotations') : _('This person\'s annotations') %>
- <%= @match_phrase %>
- </h2>
- <p><%= _('None made.')%></p>
+ <%= @match_phrase %>
+ </h2>
+ <p><%= _('None made.')%></p>
<% end %>
<% else %>
<h2 id="annotations">
@@ -181,55 +189,56 @@
<% end %>
<% end %>
- <% if @is_you %>
- <% if @track_things.empty? %>
- <h2 id="email_subscriptions"> <%= _('Your email subscriptions')%></h2>
- <p><%= _('None made.')%></p>
- <% else %>
- <h2 id="email_subscriptions"> Your <%=pluralize(@track_things.size, _('email subscription')) %> </h2>
- <% if @track_things_grouped.size == 1 %>
+</div>
+<% end %>
+<% if @show_profile && @is_you %>
+ <% if @track_things.empty? %>
+ <h2 id="email_subscriptions"> <%= _('Your email subscriptions')%></h2>
+ <p><%= _('None made.')%></p>
+ <% else %>
+ <h2 id="email_subscriptions"> Your <%=pluralize(@track_things.size, _('email subscription')) %> </h2>
+ <% if @track_things_grouped.size == 1 %>
+ <% form_tag({:controller => 'track', :action => 'delete_all_type'}, :class => "feed_form") do %>
+ <h3>
+ <%=TrackThing.track_type_description(@track_things[0].track_type)%>
+ <%= hidden_field_tag 'track_type', @track_things[0].track_type %>
+ <%= hidden_field_tag 'user', @display_user.id %>
+ <%= hidden_field_tag 'r', request.request_uri %>
+ <% if @track_things.size > 1 %>
+ <%= submit_tag _('unsubscribe all') %>
+ <% end %>
+ </h3>
+ <% end %>
+ <% end %>
+ <% for track_type, track_things in @track_things_grouped %>
+ <% if @track_things_grouped.size > 1 %>
<% form_tag({:controller => 'track', :action => 'delete_all_type'}, :class => "feed_form") do %>
<h3>
- <%=TrackThing.track_type_description(@track_things[0].track_type)%>
- <%= hidden_field_tag 'track_type', @track_things[0].track_type %>
+ <%=TrackThing.track_type_description(track_type)%>
+ <%= hidden_field_tag 'track_type', track_type %>
<%= hidden_field_tag 'user', @display_user.id %>
<%= hidden_field_tag 'r', request.request_uri %>
- <% if @track_things.size > 1 %>
- <%= submit_tag _('unsubscribe all') %>
- <% end %>
+ <% if track_things.size > 1 %>
+ <%= submit_tag _('unsubscribe all')%>
+ <% end %>
</h3>
<% end %>
<% end %>
- <% for track_type, track_things in @track_things_grouped %>
- <% if @track_things_grouped.size > 1 %>
- <% form_tag({:controller => 'track', :action => 'delete_all_type'}, :class => "feed_form") do %>
- <h3>
- <%=TrackThing.track_type_description(track_type)%>
- <%= hidden_field_tag 'track_type', track_type %>
- <%= hidden_field_tag 'user', @display_user.id %>
- <%= hidden_field_tag 'r', request.request_uri %>
- <% if track_things.size > 1 %>
- <%= submit_tag _('unsubscribe all')%>
- <% end %>
- </h3>
- <% end %>
- <% end %>
- <ul>
- <% for track_thing in track_things %>
- <li>
- <% form_tag({:controller => 'track', :action => 'update', :track_id => track_thing.id}, :class => "feed_form") do %>
- <div>
- <%= track_thing.params[:list_description] %>
- <%= hidden_field_tag 'track_medium', "delete", { :id => 'track_medium_' + track_thing.id.to_s } %>
- <%= hidden_field_tag 'r', request.request_uri, { :id => 'r_' + track_thing.id.to_s } %>
- <%= submit_tag _('unsubscribe') %>
- </div>
- <% end %>
- </li>
- <% end %>
- </ul>
+ <ul>
+ <% for track_thing in track_things %>
+ <li>
+ <% form_tag({:controller => 'track', :action => 'update', :track_id => track_thing.id}, :class => "feed_form") do %>
+ <div>
+ <%= track_thing.params[:list_description] %>
+ <%= hidden_field_tag 'track_medium', "delete", { :id => 'track_medium_' + track_thing.id.to_s } %>
+ <%= hidden_field_tag 'r', request.request_uri, { :id => 'r_' + track_thing.id.to_s } %>
+ <%= submit_tag _('unsubscribe') %>
+ </div>
+ <% end %>
+ </li>
<% end %>
+ </ul>
<% end %>
<% end %>
-</div> \ No newline at end of file
+<% end %>
diff --git a/app/views/user/sign.rhtml b/app/views/user/sign.rhtml
index afdb90162..bfd0fa63e 100644
--- a/app/views/user/sign.rhtml
+++ b/app/views/user/sign.rhtml
@@ -1,4 +1,4 @@
-<% if @post_redirect.reason_params[:user_name] %>
+<% if !@post_redirect.nil? && @post_redirect.reason_params[:user_name] %>
<% @title = _("Sign in") %>
<div id="sign_alone">
@@ -19,16 +19,7 @@
<% else %>
<% @title = _('Sign in or make a new account') %>
- <div id="sign_together">
-
- <!--<p id="sign_in_reason">
- <% if @post_redirect.reason_params[:web].empty? %>
- <%= _(' Please sign in or make a new account.') %>
- <% else %>
- <%= @post_redirect.reason_params[:web] %>, <%= _('please sign in or make a new account.') %>
- <% end %>
- </p>-->
-
+ <div id="sign_together">
<div id="left_half">
<h1><%= _('Sign in') %></h1>
<%= render :partial => 'signin', :locals => { :sign_in_as_existing_user => false } %>