aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/controllers/track_controller.rb3
-rw-r--r--app/models/public_body.rb2
m---------commonlib0
-rw-r--r--config/initializers/alaveteli.rb2
-rw-r--r--config/initializers/theme_loader.rb3
-rw-r--r--lib/actionmailer_patches.rb15
-rw-r--r--lib/tasks/import.rake78
-rw-r--r--lib/tasks/stats.rake5
-rw-r--r--lib/tasks/themes.rake2
-rw-r--r--lib/theme.rb3
-rwxr-xr-xscript/switch-theme.rb120
-rw-r--r--spec/controllers/track_controller_spec.rb33
-rw-r--r--spec/factories.rb7
-rw-r--r--spec/fixtures/files/fake-authority-type.csv2
-rw-r--r--spec/lib/theme_spec.rb25
-rw-r--r--spec/models/public_body_spec.rb25
19 files changed, 313 insertions, 21 deletions
diff --git a/.gitignore b/.gitignore
index f328f6705..d2c256ef1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,7 @@ TAGS
bin/
config/aliases
config/httpd.conf
+config/general*.yml
.sass-cache
alaveteli.sublime*
webrat.log
diff --git a/Gemfile b/Gemfile
index 04fa16eba..b86e04d55 100644
--- a/Gemfile
+++ b/Gemfile
@@ -49,7 +49,7 @@ gem 'globalize3', :git => 'git://github.com/henare/globalize3.git', :branch => '
gem 'locale'
gem 'routing-filter'
gem 'unicode'
-gem 'unidecode'
+gem 'unidecoder'
group :test do
gem 'fakeweb'
diff --git a/Gemfile.lock b/Gemfile.lock
index 4494c2342..9accf0283 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -136,7 +136,7 @@ GEM
net-ssh (2.6.7)
net-ssh-gateway (1.2.0)
net-ssh (>= 2.6.5)
- newrelic_rpm (3.6.2.96)
+ newrelic_rpm (3.6.8.164)
nokogiri (1.5.9)
paper_trail (2.7.2)
activerecord (~> 3.0)
@@ -236,7 +236,7 @@ GEM
polyglot (>= 0.3.1)
tzinfo (0.3.37)
unicode (0.4.4)
- unidecode (1.0.0)
+ unidecoder (1.1.2)
vpim (0.695)
webrat (0.7.3)
nokogiri (>= 1.2.0)
@@ -293,7 +293,7 @@ DEPENDENCIES
statistics2 (~> 0.54)
syslog_protocol
unicode
- unidecode
+ unidecoder
vpim
webrat
will_paginate
diff --git a/app/controllers/track_controller.rb b/app/controllers/track_controller.rb
index 40fa69290..72c092221 100644
--- a/app/controllers/track_controller.rb
+++ b/app/controllers/track_controller.rb
@@ -181,7 +181,8 @@ class TrackController < ApplicationController
if new_medium == 'delete'
track_thing.destroy
flash[:notice] = _("You are no longer following {{track_description}}.", :track_description => track_thing.params[:list_description])
- redirect_to params[:r]
+ redirect_to URI.parse(params[:r]).path
+
# Reuse code like this if we let medium change again.
#elsif new_medium == 'email_daily'
# track_thing.track_medium = new_medium
diff --git a/app/models/public_body.rb b/app/models/public_body.rb
index 485a794b0..9adcdc4a0 100644
--- a/app/models/public_body.rb
+++ b/app/models/public_body.rb
@@ -407,6 +407,8 @@ class PublicBody < ActiveRecord::Base
fields = {}
field_names.each{|name, i| fields[name] = row[i]}
+ yield line, fields if block_given?
+
name = row[field_names['name']]
email = row[field_names['request_email']]
next if name.nil?
diff --git a/commonlib b/commonlib
-Subproject 9462a28fe12b25637d6e67d7140d444632e3ff7
+Subproject 77a6b09daa5da3808be4431799521f8bee5ab21
diff --git a/config/initializers/alaveteli.rb b/config/initializers/alaveteli.rb
index 8ae78c80c..5171c052f 100644
--- a/config/initializers/alaveteli.rb
+++ b/config/initializers/alaveteli.rb
@@ -50,6 +50,8 @@ require 'normalize_string'
require 'alaveteli_file_types'
require 'alaveteli_localization'
require 'message_prominence'
+require 'actionmailer_patches'
+require 'theme'
AlaveteliLocalization.set_locales(AlaveteliConfiguration::available_locales,
AlaveteliConfiguration::default_locale)
diff --git a/config/initializers/theme_loader.rb b/config/initializers/theme_loader.rb
index 1ad2d01f1..b3ae11e1e 100644
--- a/config/initializers/theme_loader.rb
+++ b/config/initializers/theme_loader.rb
@@ -18,7 +18,6 @@ if Rails.env == "test"
end
else
for url in AlaveteliConfiguration::theme_urls.reverse
- theme_name = url.sub(/.*\/(.*).git/, "\\1")
- require_theme(theme_name)
+ require_theme theme_url_to_theme_name(url)
end
end
diff --git a/lib/actionmailer_patches.rb b/lib/actionmailer_patches.rb
new file mode 100644
index 000000000..600d3c8cc
--- /dev/null
+++ b/lib/actionmailer_patches.rb
@@ -0,0 +1,15 @@
+# Monkey patch for CVE-2013-4389
+# derived from http://seclists.org/oss-sec/2013/q4/118 to fix
+# a possible DoS vulnerability in the log subscriber component of
+# Action Mailer.
+
+require 'action_mailer'
+module ActionMailer
+ class LogSubscriber < ActiveSupport::LogSubscriber
+ def deliver(event)
+ recipients = Array.wrap(event.payload[:to]).join(', ')
+ info("\nSent mail to #{recipients} (#{event.duration.round(1)}ms)")
+ debug(event.payload[:mail])
+ end
+ end
+end
diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake
new file mode 100644
index 000000000..0e8397fde
--- /dev/null
+++ b/lib/tasks/import.rake
@@ -0,0 +1,78 @@
+require 'csv'
+require 'tempfile'
+
+namespace :import do
+
+ desc 'Import public bodies from CSV provided on standard input'
+ task :import_csv => :environment do
+ dryrun = ENV['DRYRUN'] != '0'
+ if dryrun
+ STDERR.puts "Only a dry run; public bodies will not be created"
+ end
+
+ tmp_csv = nil
+ Tempfile.open('alaveteli') do |f|
+ f.write STDIN.read
+ tmp_csv = f
+ end
+
+ number_of_rows = 0
+
+ STDERR.puts "Preliminary check for ambiguous names or slugs..."
+
+ # Check that the name and slugified version of the name are
+ # unique:
+ url_part_count = Hash.new { 0 }
+ name_count = Hash.new { 0 }
+ reader = CSV.open tmp_csv.path, 'r'
+ header_line = reader.shift
+ headers = header_line.collect { |h| h.gsub /^#/, ''}
+
+ reader.each do |row_array|
+ row = Hash[headers.zip row_array]
+ name = row['name']
+ url_part = MySociety::Format::simplify_url_part name, "body"
+ name_count[name] += 1
+ url_part_count[url_part] += 1
+ number_of_rows += 1
+ end
+
+ non_unique_error = false
+
+ [[name_count, 'name'],
+ [url_part_count, 'url_part']].each do |counter, field|
+ counter.sort.map do |name, count|
+ if count > 1
+ non_unique_error = true
+ STDERR.puts "The #{field} #{name} was found #{count} times."
+ end
+ end
+ end
+
+ next if non_unique_error
+
+ STDERR.puts "Now importing the public bodies..."
+
+ # Now it's (probably) safe to try to import:
+ errors, notes = PublicBody.import_csv(tmp_csv.path,
+ tag='',
+ tag_behaviour='replace',
+ dryrun,
+ editor="#{ENV['USER']} (Unix user)",
+ I18n.available_locales) do |row_number, fields|
+ percent_complete = (100 * row_number.to_f / number_of_rows).to_i
+ STDERR.print "#{row_number} out of #{number_of_rows} "
+ STDERR.puts "(#{percent_complete}% complete)"
+ end
+
+ if errors.length > 0
+ STDERR.puts "Import failed, with the following errors:"
+ errors.each do |error|
+ STDERR.puts " #{error}"
+ end
+ else
+ STDERR.puts "Done."
+ end
+
+ end
+end
diff --git a/lib/tasks/stats.rake b/lib/tasks/stats.rake
index 4eda27289..eb36204c6 100644
--- a/lib/tasks/stats.rake
+++ b/lib/tasks/stats.rake
@@ -94,7 +94,7 @@ namespace :stats do
desc 'Update statistics in the public_bodies table'
task :update_public_bodies_stats => :environment do
verbose = ENV['VERBOSE'] == '1'
- PublicBody.all.each do |public_body|
+ PublicBody.find_each(:batch_size => 10) do |public_body|
puts "Counting overdue requests for #{public_body.name}" if verbose
# Look for values of 'waiting_response_overdue' and
@@ -102,7 +102,8 @@ namespace :stats do
# described_state column, and instead need to be calculated:
overdue_count = 0
very_overdue_count = 0
- InfoRequest.find_each(:conditions => {:public_body_id => public_body.id}) do |ir|
+ InfoRequest.find_each(:batch_size => 200,
+ :conditions => {:public_body_id => public_body.id}) do |ir|
case ir.calculate_status
when 'waiting_response_very_overdue'
very_overdue_count += 1
diff --git a/lib/tasks/themes.rake b/lib/tasks/themes.rake
index a8d16f108..1eed92f1e 100644
--- a/lib/tasks/themes.rake
+++ b/lib/tasks/themes.rake
@@ -85,7 +85,7 @@ namespace :themes do
def install_theme(theme_url, verbose, deprecated=false)
deprecation_string = deprecated ? " using deprecated THEME_URL" : ""
- theme_name = File.basename(theme_url, '.git')
+ theme_name = theme_url_to_theme_name theme_url
puts "Installing theme #{theme_name}#{deprecation_string} from #{theme_url}"
uninstall(theme_name, verbose) if installed?(theme_name)
install_theme_using_git(theme_name, theme_url, verbose)
diff --git a/lib/theme.rb b/lib/theme.rb
new file mode 100644
index 000000000..4f03b5d99
--- /dev/null
+++ b/lib/theme.rb
@@ -0,0 +1,3 @@
+def theme_url_to_theme_name(theme_url)
+ File.basename theme_url, '.git'
+end
diff --git a/script/switch-theme.rb b/script/switch-theme.rb
new file mode 100755
index 000000000..47f81c7a8
--- /dev/null
+++ b/script/switch-theme.rb
@@ -0,0 +1,120 @@
+#!/usr/bin/env ruby
+# -*- coding: utf-8 -*-
+
+# A simple script to swap around your Alaveteli themes when you're
+# hacking on Alaveteli. By default this assumes that you have an
+# 'alaveteli-themes' directory at the same level as your alaveteli git
+# repository, e.g.:
+#
+# alaveteli
+# ├── app
+# ├── cache
+# ...
+# └── vendor
+# alaveteli-themes/
+# ├── alavetelitheme
+# ├── asktheeu-theme
+# ├── chiediamo-theme
+# ├── ipvtheme
+# ├── queremossabertheme
+# ├── tuderechoasaber-theme
+# ├── whatdotheyknow-theme
+# └── yourrighttoknow
+#
+# However, you can override the location of your themes directory with
+# the environment variable ALAVETELI_THEMES_DIR.
+#
+# You need to have a corresponding general.yml file called, for example:
+#
+# config/general-whatdotheyknow-theme.yml
+# config/general-yourrighttoknow.yml
+
+require 'tempfile'
+
+theme_directory = ENV['ALAVETELI_THEMES_DIR']
+alaveteli_directory = File.expand_path(File.join(File.dirname(__FILE__),
+ ".."))
+unless theme_directory
+ theme_directory = File.expand_path File.join(alaveteli_directory,
+ '..',
+ 'alaveteli-themes')
+end
+
+unless File.exists? theme_directory
+ STDERR.puts "The theme directory '#{theme_directory}' didn't exist."
+ exit 1
+end
+
+# Assume that any directory directly under theme_directory is a theme:
+$available_themes = Dir.entries(theme_directory).find_all do |local_theme_name|
+ next if [".", ".."].index local_theme_name
+ next unless local_theme_name
+ full_path = File.join theme_directory, local_theme_name
+ next unless File.directory? full_path
+ next unless File.directory? File.join(full_path, '.git')
+ local_theme_name
+end
+
+if $available_themes.empty?
+ STDERR.puts "There were no theme directories found in '#{theme_directory}'"
+ exit
+end
+
+def usage_and_exit
+ STDERR.puts "Usage: #{$0} <THEME-NAME>"
+ $available_themes.sort.each do |theme_name|
+ STDERR.puts " #{theme_name}"
+ end
+ exit 1
+end
+
+usage_and_exit unless ARGV.length == 1
+requested_theme = ARGV[0]
+usage_and_exit unless $available_themes.include? requested_theme
+
+full_theme_path = File.join theme_directory, requested_theme
+
+config_directory = File.join alaveteli_directory, 'config'
+general_filename = File.join config_directory, "general.yml"
+theme_filename = File.join config_directory, "general-#{requested_theme}.yml"
+
+if File.exists?(general_filename) && ! (File.symlink? general_filename)
+ STDERR.puts "'#{general_filename}' exists, but isn't a symlink"
+ exit 1
+end
+
+unless File.exists? theme_filename
+ STDERR.puts "'#{theme_filename}' didn't exist"
+ exit 1
+end
+
+def symlink target, link_directory, link_name
+ tmp = Tempfile.new link_name, link_directory
+ if system("ln", "-sfn", target, tmp.path)
+ full_link_name = File.join(link_directory, link_name)
+ begin
+ File.rename tmp.path, full_link_name
+ rescue Errno::EISDIR
+ STDERR.puts "Couldn't overwrite #{full_link_name} since it's a directory"
+ exit 1
+ end
+ else
+ STDERR.puts "Failed to create a symlink from #{tmp.path} to #{target}"
+ exit 1
+ end
+end
+
+symlink(File.basename(theme_filename),
+ config_directory,
+ "general.yml")
+
+symlink(File.join(full_theme_path, 'public'),
+ File.join(alaveteli_directory, 'public'),
+ 'alavetelitheme')
+
+symlink(full_theme_path,
+ File.join(alaveteli_directory, 'vendor', 'plugins'),
+ requested_theme)
+
+STDERR.puts """Switched to #{requested_theme}!
+You will need to restart any development server you have running."""
diff --git a/spec/controllers/track_controller_spec.rb b/spec/controllers/track_controller_spec.rb
index a16024828..57d084f6b 100644
--- a/spec/controllers/track_controller_spec.rb
+++ b/spec/controllers/track_controller_spec.rb
@@ -55,6 +55,39 @@ describe TrackController, "when making a new track on a request" do
end
+describe TrackController, "when unsubscribing from a track" do
+
+ before do
+ @track_thing = FactoryGirl.create(:track_thing)
+ end
+
+ it 'should destroy the track thing' do
+ get :update, {:track_id => @track_thing.id,
+ :track_medium => 'delete',
+ :r => 'http://example.com'},
+ {:user_id => @track_thing.tracking_user.id}
+ TrackThing.find(:first, :conditions => ['id = ? ', @track_thing.id]).should == nil
+ end
+
+ it 'should redirect to a URL on the site' do
+ get :update, {:track_id => @track_thing.id,
+ :track_medium => 'delete',
+ :r => '/'},
+ {:user_id => @track_thing.tracking_user.id}
+ response.should redirect_to('/')
+ end
+
+ it 'should not redirect to a url on another site' do
+ track_thing = FactoryGirl.create(:track_thing)
+ get :update, {:track_id => @track_thing.id,
+ :track_medium => 'delete',
+ :r => 'http://example.com/'},
+ {:user_id => @track_thing.tracking_user.id}
+ response.should redirect_to('/')
+ end
+
+end
+
describe TrackController, "when sending alerts for a track" do
render_views
diff --git a/spec/factories.rb b/spec/factories.rb
index 653525920..7d8f94ac1 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -137,4 +137,11 @@ FactoryGirl.define do
last_edit_comment "Making an edit"
end
+ factory :track_thing do
+ association :tracking_user, :factory => :user
+ track_medium 'email_daily'
+ track_type 'search_query'
+ track_query 'Example Query'
+ end
+
end
diff --git a/spec/fixtures/files/fake-authority-type.csv b/spec/fixtures/files/fake-authority-type.csv
index cb25050c6..a320941c7 100644
--- a/spec/fixtures/files/fake-authority-type.csv
+++ b/spec/fixtures/files/fake-authority-type.csv
@@ -2,3 +2,5 @@
,Scottish Fake Authority,scottish_foi@localhost
,Fake Authority of Northern Ireland,ni_foi@localhost
,Gobierno de Aragón,spain_foi@localhost
+,Nordic æøå,no_foi@localhost
+
diff --git a/spec/lib/theme_spec.rb b/spec/lib/theme_spec.rb
new file mode 100644
index 000000000..829c1a269
--- /dev/null
+++ b/spec/lib/theme_spec.rb
@@ -0,0 +1,25 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe "theme_url_to_theme_name" do
+
+ it "should deal with a typical bare repo URL" do
+ url = 'git://wherever/blah-theme.git'
+ theme_url_to_theme_name(url).should == 'blah-theme'
+ end
+
+ it "should deal with a typical bare repo URL with trailing slashes" do
+ url = 'ssh://wherever/blah-theme.git//'
+ theme_url_to_theme_name(url).should == 'blah-theme'
+ end
+
+ it "should deal with a typical non-bare repo URL" do
+ url = '/home/whoever/themes/blah-theme'
+ theme_url_to_theme_name(url).should == 'blah-theme'
+ end
+
+ it "should deal with a typical non-bare repo URL with a trailing slash" do
+ url = '/home/whoever/themes/blah-theme/'
+ theme_url_to_theme_name(url).should == 'blah-theme'
+ end
+
+end
diff --git a/spec/models/public_body_spec.rb b/spec/models/public_body_spec.rb
index 0324e3f5a..7a2c60722 100644
--- a/spec/models/public_body_spec.rb
+++ b/spec/models/public_body_spec.rb
@@ -320,14 +320,15 @@ describe PublicBody, " when loading CSV files" do
csv_contents = normalize_string_to_utf8(load_file_fixture("fake-authority-type.csv"))
errors, notes = PublicBody.import_csv(csv_contents, '', 'replace', true, 'someadmin') # true means dry run
errors.should == []
- notes.size.should == 5
- notes[0..3].should == [
+ notes.size.should == 6
+ notes[0..4].should == [
"line 1: creating new authority 'North West Fake Authority' (locale: en):\n\t\{\"name\":\"North West Fake Authority\",\"request_email\":\"north_west_foi@localhost\"\}",
"line 2: creating new authority 'Scottish Fake Authority' (locale: en):\n\t\{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\"\}",
"line 3: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t\{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\"\}",
"line 4: creating new authority 'Gobierno de Aragón' (locale: en):\n\t\{\"name\":\"Gobierno de Arag\\u00f3n\",\"request_email\":\"spain_foi@localhost\"}",
+ "line 5: creating new authority 'Nordic æøå' (locale: en):\n\t{\"name\":\"Nordic \\u00e6\\u00f8\\u00e5\",\"request_email\":\"no_foi@localhost\"}"
]
- notes[4].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
+ notes[5].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
PublicBody.count.should == original_count
end
@@ -338,16 +339,17 @@ describe PublicBody, " when loading CSV files" do
csv_contents = normalize_string_to_utf8(load_file_fixture("fake-authority-type.csv"))
errors, notes = PublicBody.import_csv(csv_contents, '', 'replace', false, 'someadmin') # false means real run
errors.should == []
- notes.size.should == 5
- notes[0..3].should == [
+ notes.size.should == 6
+ notes[0..4].should == [
"line 1: creating new authority 'North West Fake Authority' (locale: en):\n\t\{\"name\":\"North West Fake Authority\",\"request_email\":\"north_west_foi@localhost\"\}",
"line 2: creating new authority 'Scottish Fake Authority' (locale: en):\n\t\{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\"\}",
"line 3: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t\{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\"\}",
"line 4: creating new authority 'Gobierno de Aragón' (locale: en):\n\t\{\"name\":\"Gobierno de Arag\\u00f3n\",\"request_email\":\"spain_foi@localhost\"}",
+ "line 5: creating new authority 'Nordic æøå' (locale: en):\n\t{\"name\":\"Nordic \\u00e6\\u00f8\\u00e5\",\"request_email\":\"no_foi@localhost\"}"
]
- notes[4].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
+ notes[5].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
- PublicBody.count.should == original_count + 4
+ PublicBody.count.should == original_count + 5
end
it "should do imports without a tag successfully" do
@@ -356,15 +358,16 @@ describe PublicBody, " when loading CSV files" do
csv_contents = normalize_string_to_utf8(load_file_fixture("fake-authority-type.csv"))
errors, notes = PublicBody.import_csv(csv_contents, '', 'replace', false, 'someadmin') # false means real run
errors.should == []
- notes.size.should == 5
- notes[0..3].should == [
+ notes.size.should == 6
+ notes[0..4].should == [
"line 1: creating new authority 'North West Fake Authority' (locale: en):\n\t\{\"name\":\"North West Fake Authority\",\"request_email\":\"north_west_foi@localhost\"\}",
"line 2: creating new authority 'Scottish Fake Authority' (locale: en):\n\t\{\"name\":\"Scottish Fake Authority\",\"request_email\":\"scottish_foi@localhost\"\}",
"line 3: creating new authority 'Fake Authority of Northern Ireland' (locale: en):\n\t\{\"name\":\"Fake Authority of Northern Ireland\",\"request_email\":\"ni_foi@localhost\"\}",
"line 4: creating new authority 'Gobierno de Aragón' (locale: en):\n\t\{\"name\":\"Gobierno de Arag\\u00f3n\",\"request_email\":\"spain_foi@localhost\"}",
+ "line 5: creating new authority 'Nordic æøå' (locale: en):\n\t{\"name\":\"Nordic \\u00e6\\u00f8\\u00e5\",\"request_email\":\"no_foi@localhost\"}"
]
- notes[4].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
- PublicBody.count.should == original_count + 4
+ notes[5].should =~ /Notes: Some bodies are in database, but not in CSV file:\n( .+\n)*You may want to delete them manually.\n/
+ PublicBody.count.should == original_count + 5
end
it "should handle a field list and fields out of order" do