Adds email confirmation logic and view.

This commit is contained in:
gkolokythas 2019-07-12 17:12:16 +03:00
parent b691084d73
commit f8310b2362
25 changed files with 854 additions and 2 deletions

View File

@ -0,0 +1,6 @@
package eu.eudat.data.dao.criteria;
import eu.eudat.data.entities.LoginConfirmationEmail;
public class LoginConfirmationEmailCriteria extends Criteria<LoginConfirmationEmail>{
}

View File

@ -0,0 +1,13 @@
package eu.eudat.data.dao.entities;
import eu.eudat.data.dao.DatabaseAccessLayer;
import eu.eudat.data.dao.criteria.LoginConfirmationEmailCriteria;
import eu.eudat.data.entities.LoginConfirmationEmail;
import eu.eudat.queryable.QueryableList;
import java.util.UUID;
public interface LoginConfirmationEmailDao extends DatabaseAccessLayer<LoginConfirmationEmail, UUID> {
QueryableList<LoginConfirmationEmail> getWithCriteria(LoginConfirmationEmailCriteria criteria);
}

View File

@ -0,0 +1,56 @@
package eu.eudat.data.dao.entities;
import eu.eudat.data.dao.DatabaseAccess;
import eu.eudat.data.dao.criteria.LoginConfirmationEmailCriteria;
import eu.eudat.data.dao.databaselayer.service.DatabaseService;
import eu.eudat.data.entities.LoginConfirmationEmail;
import eu.eudat.queryable.QueryableList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@Service("LoginConfirmationEmailDao")
public class LoginConfirmationEmailDaoImpl extends DatabaseAccess<LoginConfirmationEmail> implements LoginConfirmationEmailDao {
@Autowired
public LoginConfirmationEmailDaoImpl(DatabaseService<LoginConfirmationEmail> databaseService) {
super(databaseService);
}
@Override
public QueryableList<LoginConfirmationEmail> getWithCriteria(LoginConfirmationEmailCriteria criteria) {
return null;
}
@Override
public LoginConfirmationEmail createOrUpdate(LoginConfirmationEmail item) {
return this.getDatabaseService().createOrUpdate(item, LoginConfirmationEmail.class);
}
@Override
public CompletableFuture<LoginConfirmationEmail> createOrUpdateAsync(LoginConfirmationEmail item) {
return null;
}
@Override
public LoginConfirmationEmail find(UUID id) {
return this.getDatabaseService().getQueryable(LoginConfirmationEmail.class).where((builder, root) -> builder.equal(root.get("id"), id)).getSingle();
}
@Override
public LoginConfirmationEmail find(UUID id, String hint) {
throw new UnsupportedOperationException();
}
@Override
public void delete(LoginConfirmationEmail item) {
throw new UnsupportedOperationException();
}
@Override
public QueryableList<LoginConfirmationEmail> asQueryable() {
return this.getDatabaseService().getQueryable(LoginConfirmationEmail.class);
}
}

View File

@ -0,0 +1,94 @@
package eu.eudat.data.entities;
import eu.eudat.data.converters.DateToUTCConverter;
import eu.eudat.queryable.queryableentity.DataEntity;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Entity
@Table(name = "\"LoginConfirmationEmail\"")
public class LoginConfirmationEmail implements DataEntity<LoginConfirmationEmail, UUID> {
@Id
@GeneratedValue
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(name = "\"ID\"", updatable = false, nullable = false)
private UUID id;
@Column(name = "\"email\"", nullable = false)
private String email;
@Column(name = "\"isConfirmed\"", nullable = false)
private boolean isConfirmed;
@Column(name = "\"token\"", updatable = false, nullable = false, columnDefinition = "BINARY(16)")
private UUID token;
@Column(name = "\"userId\"", nullable = false)
private UUID userId;
@Column(name = "\"expiresAt\"", nullable = false)
@Convert(converter = DateToUTCConverter.class)
private Date expiresAt;
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public boolean getIsConfirmed() {
return isConfirmed;
}
public void setIsConfirmed(boolean confirmed) {
isConfirmed = confirmed;
}
public UUID getToken() {
return token;
}
public void setToken(UUID token) {
this.token = token;
}
public UUID getUserId() {
return userId;
}
public void setUserId(UUID userId) {
this.userId = userId;
}
public Date getExpiresAt() {
return expiresAt;
}
public void setExpiresAt(Date expiresAt) {
this.expiresAt = expiresAt;
}
@Override
public void update(LoginConfirmationEmail entity) {
}
@Override
public UUID getKeys() {
return null;
}
@Override
public LoginConfirmationEmail buildFromTuple(List<Tuple> tuple, List<String> fields, String base) {
return null;
}
}

