diff --git a/src/main/java/org/olat/basesecurity/model/IdentityLastLoginImpl.java b/src/main/java/org/olat/basesecurity/model/IdentityLastLoginImpl.java
index 2e5ca8515161cc02361ce78321dc9679a2e4cebc..f623e8279b5c99602fe3ac5bd2a003e8927e1e3d 100644
--- a/src/main/java/org/olat/basesecurity/model/IdentityLastLoginImpl.java
+++ b/src/main/java/org/olat/basesecurity/model/IdentityLastLoginImpl.java
@@ -42,7 +42,7 @@ import org.olat.core.id.Persistable;
  */
 @Entity(name="bidentitylastlogin")
 @Table(name="o_bs_identity")
-@NamedQuery(name="updateIdentityLastLogin", query="update bidentitylastlogin set lastLogin=:now where key=:identityKey")
+@NamedQuery(name="updateIdentityLastLogin", query="update bidentitylastlogin set lastLogin=:now, inactivationEmailDate=null where key=:identityKey")
 public class IdentityLastLoginImpl implements Persistable, IdentityRef {
 
 	private static final long serialVersionUID = 2090002193918262648L;
@@ -56,6 +56,10 @@ public class IdentityLastLoginImpl implements Persistable, IdentityRef {
 	@Column(name="lastlogin", nullable=false, insertable=true, updatable=true)
 	private Date lastLogin;
 	
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="inactivationemaildate", nullable=false, insertable=true, updatable=true)
+	private Date inactivationEmailDate;
+	
 	public IdentityLastLoginImpl() {
 		//
 	}
diff --git a/src/main/java/org/olat/ims/qti21/restapi/AssessmentTestWebService.java b/src/main/java/org/olat/ims/qti21/restapi/AssessmentTestWebService.java
new file mode 100644
index 0000000000000000000000000000000000000000..cb1e5dfe24718730b83486744666014f6f2ae5a0
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/restapi/AssessmentTestWebService.java
@@ -0,0 +1,112 @@
+/**
+ * <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.ims.qti21.restapi;
+
+import static org.olat.restapi.security.RestSecurityHelper.getRoles;
+
+import java.io.File;
+import java.net.URI;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.logging.log4j.Logger;
+import org.olat.core.id.Roles;
+import org.olat.core.logging.Tracing;
+import org.olat.core.util.StringHelper;
+import org.olat.fileresource.FileResourceManager;
+import org.olat.ims.qti21.QTI21Service;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import uk.ac.ed.ph.jqtiplus.node.test.AssessmentTest;
+import uk.ac.ed.ph.jqtiplus.node.test.ItemSessionControl;
+import uk.ac.ed.ph.jqtiplus.node.test.TestPart;
+import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentTest;
+
+/**
+ * 
+ * Initial date: 2 sept. 2020<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+@Path("repo/tests")
+public class AssessmentTestWebService {
+	
+	private static final Logger log = Tracing.createLoggerFor(AssessmentTestWebService.class);
+
+	@Autowired
+	private QTI21Service qtiService;
+	@Autowired
+	private RepositoryManager repositoryManager;
+	
+	@PUT
+	@Path("{repoEntryKey}/parts/maxattempts")
+	public Response getRepositoryEntryResource(@PathParam("repoEntryKey")String repoEntryKey, @Context HttpServletRequest httpRequest)
+	throws WebApplicationException {
+		Roles roles = getRoles(httpRequest);
+		if(roles == null || !roles.isAdministrator()) {
+	    	throw new WebApplicationException(Status.FORBIDDEN);
+		}
+		RepositoryEntry re = lookupRepositoryEntry(repoEntryKey);
+	    if(re == null) {
+	    	throw new WebApplicationException(Status.NOT_FOUND);
+	    }
+	    
+	    FileResourceManager frm = FileResourceManager.getInstance();
+		File unzippedDirRoot = frm.unzipFileResource(re.getOlatResource());
+		ResolvedAssessmentTest resolvedObject = qtiService.loadAndResolveAssessmentTest(unzippedDirRoot, false, true);
+		AssessmentTest assessmentTest = resolvedObject.getRootNodeLookup().extractIfSuccessful();
+		
+		List<TestPart> parts = assessmentTest.getChildAbstractParts();
+		for(TestPart part:parts) {
+			ItemSessionControl itemSessionControl = part.getItemSessionControl();
+			itemSessionControl.setMaxAttempts(Integer.valueOf(0));
+		}
+		
+		URI testURI = resolvedObject.getTestLookup().getSystemId();
+		File testFile = new File(testURI);
+		qtiService.updateAssesmentObject(testFile, resolvedObject);
+		return Response.ok().build();
+	}
+	
+	private RepositoryEntry lookupRepositoryEntry(String key) {
+		RepositoryEntry re = null;
+		if (StringHelper.isLong(key)) {// looks like a primary key
+			try {
+				re = repositoryManager.lookupRepositoryEntry(Long.valueOf(key));
+			} catch (NumberFormatException e) {
+				log.warn("", e);
+			}
+		}
+		return re;
+	}
+
+}
diff --git a/src/main/java/org/olat/user/ui/admin/lifecycle/UserAdminLifecycleConfigurationController.java b/src/main/java/org/olat/user/ui/admin/lifecycle/UserAdminLifecycleConfigurationController.java
index 11c36b02e5124039ced9665c88544fb56f6f2334..2fa12a9ae8c8ab0354487a93aedbf9834059df96 100644
--- a/src/main/java/org/olat/user/ui/admin/lifecycle/UserAdminLifecycleConfigurationController.java
+++ b/src/main/java/org/olat/user/ui/admin/lifecycle/UserAdminLifecycleConfigurationController.java
@@ -92,7 +92,6 @@ public class UserAdminLifecycleConfigurationController extends FormBasicControll
 		formLayout.add(buttonsLayout);
 		uifactory.addFormSubmitButton("save", buttonsLayout);
 	}
-	
 
 	protected void initDeactivationForm(FormItemContainer formLayout) {
 		String[] onValues = new String[] { translate("enabled") };
@@ -116,8 +115,8 @@ public class UserAdminLifecycleConfigurationController extends FormBasicControll
 		initDays(numberOfDayBeforeDeactivationMailEl);
 		
 		// subject + content mail
-		TranslationBundle beforeBundleSubject = initForm("mail.before.deactivation.subject.label", "mail.before.deactivation.subject", formLayout);
-		TranslationBundle beforeBundle = initForm("mail.before.deactivation.body.label", "mail.before.deactivation.body", formLayout);
+		TranslationBundle beforeBundleSubject = initForm("mail.before.deactivation.subject.label", "mail.before.deactivation.subject", false, formLayout);
+		TranslationBundle beforeBundle = initForm("mail.before.deactivation.body.label", "mail.before.deactivation.body", true, formLayout);
 		mailBeforeDeactivationBundles = new TranslationBundles(beforeBundleSubject, beforeBundle);
 
 		// enable mail after
@@ -126,8 +125,8 @@ public class UserAdminLifecycleConfigurationController extends FormBasicControll
 		enableMailAfterDeactivationEl.select(onKeys[0], userModule.isMailAfterDeactivation());
 
 		// subject + content mail
-		TranslationBundle afterBundleSubject = initForm("mail.after.deactivation.subject.label", "mail.after.deactivation.subject", formLayout);
-		TranslationBundle afterBundle = initForm("mail.after.deactivation.body.label", "mail.after.deactivation.body", formLayout);
+		TranslationBundle afterBundleSubject = initForm("mail.after.deactivation.subject.label", "mail.after.deactivation.subject", false, formLayout);
+		TranslationBundle afterBundle = initForm("mail.after.deactivation.body.label", "mail.after.deactivation.body", true, formLayout);
 		mailAfterDeactivationBundles = new TranslationBundles(afterBundleSubject, afterBundle);
 	}
 
@@ -152,8 +151,8 @@ public class UserAdminLifecycleConfigurationController extends FormBasicControll
 		initDays(numberOfDayBeforeDeletionMailEl);
 		
 		// subject + content mail
-		TranslationBundle beforeBundleSubject = initForm("mail.before.deletion.subject.label", "mail.before.deletion.subject", formLayout);
-		TranslationBundle beforeBundle = initForm("mail.before.deletion.body.label", "mail.before.deletion.body", formLayout);
+		TranslationBundle beforeBundleSubject = initForm("mail.before.deletion.subject.label", "mail.before.deletion.subject", false, formLayout);
+		TranslationBundle beforeBundle = initForm("mail.before.deletion.body.label", "mail.before.deletion.body", true, formLayout);
 		mailBeforeDeletionBundles = new TranslationBundles(beforeBundleSubject, beforeBundle);
 		
 		// enable mail after
@@ -162,17 +161,17 @@ public class UserAdminLifecycleConfigurationController extends FormBasicControll
 		enableMailAfterDeletionEl.select(onKeys[0], userModule.isMailAfterDeletion());
 		
 		// subject + content mail
-		TranslationBundle afterBundleSubject = initForm("mail.after.deletion.subject.label", "mail.after.deletion.subject", formLayout);
-		TranslationBundle afterBundle = initForm("mail.after.deletion.body.label", "mail.after.deletion.body", formLayout);
+		TranslationBundle afterBundleSubject = initForm("mail.after.deletion.subject.label", "mail.after.deletion.subject", false, formLayout);
+		TranslationBundle afterBundle = initForm("mail.after.deletion.body.label", "mail.after.deletion.body", true, formLayout);
 		mailAfterDeletionBundles = new TranslationBundles(afterBundleSubject, afterBundle);
 		
 	}
 	
-	private TranslationBundle initForm(String labelI18nKey, String textI18nKey, FormItemContainer formLayout) {
+	private TranslationBundle initForm(String labelI18nKey, String textI18nKey, boolean textArea, FormItemContainer formLayout) {
 		String text = translate(textI18nKey);
 		StaticTextElement viewEl = uifactory.addStaticTextElement("view." + counter++, labelI18nKey, text, formLayout);
 		FormLink translationLink = uifactory.addFormLink("translate." + counter++, "translation.edit", null, formLayout, Link.LINK);
-		TranslationBundle bundle = new TranslationBundle(textI18nKey, labelI18nKey, viewEl, translationLink);
+		TranslationBundle bundle = new TranslationBundle(textI18nKey, labelI18nKey, viewEl, translationLink, textArea);
 		translationLink.setUserObject(bundle);
 		return bundle;
 	}
@@ -310,7 +309,7 @@ public class UserAdminLifecycleConfigurationController extends FormBasicControll
 		if(guardModalController(translatorCtrl)) return;
 		
 		translatorCtrl = new SingleKeyTranslatorController(ureq, getWindowControl(), bundle.getI18nKey(),
-				UserAdminLifecycleConfigurationController.class);
+				UserAdminLifecycleConfigurationController.class, bundle.isTextArea());
 		translatorCtrl.setUserObject(bundle);
 		listenTo(translatorCtrl);
 
@@ -326,12 +325,14 @@ public class UserAdminLifecycleConfigurationController extends FormBasicControll
 	
 	private static class TranslationBundle {
 		
+		private final boolean textArea;
 		private final String i18nKey;
 		private final String labelI18nKey;
 		private final StaticTextElement viewEl;
 		private final FormLink translationLink;
 		
-		public TranslationBundle(String i18nKey, String labelI18nKey, StaticTextElement viewEl, FormLink translationLink) {
+		public TranslationBundle(String i18nKey, String labelI18nKey, StaticTextElement viewEl, FormLink translationLink, boolean textArea) {
+			this.textArea = textArea;
 			this.i18nKey = i18nKey;
 			this.viewEl = viewEl;
 			this.labelI18nKey = labelI18nKey;
@@ -341,6 +342,10 @@ public class UserAdminLifecycleConfigurationController extends FormBasicControll
 		public StaticTextElement getViewEl() {
 			return viewEl;
 		}
+		
+		public boolean isTextArea() {
+			return textArea;
+		}
 
 		public String getI18nKey() {
 			return i18nKey;
diff --git a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
index c40ba89973afbf01bc90c304f3b73863c64117c5..c83ec0d3c85828857159a384da22dc60c1a582d3 100644
--- a/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
+++ b/src/test/java/org/olat/basesecurity/BaseSecurityManagerTest.java
@@ -722,6 +722,29 @@ public class BaseSecurityManagerTest extends OlatTestCase {
 		Assert.assertNotNull(lastLogin);
 	}
 
+	@Test
+	public void updateLastLoginAndInactivationDate() {
+		Identity id = JunitTestHelper.createAndPersistIdentityAsRndUser("last-login-0");
+		((IdentityImpl)id).setInactivationEmailDate(new Date());
+		id = dbInstance.getCurrentEntityManager().merge(id);
+		dbInstance.commitAndCloseSession();
+
+		id = securityManager.loadIdentityByKey(id.getKey());
+		Date mergedInactivationDate = ((IdentityImpl)id).getInactivationEmailDate();
+		Assert.assertNotNull(mergedInactivationDate);
+		dbInstance.commitAndCloseSession();
+		
+		securityManager.setIdentityLastLogin(id);
+		dbInstance.commitAndCloseSession();
+
+		id = securityManager.loadIdentityByKey(id.getKey());
+		Date lastLogin = id.getLastLogin();
+		Assert.assertNotNull(lastLogin);
+		Date inactivationDate = ((IdentityImpl)id).getInactivationEmailDate();
+		Assert.assertNull(inactivationDate);
+	}
+	
+	
 	@Test
 	public void countUniqueUserLoginsSince() {
 		Calendar cal = Calendar.getInstance();