diff --git a/app/controllers/admin/poll/questions/answers_controller.rb b/app/controllers/admin/poll/questions/answers_controller.rb new file mode 100644 index 000000000..d0a7c4cc7 --- /dev/null +++ b/app/controllers/admin/poll/questions/answers_controller.rb @@ -0,0 +1,30 @@ +class Admin::Poll::Questions::AnswersController < Admin::Poll::BaseController + before_action :load_question + + load_and_authorize_resource :question, class: "::Poll::Question" + + def new + @answer = ::Poll::Question::Answer.new + end + + def create + @answer = ::Poll::Question::Answer.new(answer_params) + + if @answer.save + redirect_to admin_question_path(@question), + notice: t("flash.actions.create.poll_question_answer") + else + render :new + end + end + + private + + def answer_params + params.require(:poll_question_answer).permit(:title, :description, :poll_question_id) + end + + def load_question + @question = ::Poll::Question.find(params[:question_id]) + end +end diff --git a/app/controllers/polls/questions_controller.rb b/app/controllers/polls/questions_controller.rb index 74266a6fc..490b32fbe 100644 --- a/app/controllers/polls/questions_controller.rb +++ b/app/controllers/polls/questions_controller.rb @@ -12,7 +12,7 @@ class Polls::QuestionsController < ApplicationController answer.save! answer.record_voter_participation - @answers_by_question_id = {@question.id => params[:answer]} + @answers_by_question_id = { @question.id => params[:answer] } end end diff --git a/app/models/poll/answer.rb b/app/models/poll/answer.rb index 01eebd553..2378de6ba 100644 --- a/app/models/poll/answer.rb +++ b/app/models/poll/answer.rb @@ -8,8 +8,10 @@ class Poll::Answer < ActiveRecord::Base validates :question, presence: true validates :author, presence: true validates :answer, presence: true - validates :answer, inclusion: { in: ->(a) { a.question.valid_answers }}, - unless: ->(a) { a.question.blank? } + + # temporary skipping validation, review when removing valid_answers + # validates :answer, inclusion: { in: ->(a) { a.question.valid_answers }}, + # unless: ->(a) { a.question.blank? } scope :by_author, ->(author_id) { where(author_id: author_id) } scope :by_question, ->(question_id) { where(question_id: question_id) } diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb index 55396fd3b..1e94e0180 100644 --- a/app/models/poll/question.rb +++ b/app/models/poll/question.rb @@ -14,7 +14,8 @@ class Poll::Question < ActiveRecord::Base belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id' has_many :comments, as: :commentable - has_many :answers + has_many :answers, class_name: 'Poll::Answer' + has_many :question_answers, class_name: 'Poll::Question::Answer' has_many :partial_results belongs_to :proposal diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb new file mode 100644 index 000000000..3f8f4a172 --- /dev/null +++ b/app/models/poll/question/answer.rb @@ -0,0 +1,9 @@ +class Poll::Question::Answer < ActiveRecord::Base + belongs_to :question, class_name: 'Poll::Question', foreign_key: 'question_id' + + validates :title, presence: true + + def description + super.try :html_safe + end +end diff --git a/app/views/admin/poll/questions/_form.html.erb b/app/views/admin/poll/questions/_form.html.erb index 6091c024f..56b7ec93f 100644 --- a/app/views/admin/poll/questions/_form.html.erb +++ b/app/views/admin/poll/questions/_form.html.erb @@ -16,10 +16,6 @@ <%= f.text_field :title, maxlength: Poll::Question.title_max_length %> - <%= f.label :valid_answers %> -

<%= t("admin.questions.new.valid_answers_note") %>

- <%= f.text_field :valid_answers, label: false, aria: {describedby: "valid-answers-help-text"} %> -
<%= render 'documents/nested_documents', documentable: @question, f: f %>
diff --git a/app/views/admin/poll/questions/answers/_form.html.erb b/app/views/admin/poll/questions/answers/_form.html.erb new file mode 100644 index 000000000..7c6ce8673 --- /dev/null +++ b/app/views/admin/poll/questions/answers/_form.html.erb @@ -0,0 +1,24 @@ +<%= form_for(@answer, url: form_url) do |f| %> + + <%= render 'shared/errors', resource: @answer %> + + <%= f.hidden_field :question_id, value: @question.id %> + +
+
+ <%= f.text_field :title %> + +
+ <%= f.cktext_area :description, + maxlength: Poll::Question.description_max_length, + ckeditor: { language: I18n.locale } %> +
+ +
+
+ <%= f.submit(class: "button expanded", value: t("shared.save")) %> +
+
+
+
+<% end %> diff --git a/app/views/admin/poll/questions/answers/new.html.erb b/app/views/admin/poll/questions/answers/new.html.erb new file mode 100644 index 000000000..b4d02cfeb --- /dev/null +++ b/app/views/admin/poll/questions/answers/new.html.erb @@ -0,0 +1,7 @@ +<%= back_link_to %> + +