View File

@ -0,0 +1,54 @@
package eu.eudat.controllers;
import eu.eudat.exceptions.emailconfirmation.HasConfirmedEmailException;
import eu.eudat.exceptions.emailconfirmation.TokenExpiredException;
import eu.eudat.logic.managers.EmailConfirmationManager;
import eu.eudat.logic.security.CustomAuthenticationProvider;
import eu.eudat.logic.services.operations.authentication.AuthenticationService;
import eu.eudat.models.data.helpers.responses.ResponseItem;
import eu.eudat.models.data.security.Principal;
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/emailConfirmation")
public class EmailConfirmation {
private EmailConfirmationManager emailConfirmationManager;
@Autowired
public EmailConfirmation(EmailConfirmationManager emailConfirmationManager) {
this.emailConfirmationManager = emailConfirmationManager;
}
@Transactional
@RequestMapping(method = RequestMethod.GET, value = {"/{emailToken}"})
public @ResponseBody
ResponseEntity<ResponseItem> emailConfirmation(@PathVariable(value = "emailToken") String token) {
try {
this.emailConfirmationManager.confirmEmail(token);
return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE));
} catch
(HasConfirmedEmailException | TokenExpiredException ex) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE));
}
}
@Transactional
@RequestMapping(method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public @ResponseBody
ResponseEntity sendConfirmatioEmail(@RequestBody String email, Principal principal) {
try {
this.emailConfirmationManager.sendConfirmationEmail(email, principal);
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));
}
}
}

View File

@ -0,0 +1,8 @@
package eu.eudat.exceptions.emailconfirmation;
public class HasConfirmedEmailException extends Exception {
public HasConfirmedEmailException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,8 @@
package eu.eudat.exceptions.emailconfirmation;
public class TokenExpiredException extends Exception {
public TokenExpiredException(String msg) {
super(msg);
}
}

View File

@ -0,0 +1,50 @@
package eu.eudat.logic.managers;
import eu.eudat.data.entities.LoginConfirmationEmail;
import eu.eudat.data.entities.UserInfo;
import eu.eudat.exceptions.emailconfirmation.HasConfirmedEmailException;
import eu.eudat.exceptions.emailconfirmation.TokenExpiredException;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.models.data.security.Principal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.UUID;
@Component
public class EmailConfirmationManager {
private ApiContext apiContext;
@Autowired
public EmailConfirmationManager(ApiContext apiContext) {
this.apiContext = apiContext;
}
public void confirmEmail(String token) throws TokenExpiredException, HasConfirmedEmailException {
LoginConfirmationEmail 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.");
UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().asQueryable()
.where((builder, root) -> builder.equal(root.get("id"), loginConfirmationEmail.getUserId())).getSingle();
if (user.getEmail() != null)
throw new HasConfirmedEmailException("User already has confirmed his Email.");
loginConfirmationEmail.setIsConfirmed(true);
user.setEmail(loginConfirmationEmail.getEmail());
apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().createOrUpdate(user);
apiContext.getOperationsContext().getDatabaseRepository().getLoginConfirmationEmailDao().createOrUpdate(loginConfirmationEmail);
}
public void sendConfirmationEmail(String email, Principal principal) throws HasConfirmedEmailException {
UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId());
if (user.getEmail() != null)
throw new HasConfirmedEmailException("User already has confirmed his Email.");
apiContext.getUtilitiesService().getConfirmationEmailService().createConfirmationEmail(
apiContext.getOperationsContext().getDatabaseRepository().getLoginConfirmationEmailDao(),
apiContext.getUtilitiesService().getMailService(),
email,
principal.getId());
}
}

