diff options
-rw-r--r-- | app/controllers/request_controller.rb | 22 | ||||
-rw-r--r-- | app/models/info_request.rb | 85 | ||||
-rw-r--r-- | spec/controllers/request_controller_spec.rb | 12 | ||||
-rw-r--r-- | spec/models/customstates.rb | 62 | ||||
-rw-r--r-- | spec/models/info_request_spec.rb | 36 |
5 files changed, 180 insertions, 37 deletions
diff --git a/app/controllers/request_controller.rb b/app/controllers/request_controller.rb index 103349311..9fc0a4e26 100644 --- a/app/controllers/request_controller.rb +++ b/app/controllers/request_controller.rb @@ -12,6 +12,21 @@ 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 + def load_custom_states + begin + # InfoRequestCustomStates may be `require`d in a theme + # plugin, or by a test + RequestController.send(:include, RequestControllerCustomStates) + @@custom_states_loaded = true + rescue NameError + @@custom_states_loaded = false + end + end + + def initialize + self.load_custom_states + end + def show @locale = self.locale_from_params() PublicBody.with_locale(@locale) do @@ -369,6 +384,7 @@ class RequestController < ApplicationController end return end + # Display advice for requester on what to do next, as appropriate if @info_request.calculate_status == 'waiting_response' flash[:notice] = _("<p>Thank you! Hopefully your wait isn't too long.</p> <p>By law, you should get a response promptly, and normally before the end of <strong> @@ -420,9 +436,9 @@ class RequestController < ApplicationController flash[:notice] = _("If you have not done so already, please write a message below telling the authority that you have withdrawn your request. Otherwise they will not know it has been withdrawn.") redirect_to respond_to_last_url(@info_request) else - begin - return theme_describe_state(@info_request) - rescue NoMethodError + if @@custom_states_loaded + return self.theme_describe_state(@info_request) + else raise "unknown calculate_status " + @info_request.calculate_status end end diff --git a/app/models/info_request.rb b/app/models/info_request.rb index 07245bdfd..16e66c44a 100644 --- a/app/models/info_request.rb +++ b/app/models/info_request.rb @@ -51,30 +51,9 @@ class InfoRequest < ActiveRecord::Base has_many :exim_logs, :order => 'exim_log_done_id' has_tag_string - - def self.enumerate_states - states = [ - 'waiting_response', - 'waiting_clarification', - 'gone_postal', - 'not_held', - 'rejected', # this is called 'refused' in UK FOI law and the user interface, but 'rejected' internally for historic reasons - 'successful', - 'partially_successful', - 'internal_review', - 'error_message', - 'requires_admin', - 'user_withdrawn' - ] - begin - states += theme_extra_states - rescue NoMethodError - states - end - end # user described state (also update in info_request_event, admin_request/edit.rhtml) - validates_inclusion_of :described_state, :in => InfoRequest.enumerate_states + validate :must_be_valid_state validates_inclusion_of :prominence, :in => [ 'normal', @@ -101,7 +80,31 @@ class InfoRequest < ActiveRecord::Base 'blackhole' # just dump them ] + def enumerate_states + states = [ + 'waiting_response', + 'waiting_clarification', + 'gone_postal', + 'not_held', + 'rejected', # this is called 'refused' in UK FOI law and the user interface, but 'rejected' internally for historic reasons + 'successful', + 'partially_successful', + 'internal_review', + 'error_message', + 'requires_admin', + 'user_withdrawn' + ] + + if @@custom_states_loaded + states += self.theme_extra_states + end + states + end + def must_be_valid_state + errors.add(:described_state, "is not a valid state") if + !self.enumerate_states.include? described_state + end # only check on create, so existing models with mixed case are allowed def validate_on_create if !self.title.nil? && !MySociety::Validate.uses_mixed_capitals(self.title, 10) @@ -118,6 +121,7 @@ class InfoRequest < ActiveRecord::Base OLD_AGE_IN_DAYS = 21.days def after_initialize + self.load_custom_states if self.described_state.nil? self.described_state = 'waiting_response' end @@ -127,6 +131,17 @@ class InfoRequest < ActiveRecord::Base end end + def load_custom_states + begin + # InfoRequestCustomStates may be `require`d in a theme + # plugin, or by a test + InfoRequest.send(:include, InfoRequestCustomStates) + @@custom_states_loaded = true + rescue NameError + @@custom_states_loaded = false + end + end + def visible_comments self.comments.find(:all, :conditions => 'visible') end @@ -520,10 +535,15 @@ public # waiting_response_overdue # waiting_response_very_overdue def calculate_status + if @@custom_states_loaded + return self.theme_calculate_status + else + self.base_calculate_status + end + end + + def base_calculate_status return 'waiting_classification' if self.awaiting_description - # if deadline_extended expired do waiting_response_overdue - return 'waiting_response_overdue' if - self.described_state == "deadline_extended" && Time.now.strftime("%Y-%m-%d") > self.date_deadline_extended.strftime("%Y-%m-%d") return described_state unless self.described_state == "waiting_response" # Compare by date, so only overdue on next day, not if 1 second late return 'waiting_response_very_overdue' if @@ -635,10 +655,7 @@ public return Holiday.due_date_from(self.date_initial_request_last_sent_at, 40) end end - # deadline_extended - def date_deadline_extended - return Holiday.due_date_from(self.date_initial_request_last_sent_at, 15) - end + # Where the initial request is sent to def recipient_email return self.public_body.request_email @@ -777,10 +794,6 @@ public _("Waiting clarification.") elsif status == 'gone_postal' _("Handled by post.") - elsif status == 'deadline_extended' - _("Deadline extended.") - elsif status == 'wrong_response' - _("Wrong Response.") elsif status == 'internal_review' _("Awaiting internal review.") elsif status == 'error_message' @@ -790,7 +803,11 @@ public elsif status == 'user_withdrawn' _("Withdrawn by the requester.") else - raise _("unknown status ") + status + begin + return self.theme_display_status(status) + rescue NoMethodError + raise _("unknown status ") + status + end end end diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb index 0d9916b71..64f3f8061 100644 --- a/spec/controllers/request_controller_spec.rb +++ b/spec/controllers/request_controller_spec.rb @@ -526,6 +526,8 @@ describe RequestController, "when classifying an information request" do response.should render_template('user/wrong_user') end + + describe 'when the request is old and unclassified' do before do @@ -714,6 +716,16 @@ describe RequestController, "when classifying an information request" do post_status('rejected') response.should redirect_to(:controller => 'help', :action => 'unhappy', :url_title => @dog_request.url_title) end + + describe "when using custom statuses from the theme" do + InfoRequest.send(:require, File.expand_path(File.join(File.dirname(__FILE__), '..', 'models', 'customstates'))) + + it "knows about extended states" do + Time.stub!(:now).and_return(Time.utc(2007, 11, 10, 00, 01)) + post_status('deadline_extended') + flash[:notice].should == 'Authority has requested extension of the deadline.' + end + end end describe 'when redirecting after a successful status update by the request owner' do diff --git a/spec/models/customstates.rb b/spec/models/customstates.rb new file mode 100644 index 000000000..de8d04ffb --- /dev/null +++ b/spec/models/customstates.rb @@ -0,0 +1,62 @@ +module InfoRequestCustomStates + # Mixin methods for InfoRequest + def theme_display_status(status) + if status == 'deadline_extended' + _("Deadline extended.") + elsif status == 'wrong_response' + _("Wrong Response.") + else + raise _("unknown status ") + status + end + end + + def theme_extra_states + return ['deadline_extended', + 'wrong_response'] + end + + + def theme_calculate_status + return 'waiting_classification' if self.awaiting_description + waiting_response = self.described_state == "waiting_response" || self.described_state == "deadline_extended" + return self.described_state unless waiting_response + if self.described_state == 'deadline_extended' + return 'deadline_extended' if + Time.now.strftime("%Y-%m-%d") < self.date_deadline_extended.strftime("%Y-%m-%d") + return 'waiting_response_very_overdue' if + Time.now.strftime("%Y-%m-%d") > Holiday.due_date_from(self.date_deadline_extended, 15).strftime("%Y-%m-%d") + return 'waiting_response_overdue' + end + return 'waiting_response_very_overdue' if + Time.now.strftime("%Y-%m-%d") > self.date_very_overdue_after.strftime("%Y-%m-%d") + return 'waiting_response_overdue' if + Time.now.strftime("%Y-%m-%d") > self.date_response_required_by.strftime("%Y-%m-%d") + return 'waiting_response' + end + + def date_deadline_extended + # XXX shouldn't this be 15 days after the date the status was + # changed to "deadline extended"? Or perhaps 15 days ater the + # initial request due date? + return Holiday.due_date_from(self.date_response_required_by, 15) + end + +end + +module RequestControllerCustomStates + + def theme_describe_state(info_request) + # called after the core describe_state code. It should + # end by raising an error if the status is unknown + if info_request.calculate_status == 'deadline_extended' + flash[:notice] = _("Authority has requested extension of the deadline.") + redirect_to unhappy_url(info_request) + elsif info_request.calculate_status == 'wrong_response' + flash[:notice] = _("Oh no! Sorry to hear that your request was wrong. Here is what to do now.") + redirect_to unhappy_url(info_request) + else + raise "unknown calculate_status " + info_request.calculate_status + end + end + +end diff --git a/spec/models/info_request_spec.rb b/spec/models/info_request_spec.rb index 96a4f5bc8..91b1b0876 100644 --- a/spec/models/info_request_spec.rb +++ b/spec/models/info_request_spec.rb @@ -159,6 +159,42 @@ describe InfoRequest do end end + + describe "when using a plugin and calculating the status" do + + fixtures :info_requests + + before do + InfoRequest.send(:require, File.expand_path(File.dirname(__FILE__) + '/customstates')) + @ir = info_requests(:naughty_chicken_request) + @ir.load_custom_states + end + + it "rejects invalid states" do + lambda {@ir.set_described_state("foo")}.should raise_error(ActiveRecord::RecordInvalid) + end + + it "accepts core states" do + @ir.set_described_state("requires_admin") + end + + it "accepts extended states" do + # this time would normally be "overdue" + Time.stub!(:now).and_return(Time.utc(2007, 11, 10, 00, 01)) + @ir.set_described_state("deadline_extended") + @ir.display_status.should == 'Deadline extended.' + @ir.date_deadline_extended + end + + it "is not overdue if it's had the deadline extended" do + when_overdue = Time.utc(2007, 11, 10, 00, 01) + 16.days + Time.stub!(:now).and_return(when_overdue) + @ir.calculate_status.should == 'waiting_response_overdue' + end + + end + + describe "when calculating the status for a school" do fixtures :info_requests, :info_request_events, :holidays, :public_bodies, :public_body_translations |