Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't allow modifying already started polls #4904

Merged
merged 18 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/components/admin/allowed_table_actions_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= render table_actions_component do %>
<%= content %>
<% end %>
20 changes: 20 additions & 0 deletions app/components/admin/allowed_table_actions_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class Admin::AllowedTableActionsComponent < ApplicationComponent
attr_reader :record, :options
delegate :can?, to: :helpers
delegate :action, to: :table_actions_component

def initialize(record, **options)
@record = record
@options = options
end

private

def actions
(options[:actions] || [:edit, :destroy]).select { |action| can?(action, record) }
end

def table_actions_component
@table_actions_component ||= Admin::TableActionsComponent.new(record, **options.merge(actions: actions))
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,38 @@
</ul>

<div class="poll-question-form">
<%= form_for(Poll::Question::Answer.new, url: admin_answer_documents_path(answer)) do |f| %>
<%= render "shared/errors", resource: answer %>

<div class="documents">
<%= render "documents/nested_documents", f: f %>
</div>

<div class="small-12 medium-6 large-2">
<%= f.submit(class: "button expanded", value: t("shared.save")) %>
<% if can?(:update, @answer) %>
<%= form_for(Poll::Question::Answer.new, url: admin_answer_documents_path(answer)) do |f| %>
<%= render "shared/errors", resource: answer %>

<div class="documents">
<%= render "documents/nested_documents", f: f %>
</div>

<div class="small-12 medium-6 large-2">
<%= f.submit(class: "button expanded", value: t("shared.save")) %>
</div>
<% end %>
<% else %>
<div class="callout warning">
<strong><%= t("admin.questions.no_edit") %></strong>
</div>
<% end %>

<% if answer.documents.present? %>
<% if documents.present? %>
<table>
<tr>
<th scope="col"><%= t("admin.questions.show.answers.document_title") %></th>
<th scope="col"><%= t("admin.questions.show.answers.document_actions") %></th>
</tr>

<% answer.documents.each do |document| %>
<% documents.each do |document| %>
<tr>
<td>
<%= link_to document.title, document.attachment %>
</td>
<td>
<%= render Admin::TableActionsComponent.new(document,
actions: [:destroy],
destroy_path: document_path(document)
) do |actions| %>
<%= actions.action(:download,
text: t("documents.buttons.download_document"),
path: document.attachment,
target: "_blank",
rel: "nofollow") %>

<% end %>
<%= render Admin::Poll::Questions::Answers::Documents::TableActionsComponent.new(document) %>
</td>
</tr>
<% end %>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
class Admin::Poll::Questions::Answers::Documents::IndexComponent < ApplicationComponent
attr_reader :answer
delegate :can?, to: :helpers

def initialize(answer)
@answer = answer
end

private

def documents
@documents ||= @answer.class.find(@answer.id).documents
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<%= render Admin::AllowedTableActionsComponent.new(document,
destroy_path: document_path(document)) do |actions| %>
<%= actions.action(:download,
text: t("documents.buttons.download_document"),
path: document.attachment,
target: "_blank",
rel: "nofollow") %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Admin::Poll::Questions::Answers::Documents::TableActionsComponent < ApplicationComponent
attr_reader :document

def initialize(document)
@document = document
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= render Admin::AllowedTableActionsComponent.new(answer) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Admin::Poll::Questions::Answers::TableActionsComponent < ApplicationComponent
attr_reader :answer

def initialize(answer)
@answer = answer
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= render Admin::AllowedTableActionsComponent.new(video) %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Admin::Poll::Questions::Answers::Videos::TableActionsComponent < ApplicationComponent
attr_reader :video

def initialize(video)
@video = video
end
end
38 changes: 38 additions & 0 deletions app/components/admin/poll/questions/form_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<%= render "shared/globalize_locales", resource: question %>

<%= translatable_form_for(question, url: url) do |f| %>

<%= render "shared/errors", resource: question %>

<%= f.hidden_field :proposal_id %>

<div class="row">
<div class="small-12">
<% if question.poll.present? %>
<%= f.hidden_field :poll_id, value: question.poll.id %>
<% else %>
<div class="small-12 medium-6 large-4 column">
<%= f.select :poll_id,
options_for_select(select_options),
prompt: t("admin.questions.index.select_poll"),
hint: t("admin.questions.form.poll_help") %>
</div>
<% end %>
</div>
</div>

<div class="row">
<%= f.translatable_fields do |translations_form| %>
<div class="column">
<%= translations_form.text_field :title %>
</div>
<% end %>
</div>

<div class="row">
<div class="small-12 medium-4 large-2 margin-top column">
<%= f.submit(class: "button success expanded", value: t("shared.save")) %>
</div>
</div>

<% end %>
19 changes: 19 additions & 0 deletions app/components/admin/poll/questions/form_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Admin::Poll::Questions::FormComponent < ApplicationComponent
include TranslatableFormHelper
include GlobalizeHelper
attr_reader :question, :url
delegate :can?, to: :helpers

def initialize(question, url:)
@question = question
@url = url
end

private

def select_options
Poll.all.select { |poll| can?(:create, Poll::Question.new(poll: poll)) }.map do |poll|
[poll.name, poll.id]
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= render Admin::AllowedTableActionsComponent.new(question) do |actions| %>
<%= actions.action(:answers, text: t("admin.polls.show.edit_answers")) %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class Admin::Poll::Questions::TableActionsComponent < ApplicationComponent
attr_reader :question

def initialize(question)
@question = question
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ def index

def create
@answer.attributes = documents_params
authorize! :update, @answer