<%= t('admin.answers.new.title') %>

+ +
+ <%= render "form", form_url: admin_question_answers_path %> +
diff --git a/app/views/admin/poll/questions/show.html.erb b/app/views/admin/poll/questions/show.html.erb index 438ee3a40..7037afd5f 100644 --- a/app/views/admin/poll/questions/show.html.erb +++ b/app/views/admin/poll/questions/show.html.erb @@ -24,15 +24,30 @@ <%= link_to @question.author.name, user_path(@question.author) %>

-

- <%= t("admin.questions.show.valid_answers") %> -

+ + + + + - <% @question.valid_answers.each do |answer| %> - - <%= answer %> - - <% end %> + + + + + + <% @question.question_answers.each do |answer| %> + + + + + <% end %> +
+ <%= t('admin.questions.show.valid_answers') %> + + <%= link_to t("admin.questions.show.add_answer"), + new_admin_question_answer_path(@question), + class: "button hollow float-right" %> +
<%= t("admin.questions.show.answers.title") %><%= t("admin.questions.show.answers.description") %>
<%= answer.title %><%= answer.description %>
<% if @question.video_url.present? %>

diff --git a/app/views/polls/questions/_answers.html.erb b/app/views/polls/questions/_answers.html.erb index c88156b68..2bef6de72 100644 --- a/app/views/polls/questions/_answers.html.erb +++ b/app/views/polls/questions/_answers.html.erb @@ -1,22 +1,23 @@

