diff options
Diffstat (limited to 'spec')
33 files changed, 1361 insertions, 399 deletions
diff --git a/spec/controllers/api_controller_spec.rb b/spec/controllers/api_controller_spec.rb index 749be9f85..66b8e33f0 100644 --- a/spec/controllers/api_controller_spec.rb +++ b/spec/controllers/api_controller_spec.rb @@ -259,7 +259,7 @@ describe ApiController, "when using the API" do attachments.size.should == 1 attachment = attachments[0] attachment.filename.should == "tfl.pdf" - attachment.body.should == load_file_fixture("tfl.pdf", as_binary=true) + attachment.body.should == load_file_fixture("tfl.pdf") end it "should show information about a request" do diff --git a/spec/controllers/general_controller_spec.rb b/spec/controllers/general_controller_spec.rb index 9a88dbc3a..4a1c8b134 100644 --- a/spec/controllers/general_controller_spec.rb +++ b/spec/controllers/general_controller_spec.rb @@ -19,8 +19,11 @@ end describe GeneralController, 'when getting the blog feed' do - it 'should add a lang param correctly to a url with no querystring' do + before do AlaveteliConfiguration.stub!(:blog_feed).and_return("http://blog.example.com") + end + + it 'should add a lang param correctly to a url with no querystring' do get :blog assigns[:feed_url].should == "http://blog.example.com?lang=en" end @@ -31,6 +34,12 @@ describe GeneralController, 'when getting the blog feed' do assigns[:feed_url].should == "http://blog.example.com?alt=rss&lang=en" end + it 'should parse an item from an example feed' do + controller.stub!(:quietly_try_to_open).and_return(load_file_fixture("blog_feed.atom")) + get :blog + assigns[:blog_items].count.should == 1 + end + end describe GeneralController, "when showing the frontpage" do diff --git a/spec/controllers/request_controller_spec.rb b/spec/controllers/request_controller_spec.rb index 5dd628dc9..122584c7d 100644 --- a/spec/controllers/request_controller_spec.rb +++ b/spec/controllers/request_controller_spec.rb @@ -479,11 +479,11 @@ describe RequestController, "when showing one request" do (assigns[:info_request_events].size - size_before).should == 1 ir.reload - get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello.txt', :skip_cache => 1 + get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1 response.content_type.should == "text/plain" response.should contain "Second hello" - get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 3, :file_name => 'hello.txt', :skip_cache => 1 + get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 3, :file_name => 'hello world.txt', :skip_cache => 1 response.content_type.should == "text/plain" response.should contain "First hello" end @@ -496,7 +496,7 @@ describe RequestController, "when showing one request" do get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, - :file_name => 'hello.txt' + :file_name => 'hello world.txt' end it "should convert message body to UTF8" do @@ -510,7 +510,7 @@ describe RequestController, "when showing one request" do ir = info_requests(:fancy_dog_request) receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email) ir.reload - get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello.txt.html', :skip_cache => 1 + get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1 response.content_type.should == "text/html" response.should contain "Second hello" end @@ -531,11 +531,11 @@ describe RequestController, "when showing one request" do ir.reload ugly_id = "55195" lambda { - get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello.txt.html', :skip_cache => 1 + get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1 }.should raise_error(ActiveRecord::RecordNotFound) lambda { - get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello.txt', :skip_cache => 1 + get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1 }.should raise_error(ActiveRecord::RecordNotFound) end it "should return 404 when incoming message and request ids don't match" do @@ -544,7 +544,7 @@ describe RequestController, "when showing one request" do receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email) ir.reload lambda { - get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => wrong_id, :part => 2, :file_name => 'hello.txt.html', :skip_cache => 1 + get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => wrong_id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1 }.should raise_error(ActiveRecord::RecordNotFound) end it "should return 404 for ugly URLs contain a request id that isn't an integer, even if the integer prefix refers to an actual request" do @@ -554,11 +554,11 @@ describe RequestController, "when showing one request" do ugly_id = "%d95" % [info_requests(:naughty_chicken_request).id] lambda { - get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello.txt.html', :skip_cache => 1 + get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1 }.should raise_error(ActiveRecord::RecordNotFound) lambda { - get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello.txt', :skip_cache => 1 + get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ugly_id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1 }.should raise_error(ActiveRecord::RecordNotFound) end it "should return 404 when incoming message and request ids don't match" do @@ -567,7 +567,7 @@ describe RequestController, "when showing one request" do receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email) ir.reload lambda { - get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => wrong_id, :part => 2, :file_name => 'hello.txt.html', :skip_cache => 1 + get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => wrong_id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1 }.should raise_error(ActiveRecord::RecordNotFound) end @@ -575,44 +575,66 @@ describe RequestController, "when showing one request" do ir = info_requests(:fancy_dog_request) receive_incoming_mail('incoming-request-pdf-attachment.email', ir.incoming_email) ir.reload - get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'fs_50379341.pdf.html', :skip_cache => 1 + get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'fs 50379341.pdf.html', :skip_cache => 1 response.content_type.should == "text/html" response.should contain "Walberswick Parish Council" end - it "should not cause a reparsing of the raw email, even when the result would be a 404" do + it "should not cause a reparsing of the raw email, even when the attachment can't be found" do ir = info_requests(:fancy_dog_request) receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email) ir.reload - attachment = IncomingMessage.get_attachment_by_url_part_number(ir.incoming_messages[1].get_attachments_for_display, 2) + attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(ir.incoming_messages[1].get_attachments_for_display, 2, 'hello world.txt') attachment.body.should contain "Second hello" # change the raw_email associated with the message; this only be reparsed when explicitly asked for ir.incoming_messages[1].raw_email.data = ir.incoming_messages[1].raw_email.data.sub("Second", "Third") - # asking for an attachment by the wrong filename results - # in a 404 for browsing users. This shouldn't cause a - # re-parse... - lambda { - get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello.txt.baz.html', :skip_cache => 1 - }.should raise_error(ActiveRecord::RecordNotFound) + # asking for an attachment by the wrong filename should result in redirecting + # back to the incoming message, but shouldn't cause a reparse: + get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt.baz.html', :skip_cache => 1 + response.status.should == 303 - attachment = IncomingMessage.get_attachment_by_url_part_number(ir.incoming_messages[1].get_attachments_for_display, 2) + attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(ir.incoming_messages[1].get_attachments_for_display, 2, 'hello world.txt') attachment.body.should contain "Second hello" # ...nor should asking for it by its correct filename... - get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello.txt.html', :skip_cache => 1 + get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1 response.should_not contain "Third hello" # ...but if we explicitly ask for attachments to be extracted, then they should be force = true ir.incoming_messages[1].parse_raw_email!(force) ir.reload - attachment = IncomingMessage.get_attachment_by_url_part_number(ir.incoming_messages[1].get_attachments_for_display, 2) + attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(ir.incoming_messages[1].get_attachments_for_display, 2, 'hello world.txt') attachment.body.should contain "Third hello" - get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello.txt.html', :skip_cache => 1 + get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt.html', :skip_cache => 1 response.should contain "Third hello" end + it "should redirect to the incoming message if there's a wrong part number and an ambiguous filename" do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email) + ir.reload + + im = ir.incoming_messages[1] + + attachment = IncomingMessage.get_attachment_by_url_part_number_and_filename(im.get_attachments_for_display, 5, 'hello world.txt') + attachment.should be_nil + + get :get_attachment_as_html, :incoming_message_id => im.id, :id => ir.id, :part => 5, :file_name => 'hello world.txt', :skip_cache => 1 + response.status.should == 303 + new_location = response.header['Location'] + new_location.should match(/request\/#{ir.url_title}#incoming-#{im.id}/) + end + + it "should find a uniquely named filename even if the URL part number was wrong" do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('incoming-request-pdf-attachment.email', ir.incoming_email) + ir.reload + get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 5, :file_name => 'fs 50379341.pdf', :skip_cache => 1 + response.content_type.should == "application/pdf" + end + it "should treat attachments with unknown extensions as binary" do ir = info_requests(:fancy_dog_request) receive_incoming_mail('incoming-request-attachment-unknown-extension.email', ir.incoming_email) @@ -627,10 +649,8 @@ describe RequestController, "when showing one request" do ir = info_requests(:fancy_dog_request) receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email) - lambda { - get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, - :file_name => 'http://trying.to.hack' - }.should raise_error(ActiveRecord::RecordNotFound) + get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'http://trying.to.hack' + response.status.should == 303 end it "should censor attachments downloaded as binary" do @@ -646,7 +666,7 @@ describe RequestController, "when showing one request" do begin receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email) - get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello.txt', :skip_cache => 1 + get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1 response.content_type.should == "text/plain" response.should contain "xxxxxx hello" ensure @@ -668,7 +688,7 @@ describe RequestController, "when showing one request" do receive_incoming_mail('incoming-request-two-same-name.email', ir.incoming_email) ir.reload - get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello.txt', :skip_cache => 1 + get :get_attachment, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, :file_name => 'hello world.txt', :skip_cache => 1 response.content_type.should == "text/plain" response.should contain "xxxxxx hello" ensure @@ -697,11 +717,13 @@ describe RequestController, "when showing one request" do # so at this point, assigns[:info_request].incoming_messages[1].get_attachments_for_display is returning stuff, but the equivalent thing in the template isn't. # but something odd is that the above is return a whole load of attachments which aren't there in the controller response.body.should have_selector("p.attachment strong") do |s| - s.should contain /hello.txt/m + s.should contain /hello world.txt/m end censor_rule = CensorRule.new() - censor_rule.text = "hello.txt" + # Note that the censor rule applies to the original filename, + # not the display_filename: + censor_rule.text = "hello-world.txt" censor_rule.replacement = "goodbye.txt" censor_rule.last_edit_editor = "unknown" censor_rule.last_edit_comment = "none" @@ -745,7 +767,7 @@ describe RequestController, "when showing one request" do old_path = assigns[:url_path] response.location.should contain /#{assigns[:url_path]}$/ zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", old_path)) { |zipfile| - zipfile.count.should == 3 # the message plus two "hello.txt" files + zipfile.count.should == 3 # the message plus two "hello-world.txt" files } # The path of the zip file is based on the hash of the timestamp of the last request @@ -758,7 +780,7 @@ describe RequestController, "when showing one request" do assigns[:url_path].should_not == old_path response.location.should contain assigns[:url_path] zipfile = Zip::ZipFile.open(File.join(File.dirname(__FILE__), "../../cache/zips", assigns[:url_path])) { |zipfile| - zipfile.count.should == 4 # the message, two hello.txt plus the unknown attachment + zipfile.count.should == 4 # the message, two hello-world.txt plus the unknown attachment } end @@ -877,7 +899,7 @@ describe RequestController, "when changing prominence of a request" do get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, - :file_name => 'hello.txt' + :file_name => 'hello world.txt' end.should raise_error(ActiveRecord::RecordNotFound) end @@ -892,7 +914,7 @@ describe RequestController, "when changing prominence of a request" do get :get_attachment_as_html, :incoming_message_id => ir.incoming_messages[1].id, :id => ir.id, :part => 2, - :file_name => 'hello.txt' + :file_name => 'hello world.txt' end.should raise_error(ActiveRecord::RecordNotFound) end @@ -909,6 +931,7 @@ describe RequestController, "when searching for an authority" do # so we make sure we're logged in, just in case before do @user = users(:bob_smith_user) + get_fixtures_xapian_index end it "should return nothing for the empty query string" do @@ -920,7 +943,6 @@ describe RequestController, "when searching for an authority" do end it "should return matching bodies" do - get_fixtures_xapian_index session[:user_id] = @user.id get :select_authority, :query => "Quango" @@ -2263,6 +2285,10 @@ end describe RequestController, "when showing similar requests" do render_views + before do + get_fixtures_xapian_index + end + it "should work" do get :similar, :url_title => info_requests(:badger_request).url_title response.should render_template("request/similar") @@ -2392,7 +2418,7 @@ describe RequestController, "when caching fragments" do attachment = mock(FoiAttachment, :display_filename => long_name, :body_as_html => ['some text', 'wrapper']) IncomingMessage.stub!(:find).with("44").and_return(incoming_message) - IncomingMessage.stub!(:get_attachment_by_url_part_number).and_return(attachment) + IncomingMessage.stub!(:get_attachment_by_url_part_number_and_filename).and_return(attachment) InfoRequest.stub!(:find).with("132").and_return(info_request) params = { :file_name => long_name, :controller => "request", diff --git a/spec/controllers/track_controller_spec.rb b/spec/controllers/track_controller_spec.rb index e9501b1ed..5a766b1e1 100644 --- a/spec/controllers/track_controller_spec.rb +++ b/spec/controllers/track_controller_spec.rb @@ -144,6 +144,7 @@ describe TrackController, "when viewing RSS feed for a track" do get :track_request, :feed => 'feed', :url_title => track_thing.info_request.url_title response.should render_template('track/atom_feed') + response.content_type.should == 'application/atom+xml' # XXX should check it is an atom.builder type being rendered, not sure how to assigns[:xapian_object].matches_estimated.should == 3 diff --git a/spec/controllers/user_controller_spec.rb b/spec/controllers/user_controller_spec.rb index a18554114..1d8e3dcc3 100644 --- a/spec/controllers/user_controller_spec.rb +++ b/spec/controllers/user_controller_spec.rb @@ -626,6 +626,7 @@ describe UserController, "when viewing the wall" do render_views before(:each) do + load_raw_emails_data get_fixtures_xapian_index end diff --git a/spec/fixtures/files/blog_feed.atom b/spec/fixtures/files/blog_feed.atom new file mode 100644 index 000000000..f49693938 --- /dev/null +++ b/spec/fixtures/files/blog_feed.atom @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<rss version="2.0" + xmlns:content="http://purl.org/rss/1.0/modules/content/" + xmlns:wfw="http://wellformedweb.org/CommentAPI/" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:atom="http://www.w3.org/2005/Atom" + xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" + xmlns:slash="http://purl.org/rss/1.0/modules/slash/" + > + +<channel> + <title>A Blog Feed</title> + <atom:link href="http://example.com/feed/" rel="self" type="application/rss+xml" /> + <link>http://www.example.com</link> + <description>Stuff</description> + <lastBuildDate>Tue, 30 Apr 2013 14:34:15 +0000</lastBuildDate> + <language>en</language> + <sy:updatePeriod>hourly</sy:updatePeriod> + <sy:updateFrequency>1</sy:updateFrequency> + <generator>http://wordpress.org/?v=3.3.2</generator> + <item> + <title>Example Post</title> + <link>http://www.example.com/example-post</link> + <comments>http://www.example.com/example-post#comments</comments> + <pubDate>Mon, 01 Apr 2013 19:26:08 +0000</pubDate> + <dc:creator>Example Blogger</dc:creator> + <category><![CDATA[FOI]]></category> + + <guid isPermaLink="false">http://www.example.com/?id=333</guid> + <description><![CDATA[An example post [...]]]></description> + <content:encoded><![CDATA[<h3>A blog post</h3> +<p>Example post</p> +]]></content:encoded> + <wfw:commentRss>http://www.example.com/feed/</wfw:commentRss> + <slash:comments>2</slash:comments> + </item> + + </channel> +</rss> diff --git a/spec/fixtures/files/incoming-request-two-same-name.email b/spec/fixtures/files/incoming-request-two-same-name.email index f1024d607..ecd322fe4 100644 --- a/spec/fixtures/files/incoming-request-two-same-name.email +++ b/spec/fixtures/files/incoming-request-two-same-name.email @@ -13,13 +13,13 @@ Content-Disposition: inline --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=us-ascii -Content-Disposition: attachment; filename="hello.txt" +Content-Disposition: attachment; filename="hello-world.txt" Second hello --Q68bSM7Ycu6FN28Q Content-Type: text/plain; charset=us-ascii -Content-Disposition: attachment; filename="hello.txt" +Content-Disposition: attachment; filename="hello-world.txt" First hello diff --git a/spec/fixtures/files/inline-uuencode.email b/spec/fixtures/files/inline-uuencode.email new file mode 100644 index 000000000..3134ba3ad --- /dev/null +++ b/spec/fixtures/files/inline-uuencode.email @@ -0,0 +1,27 @@ +From foo@bar Mon Jun 01 17:14:44 2009 +Return-path: <foo@bar> +Envelope-to: foi@quux +Delivery-date: Mon, 01 Jun 2009 17:14:44 +0100 +From: <foo@bar> +To: <request-whatever@quux> +Subject: something or other +Date: Mon, 1 Jun 2009 17:14:37 +0100 +X-MimeOLE: Produced By Microsoft MimeOLE V6.00.3790.181 +Message-ID: <baz@xyzzy> + +Thanks for your email - here's a truncated attachment +for you: + +********************************************************************** + +begin 666 ResponseT7363 9.doc +MT,\1X*&Q&N$`````````````````````/@`#`/[_"0`&```````````````" +M````) ``````````$ ``+@````$```#^____`````",```!L````________ +M```````````````````````````````````````````````````````````` +M```````````````````````````````````````````````````````````` +#```` +` +end + +The original of this email was scanned for viruses or something +like that. diff --git a/spec/fixtures/files/malformed-to-and-cc.email b/spec/fixtures/files/malformed-to-and-cc.email new file mode 100644 index 000000000..4fbb6e21e --- /dev/null +++ b/spec/fixtures/files/malformed-to-and-cc.email @@ -0,0 +1,11 @@ +From foo@bar Wed Mar 12 14:58:26 2008 +Return-path: <foo@bar> +Subject: example email +To: <bar@example.org +Cc: baz@example.org> +From: quux@example.org +Date: Mon, 7 May 2012 12:47:06 +0100 +Mime-Version: 1.0 +Content-Type: text/plain; charset=utf-8 + +A very basic email, but with malformed To: and Cc: lines diff --git a/spec/fixtures/files/mislabelled-as-iso-8859-1.email b/spec/fixtures/files/mislabelled-as-iso-8859-1.email new file mode 100644 index 000000000..6c8e6109e --- /dev/null +++ b/spec/fixtures/files/mislabelled-as-iso-8859-1.email @@ -0,0 +1,20 @@ +From foo@bar Thu Mar 01 15:02:33 2012 +Return-path: <foo@bar> +Envelope-to: foi@quux +Delivery-date: Thu, 01 Mar 2012 15:02:33 +0000 +Date: Thu, 01 Mar 2012 15:01:58 +0000 +Subject: some FOI request +To: foi@quux +From: foo@bar +MIME-Version: 1.0 +Content-Type: text/plain; charset="iso-8859-1" +Content-Transfer-Encoding: 7bit +Message-Id: <2468@bar.local> + +Dear Whoever, + +THERE'S A DASH NEXT REQUEST FOR INFORMATION + +Best regards, +Other Person + diff --git a/spec/fixtures/files/multipart-no-final-boundary.email b/spec/fixtures/files/multipart-no-final-boundary.email new file mode 100644 index 000000000..9c16dad52 --- /dev/null +++ b/spec/fixtures/files/multipart-no-final-boundary.email @@ -0,0 +1,21 @@ +From foo@bar Thu Sep 13 10:34:44 2012 +Return-path: <foo@bar> +Envelope-to: foi@example.org +Delivery-date: Thu, 13 Sep 2012 10:34:44 +0100 +From: foo@bar +To: foi@example.org +Subject: an acknowledgement email +Date: Thu, 13 Sep 2012 10:08:03 +0100 +Message-ID: <987654@foo.local> +Content-Type: multipart/mixed; boundary="-----7D81B75CCC90D2974F7A1CBD" + +This is a multi-part message in MIME format. +-------7D81B75CCC90D2974F7A1CBD +Content-Type: text/html + +<div> + <p> + This is an acknowledgement of your email, that irritatingly + leaves out the final MIME boundary. + </p> +<div> diff --git a/spec/fixtures/files/nested-attachments-premature-end.email b/spec/fixtures/files/nested-attachments-premature-end.email new file mode 100644 index 000000000..6b13808dc --- /dev/null +++ b/spec/fixtures/files/nested-attachments-premature-end.email @@ -0,0 +1,110 @@ +From someone@example.org Mon May 15 13:10:29 2012 +Return-path: <someone@example.org> +Envelope-to: foi@example.org +Delivery-date: Mon, 15 May 2012 13:10:29 +0100 +Message-Id: <abcde@baz.local> +Date: Mon, 15 May 2012 09:48:48 +0100 +From: "Example Person" <someone@example.org> +To: <request@example.org> +Subject: some FOI request or other +Mime-Version: 1.0 +Content-Type: multipart/mixed; boundary="=__outer__=" + +This is a MIME message. If you are reading this text, you may want to +consider changing to a mail reader or gateway that understands how to +properly handle MIME multipart messages. + +--=__outer__= +Content-Type: multipart/alternative; boundary="=__inner__=" + +--=__inner__= +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: quoted-printable +X-MIME-Autoconverted: from 8bit to quoted-printable by something + +Hello +=20 +Please find some information attached. +=20 + +--=__inner__= +Content-Description: HTML +Content-Type: text/html; charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +<html> + <head> + <title>some title text</title> + </head> + <body> + <p>blah blah blah</p> + </body> +</html> + +--=__inner__=-- + +--=__outer__= +Content-Type: message/rfc822 + +Return-path: <foo@bar> +Date: Mon, 7 May 2012 12:47:06 +0100 +From: someone-else@example.org +To: foi@example.org +Message-Id: <56789@quux.local> +Subject: a freedom of information requests +Mime-Version: 1.0 +Content-Type: text/plain; charset=utf-8 + + Dear Whoever, + + Please could you let me know, um, whatever ... + + Yours faithfully, + + Whoever I Am + +--=__outer__= +Content-Type: text/plain; charset=US-ASCII +Content-Disposition: inline +Content-Transfer-Encoding: quoted-printable + + Dear Whowever, + =20 + Please could you let me know, um, whatever ... + =20 + Yours faithfully, + =20 + Whoever I Am + =20 + +--=__outer__=-- + +--=__outer__= +Content-Type: application/png; name="maroon-square.png" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="maroon-square.png" + +iVBORw0KGgoAAAANSUhEUgAAAEEAAABCCAYAAAAIY7vrAAAABmJLR0QA/wD/AP+g +vaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QQeDSEx8qultwAAABl0 +RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAMzSURBVHja7VtL2psw +DNS4rPv1Gj1Kt71Az9ZT9F7dN9MFGGThB/YfKDX2Kp8DRBpLowcKvn/5ShERiAgl +srh8aT93tJzWdae8XR0CEICwUx59K54H4QFKp0Eg5alrAwEYIDx5DRAGCAOEAcIA +QaUFfDoIHJawpEbOPd0dRPjJDWIUiEwt933+8es2Ovz++a3dCkREXmwD4ZbsVln6 +cLkef14duAMqAGCkY0A+jBNgXGFZU/eKa3fhZjlQqLhHKF9oFbpulE2Z/oFrXTd+ +nlOWkn1dMHXrAiWguq0iG9uk/REjBggPtgQOED781my4wwBhgDBAmPmUAwR0X0UO +dxggnA8CO5xocU8HoAoEDwA6nOyCH+ZMKQ4zy+QbNBoUirquMPBJcgPyJkOi+c7S +ohhn6ZctzDIrcFalIspYILG1et9WABUtt6WztLq+/0Amp9sCnsCBUhfvK4FLiRCA +QwC7JABGTngrIIPnIjf6R5We0uxz3j+FbCvdy2nlY/IgcfrMRQuFHIC9Sap3AW8n +2gZ+cZYCVn4LzBxxnykNgJpWN8lt7yw+QCMxan2s8lQXcNlDlpAW7YmIXMszTgoH +rU91+8OFYXN9ikz/LyLgExSCDlaO+cdGsIEQkyUAIgFMKRTEn3vDjFFHwWSIzEQC +cmN4IHVNGG2PQXhhsuRl3jihwQyB6H1274gV1BhKLKNt4ZEpkygeeoC+xytdK1cr +oX0EACphnTZXbbLMmL/YBGo9lSU1OmBONMnTlQUqTa4y1VgAddg0hdTR04lyT0Xq +8RYAyHVyBX6ET/9wTBD6TWVCMH5Qo3yhXju3bNY/BBMdsoLYBMmnzQdOP56O36s5 +40r1D7UWYV5dNT2nbxVBAHb43Y36CdbXfTii6isU/U7ZXLQ4w/V/wotFoilVF2kl +w7YCDrIPkj4/G9fao7q0rYSSJdgeSqmQrCU+r/j8rOv/gpuKPm5Lffen5eN+ljeo +rcfW0Om2Enm9KwDZAgrG98txX9cMe6X2E5SGU29VTE17lFAUkMybsXclndu31BGX +hcgWv8oxonYtkf/jhc10WPGgm2IZncKlu+sg8vLm7hDSwk3f2/wFEzN3v6aAXQ0A +AAAASUVORK5CYII= + +--=__outer__=-- + diff --git a/spec/fixtures/files/no-part-charset-random-data.email b/spec/fixtures/files/no-part-charset-random-data.email new file mode 100644 index 000000000..d51fd3f38 --- /dev/null +++ b/spec/fixtures/files/no-part-charset-random-data.email @@ -0,0 +1,30 @@ +From xxxx@yahoo.cn Mon Oct 08 14:01:34 2012 +Return-path: <xxxx@yahoo.cn> +Envelope-to: foi@atlas.ukcod.org.uk +Delivery-date: Mon, 08 Oct 2012 14:01:34 +0100 +Received: (qmail 63864 invoked from network); 8 Oct 2012 13:01:12 -0000 +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.cn; s=s1024; t=1349701272; bh=T/mtlIYvhB/L5RO+CvTazeAdGf1n1zsGXBoA8EKGT9M=; h=Message-ID:X-Yahoo-Newman-Property:X-YMail-OSG:X-Yahoo-SMTP:Received:X-mailer:From:Subject:To:Content-Transfer-Encoding:Content-Type:Date; b=LYI/PXvA7DA746bmyprChUg7N8YDvN9XE/bhfTt5MW7siOmxHHzn1w+s5X33PvLI0x0UfJLo+MCkTnGPKnG5BYY38US8PkocJYyphrvF/eaUl3ALf8UvxHBOJX1iIi89Xp2NnfbS8lz9kZAWifb9GOnOA5/kLDcL5/WJXliit2k= +Message-ID: <xxxx@xxxx.yahoo.com> +X-Yahoo-Newman-Property: ymail-5 +X-YMail-OSG: nPs5jgsVM1myUoKjeEPTxxalz4BM6BZMEUYu.E8NPMPQyo_ + Yej8T2WCTurn767NOwhuDIqNxC2QGZINqfjmKcdyW7a1P_Zxqr9GsjgxODci + ihwr7qYAGDDbcsrB.PX4epnJZHl3yAwoGW.1ReEZnXQANFcNep7.zNEbZ_2k + RU1IhI9aHYvxPxt5RWugwOoFRh9P8Ym35A88IMazNtVaBiBEXF6Vk8Aqr9XP + 3Vh9xOT9Pn6X8qOUjNXkdb3xB4S5AAIRSE9mqhL1KzHBwdVQs25IoM_2FV2b + gPsQGgL4_mwBH0WcEMhdj7Kn6Nfb44L.50E_V3DH.8P7KzDK8zNVXSbAqohX + Qi6MzUK2frr8IyZyYzHb.ekff7kAcJgUoHvhnyPar8tRYxhQT3_xsUTzsx8N + oWckVPh_i3OT7U4ObgekqgtteMoYqPH2eF1SZXamGBAs- +X-Yahoo-SMTP: YUQHwRWswBDjbw_M.D6EP4KpT9khlJErDRBQi4ySZQ-- +X-mailer: MIME::Lite 3.027 (F2.74; T1.31; A2.07; B3.13; Q3.13) +From: =?GB2312?B?zsJKaWFu?= Bing <xxxx@yahoo.cn> +Subject: =?GB2312?B?yM7A1svJ?= +To: FOI Person <EMAIL_TO> +Content-Transfer-Encoding: base64 +Content-Type: text/plain +Date: Tue, 9 Oct 2012 20:53:06 +0800 + +HPBSqsndNBX+ER4hyBoPhhnclcWKVFgbevdD5cJvfI/ARbxRYqA28hZ49Pf6A/ks +NdVh4N5VPgRs/7SHYPfw5625pZJYTLj6nVdYk76sxnjiiAmwCJWGjPoWvO7nHUBv +fuLXtNVq5HmD0bWWjAbSk2n74PW7v5izbNO2fjHyiyX2CIof0rriXDmOldJqoebO +ejybrjG+Tahpu3FF1Mw98HfswzkdB46u/izLCzdUQVM= + diff --git a/spec/fixtures/files/part-without-charset-in-content-type.email b/spec/fixtures/files/part-without-charset-in-content-type.email new file mode 100644 index 000000000..439d52cc3 --- /dev/null +++ b/spec/fixtures/files/part-without-charset-in-content-type.email @@ -0,0 +1,38 @@ +From example@example.com Wed Sep 15 17:55:40 2010 +Return-path: <example@example.com> +Envelope-to: example@example.com +Delivery-date: Wed, 15 Sep 2010 17:55:40 +0100 +From: <example@example.com> +To: <request-xxxxx@whatdotheyknow.com> +Date: Wed, 15 Sep 2010 17:56:03 +0100 +Subject: FOI Internal Review response +Thread-Topic: FOI Internal Review response +Thread-Index: xxxxx +Message-ID: <xxxxxx> +Accept-Language: en-US, en-GB +Content-Language: en-US +X-MS-Has-Attach: yes +X-MS-TNEF-Correlator: +acceptlanguage: en-US, en-GB +Content-Type: multipart/mixed; + boundary="_002_E6527350F565F54A88C36C23F6C2B86702618AD0DF95SDCCPMSXMB5_" +MIME-Version: 1.0 + +--_002_E6527350F565F54A88C36C23F6C2B86702618AD0DF95SDCCPMSXMB5_ +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: base64 + +someencodedtext= + +--_002_E6527350F565F54A88C36C23F6C2B86702618AD0DF95SDCCPMSXMB5_ +Content-Type: document/pdf; name="document.pdf" +Content-Description: document.pdf +Content-Disposition: attachment; filename="document.pdf"; + size=62103; creation-date="Wed, 15 Sep 2010 17:54:27 GMT"; + modification-date="Wed, 15 Sep 2010 17:54:27 GMT" +Content-Transfer-Encoding: base64 + +somemoreencodedtext= + +--_002_E6527350F565F54A88C36C23F6C2B86702618AD0DF95SDCCPMSXMB5_-- + diff --git a/spec/fixtures/files/tnef-attachment-empty.email b/spec/fixtures/files/tnef-attachment-empty.email new file mode 100644 index 000000000..7967aa95b --- /dev/null +++ b/spec/fixtures/files/tnef-attachment-empty.email @@ -0,0 +1,196 @@ +From hello@blah.local Fri Feb 21 16:23:14 2013 +Return-path: <bar@example.org> +Envelope-to: foo@example.org +Delivery-date: Fri, 21 Feb 2013 16:23:14 +0000 +Content-Type: multipart/mixed; + boundary="_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_" +From: <bar@example.org> +To: <foo@example.org> +Sender: <hello@blah.local> +Date: Fri, 21 Feb 2013 16:23:04 +0000 +Subject: here's a useless email +Message-ID: <12345@blah.local> +Accept-Language: en-US, en-GB +Content-Language: en-US +X-MS-Has-Attach: +X-MS-TNEF-Correlator: <12345@blah.local> +acceptlanguage: en-US, en-GB +MIME-Version: 1.0 + +--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_ +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: quoted-printable + +This attachment just has a body from one of the tests +in the tnef package in Debian. + +--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_ +Content-Disposition: attachment; filename="winmail.dat" +Content-Transfer-Encoding: base64 +Content-Type: application/ms-tnef; name="winmail.dat" + +eJ8+IiURAQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAENgAQAAgAA +AAIAAgABBYADAA4AAADVBwQAGQAKAA8AIwABADYBASCAAwAOAAAA1QcEABkACgAP +ACQAAQA3AQEJgAEAIQAAADBEREEwRkNCQ0MwN0MxNDE5MkVFODZGQzQyRDE1Qjk1 +AGYHAQSQBgBkAgAAAQAAAA8AAAAfAAEwAQAAABAAAAAzAGsAdQBzAGUAcgAyAAAA +HwACMAEAAAAGAAAARQBYAAAAAAAfAAMwAQAAAI4AAAAvAE8APQBCAFIALQBFAFgA +QwBIAC0AVABFAFMAVAAvAE8AVQA9AEYASQBSAFMAVAAgAEEARABNAEkATgBJAFMA +VABSAEEAVABJAFYARQAgAEcAUgBPAFUAUAAvAEMATgA9AFIARQBDAEkAUABJAEUA +TgBUAFMALwBDAE4APQAzAGsAdQBzAGUAcgAyAAAAAAADAAAwAAAAAAMA/18AAAAA +AwAVDAEAAAACAQswAQAAAEoAAABFWDovTz1CUi1FWENILVRFU1QvT1U9RklSU1Qg +QURNSU5JU1RSQVRJVkUgR1JPVVAvQ049UkVDSVBJRU5UUy9DTj0zS1VTRVIyAAAA +HwAgOgEAAAAQAAAAMwBrAHUAcwBlAHIAMgAAAAMA/V8BAAAACwBAOgAA+T8CAfdf +AQAAAGMAAAAAAAAA3KdAyMBCEBq0uQgAKy/hggEAAAAAAAAAL289QlItRVhDSC1U +RVNUL291PUZpcnN0IEFkbWluaXN0cmF0aXZlIEdyb3VwL2NuPVJlY2lwaWVudHMv +Y249M2t1c2VyMgAAAwAAOQAAAAAfAP45AQAAAEoAAAAzAGsAdQBzAGUAcgAyAEAA +YgByAGUAeABjAGgAYQBuAGcAZQAuAGQAbwBsAHAAaABpAG4AcwBlAGEAcgBjAGgA +LgBjAG8AbQAAAAAAAwBxOgAAAAAfAPZfAQAAABAAAAAzAGsAdQBzAGUAcgAyAAAA +m2sBA5AGAEwbAAAzAAAACwACAAEAAAAfABoAAQAAABIAAABJAFAATQAuAE4AbwB0 +AGUAAAAAAAMAJgAAAAAAAwA2AAAAAAAfADcAAQAAAB4AAABCAGkAbABsACAAbwBm +ACAAUgBpAGcAaAB0AHMAAAAAAEAAOQBgQvtkuknFAR8APQABAAAAAgAAAAAAAAAC +AUcAAQAAADgAAABjPXVzO2E9IDtwPUJSLUVYQ0gtVEVTVDtsPUJSLUVYQ0gtREVW +MS0wNTA0MjUxNzE1MzZaLTE0AB8AcAABAAAAHgAAAEIAaQBsAGwAIABvAGYAIABS +AGkAZwBoAHQAcwAAAAAAAgFxAAEAAAAWAAAAAcVJumT7yarjal9+TnmqsNvwaipi +/QAAHwAaDAEAAAAQAAAAMwBrAHIAZQBsAGEAeQAAAB8AHQ4BAAAAHgAAAEIAaQBs +AGwAIABvAGYAIABSAGkAZwBoAHQAcwAAAAAAAgETEAEAAADuFAAAPCFET0NUWVBF +IEhUTUwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDQuMCBUcmFuc2l0aW9uYWwv +L0VOIj4NCjxIVE1MPjxIRUFEPg0KPE1FVEEgaHR0cC1lcXVpdj1Db250ZW50LVR5 +cGUgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PXVzLWFzY2lpIj4NCjxNRVRB +IGNvbnRlbnQ9Ik1TSFRNTCA2LjAwLjM3OTAuMTgzMCIgbmFtZT1HRU5FUkFUT1I+ +PC9IRUFEPg0KPEJPRFk+DQo8RElWPg0KPERJVj48Rk9OVCBmYWNlPUFyaWFsIHNp +emU9Mj5USEUgQklMTCBPRiBSSUdIVFM8QlI+QW1lbmRtZW50cyAxLTEwIG9mIHRo +ZSANCkNvbnN0aXR1dGlvbjwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+ +DQo8RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPlRoZSBDb252ZW50aW9ucyBv +ZiBhIG51bWJlciBvZiB0aGUgU3RhdGVzIGhhdmluZywgDQphdCB0aGUgdGltZSBv +ZiBhZG9wdGluZyB0aGUgQ29uc3RpdHV0aW9uLCBleHByZXNzZWQgYSBkZXNpcmUs +IGluIG9yZGVyIHRvIA0KcHJldmVudCBtaXNjb25zdHJ1Y3Rpb24gb3IgYWJ1c2Ug +b2YgaXRzIHBvd2VycywgdGhhdCBmdXJ0aGVyIGRlY2xhcmF0b3J5IGFuZCANCnJl +c3RyaWN0aXZlIGNsYXVzZXMgc2hvdWxkIGJlIGFkZGVkLCBhbmQgYXMgZXh0ZW5k +aW5nIHRoZSBncm91bmQgb2YgcHVibGljIA0KY29uZmlkZW5jZSBpbiB0aGUgR292 +ZXJubWVudCB3aWxsIGJlc3QgaW5zdXJlIHRoZSBiZW5lZmljZW50IGVuZHMgb2Yg +aXRzIA0KaW5zdGl0dXRpb247IDxCUj5SZXNvbHZlZCwgYnkgdGhlIFNlbmF0ZSBh +bmQgSG91c2Ugb2YgUmVwcmVzZW50YXRpdmVzIG9mIHRoZSANClVuaXRlZCBTdGF0 +ZXMgb2YgQW1lcmljYSwgaW4gQ29uZ3Jlc3MgYXNzZW1ibGVkLCB0d28tdGhpcmRz +IG9mIGJvdGggSG91c2VzIA0KY29uY3VycmluZywgdGhhdCB0aGUgZm9sbG93aW5n +IGFydGljbGVzIGJlIHByb3Bvc2VkIHRvIHRoZSBMZWdpc2xhdHVyZXMgb2YgdGhl +IA0Kc2V2ZXJhbCBTdGF0ZXMsIGFzIGFtZW5kbWVudHMgdG8gdGhlIENvbnN0aXR1 +dGlvbiBvZiB0aGUgVW5pdGVkIFN0YXRlczsgYWxsIG9yIA0KYW55IG9mIHdoaWNo +IGFydGljbGVzLCB3aGVuIHJhdGlmaWVkIGJ5IHRocmVlLWZvdXJ0aHMgb2YgdGhl +IHNhaWQgTGVnaXNsYXR1cmVzLCANCnRvIGJlIHZhbGlkIHRvIGFsbCBpbnRlbnRz +IGFuZCBwdXJwb3NlcyBhcyBwYXJ0IG9mIHRoZSBzYWlkIENvbnN0aXR1dGlvbiwg +DQpuYW1lbHk6IDwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8RElW +PjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPkFtZW5kbWVudCBJPC9GT05UPjwvRElW +Pg0KPERJVj4mbmJzcDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1BcmlhbCBzaXpl +PTI+Q29uZ3Jlc3Mgc2hhbGwgbWFrZSBubyBsYXcgcmVzcGVjdGluZyBhbiANCmVz +dGFibGlzaG1lbnQgb2YgcmVsaWdpb24sIG9yIHByb2hpYml0aW5nIHRoZSBmcmVl +IGV4ZXJjaXNlIHRoZXJlb2Y7IG9yIA0KYWJyaWRnaW5nIHRoZSBmcmVlZG9tIG9m +IHNwZWVjaCwgb3Igb2YgdGhlIHByZXNzOyBvciB0aGUgcmlnaHQgb2YgdGhlIHBl +b3BsZSANCnBlYWNlYWJseSB0byBhc3NlbWJsZSwgYW5kIHRvIHBldGl0aW9uIHRo +ZSBnb3Zlcm5tZW50IGZvciBhIHJlZHJlc3Mgb2YgDQpncmlldmFuY2VzLiA8L0ZP +TlQ+PC9ESVY+DQo8RElWPiZuYnNwOzwvRElWPg0KPERJVj48Rk9OVCBmYWNlPUFy +aWFsIHNpemU9Mj5BbWVuZG1lbnQgSUk8L0ZPTlQ+PC9ESVY+DQo8RElWPiZuYnNw +OzwvRElWPg0KPERJVj48Rk9OVCBmYWNlPUFyaWFsIHNpemU9Mj5BIHdlbGwgcmVn +dWxhdGVkIG1pbGl0aWEsIGJlaW5nIG5lY2Vzc2FyeSB0byB0aGUgDQpzZWN1cml0 +eSBvZiBhIGZyZWUgc3RhdGUsIHRoZSByaWdodCBvZiB0aGUgcGVvcGxlIHRvIGtl +ZXAgYW5kIGJlYXIgYXJtcywgc2hhbGwgDQpub3QgYmUgaW5mcmluZ2VkLiA8L0ZP +TlQ+PC9ESVY+DQo8RElWPiZuYnNwOzwvRElWPg0KPERJVj48Rk9OVCBmYWNlPUFy +aWFsIHNpemU9Mj5BbWVuZG1lbnQgSUlJPC9GT05UPjwvRElWPg0KPERJVj4mbmJz +cDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1BcmlhbCBzaXplPTI+Tm8gc29sZGll +ciBzaGFsbCwgaW4gdGltZSBvZiBwZWFjZSBiZSBxdWFydGVyZWQgaW4gDQphbnkg +aG91c2UsIHdpdGhvdXQgdGhlIGNvbnNlbnQgb2YgdGhlIG93bmVyLCBub3IgaW4g +dGltZSBvZiB3YXIsIGJ1dCBpbiBhIG1hbm5lciANCnRvIGJlIHByZXNjcmliZWQg +YnkgbGF3LiA8L0ZPTlQ+PC9ESVY+DQo8RElWPiZuYnNwOzwvRElWPg0KPERJVj48 +Rk9OVCBmYWNlPUFyaWFsIHNpemU9Mj5BbWVuZG1lbnQgSVY8L0ZPTlQ+PC9ESVY+ +DQo8RElWPiZuYnNwOzwvRElWPg0KPERJVj48Rk9OVCBmYWNlPUFyaWFsIHNpemU9 +Mj5UaGUgcmlnaHQgb2YgdGhlIHBlb3BsZSB0byBiZSBzZWN1cmUgaW4gdGhlaXIg +DQpwZXJzb25zLCBob3VzZXMsIHBhcGVycywgYW5kIGVmZmVjdHMsIGFnYWluc3Qg +dW5yZWFzb25hYmxlIHNlYXJjaGVzIGFuZCANCnNlaXp1cmVzLCBzaGFsbCBub3Qg +YmUgdmlvbGF0ZWQsIGFuZCBubyB3YXJyYW50cyBzaGFsbCBpc3N1ZSwgYnV0IHVw +b24gcHJvYmFibGUgDQpjYXVzZSwgc3VwcG9ydGVkIGJ5IG9hdGggb3IgYWZmaXJt +YXRpb24sIGFuZCBwYXJ0aWN1bGFybHkgZGVzY3JpYmluZyB0aGUgcGxhY2UgDQp0 +byBiZSBzZWFyY2hlZCwgYW5kIHRoZSBwZXJzb25zIG9yIHRoaW5ncyB0byBiZSBz +ZWl6ZWQuIDwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8RElWPjxG +T05UIGZhY2U9QXJpYWwgc2l6ZT0yPkFtZW5kbWVudCBWPC9GT05UPjwvRElWPg0K +PERJVj4mbmJzcDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1BcmlhbCBzaXplPTI+ +Tm8gcGVyc29uIHNoYWxsIGJlIGhlbGQgdG8gYW5zd2VyIGZvciBhIGNhcGl0YWws +IG9yIA0Kb3RoZXJ3aXNlIGluZmFtb3VzIGNyaW1lLCB1bmxlc3Mgb24gYSBwcmVz +ZW50bWVudCBvciBpbmRpY3RtZW50IG9mIGEgZ3JhbmQganVyeSwgDQpleGNlcHQg +aW4gY2FzZXMgYXJpc2luZyBpbiB0aGUgbGFuZCBvciBuYXZhbCBmb3JjZXMsIG9y +IGluIHRoZSBtaWxpdGlhLCB3aGVuIGluIA0KYWN0dWFsIHNlcnZpY2UgaW4gdGlt +ZSBvZiB3YXIgb3IgcHVibGljIGRhbmdlcjsgbm9yIHNoYWxsIGFueSBwZXJzb24g +YmUgc3ViamVjdCANCmZvciB0aGUgc2FtZSBvZmZlbnNlIHRvIGJlIHR3aWNlIHB1 +dCBpbiBqZW9wYXJkeSBvZiBsaWZlIG9yIGxpbWI7IG5vciBzaGFsbCBiZSANCmNv +bXBlbGxlZCBpbiBhbnkgY3JpbWluYWwgY2FzZSB0byBiZSBhIHdpdG5lc3MgYWdh +aW5zdCBoaW1zZWxmLCBub3IgYmUgZGVwcml2ZWQgDQpvZiBsaWZlLCBsaWJlcnR5 +LCBvciBwcm9wZXJ0eSwgd2l0aG91dCBkdWUgcHJvY2VzcyBvZiBsYXc7IG5vciBz +aGFsbCBwcml2YXRlIA0KcHJvcGVydHkgYmUgdGFrZW4gZm9yIHB1YmxpYyB1c2Us +IHdpdGhvdXQganVzdCBjb21wZW5zYXRpb24uIDwvRk9OVD48L0RJVj4NCjxESVY+ +Jm5ic3A7PC9ESVY+DQo8RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPkFtZW5k +bWVudCBWSTwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8RElWPjxG +T05UIGZhY2U9QXJpYWwgc2l6ZT0yPkluIGFsbCBjcmltaW5hbCBwcm9zZWN1dGlv +bnMsIHRoZSBhY2N1c2VkIHNoYWxsIA0KZW5qb3kgdGhlIHJpZ2h0IHRvIGEgc3Bl +ZWR5IGFuZCBwdWJsaWMgdHJpYWwsIGJ5IGFuIGltcGFydGlhbCBqdXJ5IG9mIHRo +ZSBzdGF0ZSANCmFuZCBkaXN0cmljdCB3aGVyZWluIHRoZSBjcmltZSBzaGFsbCBo +YXZlIGJlZW4gY29tbWl0dGVkLCB3aGljaCBkaXN0cmljdCBzaGFsbCANCmhhdmUg +YmVlbiBwcmV2aW91c2x5IGFzY2VydGFpbmVkIGJ5IGxhdywgYW5kIHRvIGJlIGlu +Zm9ybWVkIG9mIHRoZSBuYXR1cmUgYW5kIA0KY2F1c2Ugb2YgdGhlIGFjY3VzYXRp +b247IHRvIGJlIGNvbmZyb250ZWQgd2l0aCB0aGUgd2l0bmVzc2VzIGFnYWluc3Qg +aGltOyB0byANCmhhdmUgY29tcHVsc29yeSBwcm9jZXNzIGZvciBvYnRhaW5pbmcg +d2l0bmVzc2VzIGluIGhpcyBmYXZvciwgYW5kIHRvIGhhdmUgdGhlIA0KYXNzaXN0 +YW5jZSBvZiBjb3Vuc2VsIGZvciBoaXMgZGVmZW5zZS4gPC9GT05UPjwvRElWPg0K +PERJVj4mbmJzcDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1BcmlhbCBzaXplPTI+ +QW1lbmRtZW50IFZJSTwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8 +RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPkluIHN1aXRzIGF0IGNvbW1vbiBs +YXcsIHdoZXJlIHRoZSB2YWx1ZSBpbiANCmNvbnRyb3ZlcnN5IHNoYWxsIGV4Y2Vl +ZCB0d2VudHkgZG9sbGFycywgdGhlIHJpZ2h0IG9mIHRyaWFsIGJ5IGp1cnkgc2hh +bGwgYmUgDQpwcmVzZXJ2ZWQsIGFuZCBubyBmYWN0IHRyaWVkIGJ5IGEganVyeSwg +c2hhbGwgYmUgb3RoZXJ3aXNlIHJlZXhhbWluZWQgaW4gYW55IA0KY291cnQgb2Yg +dGhlIFVuaXRlZCBTdGF0ZXMsIHRoYW4gYWNjb3JkaW5nIHRvIHRoZSBydWxlcyBv +ZiB0aGUgY29tbW9uIGxhdy4gDQo8L0ZPTlQ+PC9ESVY+DQo8RElWPiZuYnNwOzwv +RElWPg0KPERJVj48Rk9OVCBmYWNlPUFyaWFsIHNpemU9Mj5BbWVuZG1lbnQgVklJ +STwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8RElWPjxGT05UIGZh +Y2U9QXJpYWwgc2l6ZT0yPkV4Y2Vzc2l2ZSBiYWlsIHNoYWxsIG5vdCBiZSByZXF1 +aXJlZCwgbm9yIGV4Y2Vzc2l2ZSANCmZpbmVzIGltcG9zZWQsIG5vciBjcnVlbCBh +bmQgdW51c3VhbCBwdW5pc2htZW50cyBpbmZsaWN0ZWQuIDwvRk9OVD48L0RJVj4N +CjxESVY+Jm5ic3A7PC9ESVY+DQo8RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0y +PkFtZW5kbWVudCBJWDwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9ESVY+DQo8 +RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPlRoZSBlbnVtZXJhdGlvbiBpbiB0 +aGUgQ29uc3RpdHV0aW9uLCBvZiBjZXJ0YWluIA0KcmlnaHRzLCBzaGFsbCBub3Qg +YmUgY29uc3RydWVkIHRvIGRlbnkgb3IgZGlzcGFyYWdlIG90aGVycyByZXRhaW5l +ZCBieSB0aGUgDQpwZW9wbGUuIDwvRk9OVD48L0RJVj4NCjxESVY+Jm5ic3A7PC9E +SVY+DQo8RElWPjxGT05UIGZhY2U9QXJpYWwgc2l6ZT0yPkFtZW5kbWVudCBYPC9G +T05UPjwvRElWPg0KPERJVj4mbmJzcDs8L0RJVj4NCjxESVY+PEZPTlQgZmFjZT1B +cmlhbCBzaXplPTI+VGhlIHBvd2VycyBub3QgZGVsZWdhdGVkIHRvIHRoZSBVbml0 +ZWQgU3RhdGVzIGJ5IA0KdGhlIENvbnN0aXR1dGlvbiwgbm9yIHByb2hpYml0ZWQg +YnkgaXQgdG8gdGhlIHN0YXRlcywgYXJlIHJlc2VydmVkIHRvIHRoZSBzdGF0ZXMg +DQpyZXNwZWN0aXZlbHksIG9yIHRvIHRoZSBwZW9wbGUuIDwvRk9OVD48L0RJVj48 +L0RJVj48L0JPRFk+PC9IVE1MPg0KAAAfADUQAQAAAKIAAAA8ADQANQAyADAARgA2 +ADEANQAxAEQAQQBGADIAQQA0ADQAQgBBADgANwA4AEIARgAyAEYAMwA4ADAAMwA0 +ADgARQAyADYARQA1AEAAYgByAC0AZQB4AGMAaAAtAGQAZQB2ADEALgBiAHIAZQB4 +AGMAaABhAG4AZwBlAC4AZABvAGwAcABoAGkAbgBzAGUAYQByAGMAaAAuAGMAbwBt +AD4AAAAAAAMAgBD/////HwDzEAEAAAAmAAAAQgBpAGwAbAAgAG8AZgAgAFIAaQBn +AGgAdABzAC4ARQBNAEwAAAAAAAsA9BAAAAAACwD1EAAAAAALAPYQAAAAAEAABzBR +lpFluknFAUAACDBRlpFluknFAQMA3j+fTgAAAwDxPwkEAAAfAPg/AQAAABAAAAAz +AGsAcgBlAGwAYQB5AAAAAgH5PwEAAABjAAAAAAAAANynQMjAQhAatLkIACsv4YIB +AAAAAAAAAC9PPUJSLUVYQ0gtVEVTVC9PVT1GSVJTVCBBRE1JTklTVFJBVElWRSBH +Uk9VUC9DTj1SRUNJUElFTlRTL0NOPTNLUkVMQVkAAB8A+j8BAAAAEAAAADMAawBy +AGUAbABhAHkAAAACAfs/AQAAAGMAAAAAAAAA3KdAyMBCEBq0uQgAKy/hggEAAAAA +AAAAL089QlItRVhDSC1URVNUL09VPUZJUlNUIEFETUlOSVNUUkFUSVZFIEdST1VQ +L0NOPVJFQ0lQSUVOVFMvQ049M0tSRUxBWQAAAwD9P+QEAAADABlAAAAAAAMAGkAA +AAAAHwAwQAEAAAAQAAAAMwBLAFIARQBMAEEAWQAAAB8AMUABAAAAEAAAADMASwBS +AEUATABBAFkAAAAfADhAAQAAABAAAAAzAEsAUgBFAEwAQQBZAAAAHwA5QAEAAAAQ +AAAAMwBLAFIARQBMAEEAWQAAAAMAdkD/////AwACWQAAFgADAAlZAgAAAAsAhYEI +IAYAAAAAAMAAAAAAAABGAAAAAA6FAAAAAAAAAwCdgQggBgAAAAAAwAAAAAAAAEYA +AAAAUoUAAJjDAQAfAJ6BCCAGAAAAAADAAAAAAAAARgAAAABUhQAAAQAAAAoAAAAx +ADEALgAwAAAAAAADAOmBCCAGAAAAAADAAAAAAAAARgAAAAABhQAAAAAAAAsA7oEI +IAYAAAAAAMAAAAAAAABGAAAAAAOFAAAAAAAAAwD4gQggBgAAAAAAwAAAAAAAAEYA +AAAAEIUAAAAAAAADAP+BCCAGAAAAAADAAAAAAAAARgAAAAAYhQAAAAAAAAsAIIII +IAYAAAAAAMAAAAAAAABGAAAAAAaFAAAAAAAACwAkggggBgAAAAAAwAAAAAAAAEYA +AAAAgoUAAAAAAAAfACaCCCAGAAAAAADAAAAAAAAARgAAAACDhQAAAQAAACYAAAA0 +ADAANQAxADMAMQA1ADEANwAtADIANQAwADQAMgAwADAANQAAAAAAAwBxggggBgAA +AAAAwAAAAAAAAEYAAAAAk4UAAAAAAAALACkAAAAAAAsAIwAAAAAAAgF/AAEAAABR +AAAAPDQ1MjBGNjE1MURBRjJBNDRCQTg3OEJGMkYzODAzNDhFMjZFNUBici1leGNo +LWRldjEuYnJleGNoYW5nZS5kb2xwaGluc2VhcmNoLmNvbT4AAAAAC/o= + +--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_-- + diff --git a/spec/fixtures/files/tnef-attachment-truncated.email b/spec/fixtures/files/tnef-attachment-truncated.email new file mode 100644 index 000000000..365a5a442 --- /dev/null +++ b/spec/fixtures/files/tnef-attachment-truncated.email @@ -0,0 +1,34 @@ +From hello@blah.local Fri Feb 21 16:23:14 2013 +Return-path: <bar@example.org> +Envelope-to: foo@example.org +Delivery-date: Fri, 21 Feb 2013 16:23:14 +0000 +Content-Type: multipart/mixed; + boundary="_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_" +From: <bar@example.org> +To: <foo@example.org> +Sender: <hello@blah.local> +Date: Fri, 21 Feb 2013 16:23:04 +0000 +Subject: here's a useless email +Message-ID: <12345@blah.local> +Accept-Language: en-US, en-GB +Content-Language: en-US +X-MS-Has-Attach: +X-MS-TNEF-Correlator: <12345@blah.local> +acceptlanguage: en-US, en-GB +MIME-Version: 1.0 + +--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_ +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: quoted-printable + +Some introductory text here, before the malformed TNEF attachment. + +--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_ +Content-Disposition: attachment; filename="winmail.dat" +Content-Transfer-Encoding: base64 +Content-Type: application/ms-tnef; name="winmail.dat" + +eJ8+IkV9AQaQCAAEAAAAAAABAAEAAQeQBgAIAAAA5AQAAAAAAADoAAEJgAEAIQAAAEMyRUUzRUYx + +--_000_553468B23EE29B4F8836CBD0E1B2A15A275C3AA855POLNIEXMBV2po_-- + diff --git a/spec/fixtures/locale/en/app.po b/spec/fixtures/locale/en/app.po index 3fe8a569b..ee5c8d9c8 100644 --- a/spec/fixtures/locale/en/app.po +++ b/spec/fixtures/locale/en/app.po @@ -31,7 +31,7 @@ msgid "" msgstr "" #: app/views/comment/_comment_form.rhtml:16 -msgid " (<strong>no ranty</strong> politics, read our <a href=\"%s\">moderation policy</a>)" +msgid " (<strong>no ranty</strong> politics, read our <a href=\"{{url}}\">moderation policy</a>)" msgstr "" #: app/views/request/upload_response.rhtml:40 @@ -71,7 +71,7 @@ msgstr "" #: app/views/public_body/view_email.rhtml:30 msgid "" -" If you know the address to use, then please <a href=\"%s\">send it to us</a>.\n" +" If you know the address to use, then please <a href=\"{{url}}\">send it to us</a>.\n" " You may be able to find the address on their website, or by phoning them up and asking." msgstr "" @@ -123,20 +123,20 @@ msgid " when you send this message." msgstr "" #: app/views/public_body/show.rhtml:87 -msgid "%d Freedom of Information request to %s" -msgid_plural "%d Freedom of Information requests to %s" +msgid "{{count}} Freedom of Information request to {{public_body_name}}" +msgid_plural "{{count}} Freedom of Information requests to {{public_body_name}}" msgstr[0] "" msgstr[1] "" #: app/views/general/frontpage.rhtml:43 -msgid "%d request" -msgid_plural "%d requests" +msgid "{{count}} request" +msgid_plural "{{count}} requests" msgstr[0] "" msgstr[1] "" #: app/views/public_body/_body_listing_single.rhtml:21 -msgid "%d request made." -msgid_plural "%d requests made." +msgid "{{count}} request made." +msgid_plural "{{count}} requests made." msgstr[0] "" msgstr[1] "" @@ -187,43 +187,37 @@ msgstr "" msgid "3. Now check your request" msgstr "" -#: app/views/public_body/show.rhtml:56 -msgid "<a class=\"link_button_green\" href=\"{{url}}\">{{text}}</a>" -msgstr "" - #: app/views/request/_after_actions.rhtml:9 -msgid "<a href=\"%s\">Add an annotation</a> (to help the requester or others)" +msgid "<a href=\"{{url}}\">Add an annotation</a> (to help the requester or others)" msgstr "" #: app/views/public_body/list.rhtml:29 -msgid "<a href=\"%s\">Are we missing a public authority?</a>." +msgid "Are we missing a public authority?" msgstr "" #: app/views/request/_sidebar.rhtml:39 -msgid "" -"<a href=\"%s\">Are you the owner of\n" -" any commercial copyright on this page?</a>" +msgid "Are you the owner of any commercial copyright on this page?" msgstr "" #: app/views/general/search.rhtml:168 -msgid "<a href=\"%s\">Browse all</a> or <a href=\"%s\">ask us to add one</a>." +msgid "<a href=\"{{browse_url}}\">Browse all</a> or <a href=\"{{add_url}}\">ask us to add one</a>." msgstr "" #: app/views/public_body/list.rhtml:51 -msgid "<a href=\"%s\">Can't find the one you want?</a>" +msgid "Can't find the one you want?" msgstr "" #: app/views/user/show.rhtml:118 -msgid "<a href=\"%s\">Sign in</a> to change password, subscriptions and more ({{user_name}} only)" +msgid "<a href=\"{{url}}\">Sign in</a> to change password, subscriptions and more ({{user_name}} only)" msgstr "" #: app/views/request/_followup.rhtml:66 app/views/request/_followup.rhtml:73 #: app/views/request/show.rhtml:83 app/views/request/show.rhtml:87 -msgid "<a href=\"%s\">details</a>" +msgid "details" msgstr "" #: app/views/request/_followup.rhtml:101 -msgid "<a href=\"%s\">what's that?</a>" +msgid "what's that?" msgstr "" #: app/controllers/request_game_controller.rb:23 @@ -293,11 +287,11 @@ msgid "<p>We're glad you got some of the information that you wanted. If you fou msgstr "" #: app/controllers/request_controller.rb:318 -msgid "<p>You do not need to include your email in the request in order to get a reply (<a href=\"%s\">details</a>).</p>" +msgid "<p>You do not need to include your email in the request in order to get a reply (<a href=\"{{url}}\">details</a>).</p>" msgstr "" #: app/controllers/request_controller.rb:316 -msgid "<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"%s\">details</a>).</p>" +msgid "<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"{{url}}\">details</a>).</p>" msgstr "" #: app/controllers/request_controller.rb:324 @@ -327,7 +321,7 @@ msgstr "" #: app/views/request/new.rhtml:135 msgid "" "<strong> Can I request information about myself?</strong>\n" -"\t\t\t<a href=\"%s\">No! (Click here for details)</a>" +"\t\t\t<a href=\"{{url}}\">No! (Click here for details)</a>" msgstr "" #: app/views/general/_advanced_search_tips.rhtml:12 @@ -413,7 +407,7 @@ msgstr "" #: app/views/request/preview.rhtml:31 msgid "" "<strong>Privacy note:</strong> If you want to request private information about\n" -" yourself then <a href=\"%s\">click here</a>." +" yourself then <a href=\"{{url}}\">click here</a>." msgstr "" #: app/views/user/set_crop_profile_photo.rhtml:35 @@ -576,7 +570,7 @@ msgstr "" #: app/views/request/show_response.rhtml:29 msgid "" "At the bottom of this page, write a reply to them trying to persuade them to scan it in\n" -" (<a href=\"%s\">more details</a>)." +" (<a href=\"{{url}}\">more details</a>)." msgstr "" #: app/views/request/upload_response.rhtml:33 @@ -921,7 +915,7 @@ msgstr "" #: app/views/request/upload_response.rhtml:23 msgid "" "Enter your response below. You may attach one file (use email, or \n" -"<a href=\"%s\">contact us</a> if you need more)." +"<a href=\"{{url}}\">contact us</a> if you need more)." msgstr "" #: app/models/info_request.rb:259 app/models/info_request.rb:277 @@ -952,14 +946,14 @@ msgstr "" msgid "" "Everything that you enter on this page\n" " will be <strong>displayed publicly</strong> on\n" -" this website forever (<a href=\"%s\">why?</a>)." +" this website forever (<a href=\"{{url}}\">why?</a>)." msgstr "" #: app/views/request/new.rhtml:120 msgid "" "Everything that you enter on this page, including <strong>your name</strong>,\n" " will be <strong>displayed publicly</strong> on\n" -" this website forever (<a href=\"%s\">why?</a>)." +" this website forever (<a href=\"{{url}}\">why?</a>)." msgstr "" #: locale/model_attributes.rb:68 @@ -1007,7 +1001,7 @@ msgid "Failed to convert image to a PNG" msgstr "" #: app/models/profile_photo.rb:105 -msgid "Failed to convert image to the correct size: at %{cols}x%{rows}, need %{width}x%{height}" +msgid "Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}" msgstr "" #: app/views/general/search.rhtml:117 @@ -1018,7 +1012,7 @@ msgstr "" msgid "" "First, type in the <strong>name of the UK public authority</strong> you'd \n" " like information from. <strong>By law, they have to respond</strong>\n" -" (<a href=\"%s#%s\">why?</a>)." +" (<a href=\"{{url}}\">why?</a>)." msgstr "" #: locale/model_attributes.rb:88 @@ -1147,7 +1141,7 @@ msgstr "" msgid "" "From the request page, try replying to a particular message, rather than sending\n" " a general followup. If you need to make a general followup, and know\n" -" an email which will go to the right place, please <a href=\"%s\">send it to us</a>." +" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>." msgstr "" #: app/views/request/_correspondence.rhtml:12 @@ -1278,7 +1272,7 @@ msgid "I've received an <strong>error message</strong>" msgstr "" #: app/views/public_body/view_email.rhtml:28 -msgid "If the address is wrong, or you know a better address, please <a href=\"%s\">contact us</a>." +msgid "If the address is wrong, or you know a better address, please <a href=\"{{url}}\">contact us</a>." msgstr "" #: app/views/request_mailer/stopped_responses.rhtml:10 @@ -1292,21 +1286,21 @@ msgstr "" msgid "" "If you are dissatisfied by the response you got from\n" " the public authority, you have the right to\n" -" complain (<a href=\"%s\">details</a>)." +" complain (<a href=\"{{url}}\">details</a>)." msgstr "" #: app/views/user/no_cookies.rhtml:20 -msgid "If you are still having trouble, please <a href=\"%s\">contact us</a>." +msgid "If you are still having trouble, please <a href=\"{{url}}\">contact us</a>." msgstr "" #: app/views/request/hidden.rhtml:15 -msgid "If you are the requester, then you may <a href=\"%s\">sign in</a> to view the request." +msgid "If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the request." msgstr "" #: app/views/request/new.rhtml:123 msgid "" "If you are thinking of using a pseudonym,\n" -" please <a href=\"%s\">read this first</a>." +" please <a href=\"{{url}}\">read this first</a>." msgstr "" #: app/views/request/show.rhtml:105 @@ -1497,7 +1491,7 @@ msgid "Joined {{site_name}} in" msgstr "" #: app/views/request/new.rhtml:106 -msgid "Keep it <strong>focused</strong>, you'll be more likely to get what you want (<a href=\"%s\">why?</a>)." +msgid "Keep it <strong>focused</strong>, you'll be more likely to get what you want (<a href=\"{{url}}\">why?</a>)." msgstr "" #: app/views/request/_request_filter_form.rhtml:6 @@ -1837,7 +1831,7 @@ msgid "Please" msgstr "" #: app/views/user/no_cookies.rhtml:15 -msgid "Please <a href=\"%s\">get in touch</a> with us so we can fix it." +msgid "Please <a href=\"{{url}}\">get in touch</a> with us so we can fix it." msgstr "" #: app/views/request/show.rhtml:52 @@ -1990,7 +1984,7 @@ msgid "" msgstr "" #: app/models/outgoing_message.rb:157 -msgid "Please sign at the bottom with your name, or alter the \"%{signoff}\" signature" +msgid "Please sign at the bottom with your name, or alter the \"{{signoff}}\" signature" msgstr "" #: app/views/user/sign.rhtml:8 @@ -2337,8 +2331,9 @@ msgid "Search the site to find what you were looking for." msgstr "" #: app/views/public_body/show.rhtml:85 -msgid "Search within the %d Freedom of Information requests to %s" -msgid_plural "Search within the %d Freedom of Information requests made to %s" +msgid "Search within the {{count}} Freedom of Information requests to {{public_body_name}}" +msgid_plural "Search within the {{count}} Freedom of Information requests to {{public_body_name}}" + msgstr[0] "" msgstr[1] "" @@ -2680,10 +2675,7 @@ msgid "The request was refused by the public authority" msgstr "" #: app/views/request/hidden.rhtml:9 -msgid "" -"The request you have tried to view has been removed. There are\n" -"various reasons why we might have done this, sorry we can't be more specific here. Please <a\n" -" href=\"%s\">contact us</a> if you have any questions." +msgid "The request you have tried to view has been removed. There are\\nvarious reasons why we might have done this, sorry we can't be more specific here. Please <a\\n href=\"{{url}}\">contact us</a> if you have any questions." msgstr "" #: app/views/general/_advanced_search_tips.rhtml:36 @@ -2692,14 +2684,14 @@ msgstr "" #: app/views/request/_followup.rhtml:59 msgid "" -"The response to your request has been <strong>delayed</strong>. You can say that, \n" +"The response to your request has been <strong>delayed</strong>. You can say that,\n" " by law, the authority should normally have responded\n" " <strong>promptly</strong> and" msgstr "" #: app/views/request/_followup.rhtml:71 msgid "" -"The response to your request is <strong>long overdue</strong>. You can say that, by \n" +"The response to your request is <strong>long overdue</strong>. You can say that, by\n" " law, under all circumstances, the authority should have responded\n" " by now" msgstr "" @@ -2804,15 +2796,9 @@ msgstr "" msgid "There are {{count}} new annotations on your {{info_request}} request. Follow this link to see what they wrote." msgstr "" -#: app/views/public_body/show.rhtml:7 -msgid "There is %d person following this authority" -msgid_plural "There are %d people following this authority" -msgstr[0] "" -msgstr[1] "" - #: app/views/request/_sidebar.rhtml:5 -msgid "There is %d person following this request" -msgid_plural "There are %d people following this request" +msgid "There is {{count}} person following this request" +msgid_plural "There are {{count}} people following this request" msgstr[0] "" msgstr[1] "" @@ -2876,7 +2862,7 @@ msgstr "" #: app/views/request/_hidden_correspondence.rhtml:23 msgid "" "This comment has been hidden. See annotations to\n" -" find out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response." +" find out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response." msgstr "" #: app/views/request/new.rhtml:63 @@ -2906,7 +2892,7 @@ msgstr "" #: app/views/request/_hidden_correspondence.rhtml:17 msgid "" "This outgoing message has been hidden. See annotations to\n" -"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response." +"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response." msgstr "" #: app/views/request/_describe_state.rhtml:44 @@ -2919,14 +2905,14 @@ msgid "This person has made no Freedom of Information requests using this site." msgstr "" #: app/views/user/show.rhtml:149 -msgid "This person's %d Freedom of Information request" -msgid_plural "This person's %d Freedom of Information requests" +msgid "This person's {{count}} Freedom of Information request" +msgid_plural "This person's {{count}} Freedom of Information requests" msgstr[0] "" msgstr[1] "" #: app/views/user/show.rhtml:179 -msgid "This person's %d annotation" -msgid_plural "This person's %d annotations" +msgid "This person's {{count}} annotation" +msgid_plural "This person's {{count}} annotations" msgstr[0] "" msgstr[1] "" @@ -2965,7 +2951,7 @@ msgstr "" #: app/views/request/show.rhtml:11 msgid "" "This request is hidden, so that only you the requester can see it. Please\n" -" <a href=\"%s\">contact us</a> if you are not sure why." +" <a href=\"{{url}}\">contact us</a> if you are not sure why." msgstr "" #: app/views/request/_describe_state.rhtml:7 @@ -2976,7 +2962,7 @@ msgstr "" #: app/views/request/_hidden_correspondence.rhtml:10 msgid "" "This response has been hidden. See annotations to find out why.\n" -" If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response." +" If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response." msgstr "" #: app/views/request/details.rhtml:6 @@ -3202,7 +3188,7 @@ msgstr "" msgid "" "Unfortunately we don't know the FOI\n" "email address for that authority, so we can't validate this.\n" -"Please <a href=\"%s\">contact us</a> to sort it out." +"Please <a href=\"{{url}}\">contact us</a> to sort it out." msgstr "" #: app/views/request/new_bad_contact.rhtml:5 @@ -3439,7 +3425,7 @@ msgid "" msgstr "" #: app/views/request/new_please_describe.rhtml:16 -msgid "When you're done, <strong>come back here</strong>, <a href=\"%s\">reload this page</a> and file your new request." +msgid "When you're done, <strong>come back here</strong>, <a href=\"{{url}}\">reload this page</a> and file your new request." msgstr "" #: app/views/request/show_response.rhtml:13 @@ -3571,7 +3557,7 @@ msgstr "" msgid "" "You may be able to find\n" " one on their website, or by phoning them up and asking. If you manage\n" -" to find one, then please <a href=\"%s\">send it to us</a>." +" to find one, then please <a href=\"{{url}}\">send it to us</a>." msgstr "" #: app/views/request/new_bad_contact.rhtml:6 @@ -3642,24 +3628,19 @@ msgid "You've now cleared your profile photo" msgstr "" #: app/views/user/show.rhtml:149 -msgid "Your %d Freedom of Information request" -msgid_plural "Your %d Freedom of Information requests" +msgid "Your {{count}} Freedom of Information request" +msgid_plural "Your {{count}} Freedom of Information requests" msgstr[0] "" msgstr[1] "" #: app/views/user/show.rhtml:179 -msgid "Your %d annotation" -msgid_plural "Your %d annotations" +msgid "Your {{count}} annotation" +msgid_plural "Your {{count}} annotations" msgstr[0] "" msgstr[1] "" #: app/views/user/_signup.rhtml:22 -msgid "" -"Your <strong>name will appear publicly</strong> \n" -" (<a href=\"%s\">why?</a>)\n" -" on this website and in search engines. If you\n" -" are thinking of using a pseudonym, please \n" -" <a href=\"%s\">read this first</a>." +msgid "Your <strong>name will appear publicly</strong>\\n (<a href=\"{{why_url}}\">why?</a>)\\n on this website and in search engines. If you\\n are thinking of using a pseudonym, please\\n <a href=\"{{help_url}}\">read this first</a>." msgstr "" #: app/views/user/show.rhtml:172 @@ -3682,7 +3663,7 @@ msgid "Your email subscriptions" msgstr "" #: app/controllers/request_controller.rb:598 -msgid "Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href=\"%s\">contact us</a> if you really want to send a follow up message." +msgid "Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href=\"{{url}}\">contact us</a> if you really want to send a follow up message." msgstr "" #: app/controllers/request_controller.rb:626 @@ -3712,7 +3693,7 @@ msgstr "" #: app/views/request/preview.rhtml:8 msgid "" "Your name, request and any responses will appear in <strong>search engines</strong>\n" -" (<a href=\"%s\">details</a>)." +" (<a href=\"{{url}}\">details</a>)." msgstr "" #: app/views/user/_signup.rhtml:18 @@ -3746,7 +3727,7 @@ msgid "Your request:" msgstr "" #: app/views/request/upload_response.rhtml:8 -msgid "Your response will <strong>appear on the Internet</strong>, <a href=\"%s\">read why</a> and answers to other questions." +msgid "Your response will <strong>appear on the Internet</strong>, <a href=\"{{url}}\">read why</a> and answers to other questions." msgstr "" #: app/views/comment/new.rhtml:63 @@ -3990,7 +3971,7 @@ msgid "" "no longer exists. If you are trying to make\n" " From the request page, try replying to a particular message, rather than sending\n" " a general followup. If you need to make a general followup, and know\n" -" an email which will go to the right place, please <a href=\"%s\">send it to us</a>." +" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>." msgstr "" #: app/views/request/show.rhtml:72 diff --git a/spec/fixtures/locale/en_GB/app.po b/spec/fixtures/locale/en_GB/app.po index 3fe8a569b..84997a319 100644 --- a/spec/fixtures/locale/en_GB/app.po +++ b/spec/fixtures/locale/en_GB/app.po @@ -31,7 +31,7 @@ msgid "" msgstr "" #: app/views/comment/_comment_form.rhtml:16 -msgid " (<strong>no ranty</strong> politics, read our <a href=\"%s\">moderation policy</a>)" +msgid " (<strong>no ranty</strong> politics, read our <a href=\"{{url}}\">moderation policy</a>)" msgstr "" #: app/views/request/upload_response.rhtml:40 @@ -71,7 +71,7 @@ msgstr "" #: app/views/public_body/view_email.rhtml:30 msgid "" -" If you know the address to use, then please <a href=\"%s\">send it to us</a>.\n" +" If you know the address to use, then please <a href=\"{{url}}\">send it to us</a>.\n" " You may be able to find the address on their website, or by phoning them up and asking." msgstr "" @@ -123,20 +123,20 @@ msgid " when you send this message." msgstr "" #: app/views/public_body/show.rhtml:87 -msgid "%d Freedom of Information request to %s" -msgid_plural "%d Freedom of Information requests to %s" +msgid "{{count}} Freedom of Information request to {{public_body_name}}" +msgid_plural "{{count}} Freedom of Information requests to {{public_body_name}}" msgstr[0] "" msgstr[1] "" #: app/views/general/frontpage.rhtml:43 -msgid "%d request" -msgid_plural "%d requests" +msgid "{{count}} request" +msgid_plural "{{count}} requests" msgstr[0] "" msgstr[1] "" #: app/views/public_body/_body_listing_single.rhtml:21 -msgid "%d request made." -msgid_plural "%d requests made." +msgid "{{count}} request made." +msgid_plural "{{count}} requests made." msgstr[0] "" msgstr[1] "" @@ -187,43 +187,37 @@ msgstr "" msgid "3. Now check your request" msgstr "" -#: app/views/public_body/show.rhtml:56 -msgid "<a class=\"link_button_green\" href=\"{{url}}\">{{text}}</a>" -msgstr "" - #: app/views/request/_after_actions.rhtml:9 -msgid "<a href=\"%s\">Add an annotation</a> (to help the requester or others)" +msgid "<a href=\"{{url}}\">Add an annotation</a> (to help the requester or others)" msgstr "" #: app/views/public_body/list.rhtml:29 -msgid "<a href=\"%s\">Are we missing a public authority?</a>." +msgid "Are we missing a public authority?" msgstr "" #: app/views/request/_sidebar.rhtml:39 -msgid "" -"<a href=\"%s\">Are you the owner of\n" -" any commercial copyright on this page?</a>" +msgid "Are you the owner of any commercial copyright on this page?" msgstr "" #: app/views/general/search.rhtml:168 -msgid "<a href=\"%s\">Browse all</a> or <a href=\"%s\">ask us to add one</a>." +msgid "<a href=\"{{browse_url}}\">Browse all</a> or <a href=\"{{add_url}}\">ask us to add one</a>." msgstr "" #: app/views/public_body/list.rhtml:51 -msgid "<a href=\"%s\">Can't find the one you want?</a>" +msgid "Can't find the one you want?" msgstr "" #: app/views/user/show.rhtml:118 -msgid "<a href=\"%s\">Sign in</a> to change password, subscriptions and more ({{user_name}} only)" +msgid "<a href=\"{{url}}\">Sign in</a> to change password, subscriptions and more ({{user_name}} only)" msgstr "" #: app/views/request/_followup.rhtml:66 app/views/request/_followup.rhtml:73 #: app/views/request/show.rhtml:83 app/views/request/show.rhtml:87 -msgid "<a href=\"%s\">details</a>" +msgid "details" msgstr "" #: app/views/request/_followup.rhtml:101 -msgid "<a href=\"%s\">what's that?</a>" +msgid "what's that?" msgstr "" #: app/controllers/request_game_controller.rb:23 @@ -293,11 +287,11 @@ msgid "<p>We're glad you got some of the information that you wanted. If you fou msgstr "" #: app/controllers/request_controller.rb:318 -msgid "<p>You do not need to include your email in the request in order to get a reply (<a href=\"%s\">details</a>).</p>" +msgid "<p>You do not need to include your email in the request in order to get a reply (<a href=\"{{url}}\">details</a>).</p>" msgstr "" #: app/controllers/request_controller.rb:316 -msgid "<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"%s\">details</a>).</p>" +msgid "<p>You do not need to include your email in the request in order to get a reply, as we will ask for it on the next screen (<a href=\"{{url}}\">details</a>).</p>" msgstr "" #: app/controllers/request_controller.rb:324 @@ -327,7 +321,7 @@ msgstr "" #: app/views/request/new.rhtml:135 msgid "" "<strong> Can I request information about myself?</strong>\n" -"\t\t\t<a href=\"%s\">No! (Click here for details)</a>" +"\t\t\t<a href=\"{{url}}\">No! (Click here for details)</a>" msgstr "" #: app/views/general/_advanced_search_tips.rhtml:12 @@ -413,7 +407,7 @@ msgstr "" #: app/views/request/preview.rhtml:31 msgid "" "<strong>Privacy note:</strong> If you want to request private information about\n" -" yourself then <a href=\"%s\">click here</a>." +" yourself then <a href=\"{{url}}\">click here</a>." msgstr "" #: app/views/user/set_crop_profile_photo.rhtml:35 @@ -576,7 +570,7 @@ msgstr "" #: app/views/request/show_response.rhtml:29 msgid "" "At the bottom of this page, write a reply to them trying to persuade them to scan it in\n" -" (<a href=\"%s\">more details</a>)." +" (<a href=\"{{url}}\">more details</a>)." msgstr "" #: app/views/request/upload_response.rhtml:33 @@ -921,7 +915,7 @@ msgstr "" #: app/views/request/upload_response.rhtml:23 msgid "" "Enter your response below. You may attach one file (use email, or \n" -"<a href=\"%s\">contact us</a> if you need more)." +"<a href=\"{{url}}\">contact us</a> if you need more)." msgstr "" #: app/models/info_request.rb:259 app/models/info_request.rb:277 @@ -952,14 +946,14 @@ msgstr "" msgid "" "Everything that you enter on this page\n" " will be <strong>displayed publicly</strong> on\n" -" this website forever (<a href=\"%s\">why?</a>)." +" this website forever (<a href=\"{{url}}\">why?</a>)." msgstr "" #: app/views/request/new.rhtml:120 msgid "" "Everything that you enter on this page, including <strong>your name</strong>,\n" " will be <strong>displayed publicly</strong> on\n" -" this website forever (<a href=\"%s\">why?</a>)." +" this website forever (<a href=\"{{url}}\">why?</a>)." msgstr "" #: locale/model_attributes.rb:68 @@ -1007,7 +1001,7 @@ msgid "Failed to convert image to a PNG" msgstr "" #: app/models/profile_photo.rb:105 -msgid "Failed to convert image to the correct size: at %{cols}x%{rows}, need %{width}x%{height}" +msgid "Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}" msgstr "" #: app/views/general/search.rhtml:117 @@ -1018,7 +1012,7 @@ msgstr "" msgid "" "First, type in the <strong>name of the UK public authority</strong> you'd \n" " like information from. <strong>By law, they have to respond</strong>\n" -" (<a href=\"%s#%s\">why?</a>)." +" (<a href=\"{{url}}\">why?</a>)." msgstr "" #: locale/model_attributes.rb:88 @@ -1147,7 +1141,7 @@ msgstr "" msgid "" "From the request page, try replying to a particular message, rather than sending\n" " a general followup. If you need to make a general followup, and know\n" -" an email which will go to the right place, please <a href=\"%s\">send it to us</a>." +" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>." msgstr "" #: app/views/request/_correspondence.rhtml:12 @@ -1278,7 +1272,7 @@ msgid "I've received an <strong>error message</strong>" msgstr "" #: app/views/public_body/view_email.rhtml:28 -msgid "If the address is wrong, or you know a better address, please <a href=\"%s\">contact us</a>." +msgid "If the address is wrong, or you know a better address, please <a href=\"{{url}}\">contact us</a>." msgstr "" #: app/views/request_mailer/stopped_responses.rhtml:10 @@ -1292,21 +1286,21 @@ msgstr "" msgid "" "If you are dissatisfied by the response you got from\n" " the public authority, you have the right to\n" -" complain (<a href=\"%s\">details</a>)." +" complain (<a href=\"{{url}}\">details</a>)." msgstr "" #: app/views/user/no_cookies.rhtml:20 -msgid "If you are still having trouble, please <a href=\"%s\">contact us</a>." +msgid "If you are still having trouble, please <a href=\"{{url}}\">contact us</a>." msgstr "" #: app/views/request/hidden.rhtml:15 -msgid "If you are the requester, then you may <a href=\"%s\">sign in</a> to view the request." +msgid "If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the request." msgstr "" #: app/views/request/new.rhtml:123 msgid "" "If you are thinking of using a pseudonym,\n" -" please <a href=\"%s\">read this first</a>." +" please <a href=\"{{url}}\">read this first</a>." msgstr "" #: app/views/request/show.rhtml:105 @@ -1497,7 +1491,7 @@ msgid "Joined {{site_name}} in" msgstr "" #: app/views/request/new.rhtml:106 -msgid "Keep it <strong>focused</strong>, you'll be more likely to get what you want (<a href=\"%s\">why?</a>)." +msgid "Keep it <strong>focused</strong>, you'll be more likely to get what you want (<a href=\"{{url}}\">why?</a>)." msgstr "" #: app/views/request/_request_filter_form.rhtml:6 @@ -1837,7 +1831,7 @@ msgid "Please" msgstr "" #: app/views/user/no_cookies.rhtml:15 -msgid "Please <a href=\"%s\">get in touch</a> with us so we can fix it." +msgid "Please <a href=\"{{url}}\">get in touch</a> with us so we can fix it." msgstr "" #: app/views/request/show.rhtml:52 @@ -1990,7 +1984,7 @@ msgid "" msgstr "" #: app/models/outgoing_message.rb:157 -msgid "Please sign at the bottom with your name, or alter the \"%{signoff}\" signature" +msgid "Please sign at the bottom with your name, or alter the \"{{signoff}}\" signature" msgstr "" #: app/views/user/sign.rhtml:8 @@ -2337,8 +2331,8 @@ msgid "Search the site to find what you were looking for." msgstr "" #: app/views/public_body/show.rhtml:85 -msgid "Search within the %d Freedom of Information requests to %s" -msgid_plural "Search within the %d Freedom of Information requests made to %s" +msgid "Search within the {{count}} Freedom of Information requests to {{public_body_name}}" +msgid_plural "Search within the {{count}} Freedom of Information requests to {{public_body_name}}" msgstr[0] "" msgstr[1] "" @@ -2680,10 +2674,7 @@ msgid "The request was refused by the public authority" msgstr "" #: app/views/request/hidden.rhtml:9 -msgid "" -"The request you have tried to view has been removed. There are\n" -"various reasons why we might have done this, sorry we can't be more specific here. Please <a\n" -" href=\"%s\">contact us</a> if you have any questions." +msgid "The request you have tried to view has been removed. There are\\nvarious reasons why we might have done this, sorry we can't be more specific here. Please <a\\n href=\"{{url}}\">contact us</a> if you have any questions." msgstr "" #: app/views/general/_advanced_search_tips.rhtml:36 @@ -2692,14 +2683,14 @@ msgstr "" #: app/views/request/_followup.rhtml:59 msgid "" -"The response to your request has been <strong>delayed</strong>. You can say that, \n" +"The response to your request has been <strong>delayed</strong>. You can say that,\n" " by law, the authority should normally have responded\n" " <strong>promptly</strong> and" msgstr "" #: app/views/request/_followup.rhtml:71 msgid "" -"The response to your request is <strong>long overdue</strong>. You can say that, by \n" +"The response to your request is <strong>long overdue</strong>. You can say that, by\n" " law, under all circumstances, the authority should have responded\n" " by now" msgstr "" @@ -2804,15 +2795,9 @@ msgstr "" msgid "There are {{count}} new annotations on your {{info_request}} request. Follow this link to see what they wrote." msgstr "" -#: app/views/public_body/show.rhtml:7 -msgid "There is %d person following this authority" -msgid_plural "There are %d people following this authority" -msgstr[0] "" -msgstr[1] "" - #: app/views/request/_sidebar.rhtml:5 -msgid "There is %d person following this request" -msgid_plural "There are %d people following this request" +msgid "There is {{count}} person following this request" +msgid_plural "There are {{count}} people following this request" msgstr[0] "" msgstr[1] "" @@ -2876,7 +2861,7 @@ msgstr "" #: app/views/request/_hidden_correspondence.rhtml:23 msgid "" "This comment has been hidden. See annotations to\n" -" find out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response." +" find out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response." msgstr "" #: app/views/request/new.rhtml:63 @@ -2906,7 +2891,7 @@ msgstr "" #: app/views/request/_hidden_correspondence.rhtml:17 msgid "" "This outgoing message has been hidden. See annotations to\n" -"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response." +"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response." msgstr "" #: app/views/request/_describe_state.rhtml:44 @@ -2919,14 +2904,14 @@ msgid "This person has made no Freedom of Information requests using this site." msgstr "" #: app/views/user/show.rhtml:149 -msgid "This person's %d Freedom of Information request" -msgid_plural "This person's %d Freedom of Information requests" +msgid "This person's {{count}} Freedom of Information request" +msgid_plural "This person's {{count}} Freedom of Information requests" msgstr[0] "" msgstr[1] "" #: app/views/user/show.rhtml:179 -msgid "This person's %d annotation" -msgid_plural "This person's %d annotations" +msgid "This person's {{count}} annotation" +msgid_plural "This person's {{count}} annotations" msgstr[0] "" msgstr[1] "" @@ -2965,7 +2950,7 @@ msgstr "" #: app/views/request/show.rhtml:11 msgid "" "This request is hidden, so that only you the requester can see it. Please\n" -" <a href=\"%s\">contact us</a> if you are not sure why." +" <a href=\"{{url}}\">contact us</a> if you are not sure why." msgstr "" #: app/views/request/_describe_state.rhtml:7 @@ -2976,7 +2961,7 @@ msgstr "" #: app/views/request/_hidden_correspondence.rhtml:10 msgid "" "This response has been hidden. See annotations to find out why.\n" -" If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response." +" If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response." msgstr "" #: app/views/request/details.rhtml:6 @@ -3202,7 +3187,7 @@ msgstr "" msgid "" "Unfortunately we don't know the FOI\n" "email address for that authority, so we can't validate this.\n" -"Please <a href=\"%s\">contact us</a> to sort it out." +"Please <a href=\"{{url}}\">contact us</a> to sort it out." msgstr "" #: app/views/request/new_bad_contact.rhtml:5 @@ -3439,7 +3424,7 @@ msgid "" msgstr "" #: app/views/request/new_please_describe.rhtml:16 -msgid "When you're done, <strong>come back here</strong>, <a href=\"%s\">reload this page</a> and file your new request." +msgid "When you're done, <strong>come back here</strong>, <a href=\"{{url}}\">reload this page</a> and file your new request." msgstr "" #: app/views/request/show_response.rhtml:13 @@ -3571,7 +3556,7 @@ msgstr "" msgid "" "You may be able to find\n" " one on their website, or by phoning them up and asking. If you manage\n" -" to find one, then please <a href=\"%s\">send it to us</a>." +" to find one, then please <a href=\"{{url}}\">send it to us</a>." msgstr "" #: app/views/request/new_bad_contact.rhtml:6 @@ -3642,24 +3627,19 @@ msgid "You've now cleared your profile photo" msgstr "" #: app/views/user/show.rhtml:149 -msgid "Your %d Freedom of Information request" -msgid_plural "Your %d Freedom of Information requests" +msgid "Your {{count}} Freedom of Information request" +msgid_plural "Your {{count}} Freedom of Information requests" msgstr[0] "" msgstr[1] "" #: app/views/user/show.rhtml:179 -msgid "Your %d annotation" -msgid_plural "Your %d annotations" +msgid "Your {{count}} annotation" +msgid_plural "Your {{count}} annotations" msgstr[0] "" msgstr[1] "" #: app/views/user/_signup.rhtml:22 -msgid "" -"Your <strong>name will appear publicly</strong> \n" -" (<a href=\"%s\">why?</a>)\n" -" on this website and in search engines. If you\n" -" are thinking of using a pseudonym, please \n" -" <a href=\"%s\">read this first</a>." +msgid "Your <strong>name will appear publicly</strong>\\n (<a href=\"{{why_url}}\">why?</a>)\\n on this website and in search engines. If you\\n are thinking of using a pseudonym, please\\n <a href=\"{{help_url}}\">read this first</a>." msgstr "" #: app/views/user/show.rhtml:172 @@ -3682,7 +3662,7 @@ msgid "Your email subscriptions" msgstr "" #: app/controllers/request_controller.rb:598 -msgid "Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href=\"%s\">contact us</a> if you really want to send a follow up message." +msgid "Your follow up has not been sent because this request has been stopped to prevent spam. Please <a href=\"{{url}}\">contact us</a> if you really want to send a follow up message." msgstr "" #: app/controllers/request_controller.rb:626 @@ -3712,7 +3692,7 @@ msgstr "" #: app/views/request/preview.rhtml:8 msgid "" "Your name, request and any responses will appear in <strong>search engines</strong>\n" -" (<a href=\"%s\">details</a>)." +" (<a href=\"{{url}}\">details</a>)." msgstr "" #: app/views/user/_signup.rhtml:18 @@ -3746,7 +3726,7 @@ msgid "Your request:" msgstr "" #: app/views/request/upload_response.rhtml:8 -msgid "Your response will <strong>appear on the Internet</strong>, <a href=\"%s\">read why</a> and answers to other questions." +msgid "Your response will <strong>appear on the Internet</strong>, <a href=\"{{url}}\">read why</a> and answers to other questions." msgstr "" #: app/views/comment/new.rhtml:63 @@ -3990,7 +3970,7 @@ msgid "" "no longer exists. If you are trying to make\n" " From the request page, try replying to a particular message, rather than sending\n" " a general followup. If you need to make a general followup, and know\n" -" an email which will go to the right place, please <a href=\"%s\">send it to us</a>." +" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>." msgstr "" #: app/views/request/show.rhtml:72 diff --git a/spec/fixtures/locale/es/app.po b/spec/fixtures/locale/es/app.po index dbc7ab65b..d45d9b3b1 100644 --- a/spec/fixtures/locale/es/app.po +++ b/spec/fixtures/locale/es/app.po @@ -35,9 +35,9 @@ msgstr " Esto aparecerá en tu perfil de {{site_name}}, para facilitar\n #: app/views/comment/_comment_form.rhtml:16 msgid "" -" (<strong>no ranty</strong> politics, read our <a href=\"%s\">moderation " +" (<strong>no ranty</strong> politics, read our <a href=\"{{url}}\">moderation " "policy</a>)" -msgstr " (<strong>sin ataques políticos</strong>, lea nuestra <a href=\"%s\">política de moderación</a>)" +msgstr " (<strong>sin ataques políticos</strong>, lea nuestra <a href=\"{{url}}\">política de moderación</a>)" #: app/views/request/upload_response.rhtml:40 msgid "" @@ -80,9 +80,9 @@ msgstr " Ideas sobre <strong>qué otra información pedir</strong> que el organi #: app/views/public_body/view_email.rhtml:30 msgid "" -" If you know the address to use, then please <a href=\"%s\">send it to us</a>.\n" +" If you know the address to use, then please <a href=\"{{url}}\">send it to us</a>.\n" " You may be able to find the address on their website, or by phoning them up and asking." -msgstr " Si conoces la dirección a utilizar, entonces por favor <a href=\"%s\">envíanosla</a>.\n Puede que la encuentres en su página web, o llamándoles por teléfono y preguntando." +msgstr " Si conoces la dirección a utilizar, entonces por favor <a href=\"{{url}}\">envíanosla</a>.\n Puede que la encuentres en su página web, o llamándoles por teléfono y preguntando." #: app/views/user/set_profile_about_me.rhtml:26 msgid "" @@ -140,22 +140,22 @@ msgid " when you send this message." msgstr " cuando envió este mensaje." #: app/views/public_body/show.rhtml:87 -msgid "%d Freedom of Information request to %s" -msgid_plural "%d Freedom of Information requests to %s" -msgstr[0] "%d solicitud de información a %s" -msgstr[1] "%d solicitudes de información a %s" +msgid "{{count}} Freedom of Information request to {{public_body_name}}" +msgid_plural "{{count}} Freedom of Information requests to {{public_body_name}}" +msgstr[0] "{{count}} solicitud de información a {{public_body_name}}" +msgstr[1] "{{count}} solicitudes de información a {{public_body_name}}" #: app/views/general/frontpage.rhtml:43 -msgid "%d request" -msgid_plural "%d requests" -msgstr[0] "%d solicitud" -msgstr[1] "%d solicitudes" +msgid "{{count}} request" +msgid_plural "{{count}} requests" +msgstr[0] "{{count}} solicitud" +msgstr[1] "{{count}} solicitudes" #: app/views/public_body/_body_listing_single.rhtml:21 -msgid "%d request made." -msgid_plural "%d requests made." -msgstr[0] "%d solicitud enviada." -msgstr[1] "%d solicitudes enviadas." +msgid "{{count}} request made." +msgid_plural "{{count}} requests made." +msgstr[0] "{{count}} solicitud enviada." +msgstr[1] "{{count}} solicitudes enviadas." #: app/views/request/new.rhtml:92 msgid "'Crime statistics by ward level for Wales'" @@ -204,46 +204,40 @@ msgstr "2. Solicite información" msgid "3. Now check your request" msgstr "3. Revisa tu solicitud" -#: app/views/public_body/show.rhtml:56 -msgid "<a class=\"link_button_green\" href=\"{{url}}\">{{text}}</a>" -msgstr "<a class=\"link_button_green\" href=\"{{url}}\">{{text}}</a>" - #: app/views/request/_after_actions.rhtml:9 -msgid "<a href=\"%s\">Add an annotation</a> (to help the requester or others)" -msgstr "<a href=\"%s\">Añade un comentario</a> (para ayudar al solicitante o a otros)" +msgid "<a href=\"{{url}}\">Add an annotation</a> (to help the requester or others)" +msgstr "<a href=\"{{url}}\">Añade un comentario</a> (para ayudar al solicitante o a otros)" #: app/views/public_body/list.rhtml:29 -msgid "<a href=\"%s\">Are we missing a public authority?</a>." -msgstr "<a href=\"%s\">¿Nos falta algún organismo público?</a>." +msgid "Are we missing a public authority?" +msgstr "¿Nos falta algún organismo público?." #: app/views/request/_sidebar.rhtml:39 -msgid "" -"<a href=\"%s\">Are you the owner of\n" -" any commercial copyright on this page?</a>" -msgstr "<a href=\"%s\">¿Posee el copyright\n de alguna información de esta página?</a>" +msgid "Are you the owner of any commercial copyright on this page?" +msgstr "¿Posee el copyright de alguna información de esta página?" #: app/views/general/search.rhtml:168 -msgid "<a href=\"%s\">Browse all</a> or <a href=\"%s\">ask us to add one</a>." -msgstr "<a href=\"%s\">Ver todas</a> o <a href=\"%s\">pídanos que añadamos una</a>." +msgid "<a href=\"{{browse_url}}\">Browse all</a> or <a href=\"{{add_url}}\">ask us to add one</a>." +msgstr "<a href=\"{{browse_url}}\">Ver todas</a> o <a href=\"{{add_url}}\">pídanos que añadamos una</a>." #: app/views/public_body/list.rhtml:51 -msgid "<a href=\"%s\">Can't find the one you want?</a>" -msgstr "<a href=\"%s\">¿No encuentra el que busca?</a>" +msgid "Can't find the one you want?" +msgstr "¿No encuentra el que busca?" #: app/views/user/show.rhtml:118 msgid "" -"<a href=\"%s\">Sign in</a> to change password, subscriptions and more " +"<a href=\"{{url}}\">Sign in</a> to change password, subscriptions and more " "({{user_name}} only)" -msgstr "<a href=\"%s\">Abre una sesión</a> para cambiar tu contraseña, suscripciones... (sólo {{user_name}})" +msgstr "<a href=\"{{url}}\">Abre una sesión</a> para cambiar tu contraseña, suscripciones... (sólo {{user_name}})" #: app/views/request/_followup.rhtml:66 app/views/request/_followup.rhtml:73 #: app/views/request/show.rhtml:83 app/views/request/show.rhtml:87 -msgid "<a href=\"%s\">details</a>" -msgstr "<a href=\"%s\">detalles</a>" +msgid "details" +msgstr "detalles" #: app/views/request/_followup.rhtml:101 -msgid "<a href=\"%s\">what's that?</a>" -msgstr "<a href=\"%s\">¿Qué es eso?</a>" +msgid "what's that?" +msgstr "¿Qué es eso?" #: app/controllers/request_game_controller.rb:23 msgid "" @@ -382,8 +376,8 @@ msgstr "<small>Si usas correo web o tiene filtros \"anti spam\", por favor compr #: app/views/request/new.rhtml:135 msgid "" "<strong> Can I request information about myself?</strong>\n" -"\t\t\t<a href=\"%s\">No! (Click here for details)</a>" -msgstr "<strong> ¿Puedo pedir información sobre mí?</strong>\n\t\t\t<a href=\"%s\">¡No! (Pulse aquí para más detalles)</a>" +"\t\t\t<a href=\"{{url}}\">No! (Click here for details)</a>" +msgstr "<strong> ¿Puedo pedir información sobre mí?</strong>\n\t\t\t<a href=\"{{url}}\">¡No! (Pulse aquí para más detalles)</a>" #: app/views/general/_advanced_search_tips.rhtml:12 msgid "" @@ -486,8 +480,8 @@ msgstr "<strong>Nota:</strong> Te estás enviando un mensaje a ti mismo, suponem #: app/views/request/preview.rhtml:31 msgid "" "<strong>Privacy note:</strong> If you want to request private information about\n" -" yourself then <a href=\"%s\">click here</a>." -msgstr "<strong>Nota sobre privacidad:</strong> Si quiere solicitar información privada\n sobre sí mismo entonces <a href=\"%s\">siga este enlace</a>." +" yourself then <a href=\"{{url}}\">click here</a>." +msgstr "<strong>Nota sobre privacidad:</strong> Si quiere solicitar información privada\n sobre sí mismo entonces <a href=\"{{url}}\">siga este enlace</a>." #: app/views/user/set_crop_profile_photo.rhtml:35 msgid "" @@ -667,8 +661,8 @@ msgstr "Pide documentos o información <strong>específica</strong>, esta web no #: app/views/request/show_response.rhtml:29 msgid "" "At the bottom of this page, write a reply to them trying to persuade them to scan it in\n" -" (<a href=\"%s\">more details</a>)." -msgstr "Al final de esta página, escribe una respuesta intentando convencerles de que lo escaneen\n (<a href=\"%s\">más detalles</a>)." +" (<a href=\"{{url}}\">more details</a>)." +msgstr "Al final de esta página, escribe una respuesta intentando convencerles de que lo escaneen\n (<a href=\"{{url}}\">más detalles</a>)." #: app/views/request/upload_response.rhtml:33 msgid "Attachment (optional):" @@ -1037,8 +1031,8 @@ msgstr "Introduzca las palabras que desee separadas por espacio, es decir <stron #: app/views/request/upload_response.rhtml:23 msgid "" "Enter your response below. You may attach one file (use email, or \n" -"<a href=\"%s\">contact us</a> if you need more)." -msgstr "Escribe tu solicitud a continuación. Puedes adjuntar un fichero (manda un correo,\n o <a href=\"%s\">contáctanos</a>, si necesita más)." +"<a href=\"{{url}}\">contact us</a> if you need more)." +msgstr "Escribe tu solicitud a continuación. Puedes adjuntar un fichero (manda un correo,\n o <a href=\"{{url}}\">contáctanos</a>, si necesita más)." #: app/models/info_request.rb:259 app/models/info_request.rb:277 msgid "Environmental Information Regulations" @@ -1068,15 +1062,15 @@ msgstr "Historial de eventos" msgid "" "Everything that you enter on this page\n" " will be <strong>displayed publicly</strong> on\n" -" this website forever (<a href=\"%s\">why?</a>)." -msgstr "Todo lo que escriba en esta página \n estará <strong>disponible públicamente</strong> en\n está web para siempre (<a href=\"%s\">¿por qué?</a>)." +" this website forever (<a href=\"{{url}}\">why?</a>)." +msgstr "Todo lo que escriba en esta página \n estará <strong>disponible públicamente</strong> en\n está web para siempre (<a href=\"{{url}}\">¿por qué?</a>)." #: app/views/request/new.rhtml:120 msgid "" "Everything that you enter on this page, including <strong>your name</strong>,\n" " will be <strong>displayed publicly</strong> on\n" -" this website forever (<a href=\"%s\">why?</a>)." -msgstr "Todo lo que escribas en esta página, incluyendo <strong>tu nombre</strong>, \n estará <strong>disponible públicamente</strong> en\n está web para siempre (<a href=\"%s\">¿por qué?</a>)." +" this website forever (<a href=\"{{url}}\">why?</a>)." +msgstr "Todo lo que escribas en esta página, incluyendo <strong>tu nombre</strong>, \n estará <strong>disponible públicamente</strong> en\n está web para siempre (<a href=\"{{url}}\">¿por qué?</a>)." #: locale/model_attributes.rb:68 msgid "EximLogDone|Filename" @@ -1123,10 +1117,8 @@ msgid "Failed to convert image to a PNG" msgstr "Error al convertir la imagen a PNG" #: app/models/profile_photo.rb:105 -msgid "" -"Failed to convert image to the correct size: at %{cols}x%{rows}, need " -"%{width}x%{height}" -msgstr "Error al convertir la imagen al tamaño adecuado: es %{cols}x%{rows}, debería ser %{width}x%{height}" +msgid "Failed to convert image to the correct size: at {{cols}}x{{rows}}, need {{width}}x{{height}}" +msgstr "Error al convertir la imagen al tamaño adecuado: es {{cols}}x{{rows}}, debería ser {{width}}x{{height}}" #: app/views/general/search.rhtml:117 msgid "Filter" @@ -1136,8 +1128,8 @@ msgstr "Filtrar" msgid "" "First, type in the <strong>name of the UK public authority</strong> you'd \n" " like information from. <strong>By law, they have to respond</strong>\n" -" (<a href=\"%s#%s\">why?</a>)." -msgstr "Primero, escribe el <strong>nombre de la institución</strong> a la que quieres pedir información. <strong>Están obligados a responder</strong> (<a href=\"%s#%s\">¿por qué?</a>)." +" (<a href=\"{{url}}\">why?</a>)." +msgstr "Primero, escribe el <strong>nombre de la institución</strong> a la que quieres pedir información. <strong>Están obligados a responder</strong> (<a href=\"{{url}}\">¿por qué?</a>)." #: locale/model_attributes.rb:88 msgid "FoiAttachment|Charset" @@ -1272,8 +1264,8 @@ msgstr "Solicitudes de información a" msgid "" "From the request page, try replying to a particular message, rather than sending\n" " a general followup. If you need to make a general followup, and know\n" -" an email which will go to the right place, please <a href=\"%s\">send it to us</a>." -msgstr "Desde la página de la solicitud, intenta responder a un mensaje en concreto, en vez de\n responder a la solicitud en general. Si necesitas hacerlo y tienes una dirección de\n correo válida, por favor <a href=\"%s\">mándanosla</a>." +" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>." +msgstr "Desde la página de la solicitud, intenta responder a un mensaje en concreto, en vez de\n responder a la solicitud en general. Si necesitas hacerlo y tienes una dirección de\n correo válida, por favor <a href=\"{{url}}\">mándanosla</a>." #: app/views/request/_correspondence.rhtml:12 #: app/views/request/_correspondence.rhtml:36 @@ -1409,8 +1401,8 @@ msgstr "He recibido un <strong>mensaje de error</strong>" #: app/views/public_body/view_email.rhtml:28 msgid "" "If the address is wrong, or you know a better address, please <a " -"href=\"%s\">contact us</a>." -msgstr "Si la dirección es incorrecta, o conoce una más actualizada, por favor <a href=\"%s\">contáctenos</a>." +"href=\"{{url}}\">contact us</a>." +msgstr "Si la dirección es incorrecta, o conoce una más actualizada, por favor <a href=\"{{url}}\">contáctenos</a>." #: app/views/request_mailer/stopped_responses.rhtml:10 msgid "" @@ -1423,24 +1415,24 @@ msgstr "Si no es correcto, o te gustaría enviar una respuesta a la solicitud\no msgid "" "If you are dissatisfied by the response you got from\n" " the public authority, you have the right to\n" -" complain (<a href=\"%s\">details</a>)." -msgstr "Si no estás satisfecho con la respuesta que has recibido del\n organismo público, tienes derecho a\n apelar (<a href=\"%s\">detalles</a>)." +" complain (<a href=\"{{url}}\">details</a>)." +msgstr "Si no estás satisfecho con la respuesta que has recibido del\n organismo público, tienes derecho a\n apelar (<a href=\"{{url}}\">detalles</a>)." #: app/views/user/no_cookies.rhtml:20 -msgid "If you are still having trouble, please <a href=\"%s\">contact us</a>." -msgstr "Si aún tienes problemas, por favor <a href=\"%s\">contáctanos</a>." +msgid "If you are still having trouble, please <a href=\"{{url}}\">contact us</a>." +msgstr "Si aún tienes problemas, por favor <a href=\"{{url}}\">contáctanos</a>." #: app/views/request/hidden.rhtml:15 msgid "" -"If you are the requester, then you may <a href=\"%s\">sign in</a> to view " +"If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view " "the request." -msgstr "Si la solicitud es tuya, puedes <a href=\"%s\">abrir una sesión</a> para verla." +msgstr "Si la solicitud es tuya, puedes <a href=\"{{url}}\">abrir una sesión</a> para verla." #: app/views/request/new.rhtml:123 msgid "" "If you are thinking of using a pseudonym,\n" -" please <a href=\"%s\">read this first</a>." -msgstr "Si está pensando en utilizar un pseudónimo,\n por favor <a href=\"%s\">lea esto primero</a>." +" please <a href=\"{{url}}\">read this first</a>." +msgstr "Si está pensando en utilizar un pseudónimo,\n por favor <a href=\"{{url}}\">lea esto primero</a>." #: app/views/request/show.rhtml:105 msgid "If you are {{user_link}}, please" @@ -1639,8 +1631,8 @@ msgstr "Registrado en {{site_name}} el" #: app/views/request/new.rhtml:106 msgid "" "Keep it <strong>focused</strong>, you'll be more likely to get what you want" -" (<a href=\"%s\">why?</a>)." -msgstr "Sea <strong>específico</strong>, tendrá más probabilidades de conseguir lo que quiere (<a href=\"%s\">¿por qué?</a>)." +" (<a href=\"{{url}}\">why?</a>)." +msgstr "Sea <strong>específico</strong>, tendrá más probabilidades de conseguir lo que quiere (<a href=\"{{url}}\">¿por qué?</a>)." #: app/views/request/_request_filter_form.rhtml:6 msgid "Keywords" @@ -1990,8 +1982,8 @@ msgid "Please" msgstr "Por favor" #: app/views/user/no_cookies.rhtml:15 -msgid "Please <a href=\"%s\">get in touch</a> with us so we can fix it." -msgstr "Por favor <a href=\"%s\">contacta</a> con nosotros para que podamos arreglarlo." +msgid "Please <a href=\"{{url}}\">get in touch</a> with us so we can fix it." +msgstr "Por favor <a href=\"{{url}}\">contacta</a> con nosotros para que podamos arreglarlo." #: app/views/request/show.rhtml:52 msgid "" @@ -2154,10 +2146,8 @@ msgid "" msgstr "Por favor elije estas solicitudes una a una, y <strong>haz que se sepa</strong>\nsi han tenido éxito o no." #: app/models/outgoing_message.rb:157 -msgid "" -"Please sign at the bottom with your name, or alter the \"%{signoff}\" " -"signature" -msgstr "Por favor, firma con tu nombre en la parte inferior, o cambia la firma \"%{signoff}\"" +msgid "Please sign at the bottom with your name, or alter the \"{{signoff}}\" signature" +msgstr "Por favor, firma con tu nombre en la parte inferior, o cambia la firma \"{{signoff}}\"" #: app/views/user/sign.rhtml:8 msgid "Please sign in as " @@ -2519,10 +2509,10 @@ msgid "Search the site to find what you were looking for." msgstr "Buscar en esta web para encontrar lo que busca." #: app/views/public_body/show.rhtml:85 -msgid "Search within the %d Freedom of Information requests to %s" -msgid_plural "Search within the %d Freedom of Information requests made to %s" -msgstr[0] "Busca en la %d solicitud de información hecha a %s" -msgstr[1] "Busca en las %d solicitudes de información hechas a %s" +msgid "Search within the {{count}} Freedom of Information requests to {{public_body_name}}" +msgid_plural "Search within the {{count}} Freedom of Information requests to {{public_body_name}}" +msgstr[0] "Busca en la {{count}} solicitud de información hecha a {{public_body_name}}" +msgstr[1] "Busca en las {{count}} solicitudes de información hechas a {{public_body_name}}" #: app/views/user/show.rhtml:132 msgid "Search your contributions" @@ -2878,11 +2868,8 @@ msgid "The request was refused by the public authority" msgstr "La solicitud ha sido rechazada por el organismo" #: app/views/request/hidden.rhtml:9 -msgid "" -"The request you have tried to view has been removed. There are\n" -"various reasons why we might have done this, sorry we can't be more specific here. Please <a\n" -" href=\"%s\">contact us</a> if you have any questions." -msgstr "La solicitud que has intentado ver ha sido eliminada. Hay\nvarios posibles motivos para esto, pero no podemos ser más específicos aquí. Por favor <a\n href=\"%s\">contáctanos</a> si tiene cualquier pregunta." +msgid "The request you have tried to view has been removed. There are\\nvarious reasons why we might have done this, sorry we can't be more specific here. Please <a\\n href=\"{{url}}\">contact us</a> if you have any questions." +msgstr "La solicitud que has intentado ver ha sido eliminada. Hay\nvarios posibles motivos para esto, pero no podemos ser más específicos aquí. Por favor <a\n href=\"{{url}}\">contáctanos</a> si tiene cualquier pregunta." #: app/views/general/_advanced_search_tips.rhtml:36 msgid "The requester has abandoned this request for some reason" @@ -2890,14 +2877,14 @@ msgstr "El creador de la solicitud la ha cancelado por algún motivo" #: app/views/request/_followup.rhtml:59 msgid "" -"The response to your request has been <strong>delayed</strong>. You can say that, \n" +"The response to your request has been <strong>delayed</strong>. You can say that,\n" " by law, the authority should normally have responded\n" " <strong>promptly</strong> and" msgstr "La respuesta a tu solicitud ha sido <strong>retrasada</strong>.\n Por ley, el organismo debería normalmente haber respondido\n <strong>rápidamente</strong> y" #: app/views/request/_followup.rhtml:71 msgid "" -"The response to your request is <strong>long overdue</strong>. You can say that, by \n" +"The response to your request is <strong>long overdue</strong>. You can say that, by\n" " law, under all circumstances, the authority should have responded\n" " by now" msgstr "La respuesta a tu solicitud ha sido <strong>muy retrasada</strong>.\n Por ley, bajo cualquier circunstancia, el organismo ya debería\n haber respondido" @@ -3016,17 +3003,11 @@ msgid "" " this link to see what they wrote." msgstr "Hay {{count}} comentarios en tu solicitud {{info_request}}. Sigue este enlace para leer lo que dicen." -#: app/views/public_body/show.rhtml:7 -msgid "There is %d person following this authority" -msgid_plural "There are %d people following this authority" -msgstr[0] "Hay %d persona siguiendo a este organismo." -msgstr[1] "Hay %d personas siguiendo a este organismo." - #: app/views/request/_sidebar.rhtml:5 -msgid "There is %d person following this request" -msgid_plural "There are %d people following this request" -msgstr[0] "Hay %d persona siguiendo esta solicitud." -msgstr[1] "Hay %d personas siguiendo esta solicitud." +msgid "There is {{count}} person following this request" +msgid_plural "There are {{count}} people following this request" +msgstr[0] "Hay {{count}} persona siguiendo esta solicitud." +msgstr[1] "Hay {{count}} personas siguiendo esta solicitud." #: app/views/user/show.rhtml:8 msgid "" @@ -3099,8 +3080,8 @@ msgstr "Este organismo ya no existe, no pueden realizarse solicitudes de informa #: app/views/request/_hidden_correspondence.rhtml:23 msgid "" "This comment has been hidden. See annotations to\n" -" find out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response." -msgstr "Este respuesta está oculta. Revisa los comentarios\n para descubrir por qué. Si es tu solicitud, <a href=\"%s\">abre una sesión</a> para ver la respuesta." +" find out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response." +msgstr "Este respuesta está oculta. Revisa los comentarios\n para descubrir por qué. Si es tu solicitud, <a href=\"{{url}}\">abre una sesión</a> para ver la respuesta." #: app/views/request/new.rhtml:63 msgid "" @@ -3140,8 +3121,8 @@ msgstr "Esta es tu solicitud, por lo que recibirás correos automáticamente cua #: app/views/request/_hidden_correspondence.rhtml:17 msgid "" "This outgoing message has been hidden. See annotations to\n" -"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response." -msgstr "Este mensaje está oculto. Lee los comentarios\n\t\t\t\t\t\tpara descubrir por qué. Si es tu solicitud, <a href=\"%s\">abra una sesión</a> para ver la respuesta." +"\t\t\t\t\t\tfind out why. If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response." +msgstr "Este mensaje está oculto. Lee los comentarios\n\t\t\t\t\t\tpara descubrir por qué. Si es tu solicitud, <a href=\"{{url}}\">abra una sesión</a> para ver la respuesta." #: app/views/request/_describe_state.rhtml:44 #: app/views/request/_other_describe_state.rhtml:40 @@ -3154,16 +3135,16 @@ msgid "" msgstr "Esta persona no ha realizado solicitudes de información usando esta web." #: app/views/user/show.rhtml:149 -msgid "This person's %d Freedom of Information request" -msgid_plural "This person's %d Freedom of Information requests" -msgstr[0] "Tu %d solicitud de información" -msgstr[1] "Tus %d solicitudes de información" +msgid "This person's {{count}} Freedom of Information request" +msgid_plural "This person's {{count}} Freedom of Information requests" +msgstr[0] "Tu {{count}} solicitud de información" +msgstr[1] "Tus {{count}} solicitudes de información" #: app/views/user/show.rhtml:179 -msgid "This person's %d annotation" -msgid_plural "This person's %d annotations" -msgstr[0] "Tu %d comentario" -msgstr[1] "Tus %d comentarios" +msgid "This person's {{count}} annotation" +msgid_plural "This person's {{count}} annotations" +msgstr[0] "Tu {{count}} comentario" +msgstr[1] "Tus {{count}} comentarios" #: app/views/user/show.rhtml:172 msgid "This person's annotations" @@ -3204,8 +3185,8 @@ msgstr "Esta solicitud tiene visibilidad 'oculta'. Puedes verla sólo porque est #: app/views/request/show.rhtml:11 msgid "" "This request is hidden, so that only you the requester can see it. Please\n" -" <a href=\"%s\">contact us</a> if you are not sure why." -msgstr "Esta solicitud está oculta, por lo que sólo tú como creador puedes verla. Por favor\n <a href=\"%s\">contáctanos</a> si no estás seguro de por qué." +" <a href=\"{{url}}\">contact us</a> if you are not sure why." +msgstr "Esta solicitud está oculta, por lo que sólo tú como creador puedes verla. Por favor\n <a href=\"{{url}}\">contáctanos</a> si no estás seguro de por qué." #: app/views/request/_describe_state.rhtml:7 #: app/views/request/_other_describe_state.rhtml:10 @@ -3215,8 +3196,8 @@ msgstr "Esta solicitud está todavía en proceso:" #: app/views/request/_hidden_correspondence.rhtml:10 msgid "" "This response has been hidden. See annotations to find out why.\n" -" If you are the requester, then you may <a href=\"%s\">sign in</a> to view the response." -msgstr "Este respuesta está oculta. Revisa los comentarios\n para descubrir por qué. Si es tu solicitud, <a href=\"%s\">abre una sesión</a> para ver la respuesta." +" If you are the requester, then you may <a href=\"{{url}}\">sign in</a> to view the response." +msgstr "Este respuesta está oculta. Revisa los comentarios\n para descubrir por qué. Si es tu solicitud, <a href=\"{{url}}\">abre una sesión</a> para ver la respuesta." #: app/views/request/details.rhtml:6 msgid "" @@ -3450,8 +3431,8 @@ msgstr "Se encontró un tipo de resultado inesperado " msgid "" "Unfortunately we don't know the FOI\n" "email address for that authority, so we can't validate this.\n" -"Please <a href=\"%s\">contact us</a> to sort it out." -msgstr "Desgraciadamente no tenemos la dirección\nde correo para este organismo, así que no podemos validarlo.\nPor favor <a href=\"%s\">contáctenos</a> para arreglarlo." +"Please <a href=\"{{url}}\">contact us</a> to sort it out." +msgstr "Desgraciadamente no tenemos la dirección\nde correo para este organismo, así que no podemos validarlo.\nPor favor <a href=\"{{url}}\">contáctenos</a> para arreglarlo." #: app/views/request/new_bad_contact.rhtml:5 msgid "" @@ -3699,9 +3680,9 @@ msgstr "Cuando reciba la respuesta en papel, por favor ayude\n a que #: app/views/request/new_please_describe.rhtml:16 msgid "" -"When you're done, <strong>come back here</strong>, <a href=\"%s\">reload " +"When you're done, <strong>come back here</strong>, <a href=\"{{url}}\">reload " "this page</a> and file your new request." -msgstr "Cuando esté listo, <strong>vuelva aquí</strong>, <a href=\"%s\">recargue esta página</a> y cree una nueva solicitud." +msgstr "Cuando esté listo, <strong>vuelva aquí</strong>, <a href=\"{{url}}\">recargue esta página</a> y cree una nueva solicitud." #: app/views/request/show_response.rhtml:13 msgid "Which of these is happening?" @@ -3840,8 +3821,8 @@ msgstr "Puede <strong>adjuntar ficheros</strong>. Si quiere adjuntar un fichero\ msgid "" "You may be able to find\n" " one on their website, or by phoning them up and asking. If you manage\n" -" to find one, then please <a href=\"%s\">send it to us</a>." -msgstr "Puede que encuentres una\n en su página web, o preguntando por teléfono. Si la consigues\n por favor <a href=\"%s\">envíanosla</a>." +" to find one, then please <a href=\"{{url}}\">send it to us</a>." +msgstr "Puede que encuentres una\n en su página web, o preguntando por teléfono. Si la consigues\n por favor <a href=\"{{url}}\">envíanosla</a>." #: app/views/request/new_bad_contact.rhtml:6 msgid "" @@ -3914,25 +3895,20 @@ msgid "You've now cleared your profile photo" msgstr "Has borrado la foto de tu perfil" #: app/views/user/show.rhtml:149 -msgid "Your %d Freedom of Information request" -msgid_plural "Your %d Freedom of Information requests" -msgstr[0] "Tu %d solicitud de información" -msgstr[1] "Tus %d solicitudes de información" +msgid "Your {{count}} Freedom of Information request" +msgid_plural "Your {{count}} Freedom of Information requests" +msgstr[0] "Tu {{count}} solicitud de información" +msgstr[1] "Tus {{count}} solicitudes de información" #: app/views/user/show.rhtml:179 -msgid "Your %d annotation" -msgid_plural "Your %d annotations" -msgstr[0] "Tu %d comentario" -msgstr[1] "Tus %d comentarios" +msgid "Your {{count}} annotation" +msgid_plural "Your {{count}} annotations" +msgstr[0] "Tu {{count}} comentario" +msgstr[1] "Tus {{count}} comentarios" #: app/views/user/_signup.rhtml:22 -msgid "" -"Your <strong>name will appear publicly</strong> \n" -" (<a href=\"%s\">why?</a>)\n" -" on this website and in search engines. If you\n" -" are thinking of using a pseudonym, please \n" -" <a href=\"%s\">read this first</a>." -msgstr "<strong>Tu nombre aparecerá públicamente</strong> \n (<a href=\"%s\">¿por qué?</a>)\n en esta web y en motores de búsqueda. Si estás\n pensando en utilizar un seudónimo, por favor \n <a href=\"%s\">lee esto primero</a>." +msgid "Your <strong>name will appear publicly</strong> \\n (<a href=\"{{why_url}}\">why?</a>)\\n on this website and in search engines. If you\\n are thinking of using a pseudonym, please \\n <a href=\"{{help_url}}\">read this first</a>." +msgstr "<strong>Tu nombre aparecerá públicamente</strong> \n (<a href=\"{{why_url}}\">¿por qué?</a>)\n en esta web y en motores de búsqueda. Si estás\n pensando en utilizar un seudónimo, por favor \n <a href=\"{{help_url}}\">lee esto primero</a>." #: app/views/user/show.rhtml:172 msgid "Your annotations" @@ -3956,9 +3932,9 @@ msgstr "Tus suscripciones de correo" #: app/controllers/request_controller.rb:598 msgid "" "Your follow up has not been sent because this request has been stopped to " -"prevent spam. Please <a href=\"%s\">contact us</a> if you really want to " +"prevent spam. Please <a href=\"{{url}}\">contact us</a> if you really want to " "send a follow up message." -msgstr "Tu respuesta no ha sido enviada porque esta solicitud ha sido bloqueada para evitar spam. Por favor <a href=\"%s\">contáctanos</a> si realmente quieres enviar una respuesta." +msgstr "Tu respuesta no ha sido enviada porque esta solicitud ha sido bloqueada para evitar spam. Por favor <a href=\"{{url}}\">contáctanos</a> si realmente quieres enviar una respuesta." #: app/controllers/request_controller.rb:626 msgid "Your follow up message has been sent on its way." @@ -3990,8 +3966,8 @@ msgstr "Tu nombre y su comentario aparecerán en los <strong>motores de búsqued #: app/views/request/preview.rhtml:8 msgid "" "Your name, request and any responses will appear in <strong>search engines</strong>\n" -" (<a href=\"%s\">details</a>)." -msgstr "Tu nombre, tu solicitud y cualquier respuesta aparecerán en los <strong>motores de búsqueda</strong>\n (<a href=\"%s\">detalles</a>)." +" (<a href=\"{{url}}\">details</a>)." +msgstr "Tu nombre, tu solicitud y cualquier respuesta aparecerán en los <strong>motores de búsqueda</strong>\n (<a href=\"{{url}}\">detalles</a>)." #: app/views/user/_signup.rhtml:18 msgid "Your name:" @@ -4028,8 +4004,8 @@ msgstr "Tu solicitud:" #: app/views/request/upload_response.rhtml:8 msgid "" "Your response will <strong>appear on the Internet</strong>, <a " -"href=\"%s\">read why</a> and answers to other questions." -msgstr "Tu respuesta <strong>aparecerá en Internet</strong>, <a href=\"%s\">lee por qué</a> y respuestas a otras preguntas." +"href=\"{{url}}\">read why</a> and answers to other questions." +msgstr "Tu respuesta <strong>aparecerá en Internet</strong>, <a href=\"{{url}}\">lee por qué</a> y respuestas a otras preguntas." #: app/views/comment/new.rhtml:63 msgid "" @@ -4276,8 +4252,8 @@ msgid "" "no longer exists. If you are trying to make\n" " From the request page, try replying to a particular message, rather than sending\n" " a general followup. If you need to make a general followup, and know\n" -" an email which will go to the right place, please <a href=\"%s\">send it to us</a>." -msgstr "ya no existe. \nDesde la página de la solicitud, intenta responder a un mensaje en concreto, en vez de\n responder a la solicitud en general. Si necesitas hacerlo y tienes una dirección de\n correo válida, por favor <a href=\"%s\">mándanosla</a>." +" an email which will go to the right place, please <a href=\"{{url}}\">send it to us</a>." +msgstr "ya no existe. \nDesde la página de la solicitud, intenta responder a un mensaje en concreto, en vez de\n responder a la solicitud en general. Si necesitas hacerlo y tienes una dirección de\n correo válida, por favor <a href=\"{{url}}\">mándanosla</a>." #: app/views/request/show.rhtml:72 msgid "normally" diff --git a/spec/integration/errors_spec.rb b/spec/integration/errors_spec.rb index edf570182..ed0d7bfec 100644 --- a/spec/integration/errors_spec.rb +++ b/spec/integration/errors_spec.rb @@ -1,40 +1,113 @@ +# -*- coding: utf-8 -*- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') -describe "When rendering errors" do +describe "When errors occur" do - before(:each) do - load_raw_emails_data + def set_consider_all_requests_local(value) + @requests_local = Rails.application.config.consider_all_requests_local + Rails.application.config.consider_all_requests_local = value end - it "should render a 404 for users that don't exist" do - get("/user/wobsnasm") - response.code.should == "404" + def restore_consider_all_requests_local + Rails.application.config.consider_all_requests_local = @requests_local end - it "should render a 404 for bodies that don't exist" do - get("/body/wobsnasm") - response.code.should == "404" + + before(:each) do + # This should happen automatically before each test but doesn't with these integration + # tests for some reason. + ActionMailer::Base.deliveries = [] end - it "should render a 500 for general errors" do - ir = info_requests(:naughty_chicken_request) - # Set an invalid state for the request. Note that update_attribute doesn't run the validations - ir.update_attribute(:described_state, "crotchety") - get("/request/#{ir.url_title}") - response.code.should == "500" + + after(:each) do + restore_consider_all_requests_local end - it "should render a 403 for attempts at directory listing for attachments" do - # make a fake cache - foi_cache_path = File.expand_path(File.join(File.dirname(__FILE__), '../../cache')) - FileUtils.mkdir_p(File.join(foi_cache_path, "views/en/request/101/101/response/1/attach/html/1")) - get("/request/101/response/1/attach/html/1/" ) - response.body.should include("Directory listing not allowed") - response.code.should == "403" - get("/request/101/response/1/attach/html" ) - response.body.should include("Directory listing not allowed") - response.code.should == "403" + + context 'when considering all requests local (by default all in development)' do + + before(:each) { set_consider_all_requests_local(true) } + + it 'should show a full trace for general errors' do + InfoRequest.stub!(:find_by_url_title!).and_raise("An example error") + get("/request/example") + response.body.should have_selector('div[id=traces]') + response.body.should match('An example error') + end + end - it "should render a 404 for non-existent 'details' pages for requests" do - get("/details/request/wobble" ) - response.code.should == "404" + + context 'when not considering all requests local' do + + before(:each) { set_consider_all_requests_local(false) } + + it "should render a 404 for unrouteable URLs using the general/exception_caught template" do + get("/frobsnasm") + response.should render_template('general/exception_caught') + response.code.should == "404" + end + + it "should render a 404 for users or bodies that don't exist using the general/exception_caught + template" do + ['/user/wobsnasm', '/body/wobsnasm'].each do |non_existent_url| + get(non_existent_url) + response.should render_template('general/exception_caught') + response.code.should == "404" + end + end + + it "should render a 500 for general errors using the general/exception_caught template" do + InfoRequest.stub!(:find_by_url_title!).and_raise("An example error") + get("/request/example") + response.should render_template('general/exception_caught') + response.body.should match('An example error') + response.code.should == "500" + end + + it 'should notify of a general error' do + InfoRequest.stub!(:find_by_url_title!).and_raise("An example error") + get("/request/example") + deliveries = ActionMailer::Base.deliveries + deliveries.size.should == 1 + mail = deliveries[0] + mail.body.should =~ /An example error/ + end + + it 'should log a general error' do + Rails.logger.should_receive(:fatal) + InfoRequest.stub!(:find_by_url_title!).and_raise("An example error") + get("/request/example") + end + + it 'should assign the locale for the general/exception_caught template' do + InfoRequest.stub!(:find_by_url_title!).and_raise("An example error") + get("/es/request/example") + response.should render_template('general/exception_caught') + response.body.should match('Lo sentimos, hubo un problema procesando esta página') + response.body.should match('An example error') + end + + it "should render a 403 with text body for attempts at directory listing for attachments" do + # make a fake cache + foi_cache_path = File.expand_path(File.join(File.dirname(__FILE__), '../../cache')) + FileUtils.mkdir_p(File.join(foi_cache_path, "views/en/request/101/101/response/1/attach/html/1")) + get("/request/101/response/1/attach/html/1/" ) + response.body.should include("Directory listing not allowed") + response.code.should == "403" + get("/request/101/response/1/attach/html" ) + response.body.should include("Directory listing not allowed") + response.code.should == "403" + end + + context "in the admin interface" do + + it 'should show a full trace for general errors' do + InfoRequest.stub!(:find).and_raise("An example error") + get("/admin/request/show/333") + response.body.should have_selector('div[id=traces]') + response.body.should match('An example error') + end + + end + end -end +end diff --git a/spec/integration/request_controller_spec.rb b/spec/integration/request_controller_spec.rb index 24667bdf1..9e585448b 100644 --- a/spec/integration/request_controller_spec.rb +++ b/spec/integration/request_controller_spec.rb @@ -6,6 +6,7 @@ describe RequestController, "when classifying an information request" do describe 'when the request is internal' do before(:each) do + load_raw_emails_data @dog_request = info_requests(:fancy_dog_request) # This should happen automatically before each test but doesn't with these integration # tests for some reason. diff --git a/spec/lib/basic_encoding_tests.rb b/spec/lib/basic_encoding_tests.rb new file mode 100644 index 000000000..35d35fd4a --- /dev/null +++ b/spec/lib/basic_encoding_tests.rb @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- +require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') + +def bytes_to_binary_string( bytes, claimed_encoding = nil ) + claimed_encoding ||= 'ASCII-8BIT' + bytes_string = bytes.pack('c*') + if RUBY_VERSION.to_f >= 1.9 + bytes_string.force_encoding! claimed_encoding + end + bytes_string +end + +random_string = bytes_to_binary_string [ 0x0f, 0x58, 0x1c, 0x8f, 0xa4, 0xcf, + 0xf6, 0x8c, 0x9d, 0xa7, 0x06, 0xd9, + 0xf7, 0x90, 0x6c, 0x6f] + +windows_1252_string = bytes_to_binary_string [ 0x44, 0x41, 0x53, 0x48, 0x20, + 0x96, 0x20, 0x44, 0x41, 0x53, + 0x48 ] + +# It's a shame this example is so long, but if we don't take enough it +# gets misinterpreted as Shift_JIS + +gb_18030_bytes = [ 0xb9, 0xf3, 0xb9, 0xab, 0xcb, 0xbe, 0xb8, 0xba, 0xd4, 0xf0, + 0xc8, 0xcb, 0x28, 0xbe, 0xad, 0xc0, 0xed, 0x2f, 0xb2, 0xc6, + 0xce, 0xf1, 0x29, 0xc4, 0xfa, 0xba, 0xc3, 0xa3, 0xba, 0x0d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xb1, 0xbe, 0xb9, 0xab, 0xcb, 0xbe, 0xd4, + 0xda, 0x31, 0x39, 0x39, 0x37, 0xc4, 0xea, 0xb3, 0xc9, 0xc1, + 0xa2, 0xb9, 0xfa, 0xbc, 0xd2, 0xb9, 0xa4, 0xc9, 0xcc, 0xd7, + 0xa2, 0xb2, 0xe1, 0x2e, 0xca, 0xb5, 0xc1, 0xa6, 0xd0, 0xdb, + 0xba, 0xf1, 0xa1, 0xa3, 0xd3, 0xd0, 0xb6, 0xc0, 0xc1, 0xa2, + 0xcb, 0xb0, 0xce, 0xf1, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xd7, 0xa8, 0xd2, 0xb5, + 0xc8, 0xcb, 0xd4, 0xb1, 0x3b, 0xd4, 0xda, 0xc8, 0xab, 0xb9, + 0xfa, 0xb8, 0xf7, 0xb3, 0xc7, 0xca, 0xd0, 0xc9, 0xe8, 0xc1, + 0xa2, 0xb7, 0xd6, 0xb9, 0xab, 0xcb, 0xbe, 0xa3, 0xa8, 0xd5, + 0xe3, 0xbd, 0xad, 0xa1, 0xa2, 0xc9, 0xcf, 0xba, 0xa3, 0xa1, + 0xa2, 0xb9, 0xe3, 0xd6, 0xdd, 0xa1, 0xa2, 0xbd, 0xad, 0xcb, + 0xd5, 0xb5, 0xc8, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xb5, 0xd8, 0xb7, 0xbd, 0xa3, + 0xa9, 0xd2, 0xf2, 0xbd, 0xf8, 0xcf, 0xee, 0xbd, 0xcf, 0xb6, + 0xe0, 0xcf, 0xd6, 0xcd, 0xea, 0xb3, 0xc9, 0xb2, 0xbb, 0xc1, + 0xcb, 0xc3, 0xbf, 0xd4, 0xc2, 0xcf, 0xfa, 0xca, 0xdb, 0xb6, + 0xee, 0xb6, 0xc8, 0xa1, 0xa3, 0xc3, 0xbf, 0xd4, 0xc2, 0xd3, + 0xd0, 0xd2, 0xbb, 0xb2, 0xbf, 0xb7, 0xd6, 0x0d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xd4, + 0xf6, 0xd6, 0xb5, 0xb6, 0x90, 0xa3, 0xa8, 0x36, 0x2d, 0x37, + 0x25, 0xd7, 0xf3, 0xd3, 0xd2, 0x29, 0xba, 0xcd, 0xc6, 0xd5, + 0xc6, 0xb1, 0xa3, 0xa8, 0x30, 0x2e, 0x35, 0x25, 0x2d, 0x32, + 0x25, 0x20, 0xd7, 0xf3, 0xd3, 0xd2, 0xa3, 0xa9, 0xd3, 0xc5, + 0xbb, 0xdd, 0xb4, 0xfa, 0xbf, 0xaa, 0xbb, 0xf2, 0xba, 0xcf, + 0xd7, 0xf7, 0xa3, 0xac, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xb5, 0xe3, 0xca, 0xfd, + 0xbd, 0xcf, 0xb5, 0xcd, 0xa1, 0xa3, 0xb4, 0xfa, 0xc0, 0xed, + 0xb7, 0xb6, 0xce, 0xa7, 0xc8, 0xe7, 0xcf, 0xc2, 0xa3, 0xba, + 0x0d, 0x0a ] + +gb_18030_spam_string = bytes_to_binary_string gb_18030_bytes + +describe "normalize_string_to_utf8" do + + describe "when passed uniterpretable character data" do + + it "should reject it as invalid" do + + expect { + normalize_string_to_utf8 random_string + }.to raise_error(EncodingNormalizationError) + + expect { + normalize_string_to_utf8 random_string, 'UTF-8' + }.to raise_error(EncodingNormalizationError) + + end + end + + describe "when passed unlabelled Windows 1252 data" do + + it "should correctly convert it to UTF-8" do + + normalized = normalize_string_to_utf8 windows_1252_string + + normalized.should == "DASH – DASH" + + end + + end + + describe "when passed GB 18030 data" do + + it "should correctly convert it to UTF-8 if unlabelled" do + + normalized = normalize_string_to_utf8 gb_18030_spam_string + + normalized.should start_with("贵公司负责人") + + end + + end + +end + +describe "convert_string_to_utf8_or_binary" do + + describe "when passed uniterpretable character data" do + + it "should return it as a binary string" do + + converted = convert_string_to_utf8_or_binary random_string + converted.should == random_string + + if RUBY_VERSION.to_f >= 1.9 + converted.encoding.should == 'ASCII-8BIT' + end + + converted = convert_string_to_utf8_or_binary random_string,'UTF-8' + converted.should == random_string + + if RUBY_VERSION.to_f >= 1.9 + converted.encoding.should == 'ASCII-8BIT' + end + + end + end + + describe "when passed unlabelled Windows 1252 data" do + + it "should correctly convert it to UTF-8" do + + converted = convert_string_to_utf8_or_binary windows_1252_string + + converted.should == "DASH – DASH" + + if RUBY_VERSION.to_f >= 1.9 + converted.encoding.should == 'UTF-8' + end + end + + end + + describe "when passed GB 18030 data" do + + it "should correctly convert it to UTF-8 if unlabelled" do + + converted = convert_string_to_utf8_or_binary gb_18030_spam_string + + converted.should start_with("贵公司负责人") + + if RUBY_VERSION.to_f >= 1.9 + converted.encoding.should == 'UTF-8' + end + end + + end + +end diff --git a/spec/lib/i18n_interpolation.rb b/spec/lib/i18n_interpolation.rb index e8d046757..b07cf1e9a 100644 --- a/spec/lib/i18n_interpolation.rb +++ b/spec/lib/i18n_interpolation.rb @@ -12,7 +12,35 @@ describe "when using i18n" do it "should assume that simple translations are always html safe" do _("Hello").should be_html_safe end +end + +describe "n_" do + it "should return the translated singular" do + FastGettext.should_receive(:n_).with("Apple", "Apples", 1).and_return("Apfel") + n_("Apple", "Apples", 1).should == "Apfel" + end + + it "should return the translated plural" do + FastGettext.should_receive(:n_).with("Apple", "Apples", 3).and_return("Äpfel") + n_("Apple", "Apples", 3).should == "Äpfel" + end + it "should return the translated singular interpolated" do + FastGettext.should_receive(:n_).with("I eat {{count}} apple", "I eat {{count}} apples", 1). + and_return("Ich esse {{count}} Apfel") + n_("I eat {{count}} apple", "I eat {{count}} apples", 1, :count => 1).should == "Ich esse 1 Apfel" + end + + it "should return the translated plural interpolated" do + FastGettext.should_receive(:n_).with("I eat {{count}} apple", "I eat {{count}} apples", 3). + and_return("Ich esse {{count}} Äpfel") + n_("I eat {{count}} apple", "I eat {{count}} apples", 3, :count => 3).should == "Ich esse 3 Äpfel" + end + + it "should always be html safe when there is no interpolation" do + FastGettext.should_receive(:n_).with("Apple", "Apples", 1).and_return("Apfel") + n_("Apple", "Apples", 1).should be_html_safe + end end describe "gettext_interpolate" do diff --git a/spec/lib/mail_handler/mail_handler_spec.rb b/spec/lib/mail_handler/mail_handler_spec.rb index 79b779687..01bf179f8 100644 --- a/spec/lib/mail_handler/mail_handler_spec.rb +++ b/spec/lib/mail_handler/mail_handler_spec.rb @@ -20,12 +20,33 @@ describe 'when creating a mail object from raw data' do mail.to.should == ["request-66666-caa77777@whatdotheyknow.com", "foi@example.com"] end + it 'should return nil for malformed To: and Cc: lines' do + mail = get_fixture_mail('malformed-to-and-cc.email') + mail.to.should == nil + mail.cc.should == nil + end + it 'should convert an iso8859 email to utf8' do mail = get_fixture_mail('iso8859_2_raw_email.email') mail.subject.should match /gjatë/u MailHandler.get_part_body(mail).is_utf8?.should == true end + it 'should convert a Windows-1252 body mislabelled as ISO-8859-1 to UTF-8' do + mail = get_fixture_mail('mislabelled-as-iso-8859-1.email') + body = MailHandler.get_part_body(mail) + body.is_utf8?.should == true + # This email is broken in at least these two ways: + # 1. It contains a top bit set character (0x96) despite the + # "Content-Transfer-Encoding: 7bit" + # 2. The charset in the Content-Type header is "iso-8859-1" + # but 0x96 is actually a Windows-1252 en dash, which would + # be Unicode codepoint 2013. It should be possible to + # spot the mislabelling, since 0x96 isn't a valid + # ISO-8859-1 character. + body.should match(/ \xe2\x80\x93 /) + end + end describe 'when asked for the from name' do @@ -275,6 +296,12 @@ end describe 'when getting attachment attributes' do + it 'should handle a mail with a non-multipart part with no charset in the Content-Type header' do + mail = get_fixture_mail('part-without-charset-in-content-type.email') + attributes = MailHandler.get_attachment_attributes(mail) + attributes.size.should == 2 + end + it 'should get two attachment parts from a multipart mail with text and html alternatives and an image' do mail = get_fixture_mail('quoted-subject-iso8859-1.email') @@ -282,6 +309,13 @@ describe 'when getting attachment attributes' do attributes.size.should == 2 end + it 'should get one attachment from a multipart mail with text and HTML alternatives, which should be UTF-8' do + mail = get_fixture_mail('iso8859_2_raw_email.email') + attributes = MailHandler.get_attachment_attributes(mail) + attributes.length.should == 1 + attributes[0][:body].is_utf8?.should == true + end + it 'should expand a mail attached as text' do # Note that this spec will only pass using Tmail in the timezone set as datetime headers # are rendered out in the local time - using the Mail gem this is not necessary @@ -304,6 +338,52 @@ describe 'when getting attachment attributes' do attributes = MailHandler.get_attachment_attributes(mail) end + it 'should ignore truncated TNEF attachment' do + mail = get_fixture_mail('tnef-attachment-truncated.email') + attributes = MailHandler.get_attachment_attributes(mail) + attributes.length.should == 2 + end + + it 'should ignore anything beyond the final MIME boundary' do + pending do + # This example raw email has a premature closing boundary for + # the outer multipart/mixed - my reading of RFC 1521 is that + # the "epilogue" beyond that should be ignored. + # See https://github.com/mysociety/alaveteli/issues/922 for + # more discussion. + mail = get_fixture_mail('nested-attachments-premature-end.email') + attributes = MailHandler.get_attachment_attributes(mail) + attributes.length.should == 3 + end + end + + it 'should cope with a missing final MIME boundary' do + mail = get_fixture_mail('multipart-no-final-boundary.email') + attributes = MailHandler.get_attachment_attributes(mail) + attributes.length.should == 1 + attributes[0][:body].should match(/This is an acknowledgement of your email/) + attributes[0][:content_type].should == "text/plain" + attributes[0][:url_part_number].should == 1 + end + + it 'should ignore a TNEF attachment with no usable contents' do + # FIXME: "no usable contents" is slightly misleading. The + # attachment in this example email does have usable content in + # the body of the TNEF attachment, but the invocation of tnef + # historically used to unpack these attachments doesn't add + # the --save-body parameter, so that they have been ignored so + # far. We probably should include the body from such + # attachments, but, at the moment, with the pending upgrade to + # Rails 3, we just want to check that the behaviour is the + # same as before. + mail = get_fixture_mail('tnef-attachment-empty.email') + attributes = MailHandler.get_attachment_attributes(mail) + attributes.length.should == 2 + # This is the size of the TNEF-encoded attachment; currently, + # we expect the code just to return this without decoding: + attributes[1][:body].length.should == 7769 + end + it 'should produce a consistent set of url_part_numbers, content_types, within_rfc822_subjects and filenames from an example mail with lots of attachments' do mail = get_fixture_mail('many-attachments-date-header.email') diff --git a/spec/mailers/track_mailer_spec.rb b/spec/mailers/track_mailer_spec.rb index 346aa79c3..509d08331 100644 --- a/spec/mailers/track_mailer_spec.rb +++ b/spec/mailers/track_mailer_spec.rb @@ -166,20 +166,19 @@ describe TrackMailer do describe 'delivering the email' do - before do + before :each do @post_redirect = mock_model(PostRedirect, :save! => true, :email_token => "token") PostRedirect.stub!(:new).and_return(@post_redirect) ActionMailer::Base.deliveries = [] + @user = mock_model(User, + :name_and_email => MailHandler.address_from_name_and_email('Tippy Test', 'tippy@localhost'), + :url_name => 'tippy_test' + ) + TrackMailer.event_digest(@user, []).deliver # no items in it email for minimal test end it 'should deliver one email, with right headers' do - @user = mock_model(User, - :name_and_email => MailHandler.address_from_name_and_email('Tippy Test', 'tippy@localhost'), - :url_name => 'tippy_test' - ) - - TrackMailer.event_digest(@user, []).deliver # no items in it email for minimal test deliveries = ActionMailer::Base.deliveries if deliveries.size > 1 # debugging if there is an error deliveries.each do |d| @@ -196,6 +195,22 @@ describe TrackMailer do deliveries.clear end + + context "force ssl is off" do + # Force SSL is off in the tests. Since the code that selectively switches the protocols + # is in the initialiser for Rails it's hard to test. Hmmm... + # We could AlaveteliConfiguration.stub!(:force_ssl).and_return(true) but the config/environment.rb + # wouldn't get reloaded + + it "should have http links in the email" do + deliveries = ActionMailer::Base.deliveries + deliveries.size.should == 1 + mail = deliveries[0] + + mail.body.should include("http://") + mail.body.should_not include("https://") + end + end end end diff --git a/spec/models/foi_attachment_spec.rb b/spec/models/foi_attachment_spec.rb index 537a3363c..9b0115c44 100644 --- a/spec/models/foi_attachment_spec.rb +++ b/spec/models/foi_attachment_spec.rb @@ -1,6 +1,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') -describe FoiAttachment, " when calculating due date" do +describe FoiAttachment do before(:each) do load_raw_emails_data diff --git a/spec/models/incoming_message_spec.rb b/spec/models/incoming_message_spec.rb index e22235298..03152f5ff 100644 --- a/spec/models/incoming_message_spec.rb +++ b/spec/models/incoming_message_spec.rb @@ -59,12 +59,19 @@ describe IncomingMessage, " when dealing with incoming mail" do message.subject.should == "Câmara Responde: Banco de ideias" end - it 'should not error on display of a message which has no charset set on the body part and - is not good utf-8' do + it 'should deal with GB18030 text even if the charset is missing' do ir = info_requests(:fancy_dog_request) receive_incoming_mail('no-part-charset-bad-utf8.email', ir.incoming_email) message = ir.incoming_messages[1] message.parse_raw_email! + message.get_main_body_text_internal.should include("贵公司负责人") + end + + it 'should not error on display of a message which has no charset set on the body part and is not good UTF-8' do + ir = info_requests(:fancy_dog_request) + receive_incoming_mail('no-part-charset-random-data.email', ir.incoming_email) + message = ir.incoming_messages[1] + message.parse_raw_email! message.get_main_body_text_internal.should include("The above text was badly encoded") end @@ -412,6 +419,17 @@ describe IncomingMessage, " when uudecoding bad messages" do im.get_attachments_for_display.size.should == 1 end + it "should still work when parsed from the raw email" do + raw_email = load_file_fixture 'inline-uuencode.email' + mail = MailHandler.mail_from_raw_email(raw_email) + im = incoming_messages :useless_incoming_message + im.stub!(:raw_email).and_return(raw_email) + im.stub!(:mail).and_return(mail) + im.parse_raw_email! + attachments = im.foi_attachments + attachments.size.should == 2 + end + it "should apply censor rules" do mail = get_fixture_mail('incoming-request-bad-uuencoding.email') @@ -524,3 +542,46 @@ describe IncomingMessage, "when TNEF attachments are attached to messages" do end end +describe IncomingMessage, "when extracting attachments" do + + it 'handles the case where reparsing changes the body of the main part + and the cached attachment has been deleted' do + # original set of attachment attributes + attachment_attributes = { :url_part_number => 1, + :within_rfc822_subject => nil, + :content_type => "text/plain", + :charset => nil, + :body => "No way!\n", + :hexdigest => "0c8b1b0f5cb9c94ed15a180e73b5c7d1", + :filename => nil } + + # Make a small change in the body returned for the attachment + new_attachment_attributes = attachment_attributes.merge(:body => "No way!", + :hexdigest => "74d2c0a41e074f9cebe49324d5b47414") + + + # Simulate parsing with the original attachments + MailHandler.stub!(:get_attachment_attributes).and_return([attachment_attributes]) + incoming_message = incoming_messages(:useless_incoming_message) + + # Extract the attachments + incoming_message.extract_attachments! + + # delete the cached file for the main body part + main = incoming_message.get_main_body_text_part + main.delete_cached_file! + + # Simulate reparsing with the slightly changed body + MailHandler.stub!(:get_attachment_attributes).and_return([new_attachment_attributes]) + + # Re-extract the attachments + incoming_message.extract_attachments! + + attachments = incoming_message.foi_attachments + attachments.size.should == 1 + attachments.first.hexdigest.should == "74d2c0a41e074f9cebe49324d5b47414" + attachments.first.body.should == 'No way!' + end + +end + diff --git a/spec/models/outgoing_message_spec.rb b/spec/models/outgoing_message_spec.rb index 51bb6fdf5..60164fb31 100644 --- a/spec/models/outgoing_message_spec.rb +++ b/spec/models/outgoing_message_spec.rb @@ -16,7 +16,7 @@ describe OutgoingMessage, " when making an outgoing message" do it "should not index the email addresses" do # also used for track emails @outgoing_message.get_text_for_indexing.should_not include("foo@bar.com") - end + end it "should not display email addresses on page" do @outgoing_message.get_body_for_html_display.should_not include("foo@bar.com") @@ -33,6 +33,23 @@ describe OutgoingMessage, " when making an outgoing message" do it "should work out a salutation" do @om.get_salutation.should == "Dear Geraldine Quango," end + + it 'should produce the expected text for an internal review request' do + public_body = mock_model(PublicBody, :name => 'A test public body') + info_request = mock_model(InfoRequest, :public_body => public_body, + :url_title => 'a_test_title', + :title => 'A test title', + :apply_censor_rules_to_text! => nil) + outgoing_message = OutgoingMessage.new({ + :status => 'ready', + :message_type => 'followup', + :what_doing => 'internal_review', + :info_request => info_request + }) + expected_text = "I am writing to request an internal review of A test public body's handling of my FOI request 'A test title'." + outgoing_message.body.should include(expected_text) + end + end diff --git a/spec/models/xapian_spec.rb b/spec/models/xapian_spec.rb index b7c8b4dbe..c40334142 100644 --- a/spec/models/xapian_spec.rb +++ b/spec/models/xapian_spec.rb @@ -1,3 +1,4 @@ +# encoding: utf-8 require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') describe User, " when indexing users with Xapian" do @@ -369,3 +370,28 @@ describe PublicBody, " when only indexing selected things on a rebuild" do end end +# I would expect ActsAsXapian to have some tests under vendor/plugins/acts_as_xapian, but +# it looks like this is not the case. Putting a test here instead. +describe ActsAsXapian::Search, "#words_to_highlight" do + it "should return a list of words used in the search" do + s = ActsAsXapian::Search.new([PublicBody], "albatross words", :limit => 100) + s.words_to_highlight.should == ["albatross", "words"] + end + + it "should remove any operators" do + s = ActsAsXapian::Search.new([PublicBody], "albatross words tag:mice", :limit => 100) + s.words_to_highlight.should == ["albatross", "words"] + end + + # This is the current behaviour but it seems a little simplistic to me + it "should separate punctuation" do + s = ActsAsXapian::Search.new([PublicBody], "The doctor's patient", :limit => 100) + s.words_to_highlight.should == ["The", "doctor", "s", "patient"] + end + + it "should handle non-ascii characters" do + s = ActsAsXapian::Search.new([PublicBody], "adatigénylés words tag:mice", :limit => 100) + s.words_to_highlight.should == ["adatigénylés", "words"] + end + +end diff --git a/spec/spec.opts b/spec/spec.opts deleted file mode 100644 index ff5051c37..000000000 --- a/spec/spec.opts +++ /dev/null @@ -1,3 +0,0 @@ ---colour ---format -specdoc diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c1b2d094c..57ab88da2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -71,6 +71,19 @@ Spork.prefork do ActionMailer::Base.deliveries = [] end + # Any test that messes with the locale needs to restore the state afterwards so that it + # doesn't interfere with any subsequent tests. This is made more complicated by the + # ApplicationController#set_gettext_locale which sets the locale and so you may be setting + # the locale in your tests and not even realising it. So, let's make things easier for + # ourselves and just always restore the locale for all tests. + config.before(:each) do + @save_i18n_locale = I18n.locale + end + + config.after(:each) do + I18n.locale = @save_i18n_locale + end + # This section makes the garbage collector run less often to speed up tests last_gc_run = Time.now diff --git a/spec/support/email_helpers.rb b/spec/support/email_helpers.rb index 7e98c39f6..252b1f137 100644 --- a/spec/support/email_helpers.rb +++ b/spec/support/email_helpers.rb @@ -8,7 +8,7 @@ end def receive_incoming_mail(email_name, email_to, email_from = 'geraldinequango@localhost') email_name = file_fixture_name(email_name) - content = File.read(email_name) + content = File.open(email_name, 'rb') { |f| f.read } content.gsub!('EMAIL_TO', email_to) content.gsub!('EMAIL_FROM', email_from) RequestMailer.receive(content) diff --git a/spec/support/load_file_fixtures.rb b/spec/support/load_file_fixtures.rb index 08079f654..a54505e99 100644 --- a/spec/support/load_file_fixtures.rb +++ b/spec/support/load_file_fixtures.rb @@ -2,13 +2,7 @@ def file_fixture_name(file_name) return File.join(RSpec.configuration.fixture_path, "files", file_name) end -def load_file_fixture(file_name, as_binary=false) +def load_file_fixture(file_name) file_name = file_fixture_name(file_name) - content = File.open(file_name, 'r') do |file| - if as_binary - file.set_encoding(Encoding::BINARY) if file.respond_to?(:set_encoding) - end - file.read - end - return content + return File.open(file_name, 'rb') { |f| f.read } end |