diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb
index add59a7cb..cbf2af121 100644
--- a/app/controllers/proposals_controller.rb
+++ b/app/controllers/proposals_controller.rb
@@ -35,6 +35,17 @@ class ProposalsController < ApplicationController
set_proposal_votes(@proposal)
end
+ def retire
+ if valid_retired_params? && @proposal.update(retired_params.merge(retired_at: Time.now))
+ redirect_to proposal_path(@proposal), notice: t('proposals.notice.retired')
+ else
+ render action: :retire_form
+ end
+ end
+
+ def retire_form
+ end
+
def vote_featured
@proposal.register_vote(current_user, 'yes')
set_featured_proposal_votes(@proposal)
@@ -51,6 +62,16 @@ class ProposalsController < ApplicationController
params.require(:proposal).permit(:title, :question, :summary, :description, :external_url, :video_url, :responsible_name, :tag_list, :terms_of_service, :captcha, :captcha_key, :geozone_id)
end
+ def retired_params
+ params.require(:proposal).permit(:retired_reason, :retired_explanation)
+ end
+
+ def valid_retired_params?
+ @proposal.errors.add(:retired_reason, I18n.t('errors.messages.blank')) if params[:proposal][:retired_reason].blank?
+ @proposal.errors.add(:retired_explanation, I18n.t('errors.messages.blank')) if params[:proposal][:retired_explanation].blank?
+ @proposal.errors.empty?
+ end
+
def resource_model
Proposal
end
diff --git a/app/helpers/proposals_helper.rb b/app/helpers/proposals_helper.rb
index f01abc053..9eaa88549 100644
--- a/app/helpers/proposals_helper.rb
+++ b/app/helpers/proposals_helper.rb
@@ -28,4 +28,8 @@ module ProposalsHelper
end
end
+ def retire_proposals_options
+ Proposal::RETIRE_OPTIONS.collect { |option| [ t("proposals.retire_options.#{option}"), option ] }
+ end
+
end
\ No newline at end of file
diff --git a/app/models/abilities/common.rb b/app/models/abilities/common.rb
index 5bb43725b..6dd36d5b0 100644
--- a/app/models/abilities/common.rb
+++ b/app/models/abilities/common.rb
@@ -16,6 +16,7 @@ module Abilities
can :update, Proposal do |proposal|
proposal.editable_by?(user)
end
+ can [:retire_form, :retire], Proposal, author_id: user.id
can :read, SpendingProposal
diff --git a/app/models/proposal.rb b/app/models/proposal.rb
index c6c7e6398..be08b5654 100644
--- a/app/models/proposal.rb
+++ b/app/models/proposal.rb
@@ -12,6 +12,8 @@ class Proposal < ActiveRecord::Base
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases
+ RETIRE_OPTIONS = %w(duplicated started unfeasible done other)
+
belongs_to :author, -> { with_hidden }, class_name: 'User', foreign_key: 'author_id'
belongs_to :geozone
has_many :comments, as: :commentable
@@ -26,6 +28,7 @@ class Proposal < ActiveRecord::Base
validates :description, length: { maximum: Proposal.description_max_length }
validates :question, length: { in: 10..Proposal.question_max_length }
validates :responsible_name, length: { in: 6..Proposal.responsible_name_max_length }
+ validates :retired_reason, inclusion: {in: RETIRE_OPTIONS, allow_nil: true}
validates :terms_of_service, acceptance: { allow_nil: false }, on: :create
diff --git a/app/views/proposals/retire_form.html.erb b/app/views/proposals/retire_form.html.erb
new file mode 100644
index 000000000..293e523bb
--- /dev/null
+++ b/app/views/proposals/retire_form.html.erb
@@ -0,0 +1,37 @@
+
+
+
+
+
+ <%= link_to @proposal.title, @proposal %>
+
+
+
<%= t("proposals.retire_form.title") %>
+
+
+ <%= t("proposals.retire_form.warning") %>
+
+
+ <%= form_for(@proposal, url: retire_proposal_path(@proposal)) do |f| %>
+ <%= render 'shared/errors', resource: @proposal %>
+
+
+
+ <%= f.label :retired_reason, t("proposals.retire_form.retired_reason_label") %>
+ <%= f.select :retired_reason, retire_proposals_options, {include_blank: t("proposals.retire_form.retired_reason_blank"), label: false} %>
+
+
+
+ <%= f.label :retired_explanation, t("proposals.retire_form.retired_explanation_label") %>
+ <%= f.text_area :retired_explanation, rows: 4, maxlength: 500, label: false,
+ placeholder: t('proposals.retire_form.retired_explanation_placeholder') %>
+
+
+
+ <%= f.submit(class: "button", value: t("proposals.retire_form.submit_button")) %>
+
+
+ <% end %>
+
+
+
\ No newline at end of file
diff --git a/app/views/proposals/show.html.erb b/app/views/proposals/show.html.erb
index 0a5b35c0c..7d1c23e59 100644
--- a/app/views/proposals/show.html.erb
+++ b/app/views/proposals/show.html.erb
@@ -22,7 +22,11 @@
<% end %>
<%= @proposal.title %>
- <% if @proposal.conflictive? %>
+ <% if @proposal.retired? %>
+
+ <%= t("proposals.show.retired_warning") %>
+
+ <% elsif @proposal.conflictive? %>
<%= t("proposals.show.flag") %>
@@ -74,6 +78,11 @@
<%= @proposal.question %>
+ <% if @proposal.retired? %>
+ <%= t('proposals.show.retired') %>: <%= t("proposals.retire_options.#{@proposal.retired_reason}") unless @proposal.retired_reason == 'other' %>
+ <%= simple_format text_with_links(@proposal.retired_explanation), {}, sanitize: false %>
+ <% end %>
+
<%= render 'shared/tags', taggable: @proposal %>
<%= render 'shared/geozone', geozonable: @proposal %>
diff --git a/app/views/users/_proposals.html.erb b/app/views/users/_proposals.html.erb
index a8ec5061e..c3aa55ed1 100644
--- a/app/views/users/_proposals.html.erb
+++ b/app/views/users/_proposals.html.erb
@@ -2,10 +2,19 @@
<% @proposals.each do |proposal| %>
- <%= link_to proposal.title, proposal %>
+ <%= link_to proposal.title, proposal, proposal.retired? ? {class: 'delete'} : {} %>
<%= proposal.summary %>
|
+
+ <% if proposal.retired? %>
+ <%= t('users.show.retired') %>
+ <% else %>
+ <%= link_to t('users.show.retire'),
+ retire_form_proposal_path(proposal),
+ class: 'delete' %>
+ <% end %>
+ |
<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 27d375496..0df604469 100755
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -254,6 +254,20 @@ en:
form:
submit_button: Save changes
show_link: View proposal
+ retire_form:
+ title: Retire proposal
+ warning: "If you retire the proposal it would still accept supports, but will be removed from the main list and a message will be visible to all users stating that the author considers the proposal should not be supported anymore"
+ retired_reason_label: Reason to retire the proposal
+ retired_reason_blank: Choose an option
+ retired_explanation_label: Explanation
+ retired_explanation_placeholder: Explain shortly why you think this proposal should not receive more supports
+ submit_button: Retire proposal
+ retire_options:
+ duplicated: Duplicated
+ started: Already underway
+ unfeasible: Unfeasible
+ done: Done
+ other: Other
form:
geozone: Scope of operation
proposal_external_url: Link to additional documentation
@@ -305,6 +319,8 @@ en:
recommendation_two: Any proposal or comment suggesting illegal action will be deleted, as well as those intending to sabotage the debate spaces. Anything else is allowed.
recommendations_title: Recommendations for creating a proposal
start_new: Create new proposal
+ notice:
+ retired: Proposal retired
proposal:
already_supported: You have already supported this proposal. Share it!
comments:
@@ -333,6 +349,8 @@ en:
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.
+ retired_warning: "The author considers this proposal should not receive more supports. check the explanation before voting for it."
+ retired: Proposal retired by the author
share: Share
update:
form:
@@ -489,6 +507,8 @@ en:
other: "%{count} Spending proposals"
no_activity: User has no public activity
private_activity: This user decided to keep the activity list private
+ retire: Retire
+ retired: Retired
votes:
agree: I agree
anonymous: Too many anonymous votes to admit vote %{verify_account}.
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 26e10874c..c67136e9e 100755
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -254,6 +254,20 @@ es:
form:
submit_button: Guardar cambios
show_link: Ver propuesta
+ retire_form:
+ title: Retirar propuesta
+ warning: "Si sigues adelante tu propuesta podrá seguir recibiendo apoyos, pero dejará de ser listada en la lista principal, y aparecerá un mensaje para todos los usuarios avisándoles de que el autor considera que esta propuesta no debe seguir recogiendo apoyos."
+ retired_reason_label: Razón por la que se retira la propuesta
+ retired_reason_blank: Selecciona una opción
+ retired_explanation_label: Explicación
+ retired_explanation_placeholder: Explica brevemente por que consideras que esta propuesta no debe recoger más apoyos
+ submit_button: Retirar propuesta
+ retire_options:
+ duplicated: Duplicada
+ started: Ejecutándose
+ unfeasible: Inviable
+ done: Hecha
+ other: Otra
form:
geozone: "Ámbito de actuación"
proposal_external_url: Enlace a documentación adicional
@@ -305,6 +319,8 @@ es:
recommendation_two: Cualquier propuesta o comentario que implique una acción ilegal será eliminada, también las que tengan la intención de sabotear los espacios de propuesta, todo lo demás está permitido.
recommendations_title: Recomendaciones para crear una propuesta
start_new: Crear una propuesta
+ notice:
+ retired: Propuesta retirada
proposal:
already_supported: "¡Ya has apoyado esta propuesta, compártela!"
comments:
@@ -333,6 +349,8 @@ es:
edit_proposal_link: Editar propuesta
flag: Esta propuesta ha sido marcada como inapropiada por varios usuarios.
login_to_comment: Necesitas %{signin} o %{signup} para comentar.
+ retired_warning: "El autor de esta propuesta considera que ya no debe seguir recogiendo apoyos. Revisa su explicación antes de apoyarla."
+ retired: Propuesta retirada por el autor
share: Compartir
update:
form:
@@ -489,6 +507,8 @@ es:
other: "%{count} Propuestas de inversión"
no_activity: Usuario sin actividad pública
private_activity: Este usuario ha decidido mantener en privado su lista de actividades
+ retire: Retirar
+ retired: Retirada
votes:
agree: Estoy de acuerdo
anonymous: Demasiados votos anónimos, para poder votar %{verify_account}.
diff --git a/config/routes.rb b/config/routes.rb
index dd4281788..aaa37cdb5 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -49,6 +49,8 @@ Rails.application.routes.draw do
post :vote_featured
put :flag
put :unflag
+ get :retire_form
+ patch :retire
end
collection do
get :map
diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb
index bde574935..adcaf486c 100644
--- a/spec/features/proposals_spec.rb
+++ b/spec/features/proposals_spec.rb
@@ -88,27 +88,27 @@ feature 'Proposals' do
end
context "Embedded video" do
- scenario "Show YouTube video" do
+ scenario "Show YouTube video" do
proposal = create(:proposal, video_url: "http://www.youtube.com/watch?v=a7UFm6ErMPU")
visit proposal_path(proposal)
expect(page).to have_selector("div[id='js-embedded-video']")
expect(page.html).to include 'https://www.youtube.com/embed/a7UFm6ErMPU'
end
-
- scenario "Show Vimeo video" do
+
+ scenario "Show Vimeo video" do
proposal = create(:proposal, video_url: "https://vimeo.com/7232823" )
visit proposal_path(proposal)
expect(page).to have_selector("div[id='js-embedded-video']")
expect(page.html).to include 'https://player.vimeo.com/video/7232823'
end
-
- scenario "Dont show video" do
+
+ scenario "Dont show video" do
proposal = create(:proposal, video_url: nil)
visit proposal_path(proposal)
expect(page).to_not have_selector("div[id='js-embedded-video']")
- end
- end
+ end
+ end
scenario 'Social Media Cards' do
proposal = create(:proposal)
@@ -375,7 +375,7 @@ feature 'Proposals' do
end
end
- context "Geozones" do
+ context 'Geozones' do
scenario "Default whole city" do
author = create(:user)
@@ -430,6 +430,44 @@ feature 'Proposals' do
end
+ context 'Retire a proposal' do
+ scenario 'Retire' do
+ proposal = create(:proposal)
+ login_as(proposal.author)
+
+ visit user_path(proposal.author)
+ within("#proposal_#{proposal.id}") do
+ click_link 'Retire'
+ end
+ expect(current_path).to eq(retire_form_proposal_path(proposal))
+
+ select 'Duplicated', from: 'proposal_retired_reason'
+ fill_in 'proposal_retired_explanation', with: 'There are three other better proposals with the same subject'
+ click_button "Retire proposal"
+
+ expect(page).to have_content "Proposal retired"
+
+ visit proposal_path(proposal)
+
+ expect(page).to have_content proposal.title
+ expect(page).to have_content 'Proposal retired by the author'
+ expect(page).to have_content 'Duplicated'
+ expect(page).to have_content 'There are three other better proposals with the same subject'
+ end
+
+ scenario 'Fields are mandatory' do
+ proposal = create(:proposal)
+ login_as(proposal.author)
+
+ visit retire_form_proposal_path(proposal)
+
+ click_button 'Retire proposal'
+
+ expect(page).to_not have_content 'Proposal retired'
+ expect(page).to have_content "can't be blank", count: 2
+ end
+ end
+
scenario 'Update should not be posible if logged user is not the author' do
proposal = create(:proposal)
expect(proposal).to be_editable