allow users to select and retain a language
authorLunar <lunar@anargeek.net>
Tue, 20 Mar 2012 17:47:19 +0000 (18:47 +0100)
committerLunar <lunar@anargeek.net>
Thu, 14 Mar 2013 09:12:09 +0000 (10:12 +0100)
Instead of relying purely on the `Accept-language` header and on an hidden
parameter, we now offer links for users to select one of the supported
languages.

This selection is then retained by using Sinatra cookie based sessions.

Gemfile.lock
coquelicot.gemspec
lib/coquelicot/app.rb
spec/coquelicot/app_spec.rb
spec/coquelicot_spec.rb
views/index.haml
views/layout.haml
views/style.sass

index dc85cec..65dbc38 100644 (file)
@@ -112,5 +112,5 @@ DEPENDENCIES
   gettext
   hpricot
   rack-test
-  rspec
+  rspec (~> 2.6)
   timecop
index b003618..edce04d 100644 (file)
@@ -42,7 +42,7 @@ Gem::Specification.new do |s|
   s.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
   s.require_paths = ['lib']
 
-  s.add_development_dependency 'rspec'
+  s.add_development_dependency 'rspec', '~>2.6'
   s.add_development_dependency 'hpricot'
   s.add_development_dependency 'timecop'
   s.add_development_dependency 'rack-test'
index e71c7fe..940dd96 100644 (file)
@@ -69,6 +69,12 @@ module Coquelicot
     register Coquelicot::Auth::Extension
     helpers Coquelicot::Helpers
 
+    enable :sessions
+    # When sessions are enabled, Rack::Protection (added by Sinatra)
+    # will choke on our lack of rewind method on our input. Let's
+    # deactivate the protections which needs to parse parameters, then.
+    set :protection, :except => [:session_hijacking, :remote_token]
+
     set :root, Proc.new { app_file && File.expand_path('../../..', app_file) }
     set :depot_path, Proc.new { File.join(root, 'files') }
     set :max_file_size, 5 * 1024 * 1024 # 5 MiB
@@ -92,12 +98,21 @@ module Coquelicot
     # limit requests other than upload to an input body of 5 kiB max
     use Rainbows::MaxBody, 5 * 1024
 
+    AVAILABLE_LOCALES = %w(en fr de)
+
     FastGettext.add_text_domain 'coquelicot', :path => 'po', :type => 'po'
-    FastGettext.available_locales = [ 'en', 'fr', 'de' ]
+    FastGettext.available_locales = AVAILABLE_LOCALES
     Haml::MagicTranslations.enable(:fast_gettext)
     before do
       FastGettext.text_domain = 'coquelicot'
-      FastGettext.locale = params[:lang] || request.env['HTTP_ACCEPT_LANGUAGE'] || 'en'
+      if params[:lang]
+        locale = session[:lang] = params[:lang]
+      elsif session[:lang]
+        locale = session[:lang]
+      else
+        locale = request.env['HTTP_ACCEPT_LANGUAGE'] || 'en'
+      end
+      FastGettext.locale = locale
     end
 
     not_found do
index f83bc4e..3308723 100644 (file)
@@ -27,13 +27,70 @@ describe Coquelicot::Application do
 
   include_context 'with Coquelicot::Application'
 
+  def upload_password
+    'secret'
+  end
+
+  before(:each) do
+    app.set :authentication_method, :name => :simplepass,
+                                    :upload_password => Digest::SHA1.hexdigest(upload_password)
+  end
+
   describe 'get /' do
-    before do
-      visit '/'
+    context 'using the default language' do
+      before do
+        visit '/'
+      end
+      it 'should display the maximum file size' do
+        find(:xpath, '//label[@for="file"]/following::*[@class="note"]').
+            should have_content("Max. size: #{Coquelicot.settings.max_file_size.as_size}")
+      end
+      context 'when I explicitly request french' do
+        before do
+          click_link 'fr'
+        end
+        it 'should display a page in french' do
+          page.should have_content('Partager')
+        end
+      end
     end
