Merge pull request #913 from consul/investment-proposal-show

Allows users to view their own spending proposals
This commit is contained in:
Juanjo Bazán
2016-02-21 18:21:05 +00:00
14 changed files with 182 additions and 26 deletions

View File

@@ -1,11 +1,12 @@
class SpendingProposalsController < ApplicationController class SpendingProposalsController < ApplicationController
include FeatureFlags include FeatureFlags
before_action :authenticate_user!, except: [:index]
before_action :verify_valuator, only: [:show]
load_and_authorize_resource load_and_authorize_resource
before_action :authenticate_user!, except: [:index]
before_action :verify_access, only: [:show]
before_filter -> { flash.now[:notice] = flash[:notice].html_safe if flash[:html_safe] && flash[:notice] }
feature_flag :spending_proposals feature_flag :spending_proposals
def index def index
@@ -20,7 +21,8 @@ class SpendingProposalsController < ApplicationController
@spending_proposal.author = current_user @spending_proposal.author = current_user
if @spending_proposal.save_with_captcha if @spending_proposal.save_with_captcha
redirect_to spending_proposals_path, notice: t("flash.actions.create.spending_proposal") notice = t('flash.actions.create.spending_proposal', activity: "<a href='#{user_path(current_user, filter: :spending_proposals)}'>#{t('layouts.header.my_activity_link')}</a>")
redirect_to @spending_proposal, notice: notice, flash: { html_safe: true }
else else
render :new render :new
end end
@@ -32,8 +34,8 @@ class SpendingProposalsController < ApplicationController
params.require(:spending_proposal).permit(:title, :description, :external_url, :geozone_id, :association_name, :terms_of_service, :captcha, :captcha_key) params.require(:spending_proposal).permit(:title, :description, :external_url, :geozone_id, :association_name, :terms_of_service, :captcha, :captcha_key)
end end
def verify_valuator def verify_access
raise CanCan::AccessDenied unless current_user.try(:valuator?) || current_user.try(:administrator?) raise CanCan::AccessDenied unless current_user.try(:valuator?) || current_user.try(:administrator?) || @spending_proposal.author == current_user
end end
end end

View File

@@ -1,7 +1,8 @@
class UsersController < ApplicationController class UsersController < ApplicationController
has_filters %w{proposals debates comments}, only: :show has_filters %w{proposals debates comments spending_proposals}, only: :show
load_and_authorize_resource load_and_authorize_resource
helper_method :authorized_for_filter?
def show def show
load_filtered_activity if valid_access? load_filtered_activity if valid_access?
@@ -12,7 +13,8 @@ class UsersController < ApplicationController
@activity_counts = HashWithIndifferentAccess.new( @activity_counts = HashWithIndifferentAccess.new(
proposals: Proposal.where(author_id: @user.id).count, proposals: Proposal.where(author_id: @user.id).count,
debates: Debate.where(author_id: @user.id).count, debates: Debate.where(author_id: @user.id).count,
comments: Comment.not_as_admin_or_moderator.where(user_id: @user.id).count) comments: Comment.not_as_admin_or_moderator.where(user_id: @user.id).count,
spending_proposals: SpendingProposal.where(author_id: @user.id).count)
end end
def load_filtered_activity def load_filtered_activity
@@ -21,6 +23,7 @@ class UsersController < ApplicationController
when "proposals" then load_proposals when "proposals" then load_proposals
when "debates" then load_debates when "debates" then load_debates
when "comments" then load_comments when "comments" then load_comments
when "spending_proposals" then load_spending_proposals
else load_available_activity else load_available_activity
end end
end end
@@ -35,6 +38,9 @@ class UsersController < ApplicationController
elsif @activity_counts[:comments] > 0 elsif @activity_counts[:comments] > 0
load_comments load_comments
@current_filter = "comments" @current_filter = "comments"
elsif @activity_counts[:spending_proposals] > 0 && author_or_admin?
load_spending_proposals
@current_filter = "spending_proposals"
end end
end end
@@ -50,11 +56,23 @@ class UsersController < ApplicationController
@comments = Comment.not_as_admin_or_moderator.where(user_id: @user.id).includes(:commentable).order(created_at: :desc).page(params[:page]) @comments = Comment.not_as_admin_or_moderator.where(user_id: @user.id).includes(:commentable).order(created_at: :desc).page(params[:page])
end end
def load_spending_proposals
@spending_proposals = SpendingProposal.where(author_id: @user.id).order(created_at: :desc).page(params[:page])
end
def valid_access? def valid_access?
@user.public_activity || authorized_current_user? @user.public_activity || authorized_current_user?
end end
def author_or_admin?
@author_or_admin ||= current_user && (current_user == @user || current_user.administrator?)
end
def authorized_current_user? def authorized_current_user?
@authorized_current_user ||= current_user && (current_user == @user || current_user.moderator? || current_user.administrator?) @authorized_current_user ||= current_user && (current_user == @user || current_user.moderator? || current_user.administrator?)
end end
def authorized_for_filter?(filter)
filter == "spending_proposals" ? author_or_admin? : true
end
end end

