diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index b73c07474..ce9861547 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -70,6 +70,7 @@ //= require polls_admin //= require leaflet //= require map +//= require polls var initialize_modules = function() { App.Comments.initialize(); @@ -108,6 +109,7 @@ var initialize_modules = function() { App.TagAutocomplete.initialize(); App.PollsAdmin.initialize(); App.Map.initialize(); + App.Polls.initialize(); }; $(function(){ diff --git a/app/assets/javascripts/polls.js.coffee b/app/assets/javascripts/polls.js.coffee new file mode 100644 index 000000000..5cf792ce2 --- /dev/null +++ b/app/assets/javascripts/polls.js.coffee @@ -0,0 +1,28 @@ +App.Polls = + generateToken: -> + token = '' + rand = '' + for n in [0..5] + rand = Math.random().toString(36).substr(2) # remove `0.` + token = token + rand; + + token = token.substring(0, 64) + return token + + replaceToken: -> + for link in $('.js-question-answer') + token_param = link.search.slice(-6) + if token_param == "token=" + link.href = link.href + @token + + initialize: -> + @token = App.Polls.generateToken() + App.Polls.replaceToken() + + $(".js-question-answer").on + click: => + token_message = $(".js-token-message") + if !token_message.is(':visible') + token_message.html(token_message.html() + "
" + @token + ""); + token_message.show() + false diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index 2df8292f6..7d6a0fff7 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -335,8 +335,16 @@ word-wrap: break-word; } - .callout.proposal-retired { - font-size: $base-font-size; + .callout { + &.token-message { + background-color: #fff; + border-color: $info-border; + color: $color-info; + } + + &.proposal-retired { + font-size: $base-font-size; + } } .social-share-full .social-share-button { @@ -1574,7 +1582,23 @@ border-bottom: 1px solid #eee; .column:nth-child(odd) { - border-right: 1px solid #eee; + border-right: 2px solid $text; + } + + .answer-divider { + border-bottom: 2px solid $text; + border-right: 0 !important; + margin-bottom: $line-height; + padding-bottom: $line-height; + } + + .answer-description { + height: 100%; + + &.short { + height: $line-height * 12; + overflow: hidden; + } } } @@ -1599,6 +1623,10 @@ .orbit-slide { max-height: none !important; + + img { + image-rendering: pixelated; + } } .orbit-caption { @@ -1606,6 +1634,11 @@ color: $text; } +.orbit-next, +.orbit-previous { + background: rgba(34, 34, 34, 0.25); +} + .zoom-link { background: #fff; border-radius: rem-calc(48); @@ -1638,7 +1671,10 @@ .poll { &.with-image { - padding: 0 $line-height / 2 0 0; + + @include breakpoint(medium) { + padding: 0 $line-height / 2 0 0; + } img { height: 100%; @@ -1769,6 +1805,10 @@ margin-right: $line-height / 4; min-width: rem-calc(168); + @include breakpoint(medium down) { + width: 100%; + } + &.answered { background: #f4f8ec; border: 2px solid #92ba48; diff --git a/app/controllers/admin/poll/questions/answers/images_controller.rb b/app/controllers/admin/poll/questions/answers/images_controller.rb index 4074eb371..1bf30907d 100644 --- a/app/controllers/admin/poll/questions/answers/images_controller.rb +++ b/app/controllers/admin/poll/questions/answers/images_controller.rb @@ -23,7 +23,8 @@ class Admin::Poll::Questions::Answers::ImagesController < Admin::Poll::BaseContr private def images_params - params.permit(images_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]) + params.require(:poll_question_answer).permit(:answer_id, + images_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]) end def load_answer diff --git a/app/controllers/admin/poll/questions/answers/videos_controller.rb b/app/controllers/admin/poll/questions/answers/videos_controller.rb new file mode 100644 index 000000000..a231c1a20 --- /dev/null +++ b/app/controllers/admin/poll/questions/answers/videos_controller.rb @@ -0,0 +1,57 @@ +class Admin::Poll::Questions::Answers::VideosController < Admin::Poll::BaseController + before_action :load_answer, only: [:index, :new, :create] + before_action :load_video, only: [:edit, :update, :destroy] + + def index + end + + def new + @video = ::Poll::Question::Answer::Video.new + end + + def create + @video = ::Poll::Question::Answer::Video.new(video_params) + + if @video.save + redirect_to admin_answer_videos_path(@answer), + notice: t("flash.actions.create.poll_question_answer_video") + else + render :new + end + end + + def edit + end + + def update + if @video.update(video_params) + redirect_to admin_answer_videos_path(@video.answer_id), + notice: t("flash.actions.save_changes.notice") + else + render :edit + end + end + + def destroy + if @video.destroy + notice = t("flash.actions.destroy.poll_question_answer_video") + else + notice = t("flash.actions.destroy.error") + end + redirect_to :back, notice: notice + end + + private + + def video_params + params.require(:poll_question_answer_video).permit(:title, :url, :answer_id) + end + + def load_answer + @answer = ::Poll::Question::Answer.find(params[:answer_id]) + end + + def load_video + @video = ::Poll::Question::Answer::Video.find(params[:id]) + end +end diff --git a/app/controllers/admin/poll/questions/answers_controller.rb b/app/controllers/admin/poll/questions/answers_controller.rb index 660339844..51c44dd94 100644 --- a/app/controllers/admin/poll/questions/answers_controller.rb +++ b/app/controllers/admin/poll/questions/answers_controller.rb @@ -1,5 +1,5 @@ class Admin::Poll::Questions::AnswersController < Admin::Poll::BaseController - before_action :load_question + before_action :load_answer, only: [:show, :edit, :update, :documents] load_and_authorize_resource :question, class: "::Poll::Question" @@ -11,20 +11,42 @@ class Admin::Poll::Questions::AnswersController < Admin::Poll::BaseController @answer = ::Poll::Question::Answer.new(answer_params) if @answer.save - redirect_to admin_question_path(@question), + redirect_to admin_question_path(@answer.question), notice: t("flash.actions.create.poll_question_answer") else render :new end end + def show + end + + def edit + end + + def update + if @answer.update(answer_params) + redirect_to admin_question_path(@answer.question), + notice: t("flash.actions.save_changes.notice") + else + redirect_to :back + end + end + + def documents + @documents = @answer.documents + + render 'admin/poll/questions/answers/documents' + end + private def answer_params - params.require(:poll_question_answer).permit(:title, :description, :question_id) + params.require(:poll_question_answer).permit(:title, :description, :question_id, documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]) end - def load_question - @question = ::Poll::Question.find(params[:question_id]) + def load_answer + @answer = ::Poll::Question::Answer.find(params[:id] || params[:answer_id]) end + end diff --git a/app/controllers/admin/poll/questions_controller.rb b/app/controllers/admin/poll/questions_controller.rb index 107c06740..5cf587735 100644 --- a/app/controllers/admin/poll/questions_controller.rb +++ b/app/controllers/admin/poll/questions_controller.rb @@ -56,8 +56,7 @@ class Admin::Poll::QuestionsController < Admin::Poll::BaseController private def question_params - params.require(:poll_question).permit(:poll_id, :title, :question, :proposal_id, :valid_answers, :video_url, - documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy]) + params.require(:poll_question).permit(:poll_id, :title, :question, :proposal_id, :valid_answers, :video_url) end def search_params diff --git a/app/controllers/polls/questions_controller.rb b/app/controllers/polls/questions_controller.rb index 490b32fbe..407e6d984 100644 --- a/app/controllers/polls/questions_controller.rb +++ b/app/controllers/polls/questions_controller.rb @@ -7,10 +7,12 @@ class Polls::QuestionsController < ApplicationController def answer answer = @question.answers.find_or_initialize_by(author: current_user) + token = params[:token] answer.answer = params[:answer] + answer.touch if answer.persisted? answer.save! - answer.record_voter_participation + answer.record_voter_participation(token) @answers_by_question_id = { @question.id => params[:answer] } end diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb index 289a33be3..6240d4809 100644 --- a/app/controllers/polls_controller.rb +++ b/app/controllers/polls_controller.rb @@ -1,6 +1,8 @@ class PollsController < ApplicationController include CommentableActions + include PollsHelper + load_and_authorize_resource has_filters %w{current expired incoming} @@ -14,15 +16,16 @@ class PollsController < ApplicationController def show @questions = @poll.questions.for_render.sort_for_list - - @commentable = @poll - @comment_tree = CommentTree.new(@commentable, params[:page], @current_order) - + @token = poll_voter_token(@poll, current_user) + @answers_by_question_id = {} poll_answers = ::Poll::Answer.by_question(@poll.question_ids).by_author(current_user.try(:id)) poll_answers.each do |answer| @answers_by_question_id[answer.question_id] = answer.answer end + + @commentable = @poll + @comment_tree = CommentTree.new(@commentable, params[:page], @current_order) end end diff --git a/app/helpers/polls_helper.rb b/app/helpers/polls_helper.rb index 27d33ea04..dd4018be7 100644 --- a/app/helpers/polls_helper.rb +++ b/app/helpers/polls_helper.rb @@ -41,4 +41,12 @@ module PollsHelper booth.name + location end + def poll_voter_token(poll, user) + Poll::Voter.where(poll: poll, user: user, origin: "web").first&.token || '' + end + + def voted_before_sign_in(question) + question.answers.where(author: current_user).any? { |vote| current_user.current_sign_in_at >= vote.updated_at } + end + end diff --git a/app/models/direct_upload.rb b/app/models/direct_upload.rb index 192f06d04..e597f9f12 100644 --- a/app/models/direct_upload.rb +++ b/app/models/direct_upload.rb @@ -19,7 +19,9 @@ class DirectUpload if @resource_type.present? && @resource_relation.present? && (@attachment.present? || @cached_attachment.present?) @resource = @resource_type.constantize.find_or_initialize_by(id: @resource_id) - if @resource.respond_to?(:images) + #Refactor + if @resource.respond_to?(:images) && + ((@attachment.present? && !@attachment.content_type.match(/pdf/)) || @cached_attachment.present?) @relation = @resource.images.send("build", relation_attributtes) elsif @resource.class.reflections[@resource_relation].macro == :has_one @relation = @resource.send("build_#{resource_relation}", relation_attributtes) diff --git a/app/models/poll/answer.rb b/app/models/poll/answer.rb index 2378de6ba..4484060dd 100644 --- a/app/models/poll/answer.rb +++ b/app/models/poll/answer.rb @@ -16,7 +16,7 @@ class Poll::Answer < ActiveRecord::Base scope :by_author, ->(author_id) { where(author_id: author_id) } scope :by_question, ->(question_id) { where(question_id: question_id) } - def record_voter_participation - Poll::Voter.find_or_create_by!(user: author, poll: poll, origin: "web") + def record_voter_participation(token) + Poll::Voter.find_or_create_by(user: author, poll: poll, origin: "web", token: token) end -end \ No newline at end of file +end diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb index 1e94e0180..e6ef482a9 100644 --- a/app/models/poll/question.rb +++ b/app/models/poll/question.rb @@ -1,11 +1,6 @@ class Poll::Question < ActiveRecord::Base include Measurable include Searchable - include Documentable - documentable max_documents_allowed: 1, - max_file_size: 3.megabytes, - accepted_content_types: [ "application/pdf" ] - accepts_nested_attributes_for :documents, allow_destroy: true acts_as_paranoid column: :hidden_at include ActsAsParanoidAliases diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb index 1746c480c..b3324e57f 100644 --- a/app/models/poll/question/answer.rb +++ b/app/models/poll/question/answer.rb @@ -1,7 +1,13 @@ class Poll::Question::Answer < ActiveRecord::Base include Galleryable + include Documentable + documentable max_documents_allowed: 3, + max_file_size: 3.megabytes, + accepted_content_types: [ "application/pdf" ] + accepts_nested_attributes_for :documents, allow_destroy: true belongs_to :question, class_name: 'Poll::Question', foreign_key: 'question_id' + has_many :videos, class_name: 'Poll::Question::Answer::Video' validates :title, presence: true diff --git a/app/models/poll/question/answer/video.rb b/app/models/poll/question/answer/video.rb new file mode 100644 index 000000000..3d214af96 --- /dev/null +++ b/app/models/poll/question/answer/video.rb @@ -0,0 +1,16 @@ +class Poll::Question::Answer::Video < ActiveRecord::Base + belongs_to :answer, class_name: 'Poll::Question::Answer', foreign_key: 'answer_id' + + VIMEO_REGEX = /vimeo.*(staffpicks\/|channels\/|videos\/|video\/|\/)([^#\&\?]*).*/ + YOUTUBE_REGEX = /youtu.*(be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/ + + validates :title, presence: true + validate :valid_url? + + def valid_url? + return if url.blank? + return if url.match(VIMEO_REGEX) + return if url.match(YOUTUBE_REGEX) + errors.add(:url, :invalid) + end +end diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb index 81e1ae9bd..21dc7cffb 100644 --- a/app/views/admin/_menu.html.erb +++ b/app/views/admin/_menu.html.erb @@ -53,7 +53,6 @@ <% end %> - <% if feature?(:polls) %>
  • diff --git a/app/views/admin/poll/officer_assignments/by_officer.html.erb b/app/views/admin/poll/officer_assignments/by_officer.html.erb index c9873db96..c6738eba0 100644 --- a/app/views/admin/poll/officer_assignments/by_officer.html.erb +++ b/app/views/admin/poll/officer_assignments/by_officer.html.erb @@ -21,7 +21,7 @@ <% @officer_assignments.each do |officer_assignment| %> - <%= officer_assignment.final? ? t('polls.final_date') : l(officer_assignment.date.to_date) %> + <%= l(officer_assignment.date.to_date)%> <%= content_tag :strong, t('polls.final_date') if officer_assignment.final %> <%= booth_name_with_location(officer_assignment.booth_assignment.booth) %> <% end %> diff --git a/app/views/admin/poll/questions/_form.html.erb b/app/views/admin/poll/questions/_form.html.erb index 5345a64b1..17ebde59d 100644 --- a/app/views/admin/poll/questions/_form.html.erb +++ b/app/views/admin/poll/questions/_form.html.erb @@ -4,7 +4,6 @@ <%= f.hidden_field :proposal_id %> -
    <%= f.select :poll_id, @@ -13,17 +12,19 @@ label: t("admin.questions.new.poll_label") %>
    - <%= f.text_field :title, maxlength: Poll::Question.title_max_length %> - -
    - <%= render 'documents/nested_documents', documentable: @question, f: f %> -
    + <%= f.text_field :title %>
    <%= f.label :video_url, t("proposals.form.proposal_video_url") %> -

    <%= t("proposals.form.proposal_video_url_note") %>

    - <%= f.text_field :video_url, placeholder: t("proposals.form.proposal_video_url"), label: false, - aria: {describedby: "video-url-help-text"} %> + +

    + <%= t("proposals.form.proposal_video_url_note") %> +

    + + <%= f.text_field :video_url, + placeholder: t("proposals.form.proposal_video_url"), + label: false, + aria: {describedby: "video-url-help-text"} %>
    diff --git a/app/views/admin/poll/questions/answers/_form.html.erb b/app/views/admin/poll/questions/answers/_form.html.erb index 72ffb4771..3bb2ebe39 100644 --- a/app/views/admin/poll/questions/answers/_form.html.erb +++ b/app/views/admin/poll/questions/answers/_form.html.erb @@ -2,20 +2,17 @@ <%= render 'shared/errors', resource: @answer %> - <%= f.hidden_field :question_id, value: @question.id %> + <%= f.hidden_field :question_id, value: @answer.question_id || @question.id %> - <%= f.label :title, t('admin.questions.new.form.title') %> - <%= f.text_field :title, label: false %> + <%= f.text_field :title %>
    - <%= f.label :description, t('admin.questions.new.form.description') %> <%= f.cktext_area :description, maxlength: Poll::Question.description_max_length, - ckeditor: { language: I18n.locale }, - label: false %> + ckeditor: { language: I18n.locale } %>
    -
    +
    <%= f.submit(class: "button expanded", value: t("shared.save")) %>
    diff --git a/app/views/admin/poll/questions/answers/documents.html.erb b/app/views/admin/poll/questions/answers/documents.html.erb new file mode 100644 index 000000000..54dd25a18 --- /dev/null +++ b/app/views/admin/poll/questions/answers/documents.html.erb @@ -0,0 +1,55 @@ +<%= back_link_to %> + +

    <%= t("admin.questions.show.answers.documents_list") %>

    + + + +
    + <%= form_for(Poll::Question::Answer.new, + url: admin_answer_path(@answer), + method: :put) do |f| %> + + <%= render 'shared/errors', resource: @answer %> + +
    +
    +
    + <%= render 'documents/nested_documents', documentable: @answer, f: f %> +
    + +
    +
    + <%= f.submit(class: "button expanded", value: t("shared.save")) %> +
    +
    +
    +
    + <% end %> + + <% if @answer.documents.present? %> + + + + + + + <% @answer.documents.each do |document| %> + + + + + <% end %> +
    <%= t("admin.questions.show.answers.document_title") %><%= t("admin.questions.show.answers.document_actions") %>
    + <%= link_to document.title, document.attachment.url %> + + <%= link_to t('documents.buttons.download_document'), + document.attachment.url, + target: "_blank", + rel: "nofollow", + class: 'button hollow' %> +
    + <% end %> +
    diff --git a/app/views/admin/poll/questions/answers/edit.html.erb b/app/views/admin/poll/questions/answers/edit.html.erb new file mode 100644 index 000000000..48fb4ad5d --- /dev/null +++ b/app/views/admin/poll/questions/answers/edit.html.erb @@ -0,0 +1,14 @@ +<%= back_link_to %> + + + +

    + <%= t("admin.answers.edit.title") %> +

    + +
    + <%= render "form", form_url: admin_answer_path(@answer) %> +
    diff --git a/app/views/admin/poll/questions/answers/show.html.erb b/app/views/admin/poll/questions/answers/show.html.erb new file mode 100644 index 000000000..e6e0244a0 --- /dev/null +++ b/app/views/admin/poll/questions/answers/show.html.erb @@ -0,0 +1,32 @@ +<%= back_link_to %> + +<%= link_to t('shared.edit'), edit_admin_answer_path(@answer), + class: "button hollow float-right" %> + + + +
    + +
    +
    +

    + <%= t("admin.answers.show.title") %> +
    + <%= @answer.title %> +

    + +

    + <%= t("admin.answers.show.description") %> + <%= @answer.description %> +

    + +

    + <%= t("admin.answers.show.images") %> +
    + <%= link_to t("admin.answers.show.images_list"), admin_answer_images_path(@answer) %> +

    +
    +
    diff --git a/app/views/admin/poll/questions/answers/videos/_form.html.erb b/app/views/admin/poll/questions/answers/videos/_form.html.erb new file mode 100644 index 000000000..28035c397 --- /dev/null +++ b/app/views/admin/poll/questions/answers/videos/_form.html.erb @@ -0,0 +1,21 @@ +<%= form_for(@video, url: form_url) do |f| %> + + <%= render 'shared/errors', resource: @video %> + + <%= f.hidden_field :answer_id, value: @video.answer_id || @answer.id %> + +
    +
    + + <%= f.text_field :title %> + <%= f.text_field :url %> + +
    +
    + <%= f.submit(class: "button expanded", value: t("shared.save")) %> +
    +
    + +
    +
    +<% end %> diff --git a/app/views/admin/poll/questions/answers/videos/edit.html.erb b/app/views/admin/poll/questions/answers/videos/edit.html.erb new file mode 100644 index 000000000..31b59c2dd --- /dev/null +++ b/app/views/admin/poll/questions/answers/videos/edit.html.erb @@ -0,0 +1,9 @@ +<%= back_link_to %> + +

    + <%= t("admin.answers.videos.edit.title") %> +

    + +
    + <%= render "form", form_url: admin_video_path(@video) %> +
    diff --git a/app/views/admin/poll/questions/answers/videos/index.html.erb b/app/views/admin/poll/questions/answers/videos/index.html.erb new file mode 100644 index 000000000..0ea64c570 --- /dev/null +++ b/app/views/admin/poll/questions/answers/videos/index.html.erb @@ -0,0 +1,47 @@ +<%= back_link_to admin_question_path(@answer.question_id) %> + +
    + +

    + <%= t("admin.answers.videos.index.title") %> +

    + +<%= link_to t("admin.answers.videos.index.add_video"), + new_admin_answer_video_path, + class: "button success float-right" %> + +
    + + + + + + + + + + + + <% @answer.videos.each do |video| %> + + + + + + <% end %> + +
    <%= t("admin.answers.videos.index.video_title") %><%= t("admin.answers.videos.index.video_url") %> + <%= t("admin.actions.actions") %> +
    <%= video.title %><%= link_to "#{video.url}", video.url %> + + <%= link_to t("shared.edit"), + edit_admin_video_path(video), + class: "button hollow" %> + + <%= link_to t("shared.delete"), + admin_video_path(video), + class: "button hollow alert", + method: :delete %> +
    + +
    diff --git a/app/views/admin/poll/questions/answers/videos/new.html.erb b/app/views/admin/poll/questions/answers/videos/new.html.erb new file mode 100644 index 000000000..d114b9104 --- /dev/null +++ b/app/views/admin/poll/questions/answers/videos/new.html.erb @@ -0,0 +1,9 @@ +<%= back_link_to admin_answer_videos_path(@answer) %> + +

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

    + +
    + <%= render "form", form_url: admin_answer_videos_path %> +
    diff --git a/app/views/admin/poll/questions/show.html.erb b/app/views/admin/poll/questions/show.html.erb index 64c30de1e..5baabeafe 100644 --- a/app/views/admin/poll/questions/show.html.erb +++ b/app/views/admin/poll/questions/show.html.erb @@ -31,7 +31,7 @@ - - - + + + + <% @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), @@ -41,19 +41,34 @@
    <%= t("admin.questions.show.answers.title") %><%= t("admin.questions.show.answers.description") %><%= t("admin.questions.show.answers.images") %><%= t("admin.questions.show.answers.description") %><%= t("admin.questions.show.answers.images") %><%= t("admin.questions.show.answers.documents") %><%= t("admin.questions.show.answers.videos") %>
    <%= answer.title %><%= link_to answer.title, admin_answer_path(answer) %> <%= answer.description %> - (<%= answer.images.count %>)
    + (<%= answer.images.count %>) +
    <%= link_to t("admin.questions.show.answers.images_list"), admin_answer_images_path(answer) %>
    + (<%= answer.documents.count rescue 0 %>) +
    + <%= link_to t("admin.questions.show.answers.documents_list"), + admin_answer_documents_path(answer) %> +
    + (<%= answer.videos.count %>) +
    + <%= link_to t("admin.questions.show.answers.video_list"), + admin_answer_videos_path(answer) %> +
    @@ -65,11 +80,3 @@
    <%= @question.video_url %>

    <% end %> - -<% if @question.documents.any? %> -

    - <%= t("admin.questions.show.documents") %> -
    - <%= @question.documents.first.title %> -

    -<% end %> diff --git a/app/views/polls/_gallery.html.erb b/app/views/polls/_gallery.html.erb index ba6cfc2e6..4e234f7fc 100644 --- a/app/views/polls/_gallery.html.erb +++ b/app/views/polls/_gallery.html.erb @@ -1,6 +1,6 @@
  • - <% answer.images.each_with_index do |image, index| %> + <% answer.images.reverse.each_with_index do |image, index| %>
  • <%= link_to image.attachment.url(:original), target: "_blank" do %> - <%= image_tag image.attachment.url(:medium), + <%= image_tag image.attachment.url(:original), class: "orbit-image", alt: image.title %> <% end %> diff --git a/app/views/polls/_poll_group.html.erb b/app/views/polls/_poll_group.html.erb index 380b35fc7..529e9a99e 100644 --- a/app/views/polls/_poll_group.html.erb +++ b/app/views/polls/_poll_group.html.erb @@ -1,11 +1,11 @@ <% poll_group.each do |poll| %>
    - <% if user_signed_in? && !poll.votable_by?(current_user) %> + <% if user_signed_in? && !current_user.unverified? && !poll.votable_by?(current_user) %>
    "> <%= t("polls.index.already_answer") %>
    <% end %> -
    +
    <% if poll.image.present? %> diff --git a/app/views/polls/questions/_answers.html.erb b/app/views/polls/questions/_answers.html.erb index 2bef6de72..7d9bc4cac 100644 --- a/app/views/polls/questions/_answers.html.erb +++ b/app/views/polls/questions/_answers.html.erb @@ -1,18 +1,20 @@
    - <% if can? :answer, question %> + <% if can?(:answer, question) && !question.poll.voted_in_booth?(current_user) %> <% question.question_answers.each do |answer| %> - <% if @answers_by_question_id[question.id] == answer.title %> - "> + <% if @answers_by_question_id[question.id] == answer.title && + (!voted_before_sign_in(question) || + question.poll.voted_in_booth?(current_user)) %> + "> <%= answer.title %> <% else %> <%= link_to answer.title, - answer_question_path(question, answer: answer.title), + answer_question_path(question, answer: answer.title, token: token), method: :post, remote: true, title: t("poll_questions.show.vote_answer", answer: answer.title), - class: "button secondary hollow" %> + class: "button secondary hollow js-question-answer" %> <% end %> <% end %> <% else %> diff --git a/app/views/polls/questions/_question.html.erb b/app/views/polls/questions/_question.html.erb index 982d0a070..b0df3fb42 100644 --- a/app/views/polls/questions/_question.html.erb +++ b/app/views/polls/questions/_question.html.erb @@ -4,6 +4,6 @@
    - <%= render 'polls/questions/answers', question: question %> + <%= render 'polls/questions/answers', question: question, token: token %>
    diff --git a/app/views/polls/questions/answer.js.erb b/app/views/polls/questions/answer.js.erb index aabbd8d89..8e8c01358 100644 --- a/app/views/polls/questions/answer.js.erb +++ b/app/views/polls/questions/answer.js.erb @@ -1 +1,2 @@ -$("#<%= dom_id(@question) %>_answers").html('<%= j render("polls/questions/answers", question: @question) %>'); +<% token = poll_voter_token(@question.poll, current_user) %> +$("#<%= dom_id(@question) %>_answers").html('<%= j render("polls/questions/answers", question: @question, token: token) %>'); diff --git a/app/views/polls/show.html.erb b/app/views/polls/show.html.erb index a67aca798..5d60ed284 100644 --- a/app/views/polls/show.html.erb +++ b/app/views/polls/show.html.erb @@ -38,10 +38,25 @@ <%= t("polls.show.already_voted_in_booth") %>
    <% else %> - <% @questions.each do |question| %> - <%= render 'polls/questions/question', question: question %> + + <% if current_user && !@poll.votable_by?(current_user) %> +
    + <%= t("polls.show.already_voted_in_web") %> +
    <% end %> <% end %> + + <% @questions.each do |question| %> + <%= render 'polls/questions/question', question: question, token: @token %> + <% end %> + + <% if poll_voter_token(@poll, current_user).empty? %> + + <% end %> + + <%= link_to t("polls.show.participate_in_other_polls"), polls_path, class: "button hollow" %>
    @@ -52,10 +67,12 @@ <%= safe_html_with_links simple_format(@poll.description) %>
    - + <% end %>
    @@ -64,23 +81,57 @@ <% @poll.questions.map(&:question_answers).flatten.each do |answer| %>
    + data-toggler="medium-6 answer-divider"> -

    <%= answer.title %>

    + <% if answer.description.present? %> +

    <%= answer.title %>

    + <% end %> <% if answer.images.any? %> <%= render "gallery", answer: answer %> <% end %> -
    - <%= safe_html_with_links simple_format(answer.description) %> -
    + <% if answer.description.present? %> + + <% end %> + <% if answer.documents.present? %> + + <% end %>
    <% end %> +
    <%= render "shared/comments" %>
    + diff --git a/config/locales/en/activerecord.yml b/config/locales/en/activerecord.yml index 5ef1917d3..bf2cbb712 100644 --- a/config/locales/en/activerecord.yml +++ b/config/locales/en/activerecord.yml @@ -213,9 +213,12 @@ en: image: title: Title attachment: Attachment - poll/question_answer: - title: "Answer" - description: "Description" + poll/question/answer: + title: Answer + description: Description + poll/question/answer/video: + title: Title + url: External video errors: models: user: diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 02d8bacdd..442c93727 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -587,9 +587,6 @@ en: new: title: "Create Question" poll_label: "Poll" - form: - title: Title - description: Description answers: images: add_image: "Add image" @@ -599,17 +596,39 @@ en: author: Author title: Title valid_answers: Valid answers - add_answer: "Add answer" + add_answer: Add answer video_url: External video - documents: Documents (1) answers: title: Answer description: Description + videos: Videos + video_list: Video list images: Images images_list: Images list + documents: Documents + documents_list: Documents list + document_title: Title + document_actions: Actions answers: new: - title: "New answer" + title: New answer + show: + title: Title + description: Description + images: Images + images_list: Images list + edit: + title: Edit answer + videos: + index: + title: Videos + add_video: Add video + video_title: Title + video_url: External video + new: + title: New video + edit: + title: Edit video recounts: index: title: "Recounts" diff --git a/config/locales/en/general.yml b/config/locales/en/general.yml index 11693d20f..413a6abf8 100644 --- a/config/locales/en/general.yml +++ b/config/locales/en/general.yml @@ -478,7 +478,8 @@ en: help_text_1: "Voting takes place when a citizen proposal supports reaches 1% of the census with voting rights. Voting can also include questions that the City Council ask to the citizens decision." help_text_2: "To participate in the next vote you have to sign up on %{org} and verify your account. All registered voters in the city over 16 years old can vote. The results of all votes are binding on the government." show: - already_voted_in_booth: "You have already participated in a booth for this poll." + already_voted_in_booth: "You have already participated in a physical booth. You can not participate again." + already_voted_in_web: "You have already participated in this poll. If you vote again it will be overwritten." back: Back to voting cant_answer_not_logged_in: "You must %{signin} or %{signup} to participate." signin: Sign in @@ -491,12 +492,16 @@ en: more_info_title: "More information" documents: Documents zoom_plus: Expand image + read_more: "Read more about %{answer}" + read_less: "Read less about %{answer}" + participate_in_other_polls: Participate in other polls poll_questions: create_question: "Create question" default_valid_answers: "Yes, No" show: vote_answer: "Vote %{answer}" voted: "You have voted %{answer}" + voted_token: "You can write down this vote identifier, to check your vote on the final results:" proposal_notifications: new: title: "Send message" diff --git a/config/locales/en/responders.yml b/config/locales/en/responders.yml index fd187c495..88f68a955 100644 --- a/config/locales/en/responders.yml +++ b/config/locales/en/responders.yml @@ -9,6 +9,7 @@ en: poll: "Poll created successfully." poll_booth: "Booth created successfully." poll_question_answer: "Answer created successfully" + poll_question_answer_video: "Video 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}" @@ -31,3 +32,4 @@ en: budget_investment: "Investment project deleted succesfully." error: "Could not delete" topic: "Topic deleted successfully." + poll_question_answer_video: "Answer video deleted successfully." diff --git a/config/locales/es/activerecord.yml b/config/locales/es/activerecord.yml index 9b3260680..f61e3bd53 100644 --- a/config/locales/es/activerecord.yml +++ b/config/locales/es/activerecord.yml @@ -207,9 +207,12 @@ es: image: title: Título attachment: Archivo adjunto - poll/question_answer: - title: "Respuesta" - description: "Descripción" + poll/question/answer: + title: Respuesta + description: Descripción + poll/question/answer/video: + title: Título + url: Vídeo externo errors: models: user: diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index 9e0de770e..5b6393b1b 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -587,9 +587,6 @@ es: new: title: "Crear pregunta ciudadana" poll_label: "Votación" - form: - title: Título - description: Descripción answers: images: add_image: "Añadir imagen" @@ -599,21 +596,41 @@ es: author: Autor title: Título valid_answers: Respuestas válidas - add_answer: "Añadir respuesta" - description: Descripción + add_answer: Añadir respuesta video_url: Video externo - documents: Documentos (1) - preview: Ver en la web answers: title: Respuesta description: Descripción + videos: Vídeos + video_list: Lista de vídeos images: Imágenes images_list: Lista de imágenes + documents: Documentos + documents_list: Lista de documentos + document_title: Título + document_actions: Acciones answers: new: - title: "Nueva respuesta" + title: Nueva respuesta video_url: Video externo documents: Documentos (1) + show: + title: Título + description: Descripción + images: Imágenes + images_list: Lista de imágenes + edit: + title: Editar respuesta + videos: + index: + title: Vídeos + add_video: Añadir vídeo + video_title: Título + video_url: External video + new: + title: Nuevo video + edit: + title: Editar vídeo recounts: index: title: "Recuentos" diff --git a/config/locales/es/general.yml b/config/locales/es/general.yml index 981f85ca6..b8747406b 100644 --- a/config/locales/es/general.yml +++ b/config/locales/es/general.yml @@ -478,7 +478,8 @@ es: help_text_1: "Las votaciones se convocan cuando una propuesta ciudadana alcanza el 1% de apoyos del censo con derecho a voto. En las votaciones también se pueden incluir cuestiones que el Ayuntamiento somete a decisión directa de la ciudadanía." help_text_2: "Para participar en la próxima votación tienes que registrarte en %{org} y verificar tu cuenta. Pueden votar todas las personas empadronadas en la ciudad mayores de 16 años. Los resultados de todas las votaciones serán vinculantes para el gobierno." show: - already_voted_in_booth: "Ya has participado en esta votación en una urna." + already_voted_in_booth: "Ya has participado en esta votación en urnas presenciales, no puedes volver a participar." + already_voted_in_web: "Ya has participado en esta votación. Si vuelves a votar se sobreescribirá tu resultado anterior." back: Volver a votaciones cant_answer_not_logged_in: "Necesitas %{signin} o %{signup} para participar." signin: iniciar sesión @@ -491,12 +492,16 @@ es: more_info_title: "Más información" documents: Documentación zoom_plus: Ampliar imagen + read_more: "Leer más sobre %{answer}" + read_less: "Leer menos sobre %{answer}" + participate_in_other_polls: Participar en otras votaciones poll_questions: create_question: "Crear pregunta para votación" default_valid_answers: "Sí, No" show: vote_answer: "Votar %{answer}" voted: "Has votado %{answer}" + voted_token: "Puedes apuntar este identificador de voto, para comprobar tu votación en el resultado final:" proposal_notifications: new: title: "Enviar mensaje" diff --git a/config/locales/es/responders.yml b/config/locales/es/responders.yml index a35387ff3..01f19d96e 100644 --- a/config/locales/es/responders.yml +++ b/config/locales/es/responders.yml @@ -9,6 +9,7 @@ es: poll: "Votación creada correctamente." poll_booth: "Urna creada correctamente." poll_question_answer: "Respuesta creada correctamente" + poll_question_answer_video: "Vídeo creado 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}" @@ -31,3 +32,4 @@ es: budget_investment: "Propuesta de inversión eliminada." error: "No se pudo borrar" topic: "Tema eliminado." + poll_question_answer_video: "Vídeo de respuesta eliminado." diff --git a/config/locales/fr/activerecord.yml b/config/locales/fr/activerecord.yml index d6ab5db80..82e9d180e 100644 --- a/config/locales/fr/activerecord.yml +++ b/config/locales/fr/activerecord.yml @@ -147,9 +147,12 @@ fr: name: Nom locale: Langue body: Contenu - poll/question_answer: - title: "Réponse" - description: "Description" + poll/question/answer: + title: Réponse + description: Description + poll/question/answer/video: + title: Titre + url: Vidéo externe errors: models: user: diff --git a/config/locales/fr/admin.yml b/config/locales/fr/admin.yml index e000c8a28..b9cdfcfd3 100644 --- a/config/locales/fr/admin.yml +++ b/config/locales/fr/admin.yml @@ -382,13 +382,34 @@ fr: author: Auteur title: Titre valid_answers: Réponses valides - add_answer: "Ajouter une réponse" - answer: "Réponse" - description: Description + add_answer: Ajouter une réponse + documents: Documents (1) preview: Voir l'aperçu + answers: + title: Réponse + description: Description + videos: Vidéos + video_list: Liste des vidéos answers: + show: + title: Titre + description: Description + images: Images + images_list: Liste des images new: - title: "Nouvelle réponse" + title: Nouvelle réponse + edit: + title: Modifier réponse + videos: + index: + title: Vidéos + add_video: Ajouter une vidéo + video_title: Titre + video_url: Vidéo externe + new: + title: Nouveau vidéo + edit: + title: Modifier la vidéo recounts: index: title: "Dépouillements" diff --git a/config/locales/fr/responders.yml b/config/locales/fr/responders.yml index 9897e3455..1d1cfae0c 100644 --- a/config/locales/fr/responders.yml +++ b/config/locales/fr/responders.yml @@ -8,6 +8,8 @@ fr: direct_message: "Votre message a été envoyé avec succès." poll: "Vote créé avec succès." poll_booth: "Urne créée avec succès." + poll_question_answer: "Réponse créée avec succès" + poll_question_answer_video: "Vidéo créée avec succès" proposal: "Proposition créée avec succès." proposal_notification: "Votre message a correctement été envoyé." spending_proposal: "Proposition de dépense créée avec succès. Vous pouvez y accéder depuis %{activity}" @@ -27,3 +29,4 @@ fr: spending_proposal: "Proposition de dépense supprimée avec succès." budget_investment: "Budget d'investissement supprimé avec succès." error: "Suppression impossible" + poll_question_answer_video: "Réponse vidéo supprimée avec succès." diff --git a/config/routes.rb b/config/routes.rb index 387983d4b..83b5754d7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -300,11 +300,12 @@ Rails.application.routes.draw do end end - resources :questions do - resources :answers, only: [:new, :create], controller: 'questions/answers', shallow: true do + resources :questions, shallow: true do + resources :answers, except: [:index, :destroy], controller: 'questions/answers', shallow: true do resources :images, controller: 'questions/answers/images' + resources :videos, controller: 'questions/answers/videos' + get :documents, to: 'questions/answers#documents' end - end end diff --git a/db/dev_seeds.rb b/db/dev_seeds.rb index b151a45cf..31902bad9 100644 --- a/db/dev_seeds.rb +++ b/db/dev_seeds.rb @@ -557,11 +557,14 @@ print "Creating Poll Questions" author = User.reorder("RANDOM()").first description = "

    #{Faker::Lorem.paragraphs.join('

    ')}

    " open_at = rand(2.months.ago..2.months.from_now) + answers = Faker::Lorem.words((2..4).to_a.sample).map { |answer| answer.capitalize } question = Poll::Question.create!(author: author, title: Faker::Lorem.sentence(3).truncate(60), - description: description, - valid_answers: Faker::Lorem.words((2..7).to_a.sample).join(', '), + valid_answers: answers.join(', '), poll: poll) + answers.each do |answer| + Poll::Question::Answer.create!(question: question, title: answer, description: Faker::ChuckNorris.fact) + end end puts " ✅" diff --git a/db/migrate/20171004210108_create_poll_question_answer_videos.rb b/db/migrate/20171004210108_create_poll_question_answer_videos.rb new file mode 100644 index 000000000..7cce33b29 --- /dev/null +++ b/db/migrate/20171004210108_create_poll_question_answer_videos.rb @@ -0,0 +1,11 @@ +class CreatePollQuestionAnswerVideos < ActiveRecord::Migration + def change + create_table :poll_question_answer_videos do |t| + t.string :title + t.string :url + t.integer :answer_id, index: true + end + + add_foreign_key :poll_question_answer_videos, :poll_question_answers, column: :answer_id + end +end diff --git a/db/migrate/20171006145053_add_token_to_poll_voters.rb b/db/migrate/20171006145053_add_token_to_poll_voters.rb new file mode 100644 index 000000000..5d07f5065 --- /dev/null +++ b/db/migrate/20171006145053_add_token_to_poll_voters.rb @@ -0,0 +1,5 @@ +class AddTokenToPollVoters < ActiveRecord::Migration + def change + add_column :poll_voters, :token, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index d393716e6..9adba12b1 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: 20171004151553) do +ActiveRecord::Schema.define(version: 20171006145053) 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: 20171004151553) 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_answer_videos", force: :cascade do |t| + t.string "title" + t.string "url" + t.integer "answer_id" + end + + add_index "poll_question_answer_videos", ["answer_id"], name: "index_poll_question_answer_videos_on_answer_id", using: :btree + create_table "poll_question_answers", force: :cascade do |t| t.string "title" t.text "description" @@ -760,6 +768,7 @@ ActiveRecord::Schema.define(version: 20171004151553) do t.integer "user_id" t.string "origin" t.integer "officer_id" + t.string "token" end add_index "poll_voters", ["booth_assignment_id"], name: "index_poll_voters_on_booth_assignment_id", using: :btree @@ -1155,6 +1164,7 @@ ActiveRecord::Schema.define(version: 20171004151553) 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_answer_videos", "poll_question_answers", column: "answer_id" add_foreign_key "poll_question_answers", "poll_questions", column: "question_id" add_foreign_key "poll_questions", "polls" add_foreign_key "poll_questions", "proposals" diff --git a/spec/features/admin/poll/questions/answers/answers_spec.rb b/spec/features/admin/poll/questions/answers/answers_spec.rb new file mode 100644 index 000000000..66918c4d8 --- /dev/null +++ b/spec/features/admin/poll/questions/answers/answers_spec.rb @@ -0,0 +1,51 @@ +require 'rails_helper' + +feature 'Answers' do + + background do + admin = create(:administrator) + login_as (admin.user) + end + + scenario 'Create' do + question = create(:poll_question) + title = 'Whatever the question may be, the answer is always 42' + description = "The Hitchhiker's Guide To The Universe" + + visit admin_question_path(question) + click_link 'Add answer' + + fill_in 'poll_question_answer_title', with: title + fill_in 'poll_question_answer_description', with: description + + click_button 'Save' + + expect(page).to have_content(title) + expect(page).to have_content(description) + end + + scenario 'Update' do + question = create(:poll_question) + answer = create(:poll_question_answer, question: question, title: "Answer title") + + visit admin_answer_path(answer) + + click_link 'Edit' + + old_title = answer.title + new_title = 'Ex Machina' + + fill_in 'poll_question_answer_title', with: new_title + + click_button 'Save' + + expect(page).to have_content('Changes saved') + expect(page).to have_content(new_title) + + visit admin_question_path(question) + + expect(page).to have_content(new_title) + expect(page).to_not have_content(old_title) + end + +end diff --git a/spec/features/admin/poll/questions/answers/videos/videos_spec.rb b/spec/features/admin/poll/questions/answers/videos/videos_spec.rb new file mode 100644 index 000000000..35d07f454 --- /dev/null +++ b/spec/features/admin/poll/questions/answers/videos/videos_spec.rb @@ -0,0 +1,33 @@ +require 'rails_helper' + +feature 'Videos' do + + background do + admin = create(:administrator) + login_as(admin.user) + end + + scenario "Create" do + question = create(:poll_question) + answer = create(:poll_question_answer, question: question) + video_title = "'Magical' by Junko Ohashi" + video_url = "https://www.youtube.com/watch?v=-JMf43st-1A" + + visit admin_question_path(question) + + within("#poll_question_answer_#{answer.id}") do + click_link "Video list" + end + + click_link "Add video" + + fill_in 'poll_question_answer_video_title', with: video_title + fill_in 'poll_question_answer_video_url', with: video_url + + click_button "Save" + + expect(page).to have_content(video_title) + expect(page).to have_content(video_url) + end + +end diff --git a/spec/features/admin/poll/questions_spec.rb b/spec/features/admin/poll/questions_spec.rb index 7e103c647..5c8cba9c1 100644 --- a/spec/features/admin/poll/questions_spec.rb +++ b/spec/features/admin/poll/questions_spec.rb @@ -111,22 +111,4 @@ feature 'Admin poll questions' do pending "Mark all city by default when creating a poll question from a successful proposal" - it_behaves_like "nested documentable", - "administrator", - "poll_question", - "new_admin_question_path", - { }, - "documentable_fill_new_valid_poll_question", - "Save", - "Star Wars: Episode IV - A New Hope" - - it_behaves_like "nested documentable", - "administrator", - "poll_question", - "edit_admin_question_path", - { "id": "id" }, - nil, - "Save", - "Changes saved" - end diff --git a/spec/features/polls/polls_spec.rb b/spec/features/polls/polls_spec.rb index 76932558b..9fdb07255 100644 --- a/spec/features/polls/polls_spec.rb +++ b/spec/features/polls/polls_spec.rb @@ -94,6 +94,9 @@ feature 'Polls' do end scenario 'Level 1 users' do + visit polls_path + expect(page).to_not have_selector('.already-answer') + poll.update(geozone_restricted: true) poll.geozones << geozone @@ -209,8 +212,7 @@ feature 'Polls' do visit poll_path(poll) expect(page).to have_link('Han Solo') - expect(page).to_not have_link('Chewbacca') - expect(page).to have_content('Chewbacca') + expect(page).to have_link('Chewbacca') end scenario 'Level 2 users answering', :js do @@ -256,82 +258,79 @@ feature 'Polls' do expect(page).to have_link('Han Solo') end - scenario 'User can write comments' do - create(:comment, commentable: poll) + scenario 'Level 2 votes, signs out, signs in, votes again', :js do + poll.update(geozone_restricted: true) + poll.geozones << geozone + 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 visit poll_path(poll) + click_link 'Han Solo' - expect(page).to have_css('.comment', count: 1) + expect(page).to_not have_link('Han Solo') + expect(page).to have_link('Chewbacca') - comment = Comment.last - within first('.comment') do - expect(page).to have_content comment.user.name - expect(page).to have_content I18n.l(comment.created_at, format: :datetime) - expect(page).to have_content comment.body - end - end - - scenario 'user b can access to comments from user a' do - oliver = create(:user, username: 'Oliver Atom') - benji = create(:user, username: 'Benji Prince') - create(:comment, commentable: poll, user: oliver) - - login_as(benji) + click_link "Sign out" + login_as user visit poll_path(poll) + click_link 'Han Solo' - expect(page).to have_content oliver.username - end + expect(page).to_not have_link('Han Solo') + expect(page).to have_link('Chewbacca') - scenario 'user b can reply to comments from user a', :js do - koji = create(:user, username: 'Koji Kabuto') - sayaka = create(:user, username: 'Sayaka') - comment = create(:comment, commentable: poll, user: koji) - - login_as(sayaka) + click_link "Sign out" + login_as user visit poll_path(poll) + click_link 'Chewbacca' - click_link "Reply" - - within "#js-comment-form-comment_#{comment.id}" do - fill_in "comment-body-comment_#{comment.id}", with: 'MAZINGER!!.' - click_button 'Publish reply' - end - - within "#comment_#{comment.id}" do - expect(page).to have_content 'MAZINGER!!.' - end - - expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true) - end - - scenario 'user can upvote a comment', :js do - goku = create(:user, username: 'Goku') - vegeta = create(:user, username: 'Vegeta') - comment = create(:comment, commentable: poll, user: goku) - - login_as(vegeta) - visit poll_path(poll) - - within("#comment_#{comment.id}_votes") do - find('.in_favor a').click - - expect(page).to have_content "1 vote" - end - end - - scenario 'user can downvote a comment', :js do - doraemon = create(:user, username: 'Doraemon') - nobita = create(:user, username: 'Nobi Nobita') - comment = create(:comment, commentable: poll, user: doraemon) - - login_as(nobita) - visit poll_path(poll) - - within("#comment_#{comment.id}_votes") do - find('.against a').click - - expect(page).to have_content "1 vote" - end + expect(page).to_not have_link('Chewbacca') + expect(page).to have_link('Han Solo') end end + + context 'Booth & Website' do + + let(:poll) { create(:poll, summary: "Summary", description: "Description") } + let(:booth) { create(:poll_booth) } + let(:officer) { create(:poll_officer) } + + scenario 'Already voted on booth cannot vote on website', :js do + + create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :vote_collection) + booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth) + create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment) + question = create(:poll_question, poll: poll) + create(:poll_question_answer, question: question, title: 'Han Solo') + create(:poll_question_answer, question: question, title: 'Chewbacca') + user = create(:user, :level_two, :in_census) + + login_as(officer.user) + visit new_officing_residence_path + officing_verify_residence + click_button "Confirm vote" + + expect(page).to have_content "Vote introduced!" + + visit new_officing_residence_path + click_link "Sign out" + login_as user + visit poll_path(poll) + + expect(page).to have_content "You have already participated in a physical booth. You can not participate again." + + within("#poll_question_#{question.id}_answers") do + expect(page).to have_content('Han Solo') + expect(page).to have_content('Chewbacca') + + expect(page).to_not have_link('Han Solo') + expect(page).to_not have_link('Chewbacca') + end + end + + end end diff --git a/spec/features/polls/voter_spec.rb b/spec/features/polls/voter_spec.rb index 453924674..256807928 100644 --- a/spec/features/polls/voter_spec.rb +++ b/spec/features/polls/voter_spec.rb @@ -32,6 +32,11 @@ feature "Voter" do expect(page).to_not have_link('Yes') end + find(:css, ".js-token-message").should be_visible + token = find(:css, ".js-question-answer")[:href].gsub(/.+?(?=token)/, '').gsub('token=', '') + + expect(page).to have_content "You can write down this vote identifier, to check your vote on the final results: #{token}" + expect(Poll::Voter.count).to eq(1) expect(Poll::Voter.first.origin).to eq("web") end @@ -91,9 +96,34 @@ feature "Voter" do visit poll_path(poll) expect(page).to_not have_link('Yes') - expect(page).to have_content "You have already participated in a booth for this poll." + expect(page).to have_content "You have already participated in a physical booth. You can not participate again." expect(Poll::Voter.count).to eq(1) end + + scenario "Trying to vote in web again", :js do + login_as user + vote_for_poll_via_web(poll, question) + + visit poll_path(poll) + + expect(page).to_not have_selector('.js-token-message') + + expect(page).to have_content "You have already participated in this poll. If you vote again it will be overwritten." + within("#poll_question_#{question.id}_answers") do + expect(page).to_not have_link('Yes') + end + + click_link "Sign out" + + login_as user + visit poll_path(poll) + + within("#poll_question_#{question.id}_answers") do + expect(page).to have_link('Yes') + expect(page).to have_link('No') + end + + end end end diff --git a/spec/models/poll/answer_spec.rb b/spec/models/poll/answer_spec.rb index 2c27bc060..8731445cc 100644 --- a/spec/models/poll/answer_spec.rb +++ b/spec/models/poll/answer_spec.rb @@ -46,7 +46,7 @@ describe Poll::Answer do answer = create(:poll_answer, question: question, author: author, answer: "Yes") expect(answer.poll.voters).to be_blank - answer.record_voter_participation + answer.record_voter_participation('token') expect(poll.reload.voters.size).to eq(1) voter = poll.voters.first @@ -57,12 +57,12 @@ describe Poll::Answer do it "updates a poll_voter with user and poll data" do answer = create(:poll_answer, question: question, author: author, answer: "Yes") - answer.record_voter_participation + answer.record_voter_participation('token') expect(poll.reload.voters.size).to eq(1) answer = create(:poll_answer, question: question, author: author, answer: "No") - answer.record_voter_participation + answer.record_voter_participation('token') expect(poll.reload.voters.size).to eq(1) diff --git a/spec/models/poll/voter_spec.rb b/spec/models/poll/voter_spec.rb index f306248dc..ae0f84a49 100644 --- a/spec/models/poll/voter_spec.rb +++ b/spec/models/poll/voter_spec.rb @@ -76,7 +76,7 @@ describe :voter do it "should not be valid if the user has voted via web" do answer = create(:poll_answer) - answer.record_voter_participation + answer.record_voter_participation('token') voter = build(:poll_voter, poll: answer.question.poll, user: answer.author) expect(voter).to_not be_valid @@ -162,11 +162,12 @@ describe :voter do it "sets user info" do user = create(:user, document_number: "1234A", document_type: "1") - voter = build(:poll_voter, user: user) + voter = build(:poll_voter, user: user, token: "1234abcd") voter.save expect(voter.document_number).to eq("1234A") expect(voter.document_type).to eq("1") + expect(voter.token).to eq("1234abcd") end end -end \ No newline at end of file +end diff --git a/spec/shared/features/nested_documentable.rb b/spec/shared/features/nested_documentable.rb index 8780936a7..24d6631a8 100644 --- a/spec/shared/features/nested_documentable.rb +++ b/spec/shared/features/nested_documentable.rb @@ -191,9 +191,17 @@ shared_examples "nested documentable" do |login_as_name, documentable_factory_na documentable_attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf") click_on submit_button + documentable_redirected_to_resource_show_or_navigate_to - expect(page).to have_content "Documents (1)" + expect(page).to have_content "Documents" + + find("#tab-documents-label").click + expect(page).to have_content "empty.pdf" + + #Review + #Doble check why the file is stored with a name different to empty.pdf + expect(page).to have_css("a[href$='.pdf']") end scenario "Should show resource with new document after successful creation with maximum allowed uploaded files", :js do @@ -263,7 +271,6 @@ shared_examples "nested documentable" do |login_as_name, documentable_factory_na end - end end @@ -275,7 +282,7 @@ rescue return end -def documentable_attach_new_file(documentable_factory_name, index, path, success = true) +def documentable_attach_new_file(_documentable_factory_name, index, path, success = true) click_link "Add new document" document = all(".document")[index] document_input = document.find("input[type=file]", visible: false) @@ -318,8 +325,3 @@ def documentable_fill_new_valid_budget_investment fill_in_ckeditor "budget_investment_description", with: "Budget investment description" check :budget_investment_terms_of_service end - -def documentable_fill_new_valid_poll_question - page.select documentable.poll.name, from: 'poll_question_poll_id' - fill_in 'poll_question_title', with: "Star Wars: Episode IV - A New Hope" -end diff --git a/spec/shared/features/nested_imageable.rb b/spec/shared/features/nested_imageable.rb index bd519a5bb..1eef9a269 100644 --- a/spec/shared/features/nested_imageable.rb +++ b/spec/shared/features/nested_imageable.rb @@ -146,7 +146,11 @@ shared_examples "nested imageable" do |imageable_factory_name, path, imageable_p send(fill_resource_method_name) if fill_resource_method_name click_on submit_button - expect(page).to have_content imageable_success_notice + if has_many_images + skip "no need to test, there are no attributes for the parent resource" + else + expect(page).to have_content imageable_success_notice + end end scenario "Should show successful notice when resource filled correctly and after valid file uploads", :js do