View File

@ -46,5 +46,7 @@ public interface DatabaseRepository {
DatasetServiceDao getDatasetServiceDao();
LoginConfirmationEmailDao getLoginConfirmationEmailDao();
<T> void detachEntity(T entity);
}

View File

@ -32,6 +32,7 @@ public class DatabaseRepositoryImpl implements DatabaseRepository {
private DMPProfileDao dmpProfileDao;
private DatasetExternalDatasetDao datasetExternalDatasetDao;
private DatasetServiceDao datasetServiceDao;
private LoginConfirmationEmailDao loginConfirmationEmailDao;
private EntityManager entityManager;
@ -240,6 +241,16 @@ public class DatabaseRepositoryImpl implements DatabaseRepository {
this.datasetServiceDao = datasetServiceDao;
}
@Override
public LoginConfirmationEmailDao getLoginConfirmationEmailDao() {
return loginConfirmationEmailDao;
}
@Autowired
public void setLoginConfirmationEmailDao(LoginConfirmationEmailDao loginConfirmationEmailDao) {
this.loginConfirmationEmailDao = loginConfirmationEmailDao;
}
public <T> void detachEntity(T entity) {
this.entityManager.detach(entity);
}

View File

@ -0,0 +1,13 @@
package eu.eudat.logic.services.utilities;
import eu.eudat.data.dao.entities.LoginConfirmationEmailDao;
import eu.eudat.data.entities.LoginConfirmationEmail;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public interface ConfirmationEmailService {
public void createConfirmationEmail(LoginConfirmationEmailDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId);
public CompletableFuture sentConfirmationEmail(LoginConfirmationEmail confirmationEmail, MailService mailService);
}

View File

@ -0,0 +1,73 @@
package eu.eudat.logic.services.utilities;
import eu.eudat.core.logger.Logger;
import eu.eudat.data.dao.entities.LoginConfirmationEmailDao;
import eu.eudat.data.entities.LoginConfirmationEmail;
import eu.eudat.models.data.mail.SimpleMail;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@Service("ConfirmationEmailService")
public class ConfirmationEmailServiceImpl implements ConfirmationEmailService {
private Logger logger;
private Environment environment;
public ConfirmationEmailServiceImpl(Logger logger, Environment environment) {
this.logger = logger;
this.environment = environment;
}
@Override
public void createConfirmationEmail(LoginConfirmationEmailDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId) {
LoginConfirmationEmail confirmationEmail = new LoginConfirmationEmail();
confirmationEmail.setEmail(email);
confirmationEmail.setExpiresAt(Date
.from(new Date()
.toInstant()
.plusSeconds(Long.parseLong(this.environment.getProperty("conf_email.expiration_time_seconds")))
)
);
confirmationEmail.setUserId(userId);
confirmationEmail.setIsConfirmed(false);
confirmationEmail.setToken(UUID.randomUUID());
confirmationEmail = loginConfirmationEmailDao.createOrUpdate(confirmationEmail);
sentConfirmationEmail(confirmationEmail, mailService);
}
@Override
public CompletableFuture sentConfirmationEmail(LoginConfirmationEmail confirmationEmail, MailService mailService) {
return CompletableFuture.runAsync(() -> {
SimpleMail mail = new SimpleMail();
mail.setSubject(environment.getProperty("conf_email.subject"));
mail.setContent(createContent(confirmationEmail.getToken(), mailService));
mail.setTo(confirmationEmail.getEmail());
try {
mailService.sendSimpleMail(mail);
} catch (Exception ex) {
ex.printStackTrace();
this.logger.error(ex, ex.getMessage());
}
});
}
private String createContent(UUID confirmationToken, MailService mailService) {
String content = mailService.getMailTemplateContent("classpath:emailConfirmation.html");
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"));
return content;
}
private String secondsToTime(int seconds) {
int sec = seconds % 60;
int hour = seconds / 60;
int min = hour % 60;
hour = hour / 60;
return (hour + ":" + min + ":" + sec);
}
}

View File

@ -12,4 +12,6 @@ public interface UtilitiesService {
MailService getMailService();
VisibilityRuleService getVisibilityRuleService();
ConfirmationEmailService getConfirmationEmailService();
}

View File

@ -13,12 +13,14 @@ public class UtilitiesServiceImpl implements UtilitiesService {
private InvitationService invitationService;
private MailService mailService;
private VisibilityRuleService visibilityRuleService;
private ConfirmationEmailService confirmationEmailService;
@Autowired
public UtilitiesServiceImpl(InvitationService invitationService, MailService mailService, VisibilityRuleService visibilityRuleService) {
public UtilitiesServiceImpl(InvitationService invitationService, MailService mailService, VisibilityRuleService visibilityRuleService, ConfirmationEmailService confirmationEmailService) {
this.invitationService = invitationService;
this.mailService = mailService;
this.visibilityRuleService = visibilityRuleService;
this.confirmationEmailService = confirmationEmailService;
}
@Override
@ -26,6 +28,11 @@ public class UtilitiesServiceImpl implements UtilitiesService {
return visibilityRuleService;
}
@Override
public ConfirmationEmailService getConfirmationEmailService() {
return confirmationEmailService;
}
@Override
public InvitationService getInvitationService() {
return invitationService;

View File

@ -41,6 +41,10 @@ orcid.login.client_secret=f6ddc717-f49e-4bce-b302-2e479b226a24
orcid.login.access_token_url=https://orcid.org/oauth/token
orcid.login.redirect_uri=http://localhost:4200/login/external/orcid
#############CONFIRMATION EMAIL CONFIGURATIONS#########
conf_email.expiration_time_seconds=14400
conf_email.subject=OpenDMP email confirmation
#############ZENODO CONFIGURATIONS#########
zenodo.url=https://sandbox.zenodo.org/api/
zenodo.access_token=

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=".\images\OpenDMP.png" alt="OpenDMP" width="100" height="81">
<h2>Thank you for joining OpenDMP!</h2>
<p>Please confirm that your email address is correct to continue.
<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}/confirmation/{confirmationToken}" target="_blank">Confirm Email Address</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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -0,0 +1,17 @@
CREATE TABLE public."LoginConfirmationEmail"
(
"ID" uuid NOT NULL,
email character varying COLLATE pg_catalog."default" NOT NULL,
"isConfirmed" boolean NOT NULL,
token uuid NOT NULL,
userId uuid NOT NULL,
"expiresAt" timestamp(4) with time zone NOT NULL,
CONSTRAINT "LoginConfirmationEmail_pkey" PRIMARY KEY ("ID")
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE public."LoginConfirmationEmail"
OWNER to dmtadm;

View File

@ -0,0 +1,3 @@
ALTER TABLE public."UserInfo"
ALTER COLUMN "email"
DROP NOT NULL

View File

@ -33,6 +33,7 @@ import { CollectionUtils } from './services/utilities/collection-utils.service';
import { TypeUtils } from './services/utilities/type-utils.service';
import { QuickWizardService } from './services/quick-wizard/quick-wizard.service';
import { OrganisationService } from './services/organisation/organisation.service';
import { EmailConfirmationService } from './services/email-confirmation/email-confirmation.service';
//
//
// This is shared module that provides all the services. Its imported only once on the AppModule.
@ -85,7 +86,8 @@ export class CoreServiceModule {
DmpInvitationService,
DatasetExternalAutocompleteService,
QuickWizardService,
OrganisationService
OrganisationService,
EmailConfirmationService
],
};
}

View File

@ -0,0 +1,22 @@
import { Injectable } from "@angular/core";
import { HttpHeaders } from "@angular/common/http/src/headers";
import { BaseHttpService } from "../http/base-http.service";
import { environment } from "../../../../environments/environment";
@Injectable()
export class EmailConfirmationService {
private actioUrl: string;
private headers: HttpHeaders
constructor(private http: BaseHttpService) {
this.actioUrl = environment.Server + 'emailConfirmation/';
}
public emailConfirmation(token: string) {
return this.http.get<String>(this.actioUrl + token, { headers: this.headers });
}
public sendConfirmationEmail(email: string) {
return this.http.post<String>(this.actioUrl, email, {headers: this.headers});
}
}

View File

@ -0,0 +1,22 @@
<div class="main-content" *ngIf="showForm">
<div class="container-fluid">
<div class="card">
<div class="card-header card-header-plain d-flex">
<h4 class="card-title">{{ 'EMAIL-CONFIRMATION.CARD-TITLE' | translate }}</h4>
</div>
<div class="card-body table-responsive">
<form class="form" *ngIf="!mailSent">
<h5>{{ 'EMAIL-CONFIRMATION.REQUEST-EMAIL-HEADER' | translate }}</h5>
<p>{{ 'EMAIL-CONFIRMATION.REQUEST-EMAIL-TEXT' | translate }}</p>
<mat-form-field class="full-width">
<input matInput placeholder="Your Email" [formControl]="emailFormControl">
</mat-form-field>
<button mat-raised-button color="primary" (click)="sendConfirmationEmail()" class="lightblue-btn">
{{ 'EMAIL-CONFIRMATION.SUBMIT' | translate }}
</button>
</form>
<h5 *ngIf="mailSent">{{ 'EMAIL-CONFIRMATION.SENT-EMAIL-HEADER' | translate }}</h5>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,5 @@
.form {
min-width: 150px;
max-width: 500px;
width: 100%;
}

View File

@ -0,0 +1,68 @@
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Params, Router } from "@angular/router";
import { takeUntil } from "rxjs/operators";
import { BaseComponent } from "../../../../core/common/base/base.component";
import { EmailConfirmationService } from "../../../../core/services/email-confirmation/email-confirmation.service";
import { UiNotificationService, SnackBarNotificationLevel } from "../../../../core/services/notification/ui-notification-service";
import { TranslateService } from "@ngx-translate/core";
import { FormControl } from "@angular/forms";
@Component({
selector: 'app-email-confirmation-component',
templateUrl: './email-confirmation.component.html'
})
export class EmailConfirmation extends BaseComponent implements OnInit {
private emailFormControl = new FormControl('');
private showForm: boolean = false;
private mailSent: boolean = false;
constructor(
private emailConfirmationService: EmailConfirmationService,
private route: ActivatedRoute,
private router: Router,
private language: TranslateService,
private uiNotificationService: UiNotificationService
) { super(); }
ngOnInit() {
this.route.params
.pipe(takeUntil(this._destroyed))
.subscribe(params => {
const token = params['token']
if (token != null) {
this.showForm = false;
this.emailConfirmationService.emailConfirmation(token)
.pipe(takeUntil(this._destroyed))
.subscribe(
result => this.onCallbackEmailConfirmationSuccess(),
error => this.onCallbackError(error)
)
} else {
this.showForm = true;
}
});
}
sendConfirmationEmail() {
this.emailConfirmationService.sendConfirmationEmail(this.emailFormControl.value)
.pipe(takeUntil(this._destroyed))
.subscribe(
result => this.onCallbackSuccess(),
error => this.onCallbackError(error)
)
}
onCallbackSuccess() {
this.mailSent = true;
}
onCallbackEmailConfirmationSuccess() {
this.router.navigate(['home']);
}
onCallbackError(error: any) {
this.uiNotificationService.snackBarNotification(this.language.instant('EMAIL-CONFIRMATION.EXPIRED-EMAIL'), SnackBarNotificationLevel.Error);
this.router.navigate(['login']);
}
}

View File

@ -52,6 +52,14 @@
"LOG-IN": "Log in"
}
},
"EMAIL-CONFIRMATION": {
"EXPIRED-EMAIL": "Mail invitation expired",
"CARD-TITLE": "E-mail",
"REQUEST-EMAIL-HEADER": "We are almost done! Please fill your e-mail.",
"REQUEST-EMAIL-TEXT": "You will need to confirm it to use the application.",
"SUBMIT": "Submit",
"SENT-EMAIL-HEADER": "Email was send!"
},
"HOME": {
"DMPS": "DMPs",
"DATASETS": "Datasets",