<% if can? :answer, question %> - <% question.valid_answers.each do |answer| %> - <% if @answers_by_question_id[question.id] == answer %> - "> - <%= answer %> + <% question.question_answers.each do |answer| %> + <% if @answers_by_question_id[question.id] == answer.title %> + "> + <%= answer.title %> <% else %> - <%= link_to answer, - answer_question_path(question, answer: answer), + <%= link_to answer.title, + answer_question_path(question, answer: answer.title), method: :post, remote: true, - title: t("poll_questions.show.vote_answer", answer: answer), + title: t("poll_questions.show.vote_answer", answer: answer.title), class: "button secondary hollow" %> <% end %> <% end %> <% else %> - <% question.valid_answers.each do |answer| %> - <%= answer %> + <% question.question_answers.each do |answer| %> + <%= answer.title %> <% end %> <% end %>
diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index 8bb2c3286..5ef1917d3 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -213,6 +213,9 @@ en: image: title: Title attachment: Attachment + poll/question_answer: + title: "Answer" + description: "Description" errors: models: user: diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index eadcae62d..8202695f1 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -587,14 +587,20 @@ en: new: title: "Create Question" poll_label: "Poll" - valid_answers_note: "Enter the answers separated by commas (,)" show: proposal: Original proposal author: Author title: Title valid_answers: Valid answers + add_answer: "Add answer" video_url: External video documents: Documents (1) + answers: + title: Answer + description: Description + answers: + new: + title: "New answer" recounts: index: title: "Recounts" diff --git a/config/locales/en/responders.yml b/config/locales/en/responders.yml index 203d5700e..fd187c495 100644 --- a/config/locales/en/responders.yml +++ b/config/locales/en/responders.yml @@ -8,6 +8,7 @@ en: direct_message: "You message has been sent successfully." poll: "Poll created successfully." poll_booth: "Booth created successfully." + poll_question_answer: "Answer created successfully" proposal: "Proposal created successfully." proposal_notification: "Your message has been sent correctly." spending_proposal: "Spending proposal created successfully. You can access it from %{activity}" diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index 415e81bed..9b3260680 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -207,6 +207,9 @@ es: image: title: Título attachment: Archivo adjunto + poll/question_answer: + title: "Respuesta" + description: "Descripción" errors: models: user: diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index fc3945ddf..3ff3abcb5 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -587,12 +587,22 @@ es: new: title: "Crear pregunta ciudadana" poll_label: "Votación" - valid_answers_note: "Escribe las respuestas separadas por comas (,)" show: proposal: Propuesta ciudadana original author: Autor title: Título valid_answers: Respuestas válidas + add_answer: "Añadir respuesta" + description: Descripción + video_url: Video externo + documents: Documentos (1) + preview: Ver en la web + answers: + title: Respuesta + description: Descripción + answers: + new: + title: "Nueva respuesta" video_url: Video externo documents: Documentos (1) recounts: diff --git a/config/locales/es/responders.yml b/config/locales/es/responders.yml index 74e3ea20a..a35387ff3 100644 --- a/config/locales/es/responders.yml +++ b/config/locales/es/responders.yml @@ -8,6 +8,7 @@ es: direct_message: "Tu mensaje ha sido enviado correctamente." poll: "Votación creada correctamente." poll_booth: "Urna creada correctamente." + poll_question_answer: "Respuesta creada correctamente" proposal: "Propuesta creada correctamente." proposal_notification: "Tu message ha sido enviado correctamente." spending_proposal: "Propuesta de inversión creada correctamente. Puedes acceder a ella desde %{activity}" diff --git a/config/locales/fr/activerecord.yml b/config/locales/fr/activerecord.yml index b588d37dc..d6ab5db80 100644 --- a/config/locales/fr/activerecord.yml +++ b/config/locales/fr/activerecord.yml @@ -147,6 +147,9 @@ fr: name: Nom locale: Langue body: Contenu + poll/question_answer: + title: "Réponse" + description: "Description" errors: models: user: diff --git a/config/locales/fr/admin.yml b/config/locales/fr/admin.yml index ae9a44277..e000c8a28 100644 --- a/config/locales/fr/admin.yml +++ b/config/locales/fr/admin.yml @@ -377,14 +377,18 @@ fr: new: title: "Créer une question" poll_label: "Vote" - valid_answers_note: "Saisir les réponses séparées par des virgules (,)" show: proposal: Proposition originale author: Auteur title: Titre valid_answers: Réponses valides + add_answer: "Ajouter une réponse" + answer: "Réponse" description: Description preview: Voir l'aperçu + answers: + new: + title: "Nouvelle réponse" recounts: index: title: "Dépouillements" diff --git a/config/routes.rb b/config/routes.rb index 2b9c4250c..89691feac 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -300,7 +300,9 @@ Rails.application.routes.draw do end end - resources :questions + resources :questions do + resources :answers, only: [:new, :create], controller: 'questions/answers' + end end resources :verifications, controller: :verifications, only: :index do diff --git a/db/migrate/20171004025903_create_poll_question_answers.rb b/db/migrate/20171004025903_create_poll_question_answers.rb new file mode 100644 index 000000000..834421f5f --- /dev/null +++ b/db/migrate/20171004025903_create_poll_question_answers.rb @@ -0,0 +1,9 @@ +class CreatePollQuestionAnswers < ActiveRecord::Migration + def change + create_table :poll_question_answers do |t| + t.string :title + t.text :description + t.references :poll_question, index: true, foreign_key: true + end + end +end diff --git a/db/migrate/20171004151553_rename_poll_question_id_to_question_id.rb b/db/migrate/20171004151553_rename_poll_question_id_to_question_id.rb new file mode 100644 index 000000000..c53298bfb --- /dev/null +++ b/db/migrate/20171004151553_rename_poll_question_id_to_question_id.rb @@ -0,0 +1,5 @@ +class RenamePollQuestionIdToQuestionId < ActiveRecord::Migration + def change + rename_column :poll_question_answers, :poll_question_id, :question_id + end +end diff --git a/db/schema.rb b/db/schema.rb index c0fccda89..dbbd3ec46 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171003223152) do +ActiveRecord::Schema.define(version: 20171004151553) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -668,6 +668,14 @@ ActiveRecord::Schema.define(version: 20171003223152) do add_index "poll_partial_results", ["origin"], name: "index_poll_partial_results_on_origin", using: :btree add_index "poll_partial_results", ["question_id"], name: "index_poll_partial_results_on_question_id", using: :btree + create_table "poll_question_answers", force: :cascade do |t| + t.string "title" + t.text "description" + t.integer "question_id" + end + + add_index "poll_question_answers", ["question_id"], name: "index_poll_question_answers_on_question_id", using: :btree + create_table "poll_questions", force: :cascade do |t| t.integer "proposal_id" t.integer "poll_id" @@ -1144,6 +1152,7 @@ ActiveRecord::Schema.define(version: 20171003223152) do add_foreign_key "poll_partial_results", "poll_officer_assignments", column: "officer_assignment_id" add_foreign_key "poll_partial_results", "poll_questions", column: "question_id" add_foreign_key "poll_partial_results", "users", column: "author_id" + add_foreign_key "poll_question_answers", "poll_questions", column: "question_id" add_foreign_key "poll_questions", "polls" add_foreign_key "poll_questions", "proposals" add_foreign_key "poll_questions", "users", column: "author_id" diff --git a/spec/factories.rb b/spec/factories.rb index 9f1fca8ed..a28471efc 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -501,6 +501,12 @@ FactoryGirl.define do valid_answers { Faker::Lorem.words(3).join(', ') } end + factory :poll_question_answer, class: 'Poll::Question::Answer' do + association :question, factory: :poll_question + sequence(:title) { |n| "Question title #{n}" } + sequence(:description) { |n| "Question description #{n}" } + end + factory :poll_booth, class: 'Poll::Booth' do sequence(:name) { |n| "Booth #{n}" } sequence(:location) { |n| "Street #{n}" } diff --git a/spec/features/admin/poll/questions_spec.rb b/spec/features/admin/poll/questions_spec.rb index 52e3783f0..7e103c647 100644 --- a/spec/features/admin/poll/questions_spec.rb +++ b/spec/features/admin/poll/questions_spec.rb @@ -25,7 +25,6 @@ feature 'Admin poll questions' do expect(page).to have_content(question.title) expect(page).to have_content(question.author.name) - expect(page).to have_content(question.valid_answers.join(" ")) end scenario 'Create' do @@ -61,7 +60,6 @@ feature 'Admin poll questions' do expect(current_path).to eq(new_admin_question_path) expect(page).to have_field('poll_question_title', with: proposal.title) - expect(page).to have_field('poll_question_valid_answers', with: "Yes, No") select 'Proposals', from: 'poll_question_poll_id' diff --git a/spec/features/polls/answers_spec.rb b/spec/features/polls/answers_spec.rb new file mode 100644 index 000000000..5b1091134 --- /dev/null +++ b/spec/features/polls/answers_spec.rb @@ -0,0 +1,41 @@ +require 'rails_helper' + +feature 'Answers' do + + background do + admin = create(:administrator) + login_as(admin.user) + end + + scenario "Index" do + question = create(:poll_question) + answer1 = create(:poll_question_answer, question: question) + answer2 = create(:poll_question_answer, question: question) + + visit admin_question_path(question) + + expect(page).to have_css(".poll_question_answer", count: 2) + + within("#poll_question_answer_#{answer1.id}") do + expect(page).to have_content answer1.title + expect(page).to have_content answer1.description + end + end + + scenario "Create" do + question = create(:poll_question) + + visit admin_question_path(question) + + click_link "Add answer" + fill_in "poll_question_answer_title", with: "¿Would you like to reform Central Park?" + fill_in "poll_question_answer_description", with: "Adding more trees, creating a play area..." + click_button "Save" + + expect(page).to have_content "Answer created successfully" + end + + pending "Update" + pending "Destroy" + +end \ No newline at end of file diff --git a/spec/features/polls/polls_spec.rb b/spec/features/polls/polls_spec.rb index 8a90fb4a8..683c0402a 100644 --- a/spec/features/polls/polls_spec.rb +++ b/spec/features/polls/polls_spec.rb @@ -73,7 +73,10 @@ feature 'Polls' do end scenario 'Non-logged in users' do - create(:poll_question, poll: poll, valid_answers: 'Han Solo, Chewbacca') + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Han Solo') + answer2 = create(:poll_question_answer, question: question, title: 'Chewbacca') + visit poll_path(poll) expect(page).to have_content('Han Solo') @@ -87,7 +90,11 @@ feature 'Polls' do scenario 'Level 1 users' do poll.update(geozone_restricted: true) poll.geozones << geozone - create(:poll_question, poll: poll, valid_answers: 'Han Solo, Chewbacca') + + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Han Solo') + answer2 = create(:poll_question_answer, question: question, title: 'Chewbacca') + login_as(create(:user, geozone: geozone)) visit poll_path(poll) @@ -103,7 +110,11 @@ feature 'Polls' do scenario 'Level 2 users in an incoming poll' do incoming_poll = create(:poll, :incoming, geozone_restricted: true) incoming_poll.geozones << geozone - create(:poll_question, poll: incoming_poll, valid_answers: 'Rey, Finn') + + question = create(:poll_question, poll: incoming_poll) + answer1 = create(:poll_question_answer, question: question, title: 'Rey') + answer2 = create(:poll_question_answer, question: question, title: 'Finn') + login_as(create(:user, :level_two, geozone: geozone)) visit poll_path(incoming_poll) @@ -119,7 +130,11 @@ feature 'Polls' do scenario 'Level 2 users in an expired poll' do expired_poll = create(:poll, :expired, geozone_restricted: true) expired_poll.geozones << geozone - create(:poll_question, poll: expired_poll, valid_answers: 'Luke, Leia') + + question = create(:poll_question, poll: expired_poll) + answer1 = create(:poll_question_answer, question: question, title: 'Luke') + answer2 = create(:poll_question_answer, question: question, title: 'Leia') + login_as(create(:user, :level_two, geozone: geozone)) visit poll_path(expired_poll) @@ -135,7 +150,11 @@ feature 'Polls' do scenario 'Level 2 users in a poll with questions for a geozone which is not theirs' do poll.update(geozone_restricted: true) poll.geozones << create(:geozone) - create(:poll_question, poll: poll, valid_answers: 'Vader, Palpatine') + + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Vader') + answer2 = create(:poll_question_answer, question: question, title: 'Palpatine') + login_as(create(:user, :level_two)) visit poll_path(poll) @@ -149,7 +168,11 @@ feature 'Polls' do scenario 'Level 2 users reading a same-geozone poll' do poll.update(geozone_restricted: true) poll.geozones << geozone - create(:poll_question, poll: poll, valid_answers: 'Han Solo, Chewbacca') + + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Han Solo') + answer2 = create(:poll_question_answer, question: question, title: 'Chewbacca') + login_as(create(:user, :level_two, geozone: geozone)) visit poll_path(poll) @@ -158,7 +181,10 @@ feature 'Polls' do end scenario 'Level 2 users reading a all-geozones poll' do - create(:poll_question, poll: poll, valid_answers: 'Han Solo, Chewbacca') + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Han Solo') + answer2 = create(:poll_question_answer, question: question, title: 'Chewbacca') + login_as(create(:user, :level_two)) visit poll_path(poll) @@ -167,7 +193,9 @@ feature 'Polls' do end scenario 'Level 2 users who have already answered' do - question = create(:poll_question, poll: poll, valid_answers: 'Han Solo, Chewbacca') + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Han Solo') + answer2 = create(:poll_question_answer, question: question, title: 'Chewbacca') user = create(:user, :level_two) create(:poll_answer, question: question, author: user, answer: 'Chewbacca') @@ -182,7 +210,11 @@ feature 'Polls' do scenario 'Level 2 users answering', :js do poll.update(geozone_restricted: true) poll.geozones << geozone - create(:poll_question, poll: poll, valid_answers: 'Han Solo, Chewbacca') + + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Han Solo') + answer2 = create(:poll_question_answer, question: question, title: 'Chewbacca') + user = create(:user, :level_two, geozone: geozone) login_as user @@ -197,7 +229,11 @@ feature 'Polls' do scenario 'Level 2 users changing answer', :js do poll.update(geozone_restricted: true) poll.geozones << geozone - create(:poll_question, poll: poll, valid_answers: 'Han Solo, Chewbacca') + + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Han Solo') + answer2 = create(:poll_question_answer, question: question, title: 'Chewbacca') + user = create(:user, :level_two, geozone: geozone) login_as user diff --git a/spec/features/polls/voter_spec.rb b/spec/features/polls/voter_spec.rb index d925d3394..745489c32 100644 --- a/spec/features/polls/voter_spec.rb +++ b/spec/features/polls/voter_spec.rb @@ -6,7 +6,9 @@ feature "Voter" do scenario "Voting via web", :js do poll = create(:poll) - question = create(:poll_question, poll: poll, valid_answers: 'Yes, No') + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Yes') + answer2 = create(:poll_question_answer, question: question, title: 'No') user = create(:user, :level_two) login_as user @@ -48,7 +50,9 @@ feature "Voter" do context "Trying to vote the same poll in booth and web" do let(:poll) { create(:poll) } - let(:question) { create(:poll_question, poll: poll, valid_answers: 'Yes, No') } + let(:question) { create(:poll_question, poll: poll) } + let!(:answer1) { create(:poll_question_answer, question: question, title: 'Yes') } + let!(:answer2) { create(:poll_question_answer, question: question, title: 'No') } let!(:user) { create(:user, :in_census) } let(:officer) { create(:poll_officer) } diff --git a/spec/models/poll/answer_spec.rb b/spec/models/poll/answer_spec.rb index d245af8d4..2c27bc060 100644 --- a/spec/models/poll/answer_spec.rb +++ b/spec/models/poll/answer_spec.rb @@ -26,6 +26,7 @@ describe Poll::Answer do end it "should be valid for answers included in the Poll::Question's list" do + skip "review when removing valid_answers" question = create(:poll_question, valid_answers: 'One, Two, Three') expect(build(:poll_answer, question: question, answer: 'One')).to be_valid expect(build(:poll_answer, question: question, answer: 'Two')).to be_valid