add unlink functionality in profile section, when an email is unlinked that account is starting from scratch resulting in an empty dashboard.

This commit is contained in:
Bernaldo Mihasi 2023-05-23 17:15:11 +03:00
parent 5e764559cf
commit b2f2a79a5d
30 changed files with 818 additions and 7 deletions

View File

@ -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<ResponseItem> 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<ResponseItem> 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."));
}
}
}

View File

@ -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<String, Object> map = new ObjectMapper().readValue(loginConfirmationEmail.getData(), new TypeReference<Map<String, Object>>() {});
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
);
}
}
}

View File

@ -12,7 +12,11 @@ public interface ConfirmationEmailService {
public void createMergeConfirmationEmail(EmailConfirmationDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId, Principal principal, Integer provider); 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 sentConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService);
public CompletableFuture sentMergeConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService, String userName); public CompletableFuture sentMergeConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService, String userName);
public CompletableFuture sentUnlinkConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService);
} }

View File

@ -1,6 +1,7 @@
package eu.eudat.logic.services.utilities; package eu.eudat.logic.services.utilities;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import eu.eudat.data.dao.entities.EmailConfirmationDao; import eu.eudat.data.dao.entities.EmailConfirmationDao;
import eu.eudat.data.entities.EmailConfirmation; 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<String, Object> map = new ObjectMapper().readValue(confirmationEmail.getData(), new TypeReference<Map<String, Object>>() {});
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) { private String createMergeContent(UUID confirmationToken, MailService mailService, String userName) {
String content = mailService.getMailTemplateContent(this.environment.getProperty("email.merge")); String content = mailService.getMailTemplateContent(this.environment.getProperty("email.merge"));
content = content.replace("{userName}", userName); content = content.replace("{userName}", userName);
@ -94,6 +119,16 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService {
return content; 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) { private String secondsToTime(int seconds) {
int sec = seconds % 60; int sec = seconds % 60;
int hour = seconds / 60; int hour = seconds / 60;
@ -128,4 +163,30 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService {
sentMergeConfirmationEmail(confirmationEmail, mailService, principal.getName()); 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<String, Object> 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);
}
} }

View File

@ -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;
}
}

View File

@ -32,6 +32,7 @@ configuration.configurable_login_providers=configurableLoginProviders.json
email.invite=classpath:templates/email/email.html email.invite=classpath:templates/email/email.html
email.confirmation=classpath:templates/email/emailConfirmation.html email.confirmation=classpath:templates/email/emailConfirmation.html
email.merge=classpath:templates/email/emailMergeConfirmation.html email.merge=classpath:templates/email/emailMergeConfirmation.html
email.unlink=classpath:templates/email/emailUnlinkConfirmation.html
#############FACEBOOK LOGIN CONFIGURATIONS######### #############FACEBOOK LOGIN CONFIGURATIONS#########
facebook.login.clientId= facebook.login.clientId=

View File

@ -31,6 +31,7 @@ configuration.configurable_login_providers=configurableLoginProviders.json
email.invite=classpath:templates/email/email.html email.invite=classpath:templates/email/email.html
email.confirmation=classpath:templates/email/emailConfirmation.html email.confirmation=classpath:templates/email/emailConfirmation.html
email.merge=classpath:templates/email/emailMergeConfirmation.html email.merge=classpath:templates/email/emailMergeConfirmation.html
email.unlink=classpath:templates/email/emailUnlinkConfirmation.html
email.dataset.template=classpath:templates/email/emailAdmin.html email.dataset.template=classpath:templates/email/emailAdmin.html
####################INVITATION MAIL CONFIGURATIONS############## ####################INVITATION MAIL CONFIGURATIONS##############

View File