-    it 'should display the maximum file size' do
-      find(:xpath, '//label[@for="file"]/following::*[@class="note"]').
-          should have_content("Max. size: #{Coquelicot.settings.max_file_size.as_size}")
+    context 'when my browser prefers french' do
+      around do |example|
+        begin
+          page.driver.header 'Accept-Language',  'fr-fr;q=1.0, en-gb;q=0.8, en;q=0.7'
+          example.run
+        ensure
+          page.driver.header 'Accept-Language', nil
+        end
+      end
+      context 'when I do nothing special' do
+        before do
+          visit '/'
+        end
+        it 'should display a page in french' do
+          page.should have_content('Partager')
+        end
+      end
+      context 'when I explicitly request german' do
+        before do
+          click_link 'de'
+        end
+        it 'should display a page in german' do
+          page.should have_content('Verteile')
+        end
+       # will fail without ordered Hash, see:
+       # <https://github.com/jnicklas/capybara/issues/670>
+        context 'after an upload', :if => RUBY_VERSION >= '1.9' do
+          before do
+            fill_in 'upload_password', :with => upload_password
+            attach_file 'file', __FILE__
+            click_button 'submit'
+          end
+          it 'should display a page in german' do
+            page.should have_content('Verteile eine weitere Datei')
+          end
+        end
+      end
     end
   end
 
index 5d9949f..994671c 100644 (file)
@@ -22,8 +22,6 @@ require 'hpricot'
 require 'tmpdir'
 require 'active_support'
 
-UPLOAD_PASSWORD = 'secret'
-
 # The specs in this file are written like what should have been Cucumber
 # features and without much knowledge of best practices with RSpec. Most of
 # them should be improved, rewritten and moved to `spec/coquelicot/app_spec.rb`.
@@ -36,10 +34,14 @@ describe 'Coquelicot' do
 
   include_context 'with Coquelicot::Application'
 
+  def upload_password
+    'secret'
+  end
+
   def upload(opts={})
     # We need the request to be in the right order
     params = ActiveSupport::OrderedHash.new
-    params[:upload_password] = UPLOAD_PASSWORD
+    params[:upload_password] = upload_password
     params[:expire] = 5
     params[:one_time] = ''
     params[:file_key] = ''
@@ -54,7 +56,7 @@ describe 'Coquelicot' do
     follow_redirect!
     last_response.should be_ok
     doc = Hpricot(last_response.body)
-    return (doc/'a').collect { |a| a.attributes['href'] }.
+    return (doc/'.url a').collect { |a| a.attributes['href'] }.
              select { |h| h.start_with? "http://#{last_request.host}/" }[0]
   end
 
@@ -92,14 +94,14 @@ PART
       get '/', :lang => 'fr'
       last_response.should be_ok
       doc = Hpricot(last_response.body)
-      (doc/"input.submit").attr('value').should == 'Partager¬†!'
+      (doc/"#submit").attr('value').should == 'Partager¬†!'
     end
   end
 
   context "when using 'simpleauth' authentication mechanism" do
     before(:each) do
       app.set :authentication_method, :name => :simplepass,
-                                      :upload_password => Digest::SHA1.hexdigest(UPLOAD_PASSWORD)
+                                      :upload_password => Digest::SHA1.hexdigest(upload_password)
     end
 
     context "after a successful upload" do
@@ -197,7 +199,7 @@ PART
       context "when sending the right password" do
         before do
           request "/authenticate", :method => "POST", :xhr => true,
-                                   :params => { :upload_password => UPLOAD_PASSWORD }
+                                   :params => { :upload_password => upload_password }
         end
         subject { last_response }
         it { should be_ok }
index 17fb21a..fbce8b5 100644 (file)
@@ -54,4 +54,4 @@
     .note Max. size: #{Coquelicot.settings.max_file_size.as_size}
   .field
     .submit
-      %input.submit{ :type => 'submit', :value => _('Share!') }
+      %input#submit{ :type => 'submit', :value => _('Share!') }
index 3dd2428..dacd7de 100644 (file)
                  };
     %script{ :type => 'text/javascript', :src => 'javascripts/coquelicot.js' }
   %body
+    #header
+      - unless uri.end_with? '/README'
+        - Coquelicot::Application::AVAILABLE_LOCALES.each do |locale|
+          %a{ :href => uri + "?lang=#{locale}" }= locale
     #container
       = yield
     #footer
index 122b0d3..9a1b3e3 100644 (file)
@@ -40,9 +40,25 @@ h1
 h2
   border-bottom: solid 1px #b60a00
 
+#header
+  width: 550px
+  margin-top: 1.5em
+  margin-bottom: 0.1em
+  margin-left: auto
+  margin-right: auto
+  padding-right: 25px
+  text-align: right
+  font-size: small
+
+#header a
+  text-decoration: none
+  color: #ccc
+
 #container
   width: 550px
-  margin: 2em auto
+  margin-bottom: 2em
+  margin-left: auto
+  margin-right: auto
   border-radius: 25px
   -moz-border-radius: 25px
   -webkit-border-radius: 25px