View File

@@ -64,4 +64,8 @@ class SpendingProposal < ActiveRecord::Base
end end
end end
def description
super.try :html_safe
end
end end

View File

@@ -1,24 +1,44 @@
<span class="flag-content"> <span class="flag-content">
<% if show_flag_action? comment %> <% if show_flag_action? comment %>
<span class="divider">&nbsp;|&nbsp;</span> <span class="divider">&nbsp;|&nbsp;</span>
<a class="button" id="flag-expand-comment-<%= comment.id %>" data-dropdown="flag-drop-comment-<%= comment.id %>" aria-controls="flag-drop-comment-<%= comment.id %>" aria-expanded="false" title="<%= t('shared.flag') %>"> <a class="button" id="flag-expand-comment-<%= comment.id %>"
&nbsp;<i class="icon-flag flag-disable"></i>&nbsp;&nbsp; data-dropdown="flag-drop-comment-<%= comment.id %>"
aria-controls="flag-drop-comment-<%= comment.id %>"
aria-expanded="false"
title="<%= t('shared.flag') %>">
&nbsp;
<i class="icon-flag flag-disable"></i>&nbsp;&nbsp;
</a> </a>
<ul id="flag-drop-comment-<%= comment.id %>" class="f-dropdown" data-dropdown-content aria-hidden="true" tabindex="-1"> <ul id="flag-drop-comment-<%= comment.id %>"
class="f-dropdown"
data-dropdown-content
aria-hidden="true"
tabindex="-1">
<li> <li>
<%= link_to t("shared.flag"), flag_comment_path(comment), method: :put, remote: true, id: "flag-comment-#{comment.id}" %> <%= link_to t("shared.flag"), flag_comment_path(comment), method: :put,
remote: true, id: "flag-comment-#{comment.id}" %>
</li> </li>
</ul> </ul>
<% end %> <% end %>
<% if show_unflag_action? comment %> <% if show_unflag_action? comment %>
<span class="divider">&nbsp;|&nbsp;</span> <span class="divider">&nbsp;|&nbsp;</span>
<a class="button" id="unflag-expand-comment-<%= comment.id %>" data-dropdown="unflag-drop-comment-<%= comment.id %>" aria-controls="unflag-drop-comment-<%= comment.id %>" aria-expanded="false" title="<%= t('shared.unflag') %>"> <a class="button" id="unflag-expand-comment-<%= comment.id %>"
&nbsp;<i class="icon-flag flag-active"></i>&nbsp;&nbsp; data-dropdown="unflag-drop-comment-<%= comment.id %>"
aria-controls="unflag-drop-comment-<%= comment.id %>"
aria-expanded="false"
title="<%= t('shared.unflag') %>">
&nbsp;
<i class="icon-flag flag-active"></i>&nbsp;&nbsp;
</a> </a>
<ul id="unflag-drop-comment-<%= comment.id %>" class="f-dropdown" data-dropdown-content aria-hidden="true" tabindex="-1"> <ul id="unflag-drop-comment-<%= comment.id %>"
class="f-dropdown"
data-dropdown-content
aria-hidden="true"
tabindex="-1">
<li> <li>
<%= link_to t("shared.unflag"), unflag_comment_path(comment), method: :put, remote: true, id: "unflag-comment-#{comment.id}" %> <%= link_to t("shared.unflag"), unflag_comment_path(comment), method: :put,
remote: true, id: "unflag-comment-#{comment.id}" %>
</li> </li>
</ul> </ul>
<% end %> <% end %>

