diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 85423df75..2f6b1f0c6 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -28,13 +28,18 @@ body.admin { margin-top: 0; } - input[type="text"] { - height: 36px\9; - line-height: 36px\9; + input[type="text"], textarea { + height: 48px\9; + line-height: 48px\9; margin-bottom: 24px\9; + width: 100%; } } + #proposals { + width: 100% !important; + } + .dashboard { margin-bottom: rem-calc(48); @@ -199,7 +204,7 @@ body.admin { font-size: rem-calc(12); &:hover, &:active, &:focus { - border: 0; + border-bottom: 1px dotted white; color: #cf2a0e; } } diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index 356299fba..d7d9a239b 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -886,12 +886,6 @@ footer { font-weight: bolder; } } - - form { - .button { - margin-top: rem-calc(24); - } - } } // 08. Forms diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss index 9aa06dd04..1f4bc85f1 100644 --- a/app/assets/stylesheets/participation.scss +++ b/app/assets/stylesheets/participation.scss @@ -436,6 +436,7 @@ padding-top: 0; font-size: rem-calc(15); line-height: rem-calc(30); + overflow: hidden; } .document-link, .video-link { diff --git a/app/assets/stylesheets/print.css b/app/assets/stylesheets/print.css index 9418d5ee0..0c67035c8 100644 --- a/app/assets/stylesheets/print.css +++ b/app/assets/stylesheets/print.css @@ -32,6 +32,8 @@ p.proposal-info span:nth-child(3) { display: none !important; } .button { display: none !important; } +.change-user { display: none !important; } + input[type="submit"] { display: none !important; } .select-order { diff --git a/app/controllers/management/proposals_controller.rb b/app/controllers/management/proposals_controller.rb index 340a6481c..e6fcc69ea 100644 --- a/app/controllers/management/proposals_controller.rb +++ b/app/controllers/management/proposals_controller.rb @@ -14,7 +14,7 @@ class Management::ProposalsController < Management::BaseController end def print - @proposals = Proposal.all.page(params[:page]).for_render.send("sort_by_#{@current_order}") + @proposals = Proposal.send("sort_by_#{@current_order}").for_render.limit(5) set_proposal_votes(@proposal) end @@ -52,4 +52,4 @@ class Management::ProposalsController < Management::BaseController end ### -end \ No newline at end of file +end diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 8e04c0d61..a707d7e75 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -10,6 +10,16 @@ class Users::RegistrationsController < Devise::RegistrationsController end end + def delete_form + build_resource({}) + end + + def delete + current_user.erase(erase_params[:erase_reason]) + sign_out + redirect_to root_url, notice: t("devise.registrations.destroyed") + end + def success end @@ -32,6 +42,10 @@ class Users::RegistrationsController < Devise::RegistrationsController params.require(:user).permit(:username, :email, :password, :password_confirmation, :captcha, :captcha_key, :terms_of_service) end + def erase_params + params.require(:user).permit(:erase_reason) + end + def after_inactive_sign_up_path_for(resource_or_scope) users_sign_up_success_path end diff --git a/app/models/user.rb b/app/models/user.rb index f652a8f9c..177befd5c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -23,9 +23,10 @@ class User < ActiveRecord::Base has_many :comments, -> { with_hidden } has_many :failed_census_calls - validates :username, presence: true, unless: :organization? - validates :username, uniqueness: true, unless: :organization? + validates :username, presence: true, if: :username_required? + validates :username, uniqueness: true, if: :username_required? validates :document_number, uniqueness: { scope: :document_type }, allow_nil: true + validate :validate_username_length validates :official_level, inclusion: {in: 0..5} @@ -145,6 +146,21 @@ class User < ActiveRecord::Base Proposal.hide_all proposal_ids end + def erase(erase_reason = nil) + self.hide + self.update( + erase_reason: erase_reason, + username: nil, + email: nil, + unconfirmed_email: nil, + document_number: nil, + phone_number: nil, + encrypted_password: "", + confirmation_token: nil, + reset_password_token: nil, + email_verification_token: nil + ) + end def email_provided? !!(email && email !~ OMNIAUTH_EMAIL_REGEX) || @@ -172,8 +188,15 @@ class User < ActiveRecord::Base super end - private + def username_required? + !organization? && !hidden? + end + def email_required? + !hidden? + end + + private def clean_document_number self.document_number = self.document_number.gsub(/[^a-z0-9]+/i, "").upcase unless self.document_number.blank? end diff --git a/app/views/account/show.html.erb b/app/views/account/show.html.erb index c52219955..40cf43a59 100644 --- a/app/views/account/show.html.erb +++ b/app/views/account/show.html.erb @@ -1,6 +1,10 @@
diff --git a/app/views/management/proposals/new.html.erb b/app/views/management/proposals/new.html.erb index 28c150e86..680552b9a 100644 --- a/app/views/management/proposals/new.html.erb +++ b/app/views/management/proposals/new.html.erb @@ -1,8 +1,9 @@ -<%= render '/shared/print' %> -
+ <%= t("devise_views.users.registrations.delete_form.info_reason") %> +
+ + <%= f.text_field :erase_reason, + autofocus: true, + placeholder: t("devise_views.users.registrations.delete_form.erase_reason_label"), + label: t("devise_views.users.registrations.delete_form.erase_reason_label") %> + + <%= f.submit t("devise_views.users.registrations.delete_form.submit"), class: "button radius alert expand" %> +<% end %> diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 5f4943744..d725a77d8 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -97,6 +97,7 @@ ignore_missing: - 'errors.messages.blank' - 'errors.messages.taken' - 'devise.failure.invalid' + - 'devise.registrations.destroyed' ## Consider these keys used: ignore_unused: diff --git a/config/locales/devise_views.en.yml b/config/locales/devise_views.en.yml index 2c04d3ce8..2c7ef86bb 100644 --- a/config/locales/devise_views.en.yml +++ b/config/locales/devise_views.en.yml @@ -77,6 +77,12 @@ en: instructions_1_html: "Please review your email inbox - we have sent you a link to confirm your account." instructions_2_html: "Once you click that link, you will be able to start participating" back_to_index: "Understood, take me back to the home page" + delete_form: + title: "Erase account" + info: "This action can not be undone. Please make sure this is what you want." + info_reason: "If you want, leave us a reason (optional)" + erase_reason_label: "Reason" + submit: "Erase my account" organizations: registrations: new: diff --git a/config/locales/devise_views.es.yml b/config/locales/devise_views.es.yml index f2ca13215..42576b6ff 100644 --- a/config/locales/devise_views.es.yml +++ b/config/locales/devise_views.es.yml @@ -77,6 +77,12 @@ es: instructions_1_html: "Por favor revisa tu correo electrónico - te hemos enviado un enlace para confirmar tu cuenta." instructions_2_html: "Una vez confirmado, podrás empezar a participar." back_to_index: "Entendido, volver a la página principal" + delete_form: + title: "Darme de baja" + info: "Esta acción no se puede deshacer. Una vez que des de baja tu cuenta, no podrás volver a hacer login con ella." + info_reason: "Si quieres, escribe el motivo por el que te das de baja (opcional)." + erase_reason_label: "Razón de la baja" + submit: "Borrar mi cuenta" organizations: registrations: new: diff --git a/config/locales/en.yml b/config/locales/en.yml index 7365d2703..e2d604c72 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -283,6 +283,7 @@ en: change_credentials_link: "Change my credentials" email_on_comment_label: "Receive email when someone comments on my debates or proposals" email_on_comment_reply_label: "Receive email when someone replies to my comments" + erase_account_link: "Erase my account" personal: "Personal data" username_label: "Username" phone_number_label: "Phone number" diff --git a/config/locales/es.yml b/config/locales/es.yml index 6c94c2ef5..c20524f47 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -283,6 +283,7 @@ es: change_credentials_link: "Cambiar mis datos de acceso" email_on_comment_label: "Recibir un email cuando alguien comenta en mis propuestas o debates" email_on_comment_reply_label: "Recibir un email cuando alguien contesta a mis comentarios" + erase_account_link: "Darme de baja" personal: "Datos personales" username_label: "Nombre de usuario" phone_number_label: "Teléfono" diff --git a/config/routes.rb b/config/routes.rb index a69f08ab3..712e835c1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,9 +1,5 @@ Rails.application.routes.draw do - as :user do - match '/user/confirmation' => 'users/confirmations#update', :via => :patch, :as => :update_user_confirmation - end - devise_for :users, controllers: { registrations: 'users/registrations', sessions: 'users/sessions', @@ -22,7 +18,11 @@ Rails.application.routes.draw do end devise_scope :user do + patch '/user/confirmation', to: 'users/confirmations#update', as: :update_user_confirmation + get 'users/sign_up/success', to: 'users/registrations#success' + get 'users/registrations/delete_form', to: 'users/registrations#delete_form' + delete 'users/registrations', to: 'users/registrations#delete' get :finish_signup, to: 'users/registrations#finish_signup' patch :do_finish_signup, to: 'users/registrations#do_finish_signup' end @@ -60,7 +60,9 @@ Rails.application.routes.draw do end end - resource :account, controller: "account", only: [:show, :update] + resource :account, controller: "account", only: [:show, :update, :delete] do + collection { get :erase } + end resource :verification, controller: "verification", only: [:show] scope module: :verification do diff --git a/db/migrate/20151016110703_add_erase_reason_to_users.rb b/db/migrate/20151016110703_add_erase_reason_to_users.rb new file mode 100644 index 000000000..be88869b5 --- /dev/null +++ b/db/migrate/20151016110703_add_erase_reason_to_users.rb @@ -0,0 +1,5 @@ +class AddEraseReasonToUsers < ActiveRecord::Migration + def change + add_column :users, :erase_reason, :string + end +end diff --git a/db/migrate/20151019133719_remove_user_constraints.rb b/db/migrate/20151019133719_remove_user_constraints.rb new file mode 100644 index 000000000..0f59a3d34 --- /dev/null +++ b/db/migrate/20151019133719_remove_user_constraints.rb @@ -0,0 +1,5 @@ +class RemoveUserConstraints < ActiveRecord::Migration + def change + change_column(:users, :email, :string, null: true, unique: true) + end +end diff --git a/db/schema.rb b/db/schema.rb index b0af9bb10..8b03adc9e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -257,7 +257,7 @@ ActiveRecord::Schema.define(version: 20151020112354) do add_index "tags", ["proposals_count"], name: "index_tags_on_proposals_count", using: :btree create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false + t.string "email", default: "" t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" @@ -293,6 +293,7 @@ ActiveRecord::Schema.define(version: 20151020112354) do t.string "letter_verification_code" t.integer "failed_census_calls_count", default: 0 t.datetime "level_two_verified_at" + t.string "erase_reason" end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree diff --git a/spec/features/account_spec.rb b/spec/features/account_spec.rb index e0249072c..029864ec9 100644 --- a/spec/features/account_spec.rb +++ b/spec/features/account_spec.rb @@ -81,4 +81,20 @@ feature 'Account' do expect(page).to have_content error_message end + + scenario 'Erasing account' do + visit account_path + + click_link 'Erase my account' + + fill_in 'user_erase_reason', with: 'a test' + + click_button 'Erase my account' + + expect(page).to have_content "Your account has been successfully cancelled" + + login_through_form_as(@user) + + expect(page).to have_content "Invalid email or password" + end end diff --git a/spec/features/management/proposals_spec.rb b/spec/features/management/proposals_spec.rb index d3339020f..46411c7af 100644 --- a/spec/features/management/proposals_spec.rb +++ b/spec/features/management/proposals_spec.rb @@ -104,16 +104,12 @@ feature 'Proposals' do context "Printing" do scenario 'Printing proposals', :js do - 5.times { create(:proposal) } + 6.times { create(:proposal) } click_link "Print proposals" - find("#print_link").click - - ### CHANGE ME - # should probably test something else here - # maybe that we are loading a print.css stylesheet? - ### + expect(page).to have_css('.proposal', count: 5) + expect(page).to have_css("a[href='javascript:window.print();']", text: 'Print') end scenario "Filtering proposals to be printed", :js do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 14f20ad1d..2e92ced35 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -302,4 +302,36 @@ describe User do end + describe "#erase" do + it "anonymizes a user and marks him as hidden" do + user = create(:user, + username: "manolo", + unconfirmed_email: "a@a.com", + document_number: "1234", + phone_number: "5678", + encrypted_password: "foobar", + confirmation_token: "token1", + reset_password_token: "token2", + email_verification_token: "token3") + user.erase('a test') + user.reload + + expect(user.erase_reason).to eq('a test') + expect(user.hidden_at).to be + + expect(user.username).to be_nil + + expect(user.email).to be_nil + expect(user.unconfirmed_email).to be_nil + expect(user.document_number).to be_nil + expect(user.phone_number).to be_nil + + expect(user.encrypted_password).to be_empty + + expect(user.confirmation_token).to be_nil + expect(user.reset_password_token).to be_nil + expect(user.email_verification_token).to be_nil + end + end + end