@ -58,6 +58,7 @@ configuration.configurable_login_providers=configurableLoginProviders.json
email.invite=file:templates/email/email.html email.invite=file:templates/email/email.html
email.confirmation=file:templates/email/emailConfirmation.html email.confirmation=file:templates/email/emailConfirmation.html
email.merge=file:templates/email/emailMergeConfirmation.html email.merge=file:templates/email/emailMergeConfirmation.html
email.unlink=classpath:templates/email/emailUnlinkConfirmation.html
email.dataset.template=file:templates/email/emailAdmin.html email.dataset.template=file:templates/email/emailAdmin.html
#############LOGIN CONFIGURATIONS######### #############LOGIN CONFIGURATIONS#########

View File

@ -0,0 +1,304 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Simple Transactional Email</title>
<style>
/* -------------------------------------
GLOBAL RESETS
------------------------------------- */
img {
border: none;
-ms-interpolation-mode: bicubic;
max-width: 100%; }
body {
background-color: #f6f6f6;
font-family: sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
line-height: 1.4;
margin: 0;
padding: 0;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%; }
table {
border-collapse: separate;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
width: 100%; }
table td {
font-family: sans-serif;
font-size: 14px;
vertical-align: top; }
/* -------------------------------------
BODY & CONTAINER
------------------------------------- */
.body {
background-color: #f6f6f6;
width: 100%; }
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
.container {
display: block;
Margin: 0 auto !important;
/* makes it centered */
max-width: 580px;
padding: 10px;
width: 580px; }
/* This should also be a block element, so that it will fill 100% of the .container */
.content {
box-sizing: border-box;
display: block;
Margin: 0 auto;
max-width: 580px;
padding: 10px; }
/* -------------------------------------
HEADER, FOOTER, MAIN
------------------------------------- */
.main {
background: #ffffff;
border-radius: 3px;
width: 100%; }
.wrapper {
box-sizing: border-box;
padding: 20px; }
.content-block {
padding-bottom: 10px;
padding-top: 10px;
}
.footer {
clear: both;
Margin-top: 10px;
text-align: center;
width: 100%; }
.footer td,
.footer p,
.footer span,
.footer a {
color: #999999;
font-size: 12px;
text-align: center; }
/* -------------------------------------
TYPOGRAPHY
------------------------------------- */
h1,
h2,
h3,
h4 {
color: #000000;
font-family: sans-serif;
font-weight: 400;
line-height: 1.4;
margin: 0;
Margin-bottom: 30px; }
h1 {
font-size: 35px;
font-weight: 300;
text-align: center;
text-transform: capitalize; }
p,
ul,
ol {
font-family: sans-serif;
font-size: 14px;
font-weight: normal;
margin: 0;
Margin-bottom: 15px; }
p li,
ul li,
ol li {
list-style-position: inside;
margin-left: 5px; }
a {
color: #3498db;
text-decoration: underline; }
/* -------------------------------------
BUTTONS
------------------------------------- */
.btn {
box-sizing: border-box;
width: 100%; }
.btn > tbody > tr > td {
padding-bottom: 15px; }
.btn table {
width: auto; }
.btn table td {
background-color: #ffffff;
border-radius: 5px;
text-align: center; }
.btn a {
background-color: #ffffff;
border: solid 1px #3498db;
border-radius: 5px;
box-sizing: border-box;
color: #3498db;
cursor: pointer;
display: inline-block;
font-size: 14px;
font-weight: bold;
margin: 0;
padding: 12px 25px;
text-decoration: none;
text-transform: capitalize; }
.btn-primary table td {
background-color: #3498db; }
.btn-primary a {
background-color: #3498db;
border-color: #3498db;
color: #ffffff; }
/* -------------------------------------
OTHER STYLES THAT MIGHT BE USEFUL
------------------------------------- */
.last {
margin-bottom: 0; }
.first {
margin-top: 0; }
.align-center {
text-align: center; }
.align-right {
text-align: right; }
.align-left {
text-align: left; }
.clear {
clear: both; }
.mt0 {
margin-top: 0; }
.mb0 {
margin-bottom: 0; }
.preheader {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0; }
.powered-by a {
text-decoration: none; }
hr {
border: 0;
border-bottom: 1px solid #f6f6f6;
Margin: 20px 0; }
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important; }
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important; }
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important; }
table[class=body] .content {
padding: 0 !important; }
table[class=body] .container {
padding: 0 !important;
width: 100% !important; }
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important; }
table[class=body] .btn table {
width: 100% !important; }
table[class=body] .btn a {
width: 100% !important; }
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important; }}
/* -------------------------------------
PRESERVE THESE STYLES IN THE HEAD
------------------------------------- */
@media all {
.ExternalClass {
width: 100%; }
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%; }
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important; }
.btn-primary table td:hover {
background-color: #34495e !important; }
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important; } }
</style>
</head>
<body class="">
<table border="0" cellpadding="0" cellspacing="0" class="body">
<tr>
<td>&nbsp;</td>
<td class="container">
<div class="content">
<!-- START CENTERED WHITE CONTAINER -->
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
<table class="main">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<img src="classpath:images\OpenDMP.png" alt="OpenDMP" width="100" height="81">
<h2>You have made a request to unlink your email account in ARGOS.</h2>
<p>Please confirm that you want to unlink your {email} account.
<br/>The link will expire in {expiration_time}.</p>
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
<tbody>
<tr>
<td align="left">
<table border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td> <a href="{host}/unlink/confirmation/{confirmationToken}" target="_blank">Confirm Unlink Request</a> </td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- START FOOTER -->
<div class="footer">
</div>
<!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER -->
</div>
</td>
<td>&nbsp;</td>
</tr>
</table>
</body>
</html>