View File

@@ -41,7 +41,7 @@
<![endif]--> <![endif]-->
<% if notice %> <% if notice %>
<div class="alert-messages"> <div id="notice" class="alert-messages">
<div class="row"> <div class="row">
<div data-alert class="alert-box success radius"> <div data-alert class="alert-box success radius">
<a href="#" class="close" title="<%= t("application.close") %>">&times;</a> <a href="#" class="close" title="<%= t("application.close") %>">&times;</a>
@@ -52,7 +52,7 @@
<% end %> <% end %>
<% if alert %> <% if alert %>
<div class="alert-messages"> <div id="alert" class="alert-messages">
<div class="row"> <div class="row">
<div data-alert class="alert-box alert radius"> <div data-alert class="alert-box alert radius">
<a href="#" class="close" title="<%= t("application.close") %>">&times;</a> <a href="#" class="close" title="<%= t("application.close") %>">&times;</a>

View File

@@ -1,3 +1,4 @@
<%= render "proposals" if @proposals.present? %> <%= render "proposals" if @proposals.present? %>
<%= render "debates" if @debates.present? && feature?(:debates) %> <%= render "debates" if @debates.present? && feature?(:debates) %>
<%= render "comments" if @comments.present? %> <%= render "comments" if @comments.present? %>
<%= render "spending_proposals" if @spending_proposals.present? %>

View File

@@ -0,0 +1,13 @@
<table class="clear activity-proposals">
<% @spending_proposals.each do |spending_proposal| %>
<tr id="spending_proposal_<%= spending_proposal.id %>">
<td>
<%= link_to spending_proposal.title, spending_proposal %>
<br>
<%= spending_proposal.description %>
</td>
</tr>
<% end %>
</table>
<%= paginate @spending_proposals %>

View File

@@ -14,11 +14,17 @@
<dl class="sub-nav margin-top"> <dl class="sub-nav margin-top">
<% @valid_filters.each do |filter| %> <% @valid_filters.each do |filter| %>
<% if @activity_counts[filter] > 0 %> <% if @activity_counts[filter] > 0 %>
<% if authorized_for_filter?(filter) %>
<% if @current_filter == filter %> <% if @current_filter == filter %>
<dd class="active"> <%= t("users.show.filters.#{filter}", count: @activity_counts[filter]) %></dd> <dd class="active">
<%= t("users.show.filters.#{filter}", count: @activity_counts[filter]) %>
</dd>
<% else %> <% else %>
<dd><%= link_to t("users.show.filters.#{filter}", count: @activity_counts[filter]), <dd>
current_path_with_query_params(filter: filter, page: 1) %></dd> <%= link_to t("users.show.filters.#{filter}", count: @activity_counts[filter]),
current_path_with_query_params(filter: filter, page: 1) %>
</dd>
<% end %>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>

View File

@@ -453,6 +453,9 @@ en:
proposals: proposals:
one: 1 Proposal one: 1 Proposal
other: "%{count} Proposals" other: "%{count} Proposals"
spending_proposals:
one: 1 Spending proposal
other: "%{count} Spending proposals"
no_activity: User has no public activity no_activity: User has no public activity
private_activity: This user decided to keep the activity list private private_activity: This user decided to keep the activity list private
votes: votes:

View File

@@ -454,6 +454,9 @@ es:
proposals: proposals:
one: 1 Propuesta one: 1 Propuesta
other: "%{count} Propuestas" other: "%{count} Propuestas"
spending_proposals:
one: 1 Propuesta de inversión
other: "%{count} Propuestas de inversión"
no_activity: Usuario sin actividad pública no_activity: Usuario sin actividad pública
private_activity: Este usuario ha decidido mantener en privado su lista de actividades private_activity: Este usuario ha decidido mantener en privado su lista de actividades
votes: votes:

View File

@@ -6,7 +6,7 @@ en:
notice: "%{resource_name} created successfully." notice: "%{resource_name} created successfully."
debate: "Debate created successfully." debate: "Debate created successfully."
proposal: "Proposal created successfully." proposal: "Proposal created successfully."
spending_proposal: "Spending proposal created successfully." spending_proposal: "Spending proposal created successfully. You can access it from %{activity}"
save_changes: save_changes:
notice: Changes saved notice: Changes saved
update: update:

View File

