diff options
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 @@ -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) @@ -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 Binary files differindex a239862e1..a23916e9c 100644 --- a/app/assets/images/icon_application_octet-stream_large.png +++ b/app/assets/images/icon_application_octet-stream_large.png diff --git a/app/assets/images/icon_application_pdf_large.png b/app/assets/images/icon_application_pdf_large.png Binary files differindex 9a38ca33c..990b96c0a 100644 --- a/app/assets/images/icon_application_pdf_large.png +++ b/app/assets/images/icon_application_pdf_large.png diff --git a/app/assets/images/icon_application_rtf_large.png b/app/assets/images/icon_application_rtf_large.png Binary files differindex 2ad990608..977972124 100644 --- a/app/assets/images/icon_application_rtf_large.png +++ b/app/assets/images/icon_application_rtf_large.png diff --git a/app/assets/images/icon_application_vnd.ms-excel_large.png b/app/assets/images/icon_application_vnd.ms-excel_large.png Binary files differindex 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 diff --git a/app/assets/images/icon_application_vnd.ms-powerpoint_large.png b/app/assets/images/icon_application_vnd.ms-powerpoint_large.png Binary files differindex 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 diff --git a/app/assets/images/icon_application_vnd.ms-word_large.png b/app/assets/images/icon_application_vnd.ms-word_large.png Binary files differindex 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 diff --git a/app/assets/images/icon_application_zip_large.png b/app/assets/images/icon_application_zip_large.png Binary files differindex 0a14e978e..c52d6d5aa 100644 --- a/app/assets/images/icon_application_zip_large.png +++ b/app/assets/images/icon_application_zip_large.png diff --git a/app/assets/images/icon_image_bmp_large.png b/app/assets/images/icon_image_bmp_large.png Binary files differindex f6e8dbaed..347bdaaf1 100644..120000 --- a/app/assets/images/icon_image_bmp_large.png +++ b/app/assets/images/icon_image_bmp_large.png diff --git a/app/assets/images/icon_image_gif_large.png b/app/assets/images/icon_image_gif_large.png Binary files differindex 424d1e0fd..347bdaaf1 100644..120000 --- a/app/assets/images/icon_image_gif_large.png +++ b/app/assets/images/icon_image_gif_large.png diff --git a/app/assets/images/icon_image_img_large.png b/app/assets/images/icon_image_img_large.png Binary files differnew file mode 100644 index 000000000..e19e7553c --- /dev/null +++ b/app/assets/images/icon_image_img_large.png diff --git a/app/assets/images/icon_image_jpeg_large.png b/app/assets/images/icon_image_jpeg_large.png Binary files differindex fd50a889d..347bdaaf1 100644..120000 --- a/app/assets/images/icon_image_jpeg_large.png +++ b/app/assets/images/icon_image_jpeg_large.png diff --git a/app/assets/images/icon_image_png_large.png b/app/assets/images/icon_image_png_large.png Binary files differindex f16edb08e..347bdaaf1 100644..120000 --- a/app/assets/images/icon_image_png_large.png +++ b/app/assets/images/icon_image_png_large.png diff --git a/app/assets/images/icon_image_tiff_large.png b/app/assets/images/icon_image_tiff_large.png Binary files differindex 356f63478..000bd0318 100644 --- a/app/assets/images/icon_image_tiff_large.png +++ b/app/assets/images/icon_image_tiff_large.png diff --git a/app/assets/images/icon_message_delivery-status_large.png b/app/assets/images/icon_message_delivery-status_large.png Binary files differindex a239862e1..dccdbbccd 100644 --- a/app/assets/images/icon_message_delivery-status_large.png +++ b/app/assets/images/icon_message_delivery-status_large.png diff --git a/app/assets/images/icon_text_html_large.png b/app/assets/images/icon_text_html_large.png Binary files differindex 914502cf4..3813d2582 100644 --- a/app/assets/images/icon_text_html_large.png +++ b/app/assets/images/icon_text_html_large.png diff --git a/app/assets/images/icon_text_plain_large.png b/app/assets/images/icon_text_plain_large.png Binary files differindex f74a997ba..f15b0dbdc 100644 --- a/app/assets/images/icon_text_plain_large.png +++ b/app/assets/images/icon_text_plain_large.png diff --git a/app/assets/images/icon_text_x-vcard_large.png b/app/assets/images/icon_text_x-vcard_large.png Binary files differindex cc44d3edc..804066af8 100644 --- a/app/assets/images/icon_text_x-vcard_large.png +++ b/app/assets/images/icon_text_x-vcard_large.png diff --git a/app/assets/images/icon_unknown.png b/app/assets/images/icon_unknown.png Binary files differindex 992c646c0..9a06d9baa 100644 --- a/app/assets/images/icon_unknown.png +++ b/app/assets/images/icon_unknown.png diff --git a/app/assets/images/widget-base.png b/app/assets/images/widget-base.png Binary files differnew file mode 100644 index 000000000..872244543 --- /dev/null +++ b/app/assets/images/widget-base.png 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) |