diff options
Diffstat (limited to 'app')
30 files changed, 772 insertions, 158 deletions
diff --git a/app/assets/javascripts/new-request.js b/app/assets/javascripts/new-request.js new file mode 100644 index 000000000..f8f2a0665 --- /dev/null +++ b/app/assets/javascripts/new-request.js @@ -0,0 +1,14 @@ +$(document).ready(function() { + $('.batch_public_body_list').hide(); + var showtext = $('.batch_public_body_toggle').attr('data-showtext'); + var hidetext = $('.batch_public_body_toggle').attr('data-hidetext'); + $('.toggle-message').text(showtext); + $('.batch_public_body_toggle').click(function(){ + $('.batch_public_body_list').toggle(); + if ($('.toggle-message').text() == showtext){ + $('.toggle-message').text(hidetext); + }else{ + $('.toggle-message').text(showtext); + } + }) +}) diff --git a/app/assets/javascripts/select-authorities.js b/app/assets/javascripts/select-authorities.js new file mode 100644 index 000000000..843f5c0ad --- /dev/null +++ b/app/assets/javascripts/select-authorities.js @@ -0,0 +1,67 @@ +$(document).ready(function() { + + function add_option(selector, value, text) { + var optionExists = ($(selector + ' option[value=' + value + ']').length > 0); + if(!optionExists){ + $(selector).append("<option value=" + value + ">" + text + "</option>"); + } + } + // Transfer a set of select options defined by 'from_selector' to another select, + // defined by 'to_selector' + function transfer_options(from_selector, to_selector){ + $(from_selector).each(function() + { + add_option(to_selector, $(this).val(), $(this).text()); + $(this).remove(); + }) + $('#public_body_query').val(''); + return false; + } + + // Submit the search form once the text reaches a certain length + $("#public_body_query").keypress($.debounce( 300, function() { + if ($('#public_body_query').val().length >= 3) { + $('#body_search_form').submit(); + } + })); + + // Populate the candidate list with json search results + $('#body_search_form').on('ajax:success', function(event, data, status, xhr) { + $('#select_body_candidates').empty(); + $.each(data, function(key, value) + { + add_option('#select_body_candidates', value['id'], value['name']); + }); + }); + + // Add a hidden element to the submit form for every option in the selected list + $('#body_submit_button').click(function(){ + $('#select_body_selections option').each(function() + { + $('#body_submit_form').append('<input type="hidden" value="' + $(this).val() + '" name="public_body_ids[]">' ); + }) + }) + + // Transfer selected candidates to selected list + $('#body_select_button').click(function(){ + return transfer_options('#select_body_candidates option:selected', '#select_body_selections'); + }) + + // Transfer selected selected options back to candidate list + $('#body_deselect_button').click(function(){ + return transfer_options('#select_body_selections option:selected', '#select_body_candidates'); + }) + + // Transfer all candidates to selected list + $('#body_select_all_button').click(function(){ + return transfer_options('#select_body_candidates option', '#select_body_selections'); + }) + + // Transfer all selected back to candidate list + $('#body_deselect_all_button').click(function(){ + return transfer_options('#select_body_selections option', '#select_body_candidates'); + }) + + // Show the buttons for selecting and deselecting all + $('.select_all_button').show(); +}) diff --git a/app/assets/stylesheets/main.scss b/app/assets/stylesheets/main.scss index 6405d1dc3..3118eab3d 100644 --- a/app/assets/stylesheets/main.scss +++ b/app/assets/stylesheets/main.scss @@ -1722,7 +1722,7 @@ padding-right:50px; margin-bottom:10px; } -div.ff-icon-printfix,.comment_in_request_text img.comment_quote,body.front #other-country-notice,#other-country-notice,#authority_preview .public-body-name-prefix,#authority_preview #list-filter,#authority_preview h2.foi_results,div#show_response_view p.event_actions { +div.ff-icon-printfix,.comment_in_request_text img.comment_quote,body.front #other-country-notice,#other-country-notice,#authority_preview .public-body-name-prefix,#authority_preview #list-filter,#authority_preview h2.foi_results,div#show_response_view p.event_actions, div.batch_public_body_toggle { display:none; } @@ -1777,3 +1777,48 @@ text-decoration:none; the axes black rather than transparent grey for the moment: */ color: #000 !important; } + + +#body_selection .body_list { + width: 45%; +} + +#body_selection .body_list input[type='submit'] { + margin: 10px 0; + width: 45%; +} + +#body_selection .body_list #body_select_all_button, +#body_selection .body_list #body_deselect_button{ + float: right; +} + +#body_selection #body_candidates { + float: left; +} + +#body_selection #body_selections { + float: right; +} + +#body_selection #body_submission input[type='submit'] { + margin: 10px 0; + width:100%; +} + +#body_selection .select_all_button { + display: none; +} + +#body_selection .body_select { + width: 100%; +} + +.batch_public_body_list { + margin-left: 110px; +} + +.batch_public_body_toggle { + color: #0000EE; + font-size: 0.9em; +} diff --git a/app/controllers/admin_user_controller.rb b/app/controllers/admin_user_controller.rb index 929b93e0e..940a5fe8f 100644 --- a/app/controllers/admin_user_controller.rb +++ b/app/controllers/admin_user_controller.rb @@ -48,6 +48,7 @@ class AdminUserController < AdminController @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] + @admin_user.can_make_batch_requests = params[:admin_user][:can_make_batch_requests] if @admin_user.valid? @admin_user.save! diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7c122917d..370e8e15c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -369,9 +369,9 @@ class ApplicationController < ActionController::Base return page end - def perform_search_typeahead(query, model) + def perform_search_typeahead(query, model, per_page=25) @page = get_search_page_from_params - @per_page = 10 + @per_page = per_page query_words = query.split(/ +(?![-+]+)/) if query_words.last.nil? || query_words.last.strip.length < 3 xapian_requests = nil diff --git a/app/controllers/comment_controller.rb b/app/controllers/comment_controller.rb index d4b17e9d2..cda56a211 100644 --- a/app/controllers/comment_controller.rb +++ b/app/controllers/comment_controller.rb @@ -38,7 +38,7 @@ class CommentController < ApplicationController if params[:comment] # XXX this check should theoretically be a validation rule in the model - @existing_comment = Comment.find_by_existing_comment(@info_request.id, params[:comment][:body]) + @existing_comment = Comment.find_existing(@info_request.id, params[:comment][:body]) else # Default to subscribing to request when first viewing form params[:subscribe_to_request] = true @@ -68,7 +68,7 @@ class CommentController < ApplicationController if params[:subscribe_to_request] @track_thing = TrackThing.create_track_for_request(@info_request) - @existing_track = TrackThing.find_by_existing_track(@user, @track_thing) + @existing_track = TrackThing.find_existing(@user, @track_thing) if @user && @info_request.user == @user # don't subscribe to own request! elsif !@existing_track diff --git a/app/controllers/info_request_batch_controller.rb b/app/controllers/info_request_batch_controller.rb new file mode 100644 index 000000000..b66658757 --- /dev/null +++ b/app/controllers/info_request_batch_controller.rb @@ -0,0 +1,16 @@ +class InfoRequestBatchController < ApplicationController + + def show + @info_request_batch = InfoRequestBatch.find(params[:id]) + @per_page = 25 + @page = get_search_page_from_params + if @info_request_batch.sent_at + @info_requests = @info_request_batch.info_requests.visible.all(:offset => (@page - 1) * @per_page, + :limit => @per_page) + else + @public_bodies = @info_request_batch.public_bodies.all(:offset => (@page - 1) * @per_page, + :limit => @per_page) + end + end + +end diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb index b79ead73d..a94461758 100644 --- a/app/controllers/request_controller.rb +++ b/app/controllers/request_controller.rb @@ -11,7 +11,7 @@ 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 - + before_filter :check_batch_requests_and_user_allowed, :only => [ :select_authorities, :new_batch ] MAX_RESULTS = 500 PER_PAGE = 25 @@ -43,6 +43,32 @@ class RequestController < ApplicationController medium_cache end + def select_authorities + if !params[:public_body_query].nil? + @search_bodies = perform_search_typeahead(params[:public_body_query], PublicBody, 1000) + end + respond_to do |format| + format.html do + if !params[:public_body_ids].nil? + if !params[:remove_public_body_ids].nil? + body_ids = params[:public_body_ids] - params[:remove_public_body_ids] + else + body_ids = params[:public_body_ids] + end + @public_bodies = PublicBody.where({:id => body_ids}).all + end + end + format.json do + if @search_bodies + render :json => @search_bodies.results.map{ |result| {:name => result[:model].name, + :id => result[:model].id } } + else + render :json => [] + end + end + end + end + def show if !AlaveteliConfiguration::varnish_host.blank? # If varnish is set up to accept PURGEs, then cache for a @@ -167,6 +193,69 @@ class RequestController < ApplicationController end end + def new_batch + if params[:public_body_ids].blank? + redirect_to select_authorities_path and return + end + + # TODO: Decide if we make batch requesters describe their undescribed requests + # before being able to make a new batch request + + if !authenticated_user.can_file_requests? + @details = authenticated_user.can_fail_html + render :template => 'user/banned' and return + end + + @batch = true + + I18n.with_locale(@locale) do + @public_bodies = PublicBody.where({:id => params[:public_body_ids]}). + includes(:translations). + order('public_body_translations.name').all + end + if params[:submitted_new_request].nil? || params[:reedit] + return render_new_compose(batch=true) + end + + # Check for double submission of batch + @existing_batch = InfoRequestBatch.find_existing(authenticated_user, + params[:info_request][:title], + params[:outgoing_message][:body], + params[:public_body_ids]) + + @info_request = InfoRequest.create_from_attributes(params[:info_request], + params[:outgoing_message], + authenticated_user) + @outgoing_message = @info_request.outgoing_messages.first + @info_request.is_batch_request_template = true + if !@existing_batch.nil? || !@info_request.valid? + # We don't want the error "Outgoing messages is invalid", as in this + # case the list of errors will also contain a more specific error + # describing the reason it is invalid. + @info_request.errors.delete(:outgoing_messages) + render :action => 'new' + return + end + + # Show preview page, if it is a preview + if params[:preview].to_i == 1 + return render_new_preview + end + + @info_request_batch = InfoRequestBatch.create!(:title => params[:info_request][:title], + :body => params[:outgoing_message][:body], + :public_bodies => @public_bodies, + :user => authenticated_user) + flash[:notice] = _("<p>Your {{law_used_full}} requests will be <strong>sent</strong> shortly!</p> + <p><strong>We will email you</strong> when they have been sent. + We will also email you when there is a response to any of them, or after {{late_number_of_days}} working days if the authorities still haven't + replied by then.</p> + <p>If you write about these requests (for example in a forum or a blog) please link to this page.</p>", + :law_used_full=>@info_request.law_used_full, + :late_number_of_days => AlaveteliConfiguration::reply_late_after_days) + redirect_to info_request_batch_path(@info_request_batch) + end + # Page new form posts to def new # All new requests are of normal_sort @@ -211,71 +300,19 @@ class RequestController < ApplicationController 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) - if params[:url_name] - if params[:url_name].match(/^[0-9]+$/) - params[:info_request][:public_body] = PublicBody.find(params[:url_name]) - else - public_body = PublicBody.find_by_url_name_with_historic(params[:url_name]) - raise ActiveRecord::RecordNotFound.new("None found") if public_body.nil? # XXX proper 404 - params[:info_request][:public_body] = public_body - end - elsif params[:public_body_id] - params[:info_request][:public_body] = PublicBody.find(params[:public_body_id]) - # Explicitly load the association as this isn't done automatically in newer Rails versions - elsif params[:info_request][:public_body_id] - params[:info_request][:public_body] = PublicBody.find(params[:info_request][:public_body_id]) - end - if !params[:info_request][:public_body] - # compulsory to have a body by here, or go to front page which is start of process - redirect_to frontpage_url - return - end - - # ... next any tags or other things - params[:info_request][:title] = params[:title] if params[:title] - params[:info_request][:tag_string] = params[:tags] if params[:tags] - - @info_request = InfoRequest.new(params[:info_request]) - params[:info_request_id] = @info_request.id - params[:outgoing_message] = {} if !params[:outgoing_message] - params[:outgoing_message][:body] = params[:body] if params[:body] - params[:outgoing_message][:default_letter] = params[:default_letter] if params[:default_letter] - params[:outgoing_message][:info_request] = @info_request - @outgoing_message = OutgoingMessage.new(params[:outgoing_message]) - @outgoing_message.set_signature_name(@user.name) if !@user.nil? - - if @info_request.public_body.is_requestable? - render :action => 'new' - else - if @info_request.public_body.not_requestable_reason == 'bad_contact' - render :action => 'new_bad_contact' - else - # if not requestable because defunct or not_apply, redirect to main page - # (which doesn't link to the /new/ URL) - redirect_to public_body_url(@info_request.public_body) - end - end - return + return render_new_compose(batch=false) end # See if the exact same request has already been submitted # XXX this check should theoretically be a validation rule in the # model, except we really want to pass @existing_request to the view so # it can link to it. - @existing_request = InfoRequest.find_by_existing_request(params[:info_request][:title], params[:info_request][:public_body_id], params[:outgoing_message][:body]) + @existing_request = InfoRequest.find_existing(params[:info_request][:title], params[:info_request][:public_body_id], params[:outgoing_message][:body]) # Create both FOI request and the first request message - @info_request = InfoRequest.new(params[:info_request]) - @outgoing_message = OutgoingMessage.new(params[:outgoing_message].merge({ - :status => 'ready', - :message_type => 'initial_request' - })) - @info_request.outgoing_messages << @outgoing_message - @outgoing_message.info_request = @info_request + @info_request = InfoRequest.create_from_attributes(params[:info_request], + params[:outgoing_message]) + @outgoing_message = @info_request.outgoing_messages.first # Maybe we lost the address while they're writing it if !@info_request.public_body.is_requestable? @@ -296,24 +333,7 @@ class RequestController < ApplicationController # Show preview page, if it is a preview if params[:preview].to_i == 1 - message = "" - if @outgoing_message.contains_email? - if @user.nil? - message += _("<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"{{url}}\">details</a>).</p>", :url => (help_privacy_path+"#email_address").html_safe); - else - message += _("<p>You do not need to include your email in the request in order to get a reply (<a href=\"{{url}}\">details</a>).</p>", :url => (help_privacy_path+"#email_address").html_safe); - end - message += _("<p>We recommend that you edit your request and remove the email address. - If you leave it, the email address will be sent to the authority, but will not be displayed on the site.</p>") - end - if @outgoing_message.contains_postcode? - message += _("<p>Your request contains a <strong>postcode</strong>. Unless it directly relates to the subject of your request, please remove any address as it will <strong>appear publicly on the Internet</strong>.</p>"); - end - if not message.empty? - flash.now[:error] = message.html_safe - end - render :action => 'preview' - return + return render_new_preview end if user_exceeded_limit @@ -669,7 +689,7 @@ class RequestController < ApplicationController end if !incoming_message.user_can_view?(authenticated_user) @incoming_message = incoming_message # used by view - return render_hidden_message + return render_hidden('request/hidden_correspondence') end # Is this a completely public request that we can cache attachments for # to be served up without authentication? @@ -883,19 +903,10 @@ class RequestController < ApplicationController private - def render_hidden + def render_hidden(template='request/hidden') respond_to do |format| response_code = 403 # forbidden - format.html{ render :template => 'request/hidden', :status => response_code } - format.any{ render :nothing => true, :status => response_code } - end - false - end - - def render_hidden_message - respond_to do |format| - response_code = 403 # forbidden - format.html{ render :template => 'request/hidden_correspondence', :status => response_code } + format.html{ render :template => template, :status => response_code } format.any{ render :nothing => true, :status => response_code } end false @@ -967,6 +978,103 @@ class RequestController < ApplicationController "request/similar/#{info_request.id}/#{locale}" end + def check_batch_requests_and_user_allowed + if !AlaveteliConfiguration::allow_batch_requests + raise RouteNotFound.new("Page not enabled") + end + if !authenticated?( + :web => _("To make a batch request"), + :email => _("Then you can make a batch request"), + :email_subject => _("Make a batch request"), + :user_name => "a user who has been authorised to make batch requests") + # do nothing - as "authenticated?" has done the redirect to signin page for us + return + end + if !@user.can_make_batch_requests? + return render_hidden('request/batch_not_allowed') + end + end + + def render_new_compose(batch) + + params[:info_request] = { } if !params[:info_request] + # Read parameters in + unless batch + # first the public body (by URL name or id) + if params[:url_name] + if params[:url_name].match(/^[0-9]+$/) + params[:info_request][:public_body] = PublicBody.find(params[:url_name]) + else + public_body = PublicBody.find_by_url_name_with_historic(params[:url_name]) + raise ActiveRecord::RecordNotFound.new("None found") if public_body.nil? # XXX proper 404 + params[:info_request][:public_body] = public_body + end + elsif params[:public_body_id] + params[:info_request][:public_body] = PublicBody.find(params[:public_body_id]) + # Explicitly load the association as this isn't done automatically in newer Rails versions + elsif params[:info_request][:public_body_id] + params[:info_request][:public_body] = PublicBody.find(params[:info_request][:public_body_id]) + end + if !params[:info_request][:public_body] + # compulsory to have a body by here, or go to front page which is start of process + redirect_to frontpage_url + return + end + end + + # ... next any tags or other things + params[:info_request][:title] = params[:title] if params[:title] + params[:info_request][:tag_string] = params[:tags] if params[:tags] + + @info_request = InfoRequest.new(params[:info_request]) + if batch + @info_request.is_batch_request_template = true + end + params[:info_request_id] = @info_request.id + params[:outgoing_message] = {} if !params[:outgoing_message] + params[:outgoing_message][:body] = params[:body] if params[:body] + params[:outgoing_message][:default_letter] = params[:default_letter] if params[:default_letter] + params[:outgoing_message][:info_request] = @info_request + @outgoing_message = OutgoingMessage.new(params[:outgoing_message]) + @outgoing_message.set_signature_name(@user.name) if !@user.nil? + + if batch + render :action => 'new' + else + if @info_request.public_body.is_requestable? + render :action => 'new' + else + if @info_request.public_body.not_requestable_reason == 'bad_contact' + render :action => 'new_bad_contact' + else + # if not requestable because defunct or not_apply, redirect to main page + # (which doesn't link to the /new/ URL) + redirect_to public_body_url(@info_request.public_body) + end + end + end + return + end + + def render_new_preview + message = "" + if @outgoing_message.contains_email? + if @user.nil? + message += _("<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"{{url}}\">details</a>).</p>", :url => (help_privacy_path+"#email_address").html_safe); + else + message += _("<p>You do not need to include your email in the request in order to get a reply (<a href=\"{{url}}\">details</a>).</p>", :url => (help_privacy_path+"#email_address").html_safe); + end + message += _("<p>We recommend that you edit your request and remove the email address. + If you leave it, the email address will be sent to the authority, but will not be displayed on the site.</p>") + end + if @outgoing_message.contains_postcode? + message += _("<p>Your request contains a <strong>postcode</strong>. Unless it directly relates to the subject of your request, please remove any address as it will <strong>appear publicly on the Internet</strong>.</p>"); + end + if not message.empty? + flash.now[:error] = message.html_safe + end + render :action => 'preview' + end end diff --git a/app/controllers/track_controller.rb b/app/controllers/track_controller.rb index 1123903f9..83e05ebbc 100644 --- a/app/controllers/track_controller.rb +++ b/app/controllers/track_controller.rb @@ -116,7 +116,7 @@ class TrackController < ApplicationController # Generic request tracker - set @track_thing before calling def track_set if @user - @existing_track = TrackThing.find_by_existing_track(@user, @track_thing) + @existing_track = TrackThing.find_existing(@user, @track_thing) if @existing_track flash[:notice] = _("You are already following updates about {{track_description}}", :track_description => @track_thing.params[:list_description]) return true diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index 175425280..8d6522923 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -26,12 +26,15 @@ class UserController < ApplicationController if params[:view].nil? @show_requests = true @show_profile = true + @show_batches = false elsif params[:view] == 'profile' @show_profile = true @show_requests = false + @show_batches = false elsif params[:view] == 'requests' @show_profile = false @show_requests = true + @show_batches = true end @display_user = User.find(:first, :conditions => [ "url_name = ? and email_confirmed = ?", params[:url_name], true ]) diff --git a/app/mailers/info_request_batch_mailer.rb b/app/mailers/info_request_batch_mailer.rb new file mode 100644 index 000000000..a2becfb24 --- /dev/null +++ b/app/mailers/info_request_batch_mailer.rb @@ -0,0 +1,25 @@ +# models/info_request_batch_mailer.rb: +# Emails relating to user accounts. e.g. Confirming a new account +# +# Copyright (c) 2013 UK Citizens Online Democracy. All rights reserved. +# Email: hello@mysociety.org; WWW: http://www.mysociety.org/ + +class InfoRequestBatchMailer < ApplicationMailer + + def batch_sent(info_request_batch, unrequestable, user) + @info_request_batch, @unrequestable = info_request_batch, unrequestable + headers('Return-Path' => blackhole_email, 'Reply-To' => contact_from_name_and_email) + + # Make a link going to the info request batch page, which logs the user in. + post_redirect = PostRedirect.new( + :uri => info_request_batch_url(@info_request_batch), + :user_id => info_request_batch.user_id) + post_redirect.save! + @url = confirm_url(:email_token => post_redirect.email_token) + + mail(:from => contact_from_name_and_email, + :to => user.name_and_email, + :subject => _("Your batch request \"{{title}}\" has been sent", + :title => info_request_batch.title)) + end +end diff --git a/app/models/comment.rb b/app/models/comment.rb index 75d37e04f..b4c099123 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -62,7 +62,7 @@ class Comment < ActiveRecord::Base end # When posting a new comment, use this to check user hasn't double submitted. - def Comment.find_by_existing_comment(info_request_id, body) + def Comment.find_existing(info_request_id, body) # XXX can add other databases here which have regexp_replace if ActiveRecord::Base.connection.adapter_name == "PostgreSQL" # Exclude spaces from the body comparison using regexp_replace diff --git a/app/models/info_request.rb b/app/models/info_request.rb index def319ad4..47ad435cb 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -1,5 +1,6 @@ # encoding: utf-8 # == Schema Information +# Schema version: 20131024114346 # # Table name: info_requests # @@ -21,6 +22,7 @@ # external_url :string(255) # attention_requested :boolean default(FALSE) # comments_allowed :boolean default(TRUE), not null +# info_request_batch_id :integer # require 'digest/sha1' @@ -40,7 +42,8 @@ class InfoRequest < ActiveRecord::Base validate :must_be_internal_or_external belongs_to :public_body, :counter_cache => true - validates_presence_of :public_body_id + belongs_to :info_request_batch + validates_presence_of :public_body_id, :unless => Proc.new { |info_request| info_request.is_batch_request_template? } has_many :outgoing_messages, :order => 'created_at' has_many :incoming_messages, :order => 'created_at' @@ -50,6 +53,7 @@ class InfoRequest < ActiveRecord::Base has_many :comments, :order => 'created_at' has_many :censor_rules, :order => 'created_at desc' has_many :mail_server_logs, :order => 'mail_server_log_done_id' + attr_accessor :is_batch_request_template has_tag_string @@ -126,6 +130,10 @@ class InfoRequest < ActiveRecord::Base !InfoRequest.enumerate_states.include? described_state end + def is_batch_request_template? + is_batch_request_template == true + end + # The request must either be internal, in which case it has # a foreign key reference to a User object and no external_url or external_user_name, # or else be external in which case it has no user_id but does have an external_url, @@ -383,7 +391,7 @@ public # repeated requests, say once a quarter for time information, then might need to do that. # XXX this *should* also check outgoing message joined to is an initial # request (rather than follow up) - def InfoRequest.find_by_existing_request(title, public_body_id, body) + def InfoRequest.find_existing(title, public_body_id, body) return InfoRequest.find(:first, :conditions => [ "title = ? and public_body_id = ? and outgoing_messages.body = ?", title, public_body_id, body ], :include => [ :outgoing_messages ] ) end @@ -932,6 +940,20 @@ public self.idhash = InfoRequest.hash_from_id(self.id) end + def InfoRequest.create_from_attributes(info_request_atts, outgoing_message_atts, user=nil) + info_request = new(info_request_atts) + default_message_params = { + :status => 'ready', + :message_type => 'initial_request', + :what_doing => 'normal_sort' + } + outgoing_message = OutgoingMessage.new(outgoing_message_atts.merge(default_message_params)) + info_request.outgoing_messages << outgoing_message + outgoing_message.info_request = info_request + info_request.user = user + info_request + end + def InfoRequest.hash_from_id(id) return Digest::SHA1.hexdigest(id.to_s + AlaveteliConfiguration::incoming_email_secret)[0,8] end @@ -1078,7 +1100,10 @@ public # Get the list of censor rules that apply to this request def applicable_censor_rules - applicable_rules = [self.censor_rules, self.public_body.censor_rules, CensorRule.global.all] + applicable_rules = [self.censor_rules, CensorRule.global.all] + unless is_batch_request_template? + applicable_rules << self.public_body.censor_rules + end if self.user && !self.user.censor_rules.empty? applicable_rules << self.user.censor_rules end diff --git a/app/models/info_request_batch.rb b/app/models/info_request_batch.rb new file mode 100644 index 000000000..498ab4951 --- /dev/null +++ b/app/models/info_request_batch.rb @@ -0,0 +1,73 @@ +# == Schema Information +# Schema version: 20131024114346 +# +# Table name: info_request_batches +# +# id :integer not null, primary key +# title :text not null +# user_id :integer not null +# created_at :datetime +# updated_at :datetime +# + +class InfoRequestBatch < ActiveRecord::Base + has_many :info_requests + belongs_to :user + has_and_belongs_to_many :public_bodies + + validates_presence_of :user + validates_presence_of :title + validates_presence_of :body + + # When constructing a new batch, use this to check user hasn't double submitted. + def InfoRequestBatch.find_existing(user, title, body, public_body_ids) + find(:first, :conditions => ['user_id = ? + AND title = ? + AND body = ? + AND info_request_batches_public_bodies.public_body_id in (?)', + user, title, body, public_body_ids], + :include => :public_bodies) + end + + # Create a batch of information requests, returning a list of public bodies + # that are unrequestable from the initial list of public body ids passed. + def create_batch! + unrequestable = [] + created = [] + ActiveRecord::Base.transaction do + public_bodies.each do |public_body| + if public_body.is_requestable? + created << create_request!(public_body) + else + unrequestable << public_body + end + end + self.sent_at = Time.now + self.save! + end + created.each{ |info_request| info_request.outgoing_messages.first.send_message } + + return unrequestable + end + + # Create and send an FOI request to a public body + def create_request!(public_body) + body = OutgoingMessage.fill_in_salutation(self.body, public_body) + info_request = InfoRequest.create_from_attributes({:title => self.title}, + {:body => body}, + self.user) + info_request.public_body_id = public_body.id + info_request.info_request_batch = self + info_request.save! + info_request + end + + def InfoRequestBatch.send_batches() + find_each(:conditions => "sent_at IS NULL") do |info_request_batch| + unrequestable = info_request_batch.create_batch! + mail_message = InfoRequestBatchMailer.batch_sent(info_request_batch, + unrequestable, + info_request_batch.user).deliver + end + end +end diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb index 6efc1d2ba..a435511d3 100644 --- a/app/models/outgoing_message.rb +++ b/app/models/outgoing_message.rb @@ -1,4 +1,5 @@ # == Schema Information +# Schema version: 20131024114346 # # Table name: outgoing_messages # @@ -67,15 +68,30 @@ class OutgoingMessage < ActiveRecord::Base # How the default letter starts and ends def get_salutation + if self.info_request.is_batch_request_template? + return OutgoingMessage.placeholder_salutation + end ret = "" if self.message_type == 'followup' && !self.incoming_message_followup.nil? && !self.incoming_message_followup.safe_mail_from.nil? && self.incoming_message_followup.valid_to_reply_to? ret = ret + OutgoingMailer.name_for_followup(self.info_request, self.incoming_message_followup) else - ret = ret + self.info_request.public_body.name + return OutgoingMessage.default_salutation(self.info_request.public_body) end salutation = _("Dear {{public_body_name}},", :public_body_name => ret) end + def OutgoingMessage.default_salutation(public_body) + _("Dear {{public_body_name}},", :public_body_name => public_body.name) + end + + def OutgoingMessage.placeholder_salutation + _("Dear [Authority name],") + end + + def OutgoingMessage.fill_in_salutation(body, public_body) + body.gsub(placeholder_salutation, default_salutation(public_body)) + end + def get_signoff if self.message_type == 'followup' && !self.incoming_message_followup.nil? && !self.incoming_message_followup.safe_mail_from.nil? && self.incoming_message_followup.valid_to_reply_to? return _("Yours sincerely,") diff --git a/app/models/public_body.rb b/app/models/public_body.rb index a78a6677e..7b1ded820 100644 --- a/app/models/public_body.rb +++ b/app/models/public_body.rb @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # == Schema Information +# Schema version: 20131024114346 # # Table name: public_bodies # diff --git a/app/models/track_thing.rb b/app/models/track_thing.rb index d5e1cdb75..d5dda7bb5 100644 --- a/app/models/track_thing.rb +++ b/app/models/track_thing.rb @@ -311,7 +311,7 @@ class TrackThing < ActiveRecord::Base end # When constructing a new track, use this to avoid duplicates / double posting - def TrackThing.find_by_existing_track(tracking_user, track) + def TrackThing.find_existing(tracking_user, track) if tracking_user.nil? return nil end diff --git a/app/models/user.rb b/app/models/user.rb index 2c4f87944..e63ce8129 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,5 @@ # == Schema Information +# Schema version: 20131024114346 # # Table name: users # @@ -20,6 +21,7 @@ # email_bounce_message :text default(""), not null # no_limit :boolean default(FALSE), not null # receive_email_alerts :boolean default(TRUE), not null +# can_make_batch_requests :boolean default(FALSE), not null # require 'digest/sha1' @@ -40,6 +42,7 @@ class User < ActiveRecord::Base has_many :comments, :order => 'created_at desc' has_one :profile_photo has_many :censor_rules, :order => 'created_at desc' + has_many :info_request_batches, :order => 'created_at desc' attr_accessor :password_confirmation, :no_xapian_reindex validates_confirmation_of :password, :message => _("Please enter the same password twice") @@ -269,6 +272,9 @@ class User < ActiveRecord::Base # Some users have no limit return false if self.no_limit + # Batch request users don't have a limit + return false if self.can_make_batch_requests? + # Has the user issued as many as MAX_REQUESTS_PER_USER_PER_DAY requests in the past 24 hours? return false if AlaveteliConfiguration::max_requests_per_user_per_day.blank? recent_requests = InfoRequest.count(:conditions => ["user_id = ? and created_at > now() - '1 day'::interval", self.id]) diff --git a/app/views/admin_user/_form.html.erb b/app/views/admin_user/_form.html.erb index e7175d09c..f1edc0927 100644 --- a/app/views/admin_user/_form.html.erb +++ b/app/views/admin_user/_form.html.erb @@ -64,3 +64,12 @@ </div> </div> </div> +<div class="control-group"> + <label for="admin_user_can_make_batch_requests" class="control-label">Can make batch requests</label> + <div class="controls"> + <%= check_box 'admin_user', 'can_make_batch_requests' %> + <div class="help-block"> + allows the user to send a request to multiple authorities at once + </div> + </div> +</div> diff --git a/app/views/comment/_comment_form.html.erb b/app/views/comment/_comment_form.html.erb index b78532768..6ca3f4c9f 100644 --- a/app/views/comment/_comment_form.html.erb +++ b/app/views/comment/_comment_form.html.erb @@ -3,7 +3,7 @@ <%= f.text_area :body, :rows => 10, :cols => 55 %> </p> - <% if !TrackThing.find_by_existing_track(@user, track_thing) && (!@user || @info_request.user != @user) %> + <% if !TrackThing.find_existing(@user, track_thing) && (!@user || @info_request.user != @user) %> <p> <%= check_box_tag 'subscribe_to_request', "1", params[:subscribe_to_request] ? true : false %> <label for="subscribe_to_request"><%= _('Email me future updates to this request') %></label> </p> diff --git a/app/views/info_request_batch/_info_request_batch.html.erb b/app/views/info_request_batch/_info_request_batch.html.erb new file mode 100644 index 000000000..86ef90654 --- /dev/null +++ b/app/views/info_request_batch/_info_request_batch.html.erb @@ -0,0 +1,15 @@ +<div class="request_listing"> + <div class="request_left"> + <span class="head"> + <%= link_to highlight_words(info_request_batch.title, @highlight_words), info_request_batch_path(info_request_batch) %> + </span> + <div class="requester"> + <%= _('Batch created by {{info_request_user}} on {{date}}.', :info_request_user => user_link_absolute(info_request_batch.user),:date=>simple_date(info_request_batch.created_at)) %> + </div> + </div> + <div class="request_right"> + <span class="desc"> + <%= excerpt(info_request_batch.body, '', :radius => 150) %> + </span> + </div> +</div> diff --git a/app/views/info_request_batch/show.html.erb b/app/views/info_request_batch/show.html.erb new file mode 100644 index 000000000..aaecdd45d --- /dev/null +++ b/app/views/info_request_batch/show.html.erb @@ -0,0 +1,25 @@ +<% @title = _("{{title}} - a batch request", :title => @info_request_batch.title) %> +<h1><%= @title %></h1> +<% if @info_request_batch.sent_at %> + <%= n_('Sent to one authority by {{info_request_user}} on {{date}}.', 'Sent to {{authority_count}} authorities by {{info_request_user}} on {{date}}.', @info_request_batch.info_requests.size, :authority_count=> @info_request_batch.info_requests.size, :info_request_user => user_link(@info_request_batch.user), :date => simple_date(@info_request_batch.sent_at)) %> + <div class="results_section"> + <div class="results_block"> + <% @info_requests.each do |info_request| %> + <%= render :partial => 'request/request_listing_via_event', :locals => { :event => info_request.last_event_forming_initial_request, :info_request => info_request } %> + <% end %> + </div> + <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @info_request_batch.info_requests.visible.count) %> + </div> + +<% else %> + <%= _('Created by {{info_request_user}} on {{date}}.', :info_request_user => user_link(@info_request_batch.user), :date => simple_date(@info_request_batch.created_at)) %> + <%= _('Requests will be sent to the following bodies:') %> + <div class="results_section"> + <div class="results_block"> + <%= render :partial => 'public_body/body_listing', :locals => { :public_bodies => @public_bodies } %> + </div> + <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @info_request_batch.public_bodies.count) %> + </div> + +<% end %> + diff --git a/app/views/info_request_batch_mailer/batch_sent.text.erb b/app/views/info_request_batch_mailer/batch_sent.text.erb new file mode 100644 index 000000000..25fb7d06a --- /dev/null +++ b/app/views/info_request_batch_mailer/batch_sent.text.erb @@ -0,0 +1,15 @@ +<%= _('Your batch request "{{title}}" has been sent', :title => @info_request_batch.title) %> + + +<%= _('Follow this link to see the requests:')%> + +<%= @url %> + +<% if !@unrequestable.empty? %> +<%= _('Unfortunately, we do not have a working address for {{public_body_names}}.', :public_body_names => @unrequestable.map{|body| body.name}.join(",")) %> +<%= _('You may be able to find one on their website, or by phoning them up and asking. If you manage to find one, then please send it to us:') %> + +<%= help_contact_url %> + +<% end %> +-- <%= _('the {{site_name}} team', :site_name => site_name) %> diff --git a/app/views/request/batch_not_allowed.html.erb b/app/views/request/batch_not_allowed.html.erb new file mode 100644 index 000000000..156fa9ae1 --- /dev/null +++ b/app/views/request/batch_not_allowed.html.erb @@ -0,0 +1 @@ +<%= _('Users cannot usually make batch requests to multiple authorities at once because we don’t want public authorities to be bombarded with large numbers of inappropriate requests. Please <a href="{{url}}">contact us</a> if you think you have good reason to send the same request to multiple authorities at once.', :url => help_contact_path.html_safe) %> diff --git a/app/views/request/new.html.erb b/app/views/request/new.html.erb index 849a94216..7f1332464 100644 --- a/app/views/request/new.html.erb +++ b/app/views/request/new.html.erb @@ -1,23 +1,27 @@ -<script type="text/javascript"> - $(document).ready(function(){ - // Avoid triggering too often (on each keystroke) by using the debounce jQuery plugin: - // http://benalman.com/projects/jquery-throttle-debounce-plugin/ - $("#typeahead_search").keypress($.debounce( 300, function() { - $("#typeahead_response").load("<%=search_ahead_url%>?q="+encodeURI(this.value), function() { - // When following links in typeahead results, open new tab/window - $("#typeahead_response a").attr("target","_blank"); - - // Update the public body site search link - $("#body-site-search-link").attr("href", "http://www.google.com/#q="+encodeURI($("#typeahead_search").val())+ - "+site:<%= @info_request.public_body.calculated_home_page %>"); - }); - })); - - }); -</script> - -<% @title = _("Make an {{law_used_short}} request to '{{public_body_name}}'",:law_used_short=>h(@info_request.law_used_short),:public_body_name=>h(@info_request.public_body.name)) %> - +<% unless @batch %> + <script type="text/javascript"> + $(document).ready(function(){ + // Avoid triggering too often (on each keystroke) by using the debounce jQuery plugin: + // http://benalman.com/projects/jquery-throttle-debounce-plugin/ + $("#typeahead_search").keypress($.debounce( 300, function() { + $("#typeahead_response").load("<%=search_ahead_url%>?q="+encodeURI(this.value), function() { + // When following links in typeahead results, open new tab/window + $("#typeahead_response a").attr("target","_blank"); + + // Update the public body site search link + $("#body-site-search-link").attr("href", "http://www.google.com/#q="+encodeURI($("#typeahead_search").val())+ + "+site:<%= @info_request.public_body.calculated_home_page %>"); + }); + })); + + }); + </script> +<% end %> +<% if @batch %> + <% @title = _("Make an {{law_used_short}} request", :law_used_short=>h(@info_request.law_used_short)) %> +<% else %> + <% @title = _("Make an {{law_used_short}} request to '{{public_body_name}}'",:law_used_short=>h(@info_request.law_used_short),:public_body_name=>h(@info_request.public_body.name)) %> +<% end %> <h1><%= _('2. Ask for Information') %></h1> <% if @existing_request %> @@ -29,53 +33,82 @@ </li> </ul></div> <% end %> + <% if @existing_batch %> + <div class="errorExplanation" id="errorExplanation"><ul> + <li> + <%= _('You already created the same batch of requests on {{date}}. You can either view the <a href="{{existing_batch}}">existing batch</a>, or edit the details below to make a new but similar batch of requests.', :date=>simple_date(@existing_batch.created_at), :existing_batch=>info_request_batch_path(@existing_batch)) %> + </li> + </ul></div> + <% end %> <%= foi_error_messages_for :info_request, :outgoing_message %> - <%= form_for(@info_request, :url => new_request_path, :html => { :id => 'write_form' } ) do |f| %> + <%= form_for(@info_request, :url => (@batch ? new_batch_path : new_request_path), :html => { :id => 'write_form' } ) do |f| %> <div id="request_header"> <div id="request_header_body"> <label class="form_label"><%= _('To:') %></label> - <span id="to_public_body"><%=h(@info_request.public_body.name)%></span> - <div class="form_item_note"> - <% if @info_request.public_body.info_requests.size > 0 %> - <%= _("Browse <a href='{{url}}'>other requests</a> to '{{public_body_name}}' for examples of how to word your request.", :public_body_name=>h(@info_request.public_body.name), :url=>public_body_path(@info_request.public_body)) %> - <% else %> - <%= _("Browse <a href='{{url}}'>other requests</a> for examples of how to word your request.", :url=>request_list_url) %> - <% end %> - </div> - <% 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.html_safe %></p> - </div> + <% if @batch %> + <span id="to_public_body"> + <%= _("Your selected authorities") %> + <span class="batch_public_body_toggle" data-hidetext="<%= _("(hide)") %>" data-showtext="<%= _("(show)") %>"><a class="toggle-message"></a></span> + </span> + + <div class="batch_public_body_list"> + <ul> + <% @public_bodies.each do |public_body| %> + <li><%= public_body.name %></li> + <% end %> + </ul> + </div> + + <% else %> + <span id="to_public_body"> + <%=h(@info_request.public_body.name)%> + </span> <% end %> - <% if @info_request.public_body.eir_only? %> - <h3><%= _('Please ask for environmental information only') %></h3> + <% unless @batch %> + <div class="form_item_note"> + <% if @info_request.public_body.info_requests.size > 0 %> + <%= _("Browse <a href='{{url}}'>other requests</a> to '{{public_body_name}}' for examples of how to word your request.", :public_body_name=>h(@info_request.public_body.name), :url=>public_body_path(@info_request.public_body)) %> + <% else %> + <%= _("Browse <a href='{{url}}'>other requests</a> for examples of how to word your request.", :url=>request_list_url) %> + <% end %> + </div> + + <% 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.html_safe %></p> + </div> + <% end %> + + <% if @info_request.public_body.eir_only? %> + <h3><%= _('Please ask for environmental information only') %></h3> - <p><%= _('The Freedom of Information Act <strong>does not apply</strong> to') %> <%=h(@info_request.public_body.name)%>. - <%= _('However, you have the right to request environmental + <p><%= _('The Freedom of Information Act <strong>does not apply</strong> to') %> <%=h(@info_request.public_body.name)%>. + <%= _('However, you have the right to request environmental information under a different law') %> (<a href="/help/requesting#eir">explanation</a>). - <%= _('This covers a very wide spectrum of information about the state of + <%= _('This covers a very wide spectrum of information about the state of the <strong>natural and built environment</strong>, such as:') %> - <ul> - <li><%= _('Air, water, soil, land, flora and fauna (including how these effect + <ul> + <li><%= _('Air, water, soil, land, flora and fauna (including how these effect human beings)') %></li> - <li><%= _('Information on emissions and discharges (e.g. noise, energy, + <li><%= _('Information on emissions and discharges (e.g. noise, energy, radiation, waste materials)') %></li> - <li><%= _('Human health and safety') %></li> - <li><%= _('Cultural sites and built structures (as they may be affected by the + <li><%= _('Human health and safety') %></li> + <li><%= _('Cultural sites and built structures (as they may be affected by the environmental factors listed above)') %></li> - <li><%= _('Plans and administrative measures that affect these matters') %></li> - </ul> + <li><%= _('Plans and administrative measures that affect these matters') %></li> + </ul> - <p><%= _('Please only request information that comes under those categories, <strong>do not waste your + <p><%= _('Please only request information that comes under those categories, <strong>do not waste your time</strong> or the time of the public authority by requesting unrelated information.') %></p> - <% end %> + <% end %> + <% end %> </div> <div id="request_header_subject"> @@ -132,7 +165,13 @@ </p> <div class="form_button"> - <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %> + <% if @batch %> + <% params[:public_body_ids].each do |public_body_id| %> + <%= hidden_field_tag("public_body_ids[]", public_body_id)%> + <% end %> + <% else %> + <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %> + <% end %> <%= hidden_field_tag(:submitted_new_request, 1 ) %> <%= hidden_field_tag(:preview, 1 ) %> <%= submit_tag _("Preview your public request") %> @@ -150,5 +189,6 @@ </div> <% end %> - - +<% if @batch %> + <%= javascript_include_tag 'new-request.js' %> +<% end %> diff --git a/app/views/request/preview.html.erb b/app/views/request/preview.html.erb index 243dc90a9..0265d0328 100644 --- a/app/views/request/preview.html.erb +++ b/app/views/request/preview.html.erb @@ -1,6 +1,9 @@ -<% @title = "Preview new " + h(@info_request.law_used_short) + " request to '" + h(@info_request.public_body.name) + "'" %> - -<%= form_for(@info_request, :url => new_request_path, :html => { :id => 'preview_form' } ) do |f| %> +<% if @batch %> + <% @title = _("Preview new {{law_used_short}} request", :law_used_short => h(@info_request.law_used_short)) %> +<% else %> + <% @title = _("Preview new {{law_used_short}} request to '{{public_body_name}}", :law_used_short => h(@info_request.law_used_short), :public_body_name => h(@info_request.public_body.name)) %> +<% end %> +<%= form_for(@info_request, :url => (@batch ? new_batch_path : new_request_path), :html => { :id => 'preview_form' } ) do |f| %> <h1><%= _('3. Now check your request') %></h1> <ul> @@ -14,7 +17,12 @@ <div class="correspondence" id="outgoing-0"> <p class="preview_subject"> - <strong><%= _('To:') %></strong> <%=h @info_request.public_body.name %> + <strong><%= _('To:') %></strong> + <% if @batch %> + <%= _("Your selected authorities")%> + <% else %> + <%=h(@info_request.public_body.name)%> + <% end %> <br><strong><%= _('Subject:') %></strong> <%=h @info_request.email_subject_request %> </p> @@ -33,7 +41,13 @@ <p> <%= f.hidden_field(:title) %> - <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %> + <% if @batch %> + <% params[:public_body_ids].each do |public_body_id| %> + <%= hidden_field_tag("public_body_ids[]", public_body_id)%> + <% end %> + <% else %> + <%= f.hidden_field(:public_body_id, { :value => @info_request.public_body_id } ) %> + <% end %> <%= f.hidden_field(:tag_string) %> <%= hidden_field_tag(:submitted_new_request, 1) %> <%= hidden_field_tag(:preview, 0 ) %> diff --git a/app/views/request/select_authorities.html.erb b/app/views/request/select_authorities.html.erb new file mode 100644 index 000000000..e16bcc191 --- /dev/null +++ b/app/views/request/select_authorities.html.erb @@ -0,0 +1,77 @@ +<% @title = _("Select the authorities to write to") %> +<h1><%= _('1. Select authorities') %></h1> + +<p> + <%= _("Search for the authorities you'd like information from:") %> +</p> + +<div> + <%= form_tag(select_authorities_path, {:method => 'get', :id => 'body_search_form', :remote => true, "data-type" => 'json'}) do %> + <%= text_field_tag 'public_body_query', params[:public_body_query], { :size => 30, :title => "type your search term here" } %> + <% if !@public_bodies.blank? %> + <%- @public_bodies.each do |public_body| %> + <%= hidden_field_tag "public_body_ids[]", public_body.id, {:id => nil} %> + <%- end %> + <% end %> + <% end %> +</div> + +<div id="body_selection"> + <div id="body_lists"> + <div id="body_candidates" class="body_list"> + <%= form_tag(select_authorities_path, {:id => "body_select_form"}) do %> + <%= submit_tag _(' >> '), :id => 'body_select_all_button', :class => 'select_all_button' %> + <%= submit_tag _(' > '), :id => 'body_select_button' %> + <%= hidden_field_tag "public_body_query", params[:public_body_query], { :id => 'public_body_select_query' } %> + <% if !@public_bodies.blank? %> + <% @public_bodies.each do |public_body| %> + <%= hidden_field_tag "public_body_ids[]", public_body.id, {:id => nil} %> + <% end %> + <% end %> + <select multiple name="public_body_ids[]" id="select_body_candidates" class="body_select" size="15"> + <% if @search_bodies %> + <% @search_bodies.results.each do |result| %> + <% unless (@public_bodies && @public_bodies.include?(result[:model])) %> + <option value="<%= result[:model].id %>"><%= result[:model].name %></option> + <% end %> + <% end %> + <% end %> + </select> + <% end %> + </div> + + <div id="body_selections" class="body_list"> + <%= form_tag(select_authorities_path, {:id => "body_deselect_form"}) do %> + + <%= submit_tag _(' < '), :id => 'body_deselect_button' %> + <%= submit_tag _(' << '), :id => 'body_deselect_all_button', :class => 'select_all_button' %> + <%= hidden_field_tag "public_body_query", params[:public_body_query], { :id => 'public_body_deselect_query' } %> + <% if @public_bodies %> + <% @public_bodies.each do |public_body| %> + <%= hidden_field_tag "public_body_ids[]", public_body.id, {:id => nil} %> + <% end %> + <% end %> + <select multiple name="remove_public_body_ids[]" id="select_body_selections" class="body_select" size="15"> + <% if @public_bodies %> + <% @public_bodies.each do |public_body| %> + <option value="<%= public_body.id %>"><%= public_body.name %></option> + <% end %> + <% end %> + </select> + <% end %> + + <div id="body_submission"> + <%= form_tag(new_batch_path, {:id => "body_submit_form"}) do %> + <% if @public_bodies %> + <% @public_bodies.each do |public_body| %> + <%= hidden_field_tag "public_body_ids[]", public_body.id , {:id => nil} %> + <% end %> + <% end %> + <%= submit_tag _('Make a request to these authorities'), :id => 'body_submit_button' %> + <% end %> + </div> + + </div> + </div> +</div> +<%= javascript_include_tag 'jquery_ujs.js', 'select-authorities.js' %> diff --git a/app/views/request/select_authority.html.erb b/app/views/request/select_authority.html.erb index 4f117ee75..83abdb184 100644 --- a/app/views/request/select_authority.html.erb +++ b/app/views/request/select_authority.html.erb @@ -40,6 +40,13 @@ <%= hidden_field_tag 'bodies', 1 %> <%= submit_tag _('Search') %> </div> + <% if AlaveteliConfiguration.allow_batch_requests && @user && @user.can_make_batch_requests? %> + <div id="batch_request_link"> + <p> + <%= _('Or make a <a href="{{url}}">batch request</a> to <strong>multiple authorities</strong> at once.', :url => select_authorities_path) %> + </p> + </div> + <% end %> <% end %> <div id="typeahead_response"> diff --git a/app/views/track/_tracking_links.html.erb b/app/views/track/_tracking_links.html.erb index a3cd8fc60..5419ec605 100644 --- a/app/views/track/_tracking_links.html.erb +++ b/app/views/track/_tracking_links.html.erb @@ -1,6 +1,6 @@ <% if @user - existing_track = TrackThing.find_by_existing_track(@user, track_thing) + existing_track = TrackThing.find_existing(@user, track_thing) end %> diff --git a/app/views/user/show.html.erb b/app/views/user/show.html.erb index c9862effe..76ecdeda0 100644 --- a/app/views/user/show.html.erb +++ b/app/views/user/show.html.erb @@ -105,6 +105,17 @@ <div style="clear:both"></div> <% end %> +<% if @show_batches %> + + <% if @is_you && !@display_user.info_request_batches.empty? %> + <h2 class="batch_results" id="batch_requests"> + <%= n_('Your {{count}} batch requests', 'Your {{count}} batch requests', @display_user.info_request_batches.size, :count => @display_user.info_request_batches.size) %> + </h2> + <%= render :partial => 'info_request_batch/info_request_batch', :collection => @display_user.info_request_batches %> + <% end %> + +<% end %> + <% if @show_requests %> <div id="user_profile_search"> <%= form_tag(show_user_url, :method => "get", :id=>"search_form") do %> |