diff options
-rw-r--r-- | app/assets/stylesheets/responsive/_global_style.scss | 3 | ||||
-rw-r--r-- | app/assets/stylesheets/responsive/_new_request_layout.scss | 5 | ||||
-rw-r--r-- | app/assets/stylesheets/responsive/_user_layout.scss | 5 | ||||
-rw-r--r-- | app/controllers/general_controller.rb | 2 | ||||
-rw-r--r-- | app/models/censor_rule.rb | 6 | ||||
-rw-r--r-- | app/models/outgoing_message.rb | 216 | ||||
-rw-r--r-- | app/models/public_body.rb | 20 | ||||
-rw-r--r-- | app/views/admin_public_body/import_csv.html.erb | 18 | ||||
-rw-r--r-- | config/general.yml-example | 750 | ||||
-rw-r--r-- | doc/CHANGES.md | 11 | ||||
-rw-r--r-- | lib/configuration.rb | 5 | ||||
-rw-r--r-- | spec/models/censor_rule_spec.rb | 33 | ||||
-rw-r--r-- | spec/models/public_body_spec.rb | 52 |
13 files changed, 884 insertions, 242 deletions
diff --git a/app/assets/stylesheets/responsive/_global_style.scss b/app/assets/stylesheets/responsive/_global_style.scss index 290591b5f..af25fb0b0 100644 --- a/app/assets/stylesheets/responsive/_global_style.scss +++ b/app/assets/stylesheets/responsive/_global_style.scss @@ -17,6 +17,9 @@ a { &:focus { color: #333333; } + &:visited { + color: darken(#2688dc, 10%); + } } h1, h2, h3, h4, h5, h6 { diff --git a/app/assets/stylesheets/responsive/_new_request_layout.scss b/app/assets/stylesheets/responsive/_new_request_layout.scss index eec95ae77..aba4ffc29 100644 --- a/app/assets/stylesheets/responsive/_new_request_layout.scss +++ b/app/assets/stylesheets/responsive/_new_request_layout.scss @@ -29,6 +29,11 @@ @include lte-ie7 { width: 26.188em; } + /* Don't nest public body grid row in this context */ + #public_body_show { + @include grid-row(); + } + } /* Hide some elements of the public body that aren't appropriate in this diff --git a/app/assets/stylesheets/responsive/_user_layout.scss b/app/assets/stylesheets/responsive/_user_layout.scss index 8087f978c..a568a5fa3 100644 --- a/app/assets/stylesheets/responsive/_user_layout.scss +++ b/app/assets/stylesheets/responsive/_user_layout.scss @@ -1,2 +1,7 @@ /* Layout for user pages */ +#user_profile_search { + #search_form { + margin-top: 2rem; + } +} diff --git a/app/controllers/general_controller.rb b/app/controllers/general_controller.rb index 158492eb2..2c8abbaf4 100644 --- a/app/controllers/general_controller.rb +++ b/app/controllers/general_controller.rb @@ -32,7 +32,7 @@ class GeneralController < ApplicationController if !content.empty? @data = XmlSimple.xml_in(content) @channel = @data['channel'][0] - @blog_items = @channel['item'] + @blog_items = @channel.fetch('item') { [] } @feed_autodetect = [{:url => @feed_url, :title => "#{site_name} blog"}] end end diff --git a/app/models/censor_rule.rb b/app/models/censor_rule.rb index 3c5c77563..62cf8112f 100644 --- a/app/models/censor_rule.rb +++ b/app/models/censor_rule.rb @@ -30,7 +30,11 @@ class CensorRule < ActiveRecord::Base attr_accessor :allow_global validate :require_user_request_or_public_body, :unless => proc{ |rule| rule.allow_global == true } validate :require_valid_regexp, :if => proc{ |rule| rule.regexp? == true } - validates_presence_of :text + + validates_presence_of :text, + :replacement, + :last_edit_comment, + :last_edit_editor scope :global, {:conditions => {:info_request_id => nil, :user_id => nil, diff --git a/app/models/outgoing_message.rb b/app/models/outgoing_message.rb index 160f69d0b..9424113fc 100644 --- a/app/models/outgoing_message.rb +++ b/app/models/outgoing_message.rb @@ -28,106 +28,115 @@ class OutgoingMessage < ActiveRecord::Base extend MessageProminence include Rails.application.routes.url_helpers include LinkToHelper - self.default_url_options[:host] = AlaveteliConfiguration::domain - # https links in emails if forcing SSL - if AlaveteliConfiguration::force_ssl - self.default_url_options[:protocol] = "https" - end - strip_attributes! - - has_prominence + # To override the default letter + attr_accessor :default_letter - belongs_to :info_request validates_presence_of :info_request - validates_inclusion_of :status, :in => ['ready', 'sent', 'failed'] - validates_inclusion_of :message_type, :in => ['initial_request', 'followup' ] #, 'complaint'] + validates_inclusion_of :message_type, :in => ['initial_request', 'followup'] validate :format_of_body + belongs_to :info_request belongs_to :incoming_message_followup, :foreign_key => 'incoming_message_followup_id', :class_name => 'IncomingMessage' # can have many events, for items which were resent by site admin e.g. if # contact address changed has_many :info_request_events - # To override the default letter - attr_accessor :default_letter - + after_initialize :set_default_letter + after_save :purge_in_cache # reindex if body text is edited (e.g. by admin interface) after_update :xapian_reindex_after_update - def xapian_reindex_after_update - if self.changes.include?('body') - for info_request_event in self.info_request_events - info_request_event.xapian_mark_needs_index - end - end - end - after_initialize :set_default_letter + strip_attributes! + has_prominence - # 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 - return OutgoingMessage.default_salutation(self.info_request.public_body) - end - salutation = _("Dear {{public_body_name}},", :public_body_name => ret) + self.default_url_options[:host] = AlaveteliConfiguration.domain + + # https links in emails if forcing SSL + if AlaveteliConfiguration::force_ssl + self.default_url_options[:protocol] = "https" end - def OutgoingMessage.default_salutation(public_body) + def self.default_salutation(public_body) _("Dear {{public_body_name}},", :public_body_name => public_body.name) end - def OutgoingMessage.placeholder_salutation + def self.placeholder_salutation _("Dear [Authority name],") end - def OutgoingMessage.fill_in_salutation(body, public_body) + def self.fill_in_salutation(body, public_body) body.gsub(placeholder_salutation, default_salutation(public_body)) end + # How the default letter starts and ends + def get_salutation + if info_request.is_batch_request_template? + return OutgoingMessage.placeholder_salutation + end + + ret = "" + if message_type == 'followup' && + !incoming_message_followup.nil? && + !incoming_message_followup.safe_mail_from.nil? && + incoming_message_followup.valid_to_reply_to? + + ret += OutgoingMailer.name_for_followup(info_request, incoming_message_followup) + else + return OutgoingMessage.default_salutation(info_request.public_body) + end + salutation = _("Dear {{public_body_name}},", :public_body_name => ret) + 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,") + if message_type == 'followup' && + !incoming_message_followup.nil? && + !incoming_message_followup.safe_mail_from.nil? && + incoming_message_followup.valid_to_reply_to? + + _("Yours sincerely,") else - return _("Yours faithfully,") + _("Yours faithfully,") end end + def get_internal_review_insert_here_note - return _("GIVE DETAILS ABOUT YOUR COMPLAINT HERE") + _("GIVE DETAILS ABOUT YOUR COMPLAINT HERE") end - def get_default_letter - if self.default_letter - return self.default_letter - end - if self.what_doing == 'internal_review' - _("Please pass this on to the person who conducts Freedom of Information reviews.") + - "\n\n" + - _("I am writing to request an internal review of {{public_body_name}}'s handling of my FOI request '{{info_request_title}}'.", - :public_body_name => self.info_request.public_body.name, - :info_request_title => self.info_request.title) + - "\n\n\n\n [ " + self.get_internal_review_insert_here_note + " ] \n\n\n\n" + - _("A full history of my FOI request and all correspondence is available on the Internet at this address: {{url}}", - :url => request_url(self.info_request)) + - "\n" + def get_default_letter + return default_letter if default_letter + + if what_doing == 'internal_review' + letter = _("Please pass this on to the person who conducts Freedom of Information reviews.") + letter += "\n\n" + letter += _("I am writing to request an internal review of {{public_body_name}}'s handling of my FOI request '{{info_request_title}}'.", + :public_body_name => info_request.public_body.name, + :info_request_title => info_request.title) + letter += "\n\n\n\n [ #{ get_internal_review_insert_here_note } ] \n\n\n\n" + letter += _("A full history of my FOI request and all correspondence is available on the Internet at this address: {{url}}", + :url => request_url(info_request)) + letter += "\n" else "" end end + def get_default_message - get_salutation + "\n\n" + get_default_letter + "\n\n" + get_signoff + "\n\n" + msg = get_salutation + msg += "\n\n" + msg += get_default_letter + msg += "\n\n" + msg += get_signoff + msg += "\n\n" end + def set_signature_name(name) # TODO: We use raw_body here to get unstripped one - if self.raw_body == self.get_default_message - self.body = self.raw_body + name + if raw_body == get_default_message + self.body = raw_body + name end end @@ -142,84 +151,88 @@ class OutgoingMessage < ActiveRecord::Base ret.gsub!(/(?:\n\s*){2,}/, "\n\n") # remove excess linebreaks that unnecessarily space it out # Remove things from censor rules - if !self.info_request.nil? + unless info_request.nil? self.info_request.apply_censor_rules_to_text!(ret) end ret end + def raw_body read_attribute(:body) end # Used to give warnings when writing new messages def contains_email? - MySociety::Validate.email_find_regexp.match(self.body) + MySociety::Validate.email_find_regexp.match(body) end + def contains_postcode? - MySociety::Validate.contains_postcode?(self.body) + MySociety::Validate.contains_postcode?(body) end # Deliver outgoing message # Note: You can test this from script/console with, say: # InfoRequest.find(1).outgoing_messages[0].send_message def send_message(log_event_type = 'sent') - if self.status == 'ready' - if self.message_type == 'initial_request' + if status == 'ready' + if message_type == 'initial_request' self.last_sent_at = Time.now self.status = 'sent' self.save! - mail_message = OutgoingMailer.initial_request(self.info_request, self).deliver + mail_message = OutgoingMailer.initial_request(info_request, self).deliver self.info_request.log_event(log_event_type, { :email => mail_message.to_addrs.join(", "), :outgoing_message_id => self.id, :smtp_message_id => mail_message.message_id }) self.info_request.set_described_state('waiting_response') - elsif self.message_type == 'followup' + elsif message_type == 'followup' self.last_sent_at = Time.now self.status = 'sent' self.save! - mail_message = OutgoingMailer.followup(self.info_request, self, self.incoming_message_followup).deliver + mail_message = OutgoingMailer.followup(info_request, self, incoming_message_followup).deliver self.info_request.log_event('followup_' + log_event_type, { :email => mail_message.to_addrs.join(", "), :outgoing_message_id => self.id, :smtp_message_id => mail_message.message_id }) - if self.info_request.described_state == 'waiting_clarification' + if info_request.described_state == 'waiting_clarification' self.info_request.set_described_state('waiting_response') end - if self.what_doing == 'internal_review' + if what_doing == 'internal_review' self.info_request.set_described_state('internal_review') end else - raise "Message id #{self.id} has type '#{self.message_type}' which send_message can't handle" + raise "Message id #{id} has type '#{message_type}' which send_message can't handle" end - elsif self.status == 'sent' - raise "Message id #{self.id} has already been sent" + elsif status == 'sent' + raise "Message id #{id} has already been sent" else - raise "Message id #{self.id} not in state for send_message" + raise "Message id #{id} not in state for send_message" end end # An admin function def resend_message - if ['initial_request', 'followup'].include?(self.message_type) and self.status == 'sent' + if ['initial_request', 'followup'].include?(message_type) and status == 'sent' self.status = 'ready' send_message('resent') else - raise "Message id #{self.id} has type '#{self.message_type}' status '#{self.status}' which resend_message can't handle" + raise "Message id #{id} has type '#{message_type}' status '#{status}' which resend_message can't handle" end end # Returns the text to quote the original message when sending this one def quoted_part_to_append_to_email - if self.message_type == 'followup' && !self.incoming_message_followup.nil? - return "\n\n-----Original Message-----\n\n" + self.incoming_message_followup.get_body_for_quoting + "\n" + if message_type == 'followup' && !incoming_message_followup.nil? + quoted = "\n\n-----Original Message-----\n\n" + quoted += incoming_message_followup.get_body_for_quoting + quoted += "\n" else - return "" + "" end end @@ -229,8 +242,8 @@ class OutgoingMessage < ActiveRecord::Base end # Returns text for indexing / text display - def get_text_for_indexing(strip_salutation=true) - text = self.body.strip + def get_text_for_indexing(strip_salutation = true) + text = body.strip # Remove salutation text.sub!(/Dear .+,/, "") if strip_salutation @@ -238,19 +251,20 @@ class OutgoingMessage < ActiveRecord::Base # Remove email addresses from display/index etc. self.remove_privacy_sensitive_things!(text) - return text + text end # Return body for display as HTML def get_body_for_html_display - text = self.body.strip + text = body.strip self.remove_privacy_sensitive_things!(text) - text = MySociety::Format.wrap_email_body_by_lines(text) # reparagraph and wrap it so is good preview of emails + # reparagraph and wrap it so is good preview of emails + text = MySociety::Format.wrap_email_body_by_lines(text) text = CGI.escapeHTML(text) text = MySociety::Format.make_clickable(text, :contract => 1) text.gsub!(/\[(email address|mobile number)\]/, '[<a href="/help/officers#mobiles">\1</a>]') text = text.gsub(/\n/, '<br>') - return text.html_safe + text.html_safe end # Return body for display as text @@ -261,17 +275,16 @@ class OutgoingMessage < ActiveRecord::Base def fully_destroy ActiveRecord::Base.transaction do - info_request_event = InfoRequestEvent.find_by_outgoing_message_id(self.id) + info_request_event = InfoRequestEvent.find_by_outgoing_message_id(id) info_request_event.track_things_sent_emails.each { |a| a.destroy } info_request_event.user_info_request_sent_alerts.each { |a| a.destroy } info_request_event.destroy - self.destroy + destroy end end - after_save(:purge_in_cache) def purge_in_cache - self.info_request.purge_in_cache + info_request.purge_in_cache end def for_admin_column @@ -280,18 +293,24 @@ class OutgoingMessage < ActiveRecord::Base end end + def xapian_reindex_after_update + if changes.include?('body') + info_request_events.each do |event| + event.xapian_mark_needs_index + end + end + end + private def set_default_letter - if self.body.nil? - self.body = get_default_message - end + self.body = get_default_message if body.nil? end def format_of_body - if self.body.empty? || self.body =~ /\A#{Regexp.escape(get_salutation)}\s+#{Regexp.escape(get_signoff)}/ || self.body =~ /#{Regexp.escape(get_internal_review_insert_here_note)}/ - if self.message_type == 'followup' - if self.what_doing == 'internal_review' + if body.empty? || body =~ /\A#{Regexp.escape(get_salutation)}\s+#{Regexp.escape(get_signoff)}/ || body =~ /#{Regexp.escape(get_internal_review_insert_here_note)}/ + if message_type == 'followup' + if what_doing == 'internal_review' errors.add(:body, _("Please give details explaining why you want a review")) else errors.add(:body, _("Please enter your follow up message")) @@ -299,16 +318,19 @@ class OutgoingMessage < ActiveRecord::Base elsif errors.add(:body, _("Please enter your letter requesting information")) else - raise "Message id #{self.id} has type '#{self.message_type}' which validate can't handle" + raise "Message id #{id} has type '#{message_type}' which validate can't handle" end end - if self.body =~ /#{get_signoff}\s*\Z/m + + if body =~ /#{get_signoff}\s*\Z/m errors.add(:body, _("Please sign at the bottom with your name, or alter the \"{{signoff}}\" signature", :signoff => get_signoff)) end - if !MySociety::Validate.uses_mixed_capitals(self.body) + + unless MySociety::Validate.uses_mixed_capitals(body) errors.add(:body, _('Please write your message using a mixture of capital and lower case letters. This makes it easier for others to read.')) end - if self.what_doing.nil? || !['new_information', 'internal_review', 'normal_sort'].include?(self.what_doing) + + if what_doing.nil? || !['new_information', 'internal_review', 'normal_sort'].include?(what_doing) errors.add(:what_doing_dummy, _('Please choose what sort of reply you are making.')) end end diff --git a/app/models/public_body.rb b/app/models/public_body.rb index b22482541..87b5c2227 100644 --- a/app/models/public_body.rb +++ b/app/models/public_body.rb @@ -60,6 +60,21 @@ class PublicBody < ActiveRecord::Base translates :name, :short_name, :request_email, :url_name, :notes, :first_letter, :publication_scheme + # Default fields available for importing from CSV, in the format + # [field_name, 'short description of field (basic html allowed)'] + cattr_accessor :csv_import_fields do + [ + ['name', '(i18n)<strong>Existing records cannot be renamed</strong>'], + ['short_name', '(i18n)'], + ['request_email', '(i18n)'], + ['notes', '(i18n)'], + ['publication_scheme', '(i18n)'], + ['disclosure_log', '(i18n)'], + ['home_page', ''], + ['tag_string', '(tags separated by spaces)'], + ] + end + # Public: Search for Public Bodies whose name, short_name, request_email or # tags contain the given query # @@ -477,7 +492,10 @@ class PublicBody < ActiveRecord::Base next end - field_list = ['name', 'short_name', 'request_email', 'notes', 'publication_scheme', 'disclosure_log', 'home_page', 'tag_string'] + field_list = [] + self.csv_import_fields.each do |field_name, field_notes| + field_list.push field_name + end if public_body = bodies_by_name[name] # Existing public body available_locales.each do |locale| diff --git a/app/views/admin_public_body/import_csv.html.erb b/app/views/admin_public_body/import_csv.html.erb index d15ef1791..c690f0fc2 100644 --- a/app/views/admin_public_body/import_csv.html.erb +++ b/app/views/admin_public_body/import_csv.html.erb @@ -51,19 +51,11 @@ Another One,another@example.com,Otro organismo,a_tag </pre> <p><strong>Supported fields:</strong> - <ul> - <li> - <code>name</code> (i18n) - <strong>Existing records cannot be renamed</strong> - </li> - <li><code>short_name</code> (i18n)</li> - <li><code>request_email</code> (i18n)</li> - <li><code>notes</code> (i18n)</li> - <li><code>publication_scheme</code> (i18n)</li> - <li><code>disclosure_log</code> (i18n)</li> - <li><code>home_page</code></li> - <li><code>tag_string</code> (tags separated by spaces)</li> - </ul> + <ul> + <% PublicBody.csv_import_fields.each do |field, notes| %> + <li><code><%= field %></code> <%= sanitize(notes) %></li> + <% end %> + </ul> </p> <p><strong>Note:</strong> Choose <strong>dry run</strong> to test, without diff --git a/config/general.yml-example b/config/general.yml-example index 0f32f6192..a80784712 100644 --- a/config/general.yml-example +++ b/config/general.yml-example @@ -5,256 +5,764 @@ # # Copy this file to one called "general.yml" in the same directory. Or # have multiple config files and use a symlink to change between them. +# +# Default values for these settings can be found in +# RAILS_ROOT/lib/configuration.rb +# +# ============================================================================== # Site name appears in various places throughout the site +# +# SITE_NAME - String name of the site (default: 'Alaveteli') +# +# Examples: +# +# SITE_NAME: 'Alaveteli' +# SITE_NAME: 'WhatDoTheyKnow' +# +# --- SITE_NAME: 'Alaveteli' # Domain used in URLs generated by scripts (e.g. for going in some emails) -DOMAIN: '127.0.0.1:3000' +# +# DOMAIN - String domain or IP address (default: 'localhost:3000') +# +# Examples: +# +# DOMAIN: '127.0.0.1:3000' +# DOMAIN: 'www.example.com' +# +# --- +DOMAIN: 'www.example.org' -# If true forces everyone (in the production environment) to use encrypted connections -# (via https) by redirecting unencrypted connections. This is *highly* recommended -# so that logins can't be intercepted by naughty people. +# If true forces everyone (in the production environment) to use encrypted +# connections (via https) by redirecting unencrypted connections. This is +# *highly* recommended so that logins can't be intercepted by naughty people. +# +# FORCE_SSL - Boolean (default: true) +# +# --- FORCE_SSL: true # ISO country code of country currrently deployed in # (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) +# +# ISO_COUNTRY_CODE - String country code (default: GB) +# +# Examples: +# +# ISO_COUNTRY_CODE: GB +# +# --- ISO_COUNTRY_CODE: GB # This is the timezone that times and dates are displayed in # If not set defaults to UTC. -TIME_ZONE: Australia/Sydney +# +# TIME_ZONE - String time zone (default: UTC) +# +# Examples: +# +# TIME_ZONE: Australia/Sydney +# TIME_ZONE: Europe/London +# +# --- +TIME_ZONE: UTC # These feeds are displayed accordingly on the Alaveteli "blog" page: -BLOG_FEED: 'https://www.mysociety.org/category/projects/whatdotheyknow/feed/' -TWITTER_USERNAME: 'whatdotheyknow' +# +# BLOG_FEED - String url to the blog feed (default: nil) +# +# Examples: +# +# BLOG_FEED: https://www.mysociety.org/category/projects/whatdotheyknow/feed/ +# +# --- +BLOG_FEED: '' + +# If you want a twitter feed displayed on the "blog" page, provide the +# widget ID and username. +# +# TWITTER_USERNAME - String Twitter username (default: nil) +# +# Examples: +# +# TWITTER_USERNAME: 'whatdotheyknow' +# +# --- +TWITTER_USERNAME: '' + # Set the widget_id to get the Twitter sidebar on the blog page. # To get one https://twitter.com/settings/widgets +# +# TWITTER_WIDGET_ID - String widget ID (default: false) +# +# Examples: +# +# TWITTER_WIDGET_ID: '833549204689320031' +# +# --- TWITTER_WIDGET_ID: '' -# Locales we wish to support in this app, space-delimited -AVAILABLE_LOCALES: 'en es' +# The locales you want your site to support. If there is more than one, use +# spaces betwween the entries. +# +# AVAILABLE_LOCALES – String of space-separated locales (default: nil) +# +# Examples: +# +# AVAILABLE_LOCALES: 'en es' +# +# --- +AVAILABLE_LOCALES: 'en' + +# Nominate one of the AVAILABLE_LOCALES locales as the default +# +# DEFAULT_LOCALE – String locale (default: nil) +# +# Examples: +# +# DEFAULT_LOCALE: 'en' +# +# --- DEFAULT_LOCALE: 'en' + +# Should Alaveteli try to use the default language of the user's browser? +# +# USE_DEFAULT_BROWSER_LANGUAGE - Boolean (default: true) +# +# Examples: +# +# USE_DEFAULT_BROWSER_LANGUAGE: true +# +# --- USE_DEFAULT_BROWSER_LANGUAGE: true -# If you don't want the default locale to be included in URLs generated -# by the application, set this to false +# Normally, Alaveteli will put the locale into its URLs, like this +# www.example.com/en/body/list/all. If you don't want this behaviour whenever +# the locale is the default one, set INCLUDE_DEFAULT_LOCALE_IN_URLS to false. +# +# INCLUDE_DEFAULT_LOCALE_IN_URLS: Boolean (default: true) +# +# Examples: +# +# INCLUDE_DEFAULT_LOCALE_IN_URLS: false +# +# --- INCLUDE_DEFAULT_LOCALE_IN_URLS: true -# How many days should have passed before an answer to a request is officially late? +# The REPLY...AFTER_DAYS settings define how many days must have passed before +# an answer to a request is officially late. The SPECIAL case is for some types +# of authority (for example: in the UK, schools) which are granted a bit longer +# than everyone else to respond to questions. +# +# REPLY_LATE_AFTER_DAYS - Integer (default: 20) +# REPLY_VERY_LATE_AFTER_DAYS - Integer (default: 40) +# SPECIAL_REPLY_VERY_LATE_AFTER_DAYS - Integer (default: 60) +# +# Examples: +# +# REPLY_LATE_AFTER_DAYS: 20 +# REPLY_VERY_LATE_AFTER_DAYS: 40 +# SPECIAL_REPLY_VERY_LATE_AFTER_DAYS: 60 +# +# --- REPLY_LATE_AFTER_DAYS: 20 REPLY_VERY_LATE_AFTER_DAYS: 40 -# We give some types of authority like schools a bit longer than everyone else SPECIAL_REPLY_VERY_LATE_AFTER_DAYS: 60 -# Whether the days above are given in working or calendar days. Value can be "working" or "calendar". -# Default is "working". + +# The WORKING_OR_CALENDAR_DAYS setting can be either "working" (the default) or +# "calendar", and determines which days are counted when calculating whether a +# request is officially late. +# +# WORKING_OR_CALENDAR_DAYS - String in [working, calendar] (default: working) +# +# Examples: +# +# WORKING_OR_CALENDAR_DAYS: working +# WORKING_OR_CALENDAR_DAYS: calendar +# +# --- WORKING_OR_CALENDAR_DAYS: working -# example public bodies for the home page, semicolon delimited - short_names -# Comment out if you want this to be auto-generated. WARNING: this is slow & don't use production! -FRONTPAGE_PUBLICBODY_EXAMPLES: 'tgq' +# Specify which public bodies you want to be listed as examples on the home +# page, using their short_names. If you want more than one, separate them with +# semicolons. List is auto-generated if not set. +# +# *Warning:* this is slow — don't use in production! +# +# FRONTPAGE_PUBLICBODY_EXAMPLES - String semicolon-separated list of public +# bodies (default: nil) +# +# Examples: +# +# FRONTPAGE_PUBLICBODY_EXAMPLES: 'tgq' +# FRONTPAGE_PUBLICBODY_EXAMPLES: 'tgq;foo;bar' +# +# --- +FRONTPAGE_PUBLICBODY_EXAMPLES: '' -# URLs of themes to download and use (when running rails-post-deploy -# script). Earlier in the list means the templates have a higher -# priority. +# URLs of themes to download and use (when running the rails-post-deploy +# script). The earlier in the list means the templates have a higher priority. +# +# THEME_URLS - Array of theme URLs (default: []) +# +# Examples: +# +# THEME_URLS: +# - 'git://github.com/mysociety/alavetelitheme.git' +# - 'git://github.com/mysociety/whatdotheyknow-theme.git' +# +# --- THEME_URLS: - - 'git://github.com/mysociety/alavetelitheme.git' + - 'git://github.com/mysociety/alavetelitheme.git' -# When rails-post-deploy installs the themes it will try this branch first -# (but only if this config is set). If the branch doesn't exist it will fall -# back to using a tagged version specific to your installed alaveteli version. -# If that doesn't exist it will back to master. +# When rails-post-deploy installs the themes, it will try to use the branch +# specified by THEME_BRANCH first. If the branch doesn't exist it will fall +# back to using a tagged version specific to your installed alaveteli version, +# and if that doesn't exist it will fall back to master. +# +# THEME_BRANCH - Boolean (default: false) +# +# Examples: +# +# # Use the develop branch if it exists, otherwise fall back as described +# THEME_BRANCH: 'develop' +# +# # try the use-with-alaveteli-xxx branch/tag, otherwise fall back to HEAD +# THEME_BRANCH: false +# +# --- THEME_BRANCH: false -# Whether a user needs to sign in to start the New Request process +# Does a user needs to sign in to start the New Request process? +# +# FORCE_REGISTRATION_ON_NEW_REQUEST - Boolean (default: false) +# +# --- FORCE_REGISTRATION_ON_NEW_REQUEST: false - -## Incoming email -# Your email domain, e.g. 'foifa.com' +# Your email domain for incoming mail. +# +# INCOMING_EMAIL_DOMAIN – String domain (default: localhost) +# +# Examples: +# +# INCOMING_EMAIL_DOMAIN: 'localhost' +# INCOMING_EMAIL_DOMAIN: 'foifa.com' +# +# --- INCOMING_EMAIL_DOMAIN: 'localhost' -# An optional prefix to help you distinguish FOI requests, e.g. 'foi+' -INCOMING_EMAIL_PREFIX: '' +# An optional prefix to help you distinguish FOI requests. +# +# INCOMING_EMAIL_PREFIX - String (default: foi+) +# +# Examples: +# +# INCOMING_EMAIL_PREFIX: '' # No prefix +# INCOMING_EMAIL_PREFIX: 'alaveteli+' +# +# --- +INCOMING_EMAIL_PREFIX: 'foi+' -# used for hash in request email address +# Used for hash in request email address. +# +# INCOMING_EMAIL_SECRET - String (default: dummysecret) +# +# Examples: +# +# INCOMING_EMAIL_SECRET: '11ae 4e3b 70ff c001 3682 4a51 e86d ef5f' +# +# --- INCOMING_EMAIL_SECRET: 'xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx' -# used as envelope from at the incoming email domain for cases where we don't care about failure +# Used as envelope from at the incoming email domain for cases where you don't +# care about failure. +# +# BLACKHOLE_PREFIX - String (default: do-not-reply-to-this-address) +# +# Examples: +# +# BLACKHOLE_PREFIX: 'do-not-reply-to-this-address' +# BLACKHOLE_PREFIX: 'do-not-reply' +# +# --- BLACKHOLE_PREFIX: 'do-not-reply-to-this-address' -## Administration - -# The emergency user +# Emergency admin user login username. YOU SHOULD CHANGE THIS. +# +# ADMIN_USERNAME - String (default: nil) +# +# Examples: +# +# ADMIN_USERNAME: 'admin-alaveteli' +# +# --- ADMIN_USERNAME: 'adminxxxx' + +# Emergency admin user login password. YOU SHOULD CHANGE THIS. +# +# ADMIN_USERNAME - String (default: nil) +# +# Examples: +# +# ADMIN_PASSWORD: 'b38bCHBl;28' +# +# --- ADMIN_PASSWORD: 'passwordx' + +# Disable the emergency admin user? +# +# DISABLE_EMERGENCY_USER - Boolean (default: false) +# +# --- DISABLE_EMERGENCY_USER: false -# Set this to true, and the admin interface will be available to anonymous users +# Set this to true, and the admin interface will be available to anonymous +# users. Obviously, you should not set this to be true in production +# environments. +# +# SKIP_ADMIN_AUTH - Boolean (default: false) +# +# --- SKIP_ADMIN_AUTH: false -# Email "from" details -CONTACT_EMAIL: 'postmaster@localhost' -CONTACT_NAME: 'Alaveteli Webmaster' +# Email "from" email address +# +# CONTACT_EMAIL: String email address (default: contact@localhost) +# +# --- +CONTACT_EMAIL: 'contact@localhost' + +# Email "from" name +# +# CONTACT_NAME - String contact name (default: Alaveteli) +# +# --- +CONTACT_NAME: 'Alaveteli' -# Email "from" details for track messages -TRACK_SENDER_EMAIL: 'postmaster@localhost' -TRACK_SENDER_NAME: 'Alaveteli Webmaster' +# Email "from" email address for track messages +# +# TRACK_SENDER_EMAIL - String email address (default: contact@localhost) +# +# --- +TRACK_SENDER_EMAIL: 'contact@localhost' -# Where the raw incoming email data gets stored; make sure you back +# Email "from" name for track messages +# +# TRACK_SENDER_NAME - String contact name (default: Alaveteli) +# +# --- +TRACK_SENDER_NAME: 'Alaveteli' + +# Directory where the raw incoming email data gets stored; make sure you back # this up! +# +# RAW_EMAILS_LOCATION - String path (default: files/raw_emails) +# +# --- RAW_EMAILS_LOCATION: 'files/raw_emails' -# Secret key for signing cookie_store sessions +# Secret key for signing cookie_store sessions. Make it long and random. +# +# COOKIE_STORE_SESSION_SECRET - String (default: 'this default is insecure as +# code is open source, please override +# for live sites in config/general; this +# will do for local development') +# +# Examples: +# +# COOKIE_STORE_SESSION_SECRET: 'uIngVC238Jn9NsaQizMNf89pliYmDBFugPjHS2JJmzOp8' +# +# --- COOKIE_STORE_SESSION_SECRET: 'your secret key here, make it long and random' # If present, puts the site in read only mode, and uses the text as reason # (whole paragraph). Please use a read-only database user as well, as it only -# checks in a few obvious places. +# checks in a few obvious places. Typically, you do not want to run your site +# in read-only mode. +# +# READ_ONLY - String (default: nil) +# +# Examples: +# +# READ_ONLY: 'The site is not currently accepting requests while we move the +# server.' +# +# --- READ_ONLY: '' -# Is this a staging or dev site (1) or a live site (0). -# Controls whether or not the rails-post-deploy script -# will create the file config/rails_env.rb file to force -# Rails into production environment. -STAGING_SITE: 1 +# Is this a staging or development site? If not, it's a live production site. +# This setting controls whether or not the rails-post-deploy script will create +# the file config/rails_env.rb file to force Rails into production environment. +# +# STAGING_SITE: Integer in [0, 1] +# +# Examples: +# +# # For staging or development: +# STAGING_SITE: 1 +# +# # For production: +# STAGING_SITE: 0 +# +# --- +STAGING_SITE: 0 -# Recaptcha, for detecting humans. Get keys here: http://recaptcha.net/whyrecaptcha.html +# Recaptcha, for detecting humans. Get keys here: +# http://recaptcha.net/whyrecaptcha.html +# +# RECAPTCHA_PUBLIC_KEY - String (default: 'x') +# +# --- RECAPTCHA_PUBLIC_KEY: 'x' + +# Recaptcha, for detecting humans. Get keys here: +# http://recaptcha.net/whyrecaptcha.html +# +# RECAPTCHA_PRIVATE_KEY - String (default: 'x') +# +# --- RECAPTCHA_PRIVATE_KEY: 'x' # Number of days after which to send a 'new response reminder' +# +# NEW_RESPONSE_REMINDER_AFTER_DAYS – Array of Integers (default: [3, 10, 24]) +# +# Examples: +# +# NEW_RESPONSE_REMINDER_AFTER_DAYS: [3, 7] +# +# --- NEW_RESPONSE_REMINDER_AFTER_DAYS: [3, 10, 24] -# For debugging memory problems. If true, the app logs -# the memory use increase of the Ruby process due to the -# request (Linux only). Since Ruby never returns memory to the OS, if the -# existing process previously served a larger request, this won't -# show any consumption for the later request. +# For debugging memory problems. If true, Alaveteli logs the memory use +# increase of the Ruby process due to the request (Linux only). Since Ruby +# never returns memory to the OS, if the existing process previously served a +# larger request, this won't show any consumption for the later request. +# +# DEBUG_RECORD_MEMORY - Boolean (default: false) +# +# --- DEBUG_RECORD_MEMORY: false -# Currently we default to using pdftk to compress PDFs. You can -# optionally try Ghostscript, which should do a better job of -# compression. Some versions of pdftk are buggy with respect to -# compression, in which case Alaveteli doesn't recompress the PDFs at -# all and logs a warning message "Unable to compress PDF"; which would -# be another reason to try this setting. -USE_GHOSTSCRIPT_COMPRESSION: true +# Currently we default to using pdftk to compress PDFs. You can optionally try +# Ghostscript, which should do a better job of compression. Some versions of +# pdftk are buggy with respect to compression, in which case Alaveteli doesn't +# recompress the PDFs at all and logs a warning message "Unable to compress +# PDF" — which would be another reason to try this setting. +# +# USE_GHOSTSCRIPT_COMPRESSION - Boolean (default: false) +# +# --- +USE_GHOSTSCRIPT_COMPRESSION: false -# mySociety's gazeteer service. Shouldn't change. +# Alateveli uses mySociety's gazeteer service to determine country from +# incoming IP address (this lets us suggest an Alaveteli in the user's country +# if one exists). You shouldn't normally need to change this. +# +# GAZE_URL - String (default: http://gaze.mysociety.org) +# +# Examples: +# +# GAZE_URL: http://gaze.example.org +# +# --- GAZE_URL: http://gaze.mysociety.org -# The email address to which non-bounce responses should be forwarded +# The email address to which non-bounce responses to emails sent out by +# Alaveteli should be forwarded +# +# FORWARD_NONBOUNCE_RESPONSES_TO - String (default: user-support@localhost) +# +# Examples: +# +# FORWARD_NONBOUNCE_RESPONSES_TO: user-support@example.com +# +# --- FORWARD_NONBOUNCE_RESPONSES_TO: user-support@localhost -# Path to a program that converts an HTML page in a file to PDF. It -#should take two arguments: the URL, and a path to an output file. +# Path to a program that converts an HTML page in a file to PDF. Also used to +# download a zip file of all the correspondence for a request. It should take +# two arguments: the URL, and a path to an output file. +# # A static binary of wkhtmltopdf is recommended: # http://code.google.com/p/wkhtmltopdf/downloads/list # If the command is not present, a text-only version will be rendered # instead. +# +# HTML_TO_PDF_COMMAND - String (default: nil) +# +# Examples: +# +# HTML_TO_PDF_COMMAND: /usr/local/bin/wkhtmltopdf +# HTML_TO_PDF_COMMAND: /usr/local/bin/wkhtmltopdf-amd64 +# +# --- HTML_TO_PDF_COMMAND: /usr/local/bin/wkhtmltopdf-amd64 -# Exception notifications -EXCEPTION_NOTIFICATIONS_FROM: do-not-reply-to-this-address@example.com +# Email address used for sending exception notifications. +# +# EXCEPTION_NOTIFICATIONS_FROM - String (default: nil) +# +# Examples: +# +# EXCEPTION_NOTIFICATIONS_FROM: do-not-reply-to-this-address@example.com +# +# --- +EXCEPTION_NOTIFICATIONS_FROM: do-not-reply-to-this-address@localhost + +# Email address(es) used for receiving exception notifications. +# +# EXCEPTION_NOTIFICATIONS_TO - Array of Strings (default: nil) +# +# Examples: +# +# EXCEPTION_NOTIFICATIONS_TO: +# - robin@example.com +# - seb@example.com +# +# --- EXCEPTION_NOTIFICATIONS_TO: - - robin@example.org - - seb@example.org + - alaveteli@localhost # This rate limiting can be turned off per-user via the admin interface +# +# MAX_REQUESTS_PER_USER_PER_DAY - Integer (default: 6) +# +# Examples: +# +# MAX_REQUESTS_PER_USER_PER_DAY: 1 +# MAX_REQUESTS_PER_USER_PER_DAY: '' # No limit +# +# --- MAX_REQUESTS_PER_USER_PER_DAY: 6 +# If you're running behind Varnish set this to work out where to send purge +# requests. Otherwise, don't set it. +# +# VARNISH_HOST - String (default: nil) +# +# Examples: +# +# VARNISH_HOST: localhost +# +# --- +VARNISH_HOST: null -# This is used to work out where to send purge requests. Should be -# unset if you aren't running behind varnish -VARNISH_HOST: localhost - -# Adding a value here will enable Google Analytics on all non-admin pages for non-admin users. +# Adding a value here will enable Google Analytics on all non-admin pages for +# non-admin users. +# +# GA_CODE - String (default: nil) +# +# Examples: +# +# GA_CODE: 'AB-8222142-14' +# +# --- GA_CODE: '' -# If you want to override *all* the public body request emails with your own -# email so that request emails that would normally go to the public body -# go to you, then uncomment below and fill in your email. -# Useful for a staging server to play with the whole process of sending requests -# without inadvertently sending an email to a real authority -#OVERRIDE_ALL_PUBLIC_BODY_REQUEST_EMAILS: test-email@foo.com +# If you want to override all the public body request emails with your own +# email address so that request emails that would normally go to the public +# body go to you, use this setting. This is useful for a staging server, so you +# can play with the whole process of sending requests without inadvertently +# sending an email to a real authority. +# +# OVERRIDE_ALL_PUBLIC_BODY_REQUEST_EMAILS - String (default: nil) +# +# Examples: +# +# OVERRIDE_ALL_PUBLIC_BODY_REQUEST_EMAILS: test-email@example.com +# +# --- +# OVERRIDE_ALL_PUBLIC_BODY_REQUEST_EMAILS: test-email@example.com -# Search path for external commandline utilities (such as pdftohtml, pdftk, unrtf) +# Search path for external commandline utilities (such as pdftohtml, pdftk, +# unrtf) +# +# UTILITY_SEARCH_PATH - Array of Strings +# (default: ["/usr/bin", "/usr/local/bin"]) +# +# Examples: +# +# UTILITY_SEARCH_PATH: ["/usr/bin"] +# UTILITY_SEARCH_PATH: ["/usr/local/bin", "/opt/bin"] +# +# --- UTILITY_SEARCH_PATH: ["/usr/bin", "/usr/local/bin"] -# Path to your exim or postfix log files that will get sucked up by script/load-mail-server-logs +# Path to your exim or postfix log files that will get sucked up by +# script/load-mail-server-logs +# +# MTA_LOG_PATH - String (default: /var/log/exim4/exim-mainlog-*) +# +# Examples: +# +# MTA_LOG_PATH: '/var/log/exim4/exim-mainlog-*' +# +# --- MTA_LOG_PATH: '/var/log/exim4/exim-mainlog-*' -# Whether we are using "exim" or "postfix" for our MTA -MTA_LOG_TYPE: "exim" +# Are you using "exim" or "postfix" for your Mail Transfer Agent (MTA)? +# +# MTA_LOG_TYPE - String (default: exim) +# +# Examples: +# +# MTA_LOG_TYPE: exim +# MTA_LOG_TYPE: postfix +# +# --- +MTA_LOG_TYPE: exim # URL where people can donate to the organisation running the site. If set, # this will be included in the message people see when their request is # successful. +# +# DONATION_URL - String (default: nil) +# +# Examples: +# +# DONATION_URL: http://www.mysociety.org/donate +# +# --- DONATION_URL: "http://www.mysociety.org/donate/" -# If you set this to 'true' then a page of statistics on the -# performance of public bodies will be available: +# If PUBLIC_BODY_STATISTICS_PAGE is set to true, Alaveteli will make a page of +# statistics on the performance of public bodies (which you can see at +# /body_statistics). +# +# PUBLIC_BODY_STATISTICS_PAGE - Boolean (default: false) +# +# --- PUBLIC_BODY_STATISTICS_PAGE: false # The page of statistics for public bodies will only consider public -# bodies that have had at least this number of requests: -MINIMUM_REQUESTS_FOR_STATISTICS: 50 +# bodies that have had at least the number of requests set by +# MINIMUM_REQUESTS_FOR_STATISTICS. +# +# MINIMUM_REQUESTS_FOR_STATISTICS - Integer (default: 100) +# +# --- +MINIMUM_REQUESTS_FOR_STATISTICS: 100 -# If only some of the public bodies have been translated into every -# available locale, you can allow a fallback to the default locale for -# listing of public bodies. +# If you would like the public body list page to include bodies that have no +# translation in the current locale (but which do have a translation in the +# default locale), set this to true. +# +# PUBLIC_BODY_LIST_FALLBACK_TO_DEFAULT_LOCALE - Boolean (default: false) +# +# --- PUBLIC_BODY_LIST_FALLBACK_TO_DEFAULT_LOCALE: false # If true, while in development mode, try to send mail by SMTP to port -# 1025 (the port the mailcatcher listens on by default): +# 1025 (the port the mailcatcher listens on by default) +# +# USE_MAILCATCHER_IN_DEVELOPMENT - Boolean (default: true) +# +# --- USE_MAILCATCHER_IN_DEVELOPMENT: true -# Use memcached to cache HTML fragments for better performance. Will +# Use memcached to cache HTML fragments for better performance. This will # only have an effect in environments where # config.action_controller.perform_caching is set to true +# +# CACHE_FRAGMENTS - Boolean (default: true) +# +# --- CACHE_FRAGMENTS: true -# The default bundle path is vendor/bundle; you can set this option to -# change it. +# The default bundle path is vendor/bundle; you can set this option to change it +# +# BUNDLE_PATH - String +# +# Examples: +# +# BUNDLE_PATH: vendor/bundle +# BUNDLE_PATH: /var/alaveteli/bundle +# +# --- BUNDLE_PATH: vendor/bundle # In some deployments of Alaveteli you may wish to install each newly # deployed version alongside the previous ones, in which case certain -# files and resources should be shared between these installations: -# for example, the 'files' directory, the 'cache' directory and the +# files and resources should be shared between these installations. +# For example, the 'files' directory, the 'cache' directory and the # generated graphs such as 'public/foi-live-creation.png'. If you're # installing Alaveteli in such a setup then set SHARED_FILES_PATH to -# the directory you're keeping these files under. Otherwise, leave it +# the directory you're keeping these files under. Otherwise, leave it # blank. +# +# SHARED_FILES_PATH - String +# +# Examples: +# +# SHARED_FILES_PATH: /var/www/alaveteli/shared +# +# --- SHARED_FILES_PATH: '' # If you have SHARED_FILES_PATH set, then these options list the files -# and directories that are shared; i.e. those that the deploy scripts -# should create symlinks to from the repository. +# that are shared; i.e. those that the deploy scripts should create symlinks to +# from the repository. +# +# SHARED_FILES - Array of Strings +# +# Examples: +# +# SHARED_FILES: +# - config/database.yml +# - config/general.yml +# +# --- SHARED_FILES: - - config/database.yml - - config/general.yml - - config/rails_env.rb - - config/newrelic.yml - - config/httpd.conf - - public/foi-live-creation.png - - public/foi-user-use.png - - config/aliases + - config/database.yml + - config/general.yml + - config/rails_env.rb + - config/newrelic.yml + - config/httpd.conf + - public/foi-live-creation.png + - public/foi-user-use.png + - config/aliases + +# If you have SHARED_FILES_PATH set, then these options list the directories +# that are shared; i.e. those that the deploy scripts should create symlinks to +# from the repository. +# +# SHARED_DIRECTORIES - Array of Strings +# +# Examples: +# +# SHARED_DIRECTORIES: +# - files/ +# - cache/ +# +# --- SHARED_DIRECTORIES: - - files/ - - cache/ - - lib/acts_as_xapian/xapiandbs/ - - log/ - - tmp/pids - - vendor/bundle - - public/assets + - files/ + - cache/ + - lib/acts_as_xapian/xapiandbs/ + - log/ + - tmp/pids + - vendor/bundle + - public/assets # Allow some users to make batch requests to multiple authorities. Once # this is set to true, you can enable batch requests for an individual # user via the user admin page. - +# +# ALLOW_BATCH_REQUESTS - Boolean (default: false) +# +# --- ALLOW_BATCH_REQUESTS: false -# Should we use the responsive stylesheets? +# Use the responsive base stylesheets and templates, rather than those that +# only render the site at a fixed width. They allow the site to render nicely +# on mobile devices as well as larger screens. Set this to false if you want to +# continue using fixed width stylesheets. +# +# RESPONSIVE_STYLING - Boolean (default: true) +# +# --- RESPONSIVE_STYLING: true diff --git a/doc/CHANGES.md b/doc/CHANGES.md index 237355c1d..748b37665 100644 --- a/doc/CHANGES.md +++ b/doc/CHANGES.md @@ -1,3 +1,14 @@ +# rails-3-develop + +## Highlighted Features + +## Upgrade Notes + +* `CensorRule` now validates the presence of all attributes at the model layer, + rather than only as a database constraint. If you have added a `CensorRule` in + your theme, you will now have to satisfy the additional validations on the + `:replacement`, `:last_edit_comment` and `:last_edit_editor` attributes. + # Version 0.19 ## Highlighted Features diff --git a/lib/configuration.rb b/lib/configuration.rb index bd2d31ac2..2144f9954 100644 --- a/lib/configuration.rb +++ b/lib/configuration.rb @@ -42,11 +42,12 @@ module AlaveteliConfiguration :HTML_TO_PDF_COMMAND => '', :INCLUDE_DEFAULT_LOCALE_IN_URLS => true, :INCOMING_EMAIL_DOMAIN => 'localhost', - :INCOMING_EMAIL_PREFIX => '', + :INCOMING_EMAIL_PREFIX => 'foi+', :INCOMING_EMAIL_SECRET => 'dummysecret', :ISO_COUNTRY_CODE => 'GB', :MINIMUM_REQUESTS_FOR_STATISTICS => 100, - :MAX_REQUESTS_PER_USER_PER_DAY => '', + :MAX_REQUESTS_PER_USER_PER_DAY => 6, + :MTA_LOG_PATH => '/var/log/exim4/exim-mainlog-*', :MTA_LOG_TYPE => 'exim', :NEW_RESPONSE_REMINDER_AFTER_DAYS => [3, 10, 24], :OVERRIDE_ALL_PUBLIC_BODY_REQUEST_EMAILS => '', diff --git a/spec/models/censor_rule_spec.rb b/spec/models/censor_rule_spec.rb index 5b41cc0d4..4ecd2d3e1 100644 --- a/spec/models/censor_rule_spec.rb +++ b/spec/models/censor_rule_spec.rb @@ -90,17 +90,32 @@ end describe 'when validating rules' do - it 'should be invalid without text' do + it 'must have the text to redact' do censor_rule = CensorRule.new - censor_rule.valid?.should == false - censor_rule.errors[:text].should == ["can't be blank"] + expect(censor_rule).to have(1).error_on(:text) + expect(censor_rule.errors[:text]).to eql(["can't be blank"]) + end + + it 'must have a replacement' do + expect(CensorRule.new).to have(1).error_on(:replacement) + end + + it 'must have a last_edit_editor' do + expect(CensorRule.new).to have(1).error_on(:last_edit_editor) + end + + it 'must have a last_edit_comment' do + expect(CensorRule.new).to have(1).error_on(:last_edit_comment) end describe 'when validating a regexp rule' do before do @censor_rule = CensorRule.new(:regexp => true, - :text => '*') + :text => '*', + :replacement => '---', + :last_edit_comment => 'test', + :last_edit_editor => 'rspec') end it 'should try to create a regexp from the text' do @@ -133,7 +148,10 @@ describe 'when validating rules' do describe 'when the allow_global flag has been set' do before do - @censor_rule = CensorRule.new(:text => 'some text') + @censor_rule = CensorRule.new(:text => 'some text', + :replacement => '---', + :last_edit_comment => 'test', + :last_edit_editor => 'rspec') @censor_rule.allow_global = true end @@ -146,7 +164,10 @@ describe 'when validating rules' do describe 'when the allow_global flag has not been set' do before do - @censor_rule = CensorRule.new(:text => '/./') + @censor_rule = CensorRule.new(:text => '/./', + :replacement => '---', + :last_edit_comment => 'test', + :last_edit_editor => 'rspec') end it 'should not allow a global text censor rule (without user_id, request_id or public_body_id)' do diff --git a/spec/models/public_body_spec.rb b/spec/models/public_body_spec.rb index a7544c218..225958cac 100644 --- a/spec/models/public_body_spec.rb +++ b/spec/models/public_body_spec.rb @@ -546,6 +546,58 @@ CSV errors.should include("error: line 3: Url name URL name is already taken for authority 'Foobar Test'") end + it 'has a default list of fields to import' do + expected_fields = [ + ['name', '(i18n)<strong>Existing records cannot be renamed</strong>'], + ['short_name', '(i18n)'], + ['request_email', '(i18n)'], + ['notes', '(i18n)'], + ['publication_scheme', '(i18n)'], + ['disclosure_log', '(i18n)'], + ['home_page', ''], + ['tag_string', '(tags separated by spaces)'], + ] + + expect(PublicBody.csv_import_fields).to eq(expected_fields) + end + + it 'allows you to override the default list of fields to import' do + old_csv_import_fields = PublicBody.csv_import_fields.clone + expected_fields = [ + ['name', '(i18n)<strong>Existing records cannot be renamed</strong>'], + ['short_name', '(i18n)'], + ] + + PublicBody.csv_import_fields = expected_fields + + expect(PublicBody.csv_import_fields).to eq(expected_fields) + + # Reset our change so that we don't affect other specs + PublicBody.csv_import_fields = old_csv_import_fields + end + + it 'allows you to append to the default list of fields to import' do + old_csv_import_fields = PublicBody.csv_import_fields.clone + expected_fields = [ + ['name', '(i18n)<strong>Existing records cannot be renamed</strong>'], + ['short_name', '(i18n)'], + ['request_email', '(i18n)'], + ['notes', '(i18n)'], + ['publication_scheme', '(i18n)'], + ['disclosure_log', '(i18n)'], + ['home_page', ''], + ['tag_string', '(tags separated by spaces)'], + ['a_new_field', ''], + ] + + PublicBody.csv_import_fields << ['a_new_field', ''] + + expect(PublicBody.csv_import_fields).to eq(expected_fields) + + # Reset our change so that we don't affect other specs + PublicBody.csv_import_fields = old_csv_import_fields + end + end describe PublicBody do |