about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2017-06-14 18:01:27 +0200
committerGitHub <noreply@github.com>2017-06-14 18:01:27 +0200
commit4a618908e836ecb94f70e99f2198ee7b3ba3b2ec (patch)
treef2a02c2deaf9c1af2b53dae705cc652f83e08db7 /app
parenta208e7d65581168cda04be543742f302a162ac1a (diff)
Account deletion (#3728)
* Add form for account deletion

* If avatar or header are gone from source, remove them

* Add option to have SuspendAccountService remove user record, add tests

* Exclude suspended accounts from search
Diffstat (limited to 'app')
-rw-r--r--app/controllers/settings/deletes_controller.rb27
-rw-r--r--app/javascript/styles/admin.scss15
-rw-r--r--app/javascript/styles/forms.scss3
-rw-r--r--app/models/account.rb3
-rw-r--r--app/models/form/delete_confirmation.rb7
-rw-r--r--app/services/suspend_account_service.rb7
-rw-r--r--app/services/update_remote_profile_service.rb15
-rw-r--r--app/views/auth/registrations/edit.html.haml5
-rw-r--r--app/views/settings/deletes/show.html.haml16
-rw-r--r--app/workers/admin/suspension_worker.rb4
10 files changed, 97 insertions, 5 deletions
diff --git a/app/controllers/settings/deletes_controller.rb b/app/controllers/settings/deletes_controller.rb
new file mode 100644
index 000000000..55c18345b
--- /dev/null
+++ b/app/controllers/settings/deletes_controller.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+class Settings::DeletesController < ApplicationController
+  layout 'admin'
+
+  before_action :authenticate_user!
+
+  def show
+    @confirmation = Form::DeleteConfirmation.new
+  end
+
+  def destroy
+    if current_user.valid_password?(delete_params[:password])
+      Admin::SuspensionWorker.perform_async(current_user.account_id, true)
+      sign_out
+      redirect_to new_user_session_path, notice: I18n.t('deletes.success_msg')
+    else
+      redirect_to settings_delete_path, alert: I18n.t('deletes.bad_password_msg')
+    end
+  end
+
+  private
+
+  def delete_params
+    params.permit(:password)
+  end
+end
diff --git a/app/javascript/styles/admin.scss b/app/javascript/styles/admin.scss
index 541e57f32..c2bfc10a0 100644
--- a/app/javascript/styles/admin.scss
+++ b/app/javascript/styles/admin.scss
@@ -96,6 +96,13 @@
       margin-bottom: 40px;
     }
 
+    h6 {
+      font-size: 16px;
+      color: $ui-primary-color;
+      line-height: 28px;
+      font-weight: 400;
+    }
+
     & > p {
       font-size: 14px;
       line-height: 18px;
@@ -114,6 +121,14 @@
       background: transparent;
       border-bottom: 1px solid $ui-base-color;
     }
+
+    .muted-hint {
+      color: lighten($ui-base-color, 27%);
+
+      a {
+        color: $ui-primary-color;
+      }
+    }
   }
 
   .simple_form {
diff --git a/app/javascript/styles/forms.scss b/app/javascript/styles/forms.scss
index 6af846b0a..059c4a7d8 100644
--- a/app/javascript/styles/forms.scss
+++ b/app/javascript/styles/forms.scss
@@ -303,7 +303,10 @@ code {
       font-weight: 500;
     }
   }
+}
 
