Adds email confirmation logic and view.
This commit is contained in:
parent
b691084d73
commit
f8310b2362
|
@ -0,0 +1,6 @@
|
|||
package eu.eudat.data.dao.criteria;
|
||||
|
||||
import eu.eudat.data.entities.LoginConfirmationEmail;
|
||||
|
||||
public class LoginConfirmationEmailCriteria extends Criteria<LoginConfirmationEmail>{
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package eu.eudat.exceptions.emailconfirmation;
|
||||
|
||||
public class HasConfirmedEmailException extends Exception {
|
||||
|
||||
public HasConfirmedEmailException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package eu.eudat.exceptions.emailconfirmation;
|
||||
|
||||
public class TokenExpiredException extends Exception {
|
||||
|
||||
public TokenExpiredException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -46,5 +46,7 @@ public interface DatabaseRepository {
|
|||
|
||||
DatasetServiceDao getDatasetServiceDao();
|
||||
|
||||
LoginConfirmationEmailDao getLoginConfirmationEmailDao();
|
||||
|
||||
<T> void detachEntity(T entity);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -12,4 +12,6 @@ public interface UtilitiesService {
|
|||
MailService getMailService();
|
||||
|
||||
VisibilityRuleService getVisibilityRuleService();
|
||||
|
||||
ConfirmationEmailService getConfirmationEmailService();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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=
|
||||
|
|
|
@ -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> </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> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
|
@ -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;
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE public."UserInfo"
|
||||
ALTER COLUMN "email"
|
||||
DROP NOT NULL
|
|
@ -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
|
||||
],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -0,0 +1,5 @@
|
|||
.form {
|
||||
min-width: 150px;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
}
|
|
@ -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']);
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue