adds votes to spending proposals

This commit is contained in:
rgarcia
2016-03-29 14:53:46 +02:00
parent e52004edc3
commit ed1ec1c553
18 changed files with 327 additions and 3 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 }

View 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 %>

View 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>

View 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>

View 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">&nbsp;&bull;&nbsp;</span>
<%= l @spending_proposal.created_at.to_date %>
<span class="bullet">&nbsp;&bull;&nbsp;</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>

View File

@@ -0,0 +1,47 @@
<div class="supports">
<span class="total-supports">
<%= t("spending_proposals.spending_proposal.supports", count: spending_proposal.total_votes) %>&nbsp;
</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>

View File

@@ -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>

View File

@@ -0,0 +1 @@
$("#<%= dom_id(@spending_proposal) %>_votes").html('<%= j render("spending_proposals/votes", spending_proposal: @spending_proposal) %>');

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -0,0 +1,5 @@
class AddVotesUpToSpendingProposals < ActiveRecord::Migration
def change
add_column :spending_proposals, :cached_votes_up, :integer
end
end

View File

@@ -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

View File

@@ -313,7 +313,7 @@ feature 'Votes' do
scenario 'Not logged user trying to vote comments in proposals', :js do
proposal = create(:proposal)
comment = create(:comment, commentable: proposal)
visit comment_path(comment)
within("#comment_#{comment.id}_reply") do
find("div.votes").hover
@@ -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