diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
index c3a897b7637e373a2b15618cd2092bfdb1a8994e..862547f78e82ecc8e9cb7f1afdc7d62d4c5805d7 100644
--- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
+++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java
@@ -359,7 +359,6 @@ public class BaseSecurityManager implements BaseSecurity, UserDataDeletable {
 		IdentityImpl iimpl = new IdentityImpl();
 		iimpl.setUser(user);
 		iimpl.setName(username);
-		iimpl.setLastLogin(new Date());
 		iimpl.setExternalId(externalId);
 		iimpl.setStatus(Identity.STATUS_ACTIV);
 		((UserImpl)user).setIdentity(iimpl);
diff --git a/src/main/java/org/olat/core/dispatcher/DispatcherModule.java b/src/main/java/org/olat/core/dispatcher/DispatcherModule.java
index e5a9f3530db623c3a822c2590a81541cd01dd11e..4a7b19393dcf01eded1e4702c1bd57c703af705d 100644
--- a/src/main/java/org/olat/core/dispatcher/DispatcherModule.java
+++ b/src/main/java/org/olat/core/dispatcher/DispatcherModule.java
@@ -136,10 +136,6 @@ public class DispatcherModule {
 	public static final void redirectToDefaultDispatcher(HttpServletResponse response) {
 		redirectTo(response, WebappHelper.getServletContextPath() + PATH_DEFAULT);
 	}
-	
-	public static final void redirectToMobile(HttpServletResponse response) {
-		redirectTo(response, WebappHelper.getServletContextPath() + WebappHelper.getMobileContext());
-	}
 
 	/**
 	 * Generic redirect method.
diff --git a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java
index 3611692916e3bc9aa811eabfc3827f729a14d71b..2217ebe4ea58af7659c41779ea4a277185b330e8 100644
--- a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java
+++ b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java
@@ -227,7 +227,7 @@ public class LDAPAuthenticationController extends AuthenticationController imple
 				// accept disclaimer first
 				
 				removeAsListenerAndDispose(disclaimerCtr);
-				disclaimerCtr = new DisclaimerController(ureq, getWindowControl());
+				disclaimerCtr = new DisclaimerController(ureq, getWindowControl(), authenticatedIdentity, false);
 				listenTo(disclaimerCtr);
 				
 				removeAsListenerAndDispose(cmc);
diff --git a/src/main/java/org/olat/login/OLATAuthenticationController.java b/src/main/java/org/olat/login/OLATAuthenticationController.java
index 8279840daa8fd83816e8adcc16db3d2d19cdc27a..240b85a7bc068863f3850d782554668b262dc031 100644
--- a/src/main/java/org/olat/login/OLATAuthenticationController.java
+++ b/src/main/java/org/olat/login/OLATAuthenticationController.java
@@ -175,9 +175,6 @@ public class OLATAuthenticationController extends AuthenticationController imple
 		cmc.activate();
 	}
 
-	/**
-	 * @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest, org.olat.core.gui.control.Controller, org.olat.core.gui.control.Event)
-	 */
 	@Override
 	public void event(UserRequest ureq, Controller source, Event event) {
 		if (source == loginForm && event == Event.DONE_EVENT) {
@@ -218,7 +215,7 @@ public class OLATAuthenticationController extends AuthenticationController imple
 				// accept disclaimer first
 				
 				removeAsListenerAndDispose(disclaimerCtr);
-				disclaimerCtr = new DisclaimerController(ureq, getWindowControl());
+				disclaimerCtr = new DisclaimerController(ureq, getWindowControl(), authenticatedIdentity, false);
 				listenTo(disclaimerCtr);
 				
 				removeAsListenerAndDispose(cmc);
diff --git a/src/main/java/org/olat/login/oauth/ui/OAuthDisclaimerController.java b/src/main/java/org/olat/login/oauth/ui/OAuthDisclaimerController.java
index 564624601032c75adb5bff61c5d2c2474c340ac6..835ade37a290bf1c0d15620a4fc748d40b46a00e 100644
--- a/src/main/java/org/olat/login/oauth/ui/OAuthDisclaimerController.java
+++ b/src/main/java/org/olat/login/oauth/ui/OAuthDisclaimerController.java
@@ -80,7 +80,7 @@ public class OAuthDisclaimerController extends FormBasicController implements Ac
 
 	@Override
 	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
-		disclaimerController = new DisclaimerController(ureq, getWindowControl());
+		disclaimerController = new DisclaimerController(ureq, getWindowControl(), null, false);
 		listenTo(disclaimerController);
 		
 		cmc = new CloseableModalController(getWindowControl(), translate("close"), disclaimerController.getInitialComponent(),
diff --git a/src/main/java/org/olat/login/oauth/ui/OAuthRegistrationController.java b/src/main/java/org/olat/login/oauth/ui/OAuthRegistrationController.java
index 0df609b858e91339f512a6e22b36889f29a4cd12..7ec37f240c8c17758ad9a9975ee9d3f352021e4e 100644
--- a/src/main/java/org/olat/login/oauth/ui/OAuthRegistrationController.java
+++ b/src/main/java/org/olat/login/oauth/ui/OAuthRegistrationController.java
@@ -228,7 +228,7 @@ public class OAuthRegistrationController extends FormBasicController {
 		
 		//open disclaimer
 		removeAsListenerAndDispose(disclaimerController);
-		disclaimerController = new DisclaimerController(ureq, getWindowControl());
+		disclaimerController = new DisclaimerController(ureq, getWindowControl(), authenticatedIdentity, false);
 		listenTo(disclaimerController);
 		
 		cmc = new CloseableModalController(getWindowControl(), translate("close"), disclaimerController.getInitialComponent(),
diff --git a/src/main/java/org/olat/registration/DisclaimerController.java b/src/main/java/org/olat/registration/DisclaimerController.java
index 5b474cfdc30ebddda8800e5d51ecaf87255b423f..826425fbb2d9640302f455ba87a4bd9dfd6b26e3 100644
--- a/src/main/java/org/olat/registration/DisclaimerController.java
+++ b/src/main/java/org/olat/registration/DisclaimerController.java
@@ -28,6 +28,7 @@ package org.olat.registration;
 import java.io.File;
 import java.util.Locale;
 
+import org.olat.admin.user.delete.service.UserDeletionManager;
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
 import org.olat.core.gui.components.link.Link;
@@ -37,11 +38,18 @@ import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.Event;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
+import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
+import org.olat.core.id.Identity;
+import org.olat.core.id.UserConstants;
 import org.olat.core.util.WebappHelper;
+import org.olat.core.util.mail.ContactList;
+import org.olat.core.util.mail.ContactMessage;
 import org.olat.core.util.vfs.LocalFolderImpl;
 import org.olat.core.util.vfs.VFSContainer;
 import org.olat.core.util.vfs.VFSLeaf;
 import org.olat.core.util.vfs.VFSMediaResource;
+import org.olat.modules.co.ContactFormController;
+import org.olat.user.UserModule;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
@@ -69,31 +77,52 @@ public class DisclaimerController extends BasicController {
 	private static final String SR_ERROR_DISCLAIMER_CHECKBOX = "sr.error.disclaimer.checkbox";
 	private static final String SR_ERROR_DISCLAIMER_CHECKBOXES = "sr.error.disclaimer.checkboxes";
 
-	private VelocityContainer main;
-	private DisclaimerFormController disclaimerFormController;
 	private Link downloadLink;
 	private VFSLeaf downloadFile;
+	private VelocityContainer main;
+
+	private CloseableModalController cmc;
+	private ContactFormController contactCtrl;
+	private DisclaimerFormController disclaimerFormController;
+	private RequestAccountDeletionController requestAccountDeletetionCtrl;
+	private RequestAccountDataDeletetionController requestAccountDataDeletetionCtrl;
+	
+	private Identity identity;
 
+	@Autowired
+	private UserModule userModule;
 	@Autowired
 	private RegistrationModule registrationModule;
-
+	@Autowired
+	private UserDeletionManager userDeletionManager;
+	
 	/**
-	 * Display a disclaimer which can be accepted or denied.
+	 * Display the disclaimer in a read only view to the current user.
+	 * 
 	 * @param ureq
 	 * @param wControl
 	 */
 	public DisclaimerController(UserRequest ureq, WindowControl wControl) {
-		this(ureq, wControl, false);
+		this(ureq, wControl, ureq.getIdentity(), true);
+		
+		if(userModule.isAllowRequestToDeleteAccount() && ureq.getIdentity() != null) {
+			requestAccountDeletetionCtrl = new RequestAccountDeletionController(ureq, getWindowControl());
+			listenTo(requestAccountDeletetionCtrl);
+			main.put("radform", requestAccountDeletetionCtrl.getInitialComponent());
+		}
 	}
 
 	/**
 	 * Display a disclaimer which can be accepted or denied or in a read only manner
-	 * @param ureq
-	 * @param wControl
+	 * @param ureq The user request
+	 * @param wControl The window control
+	 * @param identity The identity which need to accept the disclaimer (or null if it doesn't exist now)
 	 * @param readOnly true: show only read only; false: allow user to accept
 	 */
-	public DisclaimerController(UserRequest ureq, WindowControl wControl, boolean readOnly) {
+	public DisclaimerController(UserRequest ureq, WindowControl wControl, Identity identity, boolean readOnly) {
 		super(ureq, wControl);
+		
+		this.identity = identity;
 	
 		disclaimerFormController = new DisclaimerFormController(ureq, wControl, readOnly);
 		listenTo(disclaimerFormController);
@@ -122,6 +151,7 @@ public class DisclaimerController extends BasicController {
 				}
 			}
 		}
+
 		putInitialPanel(main);
 	}
 
@@ -138,30 +168,64 @@ public class DisclaimerController extends BasicController {
 	protected void event(UserRequest ureq, Controller source, Event event) {
 		if (source == disclaimerFormController) {
 			if (event == Event.CANCELLED_EVENT) {
-				fireEvent(ureq, Event.CANCELLED_EVENT);
+				doCancel(ureq);
 			} else if (event == Event.DONE_EVENT) {
-				// Verify that, if the additional checkbox is configured to be visible, it is checked as well
-				boolean accepted = (disclaimerFormController.acceptCheckbox != null) ? (disclaimerFormController.acceptCheckbox.isSelected(0)) : false;
-				// configure additional checkbox, see class comments in DisclaimerFormController
-				if (accepted && registrationModule.isDisclaimerAdditionalCheckbox()) {
-					accepted = (disclaimerFormController.additionalCheckbox != null) ? (disclaimerFormController.additionalCheckbox.isSelected(0)) : false;
-					if (accepted && registrationModule.isDisclaimerAdditionalCheckbox2()) {
-						accepted = (disclaimerFormController.additionalCheckbox2 != null) ? (disclaimerFormController.additionalCheckbox2.isSelected(0)) : false;
-					}
-				}
-				if (accepted) {
-					fireEvent(ureq, Event.DONE_EVENT);
-				} else if (registrationModule.isDisclaimerAdditionalCheckbox()) {
-					// error handling case multiple checkboxes enabled
-					showError(SR_ERROR_DISCLAIMER_CHECKBOXES);									
-				} else {
-					// error handling case single checkboxe enabled
-					showError(SR_ERROR_DISCLAIMER_CHECKBOX);
-				}
+				acceptDisclaimer(ureq);
+			}
+		} else if(requestAccountDeletetionCtrl == source) {
+			if (event == Event.DONE_EVENT) {
+				fireEvent(ureq, Event.CANCELLED_EVENT);
+			}
+		} else if(requestAccountDataDeletetionCtrl == source) {
+			if(event == Event.CANCELLED_EVENT) {
+				fireEvent(ureq, Event.CANCELLED_EVENT);
+			}
+			cmc.deactivate();
+			cleanUp();
+			if(event == Event.DONE_EVENT) {
+				doDeleteData(ureq);
 			}
+		} else if(contactCtrl == source) {
+			cmc.deactivate();
+			cleanUp();
+			fireEvent(ureq, Event.CANCELLED_EVENT);
+			if(event == Event.DONE_EVENT) {
+				showInfo("request.delete.account.sent");
+			}
+		} else if(cmc == source) {
+			cleanUp();
+		}
+	}
+	
+	private void cleanUp() {
+		removeAsListenerAndDispose(requestAccountDataDeletetionCtrl);
+		removeAsListenerAndDispose(contactCtrl);
+		removeAsListenerAndDispose(cmc);
+		requestAccountDataDeletetionCtrl = null;
+		contactCtrl = null;
+		cmc = null;
+	}
+	
+	private void acceptDisclaimer(UserRequest ureq) {
+		// Verify that, if the additional checkbox is configured to be visible, it is checked as well
+		boolean accepted = (disclaimerFormController.acceptCheckbox != null) ? (disclaimerFormController.acceptCheckbox.isSelected(0)) : false;
+		// configure additional checkbox, see class comments in DisclaimerFormController
+		if (accepted && registrationModule.isDisclaimerAdditionalCheckbox()) {
+			accepted = (disclaimerFormController.additionalCheckbox != null) ? (disclaimerFormController.additionalCheckbox.isSelected(0)) : false;
+			if (accepted && registrationModule.isDisclaimerAdditionalCheckbox2()) {
+				accepted = (disclaimerFormController.additionalCheckbox2 != null) ? (disclaimerFormController.additionalCheckbox2.isSelected(0)) : false;
+			}
+		}
+		if (accepted) {
+			fireEvent(ureq, Event.DONE_EVENT);
+		} else if (registrationModule.isDisclaimerAdditionalCheckbox()) {
+			// error handling case multiple checkboxes enabled
+			showError(SR_ERROR_DISCLAIMER_CHECKBOXES);									
+		} else {
+			// error handling case single checkboxe enabled
+			showError(SR_ERROR_DISCLAIMER_CHECKBOX);
 		}
 	}
-
 	
 	/**
 	 * Change the locale of this controller.
@@ -172,6 +236,57 @@ public class DisclaimerController extends BasicController {
 		main.put("dclform", this.disclaimerFormController.getInitialComponent());
 	}
 	
+	private void doDeleteData(UserRequest ureq) {
+		if(identity.getLastLogin() == null) {
+			userDeletionManager.deleteIdentity(identity, identity);
+			fireEvent(ureq, Event.CANCELLED_EVENT);
+		} else {
+			doOpenContactForm(ureq);
+		}
+	}
+	
+	private void doOpenContactForm(UserRequest ureq) {
+		if(contactCtrl != null) return;
+		
+		String[] args = new String[] {
+			identity.getKey().toString(),											// 0
+			identity.getName(),														// 1
+			identity.getUser().getProperty(UserConstants.FIRSTNAME, getLocale()),	// 2
+			identity.getUser().getProperty(UserConstants.LASTNAME, getLocale())		// 3
+		};
+		ContactMessage contactMessage = new ContactMessage(identity);
+		contactMessage.setSubject(translate("request.delete.account.subject", args));
+		contactMessage.setBodyText(translate("request.delete.account.body", args));
+		
+		String mailAddress = userModule.getMailToRequestAccountDeletion();
+		ContactList contact = new ContactList(mailAddress);
+		contact.add(mailAddress);
+		contactMessage.addEmailTo(contact);
+
+		contactCtrl = new ContactFormController(ureq, getWindowControl(), true, false, false, contactMessage);
+		listenTo(contactCtrl);
+		
+		String title = translate("request.delete.account");
+		cmc = new CloseableModalController(getWindowControl(), "c", contactCtrl.getInitialComponent(), true, title);
+		listenTo(cmc);
+		cmc.activate();
+	}
+	
+	private void doCancel(UserRequest ureq) {
+		if(identity == null || !userModule.isAllowRequestToDeleteAccountDisclaimer()) {
+			fireEvent(ureq, Event.CANCELLED_EVENT);
+			return;
+		}
+		
+		requestAccountDataDeletetionCtrl = new RequestAccountDataDeletetionController(ureq, getWindowControl());
+		listenTo(requestAccountDataDeletetionCtrl);
+		
+		String title = translate("request.data.deletion.title");
+		cmc = new CloseableModalController(getWindowControl(), "c", requestAccountDataDeletetionCtrl.getInitialComponent(), true, title);
+		listenTo(cmc);
+		cmc.activate();
+	}
+	
 	@Override
 	protected void doDispose() {
 		//
diff --git a/src/main/java/org/olat/registration/RegistrationController.java b/src/main/java/org/olat/registration/RegistrationController.java
index bc4d5fd1f37c9495a5cb340f323812e93922f213..11b0e7b1661fe5fd825b8120cd9b47f6fdccb68a 100644
--- a/src/main/java/org/olat/registration/RegistrationController.java
+++ b/src/main/java/org/olat/registration/RegistrationController.java
@@ -308,7 +308,7 @@ public class RegistrationController extends BasicController implements Activatea
 			myContent.contextPut("text", translate("step4.reg.text"));
 			
 			removeAsListenerAndDispose(disclaimerController);
-			disclaimerController = new DisclaimerController(ureq, getWindowControl());
+			disclaimerController = new DisclaimerController(ureq, getWindowControl(), null, false);
 			listenTo(disclaimerController);
 			
 			regarea.setContent(disclaimerController.getInitialComponent());
diff --git a/src/main/java/org/olat/registration/RegistrationManager.java b/src/main/java/org/olat/registration/RegistrationManager.java
index e3960797af69c4a75f19b56ce0b4da1cc449f0cc..e3ebd4120e34e3b9be4fce419babbb9948c89c0c 100644
--- a/src/main/java/org/olat/registration/RegistrationManager.java
+++ b/src/main/java/org/olat/registration/RegistrationManager.java
@@ -538,6 +538,13 @@ public class RegistrationManager implements UserDataDeletable, UserDataExportabl
 		Property disclaimerProperty = propertyManager.createUserPropertyInstance(identity, "user", "dislaimer_accepted", null, 1l, null, null);
 		propertyManager.saveProperty(disclaimerProperty);
 	}
+	
+	public Date getDisclaimerConfirmationDate(Identity identity) {
+		if(identity == null) return null;
+		
+		List<Property> disclaimerProperties = propertyManager.listProperties(identity, null, null, "user", "dislaimer_accepted");
+		return disclaimerProperties.isEmpty() ? null : disclaimerProperties.get(0).getLastModified();
+	}
 
 	/**
 	 * Remove all disclaimer confirmations. This means that every user on the
diff --git a/src/main/java/org/olat/registration/RequestAccountDataDeletetionController.java b/src/main/java/org/olat/registration/RequestAccountDataDeletetionController.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ed5dbb6926d16cc0d7583d3de0b55cacfd55062
--- /dev/null
+++ b/src/main/java/org/olat/registration/RequestAccountDataDeletetionController.java
@@ -0,0 +1,84 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.registration;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.elements.FormCancel;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.Event;
+import org.olat.core.gui.control.WindowControl;
+
+/**
+ * 
+ * Initial date: 4 févr. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class RequestAccountDataDeletetionController extends FormBasicController {
+	
+	private MultipleSelectionElement confirmEl;
+	
+	public RequestAccountDataDeletetionController(UserRequest ureq, WindowControl wControl) {
+		super(ureq, wControl, "request_delete_data");
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		String[] keys = new String[] { "on" };
+		String[] values = new String[] { translate("request.data.deletion.confirm.text") };
+ 		confirmEl = uifactory.addCheckboxesHorizontal("request.data.deletion.confirm", formLayout, keys, values);
+
+		FormCancel cancel = uifactory.addFormCancelButton("no", formLayout, ureq, getWindowControl());
+		cancel.setI18nKey("no");
+		uifactory.addFormSubmitButton("yes", formLayout);
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = super.validateFormLogic(ureq);
+		
+		confirmEl.clearError();
+		if(!confirmEl.isAtLeastSelected(1)) {
+			confirmEl.setErrorKey("request.data.deletion.confirm.error", null);
+			allOk &= false;
+		}
+		
+		return allOk;
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		fireEvent(ureq, Event.DONE_EVENT);
+	}
+
+	@Override
+	protected void formCancelled(UserRequest ureq) {
+		fireEvent(ureq, Event.CANCELLED_EVENT);
+	}
+}
diff --git a/src/main/java/org/olat/registration/RequestAccountDeletionController.java b/src/main/java/org/olat/registration/RequestAccountDeletionController.java
new file mode 100644
index 0000000000000000000000000000000000000000..da3dbc0a82df5a5d8edd508b70c04d7cf360e027
--- /dev/null
+++ b/src/main/java/org/olat/registration/RequestAccountDeletionController.java
@@ -0,0 +1,157 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.registration;
+
+import java.util.Date;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItem;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.FormLink;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormEvent;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.link.Link;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.Event;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.gui.control.generic.closablewrapper.CloseableModalController;
+import org.olat.core.id.Identity;
+import org.olat.core.id.UserConstants;
+import org.olat.core.util.Formatter;
+import org.olat.core.util.mail.ContactList;
+import org.olat.core.util.mail.ContactMessage;
+import org.olat.modules.co.ContactFormController;
+import org.olat.user.UserModule;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * The panel to request per E-mail the deletion of it's account.
+ * 
+ * 
+ * Initial date: 4 févr. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class RequestAccountDeletionController extends FormBasicController {
+	
+	private FormLink requestButton;
+	
+	private CloseableModalController cmc;
+	private ContactFormController contactCtrl;
+	
+	@Autowired
+	private UserModule userModule;
+	@Autowired
+	private RegistrationManager registrationManager;
+	
+	public RequestAccountDeletionController(UserRequest ureq, WindowControl wControl) {
+		super(ureq, wControl, "request_delete");
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		if(formLayout instanceof FormLayoutContainer) {
+			FormLayoutContainer layoutCont = (FormLayoutContainer)formLayout;
+			Date confirmationDate = registrationManager.getDisclaimerConfirmationDate(getIdentity());
+			if(confirmationDate != null) {
+				String date = Formatter.getInstance(getLocale()).formatDate(confirmationDate);
+				String time = Formatter.getInstance(getLocale()).formatTimeShort(confirmationDate);
+				layoutCont.contextPut("title", translate("request.delete.account.title.date", new String[] { date, time }));
+			}
+			layoutCont.contextPut("text", translate("request.delete.account.text"));
+		}
+		
+		requestButton = uifactory.addFormLink("request.delete.account", formLayout, Link.BUTTON);
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+
+	@Override
+	protected void event(UserRequest ureq, Controller source, Event event) {
+		if(contactCtrl == source) {
+			cmc.deactivate();
+			cleanUp();
+			if(event == Event.DONE_EVENT) {
+				fireEvent(ureq, Event.DONE_EVENT);
+				showInfo("request.delete.account.sent");
+			}
+		} else if(cmc == source) {
+			cleanUp();
+		}
+		super.event(ureq, source, event);
+	}
+	
+	private void cleanUp() {
+		removeAsListenerAndDispose(contactCtrl);
+		removeAsListenerAndDispose(cmc);
+		contactCtrl = null;
+		cmc = null;
+	}
+
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if(requestButton == source) {
+			doRequestDeletion(ureq);
+		}
+		super.formInnerEvent(ureq, source, event);
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		//
+	}
+	
+	private void doRequestDeletion(UserRequest ureq) {
+		doOpenContactForm(ureq);
+	}
+	
+	private void doOpenContactForm(UserRequest ureq) {
+		if(contactCtrl != null) return;
+		
+		Identity identity = getIdentity();
+		String[] args = new String[] {
+			identity.getKey().toString(),											// 0
+			identity.getName(),														// 1
+			identity.getUser().getProperty(UserConstants.FIRSTNAME, getLocale()),	// 2
+			identity.getUser().getProperty(UserConstants.LASTNAME, getLocale())		// 3
+		};
+		ContactMessage contactMessage = new ContactMessage(identity);
+		contactMessage.setSubject(translate("request.delete.account.subject", args));
+		contactMessage.setBodyText(translate("request.delete.account.body", args));
+		
+		String mailAddress = userModule.getMailToRequestAccountDeletion();
+		ContactList contact = new ContactList(mailAddress);
+		contact.add(mailAddress);
+		contactMessage.addEmailTo(contact);
+
+		contactCtrl = new ContactFormController(ureq, getWindowControl(), true, false, false, contactMessage);
+		listenTo(contactCtrl);
+		
+		String title = translate("request.delete.account");
+		cmc = new CloseableModalController(getWindowControl(), "c", contactCtrl.getInitialComponent(), true, title);
+		listenTo(cmc);
+		cmc.activate();
+	}
+}
diff --git a/src/main/java/org/olat/registration/_content/disclaimer.html b/src/main/java/org/olat/registration/_content/disclaimer.html
index dc5e47dd516361942563a2dbc330cdfb9475a884..5202edaf2864aef415b89313bfa7c037023a2c79 100644
--- a/src/main/java/org/olat/registration/_content/disclaimer.html
+++ b/src/main/java/org/olat/registration/_content/disclaimer.html
@@ -11,4 +11,7 @@
 		<p>$r.render("disclaimer.additionallinktext")</p>
 	#end
 	$r.render("dclform")
+	#if($r.available("radform")) 
+		$r.render("radform")
+	#end
 </fieldset>
diff --git a/src/main/java/org/olat/registration/_content/request_delete.html b/src/main/java/org/olat/registration/_content/request_delete.html
new file mode 100644
index 0000000000000000000000000000000000000000..d8384b45662f31ff0cc76843baab9b198ce53676
--- /dev/null
+++ b/src/main/java/org/olat/registration/_content/request_delete.html
@@ -0,0 +1,9 @@
+<div class="o_info">
+	#if($r.isNotEmpty($title))
+	<h5>$title</h5>
+	#end
+	<p>$text</p>
+	<div class="o_button_group">
+		$r.render("request.delete.account")
+	</div>
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/registration/_content/request_delete_data.html b/src/main/java/org/olat/registration/_content/request_delete_data.html
new file mode 100644
index 0000000000000000000000000000000000000000..3e666aadaac7432fef97e023f374983ee659c754
--- /dev/null
+++ b/src/main/java/org/olat/registration/_content/request_delete_data.html
@@ -0,0 +1,11 @@
+<div class="o_info">
+	$r.translate("request.data.deletion.text")
+	$r.render("request.data.deletion.confirm")
+	#if($f.hasError("request.data.deletion.confirm"))
+	<div class="">$r.render("request.data.deletion.confirm_ERROR")</div>
+	#end
+</div>
+<div class="o_button_group">
+	$r.render("no")
+	$r.render("yes")
+</div>
\ No newline at end of file
diff --git a/src/main/java/org/olat/registration/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/registration/_i18n/LocalStrings_de.properties
index ad014c705e48d658bdd419a50efe7c731e6b7538..e68e642b76d06769d8549f8260c1fdf21deb3283 100644
--- a/src/main/java/org/olat/registration/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/registration/_i18n/LocalStrings_de.properties
@@ -110,6 +110,17 @@ registration.pending.status.pending.props=H\u00E4ngig, wenn eines der folgenden
 regkey.missing=Der Registrierungsschl\u00FCssel fehlt. Fordern Sie bitte einen neuen an.
 regkey.missingentry=Dieser Registrierungsschl\u00FCssel existiert nicht. Bitte fordern Sie einen neuen an.
 remote.login.title=Loginformular in externe Webseite/CMS einbinden
+request.data.deletion.title=Daten l\u00F6schen
+request.data.deletion.text=Wollen Sie dass ihre Daten <strong>komplett</strong> und <strong>definitve</strong> von dem OpenOLAT System?
+request.data.deletion.confirm=Best\u00E4tigung
+request.data.deletion.confirm.error=Sie m\u00FCssen best\u00E4gigen dass Sie die Konzequence verstehen.
+request.data.deletion.confirm.text=Ich verstehe dass alle meine Daten werden definitive gel\u00F6scht und k\u00F6nnen nicht mehr zur\u00FCcksetzt werden.
+request.delete.account=Konto l\u00F6schen beantragen
+request.delete.account.subject=Konto "{0}" l\u00F6schen beantragen
+request.delete.account.body=Ich m\u00F6chte meinem Benutzerkonto l\u00F6schen.<br>Konto ID: {0}<br>Benutzername: {1}<br>Name: {2} {3}<br><br>Mit freundlichen Gr\u00FCsse
+request.delete.account.title.date=Die Zustimmung erflogte am {0} um {1}
+request.delete.account.text=Falls sie den Nutzungsbedingungen nicht mehr zustimmen k\u00F6nnen Sie die L\u00F6schung Ihres Benutzerkonto beantragen.
+request.delete.account.sent=Die Anfrage wurde erfolgreich abgeschickt.
 select.language=Sprache
 select.language.description=W\u00E4hlen Sie die Sprache f\u00FCr die OpenOLAT Registrierung und Ihr Benutzerkonto. Sie k\u00F6nnen die Sprache sp\u00E4ter in Ihrem Benutzerprofil jederzeit anpassen. Anschliessend werden Sie durch den Registrationprozess gef\u00FChrt.
 sr.error.disclaimer.checkbox=Sie m\u00FCssen durch Anklicken des K\u00E4stchens best\u00E4tigen, dass Sie die Nutzungsbedingungen gelesen und verstanden haben und diese akzeptieren.
diff --git a/src/main/java/org/olat/registration/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/registration/_i18n/LocalStrings_en.properties
index 00ab19974a19af697d60c12355704cfd44dd4623..473b8a0ff7b4f647ef40e7ba42c6a9dff55fa7e0 100644
--- a/src/main/java/org/olat/registration/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/registration/_i18n/LocalStrings_en.properties
@@ -109,6 +109,17 @@ registration.pending.status.pending=Pending
 registration.pending.status.pending.props=Pending if one of the following user properties matches
 regkey.missing=Registration key missing. Please ask for a new one.
 regkey.missingentry=This registration key does not exist. Please ask for another one.
+request.data.deletion.title=Delete data
+request.data.deletion.text=Do you want that your data be <strong>completely</strong> and <strong>definitively</strong> deleted?
+request.data.deletion.confirm=Confirmation
+request.data.deletion.confirm.error=You must confirm that you understand the consequences.
+request.data.deletion.confirm.text=I understand that my data will be definitively deleted and that they cannot be restored.
+request.delete.account=Ask to delete your account
+request.delete.account.subject=Ask to delete account "{0}"
+request.delete.account.body=I want to delete my user account.<br>Account ID: {0}<br>Username: {1}<br>Name: {2} {3}<br><br>Best regards
+request.delete.account.title.date=The consent came at {0} {1}
+request.delete.account.text=If you no longer agree to the terms of use, you can request the deletion of your user account.
+request.delete.account.sent=The request was sent successfully.
 remote.login.title=Embed login form into external website/CMS
 select.language=Language
 select.language.description=Please select a language for your OpenOLAT registration and user account. Later on you can adapt your choice in your user profile at any time. You will then be guided through the registration process.
diff --git a/src/main/java/org/olat/shibboleth/ShibDisclaimerController.java b/src/main/java/org/olat/shibboleth/ShibDisclaimerController.java
index fe451d2bb4249fcc9a5ff95a4d94c191f91d9ee9..f301086a265a6fbbbfda7e2e73f977726f21ca46 100644
--- a/src/main/java/org/olat/shibboleth/ShibDisclaimerController.java
+++ b/src/main/java/org/olat/shibboleth/ShibDisclaimerController.java
@@ -69,7 +69,7 @@ public class ShibDisclaimerController extends FormBasicController implements Act
 
 	@Override
 	public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) {
-		disclaimerController = new DisclaimerController(ureq, getWindowControl());
+		disclaimerController = new DisclaimerController(ureq, getWindowControl(), null, false);
 		listenTo(disclaimerController);
 		
 		cmc = new CloseableModalController(getWindowControl(), translate("close"), disclaimerController.getInitialComponent(),
diff --git a/src/main/java/org/olat/shibboleth/ShibbolethRegistrationController.java b/src/main/java/org/olat/shibboleth/ShibbolethRegistrationController.java
index 40ad6666a52c8a4b994061ac59da18f194022ac6..cee7eed57466bea393415c1874e7eb9b786e8585 100644
--- a/src/main/java/org/olat/shibboleth/ShibbolethRegistrationController.java
+++ b/src/main/java/org/olat/shibboleth/ShibbolethRegistrationController.java
@@ -194,7 +194,7 @@ public class ShibbolethRegistrationController extends DefaultController implemen
 			setRegistrationForm(ureq, wControl, null);
 		}
 
-		dclController = new DisclaimerController(ureq, getWindowControl());
+		dclController = new DisclaimerController(ureq, getWindowControl(), null, false);
 		dclController.addControllerListener(this);
 		mainContainer.put("dclComp", dclController.getInitialComponent());
 
@@ -368,15 +368,10 @@ public class ShibbolethRegistrationController extends DefaultController implemen
 	private void doLogin(Identity identity, UserRequest ureq) {
 		int loginStatus = AuthHelper.doLogin(identity, ShibbolethDispatcher.PROVIDER_SHIB, ureq);
 		if (loginStatus != AuthHelper.LOGIN_OK) {
-			//REVIEW:2010-01-11:revisited:pb: do not redirect if already MediaResource is set before
-			//ureq.getDispatchResult().setResultingMediaResource(resultingMediaResource);
-			//instead set the media resource accordingly
-			//pb -> provide a DispatcherAction.getDefaultDispatcherRedirectMediaresource();
-			//to be used here. (and some more places like CatalogController.
 			DispatcherModule.redirectToDefaultDispatcher(ureq.getHttpResp()); // error, redirect to login screen
 			return;
 		}
-		// successfull login
+		// successful login
 		ureq.getUserSession().getIdentityEnvironment().addAttributes(
 				shibbolethModule.getAttributeTranslator().translateAttributesMap(shibbolethAttributes.toMap()));
 	}
diff --git a/src/main/java/org/olat/user/UserModule.java b/src/main/java/org/olat/user/UserModule.java
index 0e45fe6a9c5c6c8143d1197f9230ad3f7b509d4b..f9bcf4bcb1b964e9563cd41df6fc8de5688ff1e6 100644
--- a/src/main/java/org/olat/user/UserModule.java
+++ b/src/main/java/org/olat/user/UserModule.java
@@ -67,6 +67,9 @@ public class UserModule extends AbstractSpringModule {
 	
 	private static final String USER_EMAIL_MANDATORY = "userEmailMandatory";
 	private static final String USER_EMAIL_UNIQUE = "userEmailUnique";
+	private static final String ALLOW_REQUEST_DELETE_ACCOUNT = "allow.request.delete.account";
+	private static final String ALLOW_REQUEST_DELETE_ACCOUNT_DISCLAIMER = "allow.request.delete.account.disclaimer";
+	private static final String MAIL_REQUEST_DELETE_ACCOUNT = "request.delete.account.mail";
 	
 	@Autowired @Qualifier("loginBlacklist")
 	private ArrayList<String> loginBlacklist;
@@ -76,6 +79,13 @@ public class UserModule extends AbstractSpringModule {
 	private boolean pwdchangeallowed;
 	@Value("${password.change.allowed.without.authentications:false}")
 	private boolean pwdChangeWithoutAuthenticationAllowed;
+	
+	@Value("${allow.request.delete.account:false}")
+	private boolean allowRequestToDeleteAccount;
+	@Value("${allow.request.delete.account.disclaimer:false}")
+	private boolean allowRequestToDeleteAccountDisclaimer;
+	@Value("${request.delete.account.mail}")
+	private String mailToRequestAccountDeletion;
 
 	private String adminUserName = "administrator";
 	@Value("${user.logoByProfile:disabled}")
@@ -108,17 +118,8 @@ public class UserModule extends AbstractSpringModule {
 		}
 		
 		log.info("Successfully added " + count + " entries to login blacklist.");
-		
-		String userEmailOptionalValue = getStringPropertyValue(USER_EMAIL_MANDATORY, false);
-		if(StringHelper.containsNonWhitespace(userEmailOptionalValue)) {
-			isEmailMandatory = "true".equalsIgnoreCase(userEmailOptionalValue);
-		}
-		
-		String userEmailUniquenessOptionalValue = getStringPropertyValue(USER_EMAIL_UNIQUE, false);
-		if(StringHelper.containsNonWhitespace(userEmailUniquenessOptionalValue)) {
-			isEmailUnique = "true".equalsIgnoreCase(userEmailUniquenessOptionalValue);
-		}
-		
+		updateProperties();
+
 		// Check if user manager is configured properly and has user property
 		// handlers for the mandatory user properties used in OLAT
 		checkMandatoryUserProperty(UserConstants.FIRSTNAME);
@@ -142,7 +143,31 @@ public class UserModule extends AbstractSpringModule {
 
 	@Override
 	protected void initFromChangedProperties() {
-		//
+		updateProperties();
+	}
+	
+	private void updateProperties() {
+		String userEmailOptionalValue = getStringPropertyValue(USER_EMAIL_MANDATORY, false);
+		if(StringHelper.containsNonWhitespace(userEmailOptionalValue)) {
+			isEmailMandatory = "true".equalsIgnoreCase(userEmailOptionalValue);
+		}
+		String userEmailUniquenessOptionalValue = getStringPropertyValue(USER_EMAIL_UNIQUE, false);
+		if(StringHelper.containsNonWhitespace(userEmailUniquenessOptionalValue)) {
+			isEmailUnique = "true".equalsIgnoreCase(userEmailUniquenessOptionalValue);
+		}
+		
+		String allowRequestDeleteObj = getStringPropertyValue(ALLOW_REQUEST_DELETE_ACCOUNT, false);
+		if(StringHelper.containsNonWhitespace(allowRequestDeleteObj)) {
+			allowRequestToDeleteAccount = "true".equalsIgnoreCase(allowRequestDeleteObj);
+		}
+		String allowRequestDeleteDisclaimerObj = getStringPropertyValue(ALLOW_REQUEST_DELETE_ACCOUNT_DISCLAIMER, false);
+		if(StringHelper.containsNonWhitespace(allowRequestDeleteDisclaimerObj)) {
+			allowRequestToDeleteAccountDisclaimer = "true".equalsIgnoreCase(allowRequestDeleteDisclaimerObj);
+		}
+		String mailRequestDeleteObj = getStringPropertyValue(MAIL_REQUEST_DELETE_ACCOUNT, false);
+		if(StringHelper.containsNonWhitespace(mailRequestDeleteObj)) {
+			mailToRequestAccountDeletion = mailRequestDeleteObj;
+		}
 	}
 
 	private void checkMandatoryUserProperty(String userPropertyIdentifyer) {
@@ -262,4 +287,32 @@ public class UserModule extends AbstractSpringModule {
 		setStringProperty(USER_EMAIL_UNIQUE, isEmailUniqueStr, true);
 	}
 
+	public boolean isAllowRequestToDeleteAccount() {
+		return allowRequestToDeleteAccount;
+	}
+
+	public void setAllowRequestToDeleteAccount(boolean allowRequestToDeleteAccount) {
+		this.allowRequestToDeleteAccount = allowRequestToDeleteAccount;
+		String allowed = allowRequestToDeleteAccount ? "true" : "false";
+		setStringProperty(ALLOW_REQUEST_DELETE_ACCOUNT, allowed, true);
+	}
+
+	public boolean isAllowRequestToDeleteAccountDisclaimer() {
+		return allowRequestToDeleteAccountDisclaimer;
+	}
+
+	public void setAllowRequestToDeleteAccountDisclaimer(boolean allowRequestToDeleteAccountDisclaimer) {
+		this.allowRequestToDeleteAccountDisclaimer = allowRequestToDeleteAccountDisclaimer;
+		String allowed = allowRequestToDeleteAccountDisclaimer ? "true" : "false";
+		setStringProperty(ALLOW_REQUEST_DELETE_ACCOUNT_DISCLAIMER, allowed, true);
+	}
+
+	public String getMailToRequestAccountDeletion() {
+		return mailToRequestAccountDeletion;
+	}
+
+	public void setMailToRequestAccountDeletion(String mailToRequestAccountDeletion) {
+		this.mailToRequestAccountDeletion = mailToRequestAccountDeletion;
+		setStringProperty(MAIL_REQUEST_DELETE_ACCOUNT, mailToRequestAccountDeletion, true);
+	}
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/user/UserSettingsController.java b/src/main/java/org/olat/user/UserSettingsController.java
index 51f49e17a27dc4693e3a7a34d6b5b046549b0a6b..0b33b8ec76e8ebb4ef1387ae5a5a5f2b1f83b74e 100644
--- a/src/main/java/org/olat/user/UserSettingsController.java
+++ b/src/main/java/org/olat/user/UserSettingsController.java
@@ -202,7 +202,7 @@ public class UserSettingsController extends BasicController implements Activatea
 		if(disclaimerCtrl == null) {
 			OLATResourceable ores = OresHelper.createOLATResourceableInstance("Disclaimer", 0l);
 			WindowControl bwControl = BusinessControlFactory.getInstance().createBusinessWindowControl(ores, null, getWindowControl());
-			disclaimerCtrl = new DisclaimerController(ureq, bwControl, true);
+			disclaimerCtrl = new DisclaimerController(ureq, bwControl);
 			listenTo(disclaimerCtrl);
 		}
 		mainVC.put("segmentCmp", disclaimerCtrl.getInitialComponent());
diff --git a/src/main/java/org/olat/user/_spring/userContext.xml b/src/main/java/org/olat/user/_spring/userContext.xml
index f7a3b5bcf73fbd9bf03a5d9304ae2f9f5a25b9f8..ea993cc035b2a192bab2d8d065f223f35fd4047d 100644
--- a/src/main/java/org/olat/user/_spring/userContext.xml
+++ b/src/main/java/org/olat/user/_spring/userContext.xml
@@ -331,6 +331,26 @@
 		<property name="order" value="7411" />
 	</bean>
 	
+	<!-- User request to delete account -->
+	<bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints">
+		<property name="order" value="9014" />
+		<property name="actionController">	
+			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
+				<property name="className" value="org.olat.user.ui.admin.UserAccountDeletionSettingsController"/>
+			</bean>
+		</property>
+		<property name="navigationKey" value="requestdeleteaccount" />
+		<property name="parentTreeNodeIdentifier" value="modulesParent" /> 
+		<property name="i18nActionKey" value="admin.menu.title.request"/>
+		<property name="i18nDescriptionKey" value="admin.menu.title.request.alt"/>
+		<property name="translationPackage" value="org.olat.user.ui.admin"/>
+		<property name="extensionPoints">
+			<list>	
+				<value>org.olat.admin.SystemAdminMainController</value>		
+			</list>
+		</property>
+	</bean>
+	
 	<!-- Delete old user data export job -->
 	<bean id="deleteUserDataExportTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
 		<property name="jobDetail" ref="deleteUserDataExportJob.${cluster.singleton.services}" />
diff --git a/src/main/java/org/olat/user/ui/admin/UserAccountDeletionSettingsController.java b/src/main/java/org/olat/user/ui/admin/UserAccountDeletionSettingsController.java
new file mode 100644
index 0000000000000000000000000000000000000000..e951ea144cdf078fad3ec037fcff21782fa82540
--- /dev/null
+++ b/src/main/java/org/olat/user/ui/admin/UserAccountDeletionSettingsController.java
@@ -0,0 +1,124 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.user.ui.admin;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItem;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormEvent;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.util.StringHelper;
+import org.olat.core.util.mail.MailHelper;
+import org.olat.user.UserModule;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * 
+ * Initial date: 4 févr. 2019<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class UserAccountDeletionSettingsController extends FormBasicController {
+	
+	private MultipleSelectionElement requestDeleteEl;
+	private TextElement emailEl;
+	
+	@Autowired
+	private UserModule userModule;
+	
+	public UserAccountDeletionSettingsController(UserRequest ureq, WindowControl wControl) {
+		super(ureq, wControl);
+		
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		setFormTitle("allow.request.delete.account.time");
+		
+		String[] deleteKeys = new String[] { "anytime", "disclaimer" };
+		String[] deleteValues = new String[] { translate("allow.request.delete.account.anytime"), translate("allow.request.delete.account.disclaimer") };
+		
+		requestDeleteEl = uifactory.addCheckboxesVertical("allow.request.delete.account", formLayout, deleteKeys, deleteValues, 1);
+		requestDeleteEl.addActionListener(FormEvent.ONCHANGE);
+		if(userModule.isAllowRequestToDeleteAccount()) {
+			requestDeleteEl.select(deleteKeys[0], true);
+		}
+		if(userModule.isAllowRequestToDeleteAccountDisclaimer()) {
+			requestDeleteEl.select(deleteKeys[1], true);
+		}
+		
+		String email = userModule.getMailToRequestAccountDeletion();
+		emailEl = uifactory.addTextElement("allow.request.delete.account.mail", 256, email, formLayout);
+		emailEl.setVisible(requestDeleteEl.isAtLeastSelected(1));
+		
+		FormLayoutContainer layoutCont = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
+		formLayout.add(layoutCont);
+		uifactory.addFormSubmitButton("save", layoutCont);
+	}
+
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = super.validateFormLogic(ureq);
+		
+		emailEl.clearError();
+		requestDeleteEl.clearError();
+		if(requestDeleteEl.isAtLeastSelected(1)) {
+			if(!StringHelper.containsNonWhitespace(emailEl.getValue())) {
+				emailEl.setErrorKey("form.legende.mandatory", null);
+				allOk &= false;
+			} else if(!MailHelper.isValidEmailAddress(emailEl.getValue())) {
+				emailEl.setErrorKey("error.mail.not.valid", null);
+				allOk &= false;
+			}
+		}
+		
+		return allOk;
+	}
+
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if(requestDeleteEl == source) {
+			emailEl.setVisible(requestDeleteEl.isAtLeastSelected(1));
+		}
+		super.formInnerEvent(ureq, source, event);
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		userModule.setAllowRequestToDeleteAccount(requestDeleteEl.isSelected(0));
+		userModule.setAllowRequestToDeleteAccountDisclaimer(requestDeleteEl.isSelected(1));
+		if(requestDeleteEl.isAtLeastSelected(1)) {
+			userModule.setMailToRequestAccountDeletion(emailEl.getValue());
+		} else {
+			userModule.setMailToRequestAccountDeletion("");
+		}
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+}
diff --git a/src/main/java/org/olat/user/ui/admin/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/user/ui/admin/_i18n/LocalStrings_de.properties
index 79232c021377300c30e2aeee3768994003e74e7c..26480f5ee4e7e681145651387eaa5428161a9fdb 100644
--- a/src/main/java/org/olat/user/ui/admin/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/user/ui/admin/_i18n/LocalStrings_de.properties
@@ -1,6 +1,14 @@
 #Fri Mar 23 15:13:55 CET 2018
+admin.menu.title.request=Anfrage Konto l\u00F6schen
+admin.menu.title.request.alt=Anfrage Benutzerkonto l\u00F6schen
+allow.request.delete.account=Erlaubt anfragen Benutzerkonto zu l\u00F6schen
+allow.request.delete.account.anytime=Jederzeit
+allow.request.delete.account.disclaimer=Wenn der Benutzer den Nutzungbedingungen nicht akkzeptiert
+allow.request.delete.account.mail=Email f\u00FCr Anfrage
+allow.request.delete.account.time=Benutzerkonto l\u00F6schen anfragen
 command.next=Weiter zur n\u00E4chsten Benutzer
 command.previous=Zur\u00FCck zur letzten Benutzer
+error.mail.not.valid=Der Email Adresse ist nicht g\u00FCltig.
 menu.admingroup=Administratoren
 menu.admingroup.alt=Administratoren verwalten
 menu.anonymousgroup=Anonyme Benutzer / G\u00E4ste
diff --git a/src/main/java/org/olat/user/ui/admin/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/user/ui/admin/_i18n/LocalStrings_en.properties
index c28c40b57bd9d9151457a823ceb3a0417dd82045..d47e409e60d552ca002edf65e523405b8338a9b5 100644
--- a/src/main/java/org/olat/user/ui/admin/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/user/ui/admin/_i18n/LocalStrings_en.properties
@@ -1,4 +1,12 @@
 #Fri Mar 23 15:13:55 CET 2018
+admin.menu.title.request=Request account deletion
+admin.menu.title.request.alt=Request user account deletion
+allow.request.delete.account=Allow request to delete user account
+allow.request.delete.account.anytime=Anytime
+allow.request.delete.account.disclaimer=If the user doesn't accept the disclaimers
+allow.request.delete.account.mail=E-mail for requests
+allow.request.delete.account.time=Request user account deletion
+error.mail.not.valid=The E-mail addresse is not valid.
 command.next=Go to next user
 command.previous=Go to previous user
 menu.admingroup=Administrators
diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties
index a0be8ed84e98a69e71eedf41d6f4e22806fa83d9..8e708154083b04e9872f90bfaef62e677d3e5554 100644
--- a/src/main/resources/serviceconfig/olat.properties
+++ b/src/main/resources/serviceconfig/olat.properties
@@ -250,6 +250,11 @@ notification.interval.default.values=never,monthly,weekly,daily,half-daily,four-
 #notification cron job
 notification.cronjob.expression=0 10 */2 * * ?
 
+# Request to delete account
+allow.request.delete.account=false
+allow.request.delete.account.disclaimer=false
+request.delete.account.mail=
+
 ####################################################
 # Groups
 ####################################################
diff --git a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
index e4f4cab25f0e61cda9e6aa0e16cf0cebddc4fa14..a03ee79e295b8a3055b541faa933d3a8a6e7aeb5 100644
--- a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
+++ b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
@@ -270,7 +270,7 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 	@Test
 	public void loadIdentityShortByKey() {
 		//create a user it
-		String idName = "find-me-short-1-" + UUID.randomUUID().toString();
+		String idName = "find-me-short-1-" + UUID.randomUUID();
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser(idName);
 		dbInstance.commitAndCloseSession();
 		
@@ -282,19 +282,19 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 		Assert.assertEquals(id.getUser().getEmail(), foundId.getEmail());
 		Assert.assertEquals(id.getUser().getFirstName(), foundId.getFirstName());
 		Assert.assertEquals(id.getUser().getLastName(), foundId.getLastName());
-		Assert.assertNotNull(foundId.getLastLogin());
+		Assert.assertNull(foundId.getLastLogin());// no login, no last login date
 		Assert.assertEquals(id.getUser().getKey(), foundId.getUserKey());
 		Assert.assertTrue(foundId.getStatus() < Identity.STATUS_VISIBLE_LIMIT);
 	}
 
 	@Test
 	public void testLoadIdentityByKeys() {
-		//create a security group with 2 identites
-		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser( "load-1-sec-" + UUID.randomUUID().toString());
-		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser( "load-2-sec-" + UUID.randomUUID().toString());
+		//create a security group with 2 identities
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsRndUser( "load-1-sec-");
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsRndUser( "load-2-sec-");
 		dbInstance.commitAndCloseSession();
 		
-		List<Long> keys = new ArrayList<Long>(2);
+		List<Long> keys = new ArrayList<>(2);
 		keys.add(id1.getKey());
 		keys.add(id2.getKey());
 		List<Identity> identities = securityManager.loadIdentityByKeys(keys);