@@ -6,7 +6,7 @@ es:
notice: "%{resource_name} creado correctamente." notice: "%{resource_name} creado correctamente."
debate: "Debate creado correctamente." debate: "Debate creado correctamente."
proposal: "Propuesta creada correctamente." proposal: "Propuesta creada correctamente."
spending_proposal: "Propuesta de gasto creada correctamente." spending_proposal: "Propuesta de inversión creada correctamente. Puedes acceder a ella desde %{activity}"
save_changes: save_changes:
notice: Cambios guardados notice: Cambios guardados
update: update:

View File

@@ -2,7 +2,7 @@ require 'rails_helper'
feature 'Spending proposals' do feature 'Spending proposals' do
let(:author) { create(:user, :level_two) } let(:author) { create(:user, :level_two, username: 'Isabel') }
scenario 'Index' do scenario 'Index' do
visit spending_proposals_path visit spending_proposals_path
@@ -33,6 +33,37 @@ feature 'Spending proposals' do
click_button 'Create' click_button 'Create'
expect(page).to have_content 'Spending proposal created successfully' expect(page).to have_content 'Spending proposal created successfully'
expect(page).to have_content('Build a skyscraper')
expect(page).to have_content('I want to live in a high tower over the clouds')
expect(page).to have_content('Isabel')
expect(page).to have_content('People of the neighbourhood')
expect(page).to have_content('All city')
end
scenario 'Create notice' do
login_as(author)
visit new_spending_proposal_path
fill_in 'spending_proposal_title', with: 'Build a skyscraper'
fill_in 'spending_proposal_description', with: 'I want to live in a high tower over the clouds'
fill_in 'spending_proposal_external_url', with: 'http://http://skyscraperpage.com/'
fill_in 'spending_proposal_association_name', with: 'People of the neighbourhood'
fill_in 'spending_proposal_captcha', with: correct_captcha_text
select 'All city', from: 'spending_proposal_geozone_id'
check 'spending_proposal_terms_of_service'
click_button 'Create'
expect(page).to have_content 'Spending proposal created successfully'
expect(page).to have_content 'You can access it from My activity'
within "#notice" do
click_link 'My activity'
end
expect(current_url).to eq(user_url(author, filter: :spending_proposals))
expect(page).to have_content "1 Spending proposal"
expect(page).to have_content "Build a skyscraper"
end end
scenario 'Captcha is required for proposal creation' do scenario 'Captcha is required for proposal creation' do
@@ -100,6 +131,24 @@ feature 'Spending proposals' do
expect(page).to have_content(spending_proposal.geozone.name) expect(page).to have_content(spending_proposal.geozone.name)
end end
scenario "Show (as author)" do
author = create(:user)
login_as(author)
spending_proposal = create(:spending_proposal,
geozone: create(:geozone),
association_name: 'People of the neighbourhood',
author: author)
visit spending_proposal_path(spending_proposal)
expect(page).to have_content(spending_proposal.title)
expect(page).to have_content(spending_proposal.description)
expect(page).to have_content(spending_proposal.author.name)
expect(page).to have_content(spending_proposal.association_name)
expect(page).to have_content(spending_proposal.geozone.name)
end
scenario "Show (as user)" do scenario "Show (as user)" do
user = create(:user) user = create(:user)
login_as(user) login_as(user)

View File

@@ -198,6 +198,43 @@ feature 'Users' do
end end
end end
feature 'Spending proposals' do
background do
@author = create(:user)
create(:spending_proposal, author: @author, title: 'Build a school')
end
scenario 'is not shown if no user logged in' do
visit user_path(@user)
expect(page).to_not have_content('Build a school')
end
scenario 'is not shown if logged in user is a regular user' do
login_as(create(:user))
visit user_path(@author)
expect(page).to_not have_content('Build a school')
end
scenario 'is not shown if logged in user is moderator' do
login_as(create(:moderator).user)
visit user_path(@author)
expect(page).to_not have_content('Build a school')
end
scenario 'is shown if logged in user is admin' do
login_as(create(:administrator).user)
visit user_path(@author)
expect(page).to have_content('Build a school')
end
scenario 'is shown if logged in user is author' do
login_as(@author)
visit user_path(@author)
expect(page).to have_content('Build a school')
end
end
end end
feature 'Special comments' do feature 'Special comments' do