users can create spending proposals
This commit is contained in:
@@ -1,7 +1,32 @@
|
|||||||
class SpendingProposalsController < ApplicationController
|
class SpendingProposalsController < ApplicationController
|
||||||
before_action :authenticate_user!, except: [:index]
|
before_action :authenticate_user!, except: [:index]
|
||||||
|
|
||||||
|
load_and_authorize_resource
|
||||||
|
|
||||||
def index
|
def index
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def new
|
||||||
|
@spending_proposal = SpendingProposal.new
|
||||||
|
@featured_tags = ActsAsTaggableOn::Tag.where(featured: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@spending_proposal = SpendingProposal.new(spending_proposal_params)
|
||||||
|
@spending_proposal.author = current_user
|
||||||
|
|
||||||
|
if @spending_proposal.save_with_captcha
|
||||||
|
redirect_to spending_proposals_path, notice: t('flash.actions.create.notice', resource_name: t("activerecord.models.spending_proposal", count: 1))
|
||||||
|
else
|
||||||
|
@featured_tags = ActsAsTaggableOn::Tag.where(featured: true)
|
||||||
|
render :new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def spending_proposal_params
|
||||||
|
params.require(:spending_proposal).permit(:title, :description, :external_url, :tag_list, :terms_of_service, :captcha, :captcha_key)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -3,14 +3,16 @@ class SpendingProposal < ActiveRecord::Base
|
|||||||
include Sanitizable
|
include Sanitizable
|
||||||
include Taggable
|
include Taggable
|
||||||
|
|
||||||
|
apply_simple_captcha
|
||||||
|
|
||||||
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
|
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
|
||||||
|
|
||||||
validates :title, presence: true
|
validates :title, presence: true
|
||||||
validates :author, presence: true
|
validates :author, presence: true
|
||||||
validates :description, presence: true
|
validates :description, presence: true
|
||||||
|
|
||||||
validates :title, length: { in: 4..Proposal.title_max_length }
|
validates :title, length: { in: 4..SpendingProposal.title_max_length }
|
||||||
validates :description, length: { maximum: Proposal.description_max_length }
|
validates :description, length: { maximum: SpendingProposal.description_max_length }
|
||||||
|
|
||||||
validates :terms_of_service, acceptance: { allow_nil: false }, on: :create
|
validates :terms_of_service, acceptance: { allow_nil: false }, on: :create
|
||||||
end
|
end
|
||||||
|
|||||||
52
app/views/spending_proposals/_form.html.erb
Normal file
52
app/views/spending_proposals/_form.html.erb
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<%= 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 :tag_list, t("spending_proposals.form.tags_label") %>
|
||||||
|
<p class="note"><%= t("spending_proposals.form.tags_instructions") %></p>
|
||||||
|
<span class="tags">
|
||||||
|
<% @featured_tags.each do |tag| %>
|
||||||
|
<a class="js-add-tag-link"><%= tag.name %></a>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
||||||
|
<%= f.text_field :tag_list, value: @spending_proposal.tag_list.to_s, label: false, placeholder: t("spending_proposals.form.tags_placeholder"), class: 'js-tag-list' %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small-12 column">
|
||||||
|
<% if @spending_proposal.new_record? %>
|
||||||
|
<%= f.label :terms_of_service do %>
|
||||||
|
<%= f.check_box :terms_of_service, 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 radius", value: t("spending_proposals.form.submit_buttons.#{action_name}")) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
27
app/views/spending_proposals/new.html.erb
Normal file
27
app/views/spending_proposals/new.html.erb
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<div class="spending-proposal-new row">
|
||||||
|
|
||||||
|
<div class="small-12 medium-9 column">
|
||||||
|
<%= link_to spending_proposals_path, class: "left back" do %>
|
||||||
|
<i class="icon-angle-left left"></i>
|
||||||
|
<%= t("spending_proposals.new.back_link") %>
|
||||||
|
<% end %>
|
||||||
|
<h1><%= t("spending_proposals.new.start_new") %></h1>
|
||||||
|
<div class="alert-box info radius">
|
||||||
|
<%= link_to "/spending_proposals_info", 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">
|
||||||
|
<i class="icon-spending-proposals right"></i>
|
||||||
|
<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>
|
||||||
@@ -37,6 +37,9 @@ en:
|
|||||||
proposal:
|
proposal:
|
||||||
one: "Citizen proposal"
|
one: "Citizen proposal"
|
||||||
other: "Citizen proposals"
|
other: "Citizen proposals"
|
||||||
|
spending_proposal:
|
||||||
|
one: "Spending proposal"
|
||||||
|
other: "Spending proposals"
|
||||||
attributes:
|
attributes:
|
||||||
comment:
|
comment:
|
||||||
body: "Comment"
|
body: "Comment"
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ es:
|
|||||||
proposal:
|
proposal:
|
||||||
one: "Propuesta ciudadana"
|
one: "Propuesta ciudadana"
|
||||||
other: "Propuestas ciudadanas"
|
other: "Propuestas ciudadanas"
|
||||||
|
spending_proposal:
|
||||||
|
one: "Propuesta de gasto"
|
||||||
|
other: "Propuestas de gasto"
|
||||||
attributes:
|
attributes:
|
||||||
comment:
|
comment:
|
||||||
body: "Comentario"
|
body: "Comentario"
|
||||||
|
|||||||
@@ -248,9 +248,27 @@ en:
|
|||||||
submit_button: "Save changes"
|
submit_button: "Save changes"
|
||||||
spending_proposals:
|
spending_proposals:
|
||||||
index:
|
index:
|
||||||
title: "Citizen budget"
|
title: "Participatory budgeting"
|
||||||
text: "Here you can send spending proposals to be considered in the frame of the annual citizen budgets."
|
text: "Here you can send spending proposals to be considered in the frame of the annual participatory budgeting."
|
||||||
create_link: "Create spending proposal"
|
create_link: "Create spending proposal"
|
||||||
|
new:
|
||||||
|
back_link: Back
|
||||||
|
start_new: "Create spending proposal"
|
||||||
|
more_info: "How do participatory bufgeting works?"
|
||||||
|
recommendations_title: "Recommendations for creating a spending proposal"
|
||||||
|
recommendation_one: "It's mandatory that the proposal makes reference to a budgetable action."
|
||||||
|
recommendation_two: "Any proposal or comment suggesting illegal action will be deleted."
|
||||||
|
recommendation_three: "Try to go into details when describing your spending proposal so the reviewing team undertands your points."
|
||||||
|
form:
|
||||||
|
title: "Spending proposal title"
|
||||||
|
description: "Description"
|
||||||
|
external_url: "Link to additional documentation"
|
||||||
|
tags_label: "Tags"
|
||||||
|
tags_instructions: "Tag this spending proposal. You can choose from our tags or add your own."
|
||||||
|
tags_placeholder: "Enter the tags you would like to use, separated by commas (',')"
|
||||||
|
submit_buttons:
|
||||||
|
new: Create
|
||||||
|
create: Create
|
||||||
comments:
|
comments:
|
||||||
show:
|
show:
|
||||||
return_to_commentable: "Go back to "
|
return_to_commentable: "Go back to "
|
||||||
@@ -338,6 +356,7 @@ en:
|
|||||||
user: "the secret code does not match the image"
|
user: "the secret code does not match the image"
|
||||||
debate: "the secret code does not match the image"
|
debate: "the secret code does not match the image"
|
||||||
proposal: "the secret code does not match the image"
|
proposal: "the secret code does not match the image"
|
||||||
|
spendingproposal: "the secret code does not match the image"
|
||||||
shared:
|
shared:
|
||||||
author_info:
|
author_info:
|
||||||
author_deleted: "User deleted"
|
author_deleted: "User deleted"
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ es:
|
|||||||
proposal_responsible_name: "Nombre y apellidos de la persona que hace esta propuesta"
|
proposal_responsible_name: "Nombre y apellidos de la persona que hace esta propuesta"
|
||||||
proposal_responsible_name_note: "(individualmente o como representante de un colectivo; no se mostrará públicamente)"
|
proposal_responsible_name_note: "(individualmente o como representante de un colectivo; no se mostrará públicamente)"
|
||||||
tags_label: "Temas"
|
tags_label: "Temas"
|
||||||
tags_instructions: "Etiqueta esta propuesta. Puedes elegir entre nuestras propuestas o introducir las que desees."
|
tags_instructions: "Etiqueta esta propuesta. Puedes elegir entre nuestras sugerencias o introducir las que desees."
|
||||||
tags_placeholder: "Escribe las etiquetas que desees separadas por una coma (',')"
|
tags_placeholder: "Escribe las etiquetas que desees separadas por una coma (',')"
|
||||||
show:
|
show:
|
||||||
back_link: "Volver"
|
back_link: "Volver"
|
||||||
@@ -248,9 +248,27 @@ es:
|
|||||||
submit_button: "Guardar cambios"
|
submit_button: "Guardar cambios"
|
||||||
spending_proposals:
|
spending_proposals:
|
||||||
index:
|
index:
|
||||||
title: "Presupuestos ciudadanos"
|
title: "Presupuestos participativos"
|
||||||
text: "Desde esta sección podrás sugerir propuestas de gasto que irán asociadas a las partidas de presupuestos ciudadanos. El requisito principal es que sean propuestas presupuestables."
|
text: "Desde esta sección podrás sugerir propuestas de gasto que irán asociadas a las partidas de presupuestos ciudadanos. El requisito principal es que sean propuestas presupuestables."
|
||||||
create_link: "Enviar propuesta de gasto"
|
create_link: "Enviar propuesta de gasto"
|
||||||
|
new:
|
||||||
|
back_link: Volver
|
||||||
|
start_new: "Crear una propuesta de gasto"
|
||||||
|
more_info: "¿Cómo funcionan los presupuestos participativos?"
|
||||||
|
recommendations_title: "Recomendaciones para crear una propuesta de gasto"
|
||||||
|
recommendation_one: "Es fundamental que haga referencia a una actuación presupuestable."
|
||||||
|
recommendation_two: "Cualquier propuesta o comentario que implique acciones ilegales será eliminada."
|
||||||
|
recommendation_three: "Intenta detallar lo máximo posible la propuesta para que el equipo de gobierno encargado de estudiarla tenga las menor dudas posibles."
|
||||||
|
form:
|
||||||
|
title: "Título de la propuesta de gasto"
|
||||||
|
description: "Descripción detallada"
|
||||||
|
external_url: "Enlace a documentación adicional"
|
||||||
|
tags_label: "Temas"
|
||||||
|
tags_instructions: "Etiqueta esta propuesta de gasto. Puedes elegir entre nuestras sugerencias o introducir las que desees."
|
||||||
|
tags_placeholder: "Escribe las etiquetas que desees separadas por una coma (',')"
|
||||||
|
submit_buttons:
|
||||||
|
new: Crear
|
||||||
|
create: Crear
|
||||||
comments:
|
comments:
|
||||||
show:
|
show:
|
||||||
return_to_commentable: "Volver a "
|
return_to_commentable: "Volver a "
|
||||||
@@ -338,6 +356,7 @@ es:
|
|||||||
user: "el código secreto no coincide con la imagen"
|
user: "el código secreto no coincide con la imagen"
|
||||||
debate: "el código secreto no coincide con la imagen"
|
debate: "el código secreto no coincide con la imagen"
|
||||||
proposal: "el código secreto no coincide con la imagen"
|
proposal: "el código secreto no coincide con la imagen"
|
||||||
|
spendingproposal: "el código secreto no coincide con la imagen"
|
||||||
shared:
|
shared:
|
||||||
author_info:
|
author_info:
|
||||||
author_deleted: Usuario eliminado
|
author_deleted: Usuario eliminado
|
||||||
|
|||||||
@@ -8,4 +8,50 @@ feature 'Spending proposals' do
|
|||||||
expect(page).to have_link('Create spending proposal', href: new_spending_proposal_path)
|
expect(page).to have_link('Create spending proposal', href: new_spending_proposal_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario 'Create' do
|
||||||
|
author = create(:user)
|
||||||
|
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_captcha', with: correct_captcha_text
|
||||||
|
check 'spending_proposal_terms_of_service'
|
||||||
|
|
||||||
|
click_button 'Create'
|
||||||
|
|
||||||
|
expect(page).to have_content 'Spending proposal created successfully'
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Captcha is required for proposal creation' do
|
||||||
|
login_as(create(:user))
|
||||||
|
|
||||||
|
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_captcha', with: 'wrongText'
|
||||||
|
check 'spending_proposal_terms_of_service'
|
||||||
|
|
||||||
|
click_button 'Create'
|
||||||
|
|
||||||
|
expect(page).to_not have_content 'Spending proposal created successfully'
|
||||||
|
expect(page).to have_content '1 error'
|
||||||
|
|
||||||
|
fill_in 'spending_proposal_captcha', with: correct_captcha_text
|
||||||
|
click_button 'Create'
|
||||||
|
|
||||||
|
expect(page).to have_content 'Spending proposal created successfully'
|
||||||
|
end
|
||||||
|
|
||||||
|
scenario 'Errors on create' do
|
||||||
|
author = create(:user)
|
||||||
|
login_as(author)
|
||||||
|
|
||||||
|
visit new_spending_proposal_path
|
||||||
|
click_button 'Create'
|
||||||
|
expect(page).to have_content error_message
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user