diff --git a/Gemfile b/Gemfile index 02fe369..d7a97c0 100644 --- a/Gemfile +++ b/Gemfile @@ -68,6 +68,7 @@ end group :development, :test do gem "rspec-rails", '~> 2.14' + gem "capybara", '~> 2.4.4' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index bd6ed1b..c90733e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,6 +72,12 @@ GEM sass (~> 3.3) thor builder (3.2.2) + capybara (2.4.4) + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) choice (0.1.6) coderay (1.1.0) coffee-rails (4.0.1) @@ -146,10 +152,13 @@ GEM treetop (~> 1.4.8) memcachier (0.0.2) mime-types (1.25.1) + mini_portile (0.6.1) minitest (5.3.4) multi_json (1.10.1) ngannotate-rails (0.9.2) rails (>= 3.1) + nokogiri (1.6.4.1) + mini_portile (~> 0.6.0) orm_adapter (0.5.0) paranoia (2.0.0) activerecord (~> 4.0) @@ -257,6 +266,8 @@ GEM multi_json (~> 1.0, >= 1.0.2) warden (1.2.3) rack (>= 1.0) + xpath (2.0.0) + nokogiri (~> 1.3) PLATFORMS ruby @@ -266,6 +277,7 @@ DEPENDENCIES awesome_print better_errors binding_of_caller + capybara (~> 2.4.4) coffee-rails dalli database_cleaner diff --git a/app/assets/javascripts/global.js.coffee b/app/assets/javascripts/global.js.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/global.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index f08704c..ab86b88 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -11,6 +11,8 @@ *= require_self */ +@import "flags"; + .post { border-top: solid black 1px; list-style: none; diff --git a/app/assets/stylesheets/global.css.scss b/app/assets/stylesheets/global.css.scss new file mode 100644 index 0000000..094bcc0 --- /dev/null +++ b/app/assets/stylesheets/global.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the global controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b6b91ea..d59f036 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,6 @@ class ApplicationController < ActionController::Base before_filter :configure_permitted_parameters, if: :devise_controller? + before_filter :setup_vars include Pundit protect_from_forgery @@ -18,9 +19,16 @@ class ApplicationController < ActionController::Base def set_locale if params[:locale] - session[:locale] = params[:locale] + locale=params[:locale] + session[:locale] = locale + else + locale=extract_locale_from_accept_language_header end - I18n.locale = session[:locale] || I18n.default_locale + + # we check that provided locale from browser is in our supported list of languages + # if not, we use the default from rails conf + session[:locale] ||= @supported_langs.include?(locale)? locale: I18n.default_locale.to_s + I18n.locale = session[:locale] true end @@ -49,6 +57,9 @@ class ApplicationController < ActionController::Base end end + def setup_vars + @supported_langs=%w(es ca en) + end private @@ -79,4 +90,10 @@ class ApplicationController < ActionController::Base def authenticate_superuser! superuser? || redirect_to(root_path) end + + # To get locate from client supplied information + # see http://guides.rubyonrails.org/i18n.html#setting-the-locale-from-the-client-supplied-information + def extract_locale_from_accept_language_header + request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first + end end diff --git a/app/controllers/global_controller.rb b/app/controllers/global_controller.rb new file mode 100644 index 0000000..9119908 --- /dev/null +++ b/app/controllers/global_controller.rb @@ -0,0 +1,26 @@ +class GlobalController < ApplicationController + + def switch_lang + set_locale + + if current_user and current_organization + redirect_to offers_path + else + go_home + end + end + + def go_home + l = session[:locale] + + case l + when 'es' + url='home' + else + url='home_'+l + end + redirect_to page_path(url) + end + + +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index f9e1d0d..6c76c7b 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -44,7 +44,7 @@ class UsersController < ApplicationController if @user.persisted? @user.add_to_organization current_organization - redirect_to @user + redirect_to users_path else redirect_to :action => "new" end diff --git a/app/helpers/global_helper.rb b/app/helpers/global_helper.rb new file mode 100644 index 0000000..be6bce4 --- /dev/null +++ b/app/helpers/global_helper.rb @@ -0,0 +1,2 @@ +module GlobalHelper +end diff --git a/app/views/application/_navbar.html.haml b/app/views/application/_navbar.html.haml index eef7385..6490186 100644 --- a/app/views/application/_navbar.html.haml +++ b/app/views/application/_navbar.html.haml @@ -1,7 +1,7 @@ %nav.navbar.navbar-static-top.navbar-inverse{role: :navigation} .container .navbar-header - %a.navbar-brand(href="/") TimeOverflow + %a.navbar-brand(href="/global/go_home") TimeOverflow %ul.nav.navbar-nav - if current_user and current_organization @@ -81,8 +81,20 @@ = link_to m.organization do = glyph(:pencil) = t "layouts.application.edit_org", organization: m.organization - - else %li %a{href: new_user_session_path} = t "layouts.application.login" + + %li.divider{role: :presentation} + %li{role: :presentation} + = link_to "/global/switch_lang?locale=es", id: 'es_flag' do + = image_tag("blank.png",{class: "flag flag-es", alt: "Español"}) + + %li{role: :presentation} + = link_to "/global/switch_lang?locale=ca", id: 'ca_flag' do + = image_tag("blank.png",{class: "flag flag-catalonia", alt: "Català"}) + + %li{role: :presentation} + = link_to "/global/switch_lang?locale=en", id: 'en_flag' do + = image_tag("blank.png",{class: "flag flag-gb", alt: "English"}) diff --git a/app/views/global/switch_lang.html.haml b/app/views/global/switch_lang.html.haml new file mode 100644 index 0000000..25a3398 --- /dev/null +++ b/app/views/global/switch_lang.html.haml @@ -0,0 +1,2 @@ +%h1 Global#switch_lang +%p Find me in app/views/global/switch_lang.html.haml diff --git a/app/views/pages/home_ca.html.haml b/app/views/pages/home_ca.html.haml new file mode 100644 index 0000000..926c6b6 --- /dev/null +++ b/app/views/pages/home_ca.html.haml @@ -0,0 +1,66 @@ += content_for :window_title do + TimeOverflow - Gestió on-line de Bancs de Temps + +.container-inner + - if current_user and !current_organization + = render 'organization_list' + .row + .col-sm-6 + .text-center + %h1 TimeOverflow + = image_tag "sand-clock.png", width: 50 + %h2 El software disenyat per i per als Bancs de Temps + %em TimeOverflow és lliure, gratuït i col·laboratiu + + .text-left{style: "margin-top: 40px;"} + :markdown + Funcionalitats de **TimeOverflow**: + + * Gestió del Banc de Temps amb perfils d'administració + * Altes/baixes/modificacions de membres + * Publicar ofertes i demandes + * Introduir xecs i gestionar la comptabilitat + * Xarxa social i banca en línia accessible pels membres + * Els membres d'un Banc de Temps poden accedir al sistema i contactar amb altres membres + * Publicar ofertes i demandes + * Pagar hores a altres membres + + %h2 Ets un Banc de Temps? + .text-center + %a.btn.btn-primary.btn-large{href: "http://adbdt.org/ca/contacta/", style:"font-size:30px;margin-top:20px;"} + Sol·licita accés a TimeOverflow + .text-center + %p + %em (ens posarem en contacte amb tu per posar-lo en marxa o fer-vos una demostració) + + .col-sm-6 + .text-center + %h2 Ja ets membre d'un Banc de Temps que fa servir TimeOverflow? + %a.btn.btn-primary.btn-large{href: new_user_session_path, style:"font-size:30px;margin-top:20px;"} + Entra aquí + + %p + :markdown + Si ets una persona interessada a participar en un Banc de Temps, + busca el teu Banc de Temps més proper a + [bdtonline.org](http://www.bdtonline.org "Bancs de Temps"). + + = image_tag "oferta.png", class: "img-responsive center-block" + + %h2 Internet potencia els Bancs de Temps + :markdown + TimeOveflow està especialment dissenyat ** per i per als Bancs de Temps ** + que existeixen físicament([veure mapa](http://www.bdtonline.org "Bancs de Temps")), + la seva fi és potenciar gràcies a Internet. + + Gràcies al treball conjunt de [CoopDevs](https://github.com/coopdevs) + i la [ADBdT](http://adbdt.org/ca "Associació per al Desenvolupament dels Bancs de Temps") + avui podem oferir el sofware **TimeOverflow** a tots els Bancs de + Temps que així ho desitgin de manera lliure i gratuïta. + + El codi font de TimeOverflow està disponible sota una llicència + open source, ja es pot descarregar a [https://github.com/coopdevs/timeoverflow](https://github.com/coopdevs/timeoverflow) + + .text-center + = link_to "https://github.com/coopdevs/timeoverflow", title: "TimeOverflow Github" do + = image_tag("github.png", alt: "Github logo", height: 30) diff --git a/app/views/pages/home_en.html.haml b/app/views/pages/home_en.html.haml new file mode 100644 index 0000000..fab9fe2 --- /dev/null +++ b/app/views/pages/home_en.html.haml @@ -0,0 +1,66 @@ += content_for :window_title do + TimeOverflow - Time Bank on-line management + +.container-inner + - if current_user and !current_organization + = render 'organization_list' + .row + .col-sm-6 + .text-center + %h1 TimeOverflow + = image_tag "sand-clock.png", width: 50 + %h2 The software designed by and for Time Banks + %em TimeOverflow is open source, free and collaborative + + .text-left{style: "margin-top: 40px;"} + :markdown + Features of **TimeOverflow**: + + * Time Bank management with administration roles + * Creation/deletion/updates of bank members + * Posting of offers and inquiries + * Enter checks and manage accounting + * Social network and online banking accessible by members + * Members of a Time Bank can access the system and connect with other members + * Posting of offers and inquiries + * Pay hours to other members + + %h2 Are you a Time Bank? + .text-center + %a.btn.btn-primary.btn-large{href: "http://adbdt.org/contacta/", style:"font-size:30px;margin-top:20px;"} + Request access to TimeOverflow + .text-center + %p + %em (We will contact you to start it up or make you a demonstration) + + .col-sm-6 + .text-center + %h2 Already a member of a Time Bank that uses TimeOverflow? + %a.btn.btn-primary.btn-large{href: new_user_session_path, style:"font-size:30px;margin-top:20px;"} + Click here + + %p + :markdown + If you are a person interested in participating in a Time Bank, + find your closest Time Bank at + [bdtonline.org](http://www.bdtonline.org "Bancos de Tiempo"). + + = image_tag "oferta.png", class: "img-responsive center-block" + + %h2 Internet enhances Time Banks activity + :markdown + TimeOveflow is specially designed **for and by Time Banks** + that physically exist ([see map](http://www.bdtonline.org "Bancos de Tiempo")), + and its purpose is to boost them thanks to Internet. + + Thanks to the joint job of [CoopDevs](https://github.com/coopdevs) + and [ADBdT](http://adbdt.org "Association for the Development of Time Banks") + today que can offer the software **TimeOverflow** to all Time Banks + that want it, open and for free. + + The source code of TimeOverflow is available based on an open source + license and you can download it at [https://github.com/coopdevs/timeoverflow](https://github.com/coopdevs/timeoverflow) + + .text-center + = link_to "https://github.com/coopdevs/timeoverflow", title: "TimeOverflow Github" do + = image_tag("github.png", alt: "Github logo", height: 30) diff --git a/app/views/users/_user_rows.html.haml b/app/views/users/_user_rows.html.haml index 9e3bd34..f7ebeb8 100644 --- a/app/views/users/_user_rows.html.haml +++ b/app/views/users/_user_rows.html.haml @@ -1,4 +1,4 @@ -%tr{ "ng-repeat" => "user in users | orderBy:sort | filter:filterTerm" } +%tr{ "ng-repeat" => "user in users | orderBy:sort | filter:filterTerm", "ng-class" => "{'bg-danger': !user.active}" } %td %img{ "ng-src" => "{{user.avatar}}", height: "32px", width: "32px" } %td {{user.member_id}} @@ -38,4 +38,3 @@ %span{ "ng-if" => "!user.active" } .glyphicon.glyphicon-ok = t ".activate" - diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 57a8eab..e26c4d1 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -21,13 +21,13 @@ ca: one: Demanda other: Demandes transfer: - one: Transferencia + one: Transferència other: Transferències post: - one: Anuncio - other: Anuncios + one: Anunci + other: Anuncis comment: - one: Comentario + one: Comentari other: Comentaris attributes: @@ -41,7 +41,7 @@ ca: one: Banc de Temps other: Bancs de Temps email: Correu - registration_date: Data de alta + registration_date: Data d'alta gender: Gènere registration_number: Codi d'usuari date_of_birth: Data de naixement @@ -68,7 +68,7 @@ ca: <<: *common_attributes name: Nom parent: Categoria pare - name_translations: Nom (traducciones) + name_translations: Nom (traduccions) transfer: <<: *common_attributes amount: Quantitat @@ -82,8 +82,8 @@ ca: tag_list: Etiquetes joinable: Es poden afegir altres permanent: Permanent - start_on: Comença el - end_on: Finalitza el + start_on: Comença al + end_on: Finalitza al # ETIQUETAS VARIADAS EN PLANTILLAS @@ -106,7 +106,7 @@ ca: sing_out: Desconnectar organization_list: welcome: Benvingut a Timeoverflow %{user}. - not_member: No ets membre de cap Banc de Temps, per fer servir el sistema hauràs de possar-te en contcat amb un dels Bancs de Temps del sistema + not_member: No ets membre de cap Banc de Temps, per fer servir el sistema hauràs de possar-te en contacte amb un dels Bancs de Temps del sistema time_bank: Banc de Temps email: Correu phone: Telèfon @@ -116,7 +116,7 @@ ca: neighborhood: Barri city: Ciutat tips: - entertag: Introdueix etiquetes separadas per comes + entertag: Introdueix etiquetes separades per comes user_not_found: "Usuari no trobat." # views/users/index... @@ -210,7 +210,7 @@ ca: no_movements: Sense intercanvis days_without_swaps: Dies sense intercanvis demographics: Dades demogràfiques - by_ages: Por edats + by_ages: Per edats num_people: "Nombre de Persones" unknown: Desconegut @@ -247,13 +247,13 @@ ca: save: Desar add_new: Crear nou cancel_membership: Esborrar definitivament - back: Enrrere + back: Enrere give_time: Transferir temps filter: Filtre all: Tot search: Cercar information: Informació - member_count: "Nombre d'usuarios:" + member_count: "Nombre d'usuaris:" balance: 'Balanç:' movements: Moviments date: Data @@ -272,7 +272,7 @@ ca: layouts: application: login: Entrar - edit_profile: Modificarb perfil + edit_profile: Modificar perfil edit_org: "Modificar %{organization}" report: report_title: "INFORME" diff --git a/config/locales/devise.ca.yml b/config/locales/devise.ca.yml index 8cc7d9b..39f206d 100644 --- a/config/locales/devise.ca.yml +++ b/config/locales/devise.ca.yml @@ -25,7 +25,7 @@ ca: locked: "El teu compte ha estat bloquejat." not_found_in_database: "Correu o clau invàlida." timeout: "La teva sessió ha caducat. Si us plau, torna a entrar per continuar." - unauthenticated: "Has que entrar per continuar." + unauthenticated: "Has de entrar per continuar." unconfirmed: "Has de confiramar el teu compte abans de continuar." mailer: confirmation_instructions: @@ -35,7 +35,7 @@ ca: unlock_instructions: subject: "Instruccions de desbloqueig del compte" omniauth_callbacks: - failure: "No s'ha pogut autentificar desde %{kind} perque \"%{reason}\"." + failure: "No s'ha pogut autentificar desde %{kind} perquè \"%{reason}\"." success: "Autentificació realitzada desde el compte %{kind} ." passwords: no_token: "No pots accedir a aquesta pàgina si no vens d'un correu per restablir la contrasenya. Si vens d'un correu per a restablir la contrasenya, assegurat que has utilitzat la URL completa." @@ -92,4 +92,4 @@ ca: not_locked: "no es va bloquejar" not_saved: one: "1 error impedeix que aquest %{resource} es desi:" - other: "%{count} errors impedeix que aquest %{resource} es desi:" + other: "%{count} errors impedeixen que aquest %{resource} es desi:" diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index d5b4520..997ed11 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -2,6 +2,14 @@ en: devise: + shared: + links: + sign_in: "Login" + sign_up: "Register" + forgot_your_password: "I do not remember my password" + didnt_receive_confirmation_instructions: "Resend confirmation email" + didnt_receive_unlock_instructions: "Receive unlock instructions" + sign_in_with: "Login with %{provider}" confirmations: confirmed: "Your account was successfully confirmed." confirmed_and_signed_in: "Your account was successfully confirmed. You are now signed in." @@ -44,6 +52,9 @@ en: sessions: signed_in: "Signed in successfully." signed_out: "Signed out successfully." + new: + sign_in: "Login" + remember_me: "Remember me" unlocks: send_instructions: "You will receive an email with instructions about how to unlock your account in a few minutes." send_paranoid_instructions: "If your account exists, you will receive an email with instructions about how to unlock it in a few minutes." diff --git a/config/locales/en.yml b/config/locales/en.yml index 179c14c..59e3bab 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,5 +1,278 @@ -# Sample localization file for English. Add more files in this directory for other locales. -# See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points. - en: - hello: "Hello world" + # NOMBRES DE LAS COSAS + activerecord: + + models: + # Para usar: + # User.model_name.human + user: + one: User + other: Users + organization: + one: Time Bank + other: Time Banks + category: + one: Category + other: Categories + offer: + one: Offer + other: Offers + inquiry: + one: Inquiry + other: Inquiries + transfer: + one: Transfer + other: Transfers + post: + one: Post + other: Posts + comment: + one: Comment + other: Comments + + attributes: + common: &common_attributes + created_at: Created + updated_at: Updated + user: + <<: *common_attributes + username: Name + organization: + one: Time Bank + other: Time Banks + email: Email + registration_date: Registration date + gender: Gender + registration_number: User code + date_of_birth: Date of birth + identity_document: Identity Document + phone: Phone + alt_phone: Alternative Phone + admin: Organization Administrator + superadmin: System Administrator + description: Description + unconfirmed_email: Unconfirmed Email + organization: + <<: *common_attributes + name: Name + reg_number_seq: User sequence number + theme: Theme + email: Email + phone: Phone + public_opening_times: Opening hours + description: Description + address: Address + neighborhood: Neighborhood + city: City + category: + <<: *common_attributes + name: Name + parent: Parent category + name_translations: Name (translations) + transfer: + <<: *common_attributes + amount: Quantity + reason: Comments + source: Source + post: + <<: *common_attributes + title: Title + description: Description + category: Category + tag_list: Tags + joinable: Can join others + permanent: Permanent + start_on: Start on + end_on: Ends on + + # ETIQUETAS VARIADAS EN PLANTILLAS + + application: + terms_conditions: terms of service + navbar: + administration: Administration + reports: Reports + stats: Statistics + users: Users + categories: Services + organizations: Organizations + statistics: Statistics + admin: Manage + adminshort: Admin + tags: Tags + sign_out: Logout + menu: + sign_in: Login + sing_out: Logout + organization_list: + welcome: Welcome to Timeoverflow %{user}. + not_member: You do not belong to any time bank at this moment, in order to use this platform you need to keep in touch with one of the time banks that have joint the platform + time_bank: Time Bank + email: Email + phone: Phone + public_opening_times: Opening hours + description: Description + address: Address + neighborhood: Neighborhood + city: City + tips: + entertag: Enter tags separated by commas + user_not_found: "User not found." + # views/users/index... + + users: + + index: + create: Create new user + manage_warning_angular: "You are going to change privileges for user {{username}}" + cancel_warning_angular: "You are going to delete account from the Time Bank for user {{username}}" + active_warning_angular: "You are going to change user account status for {{username}}" + actions: Actions + new: + new_user: New user + show: + phone: + one: Phone + other: Phones + data: User details + categories: Offered Services + account: Last transactions + inactive: "(Inactive)" + accounts: Accounts + created_at: "Registered:" + user_no: "User #:" + balance: "Balance:" + movements: "Transactions" + date: Date + from_to: From/To + post: Post + reason: Reason + quantity: Quantity + + give_time: + give_time: Give time to + form: + superadmin_warning: Warning!!! You are giving GOD PRIVILEGES to this user!! + admin_warning: Warning!!! You are giving privileges to this user!! + user_rows: + activate: Activate + deactivate: Deactivate + manage_warning: "You are going to change privileges for user %{user}" + cancel_warning: "You are going to delete account from the Time Bank for user %{user}" + active_warning: "You are going to change user account status for %{user}" + + tags: + alpha_grouped_index: + maintitle: Available Tags + terms: + accept: Accept + offers: + index: + offered_by: "Offered by %{size} people" + new_offer: "New offer" + filter: Filter + by_category: By category + by_tag: By Tag + show: + offered_by: "Offered by" + new: + submit: Create offer + + inquiries: + index: + new_inquiry: New inquiry + new: + submit: Create inquiry + + categories: + index: + all: All + tree: Main + global: Global + local: Local + add: Add service + + reports: + cat_with_users: + title: Offered Services + user_list: + title: User List + statistics: + statistics: Statistics + bank_activity: "Bank Activity" + per_month: "Per Month" + global_activity: "Global Activity" + users_reg: "Registered users" + num_swaps: "Transactions" + total_hours: "Hours transfered" + inactive_users: Inactive users + last_movement: Last transaction + no_movements: Without transactions + days_without_swaps: Days without transactions + demographics: Demographic data + by_ages: Per ages + num_people: "# of people" + unknown: Unknown + + stats: + min_balance: + title: Users Minimum Balance + + organizations: + index: + member_count: Number of users + new: + new: New bank + show: + contact_information: Contact information + give_time: + give_time: Give time to + + transfers: + computation: + joiner: " and " + hour: + one: ! '%{count} hour' + other: ! '%{count} hours' + minute: + one: ! '%{count} minute' + other: ! '%{count} minutes' + + global: + table: + actions: Actions + show: Show + edit: Update + delete: Delete + save: Save + add_new: Create new + cancel_membership: Permanent deletion + back: Back + give_time: Time transfer + filter: Filter + all: All + search: Search + information: Information + member_count: 'Number of users:' + balance: 'Balance:' + movements: Transactions + date: Data + source_destination: 'From/to' + announcements: Post + reason: Reason + amount: Quantity + demote: Demote to normal user + promote: Promote to administrator + + active_admin: + comments: + resource_type: Resource type + author_type: Author type + + layouts: + application: + login: Login + edit_profile: Update my profile + edit_org: "Update %{organization}" + report: + report_title: "REPORT" diff --git a/config/routes.rb b/config/routes.rb index 311c15f..8a8edaf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Timeoverflow::Application.routes.draw do + get 'global/switch_lang' + get 'global/go_home' get 'tags/index' devise_for :users diff --git a/spec/controllers/global_controller_spec.rb b/spec/controllers/global_controller_spec.rb new file mode 100644 index 0000000..0298e53 --- /dev/null +++ b/spec/controllers/global_controller_spec.rb @@ -0,0 +1,186 @@ +require 'spec_helper' + +describe GlobalController do + include_context "stub browser locale" + before { set_browser_locale('it') } + + describe "User not logged and GET 'switch_lang' using browser's locale" do + subject { get 'switch_lang' } + + context 'browser in spanish' do + it "redirects to spanish home" do + # We stub the method that extracts browsers language info and force to return expected value for + # context + set_browser_locale('es') + + #response.should redirect to home spanish + subject.should redirect_to('/home') + end + end + + context 'browser in catalan' do + it "redirects to spanish home" do + # We stub the method that extracts browsers language info and force to return expected value for + # context + set_browser_locale('ca') + + #response.should redirect to home catalan + subject.should redirect_to('/home_ca') + end + end + + context 'broser in english' do + it "redirects to spanish home" do + # We stub the method that extracts browsers language info and force to return expected value for + # context + set_browser_locale('en') + + #response.should redirect to home english + subject.should redirect_to('/home_en') + end + end + + context 'browser in non-supported language' do + it "redirects to spanish default home" do + # We stub the method that extracts browsers language info and force to return expected value for + # context + # We stub to an invalid locale to check it redirects to the right default one + set_browser_locale('fr') + + #response.should redirect to default home (spanish) + subject.should redirect_to('/home') + end + end + end + + describe "User not logged and GET 'switch_lang' by selection in navbar in english" do + subject { get 'switch_lang', {:locale => 'en'} } + + it "redirects to english home" do + #response.should redirect to home_en + subject.should redirect_to('/home_en') + end + end + + describe "User not logged and GET 'switch_lang' by selection in navbar in spanish" do + subject { get 'switch_lang', {:locale => 'es'} } + + it "redirects to spanish home" do + #response.should redirect to home (spanish) + subject.should redirect_to('/home') + end + end + + describe "User not logged and GET 'switch_lang' by selection in navbar in catalan" do + subject { get 'switch_lang', {:locale => 'ca'} } + it "redirects to catalan home" do + #response.should redirect to home_ca + subject.should redirect_to('/home_ca') + end + end + + + # + # Now with user logged in in different default browser languages + # + + describe "User logged and GET 'switch_lang' using browser's locale" do + + let! (:test_organization) { Fabricate(:organization)} + let! (:member) { Fabricate(:member, organization: test_organization) } + + context 'browser in spanish' do + it "redirects to spanish offers page" do + # We stub the method that extracts browsers language info and force to return expected value for + # context + set_browser_locale('es') + + login(member.user) + visit offers_path + + current_path.should == "/offers" + + page.body.should include('Ofertas') + end + end + + context 'browser in catalan' do + it "redirects to catalan offers page" do + # We stub the method that extracts browsers language info and force to return expected value for + # context + set_browser_locale('ca') + + login(member.user) + visit offers_path + + current_path.should == "/offers" + + page.body.should include('Ofertes') + end + end + + context 'browser in catalan' do + it "redirects to catalan offers page" do + # We stub the method that extracts browsers language info and force to return expected value for + # context + set_browser_locale('en') + + login(member.user) + visit offers_path + + current_path.should == "/offers" + + page.body.should include('Offers') + end + end + end + + # + # Now with user logged in having selected language + # + + describe "User logged and GET 'switch_lang' using browser's locale" do + + let! (:test_organization) { Fabricate(:organization)} + let! (:member) { Fabricate(:member, organization: test_organization) } + + context 'user clicks in Spanish flag' do + it "redirects to spanish offers page" do + + visit '/' + click_link 'es_flag' + login(member.user) + visit offers_path + + current_path.should == "/offers" + page.body.should include('Ofertas') + end + end + + context 'user clicks in English flag' do + it "redirects to catalan offers page" do + + visit '/' + click_link 'en_flag' + login(member.user) + visit offers_path + + current_path.should == "/offers" + page.body.should include('Offers') + end + end + + context 'user clicks in catalan flag' do + it "redirects to catalan offers page" do + + visit '/' + click_link 'ca_flag' + login(member.user) + visit offers_path + + current_path.should == "/offers" + page.body.should include('Ofertes') + end + end + end +end diff --git a/spec/controllers/inquiries_controller_spec.rb b/spec/controllers/inquiries_controller_spec.rb index 5424c87..d3d0f72 100644 --- a/spec/controllers/inquiries_controller_spec.rb +++ b/spec/controllers/inquiries_controller_spec.rb @@ -5,6 +5,8 @@ describe InquiriesController do let (:member) { Fabricate(:member, organization: test_organization)} let (:another_member) { Fabricate(:member, organization: test_organization)} let! (:inquiry) { Fabricate(:inquiry, user: member.user, organization: test_organization)} + include_context "stub browser locale" + before { set_browser_locale('ca') } describe "GET #index" do context "with a logged user" do diff --git a/spec/controllers/offers_controller_spec.rb b/spec/controllers/offers_controller_spec.rb new file mode 100644 index 0000000..1315237 --- /dev/null +++ b/spec/controllers/offers_controller_spec.rb @@ -0,0 +1,86 @@ +require 'spec_helper' + +describe OffersController do + let (:test_organization) { Fabricate(:organization)} + let (:member) { Fabricate(:member, organization: test_organization)} + let (:another_member) { Fabricate(:member, organization: test_organization)} + let! (:offer) { Fabricate(:offer, user: member.user, organization: test_organization)} + include_context "stub browser locale" + before { set_browser_locale('ca') } + + describe "GET #index" do + context "with a logged user" do + it "populates and array of offers" do + login(another_member.user) + + get 'index' + expect(assigns(:offers)).to eq([offer]) + end + end + end + + + describe "GET #show" do + context "with valid params" do + context "with a logged user" do + it "assigns the requested offer to @offer" do + login(another_member.user) + + get 'show', id: offer.id + expect(assigns(:offer)).to eq(offer) + end + end + end + end + + describe "POST #create" do + context "with valid params" do + context "with a logged user" do + it "creates a new offer" do + login(another_member.user) + + expect { + post 'create', offer: Fabricate.to_params(:offer) + }.to change(Offer,:count).by(1) + end + end + end + end + + describe "PUT #update" do + context "with valid params" do + context "with a logged user" do + it "located the requested @offer" do + login(member.user) + + put 'update', id: offer.id, offer: Fabricate.to_params(:offer) + expect(assigns(:offer)).to eq(offer) + end + + it "changes @offer's attributes" do + login(member.user) + + put 'update', id: offer.id, offer: Fabricate.to_params(:offer, user: member, title: "New title", description: "New description") + + offer.reload + expect(offer.title).to eq("New title") + expect(offer.description).to eq("New description") + end + end + end + + context "with invalid params" do + context "with a logged user" do + it "does not change @offer's attributes" do + login(member.user) + + put :update, id: offer.id, offer: Fabricate.to_params(:offer, user: nil, title: "New title", description: "New description") + + expect(offer.title).not_to eq("New title") + expect(offer.description).not_to eq("New description") + end + end + end + end + +end diff --git a/spec/controllers/tags_controller_spec.rb b/spec/controllers/tags_controller_spec.rb index abeaffc..5c3c152 100644 --- a/spec/controllers/tags_controller_spec.rb +++ b/spec/controllers/tags_controller_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe TagsController do + include_context "stub browser locale" + before { set_browser_locale('it') } describe "GET 'index'" do it "returns http success" do diff --git a/spec/controllers/transfers_controller_spec.rb b/spec/controllers/transfers_controller_spec.rb index 122b2b9..938da17 100644 --- a/spec/controllers/transfers_controller_spec.rb +++ b/spec/controllers/transfers_controller_spec.rb @@ -5,10 +5,13 @@ describe TransfersController do let (:member_admin) { Fabricate(:member, organization: test_organization, manager: true)} let (:member_giver) { Fabricate(:member, organization: test_organization) } let (:member_taker) { Fabricate(:member, organization: test_organization) } - + include_context "stub browser locale" + before { set_browser_locale('ca') } + describe "POST #create" do before { login(user) } + context "with valid params" do context "with an admin user logged" do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb new file mode 100644 index 0000000..e9e333a --- /dev/null +++ b/spec/controllers/users_controller_spec.rb @@ -0,0 +1,146 @@ +require 'spec_helper' + +describe UsersController do + let (:test_organization) { Fabricate(:organization)} + let (:member_admin) { Fabricate(:member, organization: test_organization, manager: true)} + let (:member) { Fabricate(:member, organization: test_organization)} + let! (:user) { member.user } + let! (:admin_user) { member_admin.user} + include_context "stub browser locale" + before { set_browser_locale('ca') } + + describe "GET #index" do + context "with an normal logged user" do + it "populates and array of users" do + login(member.user) + + get 'index' + expect(assigns(:users)).to eq([user,admin_user]) + end + end + context "with an admin logged user" do + it "populates and array of users" do + login(member_admin.user) + + get 'index' + expect(assigns(:users)).to eq([user,admin_user]) + end + end + end + + + describe "GET #show" do + context "with valid params" do + context "with a normal logged user" do + it "assigns the requested user to @user" do + login(member.user) + + get 'show', id: user.id + expect(assigns(:user)).to eq(user) + end + end + context "with an admin logged user" do + it "assigns the requested user to @user" do + login(member_admin.user) + + get 'show', id: user.id + expect(assigns(:user)).to eq(user) + end + end + end + end + + describe "POST #create" do + context "with valid params" do + subject {post 'create', user: Fabricate.to_params(:user)} + + context "with a normal logged user" do + it "does not create a new user" do + login(member.user) + + # TODO: + # Expect exception... + # + expect { + post 'create', user: Fabricate.to_params(:user) + }.to change(User,:count).by(0) + end + end + + context "with an admin logged user" do + it "creates a new user" do + login(member_admin.user) + + expect { + subject + }.to change(User,:count).by(1) + + subject.should redirect_to('/members') + + end + end + end + end + + describe "PUT #update" do + context "with valid params" do + context "with a logged" do + + context "normal user" do + it "located the requested @user" do + login(member.user) + + put 'update', id: user.id, user: Fabricate.to_params(:user) + expect(assigns(:user)).to eq(user) + end + + # + # TODO RAISE EXCEPTION + # + it "changes @user's attributes" do + login(member.user) + + put 'update', id: user.id, user: Fabricate.to_params(:user, username: user.username, email: user.email, phone:'1234', alt_phone: "4321") + + user.reload + expect(user.phone).not_to eq("1234") + expect(user.alt_phone).not_to eq("4321") + end + end + + context "admin user" do + it "located the requested @user" do + login(member_admin.user) + + put 'update', id: user.id, user: Fabricate.to_params(:user) + expect(assigns(:user)).to eq(user) + end + + it "changes @user's attributes" do + login(member_admin.user) + + put 'update', id: user.id, user: Fabricate.to_params(:user, username: user.username, email: user.email, phone:'1234', alt_phone: "4321") + + user.reload + expect(user.phone).to eq("1234") + expect(user.alt_phone).to eq("4321") + end + end + end + end + + context "with invalid params" do + context "with a logged admin user" do + it "does not change @user's attributes" do + login(member_admin.user) + + put :update, id: user.id, user: Fabricate.to_params(:user, username: nil, email: nil, phone: "1234", alt_phone: "4321") + + expect(user.phone).not_to eq("1234") + expect(user.alt_phone).not_to eq("4321") + end + end + end + end + +end diff --git a/spec/helpers/global_helper_spec.rb b/spec/helpers/global_helper_spec.rb new file mode 100644 index 0000000..f00766a --- /dev/null +++ b/spec/helpers/global_helper_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +# Specs in this file have access to a helper object that includes +# the GlobalHelper. For example: +# +# describe GlobalHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +describe GlobalHelper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f1df85c..7cc4091 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -3,6 +3,7 @@ ENV["RAILS_ENV"] ||= 'test' require File.expand_path("../../config/environment", __FILE__) require 'rspec/rails' require 'rspec/autorun' +require 'capybara/rails' # Requires supporting ruby files with custom matchers and macros, etc, # in spec/support/ and its subdirectories. @@ -68,3 +69,9 @@ RSpec.configure do |config| # Controllers must render the content of the view config.render_views end + +RSpec.shared_context 'stub browser locale' do + def set_browser_locale(locale) + ApplicationController.any_instance.stub(:extract_locale_from_accept_language_header).and_return(locale) + end +end diff --git a/spec/views/global/switch_lang.html.haml_spec.rb b/spec/views/global/switch_lang.html.haml_spec.rb new file mode 100644 index 0000000..46d3bc2 --- /dev/null +++ b/spec/views/global/switch_lang.html.haml_spec.rb @@ -0,0 +1,5 @@ +require 'spec_helper' + +describe "global/switch_lang.html.haml" do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/vendor/assets/images/blank.png b/vendor/assets/images/blank.png new file mode 100644 index 0000000..86f57a5 Binary files /dev/null and b/vendor/assets/images/blank.png differ diff --git a/vendor/assets/images/flags.png b/vendor/assets/images/flags.png new file mode 100644 index 0000000..da2bff7 Binary files /dev/null and b/vendor/assets/images/flags.png differ diff --git a/vendor/assets/stylesheets/flags.css b/vendor/assets/stylesheets/flags.css new file mode 100644 index 0000000..3f4dd1b --- /dev/null +++ b/vendor/assets/stylesheets/flags.css @@ -0,0 +1,257 @@ +.flag { + width: 16px; + height: 11px; + background:url('/assets/flags.png') no-repeat +} + +.flag.flag-ad {background-position: -16px 0} +.flag.flag-ae {background-position: -32px 0} +.flag.flag-af {background-position: -48px 0} +.flag.flag-ag {background-position: -64px 0} +.flag.flag-ai {background-position: -80px 0} +.flag.flag-al {background-position: -96px 0} +.flag.flag-am {background-position: -112px 0} +.flag.flag-an {background-position: -128px 0} +.flag.flag-ao {background-position: -144px 0} +.flag.flag-ar {background-position: -160px 0} +.flag.flag-as {background-position: -176px 0} +.flag.flag-at {background-position: -192px 0} +.flag.flag-au {background-position: -208px 0} +.flag.flag-aw {background-position: -224px 0} +.flag.flag-az {background-position: -240px 0} +.flag.flag-ba {background-position: 0 -11px} +.flag.flag-bb {background-position: -16px -11px} +.flag.flag-bd {background-position: -32px -11px} +.flag.flag-be {background-position: -48px -11px} +.flag.flag-bf {background-position: -64px -11px} +.flag.flag-bg {background-position: -80px -11px} +.flag.flag-bh {background-position: -96px -11px} +.flag.flag-bi {background-position: -112px -11px} +.flag.flag-bj {background-position: -128px -11px} +.flag.flag-bm {background-position: -144px -11px} +.flag.flag-bn {background-position: -160px -11px} +.flag.flag-bo {background-position: -176px -11px} +.flag.flag-br {background-position: -192px -11px} +.flag.flag-bs {background-position: -208px -11px} +.flag.flag-bt {background-position: -224px -11px} +.flag.flag-bv {background-position: -240px -11px} +.flag.flag-bw {background-position: 0 -22px} +.flag.flag-by {background-position: -16px -22px} +.flag.flag-bz {background-position: -32px -22px} +.flag.flag-ca {background-position: -48px -22px} +.flag.flag-catalonia {background-position: -64px -22px} +.flag.flag-cd {background-position: -80px -22px} +.flag.flag-cf {background-position: -96px -22px} +.flag.flag-cg {background-position: -112px -22px} +.flag.flag-ch {background-position: -128px -22px} +.flag.flag-ci {background-position: -144px -22px} +.flag.flag-ck {background-position: -160px -22px} +.flag.flag-cl {background-position: -176px -22px} +.flag.flag-cm {background-position: -192px -22px} +.flag.flag-cn {background-position: -208px -22px} +.flag.flag-co {background-position: -224px -22px} +.flag.flag-cr {background-position: -240px -22px} +.flag.flag-cu {background-position: 0 -33px} +.flag.flag-cv {background-position: -16px -33px} +.flag.flag-cw {background-position: -32px -33px} +.flag.flag-cy {background-position: -48px -33px} +.flag.flag-cz {background-position: -64px -33px} +.flag.flag-de {background-position: -80px -33px} +.flag.flag-dj {background-position: -96px -33px} +.flag.flag-dk {background-position: -112px -33px} +.flag.flag-dm {background-position: -128px -33px} +.flag.flag-do {background-position: -144px -33px} +.flag.flag-dz {background-position: -160px -33px} +.flag.flag-ec {background-position: -176px -33px} +.flag.flag-ee {background-position: -192px -33px} +.flag.flag-eg {background-position: -208px -33px} +.flag.flag-eh {background-position: -224px -33px} +.flag.flag-england {background-position: -240px -33px} +.flag.flag-er {background-position: 0 -44px} +.flag.flag-es {background-position: -16px -44px} +.flag.flag-et {background-position: -32px -44px} +.flag.flag-eu {background-position: -48px -44px} +.flag.flag-fi {background-position: -64px -44px} +.flag.flag-fj {background-position: -80px -44px} +.flag.flag-fk {background-position: -96px -44px} +.flag.flag-fm {background-position: -112px -44px} +.flag.flag-fo {background-position: -128px -44px} +.flag.flag-fr {background-position: -144px -44px} +.flag.flag-ga {background-position: -160px -44px} +.flag.flag-gb {background-position: -176px -44px} +.flag.flag-gd {background-position: -192px -44px} +.flag.flag-ge {background-position: -208px -44px} +.flag.flag-gf {background-position: -224px -44px} +.flag.flag-gg {background-position: -240px -44px} +.flag.flag-gh {background-position: 0 -55px} +.flag.flag-gi {background-position: -16px -55px} +.flag.flag-gl {background-position: -32px -55px} +.flag.flag-gm {background-position: -48px -55px} +.flag.flag-gn {background-position: -64px -55px} +.flag.flag-gp {background-position: -80px -55px} +.flag.flag-gq {background-position: -96px -55px} +.flag.flag-gr {background-position: -112px -55px} +.flag.flag-gs {background-position: -128px -55px} +.flag.flag-gt {background-position: -144px -55px} +.flag.flag-gu {background-position: -160px -55px} +.flag.flag-gw {background-position: -176px -55px} +.flag.flag-gy {background-position: -192px -55px} +.flag.flag-hk {background-position: -208px -55px} +.flag.flag-hm {background-position: -224px -55px} +.flag.flag-hn {background-position: -240px -55px} +.flag.flag-hr {background-position: 0 -66px} +.flag.flag-ht {background-position: -16px -66px} +.flag.flag-hu {background-position: -32px -66px} +.flag.flag-ic {background-position: -48px -66px} +.flag.flag-id {background-position: -64px -66px} +.flag.flag-ie {background-position: -80px -66px} +.flag.flag-il {background-position: -96px -66px} +.flag.flag-im {background-position: -112px -66px} +.flag.flag-in {background-position: -128px -66px} +.flag.flag-io {background-position: -144px -66px} +.flag.flag-iq {background-position: -160px -66px} +.flag.flag-ir {background-position: -176px -66px} +.flag.flag-is {background-position: -192px -66px} +.flag.flag-it {background-position: -208px -66px} +.flag.flag-je {background-position: -224px -66px} +.flag.flag-jm {background-position: -240px -66px} +.flag.flag-jo {background-position: 0 -77px} +.flag.flag-jp {background-position: -16px -77px} +.flag.flag-ke {background-position: -32px -77px} +.flag.flag-kg {background-position: -48px -77px} +.flag.flag-kh {background-position: -64px -77px} +.flag.flag-ki {background-position: -80px -77px} +.flag.flag-km {background-position: -96px -77px} +.flag.flag-kn {background-position: -112px -77px} +.flag.flag-kp {background-position: -128px -77px} +.flag.flag-kr {background-position: -144px -77px} +.flag.flag-kurdistan {background-position: -160px -77px} +.flag.flag-kw {background-position: -176px -77px} +.flag.flag-ky {background-position: -192px -77px} +.flag.flag-kz {background-position: -208px -77px} +.flag.flag-la {background-position: -224px -77px} +.flag.flag-lb {background-position: -240px -77px} +.flag.flag-lc {background-position: 0 -88px} +.flag.flag-li {background-position: -16px -88px} +.flag.flag-lk {background-position: -32px -88px} +.flag.flag-lr {background-position: -48px -88px} +.flag.flag-ls {background-position: -64px -88px} +.flag.flag-lt {background-position: -80px -88px} +.flag.flag-lu {background-position: -96px -88px} +.flag.flag-lv {background-position: -112px -88px} +.flag.flag-ly {background-position: -128px -88px} +.flag.flag-ma {background-position: -144px -88px} +.flag.flag-mc {background-position: -160px -88px} +.flag.flag-md {background-position: -176px -88px} +.flag.flag-me {background-position: -192px -88px} +.flag.flag-mg {background-position: -208px -88px} +.flag.flag-mh {background-position: -224px -88px} +.flag.flag-mk {background-position: -240px -88px} +.flag.flag-ml {background-position: 0 -99px} +.flag.flag-mm {background-position: -16px -99px} +.flag.flag-mn {background-position: -32px -99px} +.flag.flag-mo {background-position: -48px -99px} +.flag.flag-mp {background-position: -64px -99px} +.flag.flag-mq {background-position: -80px -99px} +.flag.flag-mr {background-position: -96px -99px} +.flag.flag-ms {background-position: -112px -99px} +.flag.flag-mt {background-position: -128px -99px} +.flag.flag-mu {background-position: -144px -99px} +.flag.flag-mv {background-position: -160px -99px} +.flag.flag-mw {background-position: -176px -99px} +.flag.flag-mx {background-position: -192px -99px} +.flag.flag-my {background-position: -208px -99px} +.flag.flag-mz {background-position: -224px -99px} +.flag.flag-na {background-position: -240px -99px} +.flag.flag-nc {background-position: 0 -110px} +.flag.flag-ne {background-position: -16px -110px} +.flag.flag-nf {background-position: -32px -110px} +.flag.flag-ng {background-position: -48px -110px} +.flag.flag-ni {background-position: -64px -110px} +.flag.flag-nl {background-position: -80px -110px} +.flag.flag-no {background-position: -96px -110px} +.flag.flag-np {background-position: -112px -110px} +.flag.flag-nr {background-position: -128px -110px} +.flag.flag-nu {background-position: -144px -110px} +.flag.flag-nz {background-position: -160px -110px} +.flag.flag-om {background-position: -176px -110px} +.flag.flag-pa {background-position: -192px -110px} +.flag.flag-pe {background-position: -208px -110px} +.flag.flag-pf {background-position: -224px -110px} +.flag.flag-pg {background-position: -240px -110px} +.flag.flag-ph {background-position: 0 -121px} +.flag.flag-pk {background-position: -16px -121px} +.flag.flag-pl {background-position: -32px -121px} +.flag.flag-pm {background-position: -48px -121px} +.flag.flag-pn {background-position: -64px -121px} +.flag.flag-pr {background-position: -80px -121px} +.flag.flag-ps {background-position: -96px -121px} +.flag.flag-pt {background-position: -112px -121px} +.flag.flag-pw {background-position: -128px -121px} +.flag.flag-py {background-position: -144px -121px} +.flag.flag-qa {background-position: -160px -121px} +.flag.flag-re {background-position: -176px -121px} +.flag.flag-ro {background-position: -192px -121px} +.flag.flag-rs {background-position: -208px -121px} +.flag.flag-ru {background-position: -224px -121px} +.flag.flag-rw {background-position: -240px -121px} +.flag.flag-sa {background-position: 0 -132px} +.flag.flag-sb {background-position: -16px -132px} +.flag.flag-sc {background-position: -32px -132px} +.flag.flag-scotland {background-position: -48px -132px} +.flag.flag-sd {background-position: -64px -132px} +.flag.flag-se {background-position: -80px -132px} +.flag.flag-sg {background-position: -96px -132px} +.flag.flag-sh {background-position: -112px -132px} +.flag.flag-si {background-position: -128px -132px} +.flag.flag-sk {background-position: -144px -132px} +.flag.flag-sl {background-position: -160px -132px} +.flag.flag-sm {background-position: -176px -132px} +.flag.flag-sn {background-position: -192px -132px} +.flag.flag-so {background-position: -208px -132px} +.flag.flag-somaliland {background-position: -224px -132px} +.flag.flag-sr {background-position: -240px -132px} +.flag.flag-ss {background-position: 0 -143px} +.flag.flag-st {background-position: -16px -143px} +.flag.flag-sv {background-position: -32px -143px} +.flag.flag-sx {background-position: -48px -143px} +.flag.flag-sy {background-position: -64px -143px} +.flag.flag-sz {background-position: -80px -143px} +.flag.flag-tc {background-position: -96px -143px} +.flag.flag-td {background-position: -112px -143px} +.flag.flag-tf {background-position: -128px -143px} +.flag.flag-tg {background-position: -144px -143px} +.flag.flag-th {background-position: -160px -143px} +.flag.flag-tj {background-position: -176px -143px} +.flag.flag-tk {background-position: -192px -143px} +.flag.flag-tl {background-position: -208px -143px} +.flag.flag-tm {background-position: -224px -143px} +.flag.flag-tn {background-position: -240px -143px} +.flag.flag-to {background-position: 0 -154px} +.flag.flag-tr {background-position: -16px -154px} +.flag.flag-tt {background-position: -32px -154px} +.flag.flag-tv {background-position: -48px -154px} +.flag.flag-tw {background-position: -64px -154px} +.flag.flag-tz {background-position: -80px -154px} +.flag.flag-ua {background-position: -96px -154px} +.flag.flag-ug {background-position: -112px -154px} +.flag.flag-um {background-position: -128px -154px} +.flag.flag-us {background-position: -144px -154px} +.flag.flag-uy {background-position: -160px -154px} +.flag.flag-uz {background-position: -176px -154px} +.flag.flag-va {background-position: -192px -154px} +.flag.flag-vc {background-position: -208px -154px} +.flag.flag-ve {background-position: -224px -154px} +.flag.flag-vg {background-position: -240px -154px} +.flag.flag-vi {background-position: 0 -165px} +.flag.flag-vn {background-position: -16px -165px} +.flag.flag-vu {background-position: -32px -165px} +.flag.flag-wales {background-position: -48px -165px} +.flag.flag-wf {background-position: -64px -165px} +.flag.flag-ws {background-position: -80px -165px} +.flag.flag-ye {background-position: -96px -165px} +.flag.flag-yt {background-position: -112px -165px} +.flag.flag-za {background-position: -128px -165px} +.flag.flag-zanzibar {background-position: -144px -165px} +.flag.flag-zm {background-position: -160px -165px} +.flag.flag-zw {background-position: -176px -165px}