diff --git a/Gemfile b/Gemfile index 3b1d0ea4..01bd5af1 100644 --- a/Gemfile +++ b/Gemfile @@ -80,6 +80,9 @@ gem 'cocoon' # as the JavaScript library gem 'jquery-rails' +# for languages validation +gem 'iso-639' + # frontend javascripts source 'https://rails-assets.org' do # for placeholder images diff --git a/Gemfile.lock b/Gemfile.lock index ef102920..3de1b95d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -219,6 +219,7 @@ GEM i18n_data (0.7.0) inversion (0.12.3) loggability (~> 0.4) + iso-639 (0.2.5) jquery-datatables-rails (2.2.3) jquery-rails sass-rails @@ -560,6 +561,7 @@ DEPENDENCIES guard-rspec (~> 4.2.8) haml-rails hoptoad_notifier (~> 2.3) + iso-639 jquery-datatables-rails (~> 2.2.1) jquery-rails leaflet-rails @@ -615,6 +617,3 @@ DEPENDENCIES web-console (~> 2.0) webmock whenever - -BUNDLED WITH - 1.11.2 diff --git a/app/controllers/admin/programs_controller.rb b/app/controllers/admin/programs_controller.rb index 9727f82b..ba66809f 100644 --- a/app/controllers/admin/programs_controller.rb +++ b/app/controllers/admin/programs_controller.rb @@ -26,7 +26,7 @@ module Admin private def program_params - params.require(:program).permit(:rating, :schedule_public, :schedule_fluid) + params.require(:program).permit(:rating, :schedule_public, :schedule_fluid, :languages) end end end diff --git a/app/controllers/proposal_controller.rb b/app/controllers/proposal_controller.rb index 4efe8555..b9bf3b10 100644 --- a/app/controllers/proposal_controller.rb +++ b/app/controllers/proposal_controller.rb @@ -21,10 +21,12 @@ class ProposalController < ApplicationController authorize! :new, @event @user = User.new @url = conference_program_proposal_index_path(@conference.short_title) + @languages = @program.languages_list end def edit @url = conference_program_proposal_path(@conference.short_title, params[:id]) + @languages = @program.languages_list end def create @@ -152,7 +154,7 @@ class ProposalController < ApplicationController def event_params params.require(:event).permit(:event_type_id, :track_id, :difficulty_level_id, :title, :subtitle, :abstract, :description, - :require_registration, :max_attendees) + :require_registration, :max_attendees, :language) end def user_params diff --git a/app/models/program.rb b/app/models/program.rb index e336be21..971f5c55 100644 --- a/app/models/program.rb +++ b/app/models/program.rb @@ -52,6 +52,7 @@ class Program < ActiveRecord::Base before_create :create_event_types before_create :create_difficulty_levels + validate :check_languages_format ## # Checcks if the program has rating enabled @@ -83,6 +84,10 @@ class Program < ActiveRecord::Base (!conference.email_settings.program_schedule_public_subject.blank? && !conference.email_settings.program_schedule_public_body.blank?) end + def languages_list + self.languages.split(',').map {|l| ISO_639.find(l).english_name} if self.languages.present? + end + private ## @@ -113,4 +118,20 @@ class Program < ActiveRecord::Base color: '#EF6E69') true end + + ## + # Check if languages string has the right format. Used as validation. + # + def check_languages_format + return unless self.languages.present? + # All white spaces are removed to allow languages to be separated by ',' and ', '. The languages string without spaces is saved + self.languages = self.languages.delete(' ').downcase + errors.add(:languages, 'must be two letters separated by commas') && return unless + self.languages.match(/^$|(\A[a-z][a-z](,[a-z][a-z])*\z)/).present? + languages_array = self.languages.split(',') + # We check that languages are not repeated + errors.add(:languages, "can't be repeated") && return unless languages_array.uniq!.nil? + # We check if every language is a valid ISO 639-1 language + errors.add(:languages, 'must be ISO 639-1 valid codes') unless languages_array.select{ |x| ISO_639.find(x).nil? }.empty? + end end diff --git a/app/views/admin/events/_proposal.html.haml b/app/views/admin/events/_proposal.html.haml index 7d7955c5..4d8ed950 100644 --- a/app/views/admin/events/_proposal.html.haml +++ b/app/views/admin/events/_proposal.html.haml @@ -98,6 +98,14 @@ off_text: 'No' } - if @event.require_registration = registered_text(@event) + + -if @program.languages.present? + %tr + %td + %b Language + %td + = @event.language + - if !@event.room.nil? %tr %td diff --git a/app/views/admin/events/index.html.haml b/app/views/admin/events/index.html.haml index e43fbfe0..f5aeea9c 100644 --- a/app/views/admin/events/index.html.haml +++ b/app/views/admin/events/index.html.haml @@ -22,6 +22,9 @@ %b Submitter %th %b Speaker + -if @program.languages.present? + %th + %b Language %th %b Requires Registration %th @@ -83,6 +86,10 @@ - else Unknown speaker + -if @program.languages.present? + %td + = event.language + %td.text-center{'data-order' => "#{event.require_registration}"} = check_box_tag @conference.short_title, event.id, event.require_registration, method: :patch, url: "/admin/conference/#{@conference.short_title}/program/events/#{event.id}?event[require_registration]=", diff --git a/app/views/admin/programs/_form.html.haml b/app/views/admin/programs/_form.html.haml index 13b1ce72..e50c8e56 100644 --- a/app/views/admin/programs/_form.html.haml +++ b/app/views/admin/programs/_form.html.haml @@ -4,9 +4,10 @@ %h1 Program .row .col-md-8 - = semantic_form_for(@program, :url => admin_conference_program_path(@conference.short_title),:html => {:multipart => true}) do |f| + = semantic_form_for(@program, url: admin_conference_program_path(@conference.short_title), html: {multipart: true}) do |f| = f.input :schedule_public, label: "Show Schedule on the home and splash page" = f.input :schedule_fluid, label: "Allow submitters to change their event after it is scheduled" - = f.input :rating, :hint => "Enter the number of different rating levels you want to have for voting on proposals. Enter 0 if you do not want to vote on proposals." + = f.input :rating, hint: 'Enter the number of different rating levels you want to have for voting on proposals. Enter 0 if you do not want to vote on proposals.' + = f.input :languages, hint: "Enter the languages allowed for events as values of #{link_to('ISO 639-1', 'http://www.loc.gov/standards/iso639-2/php/code_list.php', target: "_blank")} language codes separated with commas. The first language would be the default language. Leave it blank if you do not want to specify languages.".html_safe %p.text-right - = f.action :submit, :as => :button, :button_html => {:class => "btn btn-primary"} + = f.action :submit, as: :button, button_html: {class: 'btn btn-primary'} diff --git a/app/views/admin/programs/show.html.haml b/app/views/admin/programs/show.html.haml index c73f5f9b..d00d98ca 100644 --- a/app/views/admin/programs/show.html.haml +++ b/app/views/admin/programs/show.html.haml @@ -31,6 +31,10 @@ Difficulty Levels: %dd = difficulty_levels(@conference) + %dt + Languages: + %dd + = @program.languages %dt Public Schedule %dd#schedule_public diff --git a/app/views/proposal/_proposal_form.html.haml b/app/views/proposal/_proposal_form.html.haml index c7f0bb16..4df88438 100644 --- a/app/views/proposal/_proposal_form.html.haml +++ b/app/views/proposal/_proposal_form.html.haml @@ -14,6 +14,11 @@ data: { min_words: type.minimum_abstract_length, max_words: type.maximum_abstract_length }]}, include_blank: false, label: 'Type', input_html: { class: 'select-help-toggle' } + - if @program.languages.present? + = f.input :language, as: :select, + collection: @languages, + include_blank: false, label: 'Language', input_html: { class: 'select-help-toggle' } + - @conference.program.event_types.each do |event_type| %span{ class: 'help-block select-help-text event_event_type_id collapse', id: "#{event_type.id}-help" } = event_type.description diff --git a/app/views/proposal/new.html.haml b/app/views/proposal/new.html.haml index 37e415f7..03d246de 100644 --- a/app/views/proposal/new.html.haml +++ b/app/views/proposal/new.html.haml @@ -31,6 +31,11 @@ data: { min_words: type.minimum_abstract_length, max_words: type.maximum_abstract_length }]}, include_blank: false, label: 'Type', input_html: { class: 'select-help-toggle' } + - if @program.languages.present? + = f.input :language, as: :select, + collection: @languages, + include_blank: false, label: 'Language', input_html: { class: 'select-help-toggle' } + - @program.event_types.each do |event_type| %span{ class: 'help-block event_event_type_id collapse', id: "#{event_type.id}-help" } = event_type.description diff --git a/db/migrate/20160320220630_add_languages_to_program.rb b/db/migrate/20160320220630_add_languages_to_program.rb new file mode 100644 index 00000000..b49c01d1 --- /dev/null +++ b/db/migrate/20160320220630_add_languages_to_program.rb @@ -0,0 +1,5 @@ +class AddLanguagesToProgram < ActiveRecord::Migration + def change + add_column :programs, :languages, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 00e93609..2d48c4d5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -266,6 +266,7 @@ ActiveRecord::Schema.define(version: 20160427104236) do t.boolean "schedule_fluid", default: false t.datetime "created_at" t.datetime "updated_at" + t.string "languages" end create_table "qanswers", force: :cascade do |t| diff --git a/spec/models/program_spec.rb b/spec/models/program_spec.rb index 3adad84e..a743791e 100644 --- a/spec/models/program_spec.rb +++ b/spec/models/program_spec.rb @@ -58,4 +58,37 @@ describe Program do expect(conference.program.difficulty_levels.count).to eq 3 end end + + describe 'languages' do + it "is not valid if languages aren't two letters separated by commas" do + program.languages = 'eng, De es' + expect(program.valid?).to eq false + expect(program.errors[:languages]).to eq ['must be two letters separated by commas'] + end + + it 'is not valid if languages are repeated' do + program.languages = 'en,de,es,en' + expect(program.valid?).to eq false + expect(program.errors[:languages]).to eq ["can't be repeated"] + end + + it "is not valid if languages aren't ISO 639-1 valid codes" do + program.languages = 'en,hh,yu,zi,oo' + expect(program.valid?).to eq false + expect(program.errors[:languages]).to eq ['must be ISO 639-1 valid codes'] + end + + it 'is valid otherwise' do + program.languages = 'en,De, ES, ru,el' + expect(program.valid?).to eq true + end + end + + describe '#languages_list' do + it 'returns the list of readable languages' do + program.languages = 'en,de,fr,ru,zh' + expect(program.languages_list).to eq %w(English German French Russian Chinese) + end + end + end diff --git a/spec/views/admin/events/index.html.haml_spec.rb b/spec/views/admin/events/index.html.haml_spec.rb index 353e35a8..5a252862 100644 --- a/spec/views/admin/events/index.html.haml_spec.rb +++ b/spec/views/admin/events/index.html.haml_spec.rb @@ -3,12 +3,13 @@ require 'spec_helper' describe 'admin/events/index' do let!(:conference) { create(:conference) } let!(:program) { conference.program } - let!(:event1) { create(:event, program: conference.program, title: 'event1') } - let!(:event2) { create(:event, program: conference.program, title: 'event2') } + let!(:event1) { create(:event, program: conference.program, title: 'event1', language: 'English') } + let!(:event2) { create(:event, program: conference.program, title: 'event2', language: 'German') } it 'renders all conference events' do assign(:conference, conference) assign(:program, conference.program) + program.languages = 'en,de' assign(:events, [ event1, event2 ]) assign(:event_types, [ create(:event_type, program: conference.program), create(:event_type, program: conference.program) ]) assign(:tracks, [ create(:track, program: conference.program), create(:track, program: conference.program) ]) @@ -20,18 +21,21 @@ describe 'admin/events/index' do expect(rendered).to have_selector('table thead th:nth-of-type(2)', text: 'Title') expect(rendered).to have_selector('table thead th:nth-of-type(3)', text: 'Submitter') expect(rendered).to have_selector('table thead th:nth-of-type(4)', text: 'Speaker') - expect(rendered).to have_selector('table thead th:nth-of-type(5)', text: 'Requires Registration') - expect(rendered).to have_selector('table thead th:nth-of-type(6)', text: 'Highlight') - expect(rendered).to have_selector('table thead th:nth-of-type(7)', text: 'Type') - expect(rendered).to have_selector('table thead th:nth-of-type(8)', text: 'Track') - expect(rendered).to have_selector('table thead th:nth-of-type(9)', text: 'Difficulty') - expect(rendered).to have_selector('table thead th:nth-of-type(10)', text: 'State') + expect(rendered).to have_selector('table thead th:nth-of-type(5)', text: 'Language') + expect(rendered).to have_selector('table thead th:nth-of-type(6)', text: 'Requires Registration') + expect(rendered).to have_selector('table thead th:nth-of-type(7)', text: 'Highlight') + expect(rendered).to have_selector('table thead th:nth-of-type(8)', text: 'Type') + expect(rendered).to have_selector('table thead th:nth-of-type(9)', text: 'Track') + expect(rendered).to have_selector('table thead th:nth-of-type(10)', text: 'Difficulty') + expect(rendered).to have_selector('table thead th:nth-of-type(11)', text: 'State') expect(conference.program.events.count).to eq 2 expect(rendered).to have_selector('table tr:nth-of-type(1) td:nth-of-type(1)', text: event1.id) expect(rendered).to have_selector('table tr:nth-of-type(1) td:nth-of-type(2)', text: 'event1') + expect(rendered).to have_selector('table tr:nth-of-type(1) td:nth-of-type(5)', text: 'English') expect(rendered).to have_selector('table tr:nth-of-type(2) td:nth-of-type(1)', text: event2.id) expect(rendered).to have_selector('table tr:nth-of-type(2) td:nth-of-type(2)', text: 'event2') + expect(rendered).to have_selector('table tr:nth-of-type(2) td:nth-of-type(5)', text: 'German') end end diff --git a/spec/views/admin/programs/show.html.haml_spec.rb b/spec/views/admin/programs/show.html.haml_spec.rb index 807d8c34..ba4066d5 100644 --- a/spec/views/admin/programs/show.html.haml_spec.rb +++ b/spec/views/admin/programs/show.html.haml_spec.rb @@ -12,5 +12,6 @@ describe 'admin/programs/show' do expect(rendered).to have_css('dt', text: 'Tracks:') expect(rendered).to have_css('dt', text: 'Difficulty Levels:') expect(rendered).to have_css('dd', text: 'Easy, Medium and Hard') + expect(rendered).to have_css('dt', text: 'Languages:') end end