From b2f2a79a5df63c55084e37797d34e3b1e90d86ef Mon Sep 17 00:00:00 2001 From: Aldo Mihasi Date: Tue, 23 May 2023 17:15:11 +0300 Subject: [PATCH] add unlink functionality in profile section, when an email is unlinked that account is starting from scratch resulting in an empty dashboard. --- .../controllers/EmailUnlinkConfirmation.java | 57 ++++ .../UnlinkEmailConfirmationManager.java | 106 ++++++ .../utilities/ConfirmationEmailService.java | 4 + .../ConfirmationEmailServiceImpl.java | 61 ++++ .../data/userinfo/UserUnlinkRequestModel.java | 30 ++ .../config/application-devel.properties | 1 + .../config/application-docker.properties | 1 + .../resources/config/application.properties | 1 + .../email/emailUnlinkConfirmation.html | 304 ++++++++++++++++++ .../src/app/core/core-service.module.ts | 2 + .../model/unlink-account/unlink-account.ts | 5 + ...link-account-email-confirmation.service.ts | 23 ++ .../src/app/ui/auth/login/login.module.ts | 2 + .../src/app/ui/auth/login/login.routing.ts | 2 + .../unlink-email-confirmation.component.html | 1 + .../unlink-email-confirmation.component.scss | 0 .../unlink-email-confirmation.component.ts | 54 ++++ .../user-profile/user-profile.component.html | 8 +- .../user-profile/user-profile.component.scss | 15 + .../ui/user-profile/user-profile.component.ts | 48 ++- dmp-frontend/src/assets/i18n/de.json | 10 + dmp-frontend/src/assets/i18n/en.json | 10 + dmp-frontend/src/assets/i18n/es.json | 10 + dmp-frontend/src/assets/i18n/gr.json | 10 + dmp-frontend/src/assets/i18n/hr.json | 10 + dmp-frontend/src/assets/i18n/pl.json | 10 + dmp-frontend/src/assets/i18n/pt.json | 10 + dmp-frontend/src/assets/i18n/sk.json | 10 + dmp-frontend/src/assets/i18n/sr.json | 10 + dmp-frontend/src/assets/i18n/tr.json | 10 + 30 files changed, 818 insertions(+), 7 deletions(-) create mode 100644 dmp-backend/web/src/main/java/eu/eudat/controllers/EmailUnlinkConfirmation.java create mode 100644 dmp-backend/web/src/main/java/eu/eudat/logic/managers/UnlinkEmailConfirmationManager.java create mode 100644 dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserUnlinkRequestModel.java create mode 100644 dmp-backend/web/src/main/resources/templates/email/emailUnlinkConfirmation.html create mode 100644 dmp-frontend/src/app/core/model/unlink-account/unlink-account.ts create mode 100644 dmp-frontend/src/app/core/services/unlink-account-email-confirmation/unlink-account-email-confirmation.service.ts create mode 100644 dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.html create mode 100644 dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.scss create mode 100644 dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.ts diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/EmailUnlinkConfirmation.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/EmailUnlinkConfirmation.java new file mode 100644 index 000000000..5dcc639bc --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/EmailUnlinkConfirmation.java @@ -0,0 +1,57 @@ +package eu.eudat.controllers; + +import eu.eudat.exceptions.emailconfirmation.HasConfirmedEmailException; +import eu.eudat.exceptions.emailconfirmation.TokenExpiredException; +import eu.eudat.logic.managers.UnlinkEmailConfirmationManager; +import eu.eudat.models.data.helpers.responses.ResponseItem; +import eu.eudat.models.data.security.Principal; +import eu.eudat.models.data.userinfo.UserUnlinkRequestModel; +import eu.eudat.types.ApiMessageCode; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.transaction.Transactional; + +@RestController +@CrossOrigin +@RequestMapping(value = "api/emailUnlinkConfirmation") +public class EmailUnlinkConfirmation { + + private UnlinkEmailConfirmationManager unlinkEmailConfirmationManager; + + @Autowired + public EmailUnlinkConfirmation(UnlinkEmailConfirmationManager unlinkEmailConfirmationManager){ + this.unlinkEmailConfirmationManager = unlinkEmailConfirmationManager; + } + + @Transactional + @RequestMapping(method = RequestMethod.GET, value = {"/{emailToken}"}) + public @ResponseBody + ResponseEntity emailConfirmation(@PathVariable(value = "emailToken") String token) { + try { + this.unlinkEmailConfirmationManager.confirmEmail(token); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE)); + } catch (TokenExpiredException | HasConfirmedEmailException ex) { + if (ex instanceof TokenExpiredException) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE)); + } + else { + return ResponseEntity.status(HttpStatus.FOUND).body(new ResponseItem().status(ApiMessageCode.WARN_MESSAGE)); + } + } + } + + @Transactional + @RequestMapping(method = RequestMethod.POST, consumes = "application/json", produces = "application/json") + public @ResponseBody + ResponseEntity sendUnlinkConfirmationEmail(@RequestBody UserUnlinkRequestModel requestModel, Principal principal) { + try { + this.unlinkEmailConfirmationManager.sendConfirmationEmail(requestModel.getEmail(), principal, requestModel.getUserId(), requestModel.getProvider()); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE)); + } catch (Exception ex) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE).message("Could not send unlink email.")); + } + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UnlinkEmailConfirmationManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UnlinkEmailConfirmationManager.java new file mode 100644 index 000000000..5785f1676 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UnlinkEmailConfirmationManager.java @@ -0,0 +1,106 @@ +package eu.eudat.logic.managers; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.eudat.data.entities.Credential; +import eu.eudat.data.entities.EmailConfirmation; +import eu.eudat.data.entities.UserInfo; +import eu.eudat.data.entities.UserToken; +import eu.eudat.exceptions.emailconfirmation.HasConfirmedEmailException; +import eu.eudat.exceptions.emailconfirmation.TokenExpiredException; +import eu.eudat.logic.builders.entity.UserTokenBuilder; +import eu.eudat.logic.services.ApiContext; +import eu.eudat.logic.services.operations.DatabaseRepository; +import eu.eudat.models.data.security.Principal; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.transaction.Transactional; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +@Component +public class UnlinkEmailConfirmationManager { + + private static Logger logger = LoggerFactory.getLogger(UnlinkEmailConfirmationManager.class); + + private ApiContext apiContext; + private DatabaseRepository databaseRepository; + + @Autowired + public UnlinkEmailConfirmationManager(ApiContext apiContext) { + this.apiContext = apiContext; + this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository(); + } + + @Transactional + public void confirmEmail(String token) throws TokenExpiredException, HasConfirmedEmailException { + EmailConfirmation loginConfirmationEmail = apiContext.getOperationsContext() + .getDatabaseRepository().getLoginConfirmationEmailDao().asQueryable() + .where((builder, root) -> builder.equal(root.get("token"), UUID.fromString(token))).getSingle(); + + if (loginConfirmationEmail.getExpiresAt().compareTo(new Date()) < 0) + throw new TokenExpiredException("Token has expired."); + + if(loginConfirmationEmail.getIsConfirmed()) + throw new HasConfirmedEmailException("Email is already confirmed."); + +// UserInfo userAskingForUnlink = databaseRepository.getUserInfoDao().asQueryable() +// .where((builder, root) -> builder.equal(root.get("id"), loginConfirmationEmail.getUserId())).getSingle(); + + try { + Map map = new ObjectMapper().readValue(loginConfirmationEmail.getData(), new TypeReference>() {}); + String emailTobeUnlinked = (String) map.get("email"); + Integer provider = Integer.valueOf((String) map.get("provider")); + + unlinkUser(emailTobeUnlinked, provider); + + loginConfirmationEmail.setIsConfirmed(true); + databaseRepository.getLoginConfirmationEmailDao().createOrUpdate(loginConfirmationEmail); + } + catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + + @Transactional + private void unlinkUser(String emailTobeUnlinked, Integer provider){ + Credential credential = databaseRepository.getCredentialDao().asQueryable() + .where((builder, root) -> builder.and(builder.equal(root.get("email"), emailTobeUnlinked), builder.equal(root.get("provider"), provider))).getSingle(); + if(credential != null) { + UserInfo userTobeUnlinked = databaseRepository.getUserInfoDao().asQueryable() + .where((builder, root) -> builder.and(builder.equal(root.get("userStatus"), 1), builder.equal(root.get("name"), credential.getPublicValue()))).getSingle(); + userTobeUnlinked.setEmail(emailTobeUnlinked); + userTobeUnlinked.setUserStatus((short) 0); + databaseRepository.getUserInfoDao().createOrUpdate(userTobeUnlinked); + + credential.setUserInfo(userTobeUnlinked); + databaseRepository.getCredentialDao().createOrUpdate(credential); + + UserToken userToken = this.apiContext.getOperationsContext().getBuilderFactory().getBuilder(UserTokenBuilder.class) + .token(UUID.randomUUID()).user(userTobeUnlinked) + .expiresAt(Timestamp.valueOf(LocalDateTime.now().plusDays(10))).issuedAt(new Date()) + .build(); + apiContext.getOperationsContext().getDatabaseRepository().getUserTokenDao().createOrUpdate(userToken); + } + } + + public void sendConfirmationEmail(String email, Principal principal, UUID userId, Integer provider) { + UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId()); + + if (user.getEmail() != null && !user.getEmail().equals(email)) { + apiContext.getUtilitiesService().getConfirmationEmailService().createUnlinkConfirmationEmail( + databaseRepository.getLoginConfirmationEmailDao(), + apiContext.getUtilitiesService().getMailService(), + email, + userId, + principal, + provider + ); + } + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailService.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailService.java index 108550ec5..dfb712d0f 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailService.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailService.java @@ -12,7 +12,11 @@ public interface ConfirmationEmailService { public void createMergeConfirmationEmail(EmailConfirmationDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId, Principal principal, Integer provider); + public void createUnlinkConfirmationEmail(EmailConfirmationDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId, Principal principal, Integer provider); + public CompletableFuture sentConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService); public CompletableFuture sentMergeConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService, String userName); + + public CompletableFuture sentUnlinkConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService); } \ No newline at end of file diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java index f089d15c0..52400bc01 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java @@ -1,6 +1,7 @@ package eu.eudat.logic.services.utilities; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.data.dao.entities.EmailConfirmationDao; import eu.eudat.data.entities.EmailConfirmation; @@ -84,6 +85,30 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { }); } + @Override + public CompletableFuture sentUnlinkConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService) { + String email = null; + try { + Map map = new ObjectMapper().readValue(confirmationEmail.getData(), new TypeReference>() {}); + email = (String) map.get("email"); + } + catch (JsonProcessingException e){ + logger.error(e.getMessage(), e); + } + String finalEmail = email; + return CompletableFuture.runAsync(() -> { + SimpleMail mail = new SimpleMail(); + mail.setSubject(environment.getProperty("conf_email.subject")); + mail.setContent(createUnlinkContent(confirmationEmail.getToken(), mailService, finalEmail)); + mail.setTo(confirmationEmail.getEmail()); + try { + mailService.sendSimpleMail(mail); + } catch (Exception ex) { + logger.error(ex.getMessage(), ex); + } + }); + } + private String createMergeContent(UUID confirmationToken, MailService mailService, String userName) { String content = mailService.getMailTemplateContent(this.environment.getProperty("email.merge")); content = content.replace("{userName}", userName); @@ -94,6 +119,16 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { return content; } + private String createUnlinkContent(UUID confirmationToken, MailService mailService, String email) { + String content = mailService.getMailTemplateContent(this.environment.getProperty("email.unlink")); + content = content.replace("{confirmationToken}", confirmationToken.toString()); + content = content.replace("{expiration_time}", secondsToTime(Integer.parseInt(this.environment.getProperty("conf_email.expiration_time_seconds")))); + content = content.replace("{host}", this.environment.getProperty("dmp.domain")); + content = content.replace("{email}", email); + + return content; + } + private String secondsToTime(int seconds) { int sec = seconds % 60; int hour = seconds / 60; @@ -128,4 +163,30 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { sentMergeConfirmationEmail(confirmationEmail, mailService, principal.getName()); } + + @Override + public void createUnlinkConfirmationEmail(EmailConfirmationDao loginConfirmationEmailDao, MailService mailService, + String email, UUID userId, Principal principal, Integer provider) { + EmailConfirmation confirmationEmail = new EmailConfirmation(); + confirmationEmail.setEmail(principal.getEmail()); + confirmationEmail.setExpiresAt(Date + .from(new Date() + .toInstant() + .plusSeconds(Long.parseLong(this.environment.getProperty("conf_email.expiration_time_seconds"))) + ) + ); + confirmationEmail.setUserId(userId); + try { + Map map = new HashMap<>(); + map.put("email", email); + map.put("provider", provider.toString()); + confirmationEmail.setData(new ObjectMapper().writeValueAsString(map)); + } catch (JsonProcessingException e) { + logger.error(e.getMessage(), e); + } + confirmationEmail.setIsConfirmed(false); + confirmationEmail.setToken(UUID.randomUUID()); + confirmationEmail = loginConfirmationEmailDao.createOrUpdate(confirmationEmail); + sentUnlinkConfirmationEmail(confirmationEmail, mailService); + } } \ No newline at end of file diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserUnlinkRequestModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserUnlinkRequestModel.java new file mode 100644 index 000000000..5ce0b0c77 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserUnlinkRequestModel.java @@ -0,0 +1,30 @@ +package eu.eudat.models.data.userinfo; + +import java.util.UUID; + +public class UserUnlinkRequestModel { + private UUID userId; + private String email; + private Integer provider; + + public UUID getUserId() { + return userId; + } + public void setUserId(UUID userId) { + this.userId = userId; + } + + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + + public Integer getProvider() { + return provider; + } + public void setProvider(Integer provider) { + this.provider = provider; + } +} diff --git a/dmp-backend/web/src/main/resources/config/application-devel.properties b/dmp-backend/web/src/main/resources/config/application-devel.properties index 1f4091ca2..7156a62d5 100644 --- a/dmp-backend/web/src/main/resources/config/application-devel.properties +++ b/dmp-backend/web/src/main/resources/config/application-devel.properties @@ -32,6 +32,7 @@ configuration.configurable_login_providers=configurableLoginProviders.json email.invite=classpath:templates/email/email.html email.confirmation=classpath:templates/email/emailConfirmation.html email.merge=classpath:templates/email/emailMergeConfirmation.html +email.unlink=classpath:templates/email/emailUnlinkConfirmation.html #############FACEBOOK LOGIN CONFIGURATIONS######### facebook.login.clientId= diff --git a/dmp-backend/web/src/main/resources/config/application-docker.properties b/dmp-backend/web/src/main/resources/config/application-docker.properties index 075203f35..caeec702f 100644 --- a/dmp-backend/web/src/main/resources/config/application-docker.properties +++ b/dmp-backend/web/src/main/resources/config/application-docker.properties @@ -31,6 +31,7 @@ configuration.configurable_login_providers=configurableLoginProviders.json email.invite=classpath:templates/email/email.html email.confirmation=classpath:templates/email/emailConfirmation.html email.merge=classpath:templates/email/emailMergeConfirmation.html +email.unlink=classpath:templates/email/emailUnlinkConfirmation.html email.dataset.template=classpath:templates/email/emailAdmin.html ####################INVITATION MAIL CONFIGURATIONS############## diff --git a/dmp-backend/web/src/main/resources/config/application.properties b/dmp-backend/web/src/main/resources/config/application.properties index 595d49f56..0135ef7d6 100644 --- a/dmp-backend/web/src/main/resources/config/application.properties +++ b/dmp-backend/web/src/main/resources/config/application.properties @@ -58,6 +58,7 @@ configuration.configurable_login_providers=configurableLoginProviders.json email.invite=file:templates/email/email.html email.confirmation=file:templates/email/emailConfirmation.html email.merge=file:templates/email/emailMergeConfirmation.html +email.unlink=classpath:templates/email/emailUnlinkConfirmation.html email.dataset.template=file:templates/email/emailAdmin.html #############LOGIN CONFIGURATIONS######### diff --git a/dmp-backend/web/src/main/resources/templates/email/emailUnlinkConfirmation.html b/dmp-backend/web/src/main/resources/templates/email/emailUnlinkConfirmation.html new file mode 100644 index 000000000..7f6ce7c8d --- /dev/null +++ b/dmp-backend/web/src/main/resources/templates/email/emailUnlinkConfirmation.html @@ -0,0 +1,304 @@ + + + + + + Simple Transactional Email + + + + + + + + + +
  +
+ + + This is preheader text. Some clients will show this text as a preview. + + + + + + + + +
+ + + + +
+ OpenDMP +

You have made a request to unlink your email account in ARGOS.

+

Please confirm that you want to unlink your {email} account. +
The link will expire in {expiration_time}.

+ + + + + + +
+ + + + + + +
Confirm Unlink Request
+
+
+
+ + + + + + +
+
 
+ + \ No newline at end of file diff --git a/dmp-frontend/src/app/core/core-service.module.ts b/dmp-frontend/src/app/core/core-service.module.ts index af0f85591..5186586e6 100644 --- a/dmp-frontend/src/app/core/core-service.module.ts +++ b/dmp-frontend/src/app/core/core-service.module.ts @@ -51,6 +51,7 @@ import { AboutService } from './services/about/about.service'; import { FaqService } from './services/faq/faq.service'; import { GlossaryService } from './services/glossary/glossary.service'; import { TermsOfServiceService } from './services/terms-of-service/terms-of-service.service'; +import { UnlinkAccountEmailConfirmationService } from './services/unlink-account-email-confirmation/unlink-account-email-confirmation.service'; // // // This is shared module that provides all the services. Its imported only once on the AppModule. @@ -122,6 +123,7 @@ export class CoreServiceModule { TermsOfServiceService, CurrencyService, MergeEmailConfirmationService, + UnlinkAccountEmailConfirmationService, ConfigurationService, { provide: APP_INITIALIZER, diff --git a/dmp-frontend/src/app/core/model/unlink-account/unlink-account.ts b/dmp-frontend/src/app/core/model/unlink-account/unlink-account.ts new file mode 100644 index 000000000..89c6aa33a --- /dev/null +++ b/dmp-frontend/src/app/core/model/unlink-account/unlink-account.ts @@ -0,0 +1,5 @@ +export class UnlinkAccountRequestModel { + userId: String; + email: String; + provider: number; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/services/unlink-account-email-confirmation/unlink-account-email-confirmation.service.ts b/dmp-frontend/src/app/core/services/unlink-account-email-confirmation/unlink-account-email-confirmation.service.ts new file mode 100644 index 000000000..40846c568 --- /dev/null +++ b/dmp-frontend/src/app/core/services/unlink-account-email-confirmation/unlink-account-email-confirmation.service.ts @@ -0,0 +1,23 @@ +import { HttpHeaders } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { BaseHttpService } from "../http/base-http.service"; +import { ConfigurationService } from "../configuration/configuration.service"; +import { UnlinkAccountRequestModel } from "@app/core/model/unlink-account/unlink-account"; + +@Injectable() +export class UnlinkAccountEmailConfirmationService { + private actioUrl: string; + private headers: HttpHeaders; + + constructor(private http: BaseHttpService, private configurationService: ConfigurationService) { + this.actioUrl = configurationService.server + 'emailUnlinkConfirmation/'; + } + + public emailConfirmation(token: string) { + return this.http.get(this.actioUrl + token, { headers: this.headers }); + } + + public sendConfirmationEmail(unlinkRequest: UnlinkAccountRequestModel) { + return this.http.post(this.actioUrl, unlinkRequest, { headers: this.headers }); + } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/auth/login/login.module.ts b/dmp-frontend/src/app/ui/auth/login/login.module.ts index 1feb4f4dc..8d5b84740 100644 --- a/dmp-frontend/src/app/ui/auth/login/login.module.ts +++ b/dmp-frontend/src/app/ui/auth/login/login.module.ts @@ -18,6 +18,7 @@ import { MergeLoginService } from './utilities/merge-login.service'; import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component'; import { SamlLoginService } from '@app/core/services/saml-login.service'; import { SamlResponseLoginComponent } from './saml/saml-login-response/saml-login-response.component'; +import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-email-confirmation.component'; @NgModule({ imports: [ @@ -37,6 +38,7 @@ import { SamlResponseLoginComponent } from './saml/saml-login-response/saml-logi ConfigurableLoginComponent, ZenodoLoginComponent, MergeEmailConfirmation, + UnlinkEmailConfirmation, SamlResponseLoginComponent ], exports: [ diff --git a/dmp-frontend/src/app/ui/auth/login/login.routing.ts b/dmp-frontend/src/app/ui/auth/login/login.routing.ts index 68fc9e558..2ba809b6b 100644 --- a/dmp-frontend/src/app/ui/auth/login/login.routing.ts +++ b/dmp-frontend/src/app/ui/auth/login/login.routing.ts @@ -12,6 +12,7 @@ import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component'; import { Oauth2DialogComponent } from '@app/ui/misc/oauth2-dialog/oauth2-dialog.component'; import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component'; import { SamlResponseLoginComponent } from './saml/saml-login-response/saml-login-response.component'; +import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-email-confirmation.component'; const routes: Routes = [ { path: '', component: LoginComponent }, @@ -21,6 +22,7 @@ const routes: Routes = [ { path: 'external/b2access', component: Oauth2DialogComponent }, { path: 'confirmation/:token', component: EmailConfirmation }, { path: 'merge/confirmation/:token', component: MergeEmailConfirmation }, + { path: 'unlink/confirmation/:token', component: UnlinkEmailConfirmation }, { path: 'confirmation', component: EmailConfirmation }, { path: 'openaire', component: Oauth2DialogComponent}, { path: 'configurable/:id', component: ConfigurableLoginComponent}, diff --git a/dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.html b/dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.html new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.html @@ -0,0 +1 @@ + diff --git a/dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.scss b/dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.ts b/dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.ts new file mode 100644 index 000000000..453eb754c --- /dev/null +++ b/dmp-frontend/src/app/ui/auth/login/unlink-email-confirmation/unlink-email-confirmation.component.ts @@ -0,0 +1,54 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { UnlinkAccountEmailConfirmationService } from '@app/core/services/unlink-account-email-confirmation/unlink-account-email-confirmation.service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { BaseComponent } from '@common/base/base.component'; +import { TranslateService } from '@ngx-translate/core'; +import { takeUntil } from "rxjs/operators"; + +@Component({ + selector: 'app-unlink-email-confirmation-component', + templateUrl: './unlink-email-confirmation.component.html' +}) +export class UnlinkEmailConfirmation extends BaseComponent implements OnInit { + + constructor( + private emailConfirmationService: UnlinkAccountEmailConfirmationService, + private route: ActivatedRoute, + private router: Router, + private language: TranslateService, + private uiNotificationService: UiNotificationService + ) { super(); } + + ngOnInit(): void { + this.route.params + .pipe(takeUntil(this._destroyed)) + .subscribe(params => { + const token = params['token'] + if (token != null) { + this.emailConfirmationService.emailConfirmation(token) + .pipe(takeUntil(this._destroyed)) + .subscribe( + result => this.onCallbackEmailConfirmationSuccess(), + error => this.onCallbackError(error) + ) + } + }); + } + + onCallbackEmailConfirmationSuccess() { + this.router.navigate(['home']); + } + + onCallbackError(error: any) { + if (error.status === 302) { + this.uiNotificationService.snackBarNotification(this.language.instant('EMAIL-CONFIRMATION.EMAIL-FOUND'), SnackBarNotificationLevel.Warning); + this.router.navigate(['home']); + } + else { + this.uiNotificationService.snackBarNotification(this.language.instant('EMAIL-CONFIRMATION.EXPIRED-EMAIL'), SnackBarNotificationLevel.Error); + this.router.navigate(['login']); + } + } + +} diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.html b/dmp-frontend/src/app/ui/user-profile/user-profile.component.html index 567239f41..b6f7152ea 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.html +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.html @@ -45,7 +45,7 @@
-
+
{{userCredential.email}}
@@ -58,8 +58,10 @@ - + +
diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.scss b/dmp-frontend/src/app/ui/user-profile/user-profile.component.scss index 811d7b5a8..244266b3c 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.scss +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.scss @@ -195,6 +195,21 @@ border-radius: 50%; } +.unlink-mail { + line-height: 2rem; + cursor: pointer; + + mat-icon { + color: var(--primary-color); + display: inline-flex; + vertical-align: middle; + } +} + +.unlink-mail:hover { + background-color: #f4f8f9; +} + .add-new-btn { margin-top: 2rem; } diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts index accdd49d5..0cd1f1086 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts @@ -30,8 +30,11 @@ import { MergeEmailConfirmationService } from '@app/core/services/merge-email-co import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { PopupNotificationDialogComponent } from "@app/library/notification/popup/popup-notification.component"; +import { UnlinkAccountRequestModel } from '@app/core/model/unlink-account/unlink-account'; +import { UnlinkAccountEmailConfirmationService } from '@app/core/services/unlink-account-email-confirmation/unlink-account-email-confirmation.service'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; @Component({ selector: 'app-user-profile', @@ -82,6 +85,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes private dialog: MatDialog, public enumUtils: EnumUtils, private mergeEmailConfirmation: MergeEmailConfirmationService, + private unlinkAccountEmailConfirmation: UnlinkAccountEmailConfirmationService, private httpClient: HttpClient, private matomoService: MatomoService ) { @@ -123,8 +127,12 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes this.userService.getEmails(userId).pipe(takeUntil(this._destroyed)) .subscribe(result => { - this.userCredentials = result; - }); + this.user.subscribe(x => { + const mainEmail = result.filter(el => el.email === x.email) + const otherEmails = result.filter(el => el.email !== x.email) + this.userCredentials = [...mainEmail, ...otherEmails]; + } + )}); }); } @@ -292,8 +300,40 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes .subscribe(() => this.router.navigate(['/reload']).then(() => this.router.navigate(['/profile']))); } - public removeAccount() { + public removeAccount(userCredential :UserCredentialModel) { + this.dialog.open(ConfirmationDialogComponent, { + data:{ + message: this.language.instant('USER-PROFILE.UNLINK-ACCOUNT-DIALOG.MESSAGE', {'accountToBeUnlinked': userCredential.email}), + confirmButton: this.language.instant('USER-PROFILE.UNLINK-ACCOUNT-DIALOG.CONFIRM'), + cancelButton: this.language.instant('USER-PROFILE.UNLINK-ACCOUNT-DIALOG.CANCEL') + }, + maxWidth:'35em' + }) + .afterClosed() + .subscribe(confirm=>{ + if(confirm){ + const unlinkAccountModel: UnlinkAccountRequestModel = { + userId: this.currentUserId, + email: userCredential.email, + provider: userCredential.provider + }; + this.unlinkAccountEmailConfirmation.sendConfirmationEmail(unlinkAccountModel).pipe(takeUntil(this._destroyed)).subscribe( + result => { + this.dialog.open(PopupNotificationDialogComponent, { + data: { + title: this.language.instant('USER-PROFILE.UNLINK-ACCOUNT.TITLE'), + message: this.language.instant('USER-PROFILE.UNLINK-ACCOUNT.MESSAGE', {'accountToBeUnlinked': userCredential.email}) + }, maxWidth: '35em' + }); + }, + error => { this.onCallbackError(error); } + ); + } + }); + } + onCallbackError(errorResponse: HttpErrorResponse) { + this.uiNotificationService.snackBarNotification(errorResponse.message, SnackBarNotificationLevel.Warning); } public addAccount() { diff --git a/dmp-frontend/src/assets/i18n/de.json b/dmp-frontend/src/assets/i18n/de.json index 5679391ea..00deac97b 100644 --- a/dmp-frontend/src/assets/i18n/de.json +++ b/dmp-frontend/src/assets/i18n/de.json @@ -1691,6 +1691,15 @@ "TITLE": "Verify linked account", "MESSAGE": "An email to verify this action has been sent to you. Once accepted, you will be able to see your accounts merged. The last email account that you merge will be the one containing all of your DMP records and activity in {{ APP_NAME }}." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be unlinked." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Einstellungen", "TIMEZONE": "Zeitzone", @@ -1738,6 +1747,7 @@ "SAVE": "Save", "LINK-NEW": "Link new", "LINK-NEW-ACCOUNT": "Link new account", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Add", "CANCEL": "Cancel" } diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index b86d57771..00f2cc29c 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -1691,6 +1691,15 @@ "TITLE": "Verify linked account", "MESSAGE": "An email to verify this action has been sent to you. Once accepted, you will be able to see your accounts merged. The last email account that you merge will be the one containing all of your DMP records and activity in {{ APP_NAME }}." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be no longer connected to your current ARGOS profile." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Settings", "TIMEZONE": "Time Zone", @@ -1738,6 +1747,7 @@ "SAVE": "Save", "LINK-NEW": "Link new", "LINK-NEW-ACCOUNT": "Link new account", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Add", "CANCEL": "Cancel" } diff --git a/dmp-frontend/src/assets/i18n/es.json b/dmp-frontend/src/assets/i18n/es.json index 17168d8a7..a7a64d590 100644 --- a/dmp-frontend/src/assets/i18n/es.json +++ b/dmp-frontend/src/assets/i18n/es.json @@ -1691,6 +1691,15 @@ "TITLE": "Verify linked account", "MESSAGE": "An email to verify this action has been sent to you. Once accepted, you will be able to see your accounts merged. The last email account that you merge will be the one containing all of your DMP records and activity in {{ APP_NAME }}." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be unlinked." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Configuración", "TIMEZONE": "Zona horaria", @@ -1738,6 +1747,7 @@ "SAVE": "Grabar", "LINK-NEW": "Nuevo enlace", "LINK-NEW-ACCOUNT": "Enlazar nueva cuenta", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Añadir", "CANCEL": "Cancelar" } diff --git a/dmp-frontend/src/assets/i18n/gr.json b/dmp-frontend/src/assets/i18n/gr.json index c2297fc2c..0c4b43a3f 100644 --- a/dmp-frontend/src/assets/i18n/gr.json +++ b/dmp-frontend/src/assets/i18n/gr.json @@ -1691,6 +1691,15 @@ "TITLE": "Verify linked account", "MESSAGE": "An email to verify this action has been sent to you. Once accepted, you will be able to see your accounts merged. The last email account that you merge will be the one containing all of your DMP records and activity in {{ APP_NAME }}." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be unlinked." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Ρυθμίσεις", "TIMEZONE": "Ζώνη Ώρας", @@ -1738,6 +1747,7 @@ "SAVE": "Save", "LINK-NEW": "Link new", "LINK-NEW-ACCOUNT": "Link new account", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Add", "CANCEL": "Cancel" } diff --git a/dmp-frontend/src/assets/i18n/hr.json b/dmp-frontend/src/assets/i18n/hr.json index ebdbb9bbb..91e5a150f 100644 --- a/dmp-frontend/src/assets/i18n/hr.json +++ b/dmp-frontend/src/assets/i18n/hr.json @@ -1691,6 +1691,15 @@ "TITLE": "Potvrdi povezani korisnički račun", "MESSAGE": "Potvrdu za ovu radnju poslali smo Vam putem elektroničke pošte. Kada potvrdite, korisnički računi biti će povezani. Posljednji račun elektroničke pošte koji povežete biti će onaj koji sadrži sve podatke o Planu upravljanja podacima i aktivnostima u {{ APP_NAME }}u." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be unlinked." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Postavke", "TIMEZONE": "Vremenska zona", @@ -1738,6 +1747,7 @@ "SAVE": "Spremi", "LINK-NEW": "Poveži novo", "LINK-NEW-ACCOUNT": "Poveži novi korisnički račun", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Unesi", "CANCEL": "Poništi" } diff --git a/dmp-frontend/src/assets/i18n/pl.json b/dmp-frontend/src/assets/i18n/pl.json index 34a720a43..b389d0ed5 100644 --- a/dmp-frontend/src/assets/i18n/pl.json +++ b/dmp-frontend/src/assets/i18n/pl.json @@ -1691,6 +1691,15 @@ "TITLE": "Zweryfikuj połączone konto", "MESSAGE": "Wysłano wiadomość e-mail w celu weryfikacji tej akcji. Po zaakceptowaniu będzie można zobaczyć połączone konta. Ostatnie połączone konto e-mail będzie tym, które zawiera wszystkie twoje rejestry DMP i aktywność w {{ APP_NAME }}." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be unlinked." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Ustawienia", "TIMEZONE": "Strefa czasowa", @@ -1738,6 +1747,7 @@ "SAVE": "Zapisz", "LINK-NEW": "Połącz nowy", "LINK-NEW-ACCOUNT": "Połącz nowe konto", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Dodaj", "CANCEL": "Anuluj" } diff --git a/dmp-frontend/src/assets/i18n/pt.json b/dmp-frontend/src/assets/i18n/pt.json index 24708c315..ca0829710 100644 --- a/dmp-frontend/src/assets/i18n/pt.json +++ b/dmp-frontend/src/assets/i18n/pt.json @@ -1691,6 +1691,15 @@ "TITLE": "Verify linked account", "MESSAGE": "An email to verify this action has been sent to you. Once accepted, you will be able to see your accounts merged. The last email account that you merge will be the one containing all of your DMP records and activity in {{ APP_NAME }}." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be unlinked." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Definições", "TIMEZONE": "Fuso horário", @@ -1738,6 +1747,7 @@ "SAVE": "Guardar", "LINK-NEW": "Ligar a nova(o)", "LINK-NEW-ACCOUNT": "Ligar a nova conta", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Adicionar", "CANCEL": "Cancelar" } diff --git a/dmp-frontend/src/assets/i18n/sk.json b/dmp-frontend/src/assets/i18n/sk.json index 546e87ad1..97338d263 100644 --- a/dmp-frontend/src/assets/i18n/sk.json +++ b/dmp-frontend/src/assets/i18n/sk.json @@ -1691,6 +1691,15 @@ "TITLE": "Verify linked account", "MESSAGE": "An email to verify this action has been sent to you. Once accepted, you will be able to see your accounts merged. The last email account that you merge will be the one containing all of your DMP records and activity in {{ APP_NAME }}." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be unlinked." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Nastavenia", "TIMEZONE": "Časové pásmo", @@ -1738,6 +1747,7 @@ "SAVE": "Save", "LINK-NEW": "Link new", "LINK-NEW-ACCOUNT": "Link new account", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Add", "CANCEL": "Cancel" } diff --git a/dmp-frontend/src/assets/i18n/sr.json b/dmp-frontend/src/assets/i18n/sr.json index d715a6ab5..6bf583b60 100644 --- a/dmp-frontend/src/assets/i18n/sr.json +++ b/dmp-frontend/src/assets/i18n/sr.json @@ -1691,6 +1691,15 @@ "TITLE": "Verify linked account", "MESSAGE": "An email to verify this action has been sent to you. Once accepted, you will be able to see your accounts merged. The last email account that you merge will be the one containing all of your DMP records and activity in {{ APP_NAME }}." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be unlinked." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Podešavanja", "TIMEZONE": "Vremenska zona", @@ -1738,6 +1747,7 @@ "SAVE": "Save", "LINK-NEW": "Link new", "LINK-NEW-ACCOUNT": "Link new account", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Add", "CANCEL": "Cancel" } diff --git a/dmp-frontend/src/assets/i18n/tr.json b/dmp-frontend/src/assets/i18n/tr.json index 3bca30340..1eadddbe4 100644 --- a/dmp-frontend/src/assets/i18n/tr.json +++ b/dmp-frontend/src/assets/i18n/tr.json @@ -1691,6 +1691,15 @@ "TITLE": "Verify linked account", "MESSAGE": "An email to verify this action has been sent to you. Once accepted, you will be able to see your accounts merged. The last email account that you merge will be the one containing all of your DMP records and activity in {{ APP_NAME }}." }, + "UNLINK-ACCOUNT": { + "TITLE": "Verify account to be unlinked", + "MESSAGE": "An email to verify this action has been sent to you. Once accepted, {{accountToBeUnlinked}} will be unlinked." + }, + "UNLINK-ACCOUNT-DIALOG": { + "MESSAGE": "By clicking \"Confirm\", you accept the transfer of your ARGOS activity performed from this account to your main ARGOS account, which is the one that is listed first. By logging in again with the unlinked account, you will be able to start your ARGOS experience from scratch, in an empty dashboard.", + "CONFIRM": "Confirm", + "CANCEL": "Cancel" + }, "SETTINGS": { "TITLE": "Ayarlar", "TIMEZONE": "Zaman Dilimi", @@ -1738,6 +1747,7 @@ "SAVE": "Kaydet", "LINK-NEW": "Yeni bağlantı", "LINK-NEW-ACCOUNT": "Yeni hesabı bağla", + "UNLINK-ACCOUNT": "Unlink", "ADD": "Ekle", "CANCEL": "İptal" }