This PR allows users to delete their own accounts. The logic implemented matches
that currently used by the admins when they manually close accounts, although
there is room to be more complex in future e.g. completely removing accounts
with no content.
The error handling has been slightly adapted for namespaced controllers, by
anchoring the controller name with a leading forward slash.
if user
can :welcome, :site
can [:revoke, :authorize], :oauth
+ can [:show], :deletion
if Settings.status != "database_offline"
can [:index, :new, :create, :show, :edit, :update, :destroy], ClientApplication
can [:index, :new, :create, :show, :edit, :update, :destroy], :oauth2_application
can [:index, :destroy], :oauth2_authorized_application
can [:new, :show, :create, :destroy], :oauth2_authorization
- can [:edit, :update], :account
+ can [:edit, :update, :destroy], :account
can [:show], :dashboard
can [:new, :create, :edit, :update, :comment, :subscribe, :unsubscribe], DiaryEntry
can [:make_friend, :remove_friend], Friendship
--- /dev/null
+module Account
+ class DeletionsController < ApplicationController
+ layout "site"
+
+ before_action :authorize_web
+ before_action :set_locale
+
+ authorize_resource :class => false
+
+ def show; end
+ end
+end
redirect_to auth_url(params[:user][:auth_provider], params[:user][:auth_uid]), :status => :temporary_redirect
end
end
+
+ def destroy
+ current_user.soft_destroy!
+
+ session.delete(:user)
+ session_expires_automatically
+
+ flash[:notice] = t ".success"
+ redirect_to root_path
+ end
end
elsif current_user
set_locale
respond_to do |format|
- format.html { redirect_to :controller => "errors", :action => "forbidden" }
+ format.html { redirect_to :controller => "/errors", :action => "forbidden" }
format.any { report_error t("application.permission_denied"), :forbidden }
end
elsif request.get?
--- /dev/null
+<% content_for :heading do %>
+ <h1><%= t ".title" %></h1>
+<% end %>
+
+<%= render :partial => "settings_menu" %>
+
+<div class="alert alert-danger row mx-0 p-3 align-items-center">
+ <div class="col-auto">
+ <picture>
+ <source srcset="<%= image_path "notice.svg" %>" type="image/svg+xml"></source>
+ <%= image_tag("notice.png", :srcset => image_path("notice.svg")) %>
+ </picture>
+ </div>
+ <div class="col">
+ <p class="mb-0"><%= t ".warning" %></p>
+ </div>
+</div>
+
+<p><%= t ".delete_introduction" %></p>
+
+<ul>
+ <li><%= t ".delete_profile" %></li>
+ <li><%= t ".delete_display_name" %></li>
+</ul>
+
+<p><%= t ".retain_caveats" %></p>
+
+<ul>
+ <li><%= t ".retain_edits" %></li>
+ <li><%= t ".retain_traces" %></li>
+ <li><%= t ".retain_diary_entries" %></li>
+ <li><%= t ".retain_notes" %></li>
+ <li><%= t ".retain_changeset_discussions" %></li>
+ <li><%= t ".retain_email" %></li>
+</ul>
+
+<%= link_to t(".delete_account"), account_path, { :method => :delete, :class => "btn btn-danger", :data => { :confirm => t(".confirm_delete") } } %>
+<%= link_to t(".cancel"), edit_account_path, :class => "btn btn-link" %>
</span>
</div>
- <%= f.primary t(".save changes button") %>
+ <div class="row justify-content-between">
+ <div class="col-auto btn-wrapper">
+ <%= f.primary t(".save changes button") %>
+ </div>
+ <div class="col-auto btn-wrapper">
+ <%= link_to t(".delete_account"), account_deletion_path, :class => "btn btn-outline-danger" %>
+ </div>
+ </div>
<% end %>
<% unless current_user.data_public? %>
<% content_for :heading do %>
<ul class="nav nav-tabs flex-column flex-sm-row">
<li class="nav-item">
- <%= link_to t(".account_settings"), edit_account_path, :class => "nav-link #{'active' if controller_name == 'accounts'}" %>
+ <%= link_to t(".account_settings"), edit_account_path, :class => "nav-link #{'active' if %w[accounts deletions].include?(controller_name)}" %>
</li>
<li class="nav-item">
<%= link_to t(".oauth1_settings"), oauth_clients_path(current_user), :class => "nav-link #{'active' if controller_name == 'oauth_clients'}" %>
entry:
comment: Comment
full: Full note
+ account:
+ deletions:
+ show:
+ title: Delete My Account
+ warning: Warning! The account deletion process is final, and cannot be reversed.
+ delete_account: Delete Account
+ delete_introduction: "You can delete your OpenStreetMap account using the button below. Please note the following details:"
+ delete_profile: Your profile information, including your avatar, description and home location will be removed.
+ delete_display_name: Your display name will be removed, and can be reused by other accounts.
+ retain_caveats: "However, some information about you will be retained on OpenStreetMap, even after your account is deleted:"
+ retain_edits: Your edits to the map database, if any, will be retained.
+ retain_traces: Your uploaded traces, if any, will be retained.
+ retain_diary_entries: Your diary entries and diary comments, if any, will be retained but hidden from view.
+ retain_notes: Your map notes and note comments, if any, will be retained but hidden from view.
+ retain_changeset_discussions: Your changeset discussions, if any, will be retained.
+ retain_email: Your email address will be retained.
+ confirm_delete: Are you sure?
+ cancel: Cancel
accounts:
edit:
title: "Edit account"
link text: "what is this?"
save changes button: Save Changes
make edits public button: Make all my edits public
+ delete_account: Delete Account...
update:
success_confirm_needed: "User information updated successfully. Check your email for a note to confirm your new email address."
success: "User information updated successfully."
+ destroy:
+ success: "Account Deleted."
browse:
created: "Created"
closed: "Closed"
get "/user/:display_name/account", :to => redirect(:path => "/account/edit")
post "/user/:display_name/set_status" => "users#set_status", :as => :set_status_user
- resource :account, :only => [:edit, :update]
+ resource :account, :only => [:edit, :update, :destroy]
+
+ namespace :account do
+ resource :deletion, :only => [:show]
+ end
resource :dashboard, :only => [:show]
resource :preferences, :only => [:show, :edit, :update]
resource :profile, :only => [:edit, :update]
--- /dev/null
+require "application_system_test_case"
+
+class AccountDeletionTest < ApplicationSystemTestCase
+ def setup
+ @user = create(:user, :display_name => "test user")
+ sign_in_as(@user)
+ end
+
+ test "the status is deleted and the personal data removed" do
+ visit edit_account_path
+
+ click_on "Delete Account..."
+ accept_confirm do
+ click_on "Delete Account"
+ end
+
+ assert_current_path root_path
+ @user.reload
+ assert_equal "deleted", @user.status
+ assert_equal "user_#{@user.id}", @user.display_name
+ end
+
+ test "the user is signed out after deletion" do
+ visit edit_account_path
+
+ click_on "Delete Account..."
+ accept_confirm do
+ click_on "Delete Account"
+ end
+
+ assert_content "Log In"
+ end
+
+ test "the user is shown a confirmation flash message" do
+ visit edit_account_path
+
+ click_on "Delete Account..."
+ accept_confirm do
+ click_on "Delete Account"
+ end
+
+ assert_content "Account Deleted"
+ end
+end