adds votes to spending proposals
This commit is contained in:
@@ -77,6 +77,10 @@ class ApplicationController < ActionController::Base
|
||||
@proposal_votes = current_user ? current_user.proposal_votes(proposals) : {}
|
||||
end
|
||||
|
||||
def set_spending_proposal_votes(spending_proposals)
|
||||
@spending_proposal_votes = current_user ? current_user.spending_proposal_votes(spending_proposals) : {}
|
||||
end
|
||||
|
||||
def set_comment_flags(comments)
|
||||
@comment_flags = current_user ? current_user.comment_flags(comments) : {}
|
||||
end
|
||||
|
||||
@@ -9,6 +9,8 @@ class SpendingProposalsController < ApplicationController
|
||||
|
||||
feature_flag :spending_proposals
|
||||
|
||||
respond_to :html, :js
|
||||
|
||||
def index
|
||||
end
|
||||
|
||||
@@ -16,6 +18,10 @@ class SpendingProposalsController < ApplicationController
|
||||
@spending_proposal = SpendingProposal.new
|
||||
end
|
||||
|
||||
def show
|
||||
set_spending_proposal_votes(@spending_proposal)
|
||||
end
|
||||
|
||||
def create
|
||||
@spending_proposal = SpendingProposal.new(spending_proposal_params)
|
||||
@spending_proposal.author = current_user
|
||||
@@ -34,6 +40,12 @@ class SpendingProposalsController < ApplicationController
|
||||
redirect_to user_path(current_user, filter: 'spending_proposals'), notice: t('flash.actions.destroy.spending_proposal')
|
||||
end
|
||||
|
||||
def vote
|
||||
@spending_proposal.register_vote(current_user, 'yes')
|
||||
set_spending_proposal_votes(@spending_proposal)
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
def spending_proposal_params
|
||||
|
||||
@@ -43,6 +43,7 @@ module Abilities
|
||||
if user.level_two_or_three_verified?
|
||||
can :vote, Proposal
|
||||
can :vote_featured, Proposal
|
||||
can :vote, SpendingProposal
|
||||
can :create, SpendingProposal
|
||||
can :destroy, SpendingProposal, author_id: user.id
|
||||
end
|
||||
|
||||
@@ -4,6 +4,7 @@ class SpendingProposal < ActiveRecord::Base
|
||||
include Taggable
|
||||
|
||||
apply_simple_captcha
|
||||
acts_as_votable
|
||||
|
||||
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
|
||||
belongs_to :geozone
|
||||
@@ -80,6 +81,10 @@ class SpendingProposal < ActiveRecord::Base
|
||||
valuation_finished
|
||||
end
|
||||
|
||||
def total_votes
|
||||
cached_votes_up
|
||||
end
|
||||
|
||||
def code
|
||||
"#{id}" + (administrator.present? ? "-A#{administrator.id}" : "")
|
||||
end
|
||||
@@ -89,4 +94,14 @@ class SpendingProposal < ActiveRecord::Base
|
||||
update(unfeasible_email_sent_at: Time.now)
|
||||
end
|
||||
|
||||
def votable_by?(user)
|
||||
user && user.level_two_or_three_verified?
|
||||
end
|
||||
|
||||
def register_vote(user, vote_value)
|
||||
if votable_by?(user)
|
||||
vote_by(voter: user, vote: vote_value)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -83,6 +83,11 @@ class User < ActiveRecord::Base
|
||||
voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value }
|
||||
end
|
||||
|
||||
def spending_proposal_votes(spending_proposals)
|
||||
voted = votes.for_spending_proposals(spending_proposals)
|
||||
voted.each_with_object({}) { |v, h| h[v.votable_id] = v.value }
|
||||
end
|
||||
|
||||
def comment_flags(comments)
|
||||
comment_flags = flags.for_comments(comments)
|
||||
comment_flags.each_with_object({}){ |f, h| h[f.flaggable_id] = true }
|
||||
|
||||
51
app/views/_votes.html.erb/_form.html.erb
Normal file
51
app/views/_votes.html.erb/_form.html.erb
Normal file
@@ -0,0 +1,51 @@
|
||||
<%= form_for(@spending_proposal, url: form_url) do |f| %>
|
||||
<%= render 'shared/errors', resource: @spending_proposal %>
|
||||
|
||||
<div class="row">
|
||||
<div class="small-12 column">
|
||||
<%= f.label :title, t("spending_proposals.form.title") %>
|
||||
<%= f.text_field :title, maxlength: SpendingProposal.title_max_length, placeholder: t("spending_proposals.form.title"), label: false %>
|
||||
</div>
|
||||
|
||||
<div class="ckeditor small-12 column">
|
||||
<%= f.label :description, t("spending_proposals.form.description") %>
|
||||
<%= f.cktext_area :description, maxlength: SpendingProposal.description_max_length, ckeditor: { language: I18n.locale }, label: false %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= f.label :external_url, t("spending_proposals.form.external_url") %>
|
||||
<%= f.text_field :external_url, placeholder: t("spending_proposals.form.external_url"), label: false %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= f.label :geozone_id, t("spending_proposals.form.geozone") %>
|
||||
<%= f.select :geozone_id, geozone_select_options, {include_blank: t("geozones.none"), label: false} %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= f.label :association_name, t("spending_proposals.form.association_name_label") %>
|
||||
<%= f.text_field :association_name, placeholder: t("spending_proposals.form.association_name"), label: false %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 column">
|
||||
<% if @spending_proposal.new_record? %>
|
||||
<%= f.label :terms_of_service do %>
|
||||
<%= f.check_box :terms_of_service, title: t('form.accept_terms_title'), label: false %>
|
||||
<span class="checkbox">
|
||||
<%= t("form.accept_terms",
|
||||
policy: link_to(t("form.policy"), "/privacy", target: "blank"),
|
||||
conditions: link_to(t("form.conditions"), "/conditions", target: "blank")).html_safe %>
|
||||
</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= f.simple_captcha input_html: { required: false } %>
|
||||
</div>
|
||||
|
||||
<div class="actions small-12 column">
|
||||
<%= f.submit(class: "button", value: t("spending_proposals.form.submit_buttons.#{action_name}")) %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
16
app/views/_votes.html.erb/index.html.erb
Normal file
16
app/views/_votes.html.erb/index.html.erb
Normal file
@@ -0,0 +1,16 @@
|
||||
<% provide :title do %><%= t('spending_proposals.index.title') %><% end %>
|
||||
<div class="page row-full">
|
||||
<div class="row">
|
||||
<div class="more-information text small-12 medium-8 column">
|
||||
<h1><%= t('spending_proposals.index.title') %></h1>
|
||||
|
||||
<p><%= t('spending_proposals.index.text_html') %></p>
|
||||
|
||||
<% if can? :create, SpendingProposal %>
|
||||
<%= link_to t('spending_proposals.index.create_link'), new_spending_proposal_path, class: 'button' %>
|
||||
<% else %>
|
||||
<p><%= t('spending_proposals.index.verified_only', verify_account: link_to(t('spending_proposals.index.verify_account'), verification_path)).html_safe %></p>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
27
app/views/_votes.html.erb/new.html.erb
Normal file
27
app/views/_votes.html.erb/new.html.erb
Normal file
@@ -0,0 +1,27 @@
|
||||
<div class="spending-proposal-new row margin-top">
|
||||
|
||||
<div class="small-12 medium-9 column">
|
||||
<%= link_to spending_proposals_path, class: "back" do %>
|
||||
<span class="icon-angle-left"></span>
|
||||
<%= t("spending_proposals.new.back_link") %>
|
||||
<% end %>
|
||||
<h1><%= t("spending_proposals.new.start_new") %></h1>
|
||||
<div data-alert class="callout primary">
|
||||
<%= link_to "/spending_proposals_info", title: t('shared.target_blank_html'), target: "_blank" do %>
|
||||
<%= t("spending_proposals.new.more_info")%>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render "spending_proposals/form", form_url: spending_proposals_url %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 medium-3 column">
|
||||
<span class="icon-budget float-right"></span>
|
||||
<h2><%= t("spending_proposals.new.recommendations_title") %></h2>
|
||||
<ul class="recommendations">
|
||||
<li><%= t("spending_proposals.new.recommendation_one") %></li>
|
||||
<li><%= t("spending_proposals.new.recommendation_two") %></li>
|
||||
<li><%= t("spending_proposals.new.recommendation_three") %></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
34
app/views/_votes.html.erb/show.html.erb
Normal file
34
app/views/_votes.html.erb/show.html.erb
Normal file
@@ -0,0 +1,34 @@
|
||||
<% provide :title do %><%= @spending_proposal.title %><% end %>
|
||||
|
||||
<section class="proposal-show">
|
||||
<div id="<%= dom_id(@spending_proposal) %>" class="row">
|
||||
<div class="small-12 medium-9 column">
|
||||
|
||||
<h1><%= @spending_proposal.title %></h1>
|
||||
|
||||
<div class="spending-proposal-info">
|
||||
<%= render '/shared/author_info', resource: @spending_proposal %>
|
||||
|
||||
<span class="bullet"> • </span>
|
||||
<%= l @spending_proposal.created_at.to_date %>
|
||||
<span class="bullet"> • </span>
|
||||
<%= geozone_name(@spending_proposal) %>
|
||||
</div>
|
||||
|
||||
<%= safe_html_with_links @spending_proposal.description.html_safe %>
|
||||
|
||||
<% if @spending_proposal.external_url.present? %>
|
||||
<div class="document-link">
|
||||
<%= text_with_links @spending_proposal.external_url %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="<%= dom_id(@spending_proposal) %>_votes" class="small-12 medium-3 column text-center">
|
||||
<%= render 'votes',
|
||||
{ spending_proposal: @spending_proposal, vote_url: vote_spending_proposal_path(@spending_proposal, value: 'yes') } %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
47
app/views/spending_proposals/_votes.html.erb
Normal file
47
app/views/spending_proposals/_votes.html.erb
Normal file
@@ -0,0 +1,47 @@
|
||||
<div class="supports">
|
||||
|
||||
<span class="total-supports">
|
||||
<%= t("spending_proposals.spending_proposal.supports", count: spending_proposal.total_votes) %>
|
||||
</span>
|
||||
|
||||
<div class="in-favor">
|
||||
<% if voted_for?(@spending_proposal_votes, spending_proposal) %>
|
||||
<div class="supported">
|
||||
<%= t("spending_proposals.spending_proposal.already_supported") %>
|
||||
</div>
|
||||
<% else %>
|
||||
<%= link_to vote_url,
|
||||
class: "button button-support small expanded",
|
||||
title: t('spending_proposals.spending_proposal.support_title'), method: "post", remote: true do %>
|
||||
<%= t("spending_proposals.spending_proposal.support") %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if user_signed_in? && current_user.organization? %>
|
||||
<div class="organizations-votes" style='display:none'>
|
||||
<p>
|
||||
<%= t("votes.organizations") %>
|
||||
</p>
|
||||
</div>
|
||||
<% elsif user_signed_in? && !spending_proposal.votable_by?(current_user)%>
|
||||
<div class="anonymous-votes" style='display:none'>
|
||||
<p>
|
||||
<%= t("votes.verified_only",
|
||||
verify_account: link_to(t("votes.verify_account"), verification_path )).html_safe %>
|
||||
</p>
|
||||
</div>
|
||||
<% elsif !user_signed_in? %>
|
||||
<div class="not-logged" style='display:none'>
|
||||
<%= t("votes.unauthenticated",
|
||||
signin: link_to(t("votes.signin"), new_user_session_path),
|
||||
signup: link_to(t("votes.signup"), new_user_registration_path)).html_safe %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% if voted_for?(@spending_proposal_votes, spending_proposal) && setting['twitter_handle'] %>
|
||||
<div class="share-supported">
|
||||
<%= social_share_button_tag(spending_proposal.title, url: spending_proposal_url(spending_proposal), via: setting['twitter_handle']) %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -25,5 +25,10 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div id="<%= dom_id(@spending_proposal) %>_votes" class="small-12 medium-3 column text-center">
|
||||
<%= render 'votes',
|
||||
{ spending_proposal: @spending_proposal, vote_url: vote_spending_proposal_path(@spending_proposal, value: 'yes') } %>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
1
app/views/spending_proposals/vote.js.erb
Normal file
1
app/views/spending_proposals/vote.js.erb
Normal file
@@ -0,0 +1 @@
|
||||
$("#<%= dom_id(@spending_proposal) %>_votes").html('<%= j render("spending_proposals/votes", spending_proposal: @spending_proposal) %>');
|
||||
@@ -7,6 +7,10 @@ ActsAsVotable::Vote.class_eval do
|
||||
where(votable_type: 'Proposal', votable_id: proposals)
|
||||
end
|
||||
|
||||
def self.for_spending_proposals(spending_proposals)
|
||||
where(votable_type: 'SpendingProposal', votable_id: spending_proposals)
|
||||
end
|
||||
|
||||
def value
|
||||
vote_flag
|
||||
end
|
||||
|
||||
@@ -426,6 +426,38 @@ en:
|
||||
recommendations_title: How to create a spending proposal
|
||||
start_new: Create spending proposal
|
||||
wrong_price_format: Only integer numbers
|
||||
spending_proposal:
|
||||
already_supported: You have already supported this. Share it!
|
||||
comments:
|
||||
one: 1 comment
|
||||
other: "%{count} comments"
|
||||
zero: No comments
|
||||
proposal: Proposal
|
||||
reason_for_supports_necessary: 2% of Census
|
||||
support: Support
|
||||
support_title: Support this proposal
|
||||
supports:
|
||||
one: 1 support
|
||||
other: "%{count} supports"
|
||||
zero: No supports
|
||||
supports_necessary: "%{number} supports needed"
|
||||
total_percent: 100%
|
||||
show:
|
||||
author_deleted: User deleted
|
||||
back_link: Go back
|
||||
code: 'Proposal code:'
|
||||
comments:
|
||||
one: 1 comment
|
||||
other: "%{count} comments"
|
||||
zero: No comments
|
||||
comments_title: Comments
|
||||
edit_proposal_link: Edit
|
||||
flag: This proposal has been flagged as inappropriate by several users.
|
||||
login_to_comment: You must %{signin} or %{signup} to leave a comment.
|
||||
share: Share
|
||||
update:
|
||||
form:
|
||||
submit_button: Save changes
|
||||
stats:
|
||||
index:
|
||||
visits: Visits
|
||||
|
||||
@@ -66,7 +66,11 @@ Rails.application.routes.draw do
|
||||
end
|
||||
|
||||
scope '/participatory_budget' do
|
||||
resources :spending_proposals, only: [:index, :new, :create, :show, :destroy], path: 'investment_projects'
|
||||
resources :spending_proposals, only: [:index, :new, :create, :show, :destroy], path: 'investment_projects' do
|
||||
member do
|
||||
post :vote
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
resources :stats, only: [:index]
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class AddVotesUpToSpendingProposals < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :spending_proposals, :cached_votes_up, :integer
|
||||
end
|
||||
end
|
||||
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20160328152843) do
|
||||
ActiveRecord::Schema.define(version: 20160329115418) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -312,6 +312,7 @@ ActiveRecord::Schema.define(version: 20160328152843) do
|
||||
t.integer "price_first_year", limit: 8
|
||||
t.string "time_scope"
|
||||
t.datetime "unfeasible_email_sent_at"
|
||||
t.integer "cached_votes_up"
|
||||
end
|
||||
|
||||
add_index "spending_proposals", ["author_id"], name: "index_spending_proposals_on_author_id", using: :btree
|
||||
|
||||
@@ -361,4 +361,64 @@ feature 'Votes' do
|
||||
expect_message_only_verified_can_vote_proposals
|
||||
end
|
||||
end
|
||||
|
||||
feature 'Spending Proposals' do
|
||||
background { login_as(@manuela) }
|
||||
|
||||
xscenario "Index shows user votes on proposals" do
|
||||
proposal1 = create(:proposal)
|
||||
proposal2 = create(:proposal)
|
||||
proposal3 = create(:proposal)
|
||||
create(:vote, voter: @manuela, votable: proposal1, vote_flag: true)
|
||||
|
||||
visit proposals_path
|
||||
|
||||
within("#proposals") do
|
||||
within("#proposal_#{proposal1.id}_votes") do
|
||||
expect(page).to have_content "You have already supported this proposal. Share it!"
|
||||
end
|
||||
|
||||
within("#proposal_#{proposal2.id}_votes") do
|
||||
expect(page).to_not have_content "You have already supported this proposal. Share it!"
|
||||
end
|
||||
|
||||
within("#proposal_#{proposal3.id}_votes") do
|
||||
expect(page).to_not have_content "You have already supported this proposal. Share it!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature 'Single spending proposal' do
|
||||
background do
|
||||
@proposal = create(:spending_proposal)
|
||||
end
|
||||
|
||||
scenario 'Show no votes' do
|
||||
visit spending_proposal_path(@proposal)
|
||||
expect(page).to have_content "No supports"
|
||||
end
|
||||
|
||||
scenario 'Trying to vote multiple times', :js do
|
||||
visit spending_proposal_path(@proposal)
|
||||
|
||||
within('.supports') do
|
||||
find('.in-favor a').click
|
||||
expect(page).to have_content "1 support"
|
||||
|
||||
expect(page).to_not have_selector ".in-favor a"
|
||||
end
|
||||
end
|
||||
|
||||
scenario 'Create from proposal show', :focus, :js do
|
||||
visit spending_proposal_path(@proposal)
|
||||
|
||||
within('.supports') do
|
||||
find('.in-favor a').click
|
||||
|
||||
expect(page).to have_content "1 support"
|
||||
expect(page).to have_content "You have already supported this. Share it!"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user