diff --git a/app/components/legislation/proposals/votes_component.rb b/app/components/legislation/proposals/votes_component.rb
index 96495800f..10d16e74f 100644
--- a/app/components/legislation/proposals/votes_component.rb
+++ b/app/components/legislation/proposals/votes_component.rb
@@ -1,6 +1,6 @@
class Legislation::Proposals::VotesComponent < ApplicationComponent
attr_reader :proposal
- delegate :current_user, :link_to_verify_account, to: :helpers
+ delegate :current_user, :link_to_verify_account, :can?, to: :helpers
def initialize(proposal)
@proposal = proposal
@@ -9,7 +9,7 @@ class Legislation::Proposals::VotesComponent < ApplicationComponent
private
def can_vote?
- proposal.votable_by?(current_user)
+ can?(:create, proposal.votes_for.new(voter: current_user))
end
def cannot_vote_text
diff --git a/app/components/shared/in_favor_against_component.html.erb b/app/components/shared/in_favor_against_component.html.erb
index be4040983..c4c403649 100644
--- a/app/components/shared/in_favor_against_component.html.erb
+++ b/app/components/shared/in_favor_against_component.html.erb
@@ -1,6 +1,6 @@
- <%= button_to polymorphic_path(votable, action: :vote, value: "yes"),
+ <%= button_to vote_in_favor_against_path("yes"),
title: t("votes.agree"),
"aria-label": agree_aria_label,
"aria-pressed": pressed?("yes"),
@@ -12,7 +12,7 @@
- <%= button_to polymorphic_path(votable, action: :vote, value: "no"),
+ <%= button_to vote_in_favor_against_path("no"),
title: t("votes.disagree"),
"aria-label": disagree_aria_label,
"aria-pressed": pressed?("no"),
diff --git a/app/components/shared/in_favor_against_component.rb b/app/components/shared/in_favor_against_component.rb
index a8561e458..0c0736407 100644
--- a/app/components/shared/in_favor_against_component.rb
+++ b/app/components/shared/in_favor_against_component.rb
@@ -26,4 +26,12 @@ class Shared::InFavorAgainstComponent < ApplicationComponent
false
end
end
+
+ def vote_in_favor_against_path(value)
+ if votable.class.name == "Debate"
+ debate_votes_path(votable, value: value)
+ else
+ legislation_process_proposal_votes_path(votable.process, votable, value: value)
+ end
+ end
end
diff --git a/app/controllers/debates/votes_controller.rb b/app/controllers/debates/votes_controller.rb
new file mode 100644
index 000000000..e0b92c472
--- /dev/null
+++ b/app/controllers/debates/votes_controller.rb
@@ -0,0 +1,15 @@
+module Debates
+ class VotesController < ApplicationController
+ before_action :authenticate_user!
+ load_and_authorize_resource :debate
+
+ def create
+ authorize! :create, Vote.new(voter: current_user, votable: @debate)
+ @debate.register_vote(current_user, params[:value])
+
+ respond_to do |format|
+ format.js { render :show }
+ end
+ end
+ end
+end
diff --git a/app/controllers/debates_controller.rb b/app/controllers/debates_controller.rb
index c08963a6c..33c7fd899 100644
--- a/app/controllers/debates_controller.rb
+++ b/app/controllers/debates_controller.rb
@@ -28,10 +28,6 @@ class DebatesController < ApplicationController
redirect_to debate_path(@debate), status: :moved_permanently if request.path != debate_path(@debate)
end
- def vote
- @debate.register_vote(current_user, params[:value])
- end
-
def unmark_featured
@debate.update!(featured_at: nil)
redirect_to debates_path
diff --git a/app/controllers/legislation/proposals/votes_controller.rb b/app/controllers/legislation/proposals/votes_controller.rb
new file mode 100644
index 000000000..03886c41c
--- /dev/null
+++ b/app/controllers/legislation/proposals/votes_controller.rb
@@ -0,0 +1,18 @@
+module Legislation
+ module Proposals
+ class VotesController < ApplicationController
+ before_action :authenticate_user!
+ load_and_authorize_resource :process, class: "Legislation::Process"
+ load_and_authorize_resource :proposal, class: "Legislation::Proposal", through: :process
+
+ def create
+ authorize! :create, Vote.new(voter: current_user, votable: @proposal)
+ @proposal.vote_by(voter: current_user, vote: params[:value])
+
+ respond_to do |format|
+ format.js { render :show }
+ end
+ end
+ end
+ end
+end
diff --git a/app/controllers/legislation/proposals_controller.rb b/app/controllers/legislation/proposals_controller.rb
index 86b34bce0..d9adb14e3 100644
--- a/app/controllers/legislation/proposals_controller.rb
+++ b/app/controllers/legislation/proposals_controller.rb
@@ -37,10 +37,6 @@ class Legislation::ProposalsController < Legislation::BaseController
end
end
- def vote
- @proposal.register_vote(current_user, params[:value])
- end
-
private
def proposal_params
diff --git a/app/models/abilities/common.rb b/app/models/abilities/common.rb
index fe96be60b..53378b4be 100644
--- a/app/models/abilities/common.rb
+++ b/app/models/abilities/common.rb
@@ -81,14 +81,15 @@ module Abilities
can [:create, :destroy], DirectUpload
unless user.organization?
- can :vote, Debate
+ can :create, ActsAsVotable::Vote, voter_id: user.id, votable_type: "Debate"
can :vote, Comment
end
if user.level_two_or_three_verified?
can :vote, Proposal, &:published?
- can :vote, Legislation::Proposal
+ can :create, ActsAsVotable::Vote, voter_id: user.id, votable_type: "Legislation::Proposal"
+
can :create, Legislation::Answer
can :create, Budget::Investment, budget: { phase: "accepting" }
diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb
index 086a4265b..1808d8033 100644
--- a/app/models/budget/investment.rb
+++ b/app/models/budget/investment.rb
@@ -286,7 +286,7 @@ class Budget
def permission_problem(user)
return :not_logged_in unless user
return :organization if user.organization?
- return :not_verified unless user.can?(:create, ActsAsVotable::Vote)
+ return :not_verified unless user.level_two_or_three_verified?
nil
end
diff --git a/app/models/legislation/proposal.rb b/app/models/legislation/proposal.rb
index ec653d4c3..497cea5db 100644
--- a/app/models/legislation/proposal.rb
+++ b/app/models/legislation/proposal.rb
@@ -108,14 +108,6 @@ class Legislation::Proposal < ApplicationRecord
author_id == user.id && editable?
end
- def votable_by?(user)
- user&.level_two_or_three_verified?
- end
-
- def register_vote(user, vote_value)
- vote_by(voter: user, vote: vote_value) if votable_by?(user)
- end
-
def code
"#{Setting["proposal_code_prefix"]}-#{created_at.strftime("%Y-%m")}-#{id}"
end
diff --git a/app/views/debates/vote.js.erb b/app/views/debates/votes/show.js.erb
similarity index 100%
rename from app/views/debates/vote.js.erb
rename to app/views/debates/votes/show.js.erb
diff --git a/app/views/legislation/proposals/vote.js.erb b/app/views/legislation/proposals/votes/show.js.erb
similarity index 100%
rename from app/views/legislation/proposals/vote.js.erb
rename to app/views/legislation/proposals/votes/show.js.erb
diff --git a/config/routes/debate.rb b/config/routes/debate.rb
index 58f1f9215..45d6f06f6 100644
--- a/config/routes/debate.rb
+++ b/config/routes/debate.rb
@@ -1,6 +1,5 @@
resources :debates do
member do
- post :vote
put :flag
put :unflag
put :mark_featured
@@ -11,4 +10,6 @@ resources :debates do
get :suggest
put "recommendations/disable", only: :index, controller: "debates", action: :disable_recommendations
end
+
+ resources :votes, controller: "debates/votes", only: :create
end
diff --git a/config/routes/legislation.rb b/config/routes/legislation.rb
index 4a18ef1cd..7e3890c42 100644
--- a/config/routes/legislation.rb
+++ b/config/routes/legislation.rb
@@ -16,13 +16,14 @@ namespace :legislation do
resources :proposals, except: [:index] do
member do
- post :vote
put :flag
put :unflag
end
collection do
get :suggest
end
+
+ resources :votes, controller: "proposals/votes", only: :create
end
resources :draft_versions, only: [:show] do
diff --git a/spec/components/shared/in_favor_against_component_spec.rb b/spec/components/shared/in_favor_against_component_spec.rb
index 32d88f574..ece805749 100644
--- a/spec/components/shared/in_favor_against_component_spec.rb
+++ b/spec/components/shared/in_favor_against_component_spec.rb
@@ -6,6 +6,22 @@ describe Shared::InFavorAgainstComponent do
let(:user) { create(:user) }
describe "Agree and disagree buttons" do
+ it "can create a vote when the user has not yet voted" do
+ sign_in user
+
+ render_inline component
+
+ page.find(".in-favor") do |in_favor_block|
+ expect(in_favor_block).to have_css "form[action*='votes'][method='post']"
+ expect(in_favor_block).not_to have_css "input[name='_method']", visible: :all
+ end
+
+ page.find(".against") do |against_block|
+ expect(against_block).to have_css "form[action*='votes'][method='post']"
+ expect(against_block).not_to have_css "input[name='_method']", visible: :all
+ end
+ end
+
it "does not include result percentages" do
create(:vote, votable: debate)
sign_in(user)
diff --git a/spec/controllers/debates/votes_controller_spec.rb b/spec/controllers/debates/votes_controller_spec.rb
new file mode 100644
index 000000000..9e6090ee6
--- /dev/null
+++ b/spec/controllers/debates/votes_controller_spec.rb
@@ -0,0 +1,43 @@
+require "rails_helper"
+
+describe Debates::VotesController do
+ describe "POST create" do
+ it "does not authorize unauthenticated users" do
+ debate = create(:debate)
+
+ post :create, xhr: true, params: { debate_id: debate.id, value: "yes" }
+
+ expect(response).to be_unauthorized
+ end
+
+ it "redirects unauthenticated users without JavaScript to the sign in page" do
+ debate = create(:debate)
+
+ post :create, params: { debate_id: debate.id, value: "yes" }
+
+ expect(response).to redirect_to new_user_session_path
+ end
+
+ describe "Vote with too many anonymous votes" do
+ it "allows vote if user is allowed" do
+ Setting["max_ratio_anon_votes_on_debates"] = 100
+ debate = create(:debate)
+ sign_in create(:user)
+
+ expect do
+ post :create, xhr: true, params: { debate_id: debate.id, value: "yes" }
+ end.to change { debate.reload.votes_for.size }.by(1)
+ end
+
+ it "does not allow voting if user is not allowed" do
+ Setting["max_ratio_anon_votes_on_debates"] = 0
+ debate = create(:debate, cached_votes_total: 1000)
+ sign_in create(:user)
+
+ expect do
+ post :create, xhr: true, params: { debate_id: debate.id, value: "yes" }
+ end.not_to change { debate.reload.votes_for.size }
+ end
+ end
+ end
+end
diff --git a/spec/controllers/debates_controller_spec.rb b/spec/controllers/debates_controller_spec.rb
index 01af1db82..c3d96dcea 100644
--- a/spec/controllers/debates_controller_spec.rb
+++ b/spec/controllers/debates_controller_spec.rb
@@ -37,28 +37,6 @@ describe DebatesController do
end
end
- describe "Vote with too many anonymous votes" do
- it "allows vote if user is allowed" do
- Setting["max_ratio_anon_votes_on_debates"] = 100
- debate = create(:debate)
- sign_in create(:user)
-
- expect do
- post :vote, xhr: true, params: { id: debate.id, value: "yes" }
- end.to change { debate.reload.votes_for.size }.by(1)
- end
-
- it "does not allow vote if user is not allowed" do
- Setting["max_ratio_anon_votes_on_debates"] = 0
- debate = create(:debate, cached_votes_total: 1000)
- sign_in create(:user)
-
- expect do
- post :vote, xhr: true, params: { id: debate.id, value: "yes" }
- end.not_to change { debate.reload.votes_for.size }
- end
- end
-
describe "PUT mark_featured" do
it "ignores query parameters" do
debate = create(:debate)
diff --git a/spec/controllers/legislation/proposals/votes_controller_spec.rb b/spec/controllers/legislation/proposals/votes_controller_spec.rb
new file mode 100644
index 000000000..fd8c780a6
--- /dev/null
+++ b/spec/controllers/legislation/proposals/votes_controller_spec.rb
@@ -0,0 +1,40 @@
+require "rails_helper"
+
+describe Legislation::Proposals::VotesController do
+ let(:legislation_process) { create(:legislation_process) }
+ let(:proposal) { create(:legislation_proposal, process: legislation_process) }
+
+ describe "POST create" do
+ let(:vote_params) do
+ { process_id: legislation_process.id, legislation_proposal_id: proposal.id, value: "yes" }
+ end
+
+ it "does not authorize unauthenticated users" do
+ post :create, xhr: true, params: vote_params
+
+ expect(response).to be_unauthorized
+ end
+
+ it "redirects unauthenticated users without JavaScript to the sign in page" do
+ post :create, params: vote_params
+
+ expect(response).to redirect_to new_user_session_path
+ end
+
+ it "allows vote if user is level_two_or_three_verified" do
+ sign_in create(:user, :level_two)
+
+ expect do
+ post :create, xhr: true, params: vote_params
+ end.to change { proposal.reload.votes_for.size }.by(1)
+ end
+
+ it "does not allow voting if user is not level_two_or_three_verified" do
+ sign_in create(:user)
+
+ expect do
+ post :create, xhr: true, params: vote_params
+ end.not_to change { proposal.reload.votes_for.size }
+ end
+ end
+end
diff --git a/spec/models/abilities/administrator_spec.rb b/spec/models/abilities/administrator_spec.rb
index 39a42dbd6..7fb48b225 100644
--- a/spec/models/abilities/administrator_spec.rb
+++ b/spec/models/abilities/administrator_spec.rb
@@ -48,7 +48,6 @@ describe Abilities::Administrator do
it { should be_able_to(:index, Debate) }
it { should be_able_to(:show, debate) }
- it { should be_able_to(:vote, debate) }
it { should be_able_to(:index, Proposal) }
it { should be_able_to(:show, proposal) }
diff --git a/spec/models/abilities/common_spec.rb b/spec/models/abilities/common_spec.rb
index 6c3918ec8..9ba947776 100644
--- a/spec/models/abilities/common_spec.rb
+++ b/spec/models/abilities/common_spec.rb
@@ -7,6 +7,7 @@ describe Abilities::Common do
let(:geozone) { create(:geozone) }
let(:user) { create(:user, geozone: geozone) }
+ let(:another_user) { create(:user) }
let(:debate) { create(:debate) }
let(:comment) { create(:comment) }
@@ -15,6 +16,7 @@ describe Abilities::Common do
let(:own_comment) { create(:comment, author: user) }
let(:own_proposal) { create(:proposal, author: user) }
let(:own_legislation_proposal) { create(:legislation_proposal, author: user) }
+ let(:legislation_proposal) { create(:legislation_proposal) }
let(:accepting_budget) { create(:budget, :accepting) }
let(:reviewing_budget) { create(:budget, :reviewing) }
@@ -73,7 +75,8 @@ describe Abilities::Common do
it { should be_able_to(:index, Debate) }
it { should be_able_to(:show, debate) }
- it { should be_able_to(:vote, debate) }
+ it { should be_able_to(:create, user.votes.build(votable: debate)) }
+ it { should_not be_able_to(:create, another_user.votes.build(votable: debate)) }
it { should be_able_to(:show, user) }
it { should be_able_to(:edit, user) }
@@ -137,20 +140,16 @@ describe Abilities::Common do
end
describe "follows" do
- let(:other_user) { create(:user) }
-
it { should be_able_to(:create, build(:follow, :followed_proposal, user: user)) }
- it { should_not be_able_to(:create, build(:follow, :followed_proposal, user: other_user)) }
+ it { should_not be_able_to(:create, build(:follow, :followed_proposal, user: another_user)) }
it { should be_able_to(:destroy, create(:follow, :followed_proposal, user: user)) }
- it { should_not be_able_to(:destroy, create(:follow, :followed_proposal, user: other_user)) }
+ it { should_not be_able_to(:destroy, create(:follow, :followed_proposal, user: another_user)) }
end
describe "other users" do
- let(:other_user) { create(:user) }
-
- it { should be_able_to(:show, other_user) }
- it { should_not be_able_to(:edit, other_user) }
+ it { should be_able_to(:show, another_user) }
+ it { should_not be_able_to(:edit, another_user) }
end
describe "editing debates" do
@@ -182,6 +181,18 @@ describe Abilities::Common do
it { should_not be_able_to(:edit, own_legislation_proposal) }
it { should_not be_able_to(:update, own_legislation_proposal) }
+ describe "vote legislation proposal" do
+ context "when user is not level_two_or_three_verified" do
+ it { should_not be_able_to(:create, user.votes.build(votable: legislation_proposal)) }
+ end
+
+ context "when user is level_two_or_three_verified" do
+ before { user.update(level_two_verified_at: Date.current) }
+ it { should be_able_to(:create, user.votes.build(votable: legislation_proposal)) }
+ it { should_not be_able_to(:create, another_user.votes.build(votable: legislation_proposal)) }
+ end
+ end
+
describe "proposals dashboard" do
it { should be_able_to(:dashboard, own_proposal) }
it { should_not be_able_to(:dashboard, proposal) }
diff --git a/spec/models/abilities/moderator_spec.rb b/spec/models/abilities/moderator_spec.rb
index b9df69067..9dd4f4c0c 100644
--- a/spec/models/abilities/moderator_spec.rb
+++ b/spec/models/abilities/moderator_spec.rb
@@ -25,7 +25,6 @@ describe Abilities::Moderator do
it { should be_able_to(:index, Debate) }
it { should be_able_to(:show, debate) }
- it { should be_able_to(:vote, debate) }
it { should be_able_to(:index, Proposal) }
it { should be_able_to(:show, proposal) }
diff --git a/spec/models/abilities/organization_spec.rb b/spec/models/abilities/organization_spec.rb
index 874a5ce40..9bfd9b69f 100644
--- a/spec/models/abilities/organization_spec.rb
+++ b/spec/models/abilities/organization_spec.rb
@@ -14,7 +14,7 @@ describe "Abilities::Organization" do
it { should be_able_to(:index, Debate) }
it { should be_able_to(:show, debate) }
- it { should_not be_able_to(:vote, debate) }
+ it { should_not be_able_to(:create, user.votes.build(votable: debate)) }
it { should be_able_to(:index, Proposal) }
it { should be_able_to(:show, proposal) }