if @answer.save
redirect_to admin_answer_documents_path(@answer),
notice: t("admin.documents.create.success_notice")
else
render :new
render :index
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ class Admin::Poll::Questions::Answers::ImagesController < Admin::Poll::BaseContr
include ImageAttributes

load_and_authorize_resource :answer, class: "::Poll::Question::Answer"
load_and_authorize_resource only: [:destroy]

def index
end
Expand All @@ -11,6 +12,7 @@ def new

def create
@answer.attributes = images_params
authorize! :update, @answer

if @answer.save
redirect_to admin_answer_images_path(@answer),
Expand All @@ -21,7 +23,6 @@ def create
end

def destroy
@image = ::Image.find(params[:id])
@image.destroy!

respond_to do |format|
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/admin/poll/questions/answers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ def update
end
end

def destroy
@answer.destroy!
redirect_to admin_question_path(@question), notice: t("admin.answers.destroy.success_notice")
end

def order_answers
::Poll::Question::Answer.order_answers(params[:ordered_list])
head :ok
Expand Down
6 changes: 5 additions & 1 deletion app/controllers/admin/poll/questions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ class Admin::Poll::QuestionsController < Admin::Poll::BaseController
include Translatable

load_and_authorize_resource :poll
load_and_authorize_resource :question, class: "Poll::Question"
load_resource class: "Poll::Question"
authorize_resource except: [:new, :index]

def index
@polls = Poll.not_budget
Expand All @@ -16,6 +17,9 @@ def new
@polls = Poll.all
proposal = Proposal.find(params[:proposal_id]) if params[:proposal_id].present?
@question.copy_attributes_from_proposal(proposal)
@question.poll = @poll

authorize! :create, @question
end

def create
Expand Down
27 changes: 20 additions & 7 deletions app/models/abilities/administrator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,23 @@ def initialize(user)
can [:search, :create, :index, :destroy], ::Poll::Officer
can [:create, :destroy, :manage], ::Poll::BoothAssignment
can [:create, :destroy], ::Poll::OfficerAssignment
can [:read, :create, :update], Poll::Question
can :destroy, Poll::Question
can :manage, Poll::Question::Answer
can :manage, Poll::Question::Answer::Video
can [:create, :destroy], Image do |image|
image.imageable_type == "Poll::Question::Answer"
can :read, Poll::Question
can [:create], Poll::Question do |question|
question.poll.blank? || !question.poll.started?
end
can [:update, :destroy], Poll::Question do |question|
!question.poll.started?
end
can [:read, :order_answers], Poll::Question::Answer
can [:create, :update, :destroy], Poll::Question::Answer do |answer|
can?(:update, answer.question)
end
can :read, Poll::Question::Answer::Video
can [:create, :update, :destroy], Poll::Question::Answer::Video do |video|
can?(:update, video.answer)
end
can [:destroy], Image do |image|
image.imageable_type == "Poll::Question::Answer" && can?(:update, image.imageable)
end

can :manage, SiteCustomization::Page
Expand All @@ -113,7 +124,9 @@ def initialize(user)
cannot :comment_as_moderator, [::Legislation::Question, Legislation::Annotation, ::Legislation::Proposal]

can [:create], Document
can [:destroy], Document, documentable_type: "Poll::Question::Answer"
can [:destroy], Document do |document|
document.documentable_type == "Poll::Question::Answer" && can?(:update, document.documentable)
end
can [:create, :destroy], DirectUpload

can [:deliver], Newsletter, hidden_at: nil
Expand Down
6 changes: 4 additions & 2 deletions app/models/abilities/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ def initialize(user)
can [:create, :destroy], Follow, user_id: user.id

can [:destroy], Document do |document|
document.documentable&.author_id == user.id
document.documentable_type != "Poll::Question::Answer" && document.documentable&.author_id == user.id
end

can [:destroy], Image, imageable: { author_id: user.id }
can [:destroy], Image do |image|
image.imageable_type != "Poll::Question::Answer" && image.imageable&.author_id == user.id
end

can [:create, :destroy], DirectUpload

Expand Down
36 changes: 36 additions & 0 deletions app/models/poll.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class Poll < ApplicationRecord

validates_translation :name, presence: true
validate :date_range
validate :start_date_is_not_past_date, on: :create
validate :start_date_change, on: :update
validate :end_date_is_not_past_date, on: :update
validate :end_date_change, on: :update
validate :only_one_active, unless: :public?

accepts_nested_attributes_for :questions, reject_if: :all_blank, allow_destroy: true
Expand Down Expand Up @@ -67,6 +71,10 @@ def title
name
end

def started?(timestamp = Time.current)
starts_at.present? && starts_at < timestamp
end

def current?(timestamp = Time.current)
starts_at <= timestamp && timestamp <= ends_at
end
Expand Down Expand Up @@ -143,6 +151,34 @@ def date_range
end
end

def start_date_is_not_past_date
if starts_at.present? && starts_at < Time.current
errors.add(:starts_at, I18n.t("errors.messages.past_date"))
end
end

def start_date_change
if will_save_change_to_starts_at?
if starts_at_in_database < Time.current
errors.add(:starts_at, I18n.t("errors.messages.cannot_change_date.poll_started"))
elsif starts_at < Time.current
errors.add(:starts_at, I18n.t("errors.messages.past_date"))
end
end
end

def end_date_is_not_past_date
if will_save_change_to_ends_at? && ends_at < Time.current
errors.add(:ends_at, I18n.t("errors.messages.past_date"))
end
end

def end_date_change
if will_save_change_to_ends_at? && ends_at_in_database < Time.current
errors.add(:ends_at, I18n.t("errors.messages.cannot_change_date.poll_ended"))
end
end

def generate_slug?
slug.nil?
end
Expand Down
Loading