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