aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--.ruby-version.example (renamed from .ruby-version)0
-rw-r--r--Gemfile13
-rw-r--r--Gemfile.lock18
-rw-r--r--README.md24
-rw-r--r--app/assets/images/icon_application_octet-stream_large.pngbin3184 -> 1315 bytes
-rw-r--r--app/assets/images/icon_application_pdf_large.pngbin2522 -> 849 bytes
-rw-r--r--app/assets/images/icon_application_rtf_large.pngbin2564 -> 818 bytes
-rw-r--r--app/assets/images/icon_application_vnd.ms-excel_large.pngbin2891 -> 848 bytes
-rw-r--r--app/assets/images/icon_application_vnd.ms-powerpoint_large.pngbin2495 -> 894 bytes
-rw-r--r--app/assets/images/icon_application_vnd.ms-word_large.pngbin2775 -> 935 bytes
-rw-r--r--app/assets/images/icon_application_zip_large.pngbin2863 -> 443 bytes
l---------[-rw-r--r--]app/assets/images/icon_image_bmp_large.pngbin2774 -> 24 bytes
l---------[-rw-r--r--]app/assets/images/icon_image_gif_large.pngbin2575 -> 24 bytes
-rw-r--r--app/assets/images/icon_image_img_large.pngbin0 -> 890 bytes
l---------[-rw-r--r--]app/assets/images/icon_image_jpeg_large.pngbin2633 -> 24 bytes
l---------[-rw-r--r--]app/assets/images/icon_image_png_large.pngbin2736 -> 24 bytes
-rw-r--r--app/assets/images/icon_image_tiff_large.pngbin2552 -> 1160 bytes
-rw-r--r--app/assets/images/icon_message_delivery-status_large.pngbin3184 -> 1010 bytes
-rw-r--r--app/assets/images/icon_text_html_large.pngbin3259 -> 1186 bytes
-rw-r--r--app/assets/images/icon_text_plain_large.pngbin2138 -> 400 bytes
-rw-r--r--app/assets/images/icon_text_x-vcard_large.pngbin2779 -> 1059 bytes
-rw-r--r--app/assets/images/icon_unknown.pngbin3604 -> 949 bytes
-rw-r--r--app/assets/images/widget-base.pngbin0 -> 569 bytes
-rw-r--r--app/assets/javascripts/general.js10
-rw-r--r--app/assets/stylesheets/responsive/_global_layout.scss7
-rw-r--r--app/assets/stylesheets/responsive/_global_style.scss3
-rw-r--r--app/assets/stylesheets/responsive/_header_layout.scss16
-rw-r--r--app/assets/stylesheets/responsive/_header_style.scss6
-rw-r--r--app/assets/stylesheets/responsive/_request_style.scss15
-rw-r--r--app/assets/stylesheets/responsive/_utils.scss15
-rw-r--r--app/assets/stylesheets/widget.scss109
-rw-r--r--app/controllers/admin_general_controller.rb3
-rw-r--r--app/controllers/track_controller.rb2
-rw-r--r--app/controllers/widgets_controller.rb63
-rw-r--r--app/helpers/health_checks_helper.rb2
-rw-r--r--app/helpers/widget_helper.rb46
-rw-r--r--app/mailers/request_mailer.rb14
-rw-r--r--app/models/info_request.rb18
-rw-r--r--app/models/public_body.rb17
-rw-r--r--app/models/user.rb1
-rw-r--r--app/models/widget_vote.rb19
-rw-r--r--app/views/admin_public_body/edit.html.erb2
-rw-r--r--app/views/comment/_single_comment.html.erb2
-rw-r--r--app/views/general/_responsive_topnav.html.erb13
-rw-r--r--app/views/help/unhappy.html.erb5
-rw-r--r--app/views/request/_act.html.erb5
-rw-r--r--app/views/request/_followup.html.erb2
-rw-r--r--app/views/request/_incoming_correspondence.html.erb2
-rw-r--r--app/views/request/_outgoing_correspondence.html.erb2
-rw-r--r--app/views/request/_resent_outgoing_correspondence.html.erb2
-rw-r--r--app/views/request/new.html.erb4
-rw-r--r--app/views/request/new_bad_contact.html.erb2
-rw-r--r--app/views/request/show.html.erb4
-rw-r--r--app/views/request_game/play.html.erb7
-rw-r--r--app/views/widgets/new.html.erb15
-rw-r--r--app/views/widgets/show.html.erb48
-rw-r--r--config/.cvsignore3
-rwxr-xr-xconfig/alert-tracks-debian.ugly2
-rw-r--r--config/application.rb1
-rw-r--r--config/deploy.rb2
-rw-r--r--config/environments/production.rb15
-rw-r--r--config/general.yml-example35
-rwxr-xr-xconfig/purge-varnish-debian.ugly2
-rw-r--r--config/routes.rb2
-rw-r--r--config/varnish-alaveteli.vcl16
-rw-r--r--db/migrate/20140824191444_create_widget_votes.rb11
-rw-r--r--doc/CHANGES.md16
-rw-r--r--lib/acts_as_xapian/acts_as_xapian.rb72
-rw-r--r--lib/alaveteli_text_masker.rb35
-rw-r--r--lib/configuration.rb13
-rw-r--r--public/.cvsignore5
-rwxr-xr-xscript/switch-theme.rb2
-rw-r--r--spec/controllers/admin_public_body_categories_controller_spec.rb2
-rw-r--r--spec/controllers/request_controller_spec.rb1
-rw-r--r--spec/controllers/widgets_controller_spec.rb181
-rw-r--r--spec/factories/widget_votes.rb7
-rw-r--r--spec/helpers/health_checks_helper_spec.rb5
-rw-r--r--spec/helpers/widget_helper_spec.rb28
-rw-r--r--spec/integration/admin_public_body_edit_spec.rb2
-rw-r--r--spec/integration/xapian_search_highlighting_spec.rb15
-rw-r--r--spec/lib/alaveteli_text_masker_spec.rb17
-rw-r--r--spec/lib/mail_handler/mail_handler_spec.rb4
-rw-r--r--spec/mailers/request_mailer_spec.rb46
-rw-r--r--spec/models/info_request_batch_spec.rb2
-rw-r--r--spec/models/info_request_spec.rb17
-rw-r--r--spec/models/public_body_spec.rb27
-rw-r--r--spec/models/widget_vote_spec.rb53
-rw-r--r--spec/spec_helper.rb7
-rw-r--r--spec/views/public_body/show.html.erb_spec.rb1
90 files changed, 1012 insertions, 176 deletions
diff --git a/.gitignore b/.gitignore
index 994f9a3a1..78a06c661 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,9 @@
._*
.DS_Store
.autotest
+.ruby-version
+.rbenv-version
+.rvmrc
*#*#
TAGS
/lib/themes
@@ -25,7 +28,7 @@ config/httpd.conf
config/general*.yml
config/deploy.yml.*
.sass-cache
-alaveteli.sublime*
+alaveteli*.sublime*
webrat.log
/.rbenv-version
/db/development_structure.sql
diff --git a/.ruby-version b/.ruby-version.example
index 7fa1d1ef4..7fa1d1ef4 100644
--- a/.ruby-version
+++ b/.ruby-version.example
diff --git a/Gemfile b/Gemfile
index eca861681..c4ac74273 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,7 +5,7 @@ gem 'rails', '3.2.21'
gem 'pg', '~> 0.17.1'
# New gem releases aren't being done. master is newer and supports Rails > 3.0
-gem 'acts_as_versioned', :git => 'git://github.com/technoweenie/acts_as_versioned.git', :ref => '63b1fc8529d028'
+gem 'acts_as_versioned', :git => 'https://github.com/technoweenie/acts_as_versioned.git', :ref => '63b1fc8529d028'
gem 'charlock_holmes', '~> 0.6.9.4'
gem 'dynamic_form', '~> 1.1.4'
gem 'exception_notification', '~> 3.0.1'
@@ -15,7 +15,7 @@ gem 'icalendar', '1.4.3'
gem 'jquery-rails', '~> 3.0.4'
gem 'jquery-ui-rails', '~> 4.1.0'
gem 'json', '~> 1.8.1'
-gem 'holidays', '~> 1.0.8'
+gem 'holidays', '~> 1.2.0'
gem 'iso_country_codes', '~> 0.6.1'
gem 'mahoro', '~> 0.4'
gem 'memcache-client', '~> 1.8.5'
@@ -26,9 +26,8 @@ gem 'rack', '~> 1.4.5'
gem 'rake', '0.9.2.2'
gem 'rails-i18n', '~> 0.7.3'
gem 'recaptcha', '~> 0.3.1', :require => 'recaptcha/rails'
-# :require avoids "already initialized constant" warnings
-gem 'rmagick', '~> 2.13.2', :require => 'RMagick'
-gem 'ruby-msg', '~> 1.5.0', :git => 'git://github.com/mysociety/ruby-msg.git'
+gem 'rmagick', '~> 2.14.0'
+gem 'ruby-msg', '~> 1.5.0', :git => 'https://github.com/mysociety/ruby-msg.git'
gem 'secure_headers', '~> 1.3.4'
gem 'statistics2', '~> 0.54'
gem 'syslog_protocol', '~> 0.9.2'
@@ -36,7 +35,7 @@ gem 'thin', '~> 1.5.1'
gem 'vpim', '~> 13.11.11'
gem 'will_paginate', '~> 3.0.5'
# when 1.2.9 is released by the maintainer, we can stop using this fork:
-gem 'xapian-full-alaveteli', '~> 1.2.9.5'
+gem 'xapian-full-alaveteli', '~> 1.2.9.7'
gem 'xml-simple', '~> 1.1.2', :require => 'xmlsimple'
gem 'zip', '~> 2.0.2'
@@ -44,7 +43,7 @@ gem 'zip', '~> 2.0.2'
gem 'fast_gettext', '~> 0.7.0'
gem 'gettext_i18n_rails', '~> 0.9.4'
gem 'gettext', '~> 2.3.9'
-gem 'globalize3', :git => 'git://github.com/globalize/globalize.git', :ref => '5fd95f2389dff1'
+gem 'globalize3', :git => 'https://github.com/globalize/globalize.git', :ref => '5fd95f2389dff1'
gem 'locale', '~> 2.0.8'
gem 'routing-filter', '~> 0.3.1'
gem 'unicode', '~> 0.4.4'
diff --git a/Gemfile.lock b/Gemfile.lock
index 2f88a474e..f00c26061 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,5 +1,5 @@
GIT
- remote: git://github.com/globalize/globalize.git
+ remote: https://github.com/globalize/globalize.git
revision: 5fd95f2389dff13c9368fb2e08c96c8a48798c72
ref: 5fd95f2389dff1
specs:
@@ -9,7 +9,7 @@ GIT
paper_trail (~> 2)
GIT
- remote: git://github.com/mysociety/ruby-msg.git
+ remote: https://github.com/mysociety/ruby-msg.git
revision: ee0086add16c755d2eaf8dbcb90ba65809061cef
specs:
ruby-msg (1.5.2)
@@ -17,7 +17,7 @@ GIT
vpim (>= 0.360)
GIT
- remote: git://github.com/technoweenie/acts_as_versioned.git
+ remote: https://github.com/technoweenie/acts_as_versioned.git
revision: 63b1fc8529d028fae632fe80ec0cb25df56cd76b
ref: 63b1fc8529d028
specs:
@@ -127,7 +127,7 @@ GEM
tilt
highline (1.6.19)
hike (1.2.3)
- holidays (1.0.8)
+ holidays (1.2.0)
i18n (0.6.11)
icalendar (1.4.3)
iso_country_codes (0.6.1)
@@ -216,7 +216,7 @@ GEM
ref (1.0.5)
rest-client (1.6.7)
mime-types (>= 1.16)
- rmagick (2.13.2)
+ rmagick (2.14.0)
routing-filter (0.3.1)
actionpack
rspec-core (2.13.1)
@@ -291,7 +291,7 @@ GEM
rack (>= 1.0)
rack-test (>= 0.5.3)
will_paginate (3.0.5)
- xapian-full-alaveteli (1.2.9.5)
+ xapian-full-alaveteli (1.2.9.7)
xml-simple (1.1.2)
zip (2.0.2)
@@ -318,7 +318,7 @@ DEPENDENCIES
gettext (~> 2.3.9)
gettext_i18n_rails (~> 0.9.4)
globalize3!
- holidays (~> 1.0.8)
+ holidays (~> 1.2.0)
icalendar (= 1.4.3)
iso_country_codes (~> 0.6.1)
jquery-rails (~> 3.0.4)
@@ -342,7 +342,7 @@ DEPENDENCIES
rake (= 0.9.2.2)
rdoc (~> 3.12.2)
recaptcha (~> 0.3.1)
- rmagick (~> 2.13.2)
+ rmagick (~> 2.14.0)
routing-filter (~> 0.3.1)
rspec-rails (~> 2.13.2)
ruby-debug (~> 0.10.4)
@@ -360,6 +360,6 @@ DEPENDENCIES
vpim (~> 13.11.11)
webrat (~> 0.7.3)
will_paginate (~> 3.0.5)
- xapian-full-alaveteli (~> 1.2.9.5)
+ xapian-full-alaveteli (~> 1.2.9.7)
xml-simple (~> 1.1.2)
zip (~> 2.0.2)
diff --git a/README.md b/README.md
index de025dcb2..d3d08f718 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,22 @@ wiki](https://github.com/mysociety/alaveteli/wiki/Home/), and upgrade
notes in the [`doc/`
folder](https://github.com/mysociety/alaveteli/tree/master/doc/CHANGES.md)
+## Installing
+
+We've been working hard to make Alaveteli easy to install and re-use anywhere. Please
+see [the project website](http://alaveteli.org) for instructions on installing Alaveteli.
+
+## Compatibility
+
+Every Alaveteli commit is tested by Travis on the [following Ruby platforms](https://github.com/mysociety/alaveteli/blob/master/.travis.yml#L7)
+
+* ruby-1.8.7
+* ruby-1.9.3
+* ruby-2.0.0
+
+
+If you use a ruby version management tool (such as RVM or .rbenv) and want to use the default development version used by the alaveteli team (currently 2.0.0), you can create a `.ruby-version` symlink with a target of `.ruby-version.example` to switch to that automatically in the project directory.
+
## How to contribute
If you find what looks like a bug:
@@ -44,3 +60,11 @@ If you want to contribute an enhancement or a fix:
Looking for the latest stable release? It's on the
[master branch](https://github.com/mysociety/alaveteli/tree/master).
+We have some more notes for developers [on the project site](http://alaveteli.org/docs/developers/).
+
+## Examples
+
+* [WhatDoTheyKnow](https://www.whatdotheyknow.com)
+* [KiMitTud](http://kimittud.atlatszo.hu)
+* [Informace Pro Všechny](http://www.infoprovsechny.cz)
+* [fyi.org.nz](https://fyi.org.nz)
diff --git a/app/assets/images/icon_application_octet-stream_large.png b/app/assets/images/icon_application_octet-stream_large.png
index a239862e1..a23916e9c 100644
--- a/app/assets/images/icon_application_octet-stream_large.png
+++ b/app/assets/images/icon_application_octet-stream_large.png
Binary files differ
diff --git a/app/assets/images/icon_application_pdf_large.png b/app/assets/images/icon_application_pdf_large.png
index 9a38ca33c..990b96c0a 100644
--- a/app/assets/images/icon_application_pdf_large.png
+++ b/app/assets/images/icon_application_pdf_large.png
Binary files differ
diff --git a/app/assets/images/icon_application_rtf_large.png b/app/assets/images/icon_application_rtf_large.png
index 2ad990608..977972124 100644
--- a/app/assets/images/icon_application_rtf_large.png
+++ b/app/assets/images/icon_application_rtf_large.png
Binary files differ
diff --git a/app/assets/images/icon_application_vnd.ms-excel_large.png b/app/assets/images/icon_application_vnd.ms-excel_large.png
index 3f346f5ef..acca5d92c 100644
--- a/app/assets/images/icon_application_vnd.ms-excel_large.png
+++ b/app/assets/images/icon_application_vnd.ms-excel_large.png
Binary files differ
diff --git a/app/assets/images/icon_application_vnd.ms-powerpoint_large.png b/app/assets/images/icon_application_vnd.ms-powerpoint_large.png
index 82c225059..9a1582930 100644
--- a/app/assets/images/icon_application_vnd.ms-powerpoint_large.png
+++ b/app/assets/images/icon_application_vnd.ms-powerpoint_large.png
Binary files differ
diff --git a/app/assets/images/icon_application_vnd.ms-word_large.png b/app/assets/images/icon_application_vnd.ms-word_large.png
index 91a696ab5..2f3cb8efa 100644
--- a/app/assets/images/icon_application_vnd.ms-word_large.png
+++ b/app/assets/images/icon_application_vnd.ms-word_large.png
Binary files differ
diff --git a/app/assets/images/icon_application_zip_large.png b/app/assets/images/icon_application_zip_large.png
index 0a14e978e..c52d6d5aa 100644
--- a/app/assets/images/icon_application_zip_large.png
+++ b/app/assets/images/icon_application_zip_large.png
Binary files differ
diff --git a/app/assets/images/icon_image_bmp_large.png b/app/assets/images/icon_image_bmp_large.png
index f6e8dbaed..347bdaaf1 100644..120000
--- a/app/assets/images/icon_image_bmp_large.png
+++ b/app/assets/images/icon_image_bmp_large.png
Binary files differ
diff --git a/app/assets/images/icon_image_gif_large.png b/app/assets/images/icon_image_gif_large.png
index 424d1e0fd..347bdaaf1 100644..120000
--- a/app/assets/images/icon_image_gif_large.png
+++ b/app/assets/images/icon_image_gif_large.png
Binary files differ
diff --git a/app/assets/images/icon_image_img_large.png b/app/assets/images/icon_image_img_large.png
new file mode 100644
index 000000000..e19e7553c
--- /dev/null
+++ b/app/assets/images/icon_image_img_large.png
Binary files differ
diff --git a/app/assets/images/icon_image_jpeg_large.png b/app/assets/images/icon_image_jpeg_large.png
index fd50a889d..347bdaaf1 100644..120000
--- a/app/assets/images/icon_image_jpeg_large.png
+++ b/app/assets/images/icon_image_jpeg_large.png
Binary files differ
diff --git a/app/assets/images/icon_image_png_large.png b/app/assets/images/icon_image_png_large.png
index f16edb08e..347bdaaf1 100644..120000
--- a/app/assets/images/icon_image_png_large.png
+++ b/app/assets/images/icon_image_png_large.png
Binary files differ
diff --git a/app/assets/images/icon_image_tiff_large.png b/app/assets/images/icon_image_tiff_large.png
index 356f63478..000bd0318 100644
--- a/app/assets/images/icon_image_tiff_large.png
+++ b/app/assets/images/icon_image_tiff_large.png
Binary files differ
diff --git a/app/assets/images/icon_message_delivery-status_large.png b/app/assets/images/icon_message_delivery-status_large.png
index a239862e1..dccdbbccd 100644
--- a/app/assets/images/icon_message_delivery-status_large.png
+++ b/app/assets/images/icon_message_delivery-status_large.png
Binary files differ
diff --git a/app/assets/images/icon_text_html_large.png b/app/assets/images/icon_text_html_large.png
index 914502cf4..3813d2582 100644
--- a/app/assets/images/icon_text_html_large.png
+++ b/app/assets/images/icon_text_html_large.png
Binary files differ
diff --git a/app/assets/images/icon_text_plain_large.png b/app/assets/images/icon_text_plain_large.png
index f74a997ba..f15b0dbdc 100644
--- a/app/assets/images/icon_text_plain_large.png
+++ b/app/assets/images/icon_text_plain_large.png
Binary files differ
diff --git a/app/assets/images/icon_text_x-vcard_large.png b/app/assets/images/icon_text_x-vcard_large.png
index cc44d3edc..804066af8 100644
--- a/app/assets/images/icon_text_x-vcard_large.png
+++ b/app/assets/images/icon_text_x-vcard_large.png
Binary files differ
diff --git a/app/assets/images/icon_unknown.png b/app/assets/images/icon_unknown.png
index 992c646c0..9a06d9baa 100644
--- a/app/assets/images/icon_unknown.png
+++ b/app/assets/images/icon_unknown.png
Binary files differ
diff --git a/app/assets/images/widget-base.png b/app/assets/images/widget-base.png
new file mode 100644
index 000000000..872244543
--- /dev/null
+++ b/app/assets/images/widget-base.png
Binary files differ
diff --git a/app/assets/javascripts/general.js b/app/assets/javascripts/general.js
index 002eef760..639a6917b 100644
--- a/app/assets/javascripts/general.js
+++ b/app/assets/javascripts/general.js
@@ -34,12 +34,12 @@ $(document).ready(function() {
box.width(location.length + " em");
box.find('input').val(location).attr('size', location.length + " em");
box.show();
- box.find('input').select();
box.position({
my: "right center",
at: "left bottom",
of: this,
collision: "fit" });
+ box.find('input').select();
return false;
});
@@ -57,4 +57,12 @@ $(document).ready(function() {
$('#everypage').hide();
}
+ // "Create widget" page
+ $("#widgetbox").select()
+ // Chrome workaround
+ $("widgetbox").mouseup(function() {
+ // Prevent further mouseup intervention
+ $this.unbind("mouseup");
+ return false;
+ });
})
diff --git a/app/assets/stylesheets/responsive/_global_layout.scss b/app/assets/stylesheets/responsive/_global_layout.scss
index d7b24df41..b34a6af74 100644
--- a/app/assets/stylesheets/responsive/_global_layout.scss
+++ b/app/assets/stylesheets/responsive/_global_layout.scss
@@ -98,3 +98,10 @@ textarea{
padding-right: 0.9375em;
}
}
+.box {
+ padding: 1em;
+
+ @include respond-min( $main_menu-mobile_menu_cutoff ){
+ padding: 1.2em;
+ }
+}
diff --git a/app/assets/stylesheets/responsive/_global_style.scss b/app/assets/stylesheets/responsive/_global_style.scss
index 24cddc0d9..27d238962 100644
--- a/app/assets/stylesheets/responsive/_global_style.scss
+++ b/app/assets/stylesheets/responsive/_global_style.scss
@@ -84,9 +84,6 @@ dd {
dt + dd {
margin-top: 0.5em;
- > p {
- margin-top: 0;
- }
}
diff --git a/app/assets/stylesheets/responsive/_header_layout.scss b/app/assets/stylesheets/responsive/_header_layout.scss
index b3103e3a9..7c7bdfe97 100644
--- a/app/assets/stylesheets/responsive/_header_layout.scss
+++ b/app/assets/stylesheets/responsive/_header_layout.scss
@@ -131,25 +131,25 @@
}
form{
@include grid-row;
- padding-right: 1em;
+ padding: 1em 1em 0;
@include lte-ie7 {
display: inline;
}
+ @include respond-min( $main_menu-mobile_menu_cutoff ){
+ padding-top: 0;
+ }
}
input{
- @include grid-column($columns:9);
- margin:0;
+ @include grid-column($columns:10);
+ margin-right:0;
@include lte-ie7 {
width: 10.063em;
}
}
- label{
+ button[type="submit"]{
@include prefix-postfix-base;
- @include grid-column($columns:3,$float:left);
+ @include grid-column($columns:2,$float:right);
border:none;
- img{
- max-width: 100%;
- }
@include lte-ie7 {
width: 2.125em;
}
diff --git a/app/assets/stylesheets/responsive/_header_style.scss b/app/assets/stylesheets/responsive/_header_style.scss
index 9008a73a7..ec1e8ea5c 100644
--- a/app/assets/stylesheets/responsive/_header_style.scss
+++ b/app/assets/stylesheets/responsive/_header_style.scss
@@ -2,3 +2,9 @@
#navigation {
border-bottom: 1px solid #e9e9e9;
}
+
+#navigation_search {
+ button[type="submit"] {
+ background:image-url('/assets/search.png') transparent no-repeat center center;
+ }
+}
diff --git a/app/assets/stylesheets/responsive/_request_style.scss b/app/assets/stylesheets/responsive/_request_style.scss
index e6f36674a..44ca9a288 100644
--- a/app/assets/stylesheets/responsive/_request_style.scss
+++ b/app/assets/stylesheets/responsive/_request_style.scss
@@ -3,12 +3,9 @@
div.correspondence {
border: 1px solid #ccc;
margin: 0 0 1em;
- padding: 1em;
- @include respond-min( $main_menu-mobile_menu_cutoff ){
- padding: 1.5em;
- }
h2 {
+ margin-top: 0;
text-align:right;
font-size:1em;
}
@@ -31,7 +28,6 @@ div.correspondence {
div.comment_in_request {
border: 1px dotted #ccc;
margin:0 0 1em 3em;
- padding:0 0.5em;
h2 {
font-size:1em;
@@ -42,13 +38,9 @@ div.comment_in_request {
}
.event_actions {
+ margin-bottom: 0;
text-align:right;
line-height: 1em;
- margin-bottom: 1em;
-}
-
-.comment_in_request_text {
- margin:0 1.2em 0 0.9em;
}
.user_photo_on_request img {
@@ -64,7 +56,6 @@ div.comment_in_request {
height:36px;
float:left;
vertical-align:middle;
- margin-top: 0.5em;
margin-right:0.5em;
}
@@ -86,11 +77,9 @@ a img.attachment_image {
}
.describe_state_form,#other_recipients {
-
border-radius:3px;
-moz-border-radius:3px;
margin:1em 0;
- padding:0.5em 1em;
}
.describe_state_form {
diff --git a/app/assets/stylesheets/responsive/_utils.scss b/app/assets/stylesheets/responsive/_utils.scss
index 68884fa7a..e19201475 100644
--- a/app/assets/stylesheets/responsive/_utils.scss
+++ b/app/assets/stylesheets/responsive/_utils.scss
@@ -33,3 +33,18 @@ $lte-ie7: false !default;
@content;
}
}
+
+// Hide content visually, but keep it available to screen readers
+// source: http://a11yproject.com/posts/how-to-hide-content/
+.visually-hidden {
+ // http://developer.yahoo.com/blogs/ydn/posts/2012/10/clip-your-hidden-content-for-better-accessibility/
+ position: absolute !important;
+ clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
+ clip: rect(1px, 1px, 1px, 1px);
+ padding:0 !important;
+ border:0 !important;
+ height: 1px !important;
+ width: 1px !important;
+ overflow: hidden;
+}
+body:hover .visually-hidden a, body:hover .visually-hidden input, body:hover .visually-hidden button { display: none !important; }
diff --git a/app/assets/stylesheets/widget.scss b/app/assets/stylesheets/widget.scss
new file mode 100644
index 000000000..a67e1f3f0
--- /dev/null
+++ b/app/assets/stylesheets/widget.scss
@@ -0,0 +1,109 @@
+/* CSS Mini Reset */
+
+html, body, div, form, fieldset, legend, label
+{
+ margin: 0;
+ padding: 0;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+th, td
+{
+ text-align: left;
+ vertical-align: top;
+}
+
+h1, h2, h3, h4, h5, h6, th, td, caption { font-weight:normal; }
+
+img { border: 0; }
+
+body {
+ background-color: #ffffff;
+ color: #333333;
+ padding: 0;
+ margin: 0;
+ font-family: "Helvetica Neue", Arial, Helvetica, Helmet, Freesans, sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1.5em;
+ position: relative;
+ cursor: default;
+ font-size: 1em;
+}
+
+a:hover,
+a:focus,
+a:active {
+ color: #333;
+}
+
+.alaveteli-widget {
+ width: 318px;
+ height: 213px;
+ border: 1px solid #e9e9e9;
+ background: #e9e9e9 url("widget-base.png") top left no-repeat;
+ position: relative;
+ border: 1px solid #eee;
+}
+
+.alaveteli-widget__title {
+ position: absolute;
+ top: 1em;
+ left: 16px;
+ width: 160px;
+ height: 75px;
+ overflow: hidden;
+}
+
+.alaveteli-widget__status {
+ position: absolute;
+ top: 97px;
+ left: 16px;
+ font-weight: bold;
+ text-transform: uppercase;
+}
+
+.alaveteli-widget__status__status-label {
+ margin: 0;
+ font-weight: normal;
+ font-size: 0.875em;
+ line-height: 1.1em;
+ color: #555;
+ text-transform: capitalize;
+}
+
+.alaveteli-widget__left {
+ position: absolute;
+ width: 145px;
+}
+
+.alaveteli-widget__people-count {
+ position: absolute;
+ left: 192px;
+ top: 44px;
+ width: 100px;
+ text-align: center;
+ line-height: 1.3em;
+}
+.alaveteli-widget__count {
+ font-size: 55px;
+ line-height: 55px;
+ text-align: center;
+}
+
+.alaveteli-widget__bottom a {
+ text-decoration: none;
+}
+
+.alaveteli-widget__button {
+ position: absolute;
+ top: 173px;
+ left: 16px;
+ text-align: center;
+}
+
diff --git a/app/controllers/admin_general_controller.rb b/app/controllers/admin_general_controller.rb
index f2414eeab..13edec37d 100644
--- a/app/controllers/admin_general_controller.rb
+++ b/app/controllers/admin_general_controller.rb
@@ -23,8 +23,7 @@ class AdminGeneralController < AdminController
@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")
+ @blank_contacts = PublicBody.where(:request_email => "").order(:updated_at).select { |pb| !pb.defunct? }
@old_unclassified = InfoRequest.find_old_unclassified(:limit => 20,
:conditions => ["prominence = 'normal'"])
@holding_pen_messages = InfoRequest.holding_pen_request.incoming_messages
diff --git a/app/controllers/track_controller.rb b/app/controllers/track_controller.rb
index 4b272797f..8b8055b55 100644
--- a/app/controllers/track_controller.rb
+++ b/app/controllers/track_controller.rb
@@ -214,6 +214,4 @@ class TrackController < ApplicationController
redirect_to URI.parse(params[:r]).path
end
-
end
-
diff --git a/app/controllers/widgets_controller.rb b/app/controllers/widgets_controller.rb
new file mode 100644
index 000000000..0cc1008a1
--- /dev/null
+++ b/app/controllers/widgets_controller.rb
@@ -0,0 +1,63 @@
+# app/controllers/widget_controller.rb:
+# Handle widgets, if enabled
+#
+# Copyright (c) 2014 UK Citizens Online Democracy. All rights reserved.
+# Email: hello@mysociety.org; WWW: http://www.mysociety.org/
+
+require 'securerandom'
+
+class WidgetsController < ApplicationController
+
+ before_filter :check_widget_config, :find_info_request, :check_prominence
+ skip_before_filter :set_x_frame_options_header, :only => [:show]
+
+ def show
+ medium_cache
+ @track_thing = TrackThing.create_track_for_request(@info_request)
+ @status = @info_request.calculate_status
+ @count = @info_request.track_things.count + @info_request.widget_votes.count + 1
+
+ if @user
+ @existing_track = TrackThing.find_existing(@user, @track_thing)
+ end
+ unless @user || cookies[:widget_vote]
+ cookies.permanent[:widget_vote] = SecureRandom.hex(10)
+ end
+ render :action => 'show', :layout => false
+ end
+
+ def new
+ long_cache
+ end
+
+ # Track interest in a request from a non-logged in user
+ def update
+ if !@user && cookies[:widget_vote]
+ @info_request.widget_votes.
+ where(:cookie => cookies[:widget_vote]).
+ first_or_create
+ end
+
+ track_thing = TrackThing.create_track_for_request(@info_request)
+ redirect_to do_track_path(track_thing), status => :temporary_redirect
+ end
+
+ private
+
+ def find_info_request
+ @info_request = InfoRequest.find(params[:request_id])
+ end
+
+ def check_widget_config
+ unless AlaveteliConfiguration::enable_widgets
+ raise ActiveRecord::RecordNotFound.new("Page not enabled")
+ end
+ end
+
+ def check_prominence
+ unless @info_request.prominence == 'normal'
+ render :nothing => true, :status => :forbidden
+ end
+ end
+
+end
diff --git a/app/helpers/health_checks_helper.rb b/app/helpers/health_checks_helper.rb
index f5769a9ba..f9e4d42df 100644
--- a/app/helpers/health_checks_helper.rb
+++ b/app/helpers/health_checks_helper.rb
@@ -1,7 +1,7 @@
module HealthChecksHelper
def check_status(check)
- style = check.ok? ? {} : "color: red"
+ style = check.ok? ? '' : 'color: red'
content_tag(:b, check.message, :style => style)
end
diff --git a/app/helpers/widget_helper.rb b/app/helpers/widget_helper.rb
new file mode 100644
index 000000000..e604954fe
--- /dev/null
+++ b/app/helpers/widget_helper.rb
@@ -0,0 +1,46 @@
+module WidgetHelper
+ def status_description(info_request, status)
+ case status
+ when 'waiting_classification'
+ _('Awaiting classification')
+ when 'waiting_response'
+ _('Awaiting response')
+ when 'waiting_response_overdue'
+ _('Delayed')
+ when 'waiting_response_very_overdue'
+ _('Long overdue')
+ when 'not_held'
+ _('Not held')
+ when 'rejected'
+ _('Rejected')
+ when 'successful'
+ _('Successful')
+ when 'partially_successful'
+ _('Partial success')
+ when 'waiting_clarification'
+ _('Awaiting clarification')
+ when 'gone_postal'
+ _('Handled by post')
+ when 'internal_review'
+ _('Internal review')
+ when 'error_message'
+ _('Delivery error')
+ when 'requires_admin'
+ _('Unusual response')
+ when 'user_withdrawn'
+ _('Withdrawn')
+ when 'attention_requested'
+ _('Needs admin attention')
+ when 'vexatious'
+ _('Vexatious')
+ when 'not_foi'
+ _('Not an FOI request')
+ else
+ if info_request.respond_to?(:theme_display_status)
+ info_request.theme_display_status(status)
+ else
+ _('Unknown')
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/app/mailers/request_mailer.rb b/app/mailers/request_mailer.rb
index c9decc6db..bf04b1ab9 100644
--- a/app/mailers/request_mailer.rb
+++ b/app/mailers/request_mailer.rb
@@ -64,7 +64,7 @@ class RequestMailer < ApplicationMailer
mail(:from => user.name_and_email,
:to => contact_from_name_and_email,
- :subject => _("FOI response requires admin ({{reason}}) - {{title}}", :reason => info_request.described_state, :title => info_request.title).html_safe)
+ :subject => _("FOI response requires admin ({{reason}}) - {{title}}", :reason => info_request.described_state, :title => info_request.title.html_safe))
end
# Tell the requester that a new response has arrived
@@ -80,7 +80,7 @@ class RequestMailer < ApplicationMailer
mail(:from => contact_from_name_and_email,
:to => info_request.user.name_and_email,
- :subject => (_("New response to your FOI request - ") + info_request.title).html_safe,
+ :subject => _("New response to your FOI request - ") + info_request.title.html_safe,
:charset => "UTF-8",
# not much we can do if the user's email is broken
:reply_to => contact_from_name_and_email)
@@ -105,7 +105,7 @@ class RequestMailer < ApplicationMailer
mail(:from => contact_from_name_and_email,
:to => user.name_and_email,
- :subject => (_("Delayed response to your FOI request - ") + info_request.title).html_safe)
+ :subject => _("Delayed response to your FOI request - ") + info_request.title.html_safe)
end
# Tell the requester that the public body is very late in replying
@@ -125,7 +125,7 @@ class RequestMailer < ApplicationMailer
mail(:from => contact_from_name_and_email,
:to => user.name_and_email,
- :subject => (_("You're long overdue a response to your FOI request - ") + info_request.title).html_safe)
+ :subject => _("You're long overdue a response to your FOI request - ") + info_request.title.html_safe)
end
# Tell the requester that they need to say if the new response
@@ -183,7 +183,7 @@ class RequestMailer < ApplicationMailer
mail(:from => contact_from_name_and_email,
:to => info_request.user.name_and_email,
- :subject => (_("Clarify your FOI request - ") + info_request.title).html_safe)
+ :subject => _("Clarify your FOI request - ") + info_request.title.html_safe)
end
# Tell requester that somebody add an annotation to their request
@@ -197,7 +197,7 @@ class RequestMailer < ApplicationMailer
mail(:from => contact_from_name_and_email,
:to => info_request.user.name_and_email,
- :subject => (_("Somebody added a note to your FOI request - ") + info_request.title).html_safe)
+ :subject => _("Somebody added a note to your FOI request - ") + info_request.title.html_safe)
end
def comment_on_alert_plural(info_request, count, earliest_unalerted_comment)
@count, @info_request = count, info_request
@@ -209,7 +209,7 @@ class RequestMailer < ApplicationMailer
mail(:from => contact_from_name_and_email,
:to => info_request.user.name_and_email,
- :subject => (_("Some notes have been added to your FOI request - ") + info_request.title).html_safe)
+ :subject => _("Some notes have been added to your FOI request - ") + info_request.title.html_safe)
end
# Class function, called by script/mailin with all incoming responses.
diff --git a/app/models/info_request.rb b/app/models/info_request.rb
index 245de1e15..f9f6cffa9 100644
--- a/app/models/info_request.rb
+++ b/app/models/info_request.rb
@@ -50,6 +50,7 @@ class InfoRequest < ActiveRecord::Base
has_many :info_request_events, :order => 'created_at'
has_many :user_info_request_sent_alerts
has_many :track_things, :order => 'created_at desc'
+ has_many :widget_votes
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'
@@ -901,21 +902,24 @@ public
# Completely delete this request and all objects depending on it
def fully_destroy
- self.track_things.each do |track_thing|
+ track_things.each do |track_thing|
track_thing.track_things_sent_emails.each { |a| a.destroy }
track_thing.destroy
end
- self.user_info_request_sent_alerts.each { |a| a.destroy }
- self.info_request_events.each do |info_request_event|
+ user_info_request_sent_alerts.each { |a| a.destroy }
+ info_request_events.each do |info_request_event|
info_request_event.track_things_sent_emails.each { |a| a.destroy }
info_request_event.destroy
end
- self.mail_server_logs.each do |mail_server_log|
+ mail_server_logs.each do |mail_server_log|
mail_server_log.destroy
end
- self.outgoing_messages.each { |a| a.destroy }
- self.incoming_messages.each { |a| a.destroy }
- self.destroy
+ outgoing_messages.each { |a| a.destroy }
+ incoming_messages.each { |a| a.destroy }
+ comments.each { |comment| comment.destroy }
+ censor_rules.each{ |censor_rule| censor_rule.destroy }
+
+ destroy
end
# Called by incoming_email - and used to be called to generate separate
diff --git a/app/models/public_body.rb b/app/models/public_body.rb
index 232c0ffa1..5bce8ecc7 100644
--- a/app/models/public_body.rb
+++ b/app/models/public_body.rb
@@ -577,17 +577,11 @@ class PublicBody < ActiveRecord::Base
return self.request_email_domain
end
- # Returns nil if configuration variable not set
- def override_request_email
- e = AlaveteliConfiguration::override_all_public_body_request_emails
- e if e != ""
- end
-
def request_email
- if override_request_email
- override_request_email
- else
+ if AlaveteliConfiguration::override_all_public_body_request_emails.blank? || read_attribute(:request_email).blank?
read_attribute(:request_email)
+ else
+ AlaveteliConfiguration::override_all_public_body_request_emails
end
end
@@ -774,10 +768,7 @@ class PublicBody < ActiveRecord::Base
end
def empty_translation_in_params?(attributes)
- attrs_with_values = attributes.select do |key, value|
- value != '' and key.to_s != 'locale'
- end
- attrs_with_values.empty?
+ attributes.select { |k, v| !v.blank? && k.to_s != 'locale' }.empty?
end
def request_email_if_requestable
diff --git a/app/models/user.rb b/app/models/user.rb
index 920c0da46..1fb5d9139 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -38,6 +38,7 @@ class User < ActiveRecord::Base
has_one :profile_photo
has_many :censor_rules, :order => 'created_at desc'
has_many :info_request_batches, :order => 'created_at desc'
+ has_many :request_classifications
validates_presence_of :email, :message => _("Please enter your email address")
validates_presence_of :name, :message => _("Please enter your name")
diff --git a/app/models/widget_vote.rb b/app/models/widget_vote.rb
new file mode 100644
index 000000000..021c38b30
--- /dev/null
+++ b/app/models/widget_vote.rb
@@ -0,0 +1,19 @@
+# == Schema Information
+#
+# Table name: widget_votes
+#
+# id :integer not null, primary key
+# cookie :string(255)
+# info_request_id :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+class WidgetVote < ActiveRecord::Base
+ belongs_to :info_request
+ validates :info_request, :presence => true
+
+ attr_accessible :cookie
+ validates :cookie, :length => { :is => 20 }
+ validates_uniqueness_of :cookie, :scope => :info_request_id
+end
diff --git a/app/views/admin_public_body/edit.html.erb b/app/views/admin_public_body/edit.html.erb
index dcafbd270..fc9c25e8f 100644
--- a/app/views/admin_public_body/edit.html.erb
+++ b/app/views/admin_public_body/edit.html.erb
@@ -13,7 +13,7 @@
<div class="row">
<div class="span8">
<div class="well">
- <%= link_to 'Show', admin_bodies_path(@public_body), :class => "btn" %>
+ <%= link_to 'Show', admin_body_path(@public_body), :class => "btn" %>
<%= link_to 'List all', admin_bodies_path, :class => "btn" %>
</div>
</div>
diff --git a/app/views/comment/_single_comment.html.erb b/app/views/comment/_single_comment.html.erb
index 07017dabf..badc39d9a 100644
--- a/app/views/comment/_single_comment.html.erb
+++ b/app/views/comment/_single_comment.html.erb
@@ -1,4 +1,4 @@
-<div class="comment_in_request" id="comment-<%=comment.id.to_s%>">
+<div class="comment_in_request box" id="comment-<%=comment.id.to_s%>">
<% if comment.user && comment.user.profile_photo && !@render_to_file %>
<div class="user_photo_on_comment">
<img src="<%= get_profile_photo_url(:url_name => comment.user.url_name) %>" alt="">
diff --git a/app/views/general/_responsive_topnav.html.erb b/app/views/general/_responsive_topnav.html.erb
index 0af6629c8..c99864cab 100644
--- a/app/views/general/_responsive_topnav.html.erb
+++ b/app/views/general/_responsive_topnav.html.erb
@@ -21,11 +21,16 @@
</li>
<li id="navigation_search">
- <form id="navigation_search_form" method="get" action="<%= search_redirect_path %>">
- <label for="navigation_search_button">
- <img src="/assets/search.png" alt="Search:">
+ <form id="navigation_search_form" method="get" action="<%= search_redirect_path %>" role="search">
+ <label class="visually-hidden" for="navigation_search_button">
+ <%= _("Search") %>
</label>
- <%= text_field_tag 'query', params[:query], { :id => "navigation_search_button", :title => "type your search term here" } %>
+ <%= text_field_tag 'query', params[:query], { :id => "navigation_search_button", :type => "search", :placeholder => _("Search"), :title => _("type your search term here") } %>
+ <button type="submit">
+ <span class="visually-hidden">
+ <%= _("Submit Search") %>
+ </span>
+ </button>
</form>
</li>
</ul>
diff --git a/app/views/help/unhappy.html.erb b/app/views/help/unhappy.html.erb
index 79e3f8273..c0444fb54 100644
--- a/app/views/help/unhappy.html.erb
+++ b/app/views/help/unhappy.html.erb
@@ -101,9 +101,8 @@ contact any registered user from their page. There may be an Internet
forum or group that they hang out in. If it is a local matter, use <a
href="http://www.groupsnearyou.com">GroupsNearYou</a> to find such a
forum.</li>
-<li><strong>Start a pledge</strong> on <a href="http://www.pledgebank.com">PledgeBank</a> to get
-others to act together with you. For example, you could arrange a meeting with
-staff from the authority. Or you could form a small local campaigns group.
+<li>You could form a small local campaign group and arrange a meeting
+with staff from the authority.</li>
</ul>
diff --git a/app/views/request/_act.html.erb b/app/views/request/_act.html.erb
index 878cdf4ff..c7bbd287f 100644
--- a/app/views/request/_act.html.erb
+++ b/app/views/request/_act.html.erb
@@ -20,3 +20,8 @@
<% end %>
<%= link_to _("Start your own blog"), "http://wordpress.com/"%>
</div>
+<% if AlaveteliConfiguration::enable_widgets %>
+ <div class="act_link">
+ <%= link_to _("Create a widget for this request"), new_request_widget_path(@info_request) %>
+ </div>
+<% end %>
diff --git a/app/views/request/_followup.html.erb b/app/views/request/_followup.html.erb
index 2643b767f..24cede824 100644
--- a/app/views/request/_followup.html.erb
+++ b/app/views/request/_followup.html.erb
@@ -20,7 +20,7 @@
</h2>
<% end %>
<% if @info_request.who_can_followup_to(incoming_message).count > 0 %>
-<div id="other_recipients">
+<div id="other_recipients" class="box">
<%= _("Don't want to address your message to {{person_or_body}}? You can also write to:", :person_or_body => name_for_followup) %>
<ul>
<% @info_request.who_can_followup_to(incoming_message).each do |name, email, id| %>
diff --git a/app/views/request/_incoming_correspondence.html.erb b/app/views/request/_incoming_correspondence.html.erb
index 70bd25c7f..9d205204e 100644
--- a/app/views/request/_incoming_correspondence.html.erb
+++ b/app/views/request/_incoming_correspondence.html.erb
@@ -1,4 +1,4 @@
-<div class="incoming correspondence <%= incoming_message.prominence %>" id="incoming-<%=incoming_message.id.to_s%>">
+<div class="incoming correspondence box <%= incoming_message.prominence %>" id="incoming-<%=incoming_message.id.to_s%>">
<%- if not incoming_message.user_can_view?(@user) %>
<%= render :partial => 'request/hidden_correspondence', :locals => { :message => incoming_message }%>
<%- else %>
diff --git a/app/views/request/_outgoing_correspondence.html.erb b/app/views/request/_outgoing_correspondence.html.erb
index dced5c94c..3b85cae7f 100644
--- a/app/views/request/_outgoing_correspondence.html.erb
+++ b/app/views/request/_outgoing_correspondence.html.erb
@@ -1,4 +1,4 @@
-<div class="outgoing correspondence" id="outgoing-<%=outgoing_message.id.to_s%>">
+<div class="outgoing correspondence box" id="outgoing-<%=outgoing_message.id.to_s%>">
<%- if not outgoing_message.user_can_view?(@user) %>
<%= render :partial => 'request/hidden_correspondence', :locals => { :message => outgoing_message }%>
<%- else %>
diff --git a/app/views/request/_resent_outgoing_correspondence.html.erb b/app/views/request/_resent_outgoing_correspondence.html.erb
index 17b6b635b..287a5cac5 100644
--- a/app/views/request/_resent_outgoing_correspondence.html.erb
+++ b/app/views/request/_resent_outgoing_correspondence.html.erb
@@ -1,4 +1,4 @@
-<div class="outgoing correspondence" id="outgoing-<%=outgoing_message.id.to_s%>">
+<div class="outgoing correspondence box" id="outgoing-<%=outgoing_message.id.to_s%>">
<h2>
<%= simple_date(info_request_event.created_at) %>
</h2>
diff --git a/app/views/request/new.html.erb b/app/views/request/new.html.erb
index 486a89d45..23f7ad76a 100644
--- a/app/views/request/new.html.erb
+++ b/app/views/request/new.html.erb
@@ -144,7 +144,7 @@
<% 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) %>
+ <%= _("Browse <a href='{{url}}'>other requests</a> for examples of how to word your request.", :url=>request_list_successful_url) %>
<% end %>
</p>
<% end %>
@@ -156,7 +156,7 @@
this website <a href="{{url}}">forever</a>', :url => (help_privacy_path+"#public_request").html_safe)) %>.
</p>
<p>
- <%= raw(_('<a href="{{url}}">Thinking of using a pseudonym?</a>.', :url => (help_privacy_path+"#real_name").html_safe)) %>
+ <%= raw(_('<a href="{{url}}">Thinking of using a pseudonym?</a>', :url => (help_privacy_path+"#real_name").html_safe)) %>
</p>
<% else %>
<p>
diff --git a/app/views/request/new_bad_contact.html.erb b/app/views/request/new_bad_contact.html.erb
index 56f3f4168..f9881b62b 100644
--- a/app/views/request/new_bad_contact.html.erb
+++ b/app/views/request/new_bad_contact.html.erb
@@ -5,6 +5,6 @@
<p><%= _('Unfortunately, we do not have a working {{info_request_law_used_full}}
address for', :info_request_law_used_full => @info_request.law_used_full) %> <%=h @info_request.public_body.name %>. <%= _('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 <a href="{{help_url}}">send it to us</a>.', :help_url => help_contact_path) %>
+to find one, then please <a href="{{help_url}}">send it to us</a>.', :help_url => new_change_request_path(:body => @info_request.public_body.url_name)) %>
</p>
diff --git a/app/views/request/show.html.erb b/app/views/request/show.html.erb
index 78e022aa9..5862413de 100644
--- a/app/views/request/show.html.erb
+++ b/app/views/request/show.html.erb
@@ -22,7 +22,7 @@
<% if ( @update_status || @info_request.awaiting_description ) && ! @info_request.is_external? %>
- <div class="describe_state_form" id="describe_state_form_1">
+ <div class="describe_state_form box" id="describe_state_form_1">
<%= render :partial => 'describe_state', :locals => { :id_suffix => "1" } %>
</div>
<% end %>
@@ -146,7 +146,7 @@
<% end %>
<% if @info_request.awaiting_description && ! @info_request.is_external? %>
- <div class="describe_state_form" id="describe_state_form_2">
+ <div class="describe_state_form box" id="describe_state_form_2">
<%= render :partial => 'describe_state', :locals => { :id_suffix => "2" } %>
</div>
<% end %>
diff --git a/app/views/request_game/play.html.erb b/app/views/request_game/play.html.erb
index 471a0e09e..44fe641f9 100644
--- a/app/views/request_game/play.html.erb
+++ b/app/views/request_game/play.html.erb
@@ -2,7 +2,12 @@
<div id="game_sidebar">
<p style="text-align: center">
- <img width=250 height=125 src="http://chart.apis.google.com/chart?chs=250x125&cht=gom&chd=t:<%=@percentage%>" alt="<%=@percentage%>% of requests have been categorised">
+ <%=
+ image_tag "https://chart.googleapis.com/chart?chs=250x125&cht=gom&chd=t:#{@percentage}",
+ :size => "250x125",
+ :alt => "A chart showing #{@percentage}% of requests have been categorised",
+ :title => "#{@percentage}% of requests have been categorised"
+ %>
<br><%=pluralize(@missing, 'request')%> left to categorise / <%=@total %> total
</p>
diff --git a/app/views/widgets/new.html.erb b/app/views/widgets/new.html.erb
new file mode 100644
index 000000000..c706155a5
--- /dev/null
+++ b/app/views/widgets/new.html.erb
@@ -0,0 +1,15 @@
+<h1>Add a widget</h1>
+
+<p>
+To add a widget for <b><%= @info_request.title %></b>, copy and paste the
+following code to your web page:
+<textarea autofocus readonly rows='4' cols='60' id='widgetbox'>
+<iframe src='<%= request_widget_url(@info_request) %>' width='320' height='215' frameborder='0' marginwidth='0' marginheight='0'></iframe>
+</textarea>
+</p>
+
+<p>
+The widget will look like this:
+<br>
+<iframe src='<%= request_widget_url(@info_request) %>' width='320' height='215' frameborder='0' marginwidth='0' marginheight='0'></iframe>
+</p>
diff --git a/app/views/widgets/show.html.erb b/app/views/widgets/show.html.erb
new file mode 100644
index 000000000..07c7b1908
--- /dev/null
+++ b/app/views/widgets/show.html.erb
@@ -0,0 +1,48 @@
+<head>
+ <%= stylesheet_link_tag "widget" %>
+</head>
+<body>
+ <div class="alaveteli-widget">
+ <div class="alaveteli-widget__top">
+ <div class="alaveteli-widget__left">
+ <div class="alaveteli-widget__title">
+ <%= link_to @info_request.title, request_path(@info_request), :target => "_top" %>
+ </div>
+ <div class="alaveteli-widget__status <%= @status %>">
+ <p class="alaveteli-widget__status__status-label">Status</p>
+ <%= status_description(@info_request, @status) %>
+ </div>
+ </div>
+ <div class="alaveteli-widget__people-count">
+ <%= n_('<div class="alaveteli-widget__count">{{count}}</div> person wants to know', '<div class="alaveteli-widget__count">{{count}}</div> people want to know', @count, :count => @count) %>
+ </div>
+ </div>
+
+
+ <div class="alaveteli-widget__bottom">
+ <% if @info_request.user && @info_request.user == @user %>
+ <div class="alaveteli-widget__button alaveteli-widget__button--down">
+ <%= _('This is your request') %>
+ </div>
+ <% elsif @existing_track %>
+ <a href="<%= url_for :controller => 'track', :action => 'update', :track_id => @existing_track.id, :track_medium => "delete", :r => request.fullpath %>">
+ <div class="alaveteli-widget__button--down">
+ <%= _('You are tracking this request') %>
+ </div>
+ </a>
+ <% else %>
+ <% if @user %>
+ <a href="<%= url_for do_track_path(@track_thing) %>" target="_blank">
+ <div class="alaveteli-widget__button">
+ <%= _('I also want to know!') %>
+ </div>
+ </a>
+ <% else %>
+ <%= form_tag request_widget_url(@info_request), :method => 'put', :target => '_blank' do %>
+ <%= submit_tag _('I also want to know!'), :class => 'alaveteli-widget__button' %>
+ <% end %>
+ <% end %>
+ <% end %>
+ </div>
+ </div>
+</body>
diff --git a/config/.cvsignore b/config/.cvsignore
deleted file mode 100644
index 2539dd3cd..000000000
--- a/config/.cvsignore
+++ /dev/null
@@ -1,3 +0,0 @@
-general
-database.yml
-rails_env.rb
diff --git a/config/alert-tracks-debian.ugly b/config/alert-tracks-debian.ugly
index f1ca68b03..a098bc332 100755
--- a/config/alert-tracks-debian.ugly
+++ b/config/alert-tracks-debian.ugly
@@ -41,7 +41,7 @@ start_daemon() {
}
stop_daemon() {
- /sbin/start-stop-daemon --stop --oknodo --pidfile "$PIDFILE"
+ /sbin/start-stop-daemon --stop --oknodo --retry 5 --pidfile "$PIDFILE"
}
restart() { stop; start; }
diff --git a/config/application.rb b/config/application.rb
index 3c01e26c4..472077f06 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -116,6 +116,7 @@ module Alaveteli
'ie6.css',
'ie7.css',
'bootstrap-dropdown.js',
+ 'widget.css',
'responsive/print.css',
'responsive/application-lte-ie7.css',
'responsive/application-ie8.css']
diff --git a/config/deploy.rb b/config/deploy.rb
index f4a0b536a..d02488bfa 100644
--- a/config/deploy.rb
+++ b/config/deploy.rb
@@ -58,6 +58,7 @@ namespace :deploy do
"#{release_path}/log" => "#{shared_path}/log",
"#{release_path}/tmp/pids" => "#{shared_path}/tmp/pids",
"#{release_path}/lib/acts_as_xapian/xapiandbs" => "#{shared_path}/xapiandbs",
+ "#{release_path}/lib/themes" => "#{shared_path}/themes",
}
# "ln -sf <a> <b>" creates a symbolic link but deletes <b> if it already exists
@@ -70,6 +71,7 @@ namespace :deploy do
run "mkdir -p #{shared_path}/log"
run "mkdir -p #{shared_path}/tmp/pids"
run "mkdir -p #{shared_path}/xapiandbs"
+ run "mkdir -p #{shared_path}/themes"
end
end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index a3e3cebd2..af2ca15b9 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -17,7 +17,20 @@ Alaveteli::Application.configure do
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false
- config.action_mailer.delivery_method = :sendmail # so is queued, rather than giving immediate errors
+
+ config.action_mailer.delivery_method = AlaveteliConfiguration::production_mailer_delivery_method.to_sym
+
+ if AlaveteliConfiguration::production_mailer_delivery_method.to_sym == :smtp
+ config.action_mailer.smtp_settings = {
+ :address => AlaveteliConfiguration::smtp_mailer_address,
+ :port => AlaveteliConfiguration.smtp_mailer_port,
+ :domain => AlaveteliConfiguration.smtp_mailer_domain,
+ :user_name => AlaveteliConfiguration.smtp_mailer_user_name,
+ :password => AlaveteliConfiguration.smtp_mailer_password,
+ :authentication => AlaveteliConfiguration.smtp_mailer_authentication,
+ :enable_starttls_auto => AlaveteliConfiguration.smtp_mailer_enable_starttls_auto
+ }
+ end
config.active_support.deprecation = :notify
diff --git a/config/general.yml-example b/config/general.yml-example
index 8acea374b..df140136c 100644
--- a/config/general.yml-example
+++ b/config/general.yml-example
@@ -775,3 +775,38 @@ ALLOW_BATCH_REQUESTS: false
#
# ---
RESPONSIVE_STYLING: true
+
+# Define the mailer delivery method to be used only in the production environment.
+# By default, use sendmail.
+#
+# The list of accepted options are available in the Rails ActionMailer configuration
+# documentation: http://guides.rubyonrails.org/action_mailer_basics.html#example-action-mailer-configuration
+#
+# The most common alternative is to use 'smtp'
+# If you choose to use an external SMTP service then you will need to also include the SMTP configuration settings.
+#
+# As a string this is coerced into a symbol in config/environments/production.rb
+#
+# PRODUCTION_MAILER_DELIVERY_METHOD - String (default: sendmail)
+#
+# Examples:
+#
+# PRODUCTION_MAILER_DELIVERY_METHOD: smtp
+# SMTP_MAILER_ADDRESS: smtp.gmail.com
+# SMTP_MAILER_PORT: 587
+# SMTP_MAILER_DOMAIN: example.com
+# SMTP_MAILER_USER_NAME: jane322
+# SMTP_MAILER_PASSWORD: supersecretpassword
+# SMTP_MAILER_AUTHENTICATION: 'plain'
+# SMTP_MAILER_ENABLE_STARTTLS_AUTO: true
+# ---
+PRODUCTION_MAILER_DELIVERY_METHOD: sendmail
+
+# If ENABLE_WIDGETS is set to true, Alaveteli will allow the embedding of a
+# 'widget' linking to a request on other sites. This widget will record
+# how many people click on an 'I also want to know' button.
+#
+# ENABLE_WIDGETS - Boolean (default: false)
+#
+# ---
+ENABLE_WIDGETS: false
diff --git a/config/purge-varnish-debian.ugly b/config/purge-varnish-debian.ugly
index dc3f74ff6..457a77ed8 100755
--- a/config/purge-varnish-debian.ugly
+++ b/config/purge-varnish-debian.ugly
@@ -43,7 +43,7 @@ start_daemon() {
}
stop_daemon() {
- /sbin/start-stop-daemon --stop --oknodo --pidfile "$PIDFILE"
+ /sbin/start-stop-daemon --stop --oknodo --retry 5 --pidfile "$PIDFILE"
}
restart() { stop; start; }
diff --git a/config/routes.rb b/config/routes.rb
index c975d6007..7319f92fc 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -65,6 +65,7 @@ Alaveteli::Application.routes.draw do
resources :request, :only => [] do
resource :report, :only => [:new, :create]
+ resource :widget, :only => [:new, :show, :update]
end
resources :info_request_batch, :only => :show
@@ -142,6 +143,7 @@ Alaveteli::Application.routes.draw do
match '/track/update/:track_id' => 'track#update', :as => :update
match '/track/delete_all_type' => 'track#delete_all_type', :as => :delete_all_type
match '/track/feed/:track_id' => 'track#atom_feed', :as => :atom_feed
+ match '/track/widget_vote/:info_request_id' => 'track#widget_vote', :as => :widget_vote
####
#### Help controller
diff --git a/config/varnish-alaveteli.vcl b/config/varnish-alaveteli.vcl
index d3726682c..59d76b0d1 100644
--- a/config/varnish-alaveteli.vcl
+++ b/config/varnish-alaveteli.vcl
@@ -2,10 +2,10 @@
# of Alaveteli. See the vcl(7) man page for details on VCL syntax and
# semantics.
-#
+#
# Default backend definition. Set this to point to your content
# server. In this case, apache + Passenger running on port 80
-#
+#
backend default {
.host = "127.0.0.1";
@@ -32,7 +32,7 @@ sub vcl_recv {
# Sanitise X-Forwarded-For...
remove req.http.X-Forwarded-For;
set req.http.X-Forwarded-For = client.ip;
-
+
# Remove Google Analytics, has_js, and last-seen cookies
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(__[a-z]+|has_js|has_seen_country_message|seen_foi2)=[^;]*", "");
@@ -50,14 +50,14 @@ sub vcl_recv {
remove req.http.Accept-Encoding;
}
}
-
+
# Ignore empty cookies
if (req.http.Cookie ~ "^\s*$") {
remove req.http.Cookie;
}
-
+
if (req.request != "GET" &&
- req.request != "HEAD" &&
+ req.request != "HEAD" &&
req.request != "POST" &&
req.request != "PUT" &&
req.request != "PURGE" &&
@@ -70,9 +70,9 @@ sub vcl_recv {
/* We only deal with GET and HEAD by default, the rest get passed direct to backend */
return (pass);
}
-
+
# Ignore Cookies on images...
- if (req.url ~ "\.(png|gif|jpg|jpeg|swf|css|js|rdf|ico|txt)(\?.*|)$") {
+ if (req.url ~ "\.(png|gif|jpg|jpeg|swf|css|js|rdf|ico)(\?.*|)$") {
remove req.http.Cookie;
return (lookup);
}
diff --git a/db/migrate/20140824191444_create_widget_votes.rb b/db/migrate/20140824191444_create_widget_votes.rb
new file mode 100644
index 000000000..0a7b4856a
--- /dev/null
+++ b/db/migrate/20140824191444_create_widget_votes.rb
@@ -0,0 +1,11 @@
+class CreateWidgetVotes < ActiveRecord::Migration
+ def change
+ create_table :widget_votes do |t|
+ t.string :cookie
+ t.belongs_to :info_request, :null => false
+
+ t.timestamps
+ end
+ add_index :widget_votes, :info_request_id
+ end
+end
diff --git a/doc/CHANGES.md b/doc/CHANGES.md
index a654f3b6a..f4f75ce2b 100644
--- a/doc/CHANGES.md
+++ b/doc/CHANGES.md
@@ -1,3 +1,19 @@
+# rails-3-develop
+
+## Highlighted Features
+* There is experimental support for using an STMP server, rather than sendmail,
+ for outgoing mail. There is not yet any ability to retry if the SMTP server is
+ unavailable.
+* HTML 'widgets' advertising requests can be displayed on other sites in iframes.
+ If 'ENABLE_WIDGETS' is set to true in `general.yml` (the default is false), a link
+ to the widget code will appear in the right hand sidebar of a request page.
+* Capistrano now caches themes (Henare Degan).
+
+## Upgrade Notes
+
+* Capistrano now caches themes in `shared/themes`. Run the `deploy:setup` task
+ to create the shared directory before making a new code deploy.
+
# Version 0.21
## Highlighted Features
diff --git a/lib/acts_as_xapian/acts_as_xapian.rb b/lib/acts_as_xapian/acts_as_xapian.rb
index 6520a20a4..f742bae52 100644
--- a/lib/acts_as_xapian/acts_as_xapian.rb
+++ b/lib/acts_as_xapian/acts_as_xapian.rb
@@ -487,41 +487,37 @@ module ActsAsXapian
# date ranges or similar. Use this for cheap highlighting with
# TextHelper::highlight, and excerpt.
def words_to_highlight(opts = {})
- default_opts = { :include_original => false, :regex => false }
- opts = default_opts.merge(opts)
-
- # Reject all prefixes other than Z, which we know is reserved for stems
- terms = query.terms.reject { |t| t.term.first.match(/^[A-Y]$/) }
- # Collect the stems including the Z prefix
- raw_stems = terms.map { |t| t.term if t.term.start_with?('Z') }.compact.uniq.sort
- # Collect stems, chopping the Z prefix off
- stems = raw_stems.map { |t| t[1..-1] }.compact.sort
- # Collect the non-stem terms
- words = terms.map { |t| t.term unless t.term.start_with?('Z') }.compact.sort
-
- # Add the unstemmed words from the original query
- # Sometimes stems can be unhelpful with the :regex option, for example
- # stemming 'boring' results in us trying to highlight 'bore'.
- if opts[:include_original]
- raw_stems.each do |raw_stem|
- words << ActsAsXapian.query_parser.unstem(raw_stem).uniq
- end
-
- words = words.any? ? words.flatten.uniq : []
- end
-
- if opts[:regex]
- stems.map! { |w| /\b(#{ w })\w*\b/iu }
- words.map! { |w| /\b(#{ w })\b/iu }
- end
-
- if RUBY_VERSION.to_f >= 1.9
- (stems + words).map! do |term|
- term.is_a?(String) ? term.force_encoding('UTF-8') : term
- end
- else
- stems + words
- end
+ default_opts = { :include_original => false, :regex => false }
+ opts = default_opts.merge(opts)
+
+ # Reject all prefixes other than Z, which we know is reserved for stems
+ terms = query.terms.reject { |t| t.term.first.match(/^[A-Y]$/) }
+ # Collect the stems including the Z prefix
+ raw_stems = terms.map { |t| t.term if t.term.start_with?('Z') }.compact.uniq.sort
+ # Collect stems, chopping the Z prefix off
+ stems = raw_stems.map { |t| t[1..-1] }.compact.sort
+ # Collect the non-stem terms
+ words = terms.map { |t| t.term unless t.term.start_with?('Z') }.compact.sort
+
+ # Add the unstemmed words from the original query
+ # Sometimes stems can be unhelpful with the :regex option, for example
+ # stemming 'boring' results in us trying to highlight 'bore'.
+ if opts[:include_original]
+ raw_stems.each do |raw_stem|
+ words << ActsAsXapian.query_parser.unstem(raw_stem).uniq
+ end
+
+ words = words.any? ? words.flatten.uniq : []
+ end
+
+ if opts[:regex]
+ stems.map! { |w| /\b(#{ correctly_encode(w) })\w*\b/iu }
+ words.map! { |w| /\b(#{ correctly_encode(w) })\b/iu }
+ end
+
+ (stems + words).map! do |term|
+ term.is_a?(String) ? correctly_encode(term) : term
+ end
end
# Text for lines in log file
@@ -529,6 +525,12 @@ module ActsAsXapian
"Search: " + self.query_string
end
+ private
+
+ def correctly_encode(w)
+ RUBY_VERSION.to_f >= 1.9 ? w.force_encoding('UTF-8') : w
+ end
+
end
# Search for models which contain theimportant terms taken from a specified
diff --git a/lib/alaveteli_text_masker.rb b/lib/alaveteli_text_masker.rb
index 68ff0d318..5d836f66c 100644
--- a/lib/alaveteli_text_masker.rb
+++ b/lib/alaveteli_text_masker.rb
@@ -28,9 +28,7 @@ module AlaveteliTextMasker
end
def apply_pdf_masks!(text, options = {})
- uncompressed_text = nil
- uncompressed_text = AlaveteliExternalCommand.run("pdftk", "-", "output", "-", "uncompress",
- :stdin_string => text)
+ uncompressed_text = uncompress_pdf(text)
# if we managed to uncompress the PDF...
if !uncompressed_text.blank?
# then censor stuff (making a copy so can compare again in a bit)
@@ -39,19 +37,13 @@ module AlaveteliTextMasker
# if the censor rule removed something...
if censored_uncompressed_text != uncompressed_text
# then use the altered file (recompressed)
- recompressed_text = nil
- if AlaveteliConfiguration::use_ghostscript_compression == true
- command = ["gs", "-sDEVICE=pdfwrite", "-dCompatibilityLevel=1.4", "-dPDFSETTINGS=/screen", "-dNOPAUSE", "-dQUIET", "-dBATCH", "-sOutputFile=-", "-"]
- else
- command = ["pdftk", "-", "output", "-", "compress"]
- end
- recompressed_text = AlaveteliExternalCommand.run(*(command + [{:stdin_string=>censored_uncompressed_text}]))
+ recompressed_text = compress_pdf(censored_uncompressed_text)
if recompressed_text.blank?
# buggy versions of pdftk sometimes fail on
# compression, I don't see it's a disaster in
# these cases to save an uncompressed version?
recompressed_text = censored_uncompressed_text
- logger.warn "Unable to compress PDF; problem with your pdftk version?"
+ Rails.logger.warn "Unable to compress PDF; problem with your pdftk version?"
end
if !recompressed_text.blank?
text.replace recompressed_text
@@ -62,6 +54,27 @@ module AlaveteliTextMasker
private
+ def uncompress_pdf(text)
+ AlaveteliExternalCommand.run("pdftk", "-", "output", "-", "uncompress", :stdin_string => text)
+ end
+
+ def compress_pdf(text)
+ if AlaveteliConfiguration::use_ghostscript_compression
+ command = ["gs",
+ "-sDEVICE=pdfwrite",
+ "-dCompatibilityLevel=1.4",
+ "-dPDFSETTINGS=/screen",
+ "-dNOPAUSE",
+ "-dQUIET",
+ "-dBATCH",
+ "-sOutputFile=-",
+ "-"]
+ else
+ command = ["pdftk", "-", "output", "-", "compress"]
+ end
+ AlaveteliExternalCommand.run(*(command + [ :stdin_string => text ]))
+ end
+
# Replace text in place
def apply_binary_masks!(text, options = {})
# Keep original size, so can check haven't resized it
diff --git a/lib/configuration.rb b/lib/configuration.rb
index 90fd30d5f..c983152e0 100644
--- a/lib/configuration.rb
+++ b/lib/configuration.rb
@@ -32,6 +32,7 @@ module AlaveteliConfiguration
:DISABLE_EMERGENCY_USER => false,
:DOMAIN => 'localhost:3000',
:DONATION_URL => '',
+ :ENABLE_WIDGETS => false,
:EXCEPTION_NOTIFICATIONS_FROM => '',
:EXCEPTION_NOTIFICATIONS_TO => '',
:FORCE_REGISTRATION_ON_NEW_REQUEST => false,
@@ -52,6 +53,7 @@ module AlaveteliConfiguration
:MTA_LOG_TYPE => 'exim',
:NEW_RESPONSE_REMINDER_AFTER_DAYS => [3, 10, 24],
:OVERRIDE_ALL_PUBLIC_BODY_REQUEST_EMAILS => '',
+ :PRODUCTION_MAILER_DELIVERY_METHOD => 'sendmail',
:PUBLIC_BODY_STATISTICS_PAGE => false,
:PUBLIC_BODY_LIST_FALLBACK_TO_DEFAULT_LOCALE => false,
:RAW_EMAILS_LOCATION => 'files/raw_emails',
@@ -63,6 +65,13 @@ module AlaveteliConfiguration
:RESPONSIVE_STYLING => true,
:SITE_NAME => 'Alaveteli',
:SKIP_ADMIN_AUTH => false,
+ :SMTP_MAILER_ADDRESS => 'localhost',
+ :SMTP_MAILER_PORT => 25,
+ :SMTP_MAILER_DOMAIN => '',
+ :SMTP_MAILER_USER_NAME => '',
+ :SMTP_MAILER_PASSWORD => '',
+ :SMTP_MAILER_AUTHENTICATION => 'plain',
+ :SMTP_MAILER_ENABLE_STARTTLS_AUTO => true,
:SPECIAL_REPLY_VERY_LATE_AFTER_DAYS => 60,
:THEME_BRANCH => false,
:THEME_URL => "",
@@ -77,9 +86,9 @@ module AlaveteliConfiguration
:USE_MAILCATCHER_IN_DEVELOPMENT => true,
:UTILITY_SEARCH_PATH => ["/usr/bin", "/usr/local/bin"],
:VARNISH_HOST => '',
- :WORKING_OR_CALENDAR_DAYS => 'working',
+ :WORKING_OR_CALENDAR_DAYS => 'working'
}
- end
+ end
def AlaveteliConfiguration.method_missing(name)
key = name.to_s.upcase
diff --git a/public/.cvsignore b/public/.cvsignore
deleted file mode 100644
index 9fc54a4a6..000000000
--- a/public/.cvsignore
+++ /dev/null
@@ -1,5 +0,0 @@
-down.html
-down.current.html
-foi-live-creation.png
-foi-user-use.png
-google*.html
diff --git a/script/switch-theme.rb b/script/switch-theme.rb
index 980853687..146d1bf0c 100755
--- a/script/switch-theme.rb
+++ b/script/switch-theme.rb
@@ -129,5 +129,5 @@ STDERR.puts """Switched to #{requested_theme}!
You will need to:
1. restart any development server you have running.
2. run: bundle exec rake assets:clean
- 3. run: bundle exec rake assets:precompile
+ 3. run: bundle exec rake assets:precompile (if running in production mode)
"""
diff --git a/spec/controllers/admin_public_body_categories_controller_spec.rb b/spec/controllers/admin_public_body_categories_controller_spec.rb
index 1131b3c0b..c15ee77f1 100644
--- a/spec/controllers/admin_public_body_categories_controller_spec.rb
+++ b/spec/controllers/admin_public_body_categories_controller_spec.rb
@@ -310,7 +310,7 @@ describe AdminPublicBodyCategoriesController do
post :update, :id => category.id,
:public_body_category => category.serializable_hash.except(:title, :description)
- expect(assigns(:tagged_public_bodies)).to eq(expected_bodies)
+ expect(assigns(:tagged_public_bodies)).to match_array(expected_bodies)
end
it "saves edits to a public body category's heading associations" do
diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb
index 02237b29d..c4f3c847e 100644
--- a/spec/controllers/request_controller_spec.rb
+++ b/spec/controllers/request_controller_spec.rb
@@ -2764,4 +2764,3 @@ describe RequestController, "#select_authorities" do
end
end
-
diff --git a/spec/controllers/widgets_controller_spec.rb b/spec/controllers/widgets_controller_spec.rb
new file mode 100644
index 000000000..6a58c7c5c
--- /dev/null
+++ b/spec/controllers/widgets_controller_spec.rb
@@ -0,0 +1,181 @@
+# coding: utf-8
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe WidgetsController do
+
+ include LinkToHelper
+
+ describe "#show" do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ AlaveteliConfiguration.stub!(:enable_widgets).and_return(true)
+ end
+
+ it 'should render the widget template' do
+ get :show, :request_id => @info_request.id
+ expect(response).to render_template('show')
+ end
+
+ it 'should find the info request' do
+ get :show, :request_id => @info_request.id
+ assigns[:info_request].should == @info_request
+ end
+
+ it 'should create a track thing for the request' do
+ get :show, :request_id => @info_request.id
+ assigns[:track_thing].info_request.should == @info_request
+ end
+
+ it 'should assign the request status' do
+ get :show, :request_id => @info_request.id
+ assigns[:status].should == @info_request.calculate_status
+ end
+
+ it 'should not send an x-frame-options header' do
+ get :show, :request_id => @info_request.id
+ response.headers["X-Frame-Options"].should be_nil
+ end
+
+ context 'for a non-logged-in user' do
+
+ context 'if no widget-vote cookie is set' do
+
+ it 'should set a widget-vote cookie' do
+ cookies[:widget_vote].should be_nil
+ get :show, :request_id => @info_request.id
+ cookies[:widget_vote].should_not be_nil
+ end
+
+ end
+
+ end
+
+ context 'when widgets are not enabled' do
+
+ it 'should return a 404' do
+ AlaveteliConfiguration.stub!(:enable_widgets).and_return(false)
+ lambda{ get :show, :request_id => @info_request.id }.should
+ raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ end
+
+ context "when the request's prominence is not 'normal'" do
+
+ it 'should return a 403' do
+ @info_request.prominence = 'hidden'
+ @info_request.save!
+ get :show, :request_id => @info_request.id
+ response.code.should == "403"
+ end
+
+ end
+
+ end
+
+ describe "#new" do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ AlaveteliConfiguration.stub!(:enable_widgets).and_return(true)
+ end
+
+ it 'should render the create widget template' do
+ get :new, :request_id => @info_request.id
+ expect(response).to render_template('new')
+ end
+
+ it 'should find the info request' do
+ get :new, :request_id => @info_request.id
+ assigns[:info_request].should == @info_request
+ end
+
+ context 'when widgets are not enabled' do
+
+ it 'should return a 404' do
+ AlaveteliConfiguration.stub!(:enable_widgets).and_return(false)
+ lambda{ get :new, :request_id => @info_request.id }.should
+ raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ end
+
+ context "when the request's prominence is not 'normal'" do
+
+ it 'should return a 403' do
+ @info_request.prominence = 'hidden'
+ @info_request.save!
+ get :show, :request_id => @info_request.id
+ response.code.should == "403"
+ end
+
+ end
+
+ end
+
+ describe :update do
+
+ before do
+ @info_request = FactoryGirl.create(:info_request)
+ AlaveteliConfiguration.stub!(:enable_widgets).and_return(true)
+ end
+
+ it 'should find the info request' do
+ get :update, :request_id => @info_request.id
+ assigns[:info_request].should == @info_request
+ end
+
+ it 'should redirect to the track path for the info request' do
+ get :update, :request_id => @info_request.id
+ track_thing = TrackThing.create_track_for_request(@info_request)
+ expect(response).to redirect_to(do_track_path(track_thing))
+ end
+
+ context 'when there is no logged-in user and a widget vote cookie' do
+
+ before do
+ @cookie_value = 'x' * 20
+ end
+
+ it 'should create a widget vote if none exists for the info request and cookie' do
+ @info_request.widget_votes.where(:cookie => @cookie_value).size.should == 0
+ request.cookies['widget_vote'] = @cookie_value
+ get :update, :request_id => @info_request.id
+ @info_request.widget_votes.where(:cookie => @cookie_value).size.should == 1
+ end
+
+ it 'should not create a widget vote if one exists for the info request and cookie' do
+ @info_request.widget_votes.create(:cookie => @cookie_value)
+ request.cookies['widget_vote'] = @cookie_value
+ get :update, :request_id => @info_request.id
+ @info_request.widget_votes.where(:cookie => @cookie_value).size.should == 1
+ end
+
+ end
+
+ context 'when widgets are not enabled' do
+
+ it 'should raise ActiveRecord::RecordNotFound' do
+ AlaveteliConfiguration.stub!(:enable_widgets).and_return(false)
+ lambda{ get :update, :request_id => @info_request.id }.should
+ raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ end
+
+ context "when the request's prominence is not 'normal'" do
+
+ it 'should return a 403' do
+ @info_request.prominence = 'hidden'
+ @info_request.save!
+ get :show, :request_id => @info_request.id
+ response.code.should == "403"
+ end
+
+ end
+
+ end
+
+end
+
diff --git a/spec/factories/widget_votes.rb b/spec/factories/widget_votes.rb
new file mode 100644
index 000000000..964bbb20d
--- /dev/null
+++ b/spec/factories/widget_votes.rb
@@ -0,0 +1,7 @@
+require 'securerandom'
+FactoryGirl.define do
+ factory :widget_vote do
+ info_request
+ cookie { SecureRandom.hex(10) }
+ end
+end \ No newline at end of file
diff --git a/spec/helpers/health_checks_helper_spec.rb b/spec/helpers/health_checks_helper_spec.rb
index 7d4083da5..7dbfaf06e 100644
--- a/spec/helpers/health_checks_helper_spec.rb
+++ b/spec/helpers/health_checks_helper_spec.rb
@@ -10,6 +10,11 @@ describe HealthChecksHelper do
expect(check_status(check)).to include('red')
end
+ it 'sets style to a blank string if ok' do
+ check = double(:message => '', :ok? => true)
+ expect(check_status(check)).to include('style=""')
+ end
+
end
end
diff --git a/spec/helpers/widget_helper_spec.rb b/spec/helpers/widget_helper_spec.rb
new file mode 100644
index 000000000..c8c41b14f
--- /dev/null
+++ b/spec/helpers/widget_helper_spec.rb
@@ -0,0 +1,28 @@
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe WidgetHelper do
+
+ include WidgetHelper
+
+ describe :status_description do
+
+ before do
+ @info_request = FactoryGirl.build(:info_request)
+ end
+
+ it 'should return "Awaiting classification" for "waiting_classification' do
+ expect(status_description(@info_request, 'waiting_classification')).to eq('Awaiting classification')
+ end
+
+ it 'should call theme_display_status for a theme status' do
+ @info_request.stub!(:theme_display_status).and_return("Special status")
+ expect(status_description(@info_request, 'special_status')).to eq('Special status')
+ end
+
+ it 'should return unknown for an unknown status' do
+ expect(status_description(@info_request, 'special_status')).to eq('Unknown')
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/spec/integration/admin_public_body_edit_spec.rb b/spec/integration/admin_public_body_edit_spec.rb
index aeec3e65a..21011b172 100644
--- a/spec/integration/admin_public_body_edit_spec.rb
+++ b/spec/integration/admin_public_body_edit_spec.rb
@@ -39,7 +39,7 @@ describe 'Editing a Public Body' do
end
end
- it 'can add a translation for multiple locales', :focus => true do
+ it 'can add a translation for multiple locales' do
@admin.visit edit_admin_body_path(@body)
@admin.fill_in 'public_body_name__en', :with => 'New Quango EN'
@admin.click_button 'Save'
diff --git a/spec/integration/xapian_search_highlighting_spec.rb b/spec/integration/xapian_search_highlighting_spec.rb
index 65a34cf91..a91df341f 100644
--- a/spec/integration/xapian_search_highlighting_spec.rb
+++ b/spec/integration/xapian_search_highlighting_spec.rb
@@ -1,8 +1,14 @@
+# encoding: utf-8
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe 'highlighting search results' do
include HighlightHelper
+ before do
+ get_fixtures_xapian_index
+ end
+
it 'ignores stopwords' do
phrase = 'department of humpadinking'
search = ActsAsXapian::Search.new([PublicBody], phrase, :limit => 1)
@@ -36,4 +42,13 @@ describe 'highlighting search results' do
highlight_matches(phrase, matches).should == '<mark>boring</mark>'
end
+ it 'handles macrons correctly' do
+ phrase = 'Māori'
+
+ search = ActsAsXapian::Search.new([PublicBody], phrase, :limit => 1)
+ matches = search.words_to_highlight(:regex => true, :include_original => true)
+
+ highlight_matches(phrase, matches).should == '<mark>Māori</mark>'
+ end
+
end
diff --git a/spec/lib/alaveteli_text_masker_spec.rb b/spec/lib/alaveteli_text_masker_spec.rb
index 1a4782a83..102d2582e 100644
--- a/spec/lib/alaveteli_text_masker_spec.rb
+++ b/spec/lib/alaveteli_text_masker_spec.rb
@@ -92,6 +92,23 @@ describe AlaveteliTextMasker do
pdf.should_not == ""
end
+ it 'should keep the uncensored original if uncompression of a PDF fails' do
+ orig_pdf = load_file_fixture('tfl.pdf')
+ pdf = orig_pdf.dup
+ stub!(:uncompress_pdf).and_return nil
+ apply_masks!(pdf, "application/pdf")
+ pdf.should == orig_pdf
+ end
+
+ it 'should use the uncompressed PDF text if re-compression of a compressed PDF fails' do
+ orig_pdf = load_file_fixture('tfl.pdf')
+ pdf = orig_pdf.dup
+ stub!(:uncompress_pdf).and_return "something about foi@tfl.gov.uk"
+ stub!(:compress_pdf).and_return nil
+ apply_masks!(pdf, "application/pdf")
+ pdf.should match "something about xxx@xxx.xxx.xx"
+ end
+
it "should apply hard-coded privacy rules to HTML files" do
data = "http://test.host/c/cheese"
apply_masks!(data, 'text/html')
diff --git a/spec/lib/mail_handler/mail_handler_spec.rb b/spec/lib/mail_handler/mail_handler_spec.rb
index e7ad93300..ea7a99b05 100644
--- a/spec/lib/mail_handler/mail_handler_spec.rb
+++ b/spec/lib/mail_handler/mail_handler_spec.rb
@@ -9,7 +9,7 @@ end
describe 'when creating a mail object from raw data' do
- it "should be able to parse a large email without raising an exception", :focus => true do
+ it "should be able to parse a large email without raising an exception" do
m = Mail.new
m.add_file(:filename => "attachment.data", :content => "a" * (8 * 1024 * 1024))
raw_email = "From jamis_buck@byu.edu Mon May 2 16:07:05 2005\r\n#{m.to_s}"
@@ -22,7 +22,7 @@ describe 'when creating a mail object from raw data' do
mail.multipart?.should == true
end
- it "should not fail on invalid byte sequence in content-disposition header", :focus => true do
+ it "should not fail on invalid byte sequence in content-disposition header" do
part = Mail::Part.new("Content-Disposition: inline; filename=a\xB8z\r\n\r\nThis is the body text.")
lambda { part.inline? }.should_not raise_error
end
diff --git a/spec/mailers/request_mailer_spec.rb b/spec/mailers/request_mailer_spec.rb
index 9e98dbc00..6b54c25d2 100644
--- a/spec/mailers/request_mailer_spec.rb
+++ b/spec/mailers/request_mailer_spec.rb
@@ -1,6 +1,8 @@
# encoding: utf-8
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+# TODO: Combine all these separate "describe" blocks to tidy things up
+
describe RequestMailer, " when receiving incoming mail" do
before(:each) do
load_raw_emails_data
@@ -411,6 +413,10 @@ describe RequestMailer, 'when sending a new response email' do
@mail = RequestMailer.new_response(@info_request, @incoming_message)
end
+ it 'should not create HTML entities in the subject line' do
+ mail = RequestMailer.new_response(FactoryGirl.create(:info_request, :title => "Here's a request"), FactoryGirl.create(:incoming_message))
+ expect(mail.subject).to eq "New response to your FOI request - Here's a request"
+ end
end
describe RequestMailer, 'requires_admin' do
@@ -419,7 +425,7 @@ describe RequestMailer, 'requires_admin' do
:name => 'Bruce Jones')
@info_request = mock_model(InfoRequest, :user => user,
:described_state => 'error_message',
- :title => 'Test request',
+ :title => "It's a Test request",
:url_title => 'test_request',
:law_used_short => 'FOI',
:id => 123)
@@ -435,4 +441,42 @@ describe RequestMailer, 'requires_admin' do
mail.body.should include 'Something has gone wrong'
end
+ it 'should not create HTML entities in the subject line' do
+ expect(RequestMailer.requires_admin(@info_request).subject).to eq "FOI response requires admin (error_message) - It's a Test request"
+ end
+end
+
+describe RequestMailer, "overdue_alert" do
+ it 'should not create HTML entities in the subject line' do
+ mail = RequestMailer.overdue_alert(FactoryGirl.create(:info_request, :title => "Here's a request"), FactoryGirl.create(:user))
+ expect(mail.subject).to eq "Delayed response to your FOI request - Here's a request"
+ end
+end
+
+describe RequestMailer, "very_overdue_alert" do
+ it 'should not create HTML entities in the subject line' do
+ mail = RequestMailer.very_overdue_alert(FactoryGirl.create(:info_request, :title => "Here's a request"), FactoryGirl.create(:user))
+ expect(mail.subject).to eq "You're long overdue a response to your FOI request - Here's a request"
+ end
+end
+
+describe RequestMailer, "not_clarified_alert" do
+ it 'should not create HTML entities in the subject line' do
+ mail = RequestMailer.not_clarified_alert(FactoryGirl.create(:info_request, :title => "Here's a request"), FactoryGirl.create(:incoming_message))
+ expect(mail.subject).to eq "Clarify your FOI request - Here's a request"
+ end
+end
+
+describe RequestMailer, "comment_on_alert" do
+ it 'should not create HTML entities in the subject line' do
+ mail = RequestMailer.comment_on_alert(FactoryGirl.create(:info_request, :title => "Here's a request"), FactoryGirl.create(:comment))
+ expect(mail.subject).to eq "Somebody added a note to your FOI request - Here's a request"
+ end
+end
+
+describe RequestMailer, "comment_on_alert_plural" do
+ it 'should not create HTML entities in the subject line' do
+ mail = RequestMailer.comment_on_alert_plural(FactoryGirl.create(:info_request, :title => "Here's a request"), 2, FactoryGirl.create(:comment))
+ expect(mail.subject).to eq "Some notes have been added to your FOI request - Here's a request"
+ end
end
diff --git a/spec/models/info_request_batch_spec.rb b/spec/models/info_request_batch_spec.rb
index 2881e7745..701422037 100644
--- a/spec/models/info_request_batch_spec.rb
+++ b/spec/models/info_request_batch_spec.rb
@@ -80,7 +80,7 @@ describe InfoRequestBatch, "when finding an existing batch" do
end
end
-describe InfoRequestBatch, "when creating a batch", :focus => true do
+describe InfoRequestBatch, "when creating a batch" do
before do
@title = 'A test title'
diff --git a/spec/models/info_request_spec.rb b/spec/models/info_request_spec.rb
index 70947584b..d8c0c59b1 100644
--- a/spec/models/info_request_spec.rb
+++ b/spec/models/info_request_spec.rb
@@ -1221,6 +1221,7 @@ describe InfoRequest do
describe InfoRequest, "when constructing a list of requests by query" do
before(:each) do
+ load_raw_emails_data
get_fixtures_xapian_index
end
@@ -1313,4 +1314,20 @@ describe InfoRequest do
end
+
+ describe 'when destroying a message' do
+
+ it 'can destroy a request with comments and censor rules' do
+ info_request = FactoryGirl.create(:info_request)
+ censor_rule = FactoryGirl.create(:censor_rule, :info_request => info_request)
+ comment = FactoryGirl.create(:comment, :info_request => info_request)
+ info_request.reload
+ info_request.fully_destroy
+
+ InfoRequest.where(:id => info_request.id).should be_empty
+ CensorRule.where(:id => censor_rule.id).should be_empty
+ Comment.where(:id => comment.id).should be_empty
+ end
+
+ end
end
diff --git a/spec/models/public_body_spec.rb b/spec/models/public_body_spec.rb
index 7b55efda1..cce017424 100644
--- a/spec/models/public_body_spec.rb
+++ b/spec/models/public_body_spec.rb
@@ -1237,6 +1237,33 @@ describe PublicBody do
end
+ describe :request_email do
+ context "when the email is set" do
+ subject(:public_body) { FactoryGirl.create(:public_body, :request_email => "request@example.com") }
+
+ it "should return the set email address" do
+ expect(public_body.request_email).to eq("request@example.com")
+ end
+
+ it "should return a different email address when overridden in configuration" do
+ AlaveteliConfiguration.stub!(:override_all_public_body_request_emails).and_return("tester@example.com")
+ expect(public_body.request_email).to eq("tester@example.com")
+ end
+ end
+
+ context "when no email is set" do
+ subject(:public_body) { FactoryGirl.create(:public_body, :request_email => "") }
+
+ it "should return a blank email address" do
+ expect(public_body.request_email).to be_blank
+ end
+
+ it "should still return a blank email address when overridden in configuration" do
+ AlaveteliConfiguration.stub!(:override_all_public_body_request_emails).and_return("tester@example.com")
+ expect(public_body.request_email).to be_blank
+ end
+ end
+ end
end
describe PublicBody::Translation do
diff --git a/spec/models/widget_vote_spec.rb b/spec/models/widget_vote_spec.rb
new file mode 100644
index 000000000..b9f990eac
--- /dev/null
+++ b/spec/models/widget_vote_spec.rb
@@ -0,0 +1,53 @@
+# == Schema Information
+#
+# Table name: widget_votes
+#
+# id :integer not null, primary key
+# cookie :string(255)
+# info_request_id :integer
+# created_at :datetime not null
+# updated_at :datetime not null
+#
+
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
+
+describe WidgetVote do
+
+ describe :new do
+
+ it 'requires an info request' do
+ widget_vote = WidgetVote.new
+ widget_vote.should_not be_valid
+ widget_vote.errors[:info_request].should == ["can't be blank"]
+ end
+
+ it 'validates the cookie length' do
+ widget_vote = WidgetVote.new
+ widget_vote.should_not be_valid
+ widget_vote.errors[:cookie].should == ["is the wrong length (should be 20 characters)"]
+ end
+
+ it 'is valid with a cookie and info request' do
+ widget_vote = FactoryGirl.create(:widget_vote)
+ widget_vote.should be_valid
+ end
+
+ it 'enforces uniqueness of cookie per info request' do
+ info_request = FactoryGirl.create(:info_request)
+ widget_vote = info_request.widget_votes.create(:cookie => 'x' * 20)
+ duplicate_vote = info_request.widget_votes.build(:cookie => 'x' * 20)
+ duplicate_vote.should_not be_valid
+ duplicate_vote.errors[:cookie].should == ["has already been taken"]
+ end
+
+ it 'allows the same cookie to be used across info requests' do
+ info_request = FactoryGirl.create(:info_request)
+ second_info_request = FactoryGirl.create(:info_request)
+ widget_vote = info_request.widget_votes.create(:cookie => 'x' * 20)
+ second_request_vote = second_info_request.widget_votes.build(:cookie => 'x' * 20)
+ second_request_vote.should be_valid
+ end
+
+ end
+
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 93bcfa1ba..4df1b5649 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -124,13 +124,6 @@ Spork.prefork do
end
end
- # TODO: No idea what namespace/class/module to put this in
- # Create a clean xapian index based on the fixture files and the raw_email data.
- def create_fixtures_xapian_index
- load_raw_emails_data
- rebuild_xapian_index
- end
-
# Use the before create job hook to simulate a race condition with
# another process by creating an acts_as_xapian_job record for the
# same model:
diff --git a/spec/views/public_body/show.html.erb_spec.rb b/spec/views/public_body/show.html.erb_spec.rb
index 6ebc39caa..2a4c21d04 100644
--- a/spec/views/public_body/show.html.erb_spec.rb
+++ b/spec/views/public_body/show.html.erb_spec.rb
@@ -13,7 +13,6 @@ describe "public_body/show" do
:publication_scheme => '',
:disclosure_log => '',
:calculated_home_page => '')
- @pb.stub!(:override_request_email).and_return(nil)
@pb.stub!(:is_requestable?).and_return(true)
@pb.stub!(:special_not_requestable_reason?).and_return(false)
@pb.stub!(:has_notes?).and_return(false)