+.simple_form,
+.table-form {
   .warning {
     max-width: 400px;
     box-sizing: border-box;
diff --git a/app/models/account.rb b/app/models/account.rb
index 5c0f70ff8..72bad51a2 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -177,6 +177,7 @@ class Account < ApplicationRecord
           account_id IN (SELECT * FROM first_degree)
           AND target_account_id NOT IN (SELECT * FROM first_degree)
           AND target_account_id NOT IN (:excluded_account_ids)
+          AND accounts.suspended = false
         GROUP BY target_account_id, accounts.id
         ORDER BY count(account_id) DESC
         OFFSET :offset
@@ -199,6 +200,7 @@ class Account < ApplicationRecord
           ts_rank_cd(#{textsearch}, #{query}, 32) AS rank
         FROM accounts
         WHERE #{query} @@ #{textsearch}
+          AND accounts.suspended = false
         ORDER BY rank DESC
         LIMIT ?
       SQL
@@ -216,6 +218,7 @@ class Account < ApplicationRecord
         FROM accounts
         LEFT OUTER JOIN follows AS f ON (accounts.id = f.account_id AND f.target_account_id = ?) OR (accounts.id = f.target_account_id AND f.account_id = ?)
         WHERE #{query} @@ #{textsearch}
+          AND accounts.suspended = false
         GROUP BY accounts.id
         ORDER BY rank DESC
         LIMIT ?
diff --git a/app/models/form/delete_confirmation.rb b/app/models/form/delete_confirmation.rb
new file mode 100644
index 000000000..0884a09b8
--- /dev/null
+++ b/app/models/form/delete_confirmation.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Form::DeleteConfirmation
+  include ActiveModel::Model
+
+  attr_accessor :password
+end
diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb
index 4a4f23b80..842500259 100644
--- a/app/services/suspend_account_service.rb
+++ b/app/services/suspend_account_service.rb
@@ -1,9 +1,10 @@
 # frozen_string_literal: true
 
 class SuspendAccountService < BaseService
-  def call(account)
+  def call(account, remove_user = false)
     @account = account
 
+    purge_user if remove_user
     purge_content
     purge_profile
     unsubscribe_push_subscribers
@@ -11,6 +12,10 @@ class SuspendAccountService < BaseService
 
   private
 
+  def purge_user
+    @account.user.destroy
+  end
+
   def purge_content
     @account.statuses.reorder(nil).find_each do |status|
       # This federates out deletes to previous followers
diff --git a/app/services/update_remote_profile_service.rb b/app/services/update_remote_profile_service.rb
index c65f35867..49a907682 100644
--- a/app/services/update_remote_profile_service.rb
+++ b/app/services/update_remote_profile_service.rb
@@ -27,8 +27,19 @@ class UpdateRemoteProfileService < BaseService
     account.locked       = remote_profile.locked?
 
     if !account.suspended? && !DomainBlock.find_by(domain: account.domain)&.reject_media?
-      account.avatar_remote_url = remote_profile.avatar if remote_profile.avatar.present?
-      account.header_remote_url = remote_profile.header if remote_profile.header.present?
+      if remote_profile.avatar.present?
+        account.avatar_remote_url = remote_profile.avatar
+      else
+        account.avatar_remote_url = ''
+        account.avatar.destroy
+      end
+
+      if remote_profile.header.present?
+        account.header_remote_url = remote_profile.header
+      else
+        account.header_remote_url = ''
+        account.header.destroy
+      end
     end
   end
 end
diff --git a/app/views/auth/registrations/edit.html.haml b/app/views/auth/registrations/edit.html.haml
index 8ab188f0c..cbaa75ae0 100644
--- a/app/views/auth/registrations/edit.html.haml
+++ b/app/views/auth/registrations/edit.html.haml
@@ -11,3 +11,8 @@
 
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
+
+%hr/
+
+%h6= t('auth.delete_account')
+%p.muted-hint= t('auth.delete_account_html', path: settings_delete_path)
diff --git a/app/views/settings/deletes/show.html.haml b/app/views/settings/deletes/show.html.haml
new file mode 100644
index 000000000..d49a7bd0c
--- /dev/null
+++ b/app/views/settings/deletes/show.html.haml
@@ -0,0 +1,16 @@
+- content_for :page_title do
+  = t('settings.delete')
+
+= simple_form_for @confirmation, url: settings_delete_path, method: :delete do |f|
+  .warning
+    %strong
+      = fa_icon('warning')
+      = t('deletes.warning_title')
+    = t('deletes.warning_html')
+
+  %p.hint= t('deletes.description_html')
+
+  = f.input :password, autocomplete: 'off', placeholder: t('simple_form.labels.defaults.current_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.current_password') }, hint: t('deletes.confirm_password')
+
+  .actions
+    = f.button :button, t('deletes.proceed'), type: :submit, class: 'negative'
diff --git a/app/workers/admin/suspension_worker.rb b/app/workers/admin/suspension_worker.rb
index 7ef2b35ec..6338b1130 100644
--- a/app/workers/admin/suspension_worker.rb
+++ b/app/workers/admin/suspension_worker.rb
@@ -5,7 +5,7 @@ class Admin::SuspensionWorker
 
   sidekiq_options queue: 'pull'
 
-  def perform(account_id)
-    SuspendAccountService.new.call(Account.find(account_id))
+  def perform(account_id, remove_user = false)
+    SuspendAccountService.new.call(Account.find(account_id), remove_user)
   end
 end