View File

@ -51,6 +51,7 @@ import { AboutService } from './services/about/about.service';
import { FaqService } from './services/faq/faq.service'; import { FaqService } from './services/faq/faq.service';
import { GlossaryService } from './services/glossary/glossary.service'; import { GlossaryService } from './services/glossary/glossary.service';
import { TermsOfServiceService } from './services/terms-of-service/terms-of-service.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. // This is shared module that provides all the services. Its imported only once on the AppModule.
@ -122,6 +123,7 @@ export class CoreServiceModule {
TermsOfServiceService, TermsOfServiceService,
CurrencyService, CurrencyService,
MergeEmailConfirmationService, MergeEmailConfirmationService,
UnlinkAccountEmailConfirmationService,
ConfigurationService, ConfigurationService,
{ {
provide: APP_INITIALIZER, provide: APP_INITIALIZER,

View File

@ -0,0 +1,5 @@
export class UnlinkAccountRequestModel {
userId: String;
email: String;
provider: number;
}

View File

@ -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<String>(this.actioUrl + token, { headers: this.headers });
}
public sendConfirmationEmail(unlinkRequest: UnlinkAccountRequestModel) {
return this.http.post<String>(this.actioUrl, unlinkRequest, { headers: this.headers });
}
}

View File

@ -18,6 +18,7 @@ import { MergeLoginService } from './utilities/merge-login.service';
import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component'; import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component';
import { SamlLoginService } from '@app/core/services/saml-login.service'; import { SamlLoginService } from '@app/core/services/saml-login.service';
import { SamlResponseLoginComponent } from './saml/saml-login-response/saml-login-response.component'; import { SamlResponseLoginComponent } from './saml/saml-login-response/saml-login-response.component';
import { UnlinkEmailConfirmation } from './unlink-email-confirmation/unlink-email-confirmation.component';
@NgModule({ @NgModule({
imports: [ imports: [
@ -37,6 +38,7 @@ import { SamlResponseLoginComponent } from './saml/saml-login-response/saml-logi
ConfigurableLoginComponent, ConfigurableLoginComponent,
ZenodoLoginComponent, ZenodoLoginComponent,
MergeEmailConfirmation, MergeEmailConfirmation,
UnlinkEmailConfirmation,
SamlResponseLoginComponent SamlResponseLoginComponent
], ],
exports: [ exports: [

View File

@ -12,6 +12,7 @@ import { ZenodoLoginComponent } from './zenodo-login/zenodo-login.component';
import { Oauth2DialogComponent } from '@app/ui/misc/oauth2-dialog/oauth2-dialog.component'; import { Oauth2DialogComponent } from '@app/ui/misc/oauth2-dialog/oauth2-dialog.component';
import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component'; import { MergeEmailConfirmation } from './merge-email-confirmation/merge-email-confirmation.component';
import { SamlResponseLoginComponent } from './saml/saml-login-response/saml-login-response.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 = [ const routes: Routes = [
{ path: '', component: LoginComponent }, { path: '', component: LoginComponent },
@ -21,6 +22,7 @@ const routes: Routes = [
{ path: 'external/b2access', component: Oauth2DialogComponent }, { path: 'external/b2access', component: Oauth2DialogComponent },
{ path: 'confirmation/:token', component: EmailConfirmation }, { path: 'confirmation/:token', component: EmailConfirmation },
{ path: 'merge/confirmation/:token', component: MergeEmailConfirmation }, { path: 'merge/confirmation/:token', component: MergeEmailConfirmation },
{ path: 'unlink/confirmation/:token', component: UnlinkEmailConfirmation },
{ path: 'confirmation', component: EmailConfirmation }, { path: 'confirmation', component: EmailConfirmation },
{ path: 'openaire', component: Oauth2DialogComponent}, { path: 'openaire', component: Oauth2DialogComponent},
{ path: 'configurable/:id', component: ConfigurableLoginComponent}, { path: 'configurable/:id', component: ConfigurableLoginComponent},

View File

@ -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']);
}
}
}

View File

@ -45,7 +45,7 @@
</div> </div>
<div class="col-auto vertical-line"> <div class="col-auto vertical-line">
<div *ngIf="userCredentials"> <div *ngIf="userCredentials">
<div *ngFor="let userCredential of userCredentials"> <div *ngFor="let userCredential of userCredentials; index as i">
<div class="row user-credential"> <div class="row user-credential">
<!-- <div class="col-auto pl-4 ml-2 pr-0 d-flex align-items-center"><mat-icon class="check-icon mat-icon">check</mat-icon></div> --> <!-- <div class="col-auto pl-4 ml-2 pr-0 d-flex align-items-center"><mat-icon class="check-icon mat-icon">check</mat-icon></div> -->
<div class="col-auto mail-text pr-0">{{userCredential.email}}</div> <div class="col-auto mail-text pr-0">{{userCredential.email}}</div>
@ -58,8 +58,10 @@
<span *ngIf="userCredential.provider === authProviderEnum.OpenAire" class="openaireIcon"></span> <span *ngIf="userCredential.provider === authProviderEnum.OpenAire" class="openaireIcon"></span>
<span *ngIf="userCredential.provider === authProviderEnum.Configurable" class="configurableIcon"></span> <span *ngIf="userCredential.provider === authProviderEnum.Configurable" class="configurableIcon"></span>
<span *ngIf="userCredential.provider === authProviderEnum.Zenodo" class="zenodoIcon"></span> <span *ngIf="userCredential.provider === authProviderEnum.Zenodo" class="zenodoIcon"></span>
<!-- <div class="col-auto"></div> <!-- <div class="col-auto"></div> -->
<div class="col-auto d-flex align-items-center" (click)="removeAccount()"><mat-icon class="clear-icon mat-icon">clear</mat-icon></div> --> <div *ngIf="i != 0" class="col-auto d-flex align-items-center unlink-mail" (click)="removeAccount(userCredential)">
<mat-icon [matTooltip]="'USER-PROFILE.ACTIONS.UNLINK-ACCOUNT' | translate" matTooltipPosition="right">link_off</mat-icon>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -195,6 +195,21 @@
border-radius: 50%; 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 { .add-new-btn {
margin-top: 2rem; margin-top: 2rem;
} }

View File

@ -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 { 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 { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { MatomoService } from '@app/core/services/matomo/matomo-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 { 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({ @Component({
selector: 'app-user-profile', selector: 'app-user-profile',
@ -82,6 +85,7 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
private dialog: MatDialog, private dialog: MatDialog,
public enumUtils: EnumUtils, public enumUtils: EnumUtils,
private mergeEmailConfirmation: MergeEmailConfirmationService, private mergeEmailConfirmation: MergeEmailConfirmationService,
private unlinkAccountEmailConfirmation: UnlinkAccountEmailConfirmationService,
private httpClient: HttpClient, private httpClient: HttpClient,
private matomoService: MatomoService private matomoService: MatomoService
) { ) {
@ -123,8 +127,12 @@ export class UserProfileComponent extends BaseComponent implements OnInit, OnDes
this.userService.getEmails(userId).pipe(takeUntil(this._destroyed)) this.userService.getEmails(userId).pipe(takeUntil(this._destroyed))
.subscribe(result => { .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']))); .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() { public addAccount() {

View File

@ -1691,6 +1691,15 @@
"TITLE": "Verify linked account", "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 }}." "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": { "SETTINGS": {
"TITLE": "Einstellungen", "TITLE": "Einstellungen",
"TIMEZONE": "Zeitzone", "TIMEZONE": "Zeitzone",
@ -1738,6 +1747,7 @@
"SAVE": "Save", "SAVE": "Save",
"LINK-NEW": "Link new", "LINK-NEW": "Link new",
"LINK-NEW-ACCOUNT": "Link new account", "LINK-NEW-ACCOUNT": "Link new account",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Add", "ADD": "Add",
"CANCEL": "Cancel" "CANCEL": "Cancel"
} }

View File

@ -1691,6 +1691,15 @@
"TITLE": "Verify linked account", "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 }}." "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": { "SETTINGS": {
"TITLE": "Settings", "TITLE": "Settings",
"TIMEZONE": "Time Zone", "TIMEZONE": "Time Zone",
@ -1738,6 +1747,7 @@
"SAVE": "Save", "SAVE": "Save",
"LINK-NEW": "Link new", "LINK-NEW": "Link new",
"LINK-NEW-ACCOUNT": "Link new account", "LINK-NEW-ACCOUNT": "Link new account",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Add", "ADD": "Add",
"CANCEL": "Cancel" "CANCEL": "Cancel"
} }

View File

@ -1691,6 +1691,15 @@
"TITLE": "Verify linked account", "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 }}." "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": { "SETTINGS": {
"TITLE": "Configuración", "TITLE": "Configuración",
"TIMEZONE": "Zona horaria", "TIMEZONE": "Zona horaria",
@ -1738,6 +1747,7 @@
"SAVE": "Grabar", "SAVE": "Grabar",
"LINK-NEW": "Nuevo enlace", "LINK-NEW": "Nuevo enlace",
"LINK-NEW-ACCOUNT": "Enlazar nueva cuenta", "LINK-NEW-ACCOUNT": "Enlazar nueva cuenta",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Añadir", "ADD": "Añadir",
"CANCEL": "Cancelar" "CANCEL": "Cancelar"
} }

View File

@ -1691,6 +1691,15 @@
"TITLE": "Verify linked account", "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 }}." "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": { "SETTINGS": {
"TITLE": "Ρυθμίσεις", "TITLE": "Ρυθμίσεις",
"TIMEZONE": "Ζώνη Ώρας", "TIMEZONE": "Ζώνη Ώρας",
@ -1738,6 +1747,7 @@
"SAVE": "Save", "SAVE": "Save",
"LINK-NEW": "Link new", "LINK-NEW": "Link new",
"LINK-NEW-ACCOUNT": "Link new account", "LINK-NEW-ACCOUNT": "Link new account",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Add", "ADD": "Add",
"CANCEL": "Cancel" "CANCEL": "Cancel"
} }

View File

@ -1691,6 +1691,15 @@
"TITLE": "Potvrdi povezani korisnički račun", "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." "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": { "SETTINGS": {
"TITLE": "Postavke", "TITLE": "Postavke",
"TIMEZONE": "Vremenska zona", "TIMEZONE": "Vremenska zona",
@ -1738,6 +1747,7 @@
"SAVE": "Spremi", "SAVE": "Spremi",
"LINK-NEW": "Poveži novo", "LINK-NEW": "Poveži novo",
"LINK-NEW-ACCOUNT": "Poveži novi korisnički račun", "LINK-NEW-ACCOUNT": "Poveži novi korisnički račun",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Unesi", "ADD": "Unesi",
"CANCEL": "Poništi" "CANCEL": "Poništi"
} }

View File

@ -1691,6 +1691,15 @@
"TITLE": "Zweryfikuj połączone konto", "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 }}." "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": { "SETTINGS": {
"TITLE": "Ustawienia", "TITLE": "Ustawienia",
"TIMEZONE": "Strefa czasowa", "TIMEZONE": "Strefa czasowa",
@ -1738,6 +1747,7 @@
"SAVE": "Zapisz", "SAVE": "Zapisz",
"LINK-NEW": "Połącz nowy", "LINK-NEW": "Połącz nowy",
"LINK-NEW-ACCOUNT": "Połącz nowe konto", "LINK-NEW-ACCOUNT": "Połącz nowe konto",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Dodaj", "ADD": "Dodaj",
"CANCEL": "Anuluj" "CANCEL": "Anuluj"
} }

View File

@ -1691,6 +1691,15 @@
"TITLE": "Verify linked account", "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 }}." "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": { "SETTINGS": {
"TITLE": "Definições", "TITLE": "Definições",
"TIMEZONE": "Fuso horário", "TIMEZONE": "Fuso horário",
@ -1738,6 +1747,7 @@
"SAVE": "Guardar", "SAVE": "Guardar",
"LINK-NEW": "Ligar a nova(o)", "LINK-NEW": "Ligar a nova(o)",
"LINK-NEW-ACCOUNT": "Ligar a nova conta", "LINK-NEW-ACCOUNT": "Ligar a nova conta",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Adicionar", "ADD": "Adicionar",
"CANCEL": "Cancelar" "CANCEL": "Cancelar"
} }

View File

@ -1691,6 +1691,15 @@
"TITLE": "Verify linked account", "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 }}." "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": { "SETTINGS": {
"TITLE": "Nastavenia", "TITLE": "Nastavenia",
"TIMEZONE": "Časové pásmo", "TIMEZONE": "Časové pásmo",
@ -1738,6 +1747,7 @@
"SAVE": "Save", "SAVE": "Save",
"LINK-NEW": "Link new", "LINK-NEW": "Link new",
"LINK-NEW-ACCOUNT": "Link new account", "LINK-NEW-ACCOUNT": "Link new account",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Add", "ADD": "Add",
"CANCEL": "Cancel" "CANCEL": "Cancel"
} }

View File

@ -1691,6 +1691,15 @@
"TITLE": "Verify linked account", "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 }}." "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": { "SETTINGS": {
"TITLE": "Podešavanja", "TITLE": "Podešavanja",
"TIMEZONE": "Vremenska zona", "TIMEZONE": "Vremenska zona",
@ -1738,6 +1747,7 @@
"SAVE": "Save", "SAVE": "Save",
"LINK-NEW": "Link new", "LINK-NEW": "Link new",
"LINK-NEW-ACCOUNT": "Link new account", "LINK-NEW-ACCOUNT": "Link new account",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Add", "ADD": "Add",
"CANCEL": "Cancel" "CANCEL": "Cancel"
} }

View File

@ -1691,6 +1691,15 @@
"TITLE": "Verify linked account", "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 }}." "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": { "SETTINGS": {
"TITLE": "Ayarlar", "TITLE": "Ayarlar",
"TIMEZONE": "Zaman Dilimi", "TIMEZONE": "Zaman Dilimi",
@ -1738,6 +1747,7 @@
"SAVE": "Kaydet", "SAVE": "Kaydet",
"LINK-NEW": "Yeni bağlantı", "LINK-NEW": "Yeni bağlantı",
"LINK-NEW-ACCOUNT": "Yeni hesabı bağla", "LINK-NEW-ACCOUNT": "Yeni hesabı bağla",
"UNLINK-ACCOUNT": "Unlink",
"ADD": "Ekle", "ADD": "Ekle",
"CANCEL": "İptal" "CANCEL": "İptal"
} }