diff options
Diffstat (limited to 'app')
71 files changed, 1499 insertions, 416 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_general_controller.rb b/app/controllers/admin_general_controller.rb index 196616ed6..753208c9a 100644 --- a/app/controllers/admin_general_controller.rb +++ b/app/controllers/admin_general_controller.rb @@ -27,13 +27,16 @@ class AdminGeneralController < AdminController @comment_count = Comment.count # Tasks to do - @requires_admin_requests = InfoRequest.find(:all, :select => '*, ' + InfoRequest.last_event_time_clause + ' as last_event_time', :conditions => ["described_state = 'requires_admin'"], :order => "last_event_time") - @error_message_requests = InfoRequest.find(:all, :select => '*, ' + InfoRequest.last_event_time_clause + ' as last_event_time', :conditions => ["described_state = 'error_message'"], :order => "last_event_time") - @attention_requests = InfoRequest.find(:all, :select => '*, ' + InfoRequest.last_event_time_clause + ' as last_event_time', :conditions => ["described_state = 'attention_requested'"], :order => "last_event_time") - @blank_contacts = PublicBody.find(:all, :conditions => ["request_email = ''"], :order => "updated_at") + @requires_admin_requests = InfoRequest.find_in_state('requires_admin') + @error_message_requests = InfoRequest.find_in_state('error_message') + @attention_requests = InfoRequest.find_in_state('attention_requested') + @blank_contacts = PublicBody.find(:all, :conditions => ["request_email = ''"], + :order => "updated_at") @old_unclassified = InfoRequest.find_old_unclassified(:limit => 20, - :conditions => ["prominence = 'normal'"]) + :conditions => ["prominence = 'normal'"]) @holding_pen_messages = InfoRequest.holding_pen_request.incoming_messages + @new_body_requests = PublicBodyChangeRequest.new_body_requests.open + @body_update_requests = PublicBodyChangeRequest.body_update_requests.open end def timeline diff --git a/app/controllers/admin_public_body_change_requests_controller.rb b/app/controllers/admin_public_body_change_requests_controller.rb new file mode 100644 index 000000000..d76cdc0e5 --- /dev/null +++ b/app/controllers/admin_public_body_change_requests_controller.rb @@ -0,0 +1,15 @@ +class AdminPublicBodyChangeRequestsController < AdminController + + def edit + @change_request = PublicBodyChangeRequest.find(params[:id]) + end + + def update + @change_request = PublicBodyChangeRequest.find(params[:id]) + @change_request.close! + @change_request.send_response(params[:subject], params[:response]) + flash[:notice] = 'The change request has been closed and the user has been notified' + redirect_to admin_general_index_path + end + +end diff --git a/app/controllers/admin_public_body_controller.rb b/app/controllers/admin_public_body_controller.rb index 88e275960..120419a27 100644 --- a/app/controllers/admin_public_body_controller.rb +++ b/app/controllers/admin_public_body_controller.rb @@ -85,14 +85,33 @@ class AdminPublicBodyController < AdminController def new @public_body = PublicBody.new - render + if params[:change_request_id] + @change_request = PublicBodyChangeRequest.find(params[:change_request_id]) + end + if @change_request + @change_request_user_response = render_to_string(:template => "admin_public_body_change_requests/add_accepted", + :formats => [:txt]) + @public_body.name = @change_request.public_body_name + @public_body.request_email = @change_request.public_body_email + @public_body.last_edit_comment = @change_request.comment_for_public_body + end + render :formats => [:html] end def create I18n.with_locale(I18n.default_locale) do + if params[:change_request_id] + @change_request = PublicBodyChangeRequest.find(params[:change_request_id]) + end params[:public_body][:last_edit_editor] = admin_current_user() @public_body = PublicBody.new(params[:public_body]) if @public_body.save + if @change_request + response_text = params[:response].gsub(_("[Authority URL will be inserted here]"), + public_body_url(@public_body, :only_path => false)) + @change_request.close! + @change_request.send_response(params[:subject], response_text) + end flash[:notice] = 'PublicBody was successfully created.' redirect_to admin_body_show_url(@public_body) else @@ -103,15 +122,32 @@ class AdminPublicBodyController < AdminController def edit @public_body = PublicBody.find(params[:id]) - @public_body.last_edit_comment = "" - render + if params[:change_request_id] + @change_request = PublicBodyChangeRequest.find(params[:change_request_id]) + end + if @change_request + @change_request_user_response = render_to_string(:template => "admin_public_body_change_requests/update_accepted", + :formats => [:txt]) + @public_body.request_email = @change_request.public_body_email + @public_body.last_edit_comment = @change_request.comment_for_public_body + else + @public_body.last_edit_comment = "" + end + render :formats => [:html] end def update + if params[:change_request_id] + @change_request = PublicBodyChangeRequest.find(params[:change_request_id]) + end I18n.with_locale(I18n.default_locale) do params[:public_body][:last_edit_editor] = admin_current_user() @public_body = PublicBody.find(params[:id]) if @public_body.update_attributes(params[:public_body]) + if @change_request + @change_request.close! + @change_request.send_response(params[:subject], params[:response]) + end flash[:notice] = 'PublicBody was successfully updated.' redirect_to admin_body_show_url(@public_body) else diff --git a/app/controllers/admin_request_controller.rb b/app/controllers/admin_request_controller.rb index 4d45ced8b..fc291d998 100644 --- a/app/controllers/admin_request_controller.rb +++ b/app/controllers/admin_request_controller.rb @@ -26,15 +26,13 @@ class AdminRequestController < AdminController def show @info_request = InfoRequest.find(params[:id]) - # XXX is this *really* the only way to render a template to a - # variable, rather than to the response? - vars = OpenStruct.new(:name_to => @info_request.user_name, - :name_from => AlaveteliConfiguration::contact_name, - :info_request => @info_request, :reason => params[:reason], - :info_request_url => 'http://' + AlaveteliConfiguration::domain + request_url(@info_request), - :site_name => site_name) - template = File.read(File.join(File.dirname(__FILE__), "..", "views", "admin_request", "hidden_user_explanation.html.erb")) - @request_hidden_user_explanation = ERB.new(template).result(vars.instance_eval { binding }) + vars_for_explanation = {:reason => params[:reason], + :info_request => @info_request, + :name_to => @info_request.user_name, + :name_from => AlaveteliConfiguration::contact_name, + :info_request_url => request_url(@info_request, :only_path => false)} + @request_hidden_user_explanation = render_to_string(:template => "admin_request/hidden_user_explanation", + :locals => vars_for_explanation) end def resend @@ -281,7 +279,8 @@ class AdminRequestController < AdminController if ! info_request.is_external? ContactMailer.from_admin_message( - info_request.user, + info_request.user.name, + info_request.user.email, subject, params[:explanation].strip.html_safe ).deliver 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 161a82b26..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 @@ -428,100 +428,6 @@ class ApplicationController < ActionController::Base end end - def get_request_variety_from_params(params) - query = "" - sortby = "newest" - varieties = [] - if params[:request_variety] && !(query =~ /variety:/) - if params[:request_variety].include? "sent" - varieties -= ['variety:sent', 'variety:followup_sent', 'variety:response', 'variety:comment'] - varieties << ['variety:sent', 'variety:followup_sent'] - end - if params[:request_variety].include? "response" - varieties << ['variety:response'] - end - if params[:request_variety].include? "comment" - varieties << ['variety:comment'] - end - end - if !varieties.empty? - query = " (#{varieties.join(' OR ')})" - end - return query - end - - def get_status_from_params(params) - query = "" - if params[:latest_status] - statuses = [] - if params[:latest_status].class == String - params[:latest_status] = [params[:latest_status]] - end - if params[:latest_status].include?("recent") || params[:latest_status].include?("all") - 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'] - end - if params[:latest_status].include? "unsuccessful" - 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', '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'] - end - if params[:latest_status].include? "other" - statuses << ['latest_status:gone_postal', 'latest_status:error_message', 'latest_status:requires_admin', 'latest_status:user_withdrawn'] - end - if params[:latest_status].include? "gone_postal" - statuses << ['latest_status:gone_postal'] - end - if !statuses.empty? - query = " (#{statuses.join(' OR ')})" - end - end - return query - end - - def get_date_range_from_params(params) - query = "" - if params.has_key?(:request_date_after) && !params.has_key?(:request_date_before) - params[:request_date_before] = Time.now.strftime("%d/%m/%Y") - query += " #{params[:request_date_after]}..#{params[:request_date_before]}" - elsif !params.has_key?(:request_date_after) && params.has_key?(:request_date_before) - params[:request_date_after] = "01/01/2001" - end - if params.has_key?(:request_date_after) - query = " #{params[:request_date_after]}..#{params[:request_date_before]}" - end - return query - end - - def get_tags_from_params(params) - query = "" - tags = [] - if params.has_key?(:tags) - params[:tags].split().each do |tag| - tags << "tag:#{tag}" - end - end - if !tags.empty? - query = " (#{tags.join(' OR ')})" - end - return query - end - - def make_query_from_params(params) - query = params[:query] || "" if query.nil? - query += get_date_range_from_params(params) - query += get_request_variety_from_params(params) - query += get_status_from_params(params) - query += get_tags_from_params(params) - return query - end - def country_from_ip country = "" if !AlaveteliConfiguration::gaze_url.empty? 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/general_controller.rb b/app/controllers/general_controller.rb index b01a67027..6f0d29889 100644 --- a/app/controllers/general_controller.rb +++ b/app/controllers/general_controller.rb @@ -103,7 +103,7 @@ class GeneralController < ApplicationController params[:query] = @query end if @variety_postfix != "all" && @requests - @query, _ = make_query_from_params(params) + @query = InfoRequestEvent.make_query_from_params(params) end @inputted_sortby = @sortby if @sortby.nil? 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/public_body_change_requests_controller.rb b/app/controllers/public_body_change_requests_controller.rb new file mode 100644 index 000000000..4a6c5f5cb --- /dev/null +++ b/app/controllers/public_body_change_requests_controller.rb @@ -0,0 +1,28 @@ +class PublicBodyChangeRequestsController < ApplicationController + + def create + @change_request = PublicBodyChangeRequest.from_params(params[:public_body_change_request], @user) + if @change_request.save + @change_request.send_message + flash[:notice] = @change_request.thanks_notice + redirect_to frontpage_url + return + else + render :action => 'new' + end + end + + def new + @change_request = PublicBodyChangeRequest.new + if params[:body] + @change_request.public_body = PublicBody.find_by_url_name_with_historic(params[:body]) + end + if @change_request.public_body + @title = _('Ask us to update the email address for {{public_body_name}}', + :public_body_name => @change_request.public_body.name) + else + @title = _('Ask us to add an authority') + end + + end +end diff --git a/app/controllers/public_body_controller.rb b/app/controllers/public_body_controller.rb index 308d38e4c..862f4b318 100644 --- a/app/controllers/public_body_controller.rb +++ b/app/controllers/public_body_controller.rb @@ -40,7 +40,7 @@ class PublicBodyController < ApplicationController @searched_to_send_request = true end @view = params[:view] - query = make_query_from_params(params.merge(:latest_status => @view)) + query = InfoRequestEvent.make_query_from_params(params.merge(:latest_status => @view)) query += " requested_from:#{@public_body.url_name}" # Use search query for this so can collapse and paginate easily # XXX really should just use SQL query here rather than Xapian. diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb index d982bd391..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 @@ -141,7 +167,10 @@ class RequestController < ApplicationController def list medium_cache @view = params[:view] + @locale = self.locale_from_params() @page = get_search_page_from_params if !@page # used in cache case, as perform_search sets @page as side effect + @per_page = PER_PAGE + @max_results = MAX_RESULTS if @view == "recent" return redirect_to request_list_all_url(:action => "list", :view => "all", :page => @page), :status => :moved_permanently end @@ -151,16 +180,11 @@ class RequestController < ApplicationController raise ActiveRecord::RecordNotFound.new("Sorry. No pages after #{MAX_RESULTS / PER_PAGE}.") end - query = make_query_from_params(params.merge(:latest_status => @view)) + @filters = params.merge(:latest_status => @view) @title = _("View and search requests") - sortby = "newest" - 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 @title = @title + " (page " + @page.to_s + ")" if (@page > 1) - @track_thing = TrackThing.create_track_for_search_query(query) + @track_thing = TrackThing.create_track_for_search_query(InfoRequestEvent.make_query_from_params(@filters)) @feed_autodetect = [ { :url => do_track_url(@track_thing, 'feed'), :title => @track_thing.params[:title_in_rss], :has_json => true } ] # Don't let robots go more than 20 pages in @@ -169,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 @@ -213,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? @@ -298,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 @@ -326,7 +344,7 @@ class RequestController < ApplicationController if !authenticated?( :web => _("To send your FOI request").to_str, :email => _("Then your FOI request to {{public_body_name}} will be sent.",:public_body_name=>@info_request.public_body.name), - :email_subject => _("Confirm your FOI request to ") + @info_request.public_body.name + :email_subject => _("Confirm your FOI request to {{public_body_name}}",:public_body_name=>@info_request.public_body.name) ) # do nothing - as "authenticated?" has done the redirect to signin page for us return @@ -671,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? @@ -885,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 @@ -969,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/helpers/application_helper.rb b/app/helpers/application_helper.rb index 4b603b064..154697377 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -123,5 +123,18 @@ module ApplicationHelper yield end end + + # We only want to cache request lists that have a reasonable chance of not expiring + # before they're requested again. Don't cache lists returned from specific searches + # or anything except the first page of results, just the first page of the default + # views + def request_list_cache_key + cacheable_param_list = ['controller', 'action', 'locale', 'view'] + if params.keys.all?{ |key| cacheable_param_list.include?(key) } + "request-list-#{@view}-#{@locale}" + else + nil + end + end end diff --git a/app/helpers/link_to_helper.rb b/app/helpers/link_to_helper.rb index 8df28f350..405886a85 100755 --- a/app/helpers/link_to_helper.rb +++ b/app/helpers/link_to_helper.rb @@ -121,24 +121,37 @@ module LinkToHelper end def user_link_absolute(user) - link_to h(user.name), user_url(user) + link_to user.name, user_url(user) end def user_link(user) - link_to h(user.name), user_path(user) + link_to user.name, user_path(user) end - def request_user_link_absolute(request) + def external_user_link(request, absolute, text) + if request.external_user_name + request.external_user_name + else + if absolute + url = help_privacy_url(:anchor => 'anonymous') + else + url = help_privacy_path(:anchor => 'anonymous') + end + link_to(text, url) + end + end + + def request_user_link_absolute(request, anonymous_text=_("Anonymous user")) if request.is_external? - request.external_user_name || _("Anonymous user") + external_user_link(request, absolute=true, anonymous_text) else user_link_absolute(request.user) end end - def request_user_link(request) + def request_user_link(request, anonymous_text=_("Anonymous user")) if request.is_external? - request.external_user_name || _("Anonymous user") + external_user_link(request, absolute=false, anonymous_text) else user_link(request.user) end diff --git a/app/mailers/contact_mailer.rb b/app/mailers/contact_mailer.rb index 4dc49bf8b..27e04ca4b 100644 --- a/app/mailers/contact_mailer.rb +++ b/app/mailers/contact_mailer.rb @@ -34,12 +34,31 @@ class ContactMailer < ApplicationMailer end # Send message to a user from the administrator - def from_admin_message(recipient_user, subject, message) - @message, @from_user, @recipient_user = message, contact_from_name_and_email, recipient_user - + def from_admin_message(recipient_name, recipient_email, subject, message) + @message, @from_user = message, contact_from_name_and_email + @recipient_name, @recipient_email = recipient_name, recipient_email mail(:from => contact_from_name_and_email, - :to => recipient_user.name_and_email, + :to => MailHandler.address_from_name_and_email(@recipient_name, @recipient_email), :bcc => AlaveteliConfiguration::contact_email, :subject => subject) end + + # Send a request to the administrator to add an authority + def add_public_body(change_request) + @change_request = change_request + mail(:from => MailHandler.address_from_name_and_email(@change_request.get_user_name, @change_request.get_user_email), + :to => contact_from_name_and_email, + :subject => _('Add authority - {{public_body_name}}', + :public_body_name => @change_request.get_public_body_name)) + end + + # Send a request to the administrator to update an authority email address + def update_public_body_email(change_request) + @change_request = change_request + mail(:from => MailHandler.address_from_name_and_email(@change_request.get_user_name, @change_request.get_user_email), + :to => contact_from_name_and_email, + :subject => _('Update email address - {{public_body_name}}', + :public_body_name => @change_request.get_public_body_name)) + end + end 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/incoming_message.rb b/app/models/incoming_message.rb index bcf0b6ec9..6db145348 100644 --- a/app/models/incoming_message.rb +++ b/app/models/incoming_message.rb @@ -35,7 +35,7 @@ require 'htmlentities' require 'rexml/document' require 'zip/zip' -require 'iconv' unless RUBY_VERSION >= '1.9' +require 'iconv' unless String.method_defined?(:encode) class IncomingMessage < ActiveRecord::Base extend MessageProminence @@ -294,7 +294,7 @@ 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 - if RUBY_VERSION >= '1.9' + if String.method_defined?(:encode) emails.map! do |email| # We want the ASCII representation of UCS-2 [email[0].encode('UTF-16LE').force_encoding('US-ASCII'), @@ -385,6 +385,10 @@ class IncomingMessage < ActiveRecord::Base multiline_original_message = '(' + '''>>>.* \d\d/\d\d/\d\d\d\d\s+\d\d:\d\d(?::\d\d)?\s*>>>''' + ')' text.gsub!(/^(#{multiline_original_message}\n.*)$/m, replacement) + # On Thu, Nov 28, 2013 at 9:08 AM, A User + # <[1]request-7-skm40s2ls@xxx.xxxx> wrote: + text.gsub!(/^( On [^\n]+\n\s*\<[^>\n]+\> (wrote|said):\s*\n.*)$/m, replacement) + # Single line sections text.gsub!(/^(>.*\n)/, replacement) text.gsub!(/^(On .+ (wrote|said):\n)/, replacement) @@ -516,7 +520,7 @@ class IncomingMessage < ActiveRecord::Base # should instead tell elinks to respect the source # charset use_charset = "utf-8" - if RUBY_VERSION.to_f >= 1.9 + if String.method_defined?(:encode) begin text.encode('utf-8') rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError @@ -547,7 +551,7 @@ class IncomingMessage < ActiveRecord::Base end def _sanitize_text(text) - if RUBY_VERSION.to_f >= 1.9 + if String.method_defined?(:encode) begin # Test if it's good UTF-8 text.encode('utf-8') @@ -792,27 +796,28 @@ class IncomingMessage < ActiveRecord::Base return self.cached_attachment_text_clipped end - def _get_attachment_text_internal + def _extract_text # Extract text from each attachment - text = '' - attachments = self.get_attachments_for_display - for attachment in attachments - text += MailHandler.get_attachment_text_one_file(attachment.content_type, + self.get_attachments_for_display.reduce(''){ |memo, attachment| + memo += MailHandler.get_attachment_text_one_file(attachment.content_type, attachment.body, attachment.charset) - end + } + end + + def _get_attachment_text_internal + text = self._extract_text # Remove any bad characters - if RUBY_VERSION >= '1.9' - text.encode("utf-8", :invalid => :replace, - :undef => :replace, - :replace => "") + if String.method_defined?(:encode) + # handle "problematic" encoding + text.encode!('UTF-16', 'UTF-8', :invalid => :replace, :undef => :replace, :replace => '') + text.encode('UTF-8', 'UTF-16') else Iconv.conv('utf-8//IGNORE', 'utf-8', text) end end - # Returns text for indexing def get_text_for_indexing_full return get_body_for_quoting + "\n\n" + get_attachment_text_full diff --git a/app/models/info_request.rb b/app/models/info_request.rb index 0a073dc79..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 @@ -113,8 +117,12 @@ class InfoRequest < ActiveRecord::Base # Possible reasons that a request could be reported for administrator attention def report_reasons - ["Contains defamatory material", "Not a valid request", "Request for personal information", - "Contains personal information", "Vexatious", "Other"] + [_("Contains defamatory material"), + _("Not a valid request"), + _("Request for personal information"), + _("Contains personal information"), + _("Vexatious"), + _("Other")] end def must_be_valid_state @@ -122,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, @@ -379,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 @@ -928,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 @@ -1074,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 @@ -1228,6 +1257,23 @@ public return [xapian_similar, xapian_similar_more] end + def InfoRequest.request_list(filters, page, per_page, max_results) + xapian_object = ActsAsXapian::Search.new([InfoRequestEvent], + InfoRequestEvent.make_query_from_params(filters), + :offset => (page - 1) * per_page, + :limit => 25, + :sort_by_prefix => 'created_at', + :sort_by_ascending => true, + :collapse_by_prefix => '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].min + return { :results => list_results, + :matches_estimated => matches_estimated, + :show_no_more_than => show_no_more_than } + end + def InfoRequest.recent_requests request_events = [] request_events_all_successful = false @@ -1275,6 +1321,12 @@ public return [request_events, request_events_all_successful] end + def InfoRequest.find_in_state(state) + find(:all, :select => '*, ' + last_event_time_clause + ' as last_event_time', + :conditions => ["described_state = ?", state], + :order => "last_event_time") + end + private def set_defaults 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/info_request_event.rb b/app/models/info_request_event.rb index e268b28ca..5eed5ba83 100644 --- a/app/models/info_request_event.rb +++ b/app/models/info_request_event.rb @@ -21,6 +21,9 @@ # Email: hello@mysociety.org; WWW: http://www.mysociety.org/ class InfoRequestEvent < ActiveRecord::Base + + extend XapianQueries + belongs_to :info_request validates_presence_of :info_request @@ -416,4 +419,5 @@ class InfoRequestEvent < ActiveRecord::Base yield(column.human_name, self.send(column.name), column.type.to_s, column.name) 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/profile_photo.rb b/app/models/profile_photo.rb index 322ebe53c..6c3b2cfa0 100644 --- a/app/models/profile_photo.rb +++ b/app/models/profile_photo.rb @@ -70,21 +70,25 @@ class ProfilePhoto < ActiveRecord::Base def data_and_draft_checks if self.data.nil? - errors.add(:data, N_("Please choose a file containing your photo.")) + errors.add(:data, _("Please choose a file containing your photo.")) return end if self.image.nil? - errors.add(:data, N_("Couldn't understand the image file that you uploaded. PNG, JPEG, GIF and many other common image file formats are supported.")) + errors.add(:data, _("Couldn't understand the image file that you uploaded. PNG, JPEG, GIF and many other common image file formats are supported.")) return end if self.image.format != 'PNG' - errors.add(:data, N_("Failed to convert image to a PNG")) + errors.add(:data, _("Failed to convert image to a PNG")) end if !self.draft && (self.image.columns != WIDTH || self.image.rows != HEIGHT) - errors.add(:data, N_("Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}", :cols => self.image.columns, :rows => self.image.rows, :width => WIDTH, :height => HEIGHT)) + errors.add(:data, _("Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}", + :cols => self.image.columns, + :rows => self.image.rows, + :width => WIDTH, + :height => HEIGHT)) end if self.draft && self.user_id diff --git a/app/models/public_body.rb b/app/models/public_body.rb index 933825d2a..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 # @@ -346,22 +347,26 @@ class PublicBody < ActiveRecord::Base # The "internal admin" is a special body for internal use. def PublicBody.internal_admin_body - I18n.with_locale(I18n.default_locale) do - pb = PublicBody.find_by_url_name("internal_admin_authority") - if pb.nil? - pb = PublicBody.new( - :name => 'Internal admin authority', - :short_name => "", - :request_email => AlaveteliConfiguration::contact_email, - :home_page => "", - :notes => "", - :publication_scheme => "", - :last_edit_editor => "internal_admin", - :last_edit_comment => "Made by PublicBody.internal_admin_body" - ) - pb.save! + # Use find_by_sql to avoid the search being specific to a + # locale, since url_name is a translated field: + sql = "SELECT * FROM public_bodies WHERE url_name = 'internal_admin_authority'" + matching_pbs = PublicBody.find_by_sql sql + case + when matching_pbs.empty? then + I18n.with_locale(I18n.default_locale) do + PublicBody.create!(:name => 'Internal admin authority', + :short_name => "", + :request_email => AlaveteliConfiguration::contact_email, + :home_page => "", + :notes => "", + :publication_scheme => "", + :last_edit_editor => "internal_admin", + :last_edit_comment => "Made by PublicBody.internal_admin_body") end - return pb + when matching_pbs.length == 1 then + matching_pbs[0] + else + raise "Multiple public bodies (#{matching_pbs.length}) found with url_name 'internal_admin_authority'" end end diff --git a/app/models/public_body_change_request.rb b/app/models/public_body_change_request.rb new file mode 100644 index 000000000..0e59cbecc --- /dev/null +++ b/app/models/public_body_change_request.rb @@ -0,0 +1,131 @@ +# == Schema Information +# +# Table name: public_body_change_requests +# +# id :integer not null, primary key +# user_email :string(255) +# user_name :string(255) +# user_id :integer +# public_body_name :text +# public_body_id :integer +# public_body_email :string(255) +# source_url :text +# notes :text +# is_open :boolean default(TRUE), not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class PublicBodyChangeRequest < ActiveRecord::Base + + belongs_to :user + belongs_to :public_body + validates_presence_of :public_body_name, :message => N_("Please enter the name of the authority"), + :unless => proc{ |change_request| change_request.public_body } + validates_presence_of :user_name, :message => N_("Please enter your name"), + :unless => proc{ |change_request| change_request.user } + validates_presence_of :user_email, :message => N_("Please enter your email address"), + :unless => proc{ |change_request| change_request.user } + validate :user_email_format, :unless => proc{ |change_request| change_request.user_email.blank? } + validate :body_email_format, :unless => proc{ |change_request| change_request.public_body_email.blank? } + + scope :new_body_requests, :conditions => ['public_body_id IS NULL'], :order => 'created_at' + scope :body_update_requests, :conditions => ['public_body_id IS NOT NULL'], :order => 'created_at' + scope :open, :conditions => ['is_open = ?', true] + + def self.from_params(params, user) + change_request = new + change_request.update_from_params(params, user) + end + + def update_from_params(params, user) + if user + self.user_id = user.id + else + self.user_name = params[:user_name] + self.user_email = params[:user_email] + end + self.public_body_name = params[:public_body_name] + self.public_body_id = params[:public_body_id] + self.public_body_email = params[:public_body_email] + self.source_url = params[:source_url] + self.notes = params[:notes] + self + end + + def get_user_name + user ? user.name : user_name + end + + def get_user_email + user ? user.email : user_email + end + + def get_public_body_name + public_body ? public_body.name : public_body_name + end + + def send_message + if public_body + ContactMailer.update_public_body_email(self).deliver + else + ContactMailer.add_public_body(self).deliver + end + end + + def thanks_notice + if self.public_body + _("Your request to update the address for {{public_body_name}} has been sent. Thank you for getting in touch! We'll get back to you soon.", + :public_body_name => get_public_body_name) + else + _("Your request to add an authority has been sent. Thank you for getting in touch! We'll get back to you soon.") + end + end + + def send_response(subject, response) + ContactMailer.from_admin_message(get_user_name, + get_user_email, + subject, + response.strip.html_safe).deliver + end + + def comment_for_public_body + comments = ["Requested by: #{get_user_name} (#{get_user_email})"] + if !source_url.blank? + comments << "Source URL: #{source_url}" + end + if !notes.blank? + comments << "Notes: #{notes}" + end + comments.join("\n") + end + + def default_response_subject + if self.public_body + _("Your request to update {{public_body_name}} on {{site_name}}", :site_name => AlaveteliConfiguration::site_name, + :public_body_name => public_body.name) + else + _("Your request to add {{public_body_name}} to {{site_name}}", :site_name => AlaveteliConfiguration::site_name, + :public_body_name => public_body_name) + end + end + + def close! + self.is_open = false + self.save! + end + + private + + def body_email_format + unless MySociety::Validate.is_valid_email(self.public_body_email) + errors.add(:public_body_email, _("The authority email doesn't look like a valid address")) + end + end + + def user_email_format + unless MySociety::Validate.is_valid_email(self.user_email) + errors.add(:user_email, _("Your email doesn't look like a valid address")) + end + end +end 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_general/_change_request_summary.html.erb b/app/views/admin_general/_change_request_summary.html.erb new file mode 100644 index 000000000..bec49c12c --- /dev/null +++ b/app/views/admin_general/_change_request_summary.html.erb @@ -0,0 +1,60 @@ +<table class="table table-striped table-condensed"> + <tbody> + <tr> + <td> + <b>Authority</b> + </td> + <td> + <%= @change_request.get_public_body_name %> + </td> + </tr> + + <% if @change_request.public_body %> + <tr> + <td> + <b>Current address</b> + </td> + <td> + <%= @change_request.public_body.request_email %> + </td> + </tr> + <% end %> + + <tr> + <td> + <b>Suggested address</b> + </td> + <td> + <%= @change_request.public_body_email %> + </td> + </tr> + + <tr> + <td> + <b>Source</b> + </td> + <td> + <%= @change_request.source_url %> + </td> + </tr> + + <tr> + <td> + <b>Requested by</b> + </td> + <td> + <%= @change_request.get_user_name %> (<%= @change_request.get_user_email %>) + </td> + </tr> + + <tr> + <td> + <b>Requested on</b> + </td> + <td> + <%= I18n.l(@change_request.created_at, :format => "%e %B %Y %H:%M:%S") %> + (<%= "#{time_ago_in_words(@change_request.created_at)} ago" %>) + </td> + </tr> + </tbody> +</table> diff --git a/app/views/admin_general/index.html.erb b/app/views/admin_general/index.html.erb index 976860fa7..2202663be 100644 --- a/app/views/admin_general/index.html.erb +++ b/app/views/admin_general/index.html.erb @@ -174,9 +174,39 @@ </div> </div> <% end %> + + <% if @new_body_requests.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#new-authorities" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%= @new_body_requests.size %></span><%= chevron_right %> Add new authorities</a> + </div> + <div id="new-authorities" class="accordion-body collapse"> + <% for @change_request in @new_body_requests %> + <%= render :partial => 'change_request_summary'%> + <%= link_to("Close and respond", admin_change_request_edit_path(@change_request), :class => 'btn') %> + <%= link_to("Add authority", admin_body_new_path(:change_request_id => @change_request.id), :class => 'btn btn-primary') %> + <% end %> + </div> + </div> + <% end %> + + <% if @body_update_requests.size > 0 %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a class="accordion-toggle" href="#update-authorities" data-toggle="collapse" data-parent="things-to-do"><span class="label label-important"><%= @body_update_requests.size %></span><%= chevron_right %> Update authorities</a> + </div> + <div id="update-authorities" class="accordion-body collapse"> + <% for @change_request in @body_update_requests %> + <%= render :partial => 'change_request_summary' %> + <%= link_to("Close and respond", admin_change_request_edit_path(@change_request), :class => 'btn') %> + <%= link_to("Make update", admin_body_edit_path(@change_request.public_body, :change_request_id => @change_request.id), :class => 'btn btn-primary') %> + <% end %> + </div> + </div> + <% end %> </div> -<% if @holding_pen_messages.size == 0 && @old_unclassified.size == 0 && @requires_admin_requests.size == 0 && @blank_contacts.size == 0 && @attention_requests.size == 0 %> +<% if @holding_pen_messages.size == 0 && @old_unclassified.size == 0 && @requires_admin_requests.size == 0 && @blank_contacts.size == 0 && @attention_requests.size == 0 && @new_body_requests.size == 0 && @body_update_requests.size == 0 %> <div class="row"> <div class="span12 alert alert-success"> No pending administration required. diff --git a/app/views/admin_public_body/_form.html.erb b/app/views/admin_public_body/_form.html.erb index 5a80386ec..2da13ab01 100644 --- a/app/views/admin_public_body/_form.html.erb +++ b/app/views/admin_public_body/_form.html.erb @@ -95,4 +95,8 @@ <p class="help-block">put URL or other source of new info</p> </div> </div> +<% if @change_request %> + <%= render :partial => 'admin_public_body_change_requests/response' %> + +<% end %> <!--[eoform:public_body]--> diff --git a/app/views/admin_public_body/new.html.erb b/app/views/admin_public_body/new.html.erb index 13e8238d6..24b27d7af 100644 --- a/app/views/admin_public_body/new.html.erb +++ b/app/views/admin_public_body/new.html.erb @@ -6,13 +6,15 @@ <div id="public_body_form"> <%= form_for @public_body, :as => :public_body, :url => admin_body_create_path, :html => {:class => "form form-horizontal"} do |f| %> <%= render :partial => 'form', :locals => {:f => f} %> + + <div class="form-actions"> <%= f.submit "Create", :class => "btn btn-primary" %> </div> <% end %> <div class="row"> <div class="span8 well"> - <%= link_to _('List all'), 'list' %> + <%= link_to 'List all', admin_body_list_path, :class => "btn" %> </div> </div> </div> diff --git a/app/views/admin_public_body/show.html.erb b/app/views/admin_public_body/show.html.erb index 8262287d5..f8161db26 100644 --- a/app/views/admin_public_body/show.html.erb +++ b/app/views/admin_public_body/show.html.erb @@ -67,7 +67,10 @@ </div> <% if i == versions.length - 1 %> <div class="span6"> - <%=_("This is the first version.")%> + <p>“<%= h(historic_public_body.last_edit_comment) %>”</p> + <ul> + <li><%=_("This is the first version.")%></li> + </ul> </div> <% else %> <div class="span6"> diff --git a/app/views/admin_public_body_change_requests/_response.html.erb b/app/views/admin_public_body_change_requests/_response.html.erb new file mode 100644 index 000000000..7fda8b7f8 --- /dev/null +++ b/app/views/admin_public_body_change_requests/_response.html.erb @@ -0,0 +1,15 @@ +<h3>Response to change request (will be emailed to user):</h3> +<%= hidden_field_tag 'change_request_id', @change_request.id %> +<div class="control-group" id="change_request_user_subject"> + <label for="change_request_user_subject_field" class="control-label">Subject of email:</label> + <div class="controls"> + <%= text_field_tag "subject", (params[:subject] || @change_request.default_response_subject), {:id => "change_request_user_subject_field", :class => "span6"} %> + </div> +</div> + +<div class="control-group" id="change_request_user_response"> + <label for="change_request_user_response_field" class="control-label">Response</label> + <div class="controls"> + <%= text_area_tag "response", (params[:response] || h(@change_request_user_response)), {:id => "change_request_user_response_field", :rows => 10, :class => 'span6'} %> + </div> +</div> diff --git a/app/views/admin_public_body_change_requests/add_accepted.txt.erb b/app/views/admin_public_body_change_requests/add_accepted.txt.erb new file mode 100644 index 000000000..fb22466b0 --- /dev/null +++ b/app/views/admin_public_body_change_requests/add_accepted.txt.erb @@ -0,0 +1,9 @@ +<%= _("Dear {{user_name}},", :user_name => @change_request.get_user_name) %> + +<%= _("Thanks for your suggestion to add {{public_body_name}}. It's been added to the site here:", :public_body_name => @change_request.public_body_name) %> + +<%= _("[Authority URL will be inserted here]")%> + +<%= _("Yours,") %> + +<%= _("The {{site_name}} team.", :site_name => site_name) %> diff --git a/app/views/admin_public_body_change_requests/edit.html.erb b/app/views/admin_public_body_change_requests/edit.html.erb new file mode 100644 index 000000000..cc9c5b5d9 --- /dev/null +++ b/app/views/admin_public_body_change_requests/edit.html.erb @@ -0,0 +1,8 @@ +<h1><%=@title%></h1> + +<%= form_tag admin_change_request_update_path(@change_request), :class => "form form-horizontal" do %> + <%= render :partial => 'admin_public_body_change_requests/response'%> + <div class="form-actions"> + <%= submit_tag 'Close', :accesskey => 'c', :class => "btn btn-primary" %> + </div> +<% end %> diff --git a/app/views/admin_public_body_change_requests/update_accepted.txt.erb b/app/views/admin_public_body_change_requests/update_accepted.txt.erb new file mode 100644 index 000000000..9c29c959b --- /dev/null +++ b/app/views/admin_public_body_change_requests/update_accepted.txt.erb @@ -0,0 +1,7 @@ +<%= _("Dear {{user_name}},", :user_name => @change_request.get_user_name) %> + +<%= _("Thanks for your suggestion to update the email address for {{public_body_name}} to {{public_body_email}}. This has now been done and any new requests will be sent to the new address.", :public_body_name => @change_request.public_body.name, :public_body_email => @change_request.public_body_email) %> + +<%= _("Yours,") %> + +<%= _("The {{site_name}} team.", :site_name => site_name) %> diff --git a/app/views/admin_request/_some_annotations.html.erb b/app/views/admin_request/_some_annotations.html.erb new file mode 100644 index 000000000..dfd46f828 --- /dev/null +++ b/app/views/admin_request/_some_annotations.html.erb @@ -0,0 +1,48 @@ +<% if comments.size > 0 %> + <div class="accordion" id="comments"> + <% for comment in comments %> + <div class="accordion-group"> + <div class="accordion-heading"> + <a href="#comment_<%=comment.id%>" data-toggle="collapse" data-parent="#comments"><%= chevron_right %></a> + <%= link_to admin_request_edit_comment_path(comment) do %> + #<%=comment.id%> + -- + <%=h(comment.user.name)%> + <%=admin_value(comment.created_at)%> + <% end %> + <blockquote class="incoming-message"> + <%= truncate(comment.body, :length => 400) %> + </blockquote> + </div> + <div id="comment_<%=comment.id%>" class="accordion-body collapse"> + <table class="table table-striped table-condensed"> + <tbody> + <tr> + <td colspan="2"> + By <%= user_both_links(comment.user) %> + </td> + </tr> + <% comment.for_admin_column do |name, value, type, column_name |%> + <tr> + <td> + <b><%=name%></b> + </td> + <td> + <% if column_name == 'body' && !comment.visible %> + <s><%=h comment.send(column_name) %></s> + <% else %> + <%=h comment.send(column_name) %> + <% end %> + </td> + </tr> + <% end %> + </tbody> + </table> + </div> + </div> + <% end %> + </div> +<% else %> + <p>None yet.</p> +<% end %> + diff --git a/app/views/admin_request/show.html.erb b/app/views/admin_request/show.html.erb index 83d9c3764..2589e52b4 100644 --- a/app/views/admin_request/show.html.erb +++ b/app/views/admin_request/show.html.erb @@ -325,53 +325,8 @@ <hr> <h2>Annotations</h2> -<% if @info_request.comments.size > 0 %> - <div class="accordion" id="comments"> - <% for comment in @info_request.comments %> - <div class="accordion-group"> - <div class="accordion-heading"> - <a href="#comment_<%=comment.id%>" data-toggle="collapse" data-parent="#comments"><%= chevron_right %></a> - <%= link_to admin_request_edit_comment_path(comment) do %> - #<%=comment.id%> - -- - <%=h(comment.user.name)%> - <%=admin_value(comment.created_at)%> - <% end %> - <blockquote class="incoming-message"> - <%= truncate(comment.body, :length => 400) %> - </blockquote> - </div> - <div id="comment_<%=comment.id%>" class="accordion-body collapse"> - <table class="table table-striped table-condensed"> - <tbody> - <tr> - <td colspan="2"> - By <%= user_both_links(comment.user) %> - </td> - </tr> - <% comment.for_admin_column do |name, value, type, column_name |%> - <tr> - <td> - <b><%=name%></b> - </td> - <td> - <% if column_name == 'body' && !comment.visible %> - <s><%=h comment.send(column_name) %></s> - <% else %> - <%=h comment.send(column_name) %> - <% end %> - </td> - </tr> - <% end %> - </tbody> - </table> - </div> - </div> - <% end %> - </div> -<% else %> - <p>None yet.</p> -<% end %> +<%= render :partial => 'admin_request/some_annotations' , :locals => { :comments => @info_request.comments } %> + <hr> <h2>Mail server delivery logs</h2> 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/admin_user/show.html.erb b/app/views/admin_user/show.html.erb index c93c08e50..6d12aeff5 100644 --- a/app/views/admin_user/show.html.erb +++ b/app/views/admin_user/show.html.erb @@ -91,6 +91,12 @@ <hr> +<h2>Annotations</h2> + +<%= render :partial => 'admin_request/some_annotations' , :locals => { :comments => @admin_user.comments } %> + +<hr> + <h2>Censor rules</h2> <%= render :partial => 'admin_censor_rule/show', :locals => { :censor_rules => @admin_user.censor_rules, :user => @admin_user } %> 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/contact_mailer/add_public_body.text.erb b/app/views/contact_mailer/add_public_body.text.erb new file mode 100644 index 000000000..5baa1fa1a --- /dev/null +++ b/app/views/contact_mailer/add_public_body.text.erb @@ -0,0 +1,19 @@ +<%= _("{{user_name}} would like a new authority added to {{site_name}}", :user_name => @change_request.get_user_name, :site_name => site_name) %> + +<%= _("Authority:") %> +<%= @change_request.get_public_body_name %> + +<%= _("Email:") %> +<%= @change_request.public_body_email %> + +<%= _("Source:") %> +<%= @change_request.source_url %> + +<%= _("Notes:") %> +<%= @change_request.notes %> + +<%= _('Add the authority:') %> +<%= admin_body_new_url(:change_request_id => @change_request.id, :only_path => false ) %> + +<%= _('Close the request and respond:') %> +<%= admin_change_request_edit_url(:id => @change_request.id, :only_path => false ) %> diff --git a/app/views/contact_mailer/update_public_body_email.text.erb b/app/views/contact_mailer/update_public_body_email.text.erb new file mode 100644 index 000000000..7d5a3dae0 --- /dev/null +++ b/app/views/contact_mailer/update_public_body_email.text.erb @@ -0,0 +1,16 @@ +<%= _("{{user_name}} would like the email address for {{public_body_name}} to be updated", :user_name => @change_request.get_user_name, :public_body_name => @change_request.get_public_body_name) %> + +<%= _("Email:") %> +<%= @change_request.public_body_email %> + +<%= _("Source:") %> +<%= @change_request.source_url %> + +<%= _("Notes:") %> +<%= @change_request.notes %> + +<%= _('Update the address:') %> +<%= admin_body_edit_path(@change_request.public_body, :change_request_id => @change_request.id, :only_path => false) %> + +<%= _('Close the request and respond:') %> +<%= admin_change_request_edit_url(:id => @change_request.id, :only_path => false ) %> diff --git a/app/views/help/api.html.erb b/app/views/help/api.html.erb index df7bb30b6..c6488f93e 100644 --- a/app/views/help/api.html.erb +++ b/app/views/help/api.html.erb @@ -71,7 +71,15 @@ </p> </dd> - </dl> + + + <dt> 5. Write API </dt> + <dd> + <p> + The write API is designed to be used by authorities to create their own requests in the system. The API is currently used by mySociety's <a href="https://github.com/mysociety/foi-register">FOI Register software</a> to support using Alaveteli as a disclosure log for all FOI activity at a particular public body. More technical information about the write API is available on the <a href="https://github.com/mysociety/alaveteli/wiki/API#write-api">Alaveteli wiki</a>. + </p> + </dd> +</dl> <p>Please <a href="<%= help_contact_path %>">contact us</a> if you need an API feature that isn't there yet. It's very much a work in progress, and we do add things when people ask us to.</p> diff --git a/app/views/help/privacy.html.erb b/app/views/help/privacy.html.erb index 3e26907fd..f0e82ab13 100644 --- a/app/views/help/privacy.html.erb +++ b/app/views/help/privacy.html.erb @@ -84,6 +84,11 @@ ask a friend to. We don't have the resources to do this for everyone. </dd> +<dt id="anonymous">Why are there anonymous requests on the site? <a href="#anonymous">#</a> </dt> +<dd> +Some public authorities are using mySociety's <a href="https://github.com/mysociety/foi-register">FOI Register</a> software in order to use WhatDoTheyKnow as a disclosure log for all their FOI activity. When people make requests to the authority their names will usually be withheld from publication just as they would in an authority disclosure log on an authority website. +</dd> + <dt id="full_address">They've asked for my postal address! <a href="#full_address">#</a> </dt> <dd> diff --git a/app/views/help/requesting.html.erb b/app/views/help/requesting.html.erb index d9397d763..51358c6d9 100644 --- a/app/views/help/requesting.html.erb +++ b/app/views/help/requesting.html.erb @@ -30,7 +30,7 @@ <dt id="missing_body">You're missing the public authority that I want to request from! <a href="#missing_body">#</a> </dt> <dd> - <p>Please <a href="<%= help_contact_path %>">contact us</a> with the name of the public authority and, + <p>Please <a href="<%= new_change_request_path %>">contact us</a> with the name of the public authority and, if you can find it, their contact email address for Freedom of Information requests. </p> <p>If you'd like to help add a whole category of public authority to the site, we'd love 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/outgoing_mailer/initial_request.text.erb b/app/views/outgoing_mailer/initial_request.text.erb index 5c418ecc7..b0f1dc9e8 100644 --- a/app/views/outgoing_mailer/initial_request.text.erb +++ b/app/views/outgoing_mailer/initial_request.text.erb @@ -6,7 +6,7 @@ <%= @info_request.incoming_email %> <%= _('Is {{email_address}} the wrong address for {{type_of_request}} requests to {{public_body_name}}? If so, please contact us using this form:', :email_address => @info_request.public_body.request_email, :type_of_request => @info_request.law_used_full, :public_body_name => @info_request.public_body.name)%> -<%= help_contact_url %> +<%= new_change_request_url(:body => @info_request.public_body.url_name) %> <%= render :partial => 'followup_footer' %> diff --git a/app/views/public_body/show.html.erb b/app/views/public_body/show.html.erb index b35e29eea..c36396149 100644 --- a/app/views/public_body/show.html.erb +++ b/app/views/public_body/show.html.erb @@ -32,6 +32,7 @@ <% end %> <% end %> <%= link_to _('View FOI email address'), view_public_body_email_path(@public_body.url_name) %><br> + <%= link_to _("Ask us to update FOI email"), new_change_request_path(:body => @public_body.url_name) %><br> </div> <div id="header_left"> diff --git a/app/views/public_body/view_email.html.erb b/app/views/public_body/view_email.html.erb index 3f0a558c7..5f4bc95f4 100644 --- a/app/views/public_body/view_email.html.erb +++ b/app/views/public_body/view_email.html.erb @@ -1,4 +1,4 @@ -<% @title = "FOI email address for '" + h(@public_body.name) + "'" %> +<% @title = _("FOI email address for {{public_body}}", :public_body => h(@public_body.name)) %> <h1><%= _('FOI email address for {{public_body}}',:public_body=> public_body_link(@public_body))%></h1> @@ -35,9 +35,9 @@ <div id="stepwise_make_request_view_email"> <strong> <% if @public_body.eir_only? %> - <%= link_to "Make a new EIR request", new_request_to_body_path(:url_name => @public_body.url_name)%> + <%= link_to _("Make a new EIR request"), new_request_to_body_path(:url_name => @public_body.url_name)%> <% else %> - <%= link_to "Make a new FOI request", new_request_to_body_path(:url_name => @public_body.url_name)%> + <%= link_to _("Make a new FOI request"), new_request_to_body_path(:url_name => @public_body.url_name)%> <% end %> to <%= h(@public_body.name) %> </strong> diff --git a/app/views/public_body_change_requests/new.html.erb b/app/views/public_body_change_requests/new.html.erb new file mode 100644 index 000000000..7079cd868 --- /dev/null +++ b/app/views/public_body_change_requests/new.html.erb @@ -0,0 +1,61 @@ +<h1><%= @title %></h1> +<%= foi_error_messages_for :change_request %> + <%= form_for(@change_request, :url => change_request_path) do |f| %> +<% if not @user %> + <p> + <label class="form_label" for="user_name"> + <%= _("Your name:") %> + </label> + <%= f.text_field :user_name %> + <%= _('(or <a href="{{url}}">sign in</a>)', :url => signin_path(:r => request.fullpath)) %> + </p> + + <p> + <label class="form_label" for="user_email"> + <%= ("Your email:") %> + </label> + <%= f.text_field :user_email %> + </p> +<% end %> +<% if @change_request.public_body %> + <%= f.hidden_field :public_body_id %> +<% else %> + <p> + <label class="form_label" for="public_body_name"> + <%= _("Authority:") %> + </label> + <%= f.text_field :public_body_name %> + </p> +<% end %> + <p> + <label class="form_label" for="public_body_email"> + <%= _("Authority email:") %> + </label> + <%= f.text_field :public_body_email %> + <div class="form_item_note"> + <%= _("The contact email address for FOI requests to the authority.") %> + </div> + </p> + + <p> + <label class="form_label" for="source_url"> + <%= _("Source URL:") %> + </label> + <%= f.text_field :source_url %> + <div class="form_item_note"> + <%= _("The URL where you found the email address. This field is optional, but it would help us a lot if you can provide a link to a specific page on the authority's website that gives this address, as it will make it much easier for us to check.") %> + </div> + </p> + + <p> + <label class="form_label" for="notes"> + <%= _("Notes:") %> + </label> + <%= f.text_area :notes, :rows => 10, :cols => 60 %> + </p> + + <div class="form_button"> + <%= submit_tag _("Submit request") %> + </div> + +<% end %> diff --git a/app/views/reports/new.html.erb b/app/views/reports/new.html.erb index 7d558ab4e..1197b2255 100644 --- a/app/views/reports/new.html.erb +++ b/app/views/reports/new.html.erb @@ -4,17 +4,17 @@ <p><%= _("This request has already been reported for administrator attention") %></p> <% else %> <p> - Reporting a request notifies the site administrators. They will respond as soon as possible. + <%= _("Reporting a request notifies the site administrators. They will respond as soon as possible.") %> </p> - <p>Why specifically do you consider this request unsuitable?</p> + <p><%= _("Why specifically do you consider this request unsuitable?") %></p> <%= form_tag request_report_path(:request_id => @info_request.url_title) do %> <p> <label class="form_label" for="reason">Reason:</label> - <%= select_tag :reason, options_for_select(@info_request.report_reasons, @reason), :prompt => "Choose a reason" %> + <%= select_tag :reason, options_for_select(@info_request.report_reasons, @reason), :prompt => _("Choose a reason") %> </p> <p> - <label class="form_label" for="message">Please tell us more:</label> + <label class="form_label" for="message"><%= _("Please tell us more:") %></label> <%= text_area_tag :message, @message, :rows => 10, :cols => 60 %> </p> diff --git a/app/views/request/_list_results.html.erb b/app/views/request/_list_results.html.erb new file mode 100644 index 000000000..4da042816 --- /dev/null +++ b/app/views/request/_list_results.html.erb @@ -0,0 +1,12 @@ + <% @results = InfoRequest.request_list(@filters, @page, @per_page, @max_results) %> + <% if @results[:results].empty? %> + <p> <%= _('No requests of this sort yet.')%></p> + <% else %> + <h2 class="foi_results"><%= _('{{count}} FOI requests found', :count => @results[:matches_estimated]) %></h2> + <div class="results_block"> + <% @results[:results].each do |result| %> + <%= render :partial => 'request/request_listing_via_event', :locals => { :event => result, :info_request => result.info_request } %> + <% end %> + </div> + <% end %> + <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @results[:show_no_more_than]) %> 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/list.html.erb b/app/views/request/list.html.erb index 062b77c3e..a465f03ba 100644 --- a/app/views/request/list.html.erb +++ b/app/views/request/list.html.erb @@ -14,21 +14,11 @@ <div style="clear:both"></div> <div class="results_section"> - <% # TODO: Cache for 5 minutes %> - <% if @list_results.empty? %> - <p> <%= _('No requests of this sort yet.')%></p> - <% else %> - <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' %> - <%= render :partial => 'request/request_listing_via_event', :locals => { :event => result, :info_request => result.info_request } %> - <% else %> - <p><strong><%= _('Unexpected search result type') %> <%=result.class.to_s%></strong></p> - <% end %> - <% end %> - </div> + <% if key = request_list_cache_key %> + <% cache_if_caching_fragments(key, :expires_in => 5.minutes) do %> + <%= render :partial => 'list_results' %> <% end %> - - <%= will_paginate WillPaginate::Collection.new(@page, @per_page, @show_no_more_than) %> + <% else %> + <%= render :partial => 'list_results' %> + <% end %> </div> 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/request/show.html.erb b/app/views/request/show.html.erb index c520ce40c..153b0b861 100644 --- a/app/views/request/show.html.erb +++ b/app/views/request/show.html.erb @@ -34,14 +34,16 @@ <p class="subtitle"> <% if !@user.nil? && @user.admin_page_links? %> <%= _('{{user}} ({{user_admin_link}}) made this {{law_used_full}} request (<a href="{{request_admin_url}}">admin</a>) to {{public_body_link}} (<a href="{{public_body_admin_url}}">admin</a>)', - :user => @info_request.is_external? ? (@info_request.user_name || _('An anonymous user')) : user_link(@info_request.user), + :user => request_user_link(@info_request, _('An anonymous user')), :law_used_full => h(@info_request.law_used_full), :user_admin_link => user_admin_link_for_request(@info_request, _('external'), _('admin')), :request_admin_url => admin_request_show_url(@info_request), :public_body_link => public_body_link(@info_request.public_body), :public_body_admin_url => admin_body_show_url(@info_request.public_body)) %> <% else %> - <%= _('{{user}} made this {{law_used_full}} request',:user=>@info_request.is_external? ? (@info_request.user_name || _('An anonymous user')) : user_link(@info_request.user), :law_used_full=>h(@info_request.law_used_full)) %> + <%= _('{{user}} made this {{law_used_full}} request', + :user=>request_user_link(@info_request, _('An anonymous user')), + :law_used_full=>h(@info_request.law_used_full)) %> <%= _('to {{public_body}}',:public_body=>public_body_link(@info_request.public_body)) %> <% end %> </p> 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 %> diff --git a/app/views/user/sign.html.erb b/app/views/user/sign.html.erb index 8291cdace..e8c5d5a58 100644 --- a/app/views/user/sign.html.erb +++ b/app/views/user/sign.html.erb @@ -13,7 +13,7 @@ </p> <% if @post_redirect.post_params["controller"] == "admin_general" %> <% unless AlaveteliConfiguration::disable_emergency_user %> - <p id="superuser_message">Don't have a superuser account yet? <%= link_to "Sign in as the emergency user", @post_redirect.uri + "?emergency=1" %></p> + <p id="superuser_message"><%= _("Don't have a superuser account yet?") %> <%= link_to _("Sign in as the emergency user"), @post_redirect.uri + "?emergency=1" %></p> <% end %> <% end %> diff --git a/app/views/user/wall.html.erb b/app/views/user/wall.html.erb index 190cc0a6d..6699c55fa 100644 --- a/app/views/user/wall.html.erb +++ b/app/views/user/wall.html.erb @@ -1,16 +1,19 @@ <% @title = h(@display_user.name) + _(" - wall") %> -<% if @is_you %> <div class="medium_column"> - <p><%= _('You can change the requests and users you are following on <a href="{{profile_url}}">your profile page</a>.', :profile_url => show_user_profile_path) %> - <%= render :partial => 'change_receive_email' %> + <% if @is_you %> + <h2><%= _("My wall") %></h2> + <p><%= _('You can change the requests and users you are following on <a href="{{profile_url}}">your profile page</a>.', :profile_url => show_user_profile_path) %></p> + <%= render :partial => 'change_receive_email' %> + <% else %> + <h2><%= _("This is <a href=\"{{profile_url}}\">{{user_name}}'s</a> wall", :profile_url => show_user_profile_path, :user_name => h(@display_user.name)) %></h2> + <% end %> </div> -<% end %> <div id="user_profile_search"> - <% if !@feed_results.nil? %> - <% for result in @feed_results %> - <%= render :partial => 'request/wall_listing', :locals => { :event => result, :info_request => result.info_request } %> - <% end %> - <% end %> - - + <% if !@feed_results.nil? and !@feed_results.empty? %> + <% for result in @feed_results %> + <%= render :partial => 'request/wall_listing', :locals => { :event => result, :info_request => result.info_request } %> + <% end %> + <% else %> + <p><%= _("There is nothing to display yet.") %></p> + <% end %> </div> |