\ No newline at end of file
diff --git a/app/views/proposals/show.html.erb b/app/views/proposals/show.html.erb
index 0a5b35c0c..c319c1409 100644
--- a/app/views/proposals/show.html.erb
+++ b/app/views/proposals/show.html.erb
@@ -22,7 +22,14 @@
<% end %>
<%= @proposal.title %>
- <% if @proposal.conflictive? %>
+ <% if @proposal.retired? %>
+
<% if can?(:destroy, spending_proposal) %>
<%= link_to t("users.show.delete_spending_proposal"),
spending_proposal,
method: :delete,
data: { confirm: t("users.show.confirm_deletion_spending_proposal") },
- class: 'button small warning' %>
+ class: 'delete' %>
<% end %>
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 729effd4c..de37b5975 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
@@ -282,6 +296,15 @@ en:
hot_score: most active
most_commented: most commented
relevance: relevance
+ retired_proposals: Retired proposals
+ retired_proposals_link: "Proposals retired by the author"
+ retired_links:
+ all: All
+ duplicated: Duplicated
+ started: Underway
+ unfeasible: Unfeasible
+ done: Done
+ other: Other
search_form:
button: Search
placeholder: Search proposals...
@@ -305,6 +328,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 +358,9 @@ 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."
+ retired_warning_link_to_explanation: Read the explanation before voting for it.
+ retired: Proposal retired by the author
share: Share
update:
form:
@@ -490,6 +518,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 437bd8c97..82b7d59d1 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
@@ -282,6 +296,15 @@ es:
hot_score: Más activas hoy
most_commented: Más comentadas
relevance: Más relevantes
+ retired_proposals: Propuestas retiradas
+ retired_proposals_link: "Propuestas retiradas por sus autores"
+ retired_links:
+ all: Todas
+ duplicated: Duplicadas
+ started: Empezadas
+ unfeasible: Inviables
+ done: Hechas
+ other: Otras
search_form:
button: Buscar
placeholder: Buscar propuestas...
@@ -305,6 +328,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 +358,9 @@ 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."
+ retired_warning_link_to_explanation: Revisa su explicación antes de apoyarla.
+ retired: Propuesta retirada por el autor
share: Compartir
update:
form:
@@ -490,6 +518,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 43b8b5219..6c9b629f2 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/db/migrate/20160420105023_add_retired_to_proposals.rb b/db/migrate/20160420105023_add_retired_to_proposals.rb
new file mode 100644
index 000000000..f818a9169
--- /dev/null
+++ b/db/migrate/20160420105023_add_retired_to_proposals.rb
@@ -0,0 +1,5 @@
+class AddRetiredToProposals < ActiveRecord::Migration
+ def change
+ add_column :proposals, :retired_at, :datetime, default: nil
+ end
+end
diff --git a/db/migrate/20160422094733_add_retired_texts_to_proposals.rb b/db/migrate/20160422094733_add_retired_texts_to_proposals.rb
new file mode 100644
index 000000000..3c044477d
--- /dev/null
+++ b/db/migrate/20160422094733_add_retired_texts_to_proposals.rb
@@ -0,0 +1,6 @@
+class AddRetiredTextsToProposals < ActiveRecord::Migration
+ def change
+ add_column :proposals, :retired_reason, :string, default: nil
+ add_column :proposals, :retired_explanation, :text, default: nil
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 49f0011f2..7e83ab7cc 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160418172919) do
+ActiveRecord::Schema.define(version: 20160422094733) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -239,27 +239,30 @@ ActiveRecord::Schema.define(version: 20160418172919) do
add_index "organizations", ["user_id"], name: "index_organizations_on_user_id", using: :btree
create_table "proposals", force: :cascade do |t|
- t.string "title", limit: 80
+ t.string "title", limit: 80
t.text "description"
t.string "question"
t.string "external_url"
t.integer "author_id"
t.datetime "hidden_at"
- t.integer "flags_count", default: 0
+ t.integer "flags_count", default: 0
t.datetime "ignored_flag_at"
- t.integer "cached_votes_up", default: 0
- t.integer "comments_count", default: 0
+ t.integer "cached_votes_up", default: 0
+ t.integer "comments_count", default: 0
t.datetime "confirmed_hide_at"
- t.integer "hot_score", limit: 8, default: 0
- t.integer "confidence_score", default: 0
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.string "responsible_name", limit: 60
+ t.integer "hot_score", limit: 8, default: 0
+ t.integer "confidence_score", default: 0
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "responsible_name", limit: 60
t.text "summary"
t.string "video_url"
- t.integer "physical_votes", default: 0
+ t.integer "physical_votes", default: 0
t.tsvector "tsv"
t.integer "geozone_id"
+ t.datetime "retired_at"
+ t.string "retired_reason"
+ t.text "retired_explanation"
end
add_index "proposals", ["author_id", "hidden_at"], name: "index_proposals_on_author_id_and_hidden_at", using: :btree
diff --git a/spec/features/proposals_spec.rb b/spec/features/proposals_spec.rb
index bde574935..4a3ea58a1 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,100 @@ feature 'Proposals' do
end
+ context 'Retired proposals' 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
+
+ scenario 'Index do not list retired proposals by default' do
+ create_featured_proposals
+ not_retired = create(:proposal)
+ retired = create(:proposal, retired_at: Time.now)
+
+ visit proposals_path
+
+ expect(page).to have_selector('#proposals .proposal', count: 1)
+ within('#proposals') do
+ expect(page).to have_content not_retired.title
+ expect(page).to_not have_content retired.title
+ end
+ end
+
+ scenario 'Index has a link to retired proposals list' do
+ create_featured_proposals
+ not_retired = create(:proposal)
+ retired = create(:proposal, retired_at: Time.now)
+
+ visit proposals_path
+
+ expect(page).to_not have_content retired.title
+ click_link 'Proposals retired by the author'
+
+ expect(page).to have_content retired.title
+ expect(page).to_not have_content not_retired.title
+ end
+
+ scenario 'Retired proposals index interface elements' do
+ visit proposals_path(retired: 'all')
+
+ expect(page).to_not have_content 'Advanced search'
+ expect(page).to_not have_content 'Categories'
+ expect(page).to_not have_content 'Districts'
+ end
+
+ scenario 'Retired proposals index has links to filter by retired_reason' do
+ unfeasible = create(:proposal, retired_at: Time.now, retired_reason: 'unfeasible')
+ duplicated = create(:proposal, retired_at: Time.now, retired_reason: 'duplicated')
+
+ visit proposals_path(retired: 'all')
+
+ expect(page).to have_content unfeasible.title
+ expect(page).to have_content duplicated.title
+ expect(page).to have_link 'Duplicated'
+ expect(page).to have_link 'Underway'
+ expect(page).to have_link 'Unfeasible'
+ expect(page).to have_link 'Done'
+ expect(page).to have_link 'Other'
+
+ click_link 'Unfeasible'
+
+ expect(page).to have_content unfeasible.title
+ expect(page).to_not have_content duplicated.title
+ end
+ end
+
scenario 'Update should not be posible if logged user is not the author' do
proposal = create(:proposal)
expect(proposal).to be_editable
diff --git a/spec/models/proposal_spec.rb b/spec/models/proposal_spec.rb
index eae4d4fbd..4958204ec 100644
--- a/spec/models/proposal_spec.rb
+++ b/spec/models/proposal_spec.rb
@@ -741,4 +741,30 @@ describe Proposal do
end
end
+ describe "retired" do
+ before(:all) do
+ @proposal1 = create(:proposal)
+ @proposal2 = create(:proposal, retired_at: Time.now)
+ end
+
+ it "retired? is true" do
+ expect(@proposal1.retired?).to eq false
+ expect(@proposal2.retired?).to eq true
+ end
+
+ it "scope retired" do
+ retired = Proposal.retired
+
+ expect(retired.size).to eq(1)
+ expect(retired.first).to eq(@proposal2)
+ end
+
+ it "scope not_retired" do
+ not_retired = Proposal.not_retired
+
+ expect(not_retired.size).to eq(1)
+ expect(not_retired.first).to eq(@proposal1)
+ end
+ end
+
end