diff --git a/dmp-backend/Dockerfile.CI b/dmp-backend/Dockerfile.CI index 944713adf..5b8884b7d 100644 --- a/dmp-backend/Dockerfile.CI +++ b/dmp-backend/Dockerfile.CI @@ -12,4 +12,4 @@ RUN mvn package FROM openjdk:8-jre-alpine WORKDIR /app COPY --from=MAVEN_BUILD /build/web/target/web-1.0-SNAPSHOT.jar /app/app.jar -ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom" ,"-Dspring.profiles.active=${PROF}","-jar","/app.jar"] \ No newline at end of file +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Dspring.profiles.active=${PROF}", "-Dspring.config.location=/files/config/", "-jar","/app/app.jar"] diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DataManagementPlanCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DataManagementPlanCriteria.java index 4d7b82895..6b8de9a48 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DataManagementPlanCriteria.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DataManagementPlanCriteria.java @@ -21,6 +21,7 @@ public class DataManagementPlanCriteria extends Criteria { private boolean isPublic; private boolean onlyPublic; private Short grantStatus; + private boolean hasDoi; public Date getPeriodStart() { return periodStart; @@ -114,4 +115,12 @@ public class DataManagementPlanCriteria extends Criteria { public void setGrantStatus(Short grantStatus) { this.grantStatus = grantStatus; } + + public boolean hasDoi() { + return hasDoi; + } + + public void setHasDoi(boolean hasDoi) { + this.hasDoi = hasDoi; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DatasetCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DatasetCriteria.java index acbfe8422..d730d0127 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DatasetCriteria.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DatasetCriteria.java @@ -25,6 +25,7 @@ public class DatasetCriteria extends Criteria { private List groupIds; private Boolean isPublic; private Short grantStatus; + private boolean hasDoi; public boolean getAllVersions() { return allVersions; @@ -132,4 +133,12 @@ public class DatasetCriteria extends Criteria { public void setGrantStatus(Short grantStatus) { this.grantStatus = grantStatus; } + + public boolean hasDoi() { + return hasDoi; + } + + public void setHasDoi(boolean hasDoi) { + this.hasDoi = hasDoi; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DatasetProfileCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DatasetProfileCriteria.java index cc556701d..8aa20acc2 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DatasetProfileCriteria.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/DatasetProfileCriteria.java @@ -34,6 +34,7 @@ public class DatasetProfileCriteria extends Criteria { private UUID userId; private boolean finalized; private Integer status; + private Integer role; public boolean getAllVersions() { return allVersions; } public void setAllVersions(boolean allVersions) { this.allVersions = allVersions; } @@ -69,4 +70,12 @@ public class DatasetProfileCriteria extends Criteria { public void setStatus(Integer status) { this.status = status; } + + public Integer getRole() { + return role; + } + + public void setRole(Integer role) { + this.role = role; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/EmailConfirmationCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/EmailConfirmationCriteria.java new file mode 100644 index 000000000..1436752b9 --- /dev/null +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/EmailConfirmationCriteria.java @@ -0,0 +1,6 @@ +package eu.eudat.data.dao.criteria; + +import eu.eudat.data.entities.EmailConfirmation; + +public class EmailConfirmationCriteria extends Criteria{ +} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/FunderCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/FunderCriteria.java index c8c23038c..40f12491c 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/FunderCriteria.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/FunderCriteria.java @@ -4,6 +4,7 @@ import eu.eudat.data.entities.Funder; public class FunderCriteria extends Criteria { private String reference; + private String exactReference; public String getReference() { return reference; @@ -11,4 +12,12 @@ public class FunderCriteria extends Criteria { public void setReference(String reference) { this.reference = reference; } + + public String getExactReference() { + return exactReference; + } + + public void setExactReference(String exactReference) { + this.exactReference = exactReference; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/GrantCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/GrantCriteria.java index a216b2264..1cfd88be7 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/GrantCriteria.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/GrantCriteria.java @@ -13,6 +13,8 @@ public class GrantCriteria extends Criteria { private boolean isPublic; private String funderId; private String funderReference; + private String exactReference; + private boolean isActive; public Date getPeriodStart() { return periodStart; @@ -62,4 +64,20 @@ public class GrantCriteria extends Criteria { public void setFunderReference(String funderReference) { this.funderReference = funderReference; } + + public String getExactReference() { + return exactReference; + } + + public void setExactReference(String exactReference) { + this.exactReference = exactReference; + } + + public boolean isActive() { + return isActive; + } + + public void setActive(boolean active) { + isActive = active; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/LoginConfirmationEmailCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/LoginConfirmationEmailCriteria.java deleted file mode 100644 index 4ec5be026..000000000 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/LoginConfirmationEmailCriteria.java +++ /dev/null @@ -1,6 +0,0 @@ -package eu.eudat.data.dao.criteria; - -import eu.eudat.data.entities.LoginConfirmationEmail; - -public class LoginConfirmationEmailCriteria extends Criteria{ -} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/ProjectCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/ProjectCriteria.java index 1450c8661..1ac0bbae5 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/ProjectCriteria.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/ProjectCriteria.java @@ -4,6 +4,7 @@ import eu.eudat.data.entities.Project; public class ProjectCriteria extends Criteria { private String reference; + private String exactReference; public String getReference() { return reference; @@ -11,4 +12,12 @@ public class ProjectCriteria extends Criteria { public void setReference(String reference) { this.reference = reference; } + + public String getExactReference() { + return exactReference; + } + + public void setExactReference(String exactReference) { + this.exactReference = exactReference; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/ResearcherCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/ResearcherCriteria.java index 83e822bae..c2184547a 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/ResearcherCriteria.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/ResearcherCriteria.java @@ -4,6 +4,7 @@ import eu.eudat.data.entities.Researcher; public class ResearcherCriteria extends Criteria { private String name; + private String reference; public String getName() { return name; @@ -12,4 +13,12 @@ public class ResearcherCriteria extends Criteria { public void setName(String name) { this.name = name; } + + public String getReference() { + return reference; + } + + public void setReference(String reference) { + this.reference = reference; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DMPDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DMPDaoImpl.java index 0074428eb..378112713 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DMPDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DMPDaoImpl.java @@ -57,7 +57,9 @@ public class DMPDaoImpl extends DatabaseAccess implements DMPDao { query.where((builder, root) -> builder.equal(root.get("status"), DMP.DMPStatus.ACTIVE.getValue())); } } - query.where(((builder, root) -> builder.equal(root.get("isPublic"), criteria.getIsPublic()))); + if (criteria.getIsPublic()) { + query.where(((builder, root) -> builder.equal(root.get("isPublic"), criteria.getIsPublic()))); + } /*if (criteria.getRole() != null) { if (criteria.getRole().equals(UserDMP.UserDMPRoles.OWNER.getValue())) { query.where((builder, root) -> builder.equal(root.join("users", JoinType.LEFT).get("role"), UserDMP.UserDMPRoles.OWNER.getValue())); @@ -82,6 +84,10 @@ public class DMPDaoImpl extends DatabaseAccess implements DMPDao { builder.or(builder.greaterThan(root.get("grant").get("enddate"), new Date()) , builder.isNull(root.get("grant").get("enddate")))); } + + if (criteria.hasDoi()) { + query.where((builder, root) -> builder.not(builder.isNull(root.get("doi")))); + } query.where((builder, root) -> builder.notEqual(root.get("status"), DMP.DMPStatus.DELETED.getValue())); return query; } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetDaoImpl.java index f6b8d5b7f..3c5b3d3bc 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetDaoImpl.java @@ -78,6 +78,10 @@ public class DatasetDaoImpl extends DatabaseAccess implements DatasetDa query.where((builder, root) -> root.join("dmp", JoinType.LEFT).join("users", JoinType.LEFT).join("user", JoinType.LEFT).get("id").in(criteria.getCollaborators())); if (criteria.getDatasetTemplates() != null && !criteria.getDatasetTemplates().isEmpty()) query.where((builder, root) -> root.get("profile").get("id").in(criteria.getDatasetTemplates())); + + if (criteria.hasDoi()) { + query.where((builder, root) -> builder.not(builder.isNull(root.get("dmp").get("doi")))); + } query.where((builder, root) -> builder.notEqual(root.get("status"), Dataset.Status.DELETED.getValue())); query.where((builder, root) -> builder.notEqual(root.get("status"), Dataset.Status.CANCELED.getValue())); return query; diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetProfileDao.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetProfileDao.java index b8e480917..dad3243b4 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetProfileDao.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetProfileDao.java @@ -5,6 +5,7 @@ import eu.eudat.data.dao.criteria.DatasetProfileCriteria; import eu.eudat.data.entities.DatasetProfile; import eu.eudat.queryable.QueryableList; +import java.util.List; import java.util.UUID; public interface DatasetProfileDao extends DatabaseAccessLayer { @@ -13,4 +14,6 @@ public interface DatasetProfileDao extends DatabaseAccessLayer getAll(); + QueryableList getAuthenticated(QueryableList query, UUID principal, List roles); + } \ No newline at end of file diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetProfileDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetProfileDaoImpl.java index 36fcc5433..c704f4fba 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetProfileDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetProfileDaoImpl.java @@ -3,6 +3,7 @@ package eu.eudat.data.dao.entities; import eu.eudat.data.dao.DatabaseAccess; import eu.eudat.data.dao.criteria.DatasetProfileCriteria; import eu.eudat.data.dao.databaselayer.service.DatabaseService; +import eu.eudat.data.entities.DMP; import eu.eudat.data.entities.DatasetProfile; import eu.eudat.queryable.QueryableList; import eu.eudat.queryable.types.FieldSelectionType; @@ -11,8 +12,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; +import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import java.util.Arrays; +import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -98,4 +101,18 @@ public class DatasetProfileDaoImpl extends DatabaseAccess implem public DatasetProfile find(UUID id, String hint) { throw new UnsupportedOperationException(); } + + @Override + public QueryableList getAuthenticated(QueryableList query, UUID principal, List roles) { + if (roles != null && !roles.isEmpty()) { + query.where((builder, root) -> { + Join userJoin = root.join("users", JoinType.LEFT); + return builder.and(builder.equal(userJoin.join("user", JoinType.LEFT).get("id"), principal), userJoin.get("role").in(roles)); + }); + } else { + query.where((builder, root) -> builder.equal(root.join("users", JoinType.LEFT).join("user", JoinType.LEFT).get("id"), principal)); + } + + return query; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/EmailConfirmationDao.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/EmailConfirmationDao.java new file mode 100644 index 000000000..b379d964d --- /dev/null +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/EmailConfirmationDao.java @@ -0,0 +1,13 @@ +package eu.eudat.data.dao.entities; + +import eu.eudat.data.dao.DatabaseAccessLayer; +import eu.eudat.data.dao.criteria.EmailConfirmationCriteria; +import eu.eudat.data.entities.EmailConfirmation; +import eu.eudat.queryable.QueryableList; + +import java.util.UUID; + +public interface EmailConfirmationDao extends DatabaseAccessLayer { + + QueryableList getWithCriteria(EmailConfirmationCriteria criteria); +} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/EmailConfirmationDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/EmailConfirmationDaoImpl.java new file mode 100644 index 000000000..d53fc9774 --- /dev/null +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/EmailConfirmationDaoImpl.java @@ -0,0 +1,56 @@ +package eu.eudat.data.dao.entities; + +import eu.eudat.data.dao.DatabaseAccess; +import eu.eudat.data.dao.criteria.EmailConfirmationCriteria; +import eu.eudat.data.dao.databaselayer.service.DatabaseService; +import eu.eudat.data.entities.EmailConfirmation; +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 EmailConfirmationDaoImpl extends DatabaseAccess implements EmailConfirmationDao { + + @Autowired + public EmailConfirmationDaoImpl(DatabaseService databaseService) { + super(databaseService); + } + + @Override + public QueryableList getWithCriteria(EmailConfirmationCriteria criteria) { + return null; + } + + @Override + public EmailConfirmation createOrUpdate(EmailConfirmation item) { + return this.getDatabaseService().createOrUpdate(item, EmailConfirmation.class); + } + + @Override + public CompletableFuture createOrUpdateAsync(EmailConfirmation item) { + return null; + } + + @Override + public EmailConfirmation find(UUID id) { + return this.getDatabaseService().getQueryable(EmailConfirmation.class).where((builder, root) -> builder.equal(root.get("id"), id)).getSingle(); + } + + @Override + public EmailConfirmation find(UUID id, String hint) { + throw new UnsupportedOperationException(); + } + + @Override + public void delete(EmailConfirmation item) { + throw new UnsupportedOperationException(); + } + + @Override + public QueryableList asQueryable() { + return this.getDatabaseService().getQueryable(EmailConfirmation.class); + } +} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FunderDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FunderDaoImpl.java index 40262d743..39d54385c 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FunderDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/FunderDaoImpl.java @@ -29,6 +29,8 @@ public class FunderDaoImpl extends DatabaseAccess implements FunderDao { builder.or(builder.like(builder.upper(root.get("definition")), "%" + criteria.getLike().toUpperCase() + "%")))); if (criteria.getReference() != null) query.where((builder, root) -> builder.like(builder.upper(root.get("reference")), "%" + criteria.getReference().toUpperCase() + "%")); + if (criteria.getExactReference() != null) + query.where((builder, root) -> builder.like(builder.upper(root.get("reference")), criteria.getExactReference().toUpperCase())); query.where((builder, root) -> builder.notEqual(root.get("status"), Funder.Status.DELETED.getValue())); return query; } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/GrantDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/GrantDaoImpl.java index 04e14d3b8..336fef1ab 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/GrantDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/GrantDaoImpl.java @@ -39,6 +39,8 @@ public class GrantDaoImpl extends DatabaseAccess implements GrantDao { query.where((builder, root) -> builder.greaterThan(root.get("startdate"), criteria.getPeriodStart())); if (criteria.getReference() != null) query.where((builder, root) -> builder.like(root.get("reference"), "%" + criteria.getReference() + "%")); + if (criteria.getExactReference() != null) + query.where((builder, root) -> builder.like(root.get("reference"), criteria.getExactReference())); if (criteria.getGrantStateType() != null) { if (criteria.getGrantStateType().equals(GrantStateType.FINISHED.getValue())) query.where((builder, root) -> builder.lessThan(root.get("enddate"), new Date())); @@ -50,6 +52,10 @@ public class GrantDaoImpl extends DatabaseAccess implements GrantDao { if (criteria.isPublic()) { query.where((builder, root) -> builder.equal(root.join("dmps").get("status"), DMP.DMPStatus.FINALISED.getValue())).distinct(); } + + if (criteria.isActive()) { + query.where((builder, root) -> builder.notEqual(root.join("dmps").get("status"), DMP.DMPStatus.DELETED.getValue())).distinct(); + } if (criteria.getFunderId() != null && !criteria.getFunderId().trim().isEmpty()) query.where((builder, root) -> builder.equal(root.get("funder").get("id"), UUID.fromString(criteria.getFunderId()))); if (criteria.getFunderReference() != null && !criteria.getFunderReference().isEmpty()) diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/LoginConfirmationEmailDao.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/LoginConfirmationEmailDao.java deleted file mode 100644 index 71edce65e..000000000 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/LoginConfirmationEmailDao.java +++ /dev/null @@ -1,13 +0,0 @@ -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 { - - QueryableList getWithCriteria(LoginConfirmationEmailCriteria criteria); -} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/LoginConfirmationEmailDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/LoginConfirmationEmailDaoImpl.java deleted file mode 100644 index 691c6b663..000000000 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/LoginConfirmationEmailDaoImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -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 implements LoginConfirmationEmailDao { - - @Autowired - public LoginConfirmationEmailDaoImpl(DatabaseService databaseService) { - super(databaseService); - } - - @Override - public QueryableList getWithCriteria(LoginConfirmationEmailCriteria criteria) { - return null; - } - - @Override - public LoginConfirmationEmail createOrUpdate(LoginConfirmationEmail item) { - return this.getDatabaseService().createOrUpdate(item, LoginConfirmationEmail.class); - } - - @Override - public CompletableFuture 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 asQueryable() { - return this.getDatabaseService().getQueryable(LoginConfirmationEmail.class); - } -} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/OrganisationDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/OrganisationDaoImpl.java index 4240840f4..6d2e63ee2 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/OrganisationDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/OrganisationDaoImpl.java @@ -26,13 +26,17 @@ public class OrganisationDaoImpl extends DatabaseAccess implements @Override public QueryableList getWithCriteria(OrganisationCriteria criteria) { QueryableList query = this.getDatabaseService().getQueryable(Organisation.class); - if (criteria.getLike() != null) - query.where((builder, root) -> builder.equal(root.get("reference"), criteria.getLike())); - if (criteria.getLabelLike() != null) { - query.where((builder, root) -> builder.like(builder.upper(root.get("label")), "%" + criteria.getLabelLike().toUpperCase() + "%")); - } - if (criteria.getPublic() != null && criteria.getPublic()) { - query.where((builder, root) -> builder.equal(root.join("dmps", JoinType.LEFT).get("status"), DMP.DMPStatus.FINALISED.getValue())); + if (criteria.getLabelLike() != null && criteria.getLike() != null) { + query.where((builder, root) -> builder.or(builder.equal(root.get("reference"), criteria.getLike()), builder.like(builder.upper(root.get("label")), "%" + criteria.getLabelLike().toUpperCase() + "%"))); + } else { + if (criteria.getLike() != null) + query.where((builder, root) -> builder.equal(root.get("reference"), criteria.getLike())); + if (criteria.getLabelLike() != null) { + query.where((builder, root) -> builder.like(builder.upper(root.get("label")), "%" + criteria.getLabelLike().toUpperCase() + "%")); + } + if (criteria.getPublic() != null && criteria.getPublic()) { + query.where((builder, root) -> builder.equal(root.join("dmps", JoinType.LEFT).get("status"), DMP.DMPStatus.FINALISED.getValue())); + } } return query; } @@ -64,7 +68,7 @@ public class OrganisationDaoImpl extends DatabaseAccess implements } public QueryableList getAuthenticated(QueryableList query, UserInfo principal) { - query.where((builder, root) -> builder.equal(root.join("dmps").get("creator"), principal)); + query.where((builder, root) -> builder.equal(root.join("dmps").join("users").get("user"), principal)); return query; } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/ProjectDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/ProjectDaoImpl.java index eac00713a..8b69f372a 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/ProjectDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/ProjectDaoImpl.java @@ -28,6 +28,8 @@ public class ProjectDaoImpl extends DatabaseAccess implements ProjectDa builder.or(builder.like(builder.upper(root.get("description")), "%" + criteria.getLike().toUpperCase() + "%")))); if (criteria.getReference() != null) query.where((builder, root) -> builder.like(root.get("reference"), "%" + criteria.getReference() + "%")); + if (criteria.getExactReference() != null) + query.where((builder, root) -> builder.like(root.get("reference"), criteria.getExactReference())); query.where((builder, root) -> builder.notEqual(root.get("status"), Project.Status.DELETED.getValue())); return query; } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/ResearcherDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/ResearcherDaoImpl.java index 086adf820..ad7297163 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/ResearcherDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/ResearcherDaoImpl.java @@ -27,6 +27,8 @@ public class ResearcherDaoImpl extends DatabaseAccess implements Res query.where((builder, root) ->builder.or(builder.like(builder.upper(root.get("reference")), "%" + criteria.getLike().toUpperCase() + "%"))); if (criteria.getName() != null && !criteria.getName().isEmpty()) query.where((builder, root) ->builder.or(builder.like(builder.upper(root.get("label")), "%" + criteria.getName().toUpperCase() + "%"))); + if (criteria.getReference() != null && !criteria.getReference().isEmpty()) + query.where((builder, root) ->builder.or(builder.like(builder.upper(root.get("reference")), criteria.getReference().toUpperCase()))); return query; } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserDatasetProfileDao.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserDatasetProfileDao.java new file mode 100644 index 000000000..dfe500ca4 --- /dev/null +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserDatasetProfileDao.java @@ -0,0 +1,13 @@ +package eu.eudat.data.dao.entities; + +import eu.eudat.data.dao.DatabaseAccessLayer; +import eu.eudat.data.entities.UserDMP; +import eu.eudat.data.entities.UserDatasetProfile; + +import java.util.UUID; + +/** + * Created by ikalyvas on 2/8/2018. + */ +public interface UserDatasetProfileDao extends DatabaseAccessLayer { +} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserDatasetProfileDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserDatasetProfileDaoImpl.java new file mode 100644 index 000000000..73633d824 --- /dev/null +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserDatasetProfileDaoImpl.java @@ -0,0 +1,53 @@ +package eu.eudat.data.dao.entities; + +import eu.eudat.data.dao.DatabaseAccess; +import eu.eudat.data.dao.databaselayer.service.DatabaseService; +import eu.eudat.data.entities.UserDMP; +import eu.eudat.data.entities.UserDatasetProfile; +import eu.eudat.queryable.QueryableList; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +@Component("userDatasetProfileDao") +public class UserDatasetProfileDaoImpl extends DatabaseAccess implements UserDatasetProfileDao { + + @Autowired + public UserDatasetProfileDaoImpl(DatabaseService databaseService) { + super(databaseService); + } + + @Override + public UserDatasetProfile createOrUpdate(UserDatasetProfile item) { + return this.getDatabaseService().createOrUpdate(item, UserDatasetProfile.class); + } + + @Override + public UserDatasetProfile find(UUID id) { + return this.getDatabaseService().getQueryable(UserDatasetProfile.class).where((builder, root) -> builder.equal(root.get("id"), id)).getSingleOrDefault(); + } + + @Override + public void delete(UserDatasetProfile item) { + this.getDatabaseService().delete(item); + } + + @Override + public QueryableList asQueryable() { + return this.getDatabaseService().getQueryable(UserDatasetProfile.class); + } + + @Async + @Override + public CompletableFuture createOrUpdateAsync(UserDatasetProfile item) { + return CompletableFuture.supplyAsync(() -> this.createOrUpdate(item)); + } + + @Override + public UserDatasetProfile find(UUID id, String hint) { + throw new UnsupportedOperationException(); + } +} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserInfoDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserInfoDaoImpl.java index 60526bd6a..c3f955d93 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserInfoDaoImpl.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/UserInfoDaoImpl.java @@ -26,6 +26,7 @@ public class UserInfoDaoImpl extends DatabaseAccess implements UserInf @Override public QueryableList getWithCriteria(UserInfoCriteria criteria) { QueryableList users = this.getDatabaseService().getQueryable(UserInfo.class); + users.where(((builder, root) -> builder.equal(root.get("userStatus"), 0))); if (criteria.getAppRoles() != null && !criteria.getAppRoles().isEmpty()) users.where((builder, root) -> root.join("userRoles").get("role").in(criteria.getAppRoles())); if (criteria.getLike() != null) diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/entities/Credential.java b/dmp-backend/data/src/main/java/eu/eudat/data/entities/Credential.java index 7181abfb9..88e3dc36d 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/entities/Credential.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/entities/Credential.java @@ -34,6 +34,8 @@ public class Credential implements DataEntity { private Integer provider; @Column(name = "\"Public\"", nullable = false) private String publicValue; + @Column(name = "\"Email\"", nullable = false) + private String email; @Column(name = "\"Secret\"", nullable = false) private String secret; @@ -88,6 +90,14 @@ public class Credential implements DataEntity { this.publicValue = publicValue; } + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + public String getSecret() { return secret; } @@ -139,6 +149,7 @@ public class Credential implements DataEntity { public void update(Credential entity) { this.status = entity.status; this.publicValue = entity.getPublicValue(); + this.email = entity.getEmail(); this.secret = entity.getSecret(); this.lastUpdateTime = new Date(); } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/entities/DatasetProfile.java b/dmp-backend/data/src/main/java/eu/eudat/data/entities/DatasetProfile.java index 5000b461d..7b7aa11c4 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/entities/DatasetProfile.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/entities/DatasetProfile.java @@ -89,6 +89,9 @@ public class DatasetProfile implements DataEntity{ @Column(name = "\"Language\"", nullable = false) private String language; + @OneToMany(mappedBy = "datasetProfile", fetch = FetchType.LAZY) + private Set users; + public String getDescription() { return description; @@ -158,6 +161,14 @@ public class DatasetProfile implements DataEntity{ this.language = language; } + public Set getUsers() { + return users; + } + + public void setUsers(Set users) { + this.users = users; + } + @Override public String toString() { return "DatasetProfileListingModel [id=" + id + ", label=" + label + ", dataset=" + dataset + ", definition=" + definition + ", version=" + version + ", language=" + language + "]"; diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/entities/LoginConfirmationEmail.java b/dmp-backend/data/src/main/java/eu/eudat/data/entities/EmailConfirmation.java similarity index 79% rename from dmp-backend/data/src/main/java/eu/eudat/data/entities/LoginConfirmationEmail.java rename to dmp-backend/data/src/main/java/eu/eudat/data/entities/EmailConfirmation.java index 2bc9f53c8..8d4363e9e 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/entities/LoginConfirmationEmail.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/entities/EmailConfirmation.java @@ -10,8 +10,8 @@ import java.util.List; import java.util.UUID; @Entity -@Table(name = "\"LoginConfirmationEmail\"") -public class LoginConfirmationEmail implements DataEntity { +@Table(name = "\"EmailConfirmation\"") +public class EmailConfirmation implements DataEntity { @Id @GeneratedValue @@ -30,6 +30,9 @@ public class LoginConfirmationEmail implements DataEntity tuple, List fields, String base) { + public EmailConfirmation buildFromTuple(List tuple, List fields, String base) { return null; } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/entities/UserDatasetProfile.java b/dmp-backend/data/src/main/java/eu/eudat/data/entities/UserDatasetProfile.java new file mode 100644 index 000000000..8388cdbc3 --- /dev/null +++ b/dmp-backend/data/src/main/java/eu/eudat/data/entities/UserDatasetProfile.java @@ -0,0 +1,79 @@ +package eu.eudat.data.entities; + +import eu.eudat.data.entities.helpers.EntityBinder; +import eu.eudat.queryable.queryableentity.DataEntity; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.List; +import java.util.UUID; + +@Entity +@Table(name = "\"UserDatasetProfile\"") +public class UserDatasetProfile implements DataEntity { + @Id + @GeneratedValue + @GenericGenerator(name = "uuid2", strategy = "uuid2") + @Column(name = "id", updatable = false, nullable = false, columnDefinition = "BINARY(16)") + private UUID id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "usr") + private UserInfo user; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "\"datasetProfile\"") + private DatasetProfile datasetProfile; + + @Column(name = "role") + private Integer role; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + + public UserInfo getUser() { + return user; + } + + public void setUser(UserInfo user) { + this.user = user; + } + + public DatasetProfile getDatasetProfile() { + return datasetProfile; + } + + public void setDatasetProfile(DatasetProfile datasetProfile) { + this.datasetProfile = datasetProfile; + } + + public Integer getRole() { + return role; + } + + public void setRole(Integer role) { + this.role = role; + } + + @Override + public void update(UserDatasetProfile entity) { + this.role = entity.getRole(); + } + + @Override + public UUID getKeys() { + return this.id; + } + + @Override + public UserDatasetProfile buildFromTuple(List tuple, List fields, String base) { + String currentBase = base.isEmpty() ? "" : base + "."; + if (fields.contains(currentBase + "id")) this.id = EntityBinder.fromTuple(tuple, currentBase + "id"); + return this; + } +} diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/entities/UserInfo.java b/dmp-backend/data/src/main/java/eu/eudat/data/entities/UserInfo.java index ecd4263a5..ca8b5ab51 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/entities/UserInfo.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/entities/UserInfo.java @@ -15,7 +15,7 @@ import java.util.*; @NamedEntityGraphs({ @NamedEntityGraph( name = "userInfo", - attributeNodes = {@NamedAttributeNode("userRoles"), @NamedAttributeNode("credentials")}), + attributeNodes = {@NamedAttributeNode("userRoles"), @NamedAttributeNode("credentials"), @NamedAttributeNode("additionalinfo")}), }) public class UserInfo implements DataEntity { @@ -35,6 +35,9 @@ public class UserInfo implements DataEntity { @Column(name = "usertype", nullable = false) private Short usertype; // 0 internal, 1 external + @Column(name = "userstatus", nullable = false) + private Short userStatus; // 0 active, 1 inactive + @Column(name = "verified_email", nullable = true) private Boolean verified_email = null; @@ -187,6 +190,14 @@ public class UserInfo implements DataEntity { this.notifications = notifications; } + public Short getUserStatus() { + return userStatus; + } + + public void setUserStatus(Short userStatus) { + this.userStatus = userStatus; + } + @Override public void update(UserInfo entity) { this.name = entity.getName(); @@ -194,6 +205,7 @@ public class UserInfo implements DataEntity { this.additionalinfo = entity.getAdditionalinfo(); this.lastloggedin = entity.getLastloggedin(); this.userRoles = entity.getUserRoles(); + this.userStatus = entity.getUserStatus(); } @Override diff --git a/dmp-backend/data/src/main/java/eu/eudat/query/DatasetProfileQuery.java b/dmp-backend/data/src/main/java/eu/eudat/query/DatasetProfileQuery.java index 71a74b750..a4d477c0d 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/query/DatasetProfileQuery.java +++ b/dmp-backend/data/src/main/java/eu/eudat/query/DatasetProfileQuery.java @@ -4,4 +4,13 @@ import java.util.UUID; public class DatasetProfileQuery { + private UserQuery userQuery; + + public UserQuery getUserQuery() { + return userQuery; + } + + public void setUserQuery(UserQuery userQuery) { + this.userQuery = userQuery; + } } diff --git a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/DatasetRepository.java b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/DatasetRepository.java index ebf02c512..b8c26a489 100644 --- a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/DatasetRepository.java +++ b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/DatasetRepository.java @@ -26,6 +26,7 @@ import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import java.io.IOException; @@ -37,10 +38,12 @@ import java.util.stream.Stream; public class DatasetRepository extends ElasticRepository { private final DmpRepository dmpRepository; + private final Environment environment; - public DatasetRepository(RestHighLevelClient client, DmpRepository dmpRepository) { + public DatasetRepository(RestHighLevelClient client, DmpRepository dmpRepository, Environment environment) { super(client); this.dmpRepository = dmpRepository; + this.environment = environment; } @Override @@ -64,7 +67,7 @@ public class DatasetRepository extends ElasticRepository query(DatasetCriteria criteria) throws IOException { if (this.getClient() != null) { - SearchRequest searchRequest = new SearchRequest("dmps"); + SearchRequest searchRequest = new SearchRequest(this.environment.getProperty("elasticsearch.index")); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); /*CountRequest countRequest = new CountRequest("dmps").routing("datasets").routing("id"); @@ -103,7 +106,7 @@ public class DatasetRepository extends ElasticRepository { private static final Logger logger = LoggerFactory.getLogger(DmpRepository.class); + private final Environment environment; + @Autowired - public DmpRepository(RestHighLevelClient client) { + public DmpRepository(RestHighLevelClient client, Environment environment) { super(client); + this.environment = environment; } private void generateMapping() throws IOException { @@ -52,7 +56,7 @@ public class DmpRepository extends ElasticRepository { builder.endObject(); builder.endObject(); builder.endObject(); - PutMappingRequest putMappingRequest = new PutMappingRequest("dmps"); + PutMappingRequest putMappingRequest = new PutMappingRequest(this.environment.getProperty("elasticsearch.index")); putMappingRequest.source(builder); this.getClient().indices().putMapping(putMappingRequest, RequestOptions.DEFAULT); } @@ -62,7 +66,7 @@ public class DmpRepository extends ElasticRepository { public Dmp createOrUpdate(Dmp entity) throws IOException { if (this.getClient() != null) { XContentBuilder builder = XContentFactory.jsonBuilder(); - IndexRequest request = new IndexRequest("dmps").id(entity.getId().toString()).source(entity.toElasticEntity(builder)); + IndexRequest request = new IndexRequest(this.environment.getProperty("elasticsearch.index")).id(entity.getId().toString()).source(entity.toElasticEntity(builder)); IndexResponse response = this.getClient().index(request, RequestOptions.DEFAULT); return entity; } @@ -72,7 +76,7 @@ public class DmpRepository extends ElasticRepository { @Override public Dmp findDocument(String id) throws IOException { if (this.getClient() != null) { - GetRequest request = new GetRequest("dmps", id); + GetRequest request = new GetRequest(this.environment.getProperty("elasticsearch.index"), id); GetResponse response = this.getClient().get(request, RequestOptions.DEFAULT); return new Dmp().fromElasticEntity(response.getSourceAsMap()); } @@ -82,10 +86,10 @@ public class DmpRepository extends ElasticRepository { @Override public List query(DmpCriteria criteria) throws IOException { if (this.getClient() != null) { - SearchRequest searchRequest = new SearchRequest("dmps"); + SearchRequest searchRequest = new SearchRequest(this.environment.getProperty("elasticsearch.index")); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - CountRequest countRequest = new CountRequest("dmps"); + CountRequest countRequest = new CountRequest(this.environment.getProperty("elasticsearch.index")); countRequest.query(QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery(Dmp.MapKey.STATUS.getName(), Collections.singletonList(Dmp.DMPStatus.DELETED.getValue())))); CountResponse countResponse = getClient().count(countRequest, RequestOptions.DEFAULT); Long count = countResponse.getCount(); @@ -152,7 +156,7 @@ public class DmpRepository extends ElasticRepository { public boolean createIndex() { try { if (!this.exists()) { - CreateIndexRequest createIndexRequest = new CreateIndexRequest("dmps"); + CreateIndexRequest createIndexRequest = new CreateIndexRequest(this.environment.getProperty("elasticsearch.index")); this.getClient().indices().create(createIndexRequest, RequestOptions.DEFAULT); this.generateMapping(); } @@ -166,7 +170,7 @@ public class DmpRepository extends ElasticRepository { @Override public boolean exists() throws IOException { if (this.getClient() != null) { - GetIndexRequest request = new GetIndexRequest("dmps"); + GetIndexRequest request = new GetIndexRequest(this.environment.getProperty("elasticsearch.index")); return this.getClient().indices().exists(request, RequestOptions.DEFAULT); } return false; @@ -175,10 +179,10 @@ public class DmpRepository extends ElasticRepository { @Override public void clear() throws IOException { if (exists()) { - DeleteByQueryRequest delete = new DeleteByQueryRequest("dmps"); + DeleteByQueryRequest delete = new DeleteByQueryRequest(this.environment.getProperty("elasticsearch.index")); delete.setQuery(QueryBuilders.matchAllQuery()); this.getClient().deleteByQuery(delete, RequestOptions.DEFAULT); - DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("dmps"); + DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(this.environment.getProperty("elasticsearch.index")); this.getClient().indices().delete(deleteIndexRequest, RequestOptions.DEFAULT); } } diff --git a/dmp-backend/pom.xml b/dmp-backend/pom.xml index 086bf9355..335884af1 100644 --- a/dmp-backend/pom.xml +++ b/dmp-backend/pom.xml @@ -225,6 +225,38 @@ 1.3.1 + + + io.prometheus + simpleclient + 0.10.0 + + + + io.prometheus + simpleclient_hotspot + 0.10.0 + + + + io.prometheus + simpleclient_httpserver + 0.10.0 + + + + io.prometheus + simpleclient_pushgateway + 0.10.0 + + + + + io.prometheus + simpleclient_spring_boot + 0.10.0 + + diff --git a/dmp-backend/web/src/main/java/eu/eudat/configurations/WebMVCConfiguration.java b/dmp-backend/web/src/main/java/eu/eudat/configurations/WebMVCConfiguration.java index 36d9c502c..2c64aa8d1 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/configurations/WebMVCConfiguration.java +++ b/dmp-backend/web/src/main/java/eu/eudat/configurations/WebMVCConfiguration.java @@ -4,6 +4,7 @@ import eu.eudat.controllers.interceptors.RequestInterceptor; import eu.eudat.logic.handlers.PrincipalArgumentResolver; import eu.eudat.logic.services.ApiContext; import eu.eudat.logic.services.operations.authentication.AuthenticationService; +import io.prometheus.client.spring.boot.EnablePrometheusEndpoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; @@ -17,6 +18,7 @@ import java.util.List; @EnableAsync @Configuration @EnableScheduling +@EnablePrometheusEndpoint public class WebMVCConfiguration extends WebMvcConfigurerAdapter { private ApiContext apiContext; diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java index b3ae23250..6a89b4429 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Admin.java @@ -1,10 +1,13 @@ package eu.eudat.controllers; +import eu.eudat.data.entities.UserDatasetProfile; +import eu.eudat.data.entities.UserInfo; import eu.eudat.data.query.items.table.datasetprofile.DatasetProfileTableRequestItem; import eu.eudat.exceptions.datasetprofile.DatasetProfileNewVersionException; import eu.eudat.exceptions.datasetprofile.DatasetProfileWithDatasetsExeption; import eu.eudat.logic.managers.AdminManager; import eu.eudat.logic.managers.DatasetProfileManager; +import eu.eudat.logic.managers.MetricsManager; import eu.eudat.logic.managers.UserManager; import eu.eudat.logic.proxy.config.configloaders.ConfigLoader; import eu.eudat.logic.security.claims.ClaimedAuthorities; @@ -13,7 +16,7 @@ import eu.eudat.models.data.admin.composite.DatasetProfile; import eu.eudat.models.data.datasetprofile.DatasetProfileListingModel; import eu.eudat.models.data.helpers.common.DataTableData; import eu.eudat.models.data.helpers.responses.ResponseItem; -import org.springframework.core.env.Environment; +import eu.eudat.models.data.listingmodels.UserInfoListingModel; import eu.eudat.models.data.security.Principal; import eu.eudat.models.data.user.composite.PagedDatasetProfile; import eu.eudat.types.ApiMessageCode; @@ -28,8 +31,10 @@ import javax.validation.Valid; import java.io.IOException; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; import static eu.eudat.types.Authorities.ADMIN; +import static eu.eudat.types.Authorities.DATASET_PROFILE_MANAGER; @RestController @CrossOrigin @@ -50,34 +55,48 @@ public class Admin extends BaseController { @Transactional @RequestMapping(method = RequestMethod.POST, value = {"/addDmp"}, consumes = "application/json", produces = "application/json") - public ResponseEntity addDmp(@Valid @RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) { + public ResponseEntity addDmp(@Valid @RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN ,DATASET_PROFILE_MANAGER}) Principal principal) { //this.getLoggerService().info(principal, "Admin Added Dataset Profile"); DatasetProfile shortenProfile = profile.toShort(); eu.eudat.data.entities.DatasetProfile modelDefinition = AdminManager.generateViewStyleDefinition(shortenProfile, getApiContext()); modelDefinition.setGroupId(UUID.randomUUID()); modelDefinition.setVersion((short) 0); - this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().createOrUpdate(modelDefinition); + + eu.eudat.data.entities.DatasetProfile datasetProfile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().createOrUpdate(modelDefinition); + UserDatasetProfile userDatasetProfile = new UserDatasetProfile(); + userDatasetProfile.setDatasetProfile(datasetProfile); + UserInfo userInfo = getApiContext().getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId()); + userDatasetProfile.setUser(userInfo); + userDatasetProfile.setRole(0); + getApiContext().getOperationsContext().getDatabaseRepository().getUserDatasetProfileDao().createOrUpdate(userDatasetProfile); + datasetProfileManager.storeDatasetProfileUsers(datasetProfile, profile); + MetricsManager.increaseValue("argos_dataset_templates", 1, "total"); return ResponseEntity.status(HttpStatus.OK).body(modelDefinition.getId()); } @Transactional @RequestMapping(method = RequestMethod.POST, value = {"/addDmp/{id}"}, consumes = "application/json", produces = "application/json") - public ResponseEntity> updateDmp(@PathVariable String id, @RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) { + public ResponseEntity> updateDmp(@PathVariable String id, @RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) { DatasetProfile shortenProfile = profile.toShort(); eu.eudat.data.entities.DatasetProfile modelDefinition = AdminManager.generateViewStyleDefinition(shortenProfile, getApiContext()); eu.eudat.data.entities.DatasetProfile datasetprofile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(UUID.fromString(id)); datasetprofile.setDefinition(modelDefinition.getDefinition()); + Short oldStatus = datasetprofile.getStatus(); datasetprofile.setStatus(modelDefinition.getStatus()); datasetprofile.setLabel(modelDefinition.getLabel()); datasetprofile.setDescription(modelDefinition.getDescription()); datasetprofile.setLanguage(modelDefinition.getLanguage()); - this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().createOrUpdate(datasetprofile); + eu.eudat.data.entities.DatasetProfile datasetProfile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().createOrUpdate(datasetprofile); + datasetProfileManager.storeDatasetProfileUsers(datasetProfile, profile); + if (datasetProfile.getStatus() == 1 && oldStatus == 0) { + MetricsManager.increaseValue("argos_dataset_templates", 1, "active"); + } return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE)); } @Transactional @RequestMapping(method = RequestMethod.POST, value = {"/newVersion/{id}"}, produces = "application/json") - public ResponseEntity newVersionDatasetProfile(@PathVariable String id, @RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) throws Exception { + public ResponseEntity newVersionDatasetProfile(@PathVariable String id, @RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) throws Exception { try { eu.eudat.data.entities.DatasetProfile modelDefinition = this.datasetProfileManager.createNewVersionDatasetProfile(id, profile); return ResponseEntity.status(HttpStatus.OK).body(modelDefinition.getId()); @@ -87,20 +106,21 @@ public class Admin extends BaseController { } @RequestMapping(method = RequestMethod.GET, value = {"/get/{id}"}, produces = "application/json") - public ResponseEntity> get(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) { + @Transactional + public ResponseEntity> get(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) { eu.eudat.models.data.admin.composite.DatasetProfile datasetprofile = this.datasetProfileManager.getDatasetProfile(id); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE).payload(datasetprofile)); } @RequestMapping(method = RequestMethod.POST, value = {"/datasetprofiles/getPaged"}, produces = "application/json") public @ResponseBody - ResponseEntity>> getPaged(@RequestBody DatasetProfileTableRequestItem datasetProfileTableRequestItem, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) throws Exception { - DataTableData datasetProfileTableData = this.datasetProfileManager.getPaged(datasetProfileTableRequestItem); + ResponseEntity>> getPaged(@RequestBody DatasetProfileTableRequestItem datasetProfileTableRequestItem, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) throws Exception { + DataTableData datasetProfileTableData = this.datasetProfileManager.getPaged(datasetProfileTableRequestItem, principal); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().status(ApiMessageCode.NO_MESSAGE).payload(datasetProfileTableData)); } @RequestMapping(method = RequestMethod.POST, value = {"/preview"}, consumes = "application/json", produces = "application/json") - public ResponseEntity> getPreview(@RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) { + public ResponseEntity> getPreview(@RequestBody DatasetProfile profile, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) { eu.eudat.data.entities.DatasetProfile modelDefinition = AdminManager.generateViewStyleDefinition(profile, getApiContext()); eu.eudat.models.data.user.composite.DatasetProfile datasetProfile = userManager.generateDatasetProfileModel(modelDefinition); PagedDatasetProfile pagedDatasetProfile = new PagedDatasetProfile(); @@ -110,18 +130,20 @@ public class Admin extends BaseController { @Transactional @RequestMapping(method = RequestMethod.POST, value = {"/datasetprofile/clone/{id}"}, consumes = "application/json", produces = "application/json") - public ResponseEntity> clone(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) { + public ResponseEntity> clone(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) { eu.eudat.data.entities.DatasetProfile profile = this.datasetProfileManager.clone(id); eu.eudat.models.data.admin.composite.DatasetProfile datasetprofile = AdminManager.generateDatasetProfileModel(profile); datasetprofile.setLabel(profile.getLabel() + " new "); datasetprofile.setLanguage(profile.getLanguage()); + datasetprofile.setDescription(profile.getDescription()); + //datasetProfileManager.retrieveUsers(profile, datasetprofile); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(datasetprofile)); } @Transactional @RequestMapping(method = RequestMethod.DELETE, value = {"{id}"}, consumes = "application/json", produces = "application/json") public @ResponseBody - ResponseEntity> inactivate(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) { + ResponseEntity> inactivate(@PathVariable String id, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) { try { eu.eudat.data.entities.DatasetProfile ret = AdminManager.inactivate(this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao(), this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetDao(), id); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE)); @@ -132,11 +154,13 @@ public class Admin extends BaseController { @Transactional @RequestMapping(method = RequestMethod.GET, value = {"/getXml/{id}"}, produces = "application/json") - public ResponseEntity getDatasetProfileXml(@PathVariable String id, @RequestHeader("Content-Type") String contentType, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) throws IllegalAccessException, IOException, InstantiationException { + public ResponseEntity getDatasetProfileXml(@PathVariable String id, @RequestHeader("Content-Type") String contentType, @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) throws IllegalAccessException, IOException, InstantiationException { if (contentType.equals("application/xml")) { eu.eudat.data.entities.DatasetProfile profile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(UUID.fromString(id)); eu.eudat.models.data.user.composite.DatasetProfile datasetProfile = userManager.generateDatasetProfileModel(profile); datasetProfile.setStatus(profile.getStatus()); + datasetProfile.setDescription(profile.getDescription()); + datasetProfile.setLanguage(profile.getLanguage()); return this.datasetProfileManager.getDocument(datasetProfile, profile.getLabel()); } else { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.ERROR_MESSAGE).message("NOT AUTHORIZE")); @@ -145,17 +169,23 @@ public class Admin extends BaseController { @RequestMapping(method = RequestMethod.POST, value = {"/upload"}) public ResponseEntity setDatasetProfileXml(@RequestParam("file") MultipartFile file, - @ClaimedAuthorities(claims = {ADMIN}) Principal principal) throws IllegalAccessException, IOException { + @ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) throws IllegalAccessException, IOException { eu.eudat.logic.utilities.documents.xml.datasetProfileXml.datasetProfileModel.DatasetProfile datasetProfileModel = this.datasetProfileManager.createDatasetProfileFromXml(file); eu.eudat.models.data.admin.composite.DatasetProfile datasetProfileEntity = datasetProfileModel.toAdminCompositeModel(file.getOriginalFilename()); eu.eudat.data.entities.DatasetProfile modelDefinition = AdminManager.generateViewStyleDefinition(datasetProfileEntity, getApiContext()); - this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().createOrUpdate(modelDefinition); + eu.eudat.data.entities.DatasetProfile datasetProfile = this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao().createOrUpdate(modelDefinition); + UserDatasetProfile userDatasetProfile = new UserDatasetProfile(); + userDatasetProfile.setDatasetProfile(datasetProfile); + UserInfo userInfo = getApiContext().getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId()); + userDatasetProfile.setUser(userInfo); + userDatasetProfile.setRole(0); + getApiContext().getOperationsContext().getDatabaseRepository().getUserDatasetProfileDao().createOrUpdate(userDatasetProfile); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>() .status(ApiMessageCode.SUCCESS_MESSAGE).message("")); } @RequestMapping(method = RequestMethod.GET, value = {"/getRDACommonStandards"}, produces = "application/json") - public ResponseEntity getRDACommonStandards(@ClaimedAuthorities(claims = {ADMIN}) Principal principal) { + public ResponseEntity getRDACommonStandards(@ClaimedAuthorities(claims = {ADMIN, DATASET_PROFILE_MANAGER}) Principal principal) { return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().status(ApiMessageCode.SUCCESS_MESSAGE).payload(configLoader.getRdaProperties())); } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/DMPs.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/DMPs.java index f8844b7d7..f7f7b907c 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/DMPs.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/DMPs.java @@ -161,9 +161,9 @@ public class DMPs extends BaseController { @RequestMapping(method = RequestMethod.GET, value = {"/versions/{id}"}, consumes = "application/json", produces = "application/json") public @ResponseBody - ResponseEntity>> getVersions(@PathVariable(value= "id") String groupId, + ResponseEntity>> getVersions(@PathVariable(value= "id") String groupId, @RequestParam(value= "public") Boolean isPublic, @ClaimedAuthorities(claims = {Authorities.ADMIN, Authorities.MANAGER, Authorities.USER, Authorities.ANONYMOUS}) Principal principal) throws Exception { - List versions = this.dataManagementPlanManager.getAllVersions(groupId, principal); + List versions = this.dataManagementPlanManager.getAllVersions(groupId, principal, isPublic); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().status(ApiMessageCode.NO_MESSAGE).payload(versions)); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Datasets.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Datasets.java index 8d186f0b3..d79ec0a64 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Datasets.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Datasets.java @@ -247,9 +247,9 @@ public class Datasets extends BaseController { @javax.transaction.Transactional @RequestMapping(method = RequestMethod.POST, consumes = "application/json", produces = "application/json") public @ResponseBody - ResponseEntity> createOrUpdate(@RequestBody DatasetWizardModel profile, Principal principal) throws Exception { - Dataset dataset = this.datasetManager.createOrUpdate(profile, principal); - return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message("Created").payload(dataset.getId())); + ResponseEntity> createOrUpdate(@RequestBody DatasetWizardModel profile, Principal principal) throws Exception { + DatasetWizardModel dataset = new DatasetWizardModel().fromDataModel(this.datasetManager.createOrUpdate(profile, principal)); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message("Created").payload(dataset)); } @Transactional @@ -280,6 +280,18 @@ public class Datasets extends BaseController { } } + @RequestMapping(method = RequestMethod.GET, value = {"/{id}/validate"}, produces = "application/json") + public @ResponseBody + ResponseEntity> validate(@PathVariable(value = "id") UUID id, Principal principal) throws Exception { + try { + Dataset dataset = datasetManager.getEntitySingle(id); + datasetManager.checkDatasetValidation(dataset); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message("Valid")); + } catch (Exception datasetWizardCannotUnlockException) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.ERROR_MESSAGE).message(datasetWizardCannotUnlockException.getMessage())); + } + } + /* * Data Import * */ diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/EmailMergeConfirmation.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/EmailMergeConfirmation.java new file mode 100644 index 000000000..d1e89b493 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/EmailMergeConfirmation.java @@ -0,0 +1,63 @@ +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.managers.MergeEmailConfirmationManager; +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.models.data.userinfo.UserMergeRequestModel; +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/emailMergeConfirmation") +public class EmailMergeConfirmation { + + private MergeEmailConfirmationManager emailConfirmationManager; + + @Autowired + public EmailMergeConfirmation(MergeEmailConfirmationManager emailConfirmationManager) { + this.emailConfirmationManager = emailConfirmationManager; + } + + @Transactional + @RequestMapping(method = RequestMethod.GET, value = {"/{emailToken}"}) + public @ResponseBody + ResponseEntity 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) { + 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 sendConfirmatioEmail(@RequestBody UserMergeRequestModel requestModel, Principal principal) { + try { + this.emailConfirmationManager.sendConfirmationEmail(requestModel.getEmail(), principal, requestModel.getUserId(), requestModel.getProvider()); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE)); + } catch (Exception ex) { + if (ex instanceof HasConfirmedEmailException) { + return ResponseEntity.status(HttpStatus.FOUND).body(new ResponseItem().status(ApiMessageCode.WARN_MESSAGE)); + } + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE)); + } + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java index ff6ddfe92..ac75578d9 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Login.java @@ -4,6 +4,7 @@ package eu.eudat.controllers; import eu.eudat.exceptions.security.ExpiredTokenException; import eu.eudat.exceptions.security.NonValidTokenException; import eu.eudat.exceptions.security.NullEmailException; +import eu.eudat.logic.managers.MetricsManager; import eu.eudat.logic.managers.UserManager; import eu.eudat.logic.proxy.config.configloaders.ConfigLoader; import eu.eudat.logic.security.CustomAuthenticationProvider; @@ -94,6 +95,7 @@ public class Login { public @ResponseBody ResponseEntity> externallogin(@RequestBody LoginInfo credentials) throws GeneralSecurityException, NullEmailException { logger.info("Trying To Login With " + credentials.getProvider()); + MetricsManager.increaseValue("argos_users", 1, "loggedin"); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(customAuthenticationProvider.authenticate(credentials)).status(ApiMessageCode.SUCCESS_MESSAGE)); } @@ -102,6 +104,7 @@ public class Login { public @ResponseBody ResponseEntity> nativelogin(@RequestBody Credentials credentials) throws NullEmailException { logger.info(credentials.getUsername() + " Trying To Login"); + MetricsManager.increaseValue("argos_users", 1, "loggedin"); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(userManager.authenticate(this.nonVerifiedUserAuthenticationService, credentials)).status(ApiMessageCode.SUCCESS_MESSAGE)); } @@ -162,6 +165,7 @@ public class Login { ResponseEntity> logout(Principal principal) { this.nonVerifiedUserAuthenticationService.Logout(principal.getToken()); logger.info(principal + " Logged Out"); + MetricsManager.decreaseValue("argos_users", 1, "loggedin"); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE)); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Organisations.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Organisations.java index 2158b892c..0d2d12a65 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Organisations.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Organisations.java @@ -42,6 +42,13 @@ public class Organisations extends BaseController { return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().payload(organisations).status(ApiMessageCode.NO_MESSAGE)); } + @RequestMapping(method = RequestMethod.POST, value = {"/general/organisations"}, produces = "application/json") + public @ResponseBody + ResponseEntity>> listGeneralOrganisations(@RequestBody OrganisationsTableRequest organisationsTableRequest, Principal principal) throws Exception { + List organisations = organisationsManager.getWithExternal(organisationsTableRequest, principal); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().payload(organisations).status(ApiMessageCode.NO_MESSAGE)); + } + @RequestMapping(method = RequestMethod.POST, value = {"/internal/organisations"}, produces = "application/json") public @ResponseBody ResponseEntity>> getPaged(@Valid @RequestBody OrganisationsTableRequest organisationsTableRequest, Principal principal) throws Exception{ diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/UserGuideController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/UserGuideController.java index 102bd66c2..6cb588eef 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/UserGuideController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/UserGuideController.java @@ -1,6 +1,9 @@ package eu.eudat.controllers; +import eu.eudat.logic.managers.MetricsManager; +import eu.eudat.logic.security.claims.ClaimedAuthorities; import eu.eudat.models.data.helpers.responses.ResponseItem; +import eu.eudat.models.data.security.Principal; import eu.eudat.models.data.userguide.UserGuide; import eu.eudat.types.ApiMessageCode; import org.springframework.beans.factory.annotation.Autowired; @@ -19,6 +22,8 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import static eu.eudat.types.Authorities.ADMIN; + @RestController @CrossOrigin @RequestMapping(value = {"/api/userguide/"}) @@ -31,13 +36,18 @@ public class UserGuideController { this.environment = environment; } - @RequestMapping(path = "current", method = RequestMethod.GET ) - public ResponseEntity getUserGuide() throws IOException { + @RequestMapping(path = "{lang}", method = RequestMethod.GET ) + public ResponseEntity getUserGuide(@PathVariable(name = "lang") String lang) throws IOException { + long files = Files.list(Paths.get(this.environment.getProperty("userguide.path"))).count(); + MetricsManager.calculateValue("argos_languages", (int) files, null); Stream walk = Files.walk(Paths.get(this.environment.getProperty("userguide.path"))); List result = walk.filter(Files::isRegularFile) .map(Path::toString).collect(Collectors.toList()); - String fileName = result.get(0); + String fileName = result.stream().filter(guide -> guide.contains("_" + lang)).findFirst().orElse(null); + if (fileName == null) { + fileName = result.stream().filter(guide -> guide.contains("_en")).findFirst().get(); + } InputStream is = new FileInputStream(fileName); String[] filepath = fileName.split("\\.")[0].split("\\\\"); @@ -60,7 +70,7 @@ public class UserGuideController { @RequestMapping(value = "current", method = RequestMethod.POST) public @ResponseBody - ResponseEntity> updateGuide(@RequestBody UserGuide guide) throws Exception { + ResponseEntity> updateGuide(@RequestBody UserGuide guide, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) throws Exception { String fileName = this.environment.getProperty("userguide.path") + guide.getName() + ".html"; OutputStream os = new FileOutputStream(fileName); os.write(guide.getHtml().getBytes()); diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Users.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Users.java index 1cfbc4651..2a7c56f5a 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/Users.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Users.java @@ -11,6 +11,7 @@ import eu.eudat.models.data.doi.DOIRequest; import eu.eudat.models.data.helpers.common.DataTableData; import eu.eudat.models.data.helpers.responses.ResponseItem; import eu.eudat.models.data.security.Principal; +import eu.eudat.models.data.userinfo.UserCredential; import eu.eudat.models.data.userinfo.UserListingModel; import eu.eudat.models.data.userinfo.UserProfile; import eu.eudat.types.ApiMessageCode; @@ -22,6 +23,7 @@ import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.io.IOException; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -62,6 +64,14 @@ public class Users extends BaseController { UserProfile user = userManager.getSingle(userId); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(user).status(ApiMessageCode.NO_MESSAGE)); } + + @RequestMapping(method = RequestMethod.GET, value = {"/{id}/emails"}, produces = "application/json") + public @ResponseBody + ResponseEntity>> getEmails(@PathVariable String id, Principal principal) throws Exception { + UUID userId = id.equals("me") ? principal.getId() : UUID.fromString(id); + List user = userManager.getCredentials(userId); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().payload(user).status(ApiMessageCode.NO_MESSAGE)); + } @Transactional @RequestMapping(method = RequestMethod.POST, value = {"/settings"}, produces = "application/json") @@ -103,6 +113,20 @@ public class Users extends BaseController { userManager.deleteDOIToken(principal); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE)); } + + @RequestMapping(method = RequestMethod.GET, value = {"/getCsv"}) + public @ResponseBody + ResponseEntity exportCsv(@ClaimedAuthorities(claims = {ADMIN}) Principal principal) throws Exception { + return userManager.exportToCsv(principal); + } + + @RequestMapping(method = RequestMethod.POST, value = {"/find"}, consumes = "application/json", produces = "application/json") + public @ResponseBody + ResponseEntity> find(@Valid @RequestBody String email) throws Exception { + UserProfile userProfile = userManager.getFromEmail(email); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(userProfile).status(ApiMessageCode.NO_MESSAGE)); + } + } diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/Validation.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/Validation.java new file mode 100644 index 000000000..f9e26d6dd --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/Validation.java @@ -0,0 +1,44 @@ +package eu.eudat.controllers; + +import eu.eudat.data.entities.Service; +import eu.eudat.logic.managers.ServiceManager; +import eu.eudat.logic.managers.ValidationManager; +import eu.eudat.logic.proxy.config.exceptions.HugeResultSet; +import eu.eudat.logic.proxy.config.exceptions.NoURLFound; +import eu.eudat.logic.services.ApiContext; +import eu.eudat.models.data.helpers.responses.ResponseItem; +import eu.eudat.models.data.security.Principal; +import eu.eudat.models.data.services.ServiceModel; +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.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + + +@RestController +@CrossOrigin +@RequestMapping(value = {"/api"}) +public class Validation extends BaseController { + + private ValidationManager validationManager; + + @Autowired + public Validation(ApiContext apiContext, ValidationManager validationManager) { + super(apiContext); + this.validationManager = validationManager; + } + + @RequestMapping(method = RequestMethod.GET, value = {"/external/validation"}, produces = "application/json") + public @ResponseBody + ResponseEntity> validate( + @RequestParam(value = "query", required = false) String query, @RequestParam(value = "type", required = false) String type, Principal principal + ) throws HugeResultSet, NoURLFound { + Boolean isValid = this.validationManager.validateIdentifier(query, type, principal); + return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(isValid).status(ApiMessageCode.NO_MESSAGE)); + } +} + diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/builders/entity/CredentialBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/builders/entity/CredentialBuilder.java index a072d7f63..14cbf0822 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/builders/entity/CredentialBuilder.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/builders/entity/CredentialBuilder.java @@ -30,6 +30,8 @@ public class CredentialBuilder extends Builder { private String externalId; + private String email; + public CredentialBuilder id(UUID id) { this.id = id; return this; @@ -75,6 +77,11 @@ public class CredentialBuilder extends Builder { return this; } + public CredentialBuilder email(String email) { + this.email = email; + return this; + } + public Credential build() { Credential credential = new Credential(); credential.setStatus(status); @@ -86,6 +93,7 @@ public class CredentialBuilder extends Builder { credential.setUserInfo(userInfo); credential.setId(id); credential.setExternalId(externalId); + credential.setEmail(email); return credential; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/builders/entity/UserInfoBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/builders/entity/UserInfoBuilder.java index 830262e58..e932d4299 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/builders/entity/UserInfoBuilder.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/builders/entity/UserInfoBuilder.java @@ -40,6 +40,8 @@ public class UserInfoBuilder extends Builder { private Set userRoles = new HashSet<>(); + private Short userStatus; + public UserInfoBuilder id(UUID id) { this.id = id; return this; @@ -100,6 +102,11 @@ public class UserInfoBuilder extends Builder { return this; } + public UserInfoBuilder userStatus(Short userStatus) { + this.userStatus = userStatus; + return this; + } + @Override public UserInfo build() { UserInfo userInfo = new UserInfo(); @@ -115,6 +122,7 @@ public class UserInfoBuilder extends Builder { userInfo.setCredentials(credentials); userInfo.setDmps(dmps); userInfo.setVerified_email(verified_email); + userInfo.setUserStatus(userStatus); return userInfo; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DashBoardManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DashBoardManager.java index 62b5fa194..b6bf4e918 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DashBoardManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DashBoardManager.java @@ -135,6 +135,8 @@ public class DashBoardManager { dataManagementPlanCriteria.setAllVersions(false); GrantCriteria grantCriteria = new GrantCriteria(); + grantCriteria.setActive(true); + List roles = new LinkedList<>(); List finalDmps = dmps; CompletableFuture dmpFuture = dataManagementPlanRepository.getAuthenticated((dmps != null && !dmps.isEmpty()) ? dataManagementPlanRepository.asQueryable().where((builder, root) -> root.get("id").in(finalDmps.stream().map(Dmp::getId).collect(Collectors.toList()))) : dataManagementPlanRepository.getWithCriteria(dataManagementPlanCriteria), principal.getId(), roles).distinct().countAsync() diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java index ad66be3b5..a53facf81 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementPlanManager.java @@ -321,8 +321,8 @@ public class DataManagementPlanManager { return data; } - public List getAllVersions(String groupId, Principal principal) { - UUID principalId = principal.getId(); + public List getAllVersions(String groupId, Principal principal, Boolean isPublic) { + UUID principalId = principal != null ? principal.getId() : null; List versions = new ArrayList<>(); QueryableList items = null; QueryableList authItems = null; @@ -330,10 +330,10 @@ public class DataManagementPlanManager { DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); criteria.setGroupIds(Collections.singletonList(UUID.fromString(groupId))); criteria.setAllVersions(true); - criteria.setIsPublic(principalId == null); - criteria.setOnlyPublic(principalId == null); + criteria.setIsPublic(isPublic); + criteria.setOnlyPublic(isPublic); items = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria); - if (principalId != null) { + if (!isPublic) { authItems = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getAuthenticated(items, principalId, roles); } else { authItems = items; @@ -366,7 +366,7 @@ public class DataManagementPlanManager { if (dbTime.toEpochMilli() != modelTime.toEpochMilli()) { throw new Exception("Another user have already edit that DMP."); } - List datasetList = new ArrayList<>(dmp1.getDataset()); + List datasetList = dmp1.getDataset().stream().filter(dataset -> dataset.getStatus() != 99).collect(Collectors.toList()); for (Dataset dataset : datasetList) { if (dataManagementPlan.getProfiles().stream().filter(associatedProfile -> dataset.getProfile().getId().equals(associatedProfile.getId())).findAny().orElse(null) == null) throw new Exception("Dataset Template for Dataset Description is missing from the DMP."); @@ -375,6 +375,8 @@ public class DataManagementPlanManager { throw new Exception("DMP is finalized, therefore cannot be edited."); setNotification = true; + } else { + MetricsManager.increaseValue("argos_managed_dmps", 1, "draft"); } DMP newDmp = dataManagementPlan.toDataModel(); @@ -497,6 +499,8 @@ public class DataManagementPlanManager { } if (dataManagementPlan.getStatus() == (int) DMP.DMPStatus.FINALISED.getValue() && dmp1.getStatus().equals(DMP.DMPStatus.FINALISED.getValue())) throw new Exception("DMP is finalized, therefore cannot be edited."); + } else { + MetricsManager.increaseValue("argos_managed_dmps", 1, "draft"); } List datasets = new ArrayList<>(); DMP tempDMP = dataManagementPlan.toDataModel(); @@ -612,6 +616,7 @@ public class DataManagementPlanManager { newDmp.setDataset(new HashSet<>(databaseRepository.getDatasetDao().getWithCriteria(criteria1).toList())); this.updateIndex(newDmp); + MetricsManager.increaseValue("argos_managed_dmps", 1, "draft"); return newDmp.getId(); } @@ -623,6 +628,20 @@ public class DataManagementPlanManager { if (apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).toList().size() > 0) throw new DMPWithDatasetsDeleteException("You cannot Remove Datamanagement Plan with Datasets"); DMP oldDmp = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(uuid); + switch (oldDmp.getStatus()) { + case 0: + MetricsManager.decreaseValue("argos_managed_dmps", 1, "draft"); + break; + case 1: + if (oldDmp.getDoi() != null) { + MetricsManager.decreaseValue("argos_managed_dmps", 1, "doied"); + } + if (oldDmp.isPublic()) { + MetricsManager.decreaseValue("argos_managed_dmps", 1, "published"); + } + MetricsManager.decreaseValue("argos_managed_dmps", 1, "finalized"); + break; + } oldDmp.setStatus(DMP.DMPStatus.DELETED.getValue()); apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(oldDmp); this.updateIndex(oldDmp); @@ -660,6 +679,7 @@ public class DataManagementPlanManager { else { researcher.setCreationUser(user); researcherRepository.createOrUpdate(researcher); + MetricsManager.increaseValue("argos_researchers", 1, null); } } } @@ -680,7 +700,9 @@ public class DataManagementPlanManager { } if (createNew) { - organisation.setReference("Internal:" + UUID.randomUUID().toString()); + if (organisation.getReference() == null) { + organisation.setReference("Internal:" + UUID.randomUUID().toString()); + } organisationRepository.createOrUpdate(organisation); } } @@ -739,12 +761,14 @@ public class DataManagementPlanManager { project.setType(Project.ProjectType.EXTERNAL.getValue()); if (project.getId() == null) project.setId(UUID.randomUUID()); projectDao.createOrUpdate(project); + MetricsManager.increaseValue("argos_projects", 1, null); } } else { project.setType(Project.ProjectType.EXTERNAL.getValue()); if (project.getId() == null) project.setId(UUID.randomUUID()); projectDao.createOrUpdate(project); + MetricsManager.increaseValue("argos_projects", 1, null); } } } @@ -821,6 +845,7 @@ public class DataManagementPlanManager { return newDataset; }).thenApplyAsync(item -> { futures.add(datasetDao.createOrUpdateAsync(item).whenComplete(((dataset1, throwable) -> { + MetricsManager.increaseValue("argos_managed_dataset_descriptions", 1, "draft"); eu.eudat.elastic.entities.Dataset datasetElastic = new eu.eudat.elastic.entities.Dataset(); datasetElastic.setId(dataset1.getId().toString()); datasetElastic.setLabel(dataset1.getLabel()); @@ -878,6 +903,7 @@ public class DataManagementPlanManager { dmp.setPublic(true); apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp); this.updateIndex(dmp); + MetricsManager.increaseValue("argos_managed_dmps", 1, "published"); DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); criteria.setGroupIds(Collections.singletonList(dmp.getGroupId())); criteria.setAllVersions(true); @@ -890,6 +916,7 @@ public class DataManagementPlanManager { tags = elastic.getTags(); } this.datasetManager.updateTags(dataset, tags); + MetricsManager.increaseValue("argos_managed_dataset_descriptions", 1, "published"); } catch (Exception e) { logger.error(e.getMessage(), e); } @@ -908,23 +935,37 @@ public class DataManagementPlanManager { throw new Exception("User does not have the privilege to do this action."); if (dmp.getStatus().equals(DMP.DMPStatus.FINALISED.getValue())) throw new Exception("DMP is already finalized"); - dmp.setStatus(DMP.DMPStatus.FINALISED.getValue()); - apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp); - this.updateIndex(dmp); List indexDatasets = new ArrayList<>(); if (datasetsToBeFinalized != null && datasetsToBeFinalized.getUuids() != null && !datasetsToBeFinalized.getUuids().isEmpty()) { - apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao() + List finalizedDatasets = apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao() .asQueryable().where((builder, root) -> root.get("id").in(datasetsToBeFinalized.getUuids())) - .update(root -> root.get("status"), Dataset.Status.FINALISED.getValue()); - apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao() + .toList(); + for (Dataset dataset: finalizedDatasets) { + Dataset.Status status = Dataset.Status.fromInteger(dataset.getStatus()); + Date finalizedDate = dataset.getFinalizedAt(); + dataset.setStatus(Dataset.Status.FINALISED.getValue()); + dataset.setFinalizedAt(new Date()); + DatasetWizardModel wizardModel = new DatasetWizardModel(); + wizardModel = wizardModel.fromDataModel(dataset); + wizardModel.setDatasetProfileDefinition(this.datasetManager.getPagedProfile(wizardModel, dataset)); + try { + datasetManager.createOrUpdate(wizardModel, principal); + } catch (Exception e) { + dataset.setStatus(status.getValue()); + dataset.setFinalizedAt(finalizedDate); + throw e; + } + dataset.setModified(new Date()); + } + /*apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao() .asQueryable().where((builder, root) -> root.get("id").in(datasetsToBeFinalized.getUuids())) - .update(root -> root.get("finalizedAt"), new Date()); - List finalizedDatasets = dmp.getDataset().stream().filter(dataset -> datasetsToBeFinalized.getUuids().contains(dataset.getId())).collect(Collectors.toList()); - finalizedDatasets.forEach(dataset ->{ + .update(root -> root.get("finalizedAt"), new Date());*/ + //List finalizedDatasets = dmp.getDataset().stream().filter(dataset -> datasetsToBeFinalized.getUuids().contains(dataset.getId())).collect(Collectors.toList()); +/* finalizedDatasets.forEach(dataset ->{ dataset.setStatus(Dataset.Status.FINALISED.getValue()); dataset.setFinalizedAt(new Date()); dataset.setModified(new Date()); - } ); + } );*/ indexDatasets.addAll(finalizedDatasets); List datasetsToBeCanceled = new LinkedList<>(); for (Dataset dataset : dmp.getDataset()) { @@ -955,11 +996,17 @@ public class DataManagementPlanManager { cancelledDatasets.forEach(dataset -> dataset.setStatus(Dataset.Status.CANCELED.getValue())); indexDatasets.addAll(cancelledDatasets); } - - UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId()); - sendNotification(dmp, user, NotificationType.DMP_FINALISED); } + dmp.setStatus(DMP.DMPStatus.FINALISED.getValue()); + apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp); + this.updateIndex(dmp); + UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId()); + sendNotification(dmp, user, NotificationType.DMP_FINALISED); + MetricsManager.decreaseValue("argos_managed_dmps", 1, "draft"); + MetricsManager.increaseValue("argos_managed_dmps", 1, "finalized"); this.updateDatasetsIndex(indexDatasets); + MetricsManager.decreaseValue("argos_managed_dataset_descriptions", indexDatasets.size(), "draft"); + MetricsManager.increaseValue("argos_managed_dataset_descriptions", indexDatasets.size(), "finalized"); } public void undoFinalize(UUID id, Principal principal) throws Exception { @@ -971,6 +1018,8 @@ public class DataManagementPlanManager { dmp.setStatus(DMP.DMPStatus.ACTIVE.getValue()); apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp); this.updateIndex(dmp); + MetricsManager.decreaseValue("argos_managed_dmps", 1, "finalized"); + MetricsManager.increaseValue("argos_managed_dmps", 1, "draft"); } public void updateUsers(UUID id, List users, Principal principal) throws Exception { @@ -1076,7 +1125,7 @@ public class DataManagementPlanManager { runDatasetTemplate.setBold(true); runDatasetTemplate.setFontSize(12); - XWPFParagraph externalReferencesParagraph = document.createParagraph(); + /*XWPFParagraph externalReferencesParagraph = document.createParagraph(); externalReferencesParagraph.setStyle("Heading3"); XWPFRun externalReferencesRun = externalReferencesParagraph.createRun(); externalReferencesRun.setText("External References"); @@ -1104,7 +1153,7 @@ public class DataManagementPlanManager { wordBuilder.addParagraphContent(datasetEntity.getServices().stream().map(DatasetService::getService).map(Service::getLabel).collect(Collectors.joining(", ")) , document, ParagraphStyle.TEXT, BigInteger.ZERO); } - /*wordBuilder.addParagraphContent("Tags", document, ParagraphStyle.HEADER3, BigInteger.ZERO); + *//*wordBuilder.addParagraphContent("Tags", document, ParagraphStyle.HEADER3, BigInteger.ZERO); if (datasetEntity.().size() > 0) { wordBuilder.addParagraphContent(datasetEntity.getServices().stream().map(DatasetService::getService).map(Service::getLabel).collect(Collectors.joining(", ")) , document, ParagraphStyle.HEADER4, BigInteger.ZERO); @@ -1787,6 +1836,7 @@ public class DataManagementPlanManager { dataBuilder.append(" \"publication_type\": \"datamanagementplan\",\n"); dataBuilder.append(" \"description\": \"").append((dmp.getDescription() != null && !dmp.getDescription().isEmpty() ? dmp.getDescription() : "

")).append("\",\n"); dataBuilder.append(" \"version\": \"").append(dmp.getVersion()).append("\",\n"); + dataBuilder.append(" \"communities\": [{\n \t\t\"identifier\": \"").append(environment.getProperty("zenodo.community")).append("\"\n \t\t}],\n"); dataBuilder.append(" \"access_right\": \""); if (extraProperties.get("visible") == null) { dataBuilder.append("restricted\",\n"); @@ -1823,7 +1873,20 @@ public class DataManagementPlanManager { dataBuilder.append("{\n"); dataBuilder.append(" \t\t\"name\": \"").append(userDMP.getUser().getName()).append("\",\n"); dataBuilder.append(" \t\t\"type\": \"").append("ProjectMember").append("\",\n"); - dataBuilder.append(" \t\t\"affiliation\": \"OpenDMP\"\n}"); + if (dmp.getOrganisations() != null && !dmp.getOrganisations().isEmpty()) { + dataBuilder.append(" \t\t\"affiliation\": \""); + int j = 0; + for (Organisation organization: dmp.getOrganisations()) { + if (j > 0) { + dataBuilder.append(", "); + } + dataBuilder.append(organization.getLabel()); + j++; + } + dataBuilder.append("\"\n}"); + } else { + dataBuilder.append(" \t\t\"affiliation\": \"" + this.environment.getProperty("zenodo.affiliation") +"\"\n}"); + } i++; } for(Researcher researcher: dmp.getResearchers()) { @@ -1853,7 +1916,20 @@ public class DataManagementPlanManager { } dataBuilder.append(" \"creators\": [{\n"); dataBuilder.append(" \t\t\"name\": \"").append(dmp.getUsers().stream().filter(userDMP -> userDMP.getRole().equals(UserDMP.UserDMPRoles.OWNER.getValue())).findFirst().get().getUser().getName()).append("\",\n"); - dataBuilder.append(" \t\t\"affiliation\": \"OpenDMP\"}]\n"); + if (dmp.getOrganisations() != null && !dmp.getOrganisations().isEmpty()) { + dataBuilder.append(" \t\t\"affiliation\": \""); + int j = 0; + for (Organisation organization: dmp.getOrganisations()) { + if (j > 0) { + dataBuilder.append(", "); + } + dataBuilder.append(organization.getLabel()); + j++; + } + dataBuilder.append("\"}]\n"); + } else { + dataBuilder.append(" \t\t\"affiliation\": \"" + this.environment.getProperty("zenodo.affiliation") +"\"}]\n"); + } dataBuilder.append(" }\n").append("}"); createData = dataBuilder.toString(); JsonNode createDataJSON = new ObjectMapper().readTree(createData); @@ -1919,16 +1995,31 @@ public class DataManagementPlanManager { if (unpublishedUrl == null) { // Second step, add the file to the entry. FileEnvelope file = getWordDocument(id.toString(), principal, configLoader); - String name = file.getFilename().substring(0, file.getFilename().length() - 5); + /*String name = file.getFilename().substring(0, file.getFilename().length() - 5); File pdfFile = datasetManager.convertToPDF(file, environment); - String fileName = name + ".pdf"; - FileSystemResource fileSystemResource = new FileSystemResource(pdfFile); + String fileName = name + ".pdf";*/ + FileSystemResource fileSystemResource = new FileSystemResource(file.getFile()); HttpEntity addFileMapRequest = new HttpEntity<>(fileSystemResource, null); - String addFileUrl = links.get("bucket") + "/" + fileName + "?access_token=" + zenodoToken; + String addFileUrl = links.get("bucket") + "/" + file.getFilename() + "?access_token=" + zenodoToken; restTemplate.put(addFileUrl, addFileMapRequest); Files.deleteIfExists(file.getFile().toPath()); + ResponseEntity jsonFile = getRDAJsonDocument(id.toString(), datasetManager, principal); + UUID jsonFileUUID = UUID.randomUUID(); + File tempJsonFile = new File(this.environment.getProperty("temp.temp") + jsonFileUUID.toString() + ".json"); + try (FileOutputStream jsonFos = new FileOutputStream(tempJsonFile)) { + jsonFos.write(jsonFile.getBody()); + jsonFos.flush(); + } + fileSystemResource = new FileSystemResource(tempJsonFile); + addFileMapRequest = new HttpEntity<>(fileSystemResource, null); + String jsonFileName = jsonFile.getHeaders().get("Content-Disposition").get(0).substring(jsonFile.getHeaders().get("Content-Disposition").get(0).lastIndexOf('=') + 1); + addFileUrl = links.get("bucket") + "/" + jsonFileName + "?access_token=" + zenodoToken; + restTemplate.put(addFileUrl, addFileMapRequest); + Files.deleteIfExists(tempJsonFile.toPath()); + + // Third post call to Zenodo to publish the entry and return the DOI. publishUrl = links.get("publish") + "?access_token=" + zenodoToken; } else { @@ -1986,6 +2077,51 @@ public class DataManagementPlanManager { } + public long countAllDrafts() { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setStatus(0); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).count(); + } + + public long countAllFinalized() { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setStatus(1); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).count(); + } + + public long countAllPublished() { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setIsPublic(true); + criteria.setOnlyPublic(true); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).count(); + } + + public long countAllDoied() { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setHasDoi(true); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).count(); + } + + public long countAllResearchers() { + ResearcherCriteria criteria = new ResearcherCriteria(); + return apiContext.getOperationsContext().getDatabaseRepository().getResearcherDao().getWithCriteria(criteria).count(); + } + + public long countAllProjects() { + ProjectCriteria criteria = new ProjectCriteria(); + return apiContext.getOperationsContext().getDatabaseRepository().getProjectDao().getWithCritetia(criteria).count(); + } + + public long countAllFunders() { + FunderCriteria criteria = new FunderCriteria(); + return apiContext.getOperationsContext().getDatabaseRepository().getFunderDao().getWithCritetia(criteria).count(); + } + + public long countAllGrants() { + GrantCriteria criteria = new GrantCriteria(); + return apiContext.getOperationsContext().getDatabaseRepository().getGrantDao().getWithCriteria(criteria).count(); + } + /*public DataTableData getPublicPaged(DataManagmentPlanPublicTableRequest dataManagementPlanPublicTableRequest, String fieldsGroup, Principal principal) throws Exception { dataManagementPlanPublicTableRequest.setQuery(databaseRepository.getDmpDao().asQueryable().withHint(HintedModelFactory.getHint(DataManagementPlanListingModel.class))); QueryableList items = dataManagementPlanPublicTableRequest.applyCriteria(); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementProfileManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementProfileManager.java index d9ee45421..f8f25fc3a 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementProfileManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataManagementProfileManager.java @@ -135,8 +135,8 @@ public class DataManagementProfileManager { return null; } - private static File convert(MultipartFile file) throws IOException { - File convFile = new File(file.getOriginalFilename()); + private File convert(MultipartFile file) throws IOException { + File convFile = new File(this.environment.getProperty("temp.temp") + file.getOriginalFilename()); convFile.createNewFile(); FileOutputStream fos = new FileOutputStream(convFile); fos.write(file.getBytes()); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataRepositoryManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataRepositoryManager.java index fc6cb8f9d..31634fe0c 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataRepositoryManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DataRepositoryManager.java @@ -38,7 +38,7 @@ public class DataRepositoryManager { public List getDataRepositories(String query, String type, Principal principal) throws HugeResultSet, NoURLFound { ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(query); - List> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getRepositories(externalUrlCriteria, type); + List> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getRepositories(externalUrlCriteria, type); DataRepositoryCriteria criteria = new DataRepositoryCriteria(); if (!query.isEmpty()) criteria.setLike(query); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java index c3f137c9f..e164b9092 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetManager.java @@ -83,6 +83,8 @@ import javax.xml.xpath.XPathFactory; import java.io.*; import java.math.BigInteger; import java.nio.file.Files; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -349,6 +351,10 @@ public class DatasetManager { return dataset; } + public Dataset getEntitySingle(UUID id) { + return databaseRepository.getDatasetDao().find(id); + } + public PagedDatasetProfile getPagedProfile(DatasetWizardModel dataset, eu.eudat.data.entities.Dataset datasetEntity) { eu.eudat.models.data.user.composite.DatasetProfile datasetprofile = userManager.generateDatasetProfileModel(datasetEntity.getProfile()); datasetprofile.setStatus(dataset.getStatus()); @@ -384,7 +390,7 @@ public class DatasetManager { runDatasetTemplate.setBold(true); runDatasetTemplate.setFontSize(12); - XWPFParagraph externalReferencesParagraph = document.createParagraph(); + /*XWPFParagraph externalReferencesParagraph = document.createParagraph(); externalReferencesParagraph.setStyle("Heading2"); XWPFRun externalReferencesRun = externalReferencesParagraph.createRun(); externalReferencesRun.setText("External References"); @@ -411,7 +417,7 @@ public class DatasetManager { if (datasetEntity.getServices().size() > 0) { wordBuilder.addParagraphContent(datasetEntity.getServices().stream().map(DatasetService::getService).map(Service::getLabel).collect(Collectors.joining(", ")) , document, ParagraphStyle.TEXT, BigInteger.ZERO); - } + }*/ /*wordBuilder.addParagraphContent("Tags", document, ParagraphStyle.HEADER3, BigInteger.ZERO); if (datasetEntity.().size() > 0) { wordBuilder.addParagraphContent(datasetEntity.getServices().stream().map(DatasetService::getService).map(Service::getLabel).collect(Collectors.joining(", ")) @@ -580,11 +586,16 @@ public class DatasetManager { if (datasetWizardModel.getId() != null) { tempDataset = apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().find(datasetWizardModel.getId()); if (tempDataset != null) { - if (datasetWizardModel.getModified().getTime() != tempDataset.getModified().getTime()) { + + Instant dbTime = Instant.ofEpochMilli(tempDataset.getModified().getTime()).truncatedTo(ChronoUnit.SECONDS); + Instant modelTime = Instant.ofEpochMilli(datasetWizardModel.getModified().getTime()).truncatedTo(ChronoUnit.SECONDS); + if (modelTime.toEpochMilli() != dbTime.toEpochMilli()) { throw new Exception("Dataset has been modified already by another user."); } sendNotification = true; } + } else { + MetricsManager.increaseValue("argos_managed_dataset_descriptions", 1, "draft"); } if (dmp.getStatus().equals(DMP.DMPStatus.FINALISED.getValue()) && datasetWizardModel.getId() != null) throw new Exception("DMP is finalized, therefore Dataset cannot be edited."); @@ -646,7 +657,7 @@ public class DatasetManager { } - private void checkDatasetValidation(Dataset dataset) throws Exception { + public void checkDatasetValidation(Dataset dataset) throws Exception { List datasetProfileValidators = new LinkedList<>(); DatasetProfile profile = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(dataset.getProfile().getId()); DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); @@ -819,6 +830,7 @@ public class DatasetManager { if (dataset.getStatus() != eu.eudat.data.entities.Dataset.Status.FINALISED.getValue()) throw new Exception("You cannot make public a Dataset That Has not Been Finalised"); datasetDao.createOrUpdate(dataset); + MetricsManager.increaseValue("argos_managed_dataset_descriptions", 1, "published"); } public ResponseEntity getDocument(String id, VisibilityRuleService visibilityRuleService, String contentType, Principal principal) throws IllegalAccessException, IOException, InstantiationException { @@ -907,6 +919,7 @@ public class DatasetManager { createServicesIfTheyDontExist(entity); createExternalDatasetsIfTheyDontExist(entity); + MetricsManager.increaseValue("argos_managed_dataset_descriptions", 1, "draft"); return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().createOrUpdate(entity); } @@ -1062,4 +1075,28 @@ public class DatasetManager { listingModel.setProfileLatestVersion(islast); return listingModel; } + + public long countAllDraft() { + eu.eudat.data.dao.criteria.DatasetCriteria criteria = new eu.eudat.data.dao.criteria.DatasetCriteria(); + criteria.setStatus(0); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).count(); + } + + public long countAllFinalized() { + eu.eudat.data.dao.criteria.DatasetCriteria criteria = new eu.eudat.data.dao.criteria.DatasetCriteria(); + criteria.setStatus(1); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).count(); + } + + public long countAllPublic() { + eu.eudat.data.dao.criteria.DatasetCriteria criteria = new eu.eudat.data.dao.criteria.DatasetCriteria(); + criteria.setIsPublic(true); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).count(); + } + + public long countAllWithDoi() { + eu.eudat.data.dao.criteria.DatasetCriteria criteria = new eu.eudat.data.dao.criteria.DatasetCriteria(); + criteria.setHasDoi(true); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).count(); + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetProfileManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetProfileManager.java index 362acb233..56e97cfb4 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetProfileManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetProfileManager.java @@ -4,6 +4,8 @@ import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; import eu.eudat.data.dao.criteria.DatasetProfileCriteria; import eu.eudat.data.entities.DatasetProfile; +import eu.eudat.data.entities.UserDatasetProfile; +import eu.eudat.data.entities.UserInfo; import eu.eudat.data.query.items.item.datasetprofile.DatasetProfileAutocompleteRequest; import eu.eudat.data.query.items.table.datasetprofile.DatasetProfileTableRequestItem; import eu.eudat.exceptions.datasetprofile.DatasetProfileNewVersionException; @@ -14,13 +16,17 @@ import eu.eudat.logic.utilities.builders.XmlBuilder; import eu.eudat.logic.utilities.documents.helpers.FileEnvelope; import eu.eudat.logic.utilities.documents.xml.datasetProfileXml.ExportXmlBuilderDatasetProfile; import eu.eudat.logic.utilities.documents.xml.datasetProfileXml.ImportXmlBuilderDatasetProfile; +import eu.eudat.logic.utilities.helpers.StreamDistinctBy; import eu.eudat.models.data.components.commons.datafield.AutoCompleteData; import eu.eudat.models.data.datasetprofile.DatasetProfileAutocompleteItem; import eu.eudat.models.data.datasetprofile.DatasetProfileListingModel; import eu.eudat.models.data.entities.xmlmodels.datasetprofiledefinition.Field; import eu.eudat.models.data.externaldataset.ExternalAutocompleteFieldModel; import eu.eudat.models.data.helpers.common.DataTableData; +import eu.eudat.models.data.listingmodels.UserInfoListingModel; +import eu.eudat.models.data.security.Principal; import eu.eudat.queryable.QueryableList; +import eu.eudat.types.Authorities; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -37,6 +43,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; import javax.activation.MimetypesFileTypeMap; +import javax.transaction.Transactional; import javax.xml.xpath.*; import java.io.*; import java.nio.file.Files; @@ -61,6 +68,7 @@ public class DatasetProfileManager { this.cache = new ArrayList<>(); } + @Transactional public eu.eudat.models.data.admin.composite.DatasetProfile getDatasetProfile(String id) { eu.eudat.data.entities.DatasetProfile profile = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(UUID.fromString(id)); eu.eudat.models.data.admin.composite.DatasetProfile datasetprofile = AdminManager.generateDatasetProfileModel(profile); @@ -68,7 +76,8 @@ public class DatasetProfileManager { datasetprofile.setStatus(profile.getStatus()); datasetprofile.setDescription(profile.getDescription()); datasetprofile.setLanguage(profile.getLanguage()); - + datasetprofile.setUsers(new ArrayList<>()); + retrieveUsers(profile, datasetprofile); return datasetprofile; } @@ -83,12 +92,19 @@ public class DatasetProfileManager { DatasetProfile profile = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(UUID.fromString(id)); apiContext.getOperationsContext().getDatabaseRepository().detachEntity(profile); profile.setId(null); + MetricsManager.increaseValue("argos_dataset_templates", 1, "total"); return profile; } - public DataTableData getPaged(DatasetProfileTableRequestItem datasetProfileTableRequestItem) throws Exception { + public DataTableData getPaged(DatasetProfileTableRequestItem datasetProfileTableRequestItem, Principal principal) throws Exception { QueryableList items = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(datasetProfileTableRequestItem.getCriteria()); - QueryableList pagedItems = PaginationManager.applyPaging(items, datasetProfileTableRequestItem); + QueryableList authItems = null; + if (principal.getAuthz().contains(Authorities.ADMIN)) { + authItems = items; + } else if (principal.getAuthz().contains(Authorities.DATASET_PROFILE_MANAGER)) { + authItems = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getAuthenticated(items, principal.getId(), null); + } + QueryableList pagedItems = PaginationManager.applyPaging(authItems, datasetProfileTableRequestItem); List datasetProfiles = pagedItems.select(item -> new DatasetProfileListingModel().fromDataModel(item)); return apiContext.getOperationsContext().getBuilderFactory().getBuilder(DataTableDataBuilder.class).data(datasetProfiles).totalCount(items.count()).build(); } @@ -96,6 +112,7 @@ public class DatasetProfileManager { public List getAll(DatasetProfileTableRequestItem tableRequestItem) throws IllegalAccessException, InstantiationException { QueryableList items = databaseRepository.getDatasetProfileDao().getWithCriteria(tableRequestItem.getCriteria()); List datasetProfiles = items.select(item -> new DatasetProfileListingModel().fromDataModel(item)); + return datasetProfiles; } @@ -215,6 +232,7 @@ public class DatasetProfileManager { File localFile = convert(multiPartFile); eu.eudat.logic.utilities.documents.xml.datasetProfileXml.datasetProfileModel.DatasetProfile profile = xmlBuilder.build(localFile); Files.deleteIfExists(localFile.toPath()); + MetricsManager.increaseValue("argos_dataset_templates", 1, "total"); return profile; } catch (IOException e) { logger.error(e.getMessage(), e); @@ -222,8 +240,8 @@ public class DatasetProfileManager { return null; } - private static File convert(MultipartFile file) throws IOException { - File convFile = new File(file.getOriginalFilename()); + private File convert(MultipartFile file) throws IOException { + File convFile = new File(this.environment.getProperty("temp.temp") + file.getOriginalFilename()); convFile.createNewFile(); FileOutputStream fos = new FileOutputStream(convFile); fos.write(file.getBytes()); @@ -257,4 +275,61 @@ public class DatasetProfileManager { throw new DatasetProfileNewVersionException("Version to update not the latest."); } } + + public void storeDatasetProfileUsers(DatasetProfile entity, eu.eudat.models.data.admin.composite.DatasetProfile model) { + if (model.getUsers() != null && !model.getUsers().isEmpty()) { + if (entity.getUsers() == null) { + entity.setUsers(new HashSet<>()); + } + model.getUsers().stream().filter(userInfoListingModel -> entity.getUsers().stream().filter(userDatasetProfile -> userDatasetProfile.getUser().getId().equals(userInfoListingModel.getId())).count() == 0).forEach(userInfoListingModel -> { + UserDatasetProfile userDatasetProfile1 = new UserDatasetProfile(); + userDatasetProfile1.setDatasetProfile(entity); + UserInfo userInfo1 = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(userInfoListingModel.getId()); + userDatasetProfile1.setUser(userInfo1); + userDatasetProfile1.setRole(1); + apiContext.getOperationsContext().getDatabaseRepository().getUserDatasetProfileDao().createOrUpdate(userDatasetProfile1); + }); + } + if (entity.getUsers() != null && !entity.getUsers().isEmpty()) { + entity.getUsers().stream().filter(userDatasetProfile -> model.getUsers().stream().filter(userInfoListingModel -> userDatasetProfile.getUser().getId().equals(userInfoListingModel.getId())).count() == 0).forEach(userDatasetProfile -> { + userDatasetProfile.setRole(2); + apiContext.getOperationsContext().getDatabaseRepository().getUserDatasetProfileDao().createOrUpdate(userDatasetProfile); + }); + } + } + + @Transactional + public void retrieveUsers(DatasetProfile entity, eu.eudat.models.data.admin.composite.DatasetProfile model) { + if (entity.getUsers() != null && !entity.getUsers().isEmpty()) { + model.setUsers(entity.getUsers().stream().filter(userDatasetProfile -> userDatasetProfile.getRole() < 2).map(userDatasetProfile -> { + UserInfoListingModel userInfoListingModel = new UserInfoListingModel(); + userInfoListingModel.setId(userDatasetProfile.getUser().getId()); + userInfoListingModel.setName(userDatasetProfile.getUser().getName()); + userInfoListingModel.setEmail(userDatasetProfile.getUser().getEmail()); + userInfoListingModel.setRole(userDatasetProfile.getRole()); + return userInfoListingModel; + }).collect(Collectors.toList())); + } + } + + public long countAlldraft() { + DatasetProfileCriteria criteria = new DatasetProfileCriteria(); + criteria.setStatus(0); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(criteria).count(); + } + + public long countAllFinalized() { + DatasetProfileCriteria criteria = new DatasetProfileCriteria(); + criteria.setStatus(1); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(criteria).count(); + } + + @Transactional + public long countAllUsed() { + DatasetProfileCriteria criteria = new DatasetProfileCriteria(); + criteria.setStatus(1); + criteria.setAllVersions(false); + List datasetProfiles = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(criteria).toList(); + return datasetProfiles.stream().filter(StreamDistinctBy.distinctByKey(DatasetProfile::getId)).filter(datasetProfile -> !datasetProfile.getDataset().isEmpty()).count(); + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/EmailConfirmationManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/EmailConfirmationManager.java index bf26fa3e4..f6ad1d6fc 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/EmailConfirmationManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/EmailConfirmationManager.java @@ -1,7 +1,7 @@ package eu.eudat.logic.managers; import eu.eudat.data.entities.Credential; -import eu.eudat.data.entities.LoginConfirmationEmail; +import eu.eudat.data.entities.EmailConfirmation; import eu.eudat.data.entities.UserInfo; import eu.eudat.data.entities.UserToken; import eu.eudat.exceptions.emailconfirmation.HasConfirmedEmailException; @@ -30,7 +30,7 @@ public class EmailConfirmationManager { } public void confirmEmail(String token) throws TokenExpiredException, HasConfirmedEmailException { - LoginConfirmationEmail loginConfirmationEmail = apiContext.getOperationsContext() + EmailConfirmation loginConfirmationEmail = apiContext.getOperationsContext() .getDatabaseRepository().getLoginConfirmationEmailDao().asQueryable() .where((builder, root) -> builder.equal(root.get("token"), UUID.fromString(token))).getSingle(); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ExternalDatasetManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ExternalDatasetManager.java index 2d8f12fa7..f8f72ce31 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ExternalDatasetManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ExternalDatasetManager.java @@ -57,7 +57,7 @@ public class ExternalDatasetManager { // Fetch external Datasets from external sources. ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(query); - List> remoteRepos = remoteFetcher.getDatasets(externalUrlCriteria, type); + List> remoteRepos = remoteFetcher.getDatasets(externalUrlCriteria, type); // Parse items from external sources to listing models. ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/FunderManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/FunderManager.java index fd4567e03..a14a1e8df 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/FunderManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/FunderManager.java @@ -42,7 +42,7 @@ public class FunderManager { QueryableList authItems = apiContext.getOperationsContext().getDatabaseRepository().getFunderDao().getAuthenticated(items, userInfo); List funders = authItems.select(item -> new eu.eudat.models.data.funder.Funder().fromDataModel(item)); ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(funderCriteria.getCriteria().getLike()); - List> remoteRepos = remoteFetcher.getFunders(externalUrlCriteria); + List> remoteRepos = remoteFetcher.getFunders(externalUrlCriteria); FundersExternalSourcesModel fundersExternalSourcesModel = new FundersExternalSourcesModel().fromExternalItem(remoteRepos); for (ExternalSourcesItemModel externalListingItem : fundersExternalSourcesModel) { eu.eudat.models.data.funder.Funder funder = apiContext.getOperationsContext().getBuilderFactory().getBuilder(FunderBuilder.class) diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/GrantManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/GrantManager.java index 885940a36..e020593ed 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/GrantManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/GrantManager.java @@ -126,7 +126,7 @@ public class GrantManager { QueryableList authItems = apiContext.getOperationsContext().getDatabaseRepository().getGrantDao().getAuthenticated(items, userInfo); List grants = authItems.select(item -> new Grant().fromDataModel(item)); - List> remoteRepos = remoteFetcher.getGrants(externalUrlCriteria); + List> remoteRepos = remoteFetcher.getGrants(externalUrlCriteria); GrantsExternalSourcesModel grantsExternalSourcesModel = new GrantsExternalSourcesModel().fromExternalItem(remoteRepos); for (ExternalSourcesItemModel externalListingItem : grantsExternalSourcesModel) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LicenseManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LicenseManager.java index fe174fe29..da06aedd3 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LicenseManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LicenseManager.java @@ -33,7 +33,7 @@ public class LicenseManager { public List getLicenses(String query, String type) throws HugeResultSet, NoURLFound { ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(query); - List> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getlicenses(externalUrlCriteria, type); + List> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getlicenses(externalUrlCriteria, type); DataRepositoryCriteria criteria = new DataRepositoryCriteria(); if (!query.isEmpty()) criteria.setLike(query); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/MergeEmailConfirmationManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/MergeEmailConfirmationManager.java new file mode 100644 index 000000000..8a30c6acb --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/MergeEmailConfirmationManager.java @@ -0,0 +1,113 @@ +package eu.eudat.logic.managers; + +import eu.eudat.data.entities.Credential; +import eu.eudat.data.entities.EmailConfirmation; +import eu.eudat.data.entities.UserDMP; +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.services.ApiContext; +import eu.eudat.logic.services.operations.DatabaseRepository; +import eu.eudat.models.data.security.Principal; +import eu.eudat.queryable.QueryableList; +import eu.eudat.queryable.jpa.predicates.OrderByPredicate; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import javax.transaction.Transactional; +import java.io.IOException; +import java.util.*; + +@Component +public class MergeEmailConfirmationManager { + private static Logger logger = LoggerFactory.getLogger(MergeEmailConfirmationManager.class); + private ApiContext apiContext; + private DatabaseRepository databaseRepository; + + @Autowired + public MergeEmailConfirmationManager(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."); + + UserInfo userToBeMerged = databaseRepository.getUserInfoDao().asQueryable() + .where((builder, root) -> builder.equal(root.get("id"), loginConfirmationEmail.getUserId())).getSingle(); + + try { + Map map = new ObjectMapper().readValue(loginConfirmationEmail.getData(), HashMap.class); + UUID otherUserId = UUID.fromString((String) map.get("userId")); + UserInfo user = databaseRepository.getUserInfoDao().asQueryable() + .where((builder, root) -> builder.equal(root.get("id"), otherUserId)).getSingle(); + + // Checks if mail is used by another user. If it is, merges the new the old. + mergeNewUserToOld(user, userToBeMerged, Integer.valueOf((String) map.get("provider"))); + expireUserToken(userToBeMerged); + loginConfirmationEmail.setIsConfirmed(true); + databaseRepository.getLoginConfirmationEmailDao().createOrUpdate(loginConfirmationEmail); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + + public void sendConfirmationEmail(String email, Principal principal, UUID userId, Integer provider) throws HasConfirmedEmailException { + UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId()); + + if (user.getEmail() != null && !user.getEmail().equals(email)) { + apiContext.getUtilitiesService().getConfirmationEmailService().createMergeConfirmationEmail( + databaseRepository.getLoginConfirmationEmailDao(), + apiContext.getUtilitiesService().getMailService(), + email, + userId, + principal, + provider + ); + } + } + + @Transactional + private void mergeNewUserToOld(UserInfo newUser, UserInfo oldUser, Integer provider) { + Credential credential = databaseRepository.getCredentialDao().asQueryable().where((builder, root) -> builder.and(builder.equal(root.get("userInfo"), oldUser), builder.equal(root.get("provider"), provider))).getSingle(); + credential.setUserInfo(newUser); + databaseRepository.getCredentialDao().createOrUpdate(credential); + List userDmps = databaseRepository.getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("user"), oldUser)).toList(); + userDmps.forEach(userDmp -> { + userDmp.setUser(newUser); + databaseRepository.getUserDmpDao().createOrUpdate(userDmp); + }); + oldUser.setUserStatus((short)1); + oldUser.setEmail(null); + List credentials = databaseRepository.getCredentialDao().asQueryable().where((builder, root) -> builder.equal(root.get("userInfo"), oldUser)).toList(); + credentials.forEach(cred -> { + if (cred.getId() != credential.getId()) { + databaseRepository.getCredentialDao().delete(cred); + } + }); + databaseRepository.getUserInfoDao().createOrUpdate(oldUser); + } + + private void expireUserToken(UserInfo user) { + UserToken userToken = databaseRepository.getUserTokenDao().asQueryable() + .where((builder, root) -> builder.equal(root.get("user"), user)) + .orderBy((builder, root) -> builder.desc(root.get("issuedAt"))) + .take(1).toList().get(0); + userToken.setExpiresAt(new Date()); + databaseRepository.getUserTokenDao().createOrUpdate(userToken); + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/MetricsManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/MetricsManager.java new file mode 100644 index 000000000..c8c28cfbd --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/MetricsManager.java @@ -0,0 +1,126 @@ +package eu.eudat.logic.managers; + +import io.prometheus.client.Gauge; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.transaction.Transactional; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Component +public class MetricsManager { + private final static Logger logger = LoggerFactory.getLogger(MetricsManager.class); + private static Map gauges = Stream.of( new Object[][]{ + {"argos_managed_dmps", Gauge.build().name("argos_managed_dmps").help("Number of managed DMPs").labelNames("status").register()}, + + {"argos_funders", Gauge.build().name("argos_funders").help("Number of registered Funders").register()}, + {"argos_grants", Gauge.build().name("argos_grants").help("Number of registered Grants").register()}, + {"argos_projects", Gauge.build().name("argos_projects").help("Number of registered Projects").register()}, + {"argos_researchers", Gauge.build().name("argos_researchers").help("Number of Colaborators/Researchers").register()}, + + {"argos_managed_dataset_descriptions", Gauge.build().name("argos_managed_dataset_descriptions").help("Number of managed Dataset Descriptions").labelNames("status").register()}, + + {"argos_dataset_templates", Gauge.build().name("argos_dataset_templates").help("Number of dataset Templates").labelNames("status").register()}, + + {"argos_users", Gauge.build().name("argos_users").help("Number of users").labelNames("type").register()}, + + {"argos_languages", Gauge.build().name("argos_languages").help("Number of Languages").register()}, + + }).collect(Collectors.toMap(data -> (String)data[0], data -> (Gauge) data[1])); + + public static void increaseValue(String name, int amount, String label) { + + if(label != null) { + gauges.get(name).labels(label).inc(amount); + } else { + gauges.get(name).inc(amount); + } + } + + public static void decreaseValue(String name, int amount, String label) { + if(label != null) { + gauges.get(name).labels(label).dec(amount); + } else { + gauges.get(name).dec(amount); + } + } + + public static Integer getValue(String name, String label) { + if(label != null) { + return Double.valueOf(gauges.get(name).labels(label).get()).intValue(); + } else { + return Double.valueOf(gauges.get(name).get()).intValue(); + } + } + + public static void calculateValue(String name, int amount, String label) { + Integer orig = getValue(name, label); + int diff = orig - amount; + if (diff != 0) { + if (diff > 0) { + decreaseValue(name, diff, label); + } else { + increaseValue(name, Math.abs(diff), label); + } + } + } + + private final DatasetManager datasetManager; + private final DataManagementPlanManager dataManagementPlanManager; + private final DatasetProfileManager datasetProfileManager; + private final UserManager userManager; + private final Environment environment; + + @Autowired + public MetricsManager(DatasetManager datasetManager, DataManagementPlanManager dataManagementPlanManager, DatasetProfileManager datasetProfileManager, UserManager userManager, Environment environment) { + this.datasetManager = datasetManager; + this.dataManagementPlanManager = dataManagementPlanManager; + this.datasetProfileManager = datasetProfileManager; + this.userManager = userManager; + this.environment = environment; + } + + @PostConstruct + @Transactional + @Scheduled(initialDelay = 1000 * 60 * 60, fixedDelay = 1000 * 60 * 60) + public void init() throws IOException { + logger.info("Start calculating Metrics"); + calculateValue("argos_managed_dmps", (int) dataManagementPlanManager.countAllDrafts(), "draft"); + calculateValue("argos_managed_dmps", (int) dataManagementPlanManager.countAllFinalized(), "finalized"); + calculateValue("argos_managed_dmps", (int) dataManagementPlanManager.countAllPublished(), "published"); + calculateValue("argos_managed_dmps", (int) dataManagementPlanManager.countAllDoied(), "doied"); + + calculateValue("argos_funders", (int) dataManagementPlanManager.countAllFunders(), null); + calculateValue("argos_grants", (int) dataManagementPlanManager.countAllGrants(), null); + calculateValue("argos_projects", (int) dataManagementPlanManager.countAllProjects(), null); + calculateValue("argos_researchers", (int) dataManagementPlanManager.countAllResearchers(), null); + + calculateValue("argos_managed_dataset_descriptions", (int) datasetManager.countAllDraft(), "draft"); + calculateValue("argos_managed_dataset_descriptions", (int) datasetManager.countAllFinalized(), "finalized"); + calculateValue("argos_managed_dataset_descriptions", (int) datasetManager.countAllPublic(), "published"); + calculateValue("argos_managed_dataset_descriptions", (int) datasetManager.countAllWithDoi(), "doied"); + + calculateValue("argos_dataset_templates", (int) datasetProfileManager.countAlldraft(), "total"); + calculateValue("argos_dataset_templates", (int) datasetProfileManager.countAllFinalized(), "active"); + calculateValue("argos_dataset_templates", (int) datasetProfileManager.countAllUsed(), "used"); + + calculateValue("argos_users", (int) userManager.countActiveUsers().intValue(), "loggedin"); + calculateValue("argos_users", (int) userManager.countAllUsers().intValue(), "total"); + + long files = Files.list(Paths.get(this.environment.getProperty("userguide.path"))).count(); + calculateValue("argos_languages", (int) files, null); + + logger.info("Metrics calculation Completed"); + } + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/OrganisationsManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/OrganisationsManager.java index 0860c3391..238bf9be9 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/OrganisationsManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/OrganisationsManager.java @@ -66,9 +66,35 @@ public class OrganisationsManager { return organisationDataTableData; } + public List getWithExternal(OrganisationsTableRequest organisationsTableRequest, Principal principal) throws Exception { + eu.eudat.data.entities.UserInfo userInfo = new eu.eudat.data.entities.UserInfo(); + userInfo.setId(principal.getId()); + OrganisationDao organisationDao = databaseRepository.getOrganisationDao(); + + QueryableList items = organisationDao.getWithCriteria(organisationsTableRequest.getCriteria()); + QueryableList authItems = organisationDao.getAuthenticated(items, userInfo); + QueryableList pagedItems = PaginationManager.applyPaging(authItems, organisationsTableRequest); + + List org = pagedItems.toList().stream().distinct().map(item -> new Organisation().fromDataModel(item)).collect(Collectors.toList()); + + ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(organisationsTableRequest.getCriteria().getLabelLike()); + List> remoteRepos = apiContext.getOperationsContext().getRemoteFetcher().getOrganisations(externalUrlCriteria, null); + OrganisationsExternalSourcesModel organisationsExternalSourcesModel = new OrganisationsExternalSourcesModel().fromExternalItem(remoteRepos); + for (ExternalSourcesItemModel externalListingItem : organisationsExternalSourcesModel) { + Organisation organisation = apiContext.getOperationsContext().getBuilderFactory().getBuilder(OrganisationBuilder.class) + .name(externalListingItem.getName()) + .reference(externalListingItem.getRemoteId()) + .tag(externalListingItem.getTag()) + .key(externalListingItem.getKey()) + .build(); + org.add(organisation); + } + return org; + } + public List getCriteriaWithExternal(String query, String type) throws HugeResultSet, NoURLFound { ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(query); - List> remoteRepos = apiContext.getOperationsContext().getRemoteFetcher().getOrganisations(externalUrlCriteria, type); + List> remoteRepos = apiContext.getOperationsContext().getRemoteFetcher().getOrganisations(externalUrlCriteria, type); OrganisationsExternalSourcesModel organisationsExternalSourcesModel = new OrganisationsExternalSourcesModel().fromExternalItem(remoteRepos); List organisations = new LinkedList<>(); for (ExternalSourcesItemModel externalListingItem : organisationsExternalSourcesModel) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ProjectManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ProjectManager.java index f02242826..6919a5de5 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ProjectManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ProjectManager.java @@ -41,7 +41,7 @@ public class ProjectManager { QueryableList authItems = apiContext.getOperationsContext().getDatabaseRepository().getProjectDao().getAuthenticated(items, userInfo); List projects = authItems.select(item -> new Project().fromDataModel(item)); ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(projectCriteria.getCriteria().getLike()); - List> remoteRepos = remoteFetcher.getProjects(externalUrlCriteria); + List> remoteRepos = remoteFetcher.getProjects(externalUrlCriteria); ProjectsExternalSourcesModel projectsExternalSourcesModel = new ProjectsExternalSourcesModel().fromExternalItem(remoteRepos); for (ExternalSourcesItemModel externalListingItem : projectsExternalSourcesModel) { eu.eudat.models.data.project.Project project = apiContext.getOperationsContext().getBuilderFactory().getBuilder(ProjectBuilder.class) diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/RegistryManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/RegistryManager.java index c9444e002..9220d6221 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/RegistryManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/RegistryManager.java @@ -39,7 +39,7 @@ public class RegistryManager { public List getRegistries(String query, String type, Principal principal) throws HugeResultSet, NoURLFound { ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(query); - List> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getRegistries(externalUrlCriteria, type); + List> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getRegistries(externalUrlCriteria, type); RegistryCriteria criteria = new RegistryCriteria(); if (!query.isEmpty()) criteria.setLike(query); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ResearcherManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ResearcherManager.java index e7c54eecc..0d9104056 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ResearcherManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ResearcherManager.java @@ -57,7 +57,7 @@ public class ResearcherManager { item.setTag(keyToSourceMap.get(item.getKey())); } ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(researcherCriteriaRequest.getCriteria().getName()); - List> remoteRepos = remoteFetcher.getResearchers(externalUrlCriteria,null); + List> remoteRepos = remoteFetcher.getResearchers(externalUrlCriteria,null); ResearchersExternalSourcesModel researchersExternalSourcesModel = new ResearchersExternalSourcesModel().fromExternalItem(remoteRepos); for (ExternalSourcesItemModel externalListingItem : researchersExternalSourcesModel) { eu.eudat.models.data.dmp.Researcher researcher = apiContext.getOperationsContext().getBuilderFactory().getBuilder(ResearcherBuilder.class) diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ServiceManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ServiceManager.java index 2b724ca27..222aa3675 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ServiceManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ServiceManager.java @@ -36,7 +36,7 @@ public class ServiceManager { public List getServices(String query, String type, Principal principal) throws HugeResultSet, NoURLFound { ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(query); - List> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getServices(externalUrlCriteria, type); + List> remoteRepos = this.apiContext.getOperationsContext().getRemoteFetcher().getServices(externalUrlCriteria, type); ServiceCriteria criteria = new ServiceCriteria(); if (!query.isEmpty()) criteria.setLike(query); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java index 572f8e64b..4dfe09d9a 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/UserManager.java @@ -3,6 +3,7 @@ package eu.eudat.logic.managers; import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.data.dao.criteria.DataManagementPlanCriteria; import eu.eudat.data.dao.entities.UserInfoDao; +import eu.eudat.data.entities.Credential; import eu.eudat.data.entities.DMP; import eu.eudat.data.entities.UserInfo; import eu.eudat.data.entities.UserRole; @@ -26,23 +27,39 @@ import eu.eudat.models.data.helpers.common.DataTableData; import eu.eudat.models.data.login.Credentials; import eu.eudat.models.data.principal.PrincipalModel; import eu.eudat.models.data.security.Principal; +import eu.eudat.models.data.userinfo.UserCredential; import eu.eudat.models.data.userinfo.UserListingModel; import eu.eudat.models.data.userinfo.UserProfile; import eu.eudat.queryable.QueryableList; +import eu.eudat.types.Authorities; + import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.w3c.dom.Document; import org.w3c.dom.Element; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; import java.time.Instant; import java.util.*; import java.util.stream.Collectors; @Component public class UserManager { + private static final Logger logger = LoggerFactory.getLogger(UserManager.class); private ApiContext apiContext; private ZenodoCustomProvider zenodoCustomProvider; @@ -73,6 +90,19 @@ public class UserManager { List modelUsers = pagedUsers.select(item -> new UserListingModel().fromDataModel(item)); return apiContext.getOperationsContext().getBuilderFactory().getBuilder(DataTableDataBuilder.class).totalCount(users.count()).data(modelUsers).build(); } + + public List getCredentials(UUID userId) { + List results = new ArrayList<>(); + eu.eudat.data.entities.UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(userId); + List credentials = apiContext.getOperationsContext().getDatabaseRepository().getCredentialDao().asQueryable().where((builder, root) -> builder.equal(root.get("userInfo"), user)).toList(); + credentials.forEach(credential -> { + UserCredential userCredential = new UserCredential(); + userCredential.setEmail(credential.getEmail()); + userCredential.setProvider(credential.getProvider()); + results.add(userCredential); + }); + return results; + } public UserProfile getSingle(UUID userId) throws Exception { eu.eudat.data.entities.UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(userId); @@ -100,6 +130,8 @@ public class UserManager { apiContext.getOperationsContext().getDatabaseRepository().detachEntity(userInfo); HashMap result = new ObjectMapper().readValue(userInfo.getAdditionalinfo(), HashMap.class); + userInfo.setName(settings.entrySet().stream().filter(entry -> entry.getKey().equals("name")).filter(Objects::nonNull).map(entry -> entry.getValue().toString()).findFirst().orElse(userInfo.getName())); + settings.remove("name"); result.putAll(settings); userInfo.setAdditionalinfo(new JSONObject(result).toString()); apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao() @@ -168,4 +200,57 @@ public class UserManager { settings.put("expirationDate", 0); this.updateSettings(settings, principal); } + + public ResponseEntity exportToCsv(Principal principal) throws IOException { + if (!principal.getAuthz().contains(Authorities.ADMIN)) + throw new UnauthorisedException(); + + List users = this.apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().asQueryable().toList(); + StringBuilder resultBuilder = new StringBuilder(); + resultBuilder.append("User Id").append(",").append("User Name").append(",").append("User Email").append("\n"); + users.stream().forEach(user -> resultBuilder.append(user.getId().toString()).append(",") + .append(user.getName()).append(",") + .append(user.getEmail()).append("\n")); + String result = resultBuilder.toString(); + String fileName = "Users_dump";//dmp.getLabel(); + fileName = fileName.replaceAll("[^a-zA-Z0-9+ ]", ""); + String uuid = UUID.randomUUID().toString(); + File file = new File(this.environment.getProperty("temp.temp") + uuid + ".csv"); + OutputStream output = new FileOutputStream(file); + try { +// mapper.writeValue(file, rdaExportModel); + output.write(result.getBytes()); + output.flush(); + output.close(); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + + InputStream resource = new FileInputStream(file); + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setContentLength(file.length()); + responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); + responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName + ".csv"); + responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition"); + responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type"); + + byte[] content = org.apache.poi.util.IOUtils.toByteArray(resource); + resource.close(); + Files.deleteIfExists(file.toPath()); + return new ResponseEntity<>(content, responseHeaders, HttpStatus.OK); + } + + public UserProfile getFromEmail(String email) { + UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().asQueryable().where((builder, root) -> builder.equal(root.get("email"), email)).getSingle(); + return new UserProfile().fromDataModel(user); + } + + public Long countActiveUsers(){ + return apiContext.getOperationsContext().getDatabaseRepository().getUserTokenDao().asQueryable().where(((builder, root) -> builder.greaterThan(root.get("expiresAt"), new Date()))).count(); + } + + public Long countAllUsers(){ + return apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().asQueryable().count(); + } + } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ValidationManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ValidationManager.java new file mode 100644 index 000000000..566d64bc1 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/ValidationManager.java @@ -0,0 +1,31 @@ +package eu.eudat.logic.managers; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import eu.eudat.logic.proxy.config.ExternalUrlCriteria; +import eu.eudat.logic.proxy.config.exceptions.HugeResultSet; +import eu.eudat.logic.proxy.config.exceptions.NoURLFound; +import eu.eudat.logic.proxy.fetching.RemoteFetcher; +import eu.eudat.models.data.security.Principal; + +@Component +public class ValidationManager { + + private RemoteFetcher remoteFetcher; + + @Autowired + public ValidationManager(RemoteFetcher remoteFetcher) { + super(); + this.remoteFetcher = remoteFetcher; + } + + public Boolean validateIdentifier(String identifier, String type, Principal principal) throws NoURLFound, HugeResultSet { + ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(identifier); + Integer count = this.remoteFetcher.findEntries(externalUrlCriteria, type); + return principal != null && count > 0; + } + + + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrls.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrls.java index cb47186c1..2706681f7 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrls.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrls.java @@ -25,6 +25,7 @@ public class ExternalUrls implements Serializable { /*TagUrls tags;*/ FunderUrls funders; LicenseUrls licenses; + ValidationUrls validations; public RegistryUrls getRegistries() { @@ -143,6 +144,15 @@ public class ExternalUrls implements Serializable { public void setLicenses(LicenseUrls licenses) { this.licenses = licenses; } + + public ValidationUrls getValidations() { + return validations; + } + + @XmlElement(name = "validators") + public void setValidations(ValidationUrls validations) { + this.validations = validations; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/UrlConfiguration.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/UrlConfiguration.java index 762153ad4..74a35ed45 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/UrlConfiguration.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/UrlConfiguration.java @@ -15,6 +15,8 @@ public class UrlConfiguration { private String contentType; private String funderQuery; private String firstpage; + private String requestType = "GET"; + private String requestBody = ""; public String getKey() { return key; @@ -95,4 +97,21 @@ public class UrlConfiguration { public void setFirstpage(String firstpage) { this.firstpage = firstpage; } + + public String getRequestType() { + return requestType; + } + @XmlElement(name = "request") + public void setRequestType(String requestType) { + this.requestType = requestType != null ? requestType : "GET"; + } + public String getRequestBody() { + return requestBody; + } + @XmlElement(name = "requestBody") + public void setRequestBody(String requestBody) { + this.requestBody = requestBody != null ? requestBody : ""; + } + + } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/entities/ValidationUrls.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/entities/ValidationUrls.java new file mode 100644 index 000000000..56c8f4b33 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/entities/ValidationUrls.java @@ -0,0 +1,36 @@ +package eu.eudat.logic.proxy.config.entities; + +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; + +import eu.eudat.logic.proxy.config.FetchStrategy; +import eu.eudat.logic.proxy.config.UrlConfiguration; + +public class ValidationUrls { + + List urls; + FetchStrategy fetchMode; + + public List getUrls() { + return urls; + } + + @XmlElementWrapper + @XmlElement(name = "urlConfig") + public void setUrls(List urls) { + this.urls = urls; + } + + public FetchStrategy getFetchMode() { + return fetchMode; + } + + @XmlElement(name = "fetchMode") + public void setFetchMode(FetchStrategy fetchMode) { + this.fetchMode = fetchMode; + } + + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcher.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcher.java index 2b0b6c2c1..7b1bb2ac9 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcher.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcher.java @@ -1,6 +1,7 @@ package eu.eudat.logic.proxy.fetching; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; @@ -141,6 +142,15 @@ public class RemoteFetcher { FetchStrategy fetchStrategy = configLoader.getExternalUrls().getLicenses().getFetchMode(); return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } + + public Integer findEntries(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet { + List urlConfigs = + key != null && !key.isEmpty() ? configLoader.getExternalUrls().getValidations().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList()) + : configLoader.getExternalUrls().getValidations().getUrls(); + FetchStrategy fetchStrategy = configLoader.getExternalUrls().getValidations().getFetchMode(); + List> data = this.getAll(urlConfigs, fetchStrategy, externalUrlCriteria); + return data.size(); + } private List> getAll(List urlConfigs, FetchStrategy fetchStrategy, ExternalUrlCriteria externalUrlCriteria) throws NoURLFound, HugeResultSet { @@ -154,7 +164,7 @@ public class RemoteFetcher { for (UrlConfiguration urlConfig : urlConfigs) { ifFunderQueryExist(urlConfig, externalUrlCriteria); if (urlConfig.getType() == null || urlConfig.getType().equals("External")) { - results.addAll(getAllResultsFromUrl(urlConfig.getUrl(), fetchStrategy, urlConfig.getData(), urlConfig.getPaginationPath(), externalUrlCriteria, urlConfig.getLabel(), urlConfig.getKey(), urlConfig.getContentType(), urlConfig.getFirstpage())); + results.addAll(getAllResultsFromUrl(urlConfig.getUrl(), fetchStrategy, urlConfig.getData(), urlConfig.getPaginationPath(), externalUrlCriteria, urlConfig.getLabel(), urlConfig.getKey(), urlConfig.getContentType(), urlConfig.getFirstpage(), urlConfig.getRequestBody(), urlConfig.getRequestType())); } else if (urlConfig.getType() != null && urlConfig.getType().equals("Internal")) { results.addAll(getAllResultsFromMockUpJson(urlConfig.getUrl(), externalUrlCriteria.getLike())); } @@ -223,12 +233,13 @@ public class RemoteFetcher { return completedPath; } - private List> getAllResultsFromUrl(String path, FetchStrategy fetchStrategy, final DataUrlConfiguration jsonDataPath, final String jsonPaginationPath, ExternalUrlCriteria externalUrlCriteria, String tag, String key, String contentType, String firstPage) throws HugeResultSet { + private List> getAllResultsFromUrl(String path, FetchStrategy fetchStrategy, final DataUrlConfiguration jsonDataPath, final String jsonPaginationPath, ExternalUrlCriteria externalUrlCriteria, String tag, String key, String contentType, String firstPage, String requestBody, String requestType) throws HugeResultSet { Set pages = new HashSet<>(); String replacedPath = replaceCriteriaOnUrl(path, externalUrlCriteria, firstPage); + String replacedBody = replaceCriteriaOnUrl(requestBody, externalUrlCriteria, firstPage); - Results results = getResultsFromUrl(replacedPath, jsonDataPath, jsonPaginationPath, contentType); + Results results = getResultsFromUrl(replacedPath, jsonDataPath, jsonPaginationPath, contentType, replacedBody, requestType); if (fetchStrategy == FetchStrategy.FIRST) return results == null ? new LinkedList<>() : results.getResults().stream().peek(x -> x.put("tag", tag)).peek(x -> x.put("key", key)).collect(Collectors.toList()); @@ -241,7 +252,7 @@ public class RemoteFetcher { throw new HugeResultSet("The submitted search query " + externalUrlCriteria.getLike() + " is about to return " + results.getPagination().get("count") + " results... Please submit a more detailed search query"); Optional optionalResults = pages.parallelStream() - .map(page -> getResultsFromUrl(path + "&page=" + page, jsonDataPath, jsonPaginationPath, contentType)) + .map(page -> getResultsFromUrl(path + "&page=" + page, jsonDataPath, jsonPaginationPath, contentType, replacedBody, requestType)) .reduce((result1, result2) -> { result1.getResults().addAll(result2.getResults()); return result1; @@ -253,12 +264,12 @@ public class RemoteFetcher { } - private Results getResultsFromUrl(String urlString, DataUrlConfiguration jsonDataPath, String jsonPaginationPath, String contentType) { + private Results getResultsFromUrl(String urlString, DataUrlConfiguration jsonDataPath, String jsonPaginationPath, String contentType, String requestBody, String requestType) { try { RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); - HttpEntity entity; + HttpEntity entity; ResponseEntity response; /* * URL url = new URL(urlString.replaceAll(" ", "%20")); @@ -268,11 +279,12 @@ public class RemoteFetcher { */ if (contentType != null && !contentType.isEmpty()) { headers.setAccept(Collections.singletonList(MediaType.valueOf(contentType))); + headers.setContentType(MediaType.valueOf(contentType)); } - - entity = new HttpEntity<>("parameters", headers); + JsonNode jsonBody = new ObjectMapper().readTree(requestBody); + entity = new HttpEntity<>(jsonBody, headers); - response = restTemplate.exchange(urlString, HttpMethod.GET, entity, String.class); + response = restTemplate.exchange(urlString, HttpMethod.resolve(requestType), entity, String.class); if (response.getStatusCode() == HttpStatus.OK) { // success //do here all the parsing Results results = new Results(); @@ -333,7 +345,7 @@ public class RemoteFetcher { externalUrlCriteria.setPath(result.get("path")); externalUrlCriteria.setHost(result.get("host")); String replacedPath = replaceCriteriaOnUrl(jsonDataPath.getUrlConfiguration().getUrl(), externalUrlCriteria, jsonDataPath.getUrlConfiguration().getFirstpage()); - return getResultsFromUrl(replacedPath, jsonDataPath.getUrlConfiguration().getData(), jsonDataPath.getUrlConfiguration().getData().getPath(), jsonDataPath.getUrlConfiguration().getContentType()); + return getResultsFromUrl(replacedPath, jsonDataPath.getUrlConfiguration().getData(), jsonDataPath.getUrlConfiguration().getData().getPath(), jsonDataPath.getUrlConfiguration().getContentType(), requestBody, requestType); }).filter(Objects::nonNull).map(results1 -> results1.results.get(0)).collect(Collectors.toList()); results = new Results(multiResults, new HashMap<>(1, 1)); } else if (jsonDataPath.getFieldsUrlConfiguration().getTypes() != null) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepository.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepository.java index 63790784e..3866291ad 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepository.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepository.java @@ -36,6 +36,8 @@ public interface DatabaseRepository { ExternalDatasetDao getExternalDatasetDao(); + UserDatasetProfileDao getUserDatasetProfileDao(); + UserDmpDao getUserDmpDao(); ContentDao getContentDao(); @@ -46,7 +48,7 @@ public interface DatabaseRepository { DatasetServiceDao getDatasetServiceDao(); - LoginConfirmationEmailDao getLoginConfirmationEmailDao(); + EmailConfirmationDao getLoginConfirmationEmailDao(); ProjectDao getProjectDao(); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepositoryImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepositoryImpl.java index 5e456a26b..addeabe7c 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepositoryImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/DatabaseRepositoryImpl.java @@ -27,12 +27,13 @@ public class DatabaseRepositoryImpl implements DatabaseRepository { private UserTokenDao userTokenDao; private ExternalDatasetDao externalDatasetDao; private UserRoleDao userRoleDao; + private UserDatasetProfileDao userDatasetProfileDao; private UserDmpDao userDmpDao; private ContentDao contentDao; private DMPProfileDao dmpProfileDao; private DatasetExternalDatasetDao datasetExternalDatasetDao; private DatasetServiceDao datasetServiceDao; - private LoginConfirmationEmailDao loginConfirmationEmailDao; + private EmailConfirmationDao loginConfirmationEmailDao; private ProjectDao projectDao; private FunderDao funderDao; private LockDao lockDao; @@ -247,12 +248,12 @@ public class DatabaseRepositoryImpl implements DatabaseRepository { } @Override - public LoginConfirmationEmailDao getLoginConfirmationEmailDao() { + public EmailConfirmationDao getLoginConfirmationEmailDao() { return loginConfirmationEmailDao; } @Autowired - public void setLoginConfirmationEmailDao(LoginConfirmationEmailDao loginConfirmationEmailDao) { + public void setLoginConfirmationEmailDao(EmailConfirmationDao loginConfirmationEmailDao) { this.loginConfirmationEmailDao = loginConfirmationEmailDao; } @@ -306,6 +307,16 @@ public class DatabaseRepositoryImpl implements DatabaseRepository { this.notificationDao = notificationDao; } + @Override + public UserDatasetProfileDao getUserDatasetProfileDao() { + return userDatasetProfileDao; + } + + @Autowired + public void setUserDatasetProfileDao(UserDatasetProfileDao userDatasetProfileDao) { + this.userDatasetProfileDao = userDatasetProfileDao; + } + public void detachEntity(T entity) { this.entityManager.detach(entity); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/AbstractAuthenticationService.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/AbstractAuthenticationService.java index 50d5e12dd..e9f347342 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/AbstractAuthenticationService.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/operations/authentication/AbstractAuthenticationService.java @@ -54,7 +54,7 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer UserInfo userInfo = this.apiContext.getOperationsContext().getBuilderFactory().getBuilder(UserInfoBuilder.class) .name(username).email(environment.getProperty("autouser.root.email")).created(new Date()) - .lastloggedin(new Date()).authorization_level((short) 1).usertype((short) 1) + .lastloggedin(new Date()).authorization_level((short) 1).usertype((short) 1).userStatus((short)0) .build(); userInfo = this.apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().createOrUpdate(userInfo); @@ -111,14 +111,22 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer public Principal Touch(LoginProviderUser profile) throws NullEmailException { - UserInfo userInfo = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().asQueryable().withHint("userInfo").where((builder, root) -> builder.equal(root.get("email"), profile.getEmail())).getSingleOrDefault(); + UserInfo userInfo;// = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().asQueryable().withHint("userInfo").where((builder, root) -> builder.and(builder.equal(root.get("email"), profile.getEmail()), builder.equal(root.get("userStatus"), 0))).getSingleOrDefault(); - if (userInfo == null) { + //if (userInfo == null) { Optional optionalCredential = Optional.ofNullable(apiContext.getOperationsContext().getDatabaseRepository().getCredentialDao() .asQueryable().withHint("credentialUserInfo") .where((builder, root) -> builder.and(builder.equal(root.get("provider"), profile.getProvider().getValue()), builder.equal(root.get("externalId"), profile.getId()))) .getSingleOrDefault()); userInfo = optionalCredential.map(Credential::getUserInfo).orElse(null); + if (userInfo != null) { + if (userInfo.getUserStatus() == 1) { + userInfo = null; + } + } + //} + if (userInfo == null) { + userInfo = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().asQueryable().withHint("userInfo").where((builder, root) -> builder.and(builder.equal(root.get("email"), profile.getEmail()), builder.equal(root.get("userStatus"), 0))).getSingleOrDefault(); } final Credential credential = this.apiContext.getOperationsContext().getBuilderFactory().getBuilder(CredentialBuilder.class) @@ -129,6 +137,7 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer .provider(profile.getProvider().getValue()) .secret(profile.getSecret()) .externalId(profile.getId()) + .email(profile.getEmail()) .build(); if (userInfo == null) { @@ -140,11 +149,12 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer + "\", \"expirationDate\": \"" + Instant.now().plusSeconds((profile.getZenodoExpire() != null ? profile.getZenodoExpire(): 0)).toEpochMilli() + "\", \"zenodoRefresh\": \"" + profile.getZenodoRefresh() + (profile.getProvider() == TokenValidatorFactoryImpl.LoginProvider.ZENODO ? "\", \"zenodoEmail\": \"" + profile.getEmail() : "") +"\"}}") - .authorization_level((short) 1).usertype((short) 1) + .authorization_level((short) 1).usertype((short) 1).userStatus((short)0) .build(); userInfo = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().createOrUpdate(userInfo); credential.setPublicValue(userInfo.getName()); + credential.setEmail(userInfo.getEmail()); credential.setUserInfo(userInfo); apiContext.getOperationsContext().getDatabaseRepository().getCredentialDao().createOrUpdate(credential); @@ -181,6 +191,7 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer credential.setUserInfo(userInfo); credential.setId(UUID.randomUUID()); credential.setPublicValue(userInfo.getName()); + credential.setEmail(userInfo.getEmail()); apiContext.getOperationsContext().getDatabaseRepository().getCredentialDao().createOrUpdate(credential); userInfo.getCredentials().add(credential); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailService.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailService.java index 831e48b9d..108550ec5 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailService.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailService.java @@ -1,13 +1,18 @@ package eu.eudat.logic.services.utilities; -import eu.eudat.data.dao.entities.LoginConfirmationEmailDao; -import eu.eudat.data.entities.LoginConfirmationEmail; +import eu.eudat.data.dao.entities.EmailConfirmationDao; +import eu.eudat.data.entities.EmailConfirmation; +import eu.eudat.models.data.security.Principal; import java.util.UUID; import java.util.concurrent.CompletableFuture; public interface ConfirmationEmailService { - public void createConfirmationEmail(LoginConfirmationEmailDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId); + public void createConfirmationEmail(EmailConfirmationDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId); + + public void createMergeConfirmationEmail(EmailConfirmationDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId, Principal principal, Integer provider); - public CompletableFuture sentConfirmationEmail(LoginConfirmationEmail confirmationEmail, MailService mailService); + public CompletableFuture sentConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService); + + public CompletableFuture sentMergeConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService, String userName); } \ No newline at end of file diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java index dec804949..61ced9d27 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/ConfirmationEmailServiceImpl.java @@ -1,14 +1,19 @@ package eu.eudat.logic.services.utilities; -import eu.eudat.data.dao.entities.LoginConfirmationEmailDao; -import eu.eudat.data.entities.LoginConfirmationEmail; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.eudat.data.dao.entities.EmailConfirmationDao; +import eu.eudat.data.entities.EmailConfirmation; import eu.eudat.models.data.mail.SimpleMail; +import eu.eudat.models.data.security.Principal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import java.util.Date; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -24,8 +29,8 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { } @Override - public void createConfirmationEmail(LoginConfirmationEmailDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId) { - LoginConfirmationEmail confirmationEmail = new LoginConfirmationEmail(); + public void createConfirmationEmail(EmailConfirmationDao loginConfirmationEmailDao, MailService mailService, String email, UUID userId) { + EmailConfirmation confirmationEmail = new EmailConfirmation(); confirmationEmail.setEmail(email); confirmationEmail.setExpiresAt(Date .from(new Date() @@ -41,7 +46,7 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { } @Override - public CompletableFuture sentConfirmationEmail(LoginConfirmationEmail confirmationEmail, MailService mailService) { + public CompletableFuture sentConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService) { return CompletableFuture.runAsync(() -> { SimpleMail mail = new SimpleMail(); mail.setSubject(environment.getProperty("conf_email.subject")); @@ -64,6 +69,31 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { return content; } + @Override + public CompletableFuture sentMergeConfirmationEmail(EmailConfirmation confirmationEmail, MailService mailService, String userName) { + return CompletableFuture.runAsync(() -> { + SimpleMail mail = new SimpleMail(); + mail.setSubject(environment.getProperty("conf_email.subject")); + mail.setContent(createMergeContent(confirmationEmail.getToken(), mailService, userName)); + mail.setTo(confirmationEmail.getEmail()); + try { + mailService.sendSimpleMail(mail); + } catch (Exception ex) { + logger.error(ex.getMessage(), ex); + } + }); + } + + private String createMergeContent(UUID confirmationToken, MailService mailService, String userName) { + String content = mailService.getMailTemplateContent("classpath:templates/email/emailMergeConfirmation.html"); + content = content.replace("{userName}", userName); + 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; @@ -71,4 +101,31 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { hour = hour / 60; return (hour + ":" + min + ":" + sec); } + + @Override + public void createMergeConfirmationEmail(EmailConfirmationDao loginConfirmationEmailDao, MailService mailService, + String email, UUID userId, Principal principal, Integer provider) { + EmailConfirmation confirmationEmail = new EmailConfirmation(); + confirmationEmail.setEmail(email); + confirmationEmail.setExpiresAt(Date + .from(new Date() + .toInstant() + .plusSeconds(Long.parseLong(this.environment.getProperty("conf_email.expiration_time_seconds"))) + ) + ); + confirmationEmail.setUserId(userId); + try { + Map map = new HashMap<>(); + map.put("userId", principal.getId()); + 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); + sentMergeConfirmationEmail(confirmationEmail, mailService, principal.getName()); + + } } \ No newline at end of file diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/MailServiceImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/MailServiceImpl.java index 2ef6c4720..0a782c6f2 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/MailServiceImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/MailServiceImpl.java @@ -11,10 +11,16 @@ import org.springframework.core.io.Resource; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.stereotype.Service; +import javax.mail.BodyPart; import javax.mail.Message; import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; @Service("mailService") @@ -36,9 +42,34 @@ public class MailServiceImpl implements MailService { @Override public void sendSimpleMail(SimpleMail mail) throws MessagingException { + List imageSources = parseImages(mail.getContent()); + List cids = new ArrayList<>(); + if (!imageSources.isEmpty()) { + for (int i = 0; i < imageSources.size(); i++) { + cids.add(UUID.randomUUID().toString()); + } + mail.setContent(replaceImageSources(mail.getContent(), cids)); + } + MimeMultipart content = new MimeMultipart("related"); + BodyPart messageBodyPart = new MimeBodyPart(); + messageBodyPart.setContent(mail.getContent(), "text/html; charset=UTF-8"); + content.addBodyPart(messageBodyPart); + if (!imageSources.isEmpty()) { + for (int i =0; i < imageSources.size(); i++) { + MimeBodyPart imagePart = new MimeBodyPart(); + try { + imagePart.attachFile(applicationContext.getResource(imageSources.get(i)).getFile()); + imagePart.setContentID("<" + cids.get(i) + ">"); + imagePart.setDisposition(MimeBodyPart.INLINE); + content.addBodyPart(imagePart); + } catch (IOException | MessagingException e) { + logger.error(e.getMessage(), e); + } + } + } MimeMessage message = this.emailSender.createMimeMessage(); message.setSubject(mail.getSubject()); - message.setText(mail.getContent(), "utf-8", "html"); + message.setContent(content); message.addRecipients(Message.RecipientType.TO, mail.getTo()); message.setFrom(env.getProperty("mail.from")); this.emailSender.send(message); @@ -66,4 +97,39 @@ public class MailServiceImpl implements MailService { public String getMailTemplateSubject() { return env.getProperty("mail.subject"); } + + private List parseImages(String content) { + List imagePaths = new ArrayList<>(); + + int lastIndex = 0; + + while (lastIndex != -1) { + lastIndex = content.indexOf("img src=\"", lastIndex); + + if (lastIndex != -1) { + imagePaths.add(content.substring(lastIndex + 9, content.indexOf("\"", lastIndex + 9))); + lastIndex ++; + } + } + + return imagePaths; + } + + private String replaceImageSources(String content, List cids) { + + int lastIndex = 0; + int cidIndex = 0; + + while (lastIndex != -1) { + lastIndex = content.indexOf("img src=\"", lastIndex); + + if (lastIndex != -1) { + content = content.replace(content.substring(lastIndex + 9, content.indexOf("\"", lastIndex + 9)), "cid:" + cids.get(cidIndex)); + lastIndex ++; + cidIndex ++; + } + } + + return content; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/builders/ModelBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/builders/ModelBuilder.java index 5b8bea223..90b1b806e 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/builders/ModelBuilder.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/builders/ModelBuilder.java @@ -94,6 +94,7 @@ public class ModelBuilder { if (type.equals("organizations")) return (FieldData) new OrganizationsData().fromData(data); if (type.equals("datasetIdentifier")) return (FieldData) new DatasetIdentifierData().fromData(data); if (type.equals("currency")) return (FieldData) new CurrencyData().fromData(data); + if (type.equals("validation")) return (FieldData) new ValidationData().fromData(data); return null; } @@ -132,6 +133,7 @@ public class ModelBuilder { if (type.equals("organizations")) return (FieldData) new OrganizationsData().fromData(data); if (type.equals("datasetIdentifier")) return (FieldData) new DatasetIdentifierData().fromData(data); if (type.equals("currency")) return (FieldData) new CurrencyData().fromData(data); + if (type.equals("validation")) return (FieldData) new ValidationData().fromData(data); return null; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java index fcad5a7e4..f13da5c5d 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/WordBuilder.java @@ -1,5 +1,6 @@ package eu.eudat.logic.utilities.documents.word; +import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.logic.services.forms.VisibilityRuleService; @@ -226,9 +227,22 @@ public class WordBuilder { } private String formatter(Field field) throws IOException { + String comboboxType = null; switch (field.getViewStyle().getRenderStyle()) { + case "researchers": + case "projects": + case "organizations": + case "externalDatasets": + case "dataRepositories": + case "registries": + case "services": + case "tags": + case "currency": + comboboxType = "autocomplete"; case "combobox": { - String comboboxType = ((ComboBoxData) field.getData()).getType(); + if (comboboxType == null) { + comboboxType = ((ComboBoxData) field.getData()).getType(); + } if (comboboxType.equals("autocomplete")) { ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); @@ -271,13 +285,14 @@ public class WordBuilder { StringBuilder sb = new StringBuilder(); int index = 0; for (Map map: mapList) { - if (!map.containsKey("label") && !map.containsKey("description")) { + /*if (!map.containsKey("label") && !map.containsKey("description")) { logger.error("Value is missing the \"label\" and the \"description\" attributes"); map.put("label", "unknown Name"); - } + }*/ for (Map.Entry entry : map.entrySet()) { - if (entry.getValue() != null && (entry.getKey().equals("label") || entry.getKey().equals("description"))) { + if (entry.getValue() != null && (entry.getKey().equals("label") || entry.getKey().equals("description") || entry.getKey().equals("name"))) { sb.append(entry.getValue().toString()); + break; } } if (index != mapList.size() - 1) sb.append(", "); @@ -314,6 +329,23 @@ public class WordBuilder { case "datepicker": case "textarea": return field.getValue() != null ? field.getValue().toString(): ""; + case "datasetIdentifier": + case "validation": + Map identifierData; + try { + ObjectMapper mapper = new ObjectMapper(); + identifierData = mapper.readValue(field.getValue().toString(), HashMap.class); + } catch (JsonParseException ex) { + identifierData = new HashMap<>(); + String parsedData = field.getValue().toString().substring(1, field.getValue().toString().length() - 1); + StringTokenizer commaTokens = new StringTokenizer(parsedData, ", "); + while (commaTokens.hasMoreTokens()) { + String token = commaTokens.nextToken(); + StringTokenizer equalTokens = new StringTokenizer(token, "="); + identifierData.put(equalTokens.nextToken(), equalTokens.nextToken()); + } + } + return "id: " + identifierData.get("identifier") + ", Validation Type: " + identifierData.get("type"); } return null; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/ExportXmlBuilderDatasetProfile.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/ExportXmlBuilderDatasetProfile.java index c86294e74..21d1aa402 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/ExportXmlBuilderDatasetProfile.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/ExportXmlBuilderDatasetProfile.java @@ -2,6 +2,7 @@ package eu.eudat.logic.utilities.documents.xml.datasetProfileXml; import eu.eudat.models.data.admin.components.datasetprofile.Page; +import eu.eudat.models.data.components.commons.ViewStyle; import eu.eudat.models.data.components.commons.datafield.*; import eu.eudat.models.data.user.components.datasetprofile.Field; import eu.eudat.models.data.user.components.datasetprofile.FieldSet; @@ -32,6 +33,9 @@ public class ExportXmlBuilderDatasetProfile { // root.appendChild(createPages(datasetProfile.getPages(), datasetProfile.getSections(), xmlDoc)); xmlDoc.appendChild(createPages(datasetProfile.getPages(), datasetProfile.getSections(), xmlDoc)); + Element pages = (Element)xmlDoc.getFirstChild(); + pages.setAttribute("description", datasetProfile.getDescription()); + pages.setAttribute("language", datasetProfile.getLanguage()); String xml = XmlBuilder.generateXml(xmlDoc); writer.write(xml); writer.close(); @@ -199,67 +203,120 @@ public class ExportXmlBuilderDatasetProfile { if (field.getData() != null) { Element dataOut = element.createElement("data"); - if (field.getViewStyle().getRenderStyle().equals("combobox")) { - ComboBoxData comboBoxDataObject = (ComboBoxData) field.getData(); - if (comboBoxDataObject.getType().equals("wordlist")) { - WordListData wordListDataObject = (WordListData) field.getData(); - dataOut.setAttribute("label", wordListDataObject.getLabel()); - dataOut.setAttribute("type", wordListDataObject.getType()); - dataOut.setAttribute("multiList", wordListDataObject.getMultiList().toString()); + ViewStyle.Type viewStyleType = ViewStyle.Type.fromName(field.getViewStyle().getRenderStyle()); + switch (viewStyleType) { + case COMBO_BOX: + ComboBoxData comboBoxDataObject = (ComboBoxData) field.getData(); + if (comboBoxDataObject.getType().equals("wordlist")) { + WordListData wordListDataObject = (WordListData) field.getData(); + dataOut.setAttribute("label", wordListDataObject.getLabel()); + dataOut.setAttribute("type", wordListDataObject.getType()); + dataOut.setAttribute("multiList", wordListDataObject.getMultiList().toString()); + Element options = element.createElement("options"); + wordListDataObject.getOptions().forEach(optionChildFor -> { + Element optionChild = element.createElement("option"); + optionChild.setAttribute("label", optionChildFor.getLabel()); + optionChild.setAttribute("value", optionChildFor.getValue()); + options.appendChild(optionChild); + }); + dataOut.appendChild(options); + } else if (comboBoxDataObject.getType().equals("autocomplete")) { + AutoCompleteData autoCompleteDataObject = (AutoCompleteData) field.getData(); + dataOut.setAttribute("label", autoCompleteDataObject.getLabel()); + dataOut.setAttribute("type", autoCompleteDataObject.getType()); + dataOut.setAttribute("multiAutoComplete", autoCompleteDataObject.getMultiAutoComplete().toString()); + for (AutoCompleteData.AutoCompleteSingleData singleData: autoCompleteDataObject.getAutoCompleteSingleDataList()) { + Element singleItem = element.createElement("autocompleteSingle"); + singleItem.setAttribute("optionsRoot", singleData.getOptionsRoot()); + singleItem.setAttribute("url", singleData.getUrl()); + singleItem.setAttribute("autoCompleteType", Integer.toString(singleData.getAutocompleteType())); + if (singleData.getAutoCompleteOptions() != null) { + Element optionChild = element.createElement("option"); + optionChild.setAttribute("label", singleData.getAutoCompleteOptions().getLabel()); + optionChild.setAttribute("value", singleData.getAutoCompleteOptions().getValue()); + singleItem.appendChild(optionChild); + } + dataOut.appendChild(singleItem); + } + } + break; + case BOOLEAN_DECISION: + BooleanDecisionData booleanDecisionDataObject = (BooleanDecisionData) field.getData(); + dataOut.setAttribute("label", booleanDecisionDataObject.getLabel()); + break; + case RADIO_BOX: + RadioBoxData radioBoxDataObject = (RadioBoxData) field.getData(); + dataOut.setAttribute("label", radioBoxDataObject.getLabel()); + Element options = element.createElement("options"); - wordListDataObject.getOptions().forEach(optionChildFor -> { + radioBoxDataObject.getOptions().forEach(optionChildFor -> { Element optionChild = element.createElement("option"); optionChild.setAttribute("label", optionChildFor.getLabel()); optionChild.setAttribute("value", optionChildFor.getValue()); options.appendChild(optionChild); }); dataOut.appendChild(options); - } else if (comboBoxDataObject.getType().equals("autocomplete")) { - AutoCompleteData autoCompleteDataObject = (AutoCompleteData) field.getData(); - dataOut.setAttribute("label", autoCompleteDataObject.getLabel()); - dataOut.setAttribute("type", autoCompleteDataObject.getType()); - dataOut.setAttribute("multiAutoComplete", autoCompleteDataObject.getMultiAutoComplete().toString()); - for (AutoCompleteData.AutoCompleteSingleData singleData: autoCompleteDataObject.getAutoCompleteSingleDataList()) { - Element singleItem = element.createElement("autocompleteSingle"); - singleItem.setAttribute("optionsRoot", singleData.getOptionsRoot()); - singleItem.setAttribute("url", singleData.getUrl()); - singleItem.setAttribute("autoCompleteType", Integer.toString(singleData.getAutocompleteType())); - if (singleData.getAutoCompleteOptions() != null) { - Element optionChild = element.createElement("option"); - optionChild.setAttribute("label", singleData.getAutoCompleteOptions().getLabel()); - optionChild.setAttribute("value", singleData.getAutoCompleteOptions().getValue()); - singleItem.appendChild(optionChild); - } - dataOut.appendChild(singleItem); + break; + case CHECK_BOX: + case FREE_TEXT: + case TEXT_AREA: + case DATE_PICKER: + case DATASET_IDENTIFIER: + case CURRENCY: + case TAGS: + FieldData fieldDataObject = (FieldData) field.getData(); + dataOut.setAttribute("label", fieldDataObject.getLabel()); + break; + case INTERNAL_DMP_ENTRIES: + InternalDmpEntitiesData internalDmpEntitiesData = (InternalDmpEntitiesData) field.getData(); + dataOut.setAttribute("label", internalDmpEntitiesData.getLabel()); + dataOut.setAttribute("type", internalDmpEntitiesData.getType()); + switch (internalDmpEntitiesData.getType()) { + case "researchers": + ResearchersAutoCompleteData researchersAutoCompleteData = (ResearchersAutoCompleteData) internalDmpEntitiesData; + dataOut.setAttribute("multiAutocomplete", researchersAutoCompleteData.getMultiAutoComplete().toString()); + break; + case "datasets": + DatasetsAutoCompleteData datasetsAutoCompleteData = (DatasetsAutoCompleteData) internalDmpEntitiesData; + dataOut.setAttribute("multiAutocomplete", datasetsAutoCompleteData.getMultiAutoComplete().toString()); + break; + case "dmps": + DMPsAutoCompleteData dmPsAutoCompleteData = (DMPsAutoCompleteData) internalDmpEntitiesData; + dataOut.setAttribute("multiAutocomplete", dmPsAutoCompleteData.getMultiAutoComplete().toString()); + break; } - } - } else if (field.getViewStyle().getRenderStyle().equals("booleanDecision")) { - BooleanDecisionData booleanDecisionDataObject = (BooleanDecisionData) field.getData(); - dataOut.setAttribute("label", booleanDecisionDataObject.getLabel()); - } else if (field.getViewStyle().getRenderStyle().equals("radiobox")) { - RadioBoxData radioBoxDataObject = (RadioBoxData) field.getData(); - dataOut.setAttribute("label", radioBoxDataObject.getLabel()); + break; + case EXTERNAL_DATASETS: + ExternalDatasetsData externalDatasetsData = (ExternalDatasetsData) field.getData(); + dataOut.setAttribute("label", externalDatasetsData.getLabel()); + dataOut.setAttribute("multiAutocomplete", externalDatasetsData.getMultiAutoComplete().toString()); + break; + case DATA_REPOSITORIES: + DataRepositoriesData dataRepositoriesData = (DataRepositoriesData) field.getData(); + dataOut.setAttribute("label", dataRepositoriesData.getLabel()); + dataOut.setAttribute("multiAutocomplete", dataRepositoriesData.getMultiAutoComplete().toString()); + break; + case ORGANIZATIONS: + OrganizationsData organizationsData = (OrganizationsData) field.getData(); + dataOut.setAttribute("label", organizationsData.getLabel()); + dataOut.setAttribute("multiAutocomplete", organizationsData.getMultiAutoComplete().toString()); + break; + case RESEARCHERS: + ResearcherData researcherData = (ResearcherData) field.getData(); + dataOut.setAttribute("label", researcherData.getLabel()); + dataOut.setAttribute("multiAutocomplete", researcherData.getMultiAutoComplete().toString()); + break; + case REGISTRIES: + RegistriesData registriesData = (RegistriesData) field.getData(); + dataOut.setAttribute("label", registriesData.getLabel()); + dataOut.setAttribute("multiAutocomplete", registriesData.getMultiAutoComplete().toString()); + break; + case SERVICES: + ServicesData servicesData = (ServicesData) field.getData(); + dataOut.setAttribute("label", servicesData.getLabel()); + dataOut.setAttribute("multiAutocomplete", servicesData.getMultiAutoComplete().toString()); + break; - Element options = element.createElement("options"); - radioBoxDataObject.getOptions().forEach(optionChildFor -> { - Element optionChild = element.createElement("option"); - optionChild.setAttribute("label", optionChildFor.getLabel()); - optionChild.setAttribute("value", optionChildFor.getValue()); - options.appendChild(optionChild); - }); - dataOut.appendChild(options); - } else if (field.getViewStyle().getRenderStyle().equals("checkBox")) { - CheckBoxData checkBoxDataObject = (CheckBoxData) field.getData(); - dataOut.setAttribute("label", checkBoxDataObject.getLabel()); - } else if (field.getViewStyle().getRenderStyle().equals("freetext")) { - FreeTextData freeTextDataObject = (FreeTextData) field.getData(); - dataOut.setAttribute("label", freeTextDataObject.getLabel()); - } else if (field.getViewStyle().getRenderStyle().equals("textarea")) { - TextAreaData textAreaDataObject = (TextAreaData) field.getData(); - dataOut.setAttribute("label", textAreaDataObject.getLabel()); - } else if (field.getViewStyle().getRenderStyle().equals("datePicker")) { - DatePickerData datePickerDataObject = (DatePickerData) field.getData(); - dataOut.setAttribute("label", datePickerDataObject.getLabel()); } elementField.appendChild(dataOut); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/DatasetProfile.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/DatasetProfile.java index 9230cf96a..cec5df6f1 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/DatasetProfile.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/DatasetProfile.java @@ -1,6 +1,7 @@ package eu.eudat.logic.utilities.documents.xml.datasetProfileXml.datasetProfileModel; +import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.LinkedList; @@ -9,6 +10,9 @@ import java.util.List; @XmlRootElement(name = "pages") public class DatasetProfile { + private String description; + private String language; + private List page; @XmlElement(name = "page") @@ -20,11 +24,30 @@ public class DatasetProfile { this.page = page; } + @XmlAttribute(name = "description") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @XmlAttribute(name = "language") + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + public eu.eudat.models.data.admin.composite.DatasetProfile toAdminCompositeModel(String label){ eu.eudat.models.data.admin.composite.DatasetProfile newDatasetEntityProfile = new eu.eudat.models.data.admin.composite.DatasetProfile(); newDatasetEntityProfile.setLabel(label); newDatasetEntityProfile.setStatus(eu.eudat.data.entities.DatasetProfile.Status.SAVED.getValue()); - + newDatasetEntityProfile.setDescription(description); + newDatasetEntityProfile.setLanguage(language); List pagesDatasetEntity = new LinkedList<>(); List sectionDatasetEntity = new LinkedList<>(); for (Page xmlPage: page) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/Fields/Field.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/Fields/Field.java index 3e829cb6f..a4cce3d02 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/Fields/Field.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/Fields/Field.java @@ -117,7 +117,9 @@ public class Field { fieldEntity.setViewStyle(this.viewStyle.toAdminCompositeModelSection()); FieldData data = new ModelBuilder().toFieldData(null, this.viewStyle.getRenderStyle(), (Element) this.data); // fieldEntity.setData( data.fromXml((Element) this.data)); - fieldEntity.setData( data.toMap((Element)this.data)); + if (data != null) { + fieldEntity.setData(data.toMap((Element) this.data)); + } return fieldEntity; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/admin/composite/DatasetProfile.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/admin/composite/DatasetProfile.java index 4f8ada7e4..cbc23c2f4 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/admin/composite/DatasetProfile.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/admin/composite/DatasetProfile.java @@ -3,7 +3,9 @@ package eu.eudat.models.data.admin.composite; import eu.eudat.models.data.admin.components.datasetprofile.Page; import eu.eudat.models.data.admin.components.datasetprofile.Section; import eu.eudat.logic.utilities.builders.ModelBuilder; +import eu.eudat.models.data.listingmodels.UserInfoListingModel; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -16,6 +18,7 @@ public class DatasetProfile { private Short status; private Short version; private String language; + private List users; public String getLabel() { @@ -61,6 +64,13 @@ public class DatasetProfile { this.language = language; } + public List getUsers() { + return users; + } + public void setUsers(List users) { + this.users = users; + } + public void buildProfile(eu.eudat.models.data.entities.xmlmodels.datasetprofiledefinition.ViewStyleModel viewStyle) { this.sections = new ModelBuilder().fromViewStyleDefinition(viewStyle.getSections(), Section.class); this.pages = new ModelBuilder().fromViewStyleDefinition(viewStyle.getPages(), Page.class); @@ -80,6 +90,7 @@ public class DatasetProfile { shortProfile.setStatus(this.status); shortProfile.setVersion(this.version); shortProfile.setLanguage(this.language); + shortProfile.setUsers(new ArrayList<>()); return shortProfile; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/ViewStyle.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/ViewStyle.java index e5c7aeb06..f7dba3eb2 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/ViewStyle.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/ViewStyle.java @@ -20,4 +20,44 @@ public class ViewStyle { this.cssClass = cssClass; } + public enum Type { + COMBO_BOX("combobox"), + BOOLEAN_DECISION("booleanDecision"), + RADIO_BOX("radiobox"), + INTERNAL_DMP_ENTRIES("internalDmpEntities"), + CHECK_BOX("checkBox"), + FREE_TEXT("freetext"), + TEXT_AREA("textarea"), + DATE_PICKER("datePicker"), + EXTERNAL_DATASETS("externalDatasets"), + DATA_REPOSITORIES("dataRepositories"), + REGISTRIES("registries"), + SERVICES("services"), + TAGS("tags"), + RESEARCHERS("researchers"), + ORGANIZATIONS("organizations"), + DATASET_IDENTIFIER("datasetIdentifier"), + CURRENCY("currency"), + VALIDATION("validation"); + + private final String name; + + Type(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static Type fromName(String name) { + for (Type type: Type.values()) { + if (name.equals(type.getName())) { + return type; + } + } + throw new IllegalArgumentException("View Style Type [" + name + "] is not valid"); + } + } + } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/AutoCompleteData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/AutoCompleteData.java index e4e5fcf94..ca430e294 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/AutoCompleteData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/AutoCompleteData.java @@ -95,14 +95,14 @@ public class AutoCompleteData extends ComboBoxData { this.autoCompleteSingleDataList.add(new AutoCompleteSingleData()); this.mapFromXml(item, this.autoCompleteSingleDataList.get(0)); } - this.multiAutoComplete = Boolean.parseBoolean(item.getAttribute("multiAutoComplete")); + this.multiAutoComplete = Boolean.parseBoolean(item.getAttribute("multiAutocomplete")); return this; } private void mapFromXml(Element item, AutoCompleteSingleData singleData) { singleData.url = item.getAttribute("url"); singleData.optionsRoot = item.getAttribute("optionsRoot"); - this.multiAutoComplete = Boolean.parseBoolean(item.getAttribute("multiAutoComplete")); + this.multiAutoComplete = Boolean.parseBoolean(item.getAttribute("multiAutocomplete")); if (item.getAttribute("autoCompleteType") == null || item.getAttribute("autoCompleteType").equals("") ) { singleData.autocompleteType = AutocompleteType.UNCACHED.getValue(); } else { @@ -173,7 +173,7 @@ public class AutoCompleteData extends ComboBoxData { dataMap.put("label", item != null ? item.getAttribute("label") : ""); //dataMap.put("url", item != null ? item.getAttribute("url") : ""); dataMap.put("type", item != null ? item.getAttribute("type") : "autocomplete"); - dataMap.put("multiAutoComplete", item != null ? Boolean.valueOf(item.getAttribute("multiAutoComplete")) : false); + dataMap.put("multiAutoComplete", item != null ? Boolean.valueOf(item.getAttribute("multiAutocomplete")) : false); List> autoCompletes = new ArrayList<>(); NodeList autoCompleteSingles = item.getChildNodes(); for (int i = 0; i < autoCompleteSingles.getLength(); i++) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/DMPsAutoCompleteData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/DMPsAutoCompleteData.java index cbcfb39e6..025a8c036 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/DMPsAutoCompleteData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/DMPsAutoCompleteData.java @@ -53,6 +53,7 @@ public class DMPsAutoCompleteData extends InternalDmpEntitiesData { + private Boolean multiAutoComplete; + + public Boolean getMultiAutoComplete() { + return multiAutoComplete; + } + + public void setMultiAutoComplete(Boolean multiAutoComplete) { + this.multiAutoComplete = multiAutoComplete; + } + @Override public DataRepositoriesData fromData(Object data) { if (data != null) { this.setLabel((String) ((Map) data).get("label")); + this.setMultiAutoComplete(((Map) data).get("multiAutoComplete") != null && !((Map) data).get("multiAutoComplete").toString().isEmpty()? Boolean.parseBoolean(((Map) data).get("multiAutoComplete").toString()) : false); } return this; } @@ -24,19 +35,24 @@ public class DataRepositoriesData extends FieldData { public Element toXml(Document doc) { Element root = doc.createElement("data"); root.setAttribute("label", this.getLabel()); + if (this.getMultiAutoComplete() != null) { + root.setAttribute("multiAutoComplete", this.getMultiAutoComplete().toString()); + } return root; } @Override public DataRepositoriesData fromXml(Element item) { this.setLabel(item != null ? item.getAttribute("label") : ""); + this.setMultiAutoComplete(Boolean.parseBoolean(item.getAttribute("multiAutoComplete"))); return this; } @Override public Map toMap(Element item) { HashMap dataMap = new HashMap(); - dataMap.put("label", item != null ? item.getAttribute("label") : ""); + dataMap.put("label", item != null && item.getAttributes().getLength() > 0? item.getAttribute("label") : ""); + dataMap.put("multiAutoComplete", item != null && item.getAttributes().getLength() > 0 ? item.getAttribute("multiAutocomplete") : false); return dataMap; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/DatasetsAutoCompleteData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/DatasetsAutoCompleteData.java index 4ca552c92..67eba665f 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/DatasetsAutoCompleteData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/DatasetsAutoCompleteData.java @@ -53,6 +53,7 @@ public class DatasetsAutoCompleteData extends InternalDmpEntitiesData { + private Boolean multiAutoComplete; + + public Boolean getMultiAutoComplete() { + return multiAutoComplete; + } + + public void setMultiAutoComplete(Boolean multiAutoComplete) { + this.multiAutoComplete = multiAutoComplete; + } + @Override public ExternalDatasetsData fromData(Object data) { if (data != null) { this.setLabel((String) ((Map) data).get("label")); + this.setMultiAutoComplete(((Map) data).get("multiAutoComplete") != null && !((Map) data).get("multiAutoComplete").toString().isEmpty()? Boolean.parseBoolean( ((Map) data).get("multiAutoComplete").toString()) : false); } return this; } @@ -24,19 +35,24 @@ public class ExternalDatasetsData extends FieldData { public Element toXml(Document doc) { Element root = doc.createElement("data"); root.setAttribute("label", this.getLabel()); + if (this.getMultiAutoComplete() != null) { + root.setAttribute("multiAutoComplete", this.getMultiAutoComplete().toString()); + } return root; } @Override public ExternalDatasetsData fromXml(Element item) { this.setLabel(item != null ? item.getAttribute("label") : ""); + this.setMultiAutoComplete(Boolean.parseBoolean(item.getAttribute("multiAutoComplete"))); return this; } @Override public Map toMap(Element item) { HashMap dataMap = new HashMap(); - dataMap.put("label", item != null ? item.getAttribute("label") : ""); + dataMap.put("label", item != null && item.getAttributes().getLength() > 0? item.getAttribute("label") : ""); + dataMap.put("multiAutoComplete", item != null && item.getAttributes().getLength() > 0? Boolean.parseBoolean(item.getAttribute("multiAutocomplete")) : false); return dataMap; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/OrganizationsData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/OrganizationsData.java index 5f762b3c3..0ae171e38 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/OrganizationsData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/OrganizationsData.java @@ -7,10 +7,21 @@ import java.util.HashMap; import java.util.Map; public class OrganizationsData extends FieldData { + private Boolean multiAutoComplete; + + public Boolean getMultiAutoComplete() { + return multiAutoComplete; + } + + public void setMultiAutoComplete(Boolean multiAutoComplete) { + this.multiAutoComplete = multiAutoComplete; + } + @Override public OrganizationsData fromData(Object data) { if (data != null) { this.setLabel((String) ((Map) data).get("label")); + this.setMultiAutoComplete(((Map) data).get("multiAutoComplete") != null && !((Map) data).get("multiAutoComplete").toString().isEmpty()? Boolean.parseBoolean( ((Map) data).get("multiAutoComplete").toString()) : false); } return this; } @@ -24,19 +35,24 @@ public class OrganizationsData extends FieldData { public Element toXml(Document doc) { Element root = doc.createElement("data"); root.setAttribute("label", this.getLabel()); + if (this.getMultiAutoComplete() != null) { + root.setAttribute("multiAutoComplete", this.getMultiAutoComplete().toString()); + } return root; } @Override public OrganizationsData fromXml(Element item) { this.setLabel(item != null ? item.getAttribute("label") : ""); + this.setMultiAutoComplete(Boolean.parseBoolean(item.getAttribute("multiAutoComplete"))); return this; } @Override public Map toMap(Element item) { HashMap dataMap = new HashMap(); - dataMap.put("label", item != null ? item.getAttribute("label") : ""); + dataMap.put("label", item != null && item.getAttributes().getLength() > 0? item.getAttribute("label") : ""); + dataMap.put("multiAutoComplete", item != null && item.getAttributes().getLength() > 0? Boolean.parseBoolean(item.getAttribute("multiAutocomplete")) : false); return dataMap; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RadioBoxData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RadioBoxData.java index 66c2205be..5dd1d7166 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RadioBoxData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RadioBoxData.java @@ -118,7 +118,28 @@ public class RadioBoxData extends FieldData { public Map toMap(Element item) { HashMap dataMap = new HashMap(); dataMap.put("label", item != null ? item.getAttribute("label") : ""); - dataMap.put("options", item != null ? item.getAttribute("options") : ""); + Element optionsElement = (Element) item.getElementsByTagName("options").item(0); + List> option =new LinkedList<>(); + + if (optionsElement != null) { + NodeList optionElements = optionsElement.getChildNodes(); + for (int temp = 0; temp < optionElements.getLength(); temp++) { + Node optionElement = optionElements.item(temp); + if (optionElement.getNodeType() == Node.ELEMENT_NODE) { + option.add(optionToMap((Element) optionElement)); + } + } + } + + dataMap.put("options", option != null ? option : null); + return dataMap; + } + + private Map optionToMap(Element item){ + HashMap dataMap = new HashMap(); + dataMap.put("label",item.getAttribute("label")); + dataMap.put("value",item.getAttribute("value")); + return dataMap; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RegistriesData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RegistriesData.java index ed8fca59e..2de478246 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RegistriesData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RegistriesData.java @@ -7,10 +7,21 @@ import java.util.HashMap; import java.util.Map; public class RegistriesData extends FieldData { + private Boolean multiAutoComplete; + + public Boolean getMultiAutoComplete() { + return multiAutoComplete; + } + + public void setMultiAutoComplete(Boolean multiAutoComplete) { + this.multiAutoComplete = multiAutoComplete; + } + @Override public RegistriesData fromData(Object data) { if (data != null) { this.setLabel((String) ((Map) data).get("label")); + this.setMultiAutoComplete(((Map) data).get("multiAutoComplete") != null && !((Map) data).get("multiAutoComplete").toString().isEmpty()?Boolean.parseBoolean( ((Map) data).get("multiAutoComplete").toString()): false); } return this; } @@ -24,19 +35,24 @@ public class RegistriesData extends FieldData { public Element toXml(Document doc) { Element root = doc.createElement("data"); root.setAttribute("label", this.getLabel()); + if (this.getMultiAutoComplete() != null) { + root.setAttribute("multiAutoComplete", this.getMultiAutoComplete().toString()); + } return root; } @Override public RegistriesData fromXml(Element item) { this.setLabel(item != null ? item.getAttribute("label") : ""); + this.setMultiAutoComplete(Boolean.parseBoolean(item.getAttribute("multiAutoComplete"))); return this; } @Override public Map toMap(Element item) { HashMap dataMap = new HashMap(); - dataMap.put("label", item != null ? item.getAttribute("label") : ""); + dataMap.put("label", item != null && item.getAttributes().getLength() > 0? item.getAttribute("label") : ""); + dataMap.put("multiAutoComplete", item != null && item.getAttributes().getLength() > 0? Boolean.parseBoolean(item.getAttribute("multiAutocomplete")) : false); return dataMap; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ResearcherData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ResearcherData.java index efbd2fa45..c76260736 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ResearcherData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ResearcherData.java @@ -7,10 +7,21 @@ import java.util.HashMap; import java.util.Map; public class ResearcherData extends FieldData { + private Boolean multiAutoComplete; + + public Boolean getMultiAutoComplete() { + return multiAutoComplete; + } + + public void setMultiAutoComplete(Boolean multiAutoComplete) { + this.multiAutoComplete = multiAutoComplete; + } + @Override public ResearcherData fromData(Object data) { if (data != null) { this.setLabel((String) ((Map) data).get("label")); + this.setMultiAutoComplete(((Map) data).get("multiAutoComplete") != null && !((Map) data).get("multiAutoComplete").toString().isEmpty()? Boolean.parseBoolean( ((Map) data).get("multiAutoComplete").toString()) : false); } return this; } @@ -24,19 +35,24 @@ public class ResearcherData extends FieldData { public Element toXml(Document doc) { Element root = doc.createElement("data"); root.setAttribute("label", this.getLabel()); + if (this.getMultiAutoComplete() != null) { + root.setAttribute("multiAutoComplete", this.getMultiAutoComplete().toString()); + } return root; } @Override public ResearcherData fromXml(Element item) { this.setLabel(item != null ? item.getAttribute("label") : ""); + this.setMultiAutoComplete(Boolean.parseBoolean(item.getAttribute("multiAutoComplete"))); return this; } @Override public Map toMap(Element item) { HashMap dataMap = new HashMap(); - dataMap.put("label", item != null ? item.getAttribute("label") : ""); + dataMap.put("label", item != null && item.getAttributes().getLength() > 0 ? item.getAttribute("label") : ""); + dataMap.put("multiAutoComplete", item != null && item.getAttributes().getLength() > 0 ? Boolean.parseBoolean(item.getAttribute("multiAutocomplete")) : false); return dataMap; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ResearchersAutoCompleteData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ResearchersAutoCompleteData.java index 74cdff16b..03ddb717f 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ResearchersAutoCompleteData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ResearchersAutoCompleteData.java @@ -19,7 +19,7 @@ public class ResearchersAutoCompleteData extends InternalDmpEntitiesData) data).get("multiAutoComplete"); + this.multiAutoComplete = Boolean.parseBoolean(((Map) data).get("multiAutoComplete").toString()); } return this; @@ -53,7 +53,7 @@ public class ResearchersAutoCompleteData extends InternalDmpEntitiesData { + private Boolean multiAutoComplete; + + public Boolean getMultiAutoComplete() { + return multiAutoComplete; + } + + public void setMultiAutoComplete(Boolean multiAutoComplete) { + this.multiAutoComplete = multiAutoComplete; + } + @Override public ServicesData fromData(Object data) { if (data != null) { this.setLabel((String) ((Map) data).get("label")); + this.setMultiAutoComplete(((Map) data).get("multiAutoComplete") != null && !((Map) data).get("multiAutoComplete").toString().isEmpty()? Boolean.parseBoolean( ((Map) data).get("multiAutoComplete").toString()) : false); } return this; } @@ -24,19 +35,24 @@ public class ServicesData extends FieldData { public Element toXml(Document doc) { Element root = doc.createElement("data"); root.setAttribute("label", this.getLabel()); + if (this.getMultiAutoComplete() != null) { + root.setAttribute("multiAutoComplete", this.getMultiAutoComplete().toString()); + } return root; } @Override public ServicesData fromXml(Element item) { this.setLabel(item != null ? item.getAttribute("label") : ""); + this.setMultiAutoComplete(Boolean.parseBoolean(item.getAttribute("multiAutoComplete"))); return this; } @Override public Map toMap(Element item) { HashMap dataMap = new HashMap(); - dataMap.put("label", item != null ? item.getAttribute("label") : ""); + dataMap.put("label", item != null && item.getAttributes().getLength() > 0? item.getAttribute("label") : ""); + dataMap.put("multiAutoComplete", item != null && item.getAttributes().getLength() > 0? Boolean.parseBoolean(item.getAttribute("multiAutocomplete")) : false); return dataMap; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ValidationData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ValidationData.java new file mode 100644 index 000000000..32d3102e8 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ValidationData.java @@ -0,0 +1,42 @@ +package eu.eudat.models.data.components.commons.datafield; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import java.util.HashMap; +import java.util.Map; + +public class ValidationData extends FieldData { + @Override + public ValidationData fromData(Object data) { + if (data != null) { + this.setLabel((String) ((Map) data).get("label")); + } + return this; + } + + @Override + public Object toData() { + return null; + } + + @Override + public Element toXml(Document doc) { + Element root = doc.createElement("data"); + root.setAttribute("label", this.getLabel()); + return root; + } + + @Override + public ValidationData fromXml(Element item) { + this.setLabel(item != null ? item.getAttribute("label") : ""); + return this; + } + + @Override + public Map toMap(Element item) { + HashMap dataMap = new HashMap(); + dataMap.put("label", item != null ? item.getAttribute("label") : ""); + return dataMap; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/WordListData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/WordListData.java index 51cd7f840..d8fdebca4 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/WordListData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/WordListData.java @@ -79,7 +79,7 @@ public class WordListData extends ComboBoxData { } } Object multiList1 = ((Map) data).get("multiList"); - this.multiList = multiList1 instanceof String ? Boolean.parseBoolean((String) multiList1) : (Boolean) multiList1; + this.multiList = multiList1 != null && (multiList1 instanceof String ? Boolean.parseBoolean((String) multiList1) : (Boolean) multiList1); } return this; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlanEditorModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlanEditorModel.java index 9072842ee..1eca8b81c 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlanEditorModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlanEditorModel.java @@ -295,7 +295,7 @@ public class DataManagementPlanEditorModel implements DataModel, LabelGenerator { @@ -80,7 +81,7 @@ public class Organisation implements DataModel map) { + Organisation model = new Organisation(); + if (map != null) { + model.id = (String) map.get("id"); + model.key = (String) map.get("key"); + model.label = (String) map.get("label"); + model.name = (String) map.get("name"); + model.reference = (String) map.get("reference"); + model.status = (int) map.get("status"); + model.tag = (String) map.get("tag"); + } + return model; + } @Override public String generateLabel() { diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/Researcher.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/Researcher.java index eff13c6b7..d8c205c77 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/Researcher.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/Researcher.java @@ -94,8 +94,8 @@ public class Researcher implements DataModel { @Override - public ExternalDatasetSourcesModel fromExternalItem(List> values) { - for (Map item : values) { + public ExternalDatasetSourcesModel fromExternalItem(List> values) { + for (Map item : values) { ExternalSourcesItemModel model = new ExternalSourcesItemModel(); model.setId((String)item.get("pid")); model.setUri((String)item.get("uri")); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ExternalItem.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ExternalItem.java index 65c05c520..a8fed07a2 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ExternalItem.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ExternalItem.java @@ -5,5 +5,5 @@ import java.util.Map; public interface ExternalItem { - T fromExternalItem(List> values); + T fromExternalItem(List> values); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/FundersExternalSourcesModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/FundersExternalSourcesModel.java index 78a766235..2f78b024b 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/FundersExternalSourcesModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/FundersExternalSourcesModel.java @@ -6,8 +6,8 @@ import java.util.Map; public class FundersExternalSourcesModel extends ExternalListingItem { @Override - public FundersExternalSourcesModel fromExternalItem(List> values) { - for (Map item : values) { + public FundersExternalSourcesModel fromExternalItem(List> values) { + for (Map item : values) { ExternalSourcesItemModel model = new ExternalSourcesItemModel(); model.setRemoteId((String)item.get("pid")); model.setUri((String)item.get("uri")); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/GrantsExternalSourcesModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/GrantsExternalSourcesModel.java index 0a9e442c2..99bd8d448 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/GrantsExternalSourcesModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/GrantsExternalSourcesModel.java @@ -6,8 +6,8 @@ import java.util.Map; public class GrantsExternalSourcesModel extends ExternalListingItem { @Override - public GrantsExternalSourcesModel fromExternalItem(List> values) { - for (Map item : values) { + public GrantsExternalSourcesModel fromExternalItem(List> values) { + for (Map item : values) { ExternalSourcesItemModel model = new ExternalSourcesItemModel(); model.setRemoteId((String)item.get("pid")); model.setUri((String)item.get("uri")); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/LicensesExternalSourcesModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/LicensesExternalSourcesModel.java index 18c3793f4..f2def68c4 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/LicensesExternalSourcesModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/LicensesExternalSourcesModel.java @@ -6,8 +6,8 @@ import java.util.Map; public class LicensesExternalSourcesModel extends ExternalListingItem { @Override - public LicensesExternalSourcesModel fromExternalItem(List> values) { - for (Map item : values) { + public LicensesExternalSourcesModel fromExternalItem(List> values) { + for (Map item : values) { ExternalSourcesItemModel model = new ExternalSourcesItemModel(); model.setId((String)item.get("id")); model.setUri((String)item.get("uri")); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/OrganisationsExternalSourcesModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/OrganisationsExternalSourcesModel.java index 66a94b38f..29743a725 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/OrganisationsExternalSourcesModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/OrganisationsExternalSourcesModel.java @@ -8,8 +8,8 @@ import java.util.Map; @Component public class OrganisationsExternalSourcesModel extends ExternalListingItem { @Override - public OrganisationsExternalSourcesModel fromExternalItem(List> values) { - for (Map item : values) { + public OrganisationsExternalSourcesModel fromExternalItem(List> values) { + for (Map item : values) { ExternalSourcesItemModel model = new ExternalSourcesItemModel(); model.setRemoteId((String)item.get("pid")); model.setUri((String)item.get("uri")); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ProjectsExternalSourcesModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ProjectsExternalSourcesModel.java index 1d83d7939..52de8bc32 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ProjectsExternalSourcesModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ProjectsExternalSourcesModel.java @@ -14,8 +14,8 @@ public class ProjectsExternalSourcesModel extends ExternalListingItem> values) { - for (Map item : values) { + public ProjectsExternalSourcesModel fromExternalItem(List> values) { + for (Map item : values) { ExternalSourcesItemModel model = new ExternalSourcesItemModel(); try { JsonNode node = mapper.readTree(mapper.writeValueAsBytes(item)); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/RegistriesExternalSourcesModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/RegistriesExternalSourcesModel.java index 90f436758..380e59ad7 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/RegistriesExternalSourcesModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/RegistriesExternalSourcesModel.java @@ -6,8 +6,8 @@ import java.util.Map; public class RegistriesExternalSourcesModel extends ExternalListingItem { @Override - public RegistriesExternalSourcesModel fromExternalItem(List> values) { - for (Map item : values) { + public RegistriesExternalSourcesModel fromExternalItem(List> values) { + for (Map item : values) { ExternalSourcesItemModel model = new ExternalSourcesItemModel(); model.setId((String)item.get("pid")); model.setUri((String)item.get("uri")); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ServiceExternalSourcesModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ServiceExternalSourcesModel.java index 316e10f6f..f09013b28 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ServiceExternalSourcesModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/ServiceExternalSourcesModel.java @@ -6,8 +6,8 @@ import java.util.Map; public class ServiceExternalSourcesModel extends ExternalListingItem { @Override - public ServiceExternalSourcesModel fromExternalItem(List> values) { - for (Map item : values) { + public ServiceExternalSourcesModel fromExternalItem(List> values) { + for (Map item : values) { ExternalSourcesItemModel model = new ExternalSourcesItemModel(); model.setId((String)item.get("pid")); model.setUri((String)item.get("label")); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/TagExternalSourcesModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/TagExternalSourcesModel.java index 5e6ce1c97..b23d5afba 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/external/TagExternalSourcesModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/external/TagExternalSourcesModel.java @@ -9,8 +9,8 @@ import java.util.Map; public class TagExternalSourcesModel extends ExternalListingItem { @Override - public TagExternalSourcesModel fromExternalItem(List> values) { - for (Map item : values) { + public TagExternalSourcesModel fromExternalItem(List> values) { + for (Map item : values) { ExternalSourcesItemModel model = new ExternalSourcesItemModel(); model.setId((String)item.get("pid")); model.setUri((String)item.get("label")); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/funder/FunderDMPEditorModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/funder/FunderDMPEditorModel.java index 28738ed61..043cdb53b 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/funder/FunderDMPEditorModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/funder/FunderDMPEditorModel.java @@ -3,6 +3,7 @@ package eu.eudat.models.data.funder; public class FunderDMPEditorModel { private Funder existFunder; private String label; + private String reference; public Funder getExistFunder() { return existFunder; @@ -17,4 +18,12 @@ public class FunderDMPEditorModel { public void setLabel(String label) { this.label = label; } + + public String getReference() { + return reference; + } + + public void setReference(String reference) { + this.reference = reference; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/grant/GrantDMPEditorModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/grant/GrantDMPEditorModel.java index c8cb623d7..1a165d61e 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/grant/GrantDMPEditorModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/grant/GrantDMPEditorModel.java @@ -4,6 +4,7 @@ public class GrantDMPEditorModel { private Grant existGrant; private String label; private String description; + private String reference; public Grant getExistGrant() { return existGrant; @@ -25,4 +26,12 @@ public class GrantDMPEditorModel { public void setDescription(String description) { this.description = description; } + + public String getReference() { + return reference; + } + + public void setReference(String reference) { + this.reference = reference; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/project/ProjectDMPEditorModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/project/ProjectDMPEditorModel.java index d36b73594..c7ffbac35 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/project/ProjectDMPEditorModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/project/ProjectDMPEditorModel.java @@ -4,6 +4,7 @@ public class ProjectDMPEditorModel { private Project existProject; private String label; private String description; + private String reference; public Project getExistProject() { return existProject; @@ -25,4 +26,12 @@ public class ProjectDMPEditorModel { public void setDescription(String description) { this.description = description; } + + public String getReference() { + return reference; + } + + public void setReference(String reference) { + this.reference = reference; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/researcher/Researcher.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/researcher/Researcher.java index 8ecc23211..f84c204bf 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/researcher/Researcher.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/researcher/Researcher.java @@ -40,7 +40,7 @@ public class Researcher implements DataModel sections; private List rules; private List pages; private int status; + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + public List
getSections() { return sections; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserCredential.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserCredential.java new file mode 100644 index 000000000..e2ce326f8 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserCredential.java @@ -0,0 +1,19 @@ +package eu.eudat.models.data.userinfo; + +public class UserCredential { + private String email; + private Integer provider; + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + public Integer getProvider() { + return provider; + } + public void setProvider(Integer provider) { + this.provider = provider; + } + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserListingModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserListingModel.java index 40bbd24f1..05265ea22 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserListingModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserListingModel.java @@ -1,14 +1,18 @@ package eu.eudat.models.data.userinfo; +import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.data.entities.UserInfo; -import eu.eudat.data.entities.UserRole; import eu.eudat.models.DataModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.*; import java.util.stream.Collectors; public class UserListingModel implements DataModel { + private static final Logger logger = LoggerFactory.getLogger(UserListingModel.class); private UUID id; private String email; @@ -19,6 +23,7 @@ public class UserListingModel implements DataModel appRoles; + private String avatarUrl; public UUID getId() { return id; @@ -83,7 +88,13 @@ public class UserListingModel implements DataModel item.getRole()).collect(Collectors.toList()); + if (entity.getAdditionalinfo() != null) { + try { + Map additionalInfo = new ObjectMapper().readValue(entity.getAdditionalinfo(), HashMap.class); + this.avatarUrl = (String) additionalInfo.get("avatarUrl"); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } return this; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserMergeRequestModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserMergeRequestModel.java new file mode 100644 index 000000000..5a60b5b2b --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserMergeRequestModel.java @@ -0,0 +1,29 @@ +package eu.eudat.models.data.userinfo; + +import java.util.UUID; + +public class UserMergeRequestModel { + private UUID userId; + private String email; + private Integer provider; + public UUID getUserId() { + return userId; + } + public void setUserId(UUID userId) { + this.userId = userId; + } + public String getEmail() { + return email; + } + public void setEmail(String email) { + this.email = email; + } + + public Integer getProvider() { + return provider; + } + + public void setProvider(Integer provider) { + this.provider = provider; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserProfile.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserProfile.java index 05411a72f..28bea2de8 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserProfile.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/userinfo/UserProfile.java @@ -6,6 +6,7 @@ import eu.eudat.data.entities.*; import eu.eudat.data.entities.UserInfo; import eu.eudat.models.DataModel; import eu.eudat.models.data.dmp.DataManagementPlan; +import eu.eudat.models.data.dmp.Organisation; import net.minidev.json.parser.JSONParser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,6 +33,8 @@ public class UserProfile implements DataModel culture; private String avatarUrl; + private Organisation organization; + private String roleOrganization; public UUID getId() { return id; @@ -128,8 +131,26 @@ public class UserProfile implements DataModel) additionalInfo.get("organization")); + this.roleOrganization = (String) additionalInfo.get("roleOrganization"); } catch (IOException e) { logger.error(e.getMessage(), e); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ContactRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ContactRDAMapper.java index 957fef8d4..45d1248ec 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ContactRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ContactRDAMapper.java @@ -8,7 +8,13 @@ public class ContactRDAMapper { public static Contact toRDA(UserInfo creator) { Contact rda = new Contact(); + if (creator.getName() == null) { + throw new IllegalArgumentException("Contact Name is missing"); + } rda.setName(creator.getName()); + if (creator.getEmail() == null) { + throw new IllegalArgumentException("Contact Email is missing"); + } rda.setMbox(creator.getEmail()); rda.setContactId(ContactIdRDAMapper.toRDA(creator.getId())); return rda; diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ContributorRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ContributorRDAMapper.java index c95c5cd25..2c2f1bcf0 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ContributorRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ContributorRDAMapper.java @@ -17,6 +17,9 @@ public class ContributorRDAMapper { public static Contributor toRDA(UserDMP userDMP) { Contributor rda = new Contributor(); rda.setContributorId(ContributorIdRDAMapper.toRDA(userDMP.getUser().getId())); + if (userDMP.getUser().getName() == null) { + throw new IllegalArgumentException("Contributor Name is missing"); + } rda.setName(userDMP.getUser().getName()); rda.setMbox(userDMP.getUser().getEmail()); rda.setRole(new HashSet<>(Arrays.asList(UserDMP.UserDMPRoles.fromInteger(userDMP.getRole()).name()))); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/CostRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/CostRDAMapper.java new file mode 100644 index 000000000..3d93d0768 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/CostRDAMapper.java @@ -0,0 +1,22 @@ +package eu.eudat.models.rda.mapper; + +import java.util.Map; + +import eu.eudat.models.rda.Cost; + +public class CostRDAMapper { + + public static Cost toRDA(Map cost) { + Cost rda = new Cost(); + Map code = new org.json.JSONObject((String) cost.get("code")).toMap(); + rda.setCurrencyCode(Cost.CurrencyCode.fromValue((String) code.get("value"))); + rda.setDescription((String) cost.get("description")); + if (cost.get("title") == null) { + throw new IllegalArgumentException("Cost Title is missing"); + } + rda.setTitle((String) cost.get("title")); + rda.setValue(((Integer) cost.get("value")).doubleValue()); + return rda; + } + +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DatasetRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DatasetRDAMapper.java index a328e70bc..d5161094d 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DatasetRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DatasetRDAMapper.java @@ -42,6 +42,9 @@ public class DatasetRDAMapper { public Dataset toRDA(eu.eudat.data.entities.Dataset dataset, List contributors) { Dataset rda = new Dataset(); // rda.setDatasetId(DatasetIdRDAMapper.toRDA(dataset.getId())); + if (dataset.getLabel() == null) { + throw new IllegalArgumentException("Dataset Label is missing"); + } rda.setTitle(dataset.getLabel()); rda.setDescription(dataset.getDescription()); rda.setAdditionalProperty("template", dataset.getProfile().getId()); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DistributionRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DistributionRDAMapper.java index 016475767..c26f8df6a 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DistributionRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DistributionRDAMapper.java @@ -246,7 +246,14 @@ public class DistributionRDAMapper { }*/ } + + if (rda.getTitle() == null) { + throw new IllegalArgumentException("Distribution title is missing"); + } + if (rda.getDataAccess() == null) { + throw new IllegalArgumentException("Distribution Data Access is missing"); + } return rda; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DmpRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DmpRDAMapper.java index d870377ab..5bd15e34d 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DmpRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/DmpRDAMapper.java @@ -31,6 +31,9 @@ public class DmpRDAMapper { @Transactional public Dmp toRDA(DMP dmp) { + if (dmp.getDataset() == null || dmp.getDataset().isEmpty()) { + throw new IllegalArgumentException("DMP has no Datasets"); + } Map extraProperties; if (dmp.getExtraProperties() == null) { throw new IllegalArgumentException("DMP is missing required Data for RDA export"); @@ -49,6 +52,15 @@ public class DmpRDAMapper { } else { rda.setDmpId(DmpIdRDAMapper.toRDA(dmp.getId())); } + if (dmp.getCreated() == null) { + throw new IllegalArgumentException("DMP Created is missing"); + } + if (dmp.getModified() == null) { + throw new IllegalArgumentException("DMP Modified is missing"); + } + if (dmp.getLabel() == null) { + throw new IllegalArgumentException("DMP Label is missing"); + } rda.setCreated(dmp.getCreated()); rda.setDescription(dmp.getDescription()); rda.setModified(dmp.getModified()); @@ -64,13 +76,7 @@ public class DmpRDAMapper { if (extraProperties.get("costs") != null) { rda.setCost(new ArrayList<>()); ((List) extraProperties.get("costs")).forEach(costl -> { - Cost cost = new Cost(); - Map code = new org.json.JSONObject((String) ((Map) costl).get("code")).toMap(); - cost.setCurrencyCode(Cost.CurrencyCode.fromValue((String) code.get("value"))); - cost.setDescription((String) ((Map) costl).get("description")); - cost.setTitle((String) ((Map) costl).get("title")); - cost.setValue(((Integer) ((Map) costl).get("value")).doubleValue()); - rda.getCost().add(cost); + rda.getCost().add(CostRDAMapper.toRDA((Map)costl)); }); } UserInfo contact = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(UUID.fromString((String) extraProperties.get("contact"))); @@ -102,7 +108,7 @@ public class DmpRDAMapper { entity.setDoi(rda.getDmpId().getIdentifier()); } if (((List) rda.getAdditionalProperties().get("templates")) != null && !((List) rda.getAdditionalProperties().get("templates")).isEmpty()) { - entity.setAssociatedDmps(((List) rda.getAdditionalProperties().get("templates")).stream().map(this::getProfile).collect(Collectors.toSet())); + entity.setAssociatedDmps(((List) rda.getAdditionalProperties().get("templates")).stream().map(this::getProfile).filter(Objects::nonNull).collect(Collectors.toSet())); } if (entity.getAssociatedDmps() == null) { entity.setAssociatedDmps(new HashSet<>()); @@ -134,6 +140,6 @@ public class DmpRDAMapper { } private DatasetProfile getProfile(String id) { - return apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(UUID.fromString(id)); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().asQueryable().where(((builder, root) -> builder.equal(root.get("id"), UUID.fromString(id)))).getSingleOrDefault(); } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/HostRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/HostRDAMapper.java index cd66b8ffc..d96aa3ca3 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/HostRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/HostRDAMapper.java @@ -76,6 +76,14 @@ public class HostRDAMapper { } } } + + if (rda.getTitle() == null) { + throw new IllegalArgumentException("Host Title is missing"); + } + + if (rda.getUrl() == null) { + throw new IllegalArgumentException("Host Url is missing"); + } return rda; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/LicenseRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/LicenseRDAMapper.java index ee7cc78f5..108f5eecb 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/LicenseRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/LicenseRDAMapper.java @@ -38,6 +38,14 @@ public class LicenseRDAMapper { rda.setAdditionalProperty("start_dateId", node.get("id").asText()); }*/ } + + if (rda.getLicenseRef() == null) { + throw new IllegalArgumentException("Licence Reference is missing"); + } + + if (rda.getStartDate() == null) { + throw new IllegalArgumentException("License Start Date is missing"); + } return rda; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ProjectRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ProjectRDAMapper.java index b21a9403c..ba3f90383 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ProjectRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/ProjectRDAMapper.java @@ -20,6 +20,10 @@ public class ProjectRDAMapper { rda.setEnd(project.getEnddate().toString()); } rda.setFunding(Collections.singletonList(FundingRDAMapper.toRDA(grant))); + + if (rda.getTitle() == null) { + throw new IllegalArgumentException("Project Title is missing"); + } return rda; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/SecurityAndPrivacyRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/SecurityAndPrivacyRDAMapper.java index 3b5d28649..6006910a1 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/SecurityAndPrivacyRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/SecurityAndPrivacyRDAMapper.java @@ -69,9 +69,14 @@ public class SecurityAndPrivacyRDAMapper { if (rdaProperty.contains("description")) { rda.setDescription(value); - } else if (rdaProperty.contains("title")) { + } + if (rdaProperty.contains("title")) { rda.setTitle(value); } + + if (rda.getTitle() == null) { + throw new IllegalArgumentException("Security And Privacy Title is missing"); + } return rda; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/TechnicalResourceRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/TechnicalResourceRDAMapper.java index cd764c302..5214a7518 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/TechnicalResourceRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/TechnicalResourceRDAMapper.java @@ -70,9 +70,14 @@ public class TechnicalResourceRDAMapper { if (rdaProperty.contains("description")) { rda.setDescription(value); - } else if (rdaProperty.contains("name")) { + } + if (rdaProperty.contains("name")) { rda.setName(value); } + + if (rda.getName() == null) { + throw new IllegalArgumentException("Technical Resources Name is missing"); + } return rda; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/types/Authorities.java b/dmp-backend/web/src/main/java/eu/eudat/types/Authorities.java index 129db641a..55515354f 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/types/Authorities.java +++ b/dmp-backend/web/src/main/java/eu/eudat/types/Authorities.java @@ -5,7 +5,7 @@ import java.util.List; public enum Authorities { - USER(0), MANAGER(1), ADMIN(2), ANONYMOUS(99); + USER(0), MANAGER(1), ADMIN(2), DATASET_PROFILE_MANAGER(3), ANONYMOUS(99); private Integer value; @@ -25,6 +25,8 @@ public enum Authorities { return MANAGER; case 2: return ADMIN; + case 3: + return DATASET_PROFILE_MANAGER; case 99: return ANONYMOUS; default: @@ -33,6 +35,6 @@ public enum Authorities { } public static List all() { - return Arrays.asList(USER, ADMIN, MANAGER); + return Arrays.asList(USER, ADMIN, MANAGER, DATASET_PROFILE_MANAGER); } } diff --git a/dmp-backend/web/src/main/resources/config/application-devel.properties b/dmp-backend/web/src/main/resources/config/application-devel.properties index 3f5eb5dfa..599d404c3 100644 --- a/dmp-backend/web/src/main/resources/config/application-devel.properties +++ b/dmp-backend/web/src/main/resources/config/application-devel.properties @@ -4,12 +4,16 @@ dmp.domain = http://localhost:4200 database.url= database.username= database.password= +spring.datasource.maxIdle=10 +spring.datasource.minIdle=5 +spring.datasource.maxActive=10 ####################ELASTIIC SEARCH TAGS OVERRIDES CONFIGURATIONS########## elasticsearch.host = localhost elasticsearch.port = 9200 elasticsearch.username=elastic elasticsearch.password= +elasticsearch.index=dmps ####################ELK OVERRIDES CONFIGURATIONS########## http-logger.server-address = http://localhost:31311 @@ -80,4 +84,7 @@ contact_email.mail= language.path=dmp-frontend/src/assets/i18n/ #############LOGGING######### -logging.config=classpath:logging/logback-${spring.profiles.active}.xml \ No newline at end of file +logging.config=classpath:logging/logback-${spring.profiles.active}.xml + +#############PROMETHEUS######### +endpoints.prometheus.sensitive: false \ No newline at end of file diff --git a/dmp-backend/web/src/main/resources/config/application-production.properties b/dmp-backend/web/src/main/resources/config/application-production.properties index 83da1fe71..157ba1748 100644 --- a/dmp-backend/web/src/main/resources/config/application-production.properties +++ b/dmp-backend/web/src/main/resources/config/application-production.properties @@ -10,6 +10,7 @@ elasticsearch.host = tags-elastic-search elasticsearch.port = 9200 elasticsearch.username=elastic elasticsearch.password= +elasticsearch.index=dmps ####################PDF OVERRIDES CONFIGURATIONS########## pdf.converter.url=http://docsbox-web/ diff --git a/dmp-backend/web/src/main/resources/config/application-staging.properties b/dmp-backend/web/src/main/resources/config/application-staging.properties index b267bc811..afadeb087 100644 --- a/dmp-backend/web/src/main/resources/config/application-staging.properties +++ b/dmp-backend/web/src/main/resources/config/application-staging.properties @@ -10,6 +10,7 @@ elasticsearch.host = tags-elastic-search elasticsearch.port = 9200 elasticsearch.username=elastic elasticsearch.password= +elasticsearch.index=dmps ####################PDF OVERRIDES CONFIGURATIONS########## pdf.converter.url=http://docsbox-web/ diff --git a/dmp-backend/web/src/main/resources/config/application.properties b/dmp-backend/web/src/main/resources/config/application.properties index 632e533d8..34fd880db 100644 --- a/dmp-backend/web/src/main/resources/config/application.properties +++ b/dmp-backend/web/src/main/resources/config/application.properties @@ -89,4 +89,8 @@ notification.modifiedFinalised.subject=[OpenDMP] The {name} has been modified an logging.config=classpath:logging/logback-${spring.profiles.active}.xml #############TEMP######### -temp.temp=tmp/ \ No newline at end of file +temp.temp=tmp/ + +#############ZENODO######### +zenodo.affiliation=ARGOS +zenodo.community=argos \ No newline at end of file diff --git a/dmp-backend/web/src/main/resources/documents/h2020.docx b/dmp-backend/web/src/main/resources/documents/h2020.docx index 2f98459a0..0e61887c4 100644 Binary files a/dmp-backend/web/src/main/resources/documents/h2020.docx and b/dmp-backend/web/src/main/resources/documents/h2020.docx differ diff --git a/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml b/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml index 5c128c86d..74f4f8726 100644 --- a/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml +++ b/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml @@ -532,6 +532,26 @@ $['meta']['pagination']['page','pages','count'] + + openairealt2 + + 1 + External + POST + https://services.openaire.eu/openaire/ds/searchregistered/{page}/{pageSize}?requestSortBy=id&order=ASCENDING + 0 + application/json + {"officialname": "{like}"} + + $['datasourceInfo'][*] + + 'id' + 'officialname' + 'count' + + + $['meta']['pagination']['page','pages','count'] + + + + + zenodo + + 1 + External + https://zenodo.org/api/records/?page={page}&size={pageSize}&q="{like}" + 1 + application/json + + $[*] + + 'conceptrecid' + 'conceptdoi' + 'doi' + 'created' + + + $['hits']['total'] + + + FIRST + + diff --git a/dmp-backend/web/src/main/resources/externalUrls/ExternalUrlsStaging.xml b/dmp-backend/web/src/main/resources/externalUrls/ExternalUrlsStaging.xml index d0a7ff92e..14c945367 100644 --- a/dmp-backend/web/src/main/resources/externalUrls/ExternalUrlsStaging.xml +++ b/dmp-backend/web/src/main/resources/externalUrls/ExternalUrlsStaging.xml @@ -311,6 +311,31 @@ FIRST + + + + zenodo + + 1 + External + https://zenodo.org/api/records/?page={page}&size={pageSize}&q="{like}" + 1 + application/json + + $[*] + + 'conceptrecid' + 'conceptdoi' + 'doi' + 'created' + + + $['hits']['total'] + + + FIRST + + diff --git a/dmp-backend/web/src/main/resources/templates/email/emailConfirmation.html b/dmp-backend/web/src/main/resources/templates/email/emailConfirmation.html index aca51690c..a83e48013 100644 --- a/dmp-backend/web/src/main/resources/templates/email/emailConfirmation.html +++ b/dmp-backend/web/src/main/resources/templates/email/emailConfirmation.html @@ -260,7 +260,7 @@
- OpenDMP + OpenDMP

Thank you for joining OpenDMP!

Please confirm that your email address is correct to continue.
The link will expire in {expiration_time}.

diff --git a/dmp-backend/web/src/main/resources/templates/email/emailMergeConfirmation.html b/dmp-backend/web/src/main/resources/templates/email/emailMergeConfirmation.html new file mode 100644 index 000000000..6d5fea269 --- /dev/null +++ b/dmp-backend/web/src/main/resources/templates/email/emailMergeConfirmation.html @@ -0,0 +1,304 @@ + + + + + + Simple Transactional Email + + + + + + + + + +
  +
+ + + This is preheader text. Some clients will show this text as a preview. + + + + + + + + +
+ + + + +
+ OpenDMP +

User {userName} have sent you a merge Request.

+

Please confirm that you want to merge your {host} account with that account. +
The link will expire in {expiration_time}.

+ + + + + + +
+ + + + + + +
Confirm Merge Request
+
+
+
+ + + + + + +
+
 
+ + \ No newline at end of file diff --git a/dmp-db-scema/createDB/createdb.sh b/dmp-db-scema/createDB/createdb.sh index fced0c832..747cdc5df 100644 --- a/dmp-db-scema/createDB/createdb.sh +++ b/dmp-db-scema/createDB/createdb.sh @@ -2,4 +2,4 @@ cd .. source Docker/dmp-db.env export $(cut -d= -f1 Docker/dmp-db.env) psql -d postgres -U postgres -w --set=POSTGRES_USER="$POSTGRES_USER" --set=POSTGRES_PASSWORD="$POSTGRES_PASSWORD" --set=POSTGRES_DB="$POSTGRES_DB" -f main/createDatabase.sql -./initdb.sh \ No newline at end of file +./initdb.sh diff --git a/dmp-db-scema/main/data-dump.sql b/dmp-db-scema/main/data-dump.sql index 277d2ac09..a6b354637 100644 --- a/dmp-db-scema/main/data-dump.sql +++ b/dmp-db-scema/main/data-dump.sql @@ -1,4 +1,4 @@ -INSERT INTO public."UserInfo"(email, authorization_level, usertype, name, created, additionalinfo) VALUES ('fake@email.org', 1, 1, :'ADMIN_USERNAME', now(), '{}'); +INSERT INTO public."UserInfo"(email, authorization_level, usertype, userstatus, name, created, additionalinfo) VALUES ('fake@email.org', 1, 1, 0, :'ADMIN_USERNAME', now(), '{}'); INSERT INTO public."Credential" VALUES (uuid_generate_v4(), 0, 5, :'ADMIN_USERNAME', :'ADMIN_PASSWORD', now(), now(), (SELECT public."UserInfo"."id" FROM public."UserInfo" WHERE name = :'ADMIN_USERNAME'), 'dmp'); @@ -20,4 +20,4 @@ UPDATE public."DMP" UPDATE public."DatasetProfile" SET "Language"='en'; -INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.00.005', '2020-06-03 11:40:00.000000+03', now(), 'Remove user association table'); \ No newline at end of file +INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.00.007', '2020-10-27 13:40:00.000000+03', now(), 'Add userstatus on UserInfo table'); \ No newline at end of file diff --git a/dmp-db-scema/main/dmp-dump.sql b/dmp-db-scema/main/dmp-dump.sql index 5355516e4..6da64c270 100644 --- a/dmp-db-scema/main/dmp-dump.sql +++ b/dmp-db-scema/main/dmp-dump.sql @@ -59,6 +59,7 @@ CREATE TABLE public."Credential" ( "Status" numeric NOT NULL, "Provider" numeric NOT NULL, "Public" character varying NOT NULL, + "Email" character varying, "Secret" character varying NOT NULL, "CreationTime" timestamp(4) with time zone NOT NULL, "LastUpdateTime" timestamp(4) with time zone NOT NULL, @@ -571,13 +572,14 @@ ALTER TABLE public."Lock" OWNER TO :POSTGRES_USER; -- Name: LoginConfirmationEmail; Type: TABLE; Schema: public; Owner: :POSTGRES_USER -- -CREATE TABLE public."LoginConfirmationEmail" ( +CREATE TABLE public."EmailConfirmation" ( "ID" uuid NOT NULL, email character varying NOT NULL, "isConfirmed" boolean NOT NULL, token uuid NOT NULL, "userId" uuid NOT NULL, - "expiresAt" timestamp(4) with time zone NOT NULL + "expiresAt" timestamp(4) with time zone NOT NULL, + data text ); @@ -800,6 +802,19 @@ CREATE TABLE public."Service" ( ALTER TABLE public."Service" OWNER TO :POSTGRES_USER; +-- +-- Name: UserDatasetProfile; Type: TABLE; Schema: public; Owner: :POSTGRES_USER +-- + +CREATE TABLE public."UserDatasetProfile" ( + id uuid NOT NULL, + "usr" uuid NOT NULL, + "datasetProfile" uuid NOT NULL, + role integer +); + +ALTER TABLE public."UserDatasetProfile" OWNER TO :POSTGRES_USER; + -- -- Name: UserDMP; Type: TABLE; Schema: public; Owner: :POSTGRES_USER -- @@ -823,6 +838,7 @@ CREATE TABLE public."UserInfo" ( email character varying(250), authorization_level smallint NOT NULL, usertype smallint NOT NULL, + userstatus smallint NOT NULL, verified_email boolean, name character varying(250), created timestamp(6) with time zone, @@ -1119,6 +1135,12 @@ ALTER TABLE ONLY public."Project" ALTER TABLE ONLY public."Researcher" ADD CONSTRAINT "Researcher_pkey" PRIMARY KEY ("ID"); +-- +-- Name: UserDatasetProfile UserDatasetProfile_pkey; Type: CONSTRAINT; Schema: public; Owner: :POSTGRES_USER +-- + +ALTER TABLE ONLY public."UserDatasetProfile" + ADD CONSTRAINT "UserDatasetProfile_pkey" PRIMARY KEY (id); -- -- Name: UserDMP UserDMP_pkey; Type: CONSTRAINT; Schema: public; Owner: :POSTGRES_USER @@ -1325,6 +1347,20 @@ ALTER TABLE ONLY public."Lock" ALTER TABLE ONLY public."Notification" ADD CONSTRAINT "NotificationUserReference" FOREIGN KEY ("UserId") REFERENCES public."UserInfo"(id); +-- +-- Name: UserDatasetProfile UserDatasetProfile_datasetProfile_key; Type: FK CONSTRAINT; Schema: public; Owner: :POSTGRES_USER +-- + +ALTER TABLE ONLY public."UserDatasetProfile" + ADD CONSTRAINT "UserDatasetProfile_datasetProfile_fkey" FOREIGN KEY ("datasetProfile") REFERENCES public."DatasetProfile" ("ID"); + +-- +-- Name: UserDatasetProfile UserDatasetProfile_user_key; Type: FK CONSTRAINT; Schema: public; Owner: :POSTGRES_USER +-- + + +ALTER TABLE ONLY public."UserDatasetProfile" + ADD CONSTRAINT "UserDatasetProfile_usr_fkey" FOREIGN KEY ("usr") REFERENCES public."UserInfo" (id); -- -- Name: UserDMP UserDMP_dmp_fkey; Type: FK CONSTRAINT; Schema: public; Owner: :POSTGRES_USER diff --git a/dmp-db-scema/updates/00.00.006_Update_Credential_and_LoginConfirmationEmail_tables.sql b/dmp-db-scema/updates/00.00.006_Update_Credential_and_LoginConfirmationEmail_tables.sql new file mode 100644 index 000000000..f31ddbdef --- /dev/null +++ b/dmp-db-scema/updates/00.00.006_Update_Credential_and_LoginConfirmationEmail_tables.sql @@ -0,0 +1,16 @@ +DO $$DECLARE + this_version CONSTANT varchar := '00.00.006'; +BEGIN + PERFORM * FROM "DBVersion" WHERE version = this_version; + IF FOUND THEN RETURN; END IF; + + ALTER TABLE public."Credential" ADD COLUMN Email character varying; + + ALTER TABLE public."LoginConfirmationEmail" RENAME TO "EmailConfirmation"; + + ALTER TABLE public."EmailConfirmation" ADD COLUMN data text; + + UPDATE public."Credential" cred SET "Email"= u.email FROM public."UserInfo" u WHERE cred."UserId" = u.id; + + INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.00.006', '2020-10-26 10:10:00.000000+03', now(), 'Update Credential and LoginConfirmationEmail tables'); +END$$; \ No newline at end of file diff --git a/dmp-db-scema/updates/00.00.007_Add_userstatus_on_UserInfo_table.sql b/dmp-db-scema/updates/00.00.007_Add_userstatus_on_UserInfo_table.sql new file mode 100644 index 000000000..d778d6e97 --- /dev/null +++ b/dmp-db-scema/updates/00.00.007_Add_userstatus_on_UserInfo_table.sql @@ -0,0 +1,14 @@ +DO $$DECLARE + this_version CONSTANT varchar := '00.00.007'; +BEGIN + PERFORM * FROM "DBVersion" WHERE version = this_version; + IF FOUND THEN RETURN; END IF; + + ALTER TABLE public."UserInfo" ADD COLUMN userstatus smallint; + + UPDATE public."UserInfo" SET userstatus=0; + + ALTER TABLE public."UserInfo" ALTER COLUMN userstatus SET NOT NULL; + + INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.00.007', '2020-10-27 13:40:00.000000+03', now(), 'Add userstatus on UserInfo table'); +END$$; \ No newline at end of file diff --git a/dmp-db-scema/updates/00.00.008_Add_UserDatasetProfile_table.sql b/dmp-db-scema/updates/00.00.008_Add_UserDatasetProfile_table.sql new file mode 100644 index 000000000..9e9a0e798 --- /dev/null +++ b/dmp-db-scema/updates/00.00.008_Add_UserDatasetProfile_table.sql @@ -0,0 +1,20 @@ +CREATE TABLE public."UserDatasetProfile" +( + id uuid NOT NULL, + "usr" uuid NOT NULL, + "datasetProfile" uuid NOT NULL, + role integer, + CONSTRAINT "UserDatasetProfile_pkey" PRIMARY KEY (id), + CONSTRAINT "UserDatasetProfile_datasetProfile_fkey" FOREIGN KEY ("datasetProfile") + REFERENCES public."DatasetProfile" ("ID") MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID, + CONSTRAINT "UserDatasetProfile_usr_fkey" FOREIGN KEY ("usr") + REFERENCES public."UserInfo" (id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION + NOT VALID +) + +INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.00.008', '2021-04-05 17:48:00.000000+03', now(), 'Add Dataset Profile User Table'); \ No newline at end of file diff --git a/dmp-frontend/Dockerfile b/dmp-frontend/Dockerfile index 7a8d188c9..5bff47629 100644 --- a/dmp-frontend/Dockerfile +++ b/dmp-frontend/Dockerfile @@ -12,7 +12,7 @@ RUN npm install COPY . /page # Build the project and copy the files -RUN npm run ng build -- --deploy-url=/ --prod +RUN npm run ng build -- --prod FROM nginx:alpine diff --git a/dmp-frontend/Dockerfile.CI b/dmp-frontend/Dockerfile.CI index b9d0860ec..40d13c999 100644 --- a/dmp-frontend/Dockerfile.CI +++ b/dmp-frontend/Dockerfile.CI @@ -12,20 +12,24 @@ WORKDIR /src COPY . . # Build the project and copy the files -RUN npm run ng build -- --deploy-url=/ --prod +RUN npm run ng build -- --prod +#RUN node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng build -- --deploy-url=/ --prod FROM nginx:alpine #!/bin/sh -COPY nginx.conf.CI /etc/nginx/nginx.conf +COPY nginx.conf.CI /etc/nginx/conf.d/default.conf +COPY start_nginx.sh /start_nginx.sh +RUN mkdir -p /tmp/log/nginx + ## Remove default nginx index page RUN rm -rf /usr/share/nginx/html/* -# Copy from the stahg 1 +# Copy from the stage 1 COPY --from=builder /src/dist /usr/share/nginx/html +RUN cp /usr/share/nginx/html/index.html /usr/share/nginx/html/index_base.html EXPOSE 8080 - -ENTRYPOINT ["nginx", "-g", "daemon off;"] \ No newline at end of file +ENTRYPOINT ["sh", "/start_nginx.sh"] \ No newline at end of file diff --git a/dmp-frontend/nginx.conf.CI b/dmp-frontend/nginx.conf.CI index 63cee1859..082050013 100644 --- a/dmp-frontend/nginx.conf.CI +++ b/dmp-frontend/nginx.conf.CI @@ -22,7 +22,14 @@ server { location / { try_files $uri $uri/ /index.html =404; - add_header Cache-Control "no-store, no-cache, must-revalidate"; + } + + location = /index.html { + add_header Cache-Control "no-store, no-cache, must-revalidate"; + } + + location = /assets/config.json { + add_header Cache-Control "no-store, no-cache, must-revalidate"; } } \ No newline at end of file diff --git a/dmp-frontend/package.json b/dmp-frontend/package.json index a12f8bb5f..ed4ad77f2 100644 --- a/dmp-frontend/package.json +++ b/dmp-frontend/package.json @@ -31,10 +31,12 @@ "moment": "^2.24.0", "moment-timezone": "^0.5.26", "ng-dialog-animation": "^9.0.3", + "ng2-dragula": "^2.1.1", "ngx-cookie-service": "^2.2.0", "ngx-cookieconsent": "^2.2.3", "ngx-dropzone": "^2.2.2", "ngx-guided-tour": "^1.1.10", + "ngx-matomo": "^0.1.4", "rxjs": "^6.3.2", "tinymce": "^5.4.2", "tslib": "^1.10.0", diff --git a/dmp-frontend/src/app/app.component.html b/dmp-frontend/src/app/app.component.html index 3e7595fa7..87b6099ed 100644 --- a/dmp-frontend/src/app/app.component.html +++ b/dmp-frontend/src/app/app.component.html @@ -25,4 +25,7 @@ - \ No newline at end of file + diff --git a/dmp-frontend/src/app/app.component.scss b/dmp-frontend/src/app/app.component.scss index f0b0cde9c..ddc6df33c 100644 --- a/dmp-frontend/src/app/app.component.scss +++ b/dmp-frontend/src/app/app.component.scss @@ -12,7 +12,7 @@ // } .main-container { - top: 80px; + top: 80px !important; left: 0; right: 0; } diff --git a/dmp-frontend/src/app/app.component.ts b/dmp-frontend/src/app/app.component.ts index cde53ffed..1d17cb1ae 100644 --- a/dmp-frontend/src/app/app.component.ts +++ b/dmp-frontend/src/app/app.component.ts @@ -2,7 +2,7 @@ import { of as observableOf, Subscription } from 'rxjs'; import { switchMap, filter, map, takeUntil } from 'rxjs/operators'; -import { Component, OnInit } from '@angular/core'; +import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { environment } from '../environments/environment'; @@ -16,6 +16,10 @@ import { LanguageService } from './core/services/language/language.service'; import { ConfigurationService } from './core/services/configuration/configuration.service'; import { Location } from '@angular/common'; +import { MatomoInjector } from 'ngx-matomo'; +import { MatomoService } from './core/services/matomo/matomo-service'; +import { SideNavService } from './core/services/sidenav/side-nav.sevice'; +import { MatSidenav } from '@angular/material'; declare const gapi: any; @@ -26,14 +30,17 @@ declare var $: any; templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) -export class AppComponent implements OnInit { +export class AppComponent implements OnInit, AfterViewInit { hasBreadCrumb = observableOf(false); - sideNavOpen = false; + // sideNavOpen = false; + private sideNavSubscription: Subscription; helpContentEnabled: boolean; private statusChangeSubscription: Subscription; onlySplash = true; + @ViewChild('sidenav', {static:false}) sidenav:MatSidenav; + constructor( private router: Router, private route: ActivatedRoute, @@ -46,11 +53,48 @@ export class AppComponent implements OnInit { private ccService: NgcCookieConsentService, private language: LanguageService, private configurationService: ConfigurationService, - private location: Location + private location: Location, + private matomoService: MatomoService, + private sidenavService: SideNavService ) { this.initializeServices(); + this.matomoService.init(); this.helpContentEnabled = configurationService.helpService.enabled; } + ngAfterViewInit(): void { + setTimeout(() => { + this.sideNavSubscription = this.sidenavService.status().subscribe(isopen=>{ + const hamburger = document.getElementById('hamburger'); + if(isopen){ + //update value of hamburfer + if(!hamburger){//try later + setTimeout(() => { + const hamburger = document.getElementById('hamburger'); + if(hamburger){ + hamburger.classList.add('change'); + } + }, 300); + }else{ + hamburger.classList.add('change'); + } + this.sidenav.open() + }else{//closed + if(!hamburger){//try later + setTimeout(() => { + const hamburger = document.getElementById('hamburger'); + if(hamburger){ + hamburger.classList.remove('change'); + } + }, 300); + }else{ + hamburger.classList.remove('change'); + } + this.sidenav.close(); + + } + }); + }); + } onActivate(event: any) { this.breadCrumbResolverService.push(event); @@ -75,7 +119,9 @@ export class AppComponent implements OnInit { this.onlySplash = false; } if (!this.cookieService.check("cookiesConsent")) { - this.cookieService.set("cookiesConsent", "false", 356); + // this.cookieService.set("cookiesConsent", "false", 356); + this.cookieService.set("cookiesConsent", "false", 356,null,null,false, 'Lax'); + } this.hasBreadCrumb = this.router.events.pipe( @@ -108,7 +154,8 @@ export class AppComponent implements OnInit { this.statusChangeSubscription = this.ccService.statusChange$.subscribe((event: NgcStatusChangeEvent) => { if (event.status == "dismiss") { - this.cookieService.set("cookiesConsent", "true", 365); + // this.cookieService.set("cookiesConsent", "true", 365); + this.cookieService.set("cookiesConsent", "true", 356,null,null,false, 'Lax'); } }); @@ -130,7 +177,7 @@ export class AppComponent implements OnInit { } this.ccService.destroy(); this.ccService.init(this.ccService.getConfig()); - }) + }); } translateTitle(ttl: string) { @@ -149,6 +196,9 @@ export class AppComponent implements OnInit { ngOnDestroy() { this.statusChangeSubscription.unsubscribe(); + if(this.sideNavSubscription){ + this.sideNavSubscription.unsubscribe(); + } } login() { diff --git a/dmp-frontend/src/app/app.module.ts b/dmp-frontend/src/app/app.module.ts index a460a2e19..d5ad06c43 100644 --- a/dmp-frontend/src/app/app.module.ts +++ b/dmp-frontend/src/app/app.module.ts @@ -1,7 +1,8 @@ import { OverlayModule } from '@angular/cdk/overlay'; import { HttpClient, HttpClientModule } from '@angular/common/http'; -import { LOCALE_ID, NgModule, APP_INITIALIZER } from '@angular/core'; +import { LOCALE_ID, NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatFormFieldDefaultOptions, MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material'; import { MatMomentDateModule, MAT_MOMENT_DATE_FORMATS } from '@angular/material-moment-adapter'; import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; import { BrowserModule, Title } from '@angular/platform-browser'; @@ -23,16 +24,15 @@ import { MomentUtcDateAdapter } from '@common/date/moment-utc-date-adapter'; import { CommonHttpModule } from '@common/http/common-http.module'; import { CommonUiModule } from '@common/ui/common-ui.module'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { TranslateHttpLoader } from '@ngx-translate/http-loader'; -import { environment } from 'environments/environment'; import { CookieService } from 'ngx-cookie-service'; import { NgcCookieConsentConfig, NgcCookieConsentModule } from 'ngx-cookieconsent'; -import { TranslateServerLoader } from './core/services/language/server.loader'; -import { BaseHttpService } from './core/services/http/base-http.service'; +import { MatomoModule } from 'ngx-matomo'; import { ConfigurationService } from './core/services/configuration/configuration.service'; -import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module'; -import { MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormFieldDefaultOptions } from '@angular/material'; +import { TranslateServerLoader } from './core/services/language/server.loader'; +import { MatomoService } from './core/services/matomo/matomo-service'; import { GuidedTourModule } from './library/guided-tour/guided-tour.module'; +import { Oauth2DialogModule } from './ui/misc/oauth2-dialog/oauth2-dialog.module'; +import { DragulaModule } from 'ng2-dragula'; // AoT requires an exported function for factories export function HttpLoaderFactory(http: HttpClient, appConfig: ConfigurationService) { @@ -95,6 +95,7 @@ const appearance: MatFormFieldDefaultOptions = { CommonHttpModule, MatMomentDateModule, LoginModule, + MatomoModule, //Ui NotificationModule, NavigationModule, @@ -107,7 +108,8 @@ const appearance: MatFormFieldDefaultOptions = { SidebarModule, NgcCookieConsentModule.forRoot(cookieConfig), Oauth2DialogModule, - GuidedTourModule.forRoot() + GuidedTourModule.forRoot(), + DragulaModule.forRoot() ], declarations: [ AppComponent, @@ -131,7 +133,8 @@ const appearance: MatFormFieldDefaultOptions = { useValue: appearance }, Title, - CookieService + CookieService, + MatomoService ], bootstrap: [AppComponent] }) diff --git a/dmp-frontend/src/app/core/common/enum/app-role.ts b/dmp-frontend/src/app/core/common/enum/app-role.ts index 944067e78..4cd4c9c08 100644 --- a/dmp-frontend/src/app/core/common/enum/app-role.ts +++ b/dmp-frontend/src/app/core/common/enum/app-role.ts @@ -2,4 +2,5 @@ export enum AppRole { Admin = 2, Manager = 1, User = 0, -} \ No newline at end of file + DatasetTemplateEditor = 3 +} diff --git a/dmp-frontend/src/app/core/common/enum/dataset-profile-field-view-style.ts b/dmp-frontend/src/app/core/common/enum/dataset-profile-field-view-style.ts index c656787e3..a82869dec 100644 --- a/dmp-frontend/src/app/core/common/enum/dataset-profile-field-view-style.ts +++ b/dmp-frontend/src/app/core/common/enum/dataset-profile-field-view-style.ts @@ -15,5 +15,6 @@ export enum DatasetProfileFieldViewStyle { Researchers = "researchers", Organizations = "organizations", DatasetIdentifier = "datasetIdentifier", - Currency = "currency" + Currency = "currency", + Validation = 'validation' } diff --git a/dmp-frontend/src/app/core/common/enum/role-organization-type.ts b/dmp-frontend/src/app/core/common/enum/role-organization-type.ts new file mode 100644 index 000000000..e8b87c7b4 --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/role-organization-type.ts @@ -0,0 +1,16 @@ +export enum RoleOrganizationType { + Faculty = "Faculty", + Librarian = "Librarian", + Researcher = "Researcher", + Student = "Student (BA/BSc, MSc)", + EarlyCareerResearcher = "Early Career Researcher (PhD candidate, post-graduate)", + ResearchAdministrator = "Research Administrator", + RepositoryManager = "Repository Manager", + ResearchInfrastructure = "Research Infrastructure", + ServiceProvider = "Service Provider", + Publisher = "Publisher", + ResearchFunder = "Research Funder", + Policymaker = "Policymaker", + SMEIndustry = "SME/ Industry", + Other = "Other" +} diff --git a/dmp-frontend/src/app/core/core-service.module.ts b/dmp-frontend/src/app/core/core-service.module.ts index 3082ad593..0f8656770 100644 --- a/dmp-frontend/src/app/core/core-service.module.ts +++ b/dmp-frontend/src/app/core/core-service.module.ts @@ -1,8 +1,14 @@ -import { ModuleWithProviders, NgModule, Optional, SkipSelf, APP_INITIALIZER } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { APP_INITIALIZER, ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; import { CookieService } from 'ngx-cookie-service'; +import { AdminAuthGuard } from './admin-auth-guard.service'; import { AuthGuard } from './auth-guard.service'; import { AuthService } from './services/auth/auth.service'; +import { ConfigurationService } from './services/configuration/configuration.service'; +import { ContactSupportService } from './services/contact-support/contact-support.service'; import { CultureService } from './services/culture/culture-service'; +import { LanguageInfoService } from './services/culture/language-info-service'; +import { CurrencyService } from './services/currency/currency.service'; import { DashboardService } from './services/dashboard/dashboard.service'; import { DatasetProfileService } from './services/dataset-profile/dataset-profile.service'; import { DatasetWizardService } from './services/dataset-wizard/dataset-wizard.service'; @@ -11,6 +17,7 @@ import { DatasetService } from './services/dataset/dataset.service'; import { DmpInvitationService } from './services/dmp/dmp-invitation.service'; import { DmpProfileService } from './services/dmp/dmp-profile.service'; import { DmpService } from './services/dmp/dmp.service'; +import { EmailConfirmationService } from './services/email-confirmation/email-confirmation.service'; import { ExternalDataRepositoryService } from './services/external-sources/data-repository/extternal-data-repository.service'; import { ExternalDatasetService } from './services/external-sources/dataset/external-dataset.service'; import { ExternalSourcesConfigurationService } from './services/external-sources/external-sources-configuration.service'; @@ -18,31 +25,26 @@ import { ExternalSourcesService } from './services/external-sources/external-sou import { ExternalRegistryService } from './services/external-sources/registry/external-registry.service'; import { ExternalResearcherService } from './services/external-sources/researcher/external-researcher.service'; import { ExternalServiceService } from './services/external-sources/service/external-service.service'; -import { BaseHttpService } from './services/http/base-http.service'; -import { LoggingService } from './services/logging/logging-service'; -import { UiNotificationService } from './services/notification/ui-notification-service'; -import { ProgressIndicationService } from './services/progress-indication/progress-indication-service'; +import { FunderService } from './services/funder/funder.service'; import { GrantFileUploadService } from './services/grant/grant-file-upload.service'; import { GrantService } from './services/grant/grant.service'; +import { BaseHttpService } from './services/http/base-http.service'; +import { LanguageService } from './services/language/language.service'; +import { LockService } from './services/lock/lock.service'; +import { LoggingService } from './services/logging/logging-service'; +import { MergeEmailConfirmationService } from './services/merge-email-confirmation/merge-email-confirmation.service'; +import { UiNotificationService } from './services/notification/ui-notification-service'; +import { OrganisationService } from './services/organisation/organisation.service'; +import { ProgressIndicationService } from './services/progress-indication/progress-indication-service'; import { ProjectService } from './services/project/project.service'; +import { QuickWizardService } from './services/quick-wizard/quick-wizard.service'; import { SearchBarService } from './services/search-bar/search-bar.service'; import { TimezoneService } from './services/timezone/timezone-service'; +import { UserGuideService } from './services/user-guide/user-guide.service'; import { UserService } from './services/user/user.service'; 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'; -import { FunderService } from './services/funder/funder.service'; -import { ContactSupportService } from './services/contact-support/contact-support.service'; -import { LanguageService } from './services/language/language.service'; -import { AdminAuthGuard } from './admin-auth-guard.service'; -import { LockService } from './services/lock/lock.service'; -import { UserGuideService } from './services/user-guide/user-guide.service'; -import { ConfigurationService } from './services/configuration/configuration.service'; -import { HttpClient } from '@angular/common/http'; -import { LanguageInfoService } from './services/culture/language-info-service'; -import { CurrencyService } from './services/currency/currency.service'; +import { SpecialAuthGuard } from './special-auth-guard.service'; // // // This is shared module that provides all the services. Its imported only once on the AppModule. @@ -70,6 +72,7 @@ export class CoreServiceModule { CookieService, BaseHttpService, AdminAuthGuard, + SpecialAuthGuard, AuthGuard, CultureService, TimezoneService, @@ -107,6 +110,7 @@ export class CoreServiceModule { LockService, UserGuideService, CurrencyService, + MergeEmailConfirmationService, ConfigurationService, { provide: APP_INITIALIZER, diff --git a/dmp-frontend/src/app/core/formatting.module.ts b/dmp-frontend/src/app/core/formatting.module.ts index 91311f536..afa46884f 100644 --- a/dmp-frontend/src/app/core/formatting.module.ts +++ b/dmp-frontend/src/app/core/formatting.module.ts @@ -6,6 +6,7 @@ import { NgForLimitPipe } from './pipes/ng-for-limit.pipe'; import { TimezoneInfoDisplayPipe } from './pipes/timezone-info-display.pipe'; import { EnumUtils } from './services/utilities/enum-utils.service'; import { JsonParserPipe } from './pipes/json-parser.pipe'; +import { DateTimeCultureFormatPipe } from './pipes/date-time-culture-format.pipe'; // // @@ -19,6 +20,7 @@ import { JsonParserPipe } from './pipes/json-parser.pipe'; TimezoneInfoDisplayPipe, DateFormatPipe, DateTimeFormatPipe, + DateTimeCultureFormatPipe, JsonParserPipe ], exports: [ @@ -26,6 +28,7 @@ import { JsonParserPipe } from './pipes/json-parser.pipe'; TimezoneInfoDisplayPipe, DateFormatPipe, DateTimeFormatPipe, + DateTimeCultureFormatPipe, JsonParserPipe ], providers: [ @@ -35,6 +38,7 @@ import { JsonParserPipe } from './pipes/json-parser.pipe'; TimezoneInfoDisplayPipe, DateFormatPipe, DateTimeFormatPipe, + DateTimeCultureFormatPipe, JsonParserPipe ] }) diff --git a/dmp-frontend/src/app/core/model/admin/dataset-profile/dataset-profile.ts b/dmp-frontend/src/app/core/model/admin/dataset-profile/dataset-profile.ts index 2bf055029..92371e661 100644 --- a/dmp-frontend/src/app/core/model/admin/dataset-profile/dataset-profile.ts +++ b/dmp-frontend/src/app/core/model/admin/dataset-profile/dataset-profile.ts @@ -1,4 +1,5 @@ import { ValidationType } from "../../../common/enum/validation-type"; +import { UserInfoListingModel } from "../../user/user-info-listing"; export interface DatasetProfile { label: string; @@ -8,6 +9,7 @@ export interface DatasetProfile { version: number; description: string; language: string; + users: UserInfoListingModel[]; } export interface Page { diff --git a/dmp-frontend/src/app/core/model/dataset-profile-definition/field-data/field-data.ts b/dmp-frontend/src/app/core/model/dataset-profile-definition/field-data/field-data.ts index 8923345f1..47adf26c8 100644 --- a/dmp-frontend/src/app/core/model/dataset-profile-definition/field-data/field-data.ts +++ b/dmp-frontend/src/app/core/model/dataset-profile-definition/field-data/field-data.ts @@ -71,19 +71,19 @@ export interface DmpsAutoCompleteFieldData extends FieldData { } export interface ExternalDatasetsFieldData extends FieldData { - + multiAutoComplete: boolean; } export interface DataRepositoriesFieldData extends FieldData { - + multiAutoComplete: boolean; } export interface RegistriesFieldData extends FieldData { - + multiAutoComplete: boolean; } export interface ServicesFieldData extends FieldData { - + multiAutoComplete: boolean; } export interface TagsFieldData extends FieldData { @@ -94,7 +94,7 @@ export interface ResearchersFieldData extends FieldData { } -export interface OrganizationsFieldData extends FieldData { +export interface OrganizationsFieldData extends AutoCompleteFieldData { } @@ -105,3 +105,7 @@ export interface DatasetIdentifierFieldData extends FieldData { export interface CurrencyFieldData extends FieldData { } + +export interface ValidationFieldData extends FieldData { + +} diff --git a/dmp-frontend/src/app/core/model/dataset/dataset-id.model.ts b/dmp-frontend/src/app/core/model/dataset/dataset-id.model.ts index 5020bbbcb..1063a3cbf 100644 --- a/dmp-frontend/src/app/core/model/dataset/dataset-id.model.ts +++ b/dmp-frontend/src/app/core/model/dataset/dataset-id.model.ts @@ -6,11 +6,16 @@ export class DatasetIdModel { type: string; constructor(data: any) { + try{ const parsed = JSON.parse(data); if (!isNullOrUndefined(parsed)) { this.identifier = parsed.identifier; this.type = parsed.type; } + } + catch(error){ + console.warn('Could not parse DatasetIdModel'); + } } buildForm(): FormGroup { diff --git a/dmp-frontend/src/app/core/model/merge/user-merge-request.ts b/dmp-frontend/src/app/core/model/merge/user-merge-request.ts new file mode 100644 index 000000000..471463abb --- /dev/null +++ b/dmp-frontend/src/app/core/model/merge/user-merge-request.ts @@ -0,0 +1,5 @@ +export class UserMergeRequestModel { + userId: String; + email: String; + provider: number; +} diff --git a/dmp-frontend/src/app/core/model/organisation/organization.ts b/dmp-frontend/src/app/core/model/organisation/organization.ts index 9cdc13e05..7cc4afa30 100644 --- a/dmp-frontend/src/app/core/model/organisation/organization.ts +++ b/dmp-frontend/src/app/core/model/organisation/organization.ts @@ -6,4 +6,5 @@ export interface OrganizationModel { label: string; status: Status; tag: string; + reference?: string; } diff --git a/dmp-frontend/src/app/core/model/user/user-credential.ts b/dmp-frontend/src/app/core/model/user/user-credential.ts new file mode 100644 index 000000000..fe716aea0 --- /dev/null +++ b/dmp-frontend/src/app/core/model/user/user-credential.ts @@ -0,0 +1,6 @@ +import { AuthProvider } from '@app/core/common/enum/auth-provider'; + +export class UserCredentialModel { + email: string; + provider: AuthProvider; +} diff --git a/dmp-frontend/src/app/core/model/user/user-listing.ts b/dmp-frontend/src/app/core/model/user/user-listing.ts index fde223cb0..89e792cff 100644 --- a/dmp-frontend/src/app/core/model/user/user-listing.ts +++ b/dmp-frontend/src/app/core/model/user/user-listing.ts @@ -1,4 +1,6 @@ +import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type'; import { AppRole } from "../../common/enum/app-role"; +import { OrganizationModel } from '../organisation/organization'; export interface UserListingModel { id: String; @@ -11,4 +13,6 @@ export interface UserListingModel { timezone: String; zenodoEmail: String; avatarUrl: String; + organization: OrganizationModel; + roleOrganization: RoleOrganizationType; } diff --git a/dmp-frontend/src/app/core/pipes/date-time-culture-format.pipe.ts b/dmp-frontend/src/app/core/pipes/date-time-culture-format.pipe.ts new file mode 100644 index 000000000..967c699fa --- /dev/null +++ b/dmp-frontend/src/app/core/pipes/date-time-culture-format.pipe.ts @@ -0,0 +1,95 @@ +import { DatePipe, registerLocaleData } from '@angular/common'; +import localeEl from '@angular/common/locales/el'; +import localeDe from '@angular/common/locales/de'; +import localeEs from '@angular/common/locales/es'; +import localeSk from '@angular/common/locales/sk'; +import localeTr from '@angular/common/locales/tr'; +import localeSr from '@angular/common/locales/sr'; +import localePt from '@angular/common/locales/pt'; +import { Pipe, PipeTransform } from '@angular/core'; +import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; +import 'moment-timezone'; +import { CultureInfo } from '../model/culture-info'; +import { LanguageService } from '../services/language/language.service'; + +const availableCultures: CultureInfo[] = require('../../../assets/localization/available-cultures.json'); + +@Pipe({ + name: 'dateTimeCultureFormatter', + pure: false +}) +export class DateTimeCultureFormatPipe implements PipeTransform { + private cultureValues = new Map(); // cultures by name + private cache; + + constructor(private datePipe: DatePipe, + private translate: TranslateService, + private languageService: LanguageService) { + + if (availableCultures) { + this.cultureValues = new Map(); + availableCultures.forEach(culture => { + this.cultureValues.set(culture.name, culture); + }); + } + + this.translate.onLangChange.subscribe(($event: LangChangeEvent) => { + (this).locale = $event.lang; + this.cache = null; + }); + } + + transform(value: any, format?: string, timezone?: string): string | null { + if (!this.cache) { + const cultureName = this.languageService.getCurrentLanguage(); + let locale: string; + switch (cultureName) { + case 'en': { + locale = this.cultureValues.get('en-US').name; + break; + } + case 'gr': { + locale = this.cultureValues.get('el-GR').name; + registerLocaleData(localeEl); + break; + } + case 'es': { + locale = this.cultureValues.get('es-ES').name; + registerLocaleData(localeEs); + break; + } + case 'de': { + locale = this.cultureValues.get('de-DE').name; + registerLocaleData(localeDe); + break; + } + case 'tr': { + locale = this.cultureValues.get('tr-TR').name; + registerLocaleData(localeTr); + break; + } + case 'sk': { + locale = this.cultureValues.get('sk-SK').name; + registerLocaleData(localeSk); + break; + } + case 'sr': { + locale = this.cultureValues.get('sr-Latn-RS').name; + registerLocaleData(localeSr); + break; + } + case 'pt': { + locale = this.cultureValues.get('pt-PT').name; + registerLocaleData(localePt); + break; + } + default: { + locale = this.cultureValues.get('en-US').name; + break; + } + } + this.cache = this.datePipe.transform(value, format, timezone, locale); + } + return this.cache; + } +} diff --git a/dmp-frontend/src/app/core/query/dataset/daatset-external-autocomplete-criteria.ts b/dmp-frontend/src/app/core/query/dataset/daatset-external-autocomplete-criteria.ts index 9351b8ed1..d511e9432 100644 --- a/dmp-frontend/src/app/core/query/dataset/daatset-external-autocomplete-criteria.ts +++ b/dmp-frontend/src/app/core/query/dataset/daatset-external-autocomplete-criteria.ts @@ -1,6 +1,8 @@ +import { AutoCompleteSingleData } from "@app/core/model/dataset-profile-definition/field-data/field-data"; import { BaseCriteria } from "../base-criteria"; export class DatasetExternalAutocompleteCriteria extends BaseCriteria { public profileID: String; public fieldID: String; + public autocompleteOptions: AutoCompleteSingleData; } \ No newline at end of file diff --git a/dmp-frontend/src/app/core/query/funder/funder-criteria.ts b/dmp-frontend/src/app/core/query/funder/funder-criteria.ts index 97a93a4e2..6c37051b8 100644 --- a/dmp-frontend/src/app/core/query/funder/funder-criteria.ts +++ b/dmp-frontend/src/app/core/query/funder/funder-criteria.ts @@ -1,3 +1,5 @@ import { BaseCriteria } from "../base-criteria"; -export class FunderCriteria extends BaseCriteria { } +export class FunderCriteria extends BaseCriteria { + exactReference:string; + } diff --git a/dmp-frontend/src/app/core/query/grant/grant-criteria.ts b/dmp-frontend/src/app/core/query/grant/grant-criteria.ts index 587060466..da457031c 100644 --- a/dmp-frontend/src/app/core/query/grant/grant-criteria.ts +++ b/dmp-frontend/src/app/core/query/grant/grant-criteria.ts @@ -6,4 +6,5 @@ export class GrantCriteria extends BaseCriteria { public periodEnd: Date; public funderReference: String; public grantStateType: GrantStateType; + public exactReference: string; } diff --git a/dmp-frontend/src/app/core/query/organisation/organisation-criteria.ts b/dmp-frontend/src/app/core/query/organisation/organisation-criteria.ts index 66cc6a731..2012bf40a 100644 --- a/dmp-frontend/src/app/core/query/organisation/organisation-criteria.ts +++ b/dmp-frontend/src/app/core/query/organisation/organisation-criteria.ts @@ -2,4 +2,5 @@ import { BaseCriteria } from "../base-criteria"; export class OrganisationCriteria extends BaseCriteria { public labelLike: string; + public like?: string; } diff --git a/dmp-frontend/src/app/core/query/project/project-criteria.ts b/dmp-frontend/src/app/core/query/project/project-criteria.ts index a3b87757a..47a0d88b9 100644 --- a/dmp-frontend/src/app/core/query/project/project-criteria.ts +++ b/dmp-frontend/src/app/core/query/project/project-criteria.ts @@ -1,3 +1,5 @@ import { BaseCriteria } from "../base-criteria"; -export class ProjectCriteria extends BaseCriteria { } +export class ProjectCriteria extends BaseCriteria { + exactReference: string; + } diff --git a/dmp-frontend/src/app/core/query/researcher/researcher-criteria.ts b/dmp-frontend/src/app/core/query/researcher/researcher-criteria.ts index 3eda8ff2a..fd529c381 100644 --- a/dmp-frontend/src/app/core/query/researcher/researcher-criteria.ts +++ b/dmp-frontend/src/app/core/query/researcher/researcher-criteria.ts @@ -2,4 +2,5 @@ import { BaseCriteria } from "../base-criteria"; export class ResearcherCriteria extends BaseCriteria { public name: String; + public reference?: string; } diff --git a/dmp-frontend/src/app/core/services/auth/auth.service.ts b/dmp-frontend/src/app/core/services/auth/auth.service.ts index 66a52f3bc..e3fb7875e 100644 --- a/dmp-frontend/src/app/core/services/auth/auth.service.ts +++ b/dmp-frontend/src/app/core/services/auth/auth.service.ts @@ -81,7 +81,8 @@ export class AuthService extends BaseService { return this.http.post(url, loginInfo, { headers: this.headers }).pipe( map((res: any) => { const principal = this.current(res.payload); - this.cookieService.set('cookiesConsent', 'true', 356); + // this.cookieService.set('cookiesConsent', 'true', 356); + this.cookieService.set("cookiesConsent", "true", 356,null,null,false, 'Lax'); //this.loginContextSubject.next(true); return principal; }), @@ -91,6 +92,13 @@ export class AuthService extends BaseService { })); } + public mergeLogin(loginInfo: LoginInfo): Observable { + this.actionUrl = this.configurationService.server + 'auth/'; + const url = this.actionUrl + 'externallogin'; + + return this.http.post(url, loginInfo, { headers: this.headers }); + } + public nativeLogin(credentials: Credential): Observable { this.actionUrl = this.configurationService.server + 'auth/'; const url = this.actionUrl + 'nativelogin'; @@ -98,7 +106,8 @@ export class AuthService extends BaseService { return this.http.post(url, credentials, { headers: this.headers }).pipe( map((res: any) => { const principal = this.current(res.payload); - this.cookieService.set('cookiesConsent', 'true', 356); + // this.cookieService.set('cookiesConsent', 'true', 356); + this.cookieService.set("cookiesConsent", "true", 356,null,null,false, 'Lax'); //this.loginContextSubject.next(true); return principal; }), diff --git a/dmp-frontend/src/app/core/services/configuration/configuration.service.ts b/dmp-frontend/src/app/core/services/configuration/configuration.service.ts index e789a6b91..b0c9b8ae9 100644 --- a/dmp-frontend/src/app/core/services/configuration/configuration.service.ts +++ b/dmp-frontend/src/app/core/services/configuration/configuration.service.ts @@ -11,7 +11,7 @@ import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root', - }) +}) export class ConfigurationService extends BaseComponent { constructor(private http: HttpClient) { super(); } @@ -52,12 +52,12 @@ export class ConfigurationService extends BaseComponent { } private _guideAssets: string; - get guideAssets():string { + get guideAssets(): string { return this._guideAssets; } private _allowOrganizationCreator: boolean; - get allowOrganizationCreator():boolean { + get allowOrganizationCreator(): boolean { return this._allowOrganizationCreator; } @@ -76,6 +76,21 @@ export class ConfigurationService extends BaseComponent { return this._orcidPath; } + private _matomoEnabled: boolean; + get matomoEnabled(): boolean { + return this._matomoEnabled; + } + + private _matomoSiteUrl: string; + get matomoSiteUrl(): string { + return this._matomoSiteUrl; + } + + private _matomoSiteId: number; + get matomoSiteId(): number { + return this._matomoSiteId; + } + public loadConfiguration(): Promise { return new Promise((r, e) => { @@ -114,6 +129,11 @@ export class ConfigurationService extends BaseComponent { this._doiLink = config.doiLink; this._useSplash = config.useSplash; this._orcidPath = config.orcidPath; + if (config.matomo) { + this._matomoEnabled = config.matomo.enabled; + this._matomoSiteUrl = config.matomo.url; + this._matomoSiteId = config.matomo.siteId; + } } } diff --git a/dmp-frontend/src/app/core/services/culture/language-info-service.ts b/dmp-frontend/src/app/core/services/culture/language-info-service.ts index 5e7b22481..8afa52182 100644 --- a/dmp-frontend/src/app/core/services/culture/language-info-service.ts +++ b/dmp-frontend/src/app/core/services/culture/language-info-service.ts @@ -29,6 +29,7 @@ export class LanguageInfoService { getLanguageInfoValues(): LanguageInfo[] { const values: LanguageInfo[] = []; this.languageInfoValues.forEach((value) => values.push(value)); + values.sort((a,b)=> a.name.localeCompare(b.name)); return values; } diff --git a/dmp-frontend/src/app/core/services/dataset-wizard/dataset-wizard.service.ts b/dmp-frontend/src/app/core/services/dataset-wizard/dataset-wizard.service.ts index 1703704b2..437a413ea 100644 --- a/dmp-frontend/src/app/core/services/dataset-wizard/dataset-wizard.service.ts +++ b/dmp-frontend/src/app/core/services/dataset-wizard/dataset-wizard.service.ts @@ -42,8 +42,8 @@ export class DatasetWizardService { return this.http.delete(this.actionUrl + 'delete/' + id, { headers: this.headers }); } - createDataset(datasetModel: DatasetWizardModel): Observable { - return this.http.post(this.actionUrl, datasetModel, { headers: this.headers }); + createDataset(datasetModel: DatasetWizardModel): Observable { + return this.http.post(this.actionUrl, datasetModel, { headers: this.headers }); } public downloadPDF(id: string): Observable> { diff --git a/dmp-frontend/src/app/core/services/dataset/dataset-external-autocomplete.service.ts b/dmp-frontend/src/app/core/services/dataset/dataset-external-autocomplete.service.ts index 35ee12a6f..bcbfd2630 100644 --- a/dmp-frontend/src/app/core/services/dataset/dataset-external-autocomplete.service.ts +++ b/dmp-frontend/src/app/core/services/dataset/dataset-external-autocomplete.service.ts @@ -1,11 +1,12 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { environment } from '../../../../environments/environment'; import { DatasetExternalAutocompleteCriteria } from '../../query/dataset/daatset-external-autocomplete-criteria'; import { RequestItem } from '../../query/request-item'; import { DatasetProfileService } from '../dataset-profile/dataset-profile.service'; import { ConfigurationService } from '../configuration/configuration.service'; +import { map } from 'rxjs/operators'; @Injectable() export class DatasetExternalAutocompleteService { @@ -27,4 +28,14 @@ export class DatasetExternalAutocompleteService { return this.httpClient.post(this.configurationService.server + 'search/autocomplete', lookUpItem); } -} + queryApi(requestItem: RequestItem):Observable{ //TODO + + return of([ + { + label:'Preview not supported yet', + source:'' + } + ]); + } + +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/services/dataset/dataset.service.ts b/dmp-frontend/src/app/core/services/dataset/dataset.service.ts index b56e3b19c..418196418 100644 --- a/dmp-frontend/src/app/core/services/dataset/dataset.service.ts +++ b/dmp-frontend/src/app/core/services/dataset/dataset.service.ts @@ -90,6 +90,10 @@ export class DatasetService { return this.httpClient.get(this.actionUrl + 'getPDF/' + id, { responseType: 'blob', observe: 'response', headers: headerPdf }); } + public validateDataset(id: string) : Observable{ + return this.http.get(this.actionUrl+id+"/validate"); + } + //GK: NO // public downloadJson(id: string): Observable> { // return this.httpClient.get(this.actionUrl + 'rda/' + id, { responseType: 'blob', observe: 'response' }); diff --git a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts index 853ad6b84..52888b3c1 100644 --- a/dmp-frontend/src/app/core/services/dmp/dmp.service.ts +++ b/dmp-frontend/src/app/core/services/dmp/dmp.service.ts @@ -58,8 +58,8 @@ export class DmpService { return this.http.get(this.actionUrl + 'publicOverview/' + id, { headers: this.headers }) } - getAllVersions(id: string): Observable { - return this.http.get(this.actionUrl + 'versions/' + id, { headers: this.headers }) + getAllVersions(id: string, isPublic: boolean): Observable { + return this.http.get(this.actionUrl + 'versions/' + id + '?public=' + isPublic, { headers: this.headers }) } unlock(id: String): Observable { diff --git a/dmp-frontend/src/app/core/services/external-sources/external-sources.service.ts b/dmp-frontend/src/app/core/services/external-sources/external-sources.service.ts index 3e91ec516..dc04b7f3a 100644 --- a/dmp-frontend/src/app/core/services/external-sources/external-sources.service.ts +++ b/dmp-frontend/src/app/core/services/external-sources/external-sources.service.ts @@ -67,8 +67,14 @@ export class ExternalSourcesService { return this.http.post(this.configurationService.server + 'researchers/getWithExternal', requestItem, { headers: this.headers }); } - public searchDMPOrganizations(like: string): Observable { - return this.http.get(this.actionUrl + 'organisations' + '?query=' + like, { headers: this.headers }); + public searchDMPOrganizations(like: string, reference?: string): Observable { + const params = { + query:like + } + if(reference){ + params['reference'] = reference; + } + return this.http.get(this.actionUrl + 'organisations', { headers: this.headers, params: params }); } // TODO: currently not used. @@ -76,4 +82,8 @@ export class ExternalSourcesService { return this.http.get(this.actionUrl + 'datasetprofiles/get' + '?query=' + like, { headers: this.headers }); } + public validateIdentifier(like: string, type: string): Observable { + return this.http.get(this.actionUrl + 'validation?query=' + like + '&type=' + type, { headers: this.headers }); + } + } diff --git a/dmp-frontend/src/app/core/services/matomo/matomo-service.ts b/dmp-frontend/src/app/core/services/matomo/matomo-service.ts new file mode 100644 index 000000000..f4711f996 --- /dev/null +++ b/dmp-frontend/src/app/core/services/matomo/matomo-service.ts @@ -0,0 +1,39 @@ +import { Injectable } from '@angular/core'; +import { MatomoInjector, MatomoTracker } from 'ngx-matomo'; +import { AuthService } from '../auth/auth.service'; +import { ConfigurationService } from '../configuration/configuration.service'; + +@Injectable() +export class MatomoService { + + constructor( + private configurationService: ConfigurationService, + private matomoInjector: MatomoInjector, + private matomoTracker: MatomoTracker, + private authService: AuthService + ) { + + } + + init() { + if (this.configurationService.matomoEnabled) { + this.matomoInjector.init(this.configurationService.matomoSiteUrl, this.configurationService.matomoSiteId); + } + } + + trackPageView(customTitle?: string): void { + if (this.configurationService.matomoEnabled) { + var principal = this.authService.current(); + if (principal != null) { this.matomoTracker.setUserId(principal.id); } + this.matomoTracker.trackPageView(customTitle); + } + } + + trackSiteSearch(keyword: string, category?: string, resultsCount?: number): void { + if (this.configurationService.matomoEnabled) { + var principal = this.authService.current(); + if (principal != null) { this.matomoTracker.setUserId(principal.id); } + this.matomoTracker.trackSiteSearch(keyword, category, resultsCount); + } + } +} diff --git a/dmp-frontend/src/app/core/services/merge-email-confirmation/merge-email-confirmation.service.ts b/dmp-frontend/src/app/core/services/merge-email-confirmation/merge-email-confirmation.service.ts new file mode 100644 index 000000000..0f3676932 --- /dev/null +++ b/dmp-frontend/src/app/core/services/merge-email-confirmation/merge-email-confirmation.service.ts @@ -0,0 +1,23 @@ +import { HttpHeaders } from '@angular/common/http'; +import { Injectable } from "@angular/core"; +import { BaseHttpService } from "../http/base-http.service"; +import { ConfigurationService } from '../configuration/configuration.service'; +import { UserMergeRequestModel } from '@app/core/model/merge/user-merge-request'; + +@Injectable() +export class MergeEmailConfirmationService { + private actioUrl: string; + private headers: HttpHeaders + + constructor(private http: BaseHttpService, private configurationService: ConfigurationService) { + this.actioUrl = configurationService.server + 'emailMergeConfirmation/'; + } + + public emailConfirmation(token: string) { + return this.http.get(this.actioUrl + token, { headers: this.headers }); + } + + public sendConfirmationEmail(mergeRequest: UserMergeRequestModel) { + return this.http.post(this.actioUrl, mergeRequest, { headers: this.headers }); + } +} diff --git a/dmp-frontend/src/app/core/services/organisation/organisation.service.ts b/dmp-frontend/src/app/core/services/organisation/organisation.service.ts index 274968b86..998978a2c 100644 --- a/dmp-frontend/src/app/core/services/organisation/organisation.service.ts +++ b/dmp-frontend/src/app/core/services/organisation/organisation.service.ts @@ -8,6 +8,8 @@ import { OrganizationModel } from "../../model/organisation/organization"; import { OrganisationCriteria } from "../../query/organisation/organisation-criteria"; import { DataTableRequest } from "../../model/data-table/data-table-request"; import { ConfigurationService } from '../configuration/configuration.service'; +import { ExternalSourceItemModel } from "@app/core/model/external-sources/external-source-item"; +import { RequestItem } from "@app/core/query/request-item"; @Injectable() export class OrganisationService { @@ -28,6 +30,10 @@ export class OrganisationService { return this.http.post>(this.actionUrl + 'internal/organisations', dataTableRequest , { headers: this.headers }); } + public searchGeneralOrganisations(dataTableRequest: RequestItem): Observable { + return this.http.post(this.actionUrl + 'general/organisations', dataTableRequest , { headers: this.headers }); + } + public searchPublicOrganisations(dataTableRequest: DataTableRequest): Observable> { return this.http.post>(this.actionUrl + 'public/organisations', dataTableRequest , { headers: this.headers }); } diff --git a/dmp-frontend/src/app/core/services/sidenav/side-nav.sevice.ts b/dmp-frontend/src/app/core/services/sidenav/side-nav.sevice.ts new file mode 100644 index 000000000..4fe163777 --- /dev/null +++ b/dmp-frontend/src/app/core/services/sidenav/side-nav.sevice.ts @@ -0,0 +1,24 @@ +import { Injectable } from "@angular/core"; +import { Observable } from "rxjs"; +import { BehaviorSubject } from "rxjs"; + +@Injectable({ + providedIn:'root' +}) +export class SideNavService { + + private sidebar$:BehaviorSubject = new BehaviorSubject(true); + + public status():Observable{ + return this.sidebar$.asObservable(); + } + + public setStatus(isOpen: boolean){ + this.sidebar$.next(isOpen); + } + + public toggle(){ + this.sidebar$.next(!this.sidebar$.getValue()); + } + +} diff --git a/dmp-frontend/src/app/core/services/user-guide/user-guide.service.ts b/dmp-frontend/src/app/core/services/user-guide/user-guide.service.ts index abdf49738..206e2a70d 100644 --- a/dmp-frontend/src/app/core/services/user-guide/user-guide.service.ts +++ b/dmp-frontend/src/app/core/services/user-guide/user-guide.service.ts @@ -19,8 +19,8 @@ export class UserGuideService { this.userGuideUrl = `${configurationService.server}userguide`; } - public getUserGuide(): Observable> { - return this.http.get(`${this.userGuideUrl}/current`, { responseType: 'blob', observe: 'response', headers: {'Content-type': 'text/html', + public getUserGuide(lang: string): Observable> { + return this.http.get(`${this.userGuideUrl}/${lang}`, { responseType: 'blob', observe: 'response', headers: {'Content-type': 'text/html', 'Accept': 'text/html', 'Access-Control-Allow-Origin': this.configurationService.app, 'Access-Control-Allow-Credentials': 'true'} }); diff --git a/dmp-frontend/src/app/core/services/user/user.service.ts b/dmp-frontend/src/app/core/services/user/user.service.ts index 27165ca96..6f387e813 100644 --- a/dmp-frontend/src/app/core/services/user/user.service.ts +++ b/dmp-frontend/src/app/core/services/user/user.service.ts @@ -1,4 +1,4 @@ -import { HttpHeaders } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { environment } from '../../../../environments/environment'; @@ -8,14 +8,15 @@ import { UserListingModel } from '../../model/user/user-listing'; import { UserCriteria } from '../../query/user/user-criteria'; import { BaseHttpService } from '../http/base-http.service'; import { ConfigurationService } from '../configuration/configuration.service'; +import { UserCredentialModel } from '@app/core/model/user/user-credential'; @Injectable() export class UserService { private actionUrl: string; - private headers: HttpHeaders; + private headers = new HttpHeaders(); - constructor(private http: BaseHttpService, private configurationService: ConfigurationService) { + constructor(private http: BaseHttpService, private httpClient: HttpClient, private configurationService: ConfigurationService) { this.actionUrl = configurationService.server + 'user/'; } @@ -28,6 +29,10 @@ export class UserService { return this.http.get(this.actionUrl + id, { headers: this.headers }); } + getEmails(id: String): Observable { + return this.http.get(`${this.actionUrl}${id}/emails`, { headers: this.headers }); + } + updateRoles(itemToUpdate: UserListingModel): Observable { return this.http.post(this.actionUrl + 'updateRoles', JSON.stringify(itemToUpdate), { headers: this.headers }); } @@ -48,6 +53,10 @@ export class UserService { return this.http.post>(this.actionUrl + 'getCollaboratorsPaged', JSON.stringify(dataTableRequest), { headers: this.headers }); } + getFromEmail(email: string): Observable { + return this.http.post(this.actionUrl + 'find', email, {headers: this.headers}); + } + public hasDOIToken(): Observable { const url = this.actionUrl + 'hasDOIToken'; return this.http.get(url, { headers: this.headers }); @@ -62,4 +71,9 @@ export class UserService { const url = this.actionUrl + 'deleteDOIToken'; return this.http.delete(url, { headers: this.headers }); } + + downloadCSV(): Observable> { + let headerCsv: HttpHeaders = this.headers.set('Content-Type', 'application/csv') + return this.httpClient.get(this.actionUrl + 'getCsv/', { responseType: 'blob', observe: 'response', headers: headerCsv }); + } } diff --git a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts index 54d821ead..a7dada1f9 100644 --- a/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts +++ b/dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts @@ -11,6 +11,8 @@ import { ValidationType } from '../../common/enum/validation-type'; import { DatasetProfileInternalDmpEntitiesType } from '../../common/enum/dataset-profile-internal-dmp-entities-type'; import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { Role } from '@app/core/common/enum/role'; +import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type'; +import { ViewStyleType } from '@app/ui/admin/dataset-profile/editor/components/field/view-style-enum'; @Injectable() export class EnumUtils { @@ -28,6 +30,7 @@ export class EnumUtils { case AppRole.Admin: return this.language.instant('TYPES.APP-ROLE.ADMIN'); case AppRole.User: return this.language.instant('TYPES.APP-ROLE.USER'); case AppRole.Manager: return this.language.instant('TYPES.APP-ROLE.MANAGER'); + case AppRole.DatasetTemplateEditor: return this.language.instant('TYPES.APP-ROLE.DATASET-TEMPLATE-EDITOR'); } } @@ -86,8 +89,33 @@ export class EnumUtils { case DatasetProfileFieldViewStyle.Organizations: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.ORGANIZATIONS'); case DatasetProfileFieldViewStyle.DatasetIdentifier: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.DATASET-IDENTIFIER'); case DatasetProfileFieldViewStyle.Currency: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.CURRENCY'); + case DatasetProfileFieldViewStyle.Validation: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.VALIDATION'); } } + toDatasetProfileViewTypeString(status: ViewStyleType) :string{ + switch (status) { + case ViewStyleType.BooleanDecision: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.BOOLEAN-DECISION'); + case ViewStyleType.CheckBox: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.CHECKBOX'); + case ViewStyleType.Select: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.SELECT'); + case ViewStyleType.InternalDmpEntities: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.INTERNAL-DMP-ENTITIES'); + case ViewStyleType.FreeText: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.FREE-TEXT'); + case ViewStyleType.RadioBox: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.RADIO-BOX'); + case ViewStyleType.TextArea: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.TEXT-AREA'); + case ViewStyleType.DatePicker: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.DATE-PICKER'); + case ViewStyleType.ExternalDatasets: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.EXTERNAL-DATASETS'); + case ViewStyleType.DataRepositories: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.DATA-REPOSITORIES'); + case ViewStyleType.Registries: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.REGISTRIES'); + case ViewStyleType.Services: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.SERVICES'); + case ViewStyleType.Tags: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.TAGS'); + case ViewStyleType.Researchers: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.RESEARCHERS'); + case ViewStyleType.Organizations: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.ORGANIZATIONS'); + case ViewStyleType.DatasetIdentifier: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.DATASET-IDENTIFIER'); + case ViewStyleType.Currency: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.CURRENCY'); + case ViewStyleType.Validation: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.VALIDATION'); + case ViewStyleType.Other: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.OTHER'); + } + + } toDatasetProfileComboBoxTypeString(status: DatasetProfileComboBoxType): string { switch (status) { @@ -122,4 +150,23 @@ export class EnumUtils { case Role.Member: return this.language.instant('FACET-SEARCH.ROLE.MEMBER'); } } + + toRoleOrganizationString(status: RoleOrganizationType): string { + switch (status) { + case RoleOrganizationType.Faculty: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.FACULTY'); + case RoleOrganizationType.Librarian: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.LIBRARIAN'); + case RoleOrganizationType.Researcher: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.RESEARCHER'); + case RoleOrganizationType.Student: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.STUDENT'); + case RoleOrganizationType.EarlyCareerResearcher: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.EARLY-CAREER-RESEARCHER'); + case RoleOrganizationType.ResearchAdministrator: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.RESEARCH-ADMINISTRATOR'); + case RoleOrganizationType.RepositoryManager: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.REPOSITORY-MANAGER'); + case RoleOrganizationType.ResearchInfrastructure: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.RESEARCH-INFRASTRUCTURE'); + case RoleOrganizationType.ServiceProvider: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.SERVICE-PROVIDER'); + case RoleOrganizationType.Publisher: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.PUBLISHER'); + case RoleOrganizationType.ResearchFunder: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.RESEARCH-FUNDER'); + case RoleOrganizationType.Policymaker: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.POLICY-MAKER'); + case RoleOrganizationType.SMEIndustry: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.SME-INDUSTRY'); + case RoleOrganizationType.Other: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.OTHER'); + } + } } diff --git a/dmp-frontend/src/app/core/special-auth-guard.service.ts b/dmp-frontend/src/app/core/special-auth-guard.service.ts new file mode 100644 index 000000000..6c311f418 --- /dev/null +++ b/dmp-frontend/src/app/core/special-auth-guard.service.ts @@ -0,0 +1,61 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, CanLoad, Route, Router, RouterStateSnapshot } from '@angular/router'; +import { AuthService } from './services/auth/auth.service'; +import { AppRole } from './common/enum/app-role'; + +@Injectable() +export class SpecialAuthGuard implements CanActivate, CanLoad { + constructor(private auth: AuthService, private router: Router) { + } + + hasPermission(permission: AppRole): boolean { + if (!this.auth.current()) { return false; } + const principalRoles = this.auth.current().authorities; + for (let i = 0; i < principalRoles.length; i++) { + if (principalRoles[i] === permission) { + return true; + } + } + return false; + } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + const url: string = state.url; + const permissions = route.data['authContext']['permissions']; + let count = permissions.length; + if (count < 0 || count === undefined) { + return false; + } + for (let i = 0; i < permissions.length; i++) { + if (!this.hasPermission(permissions[i])) { + count--; + } + } + if (count === 0) { + this.router.navigate(['/unauthorized'], { queryParams: { returnUrl: url } }); + return false; + } else { + return true; + } + } + + canLoad(route: Route): boolean { + const url = `/${route.path}`; + const permissions = route.data['authContext']['permissions']; + let count = permissions.length; + if (count < 0 || count === undefined) { + return false; + } + for (let i = 0; i < permissions.length; i++) { + if (!this.hasPermission(permissions[i])) { + count--; + } + } + if (count === 0) { + this.router.navigate(['/unauthorized'], { queryParams: { returnUrl: url } }); + return false; + } else { + return true; + } + } +} diff --git a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete-configuration.ts b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete-configuration.ts index ebb0be39f..32b99b46c 100644 --- a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete-configuration.ts +++ b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete-configuration.ts @@ -31,7 +31,9 @@ export interface MultipleAutoCompleteConfiguration { optionTemplate?: TemplateRef; // Selected value formating template selectedValueTemplate?: TemplateRef; - + // Display icon that opens popup + popupItemActionIcon?: string; autoSelectFirstOptionOnBlur?: boolean; + } diff --git a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.html b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.html index ae3805b29..3c89cd3ff 100644 --- a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.html +++ b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.html @@ -10,9 +10,9 @@ - + + --> arrow_drop_down @@ -22,10 +22,13 @@ -
- {{_titleFn(item)}} -
- {{_subtitleFn(item)}} +
+
+ {{_titleFn(item)}} +
+ {{_subtitleFn(item)}} +
+ {{popupItemActionIcon}}
@@ -38,10 +41,13 @@ -
- {{_titleFn(item)}} -
- {{_subtitleFn(item)}} +
+
+ {{_titleFn(item)}} +
+ {{_subtitleFn(item)}} +
+ {{popupItemActionIcon}}
diff --git a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.scss b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.scss index 0888bcfa6..76cfcb18e 100644 --- a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.scss +++ b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.scss @@ -8,10 +8,10 @@ .align-arrow-right { - position: absolute; + // position: absolute; right: 0; - bottom: 0; - vertical-align: middle; + // bottom: 0; + // vertical-align: middle; cursor: pointer; align-self: center; color: rgba(0, 0, 0, 0.54); @@ -19,6 +19,49 @@ } +.title-fn { + flex-grow: 1; + white-space: nowrap; + width: calc(100% - 16px); + overflow: hidden; + text-overflow: ellipsis; +} + +.option-icon { + mat-icon { + margin: 0px 5px 0px 10px; + } + mat-icon:hover { + color: #129d99; + } +} + .two-line-mat-option { line-height: 1.2em; } + +::ng-deep .mat-chip-list-wrapper { + max-height: 4.8em !important; + overflow: auto !important; +} +.hide-placeholder{ + &::-webkit-input-placeholder { + /* WebKit browsers */ + color: transparent; + } + &:-moz-placeholder { + /* Mozilla Firefox 4 to 18 */ + color: transparent; + } + &::-moz-placeholder { + /* Mozilla Firefox 19+ */ + color: transparent; + } + &:-ms-input-placeholder { + /* Internet Explorer 10+ */ + color: transparent; + } + &::placeholder { + color: transparent; + } + } \ No newline at end of file diff --git a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.ts b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.ts index 5f05404a0..fd9854690 100644 --- a/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.ts +++ b/dmp-frontend/src/app/library/auto-complete/multiple/multiple-auto-complete.component.ts @@ -1,5 +1,5 @@ import { FocusMonitor } from '@angular/cdk/a11y'; -import { COMMA, ENTER } from '@angular/cdk/keycodes'; +import { BACKSPACE, COMMA, ENTER } from '@angular/cdk/keycodes'; import { Component, DoCheck, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Optional, Output, Self, SimpleChanges, TemplateRef, ViewChild } from '@angular/core'; import { ControlValueAccessor, FormGroupDirective, NgControl, NgForm } from '@angular/forms'; import { ErrorStateMatcher, MatChipInputEvent, mixinErrorState } from '@angular/material'; @@ -45,6 +45,9 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp @Output() optionSelected: EventEmitter = new EventEmitter(); @Output() optionRemoved: EventEmitter = new EventEmitter(); + @Output() optionActionClicked: EventEmitter = new EventEmitter(); + + id = `multiple-autocomplete-${MultipleAutoCompleteComponent.nextId++}`; stateChanges = new Subject(); focused = false; @@ -63,6 +66,7 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp get shouldLabelFloat() { return this.focused || !this.empty; } + @Input() hidePlaceholder: boolean = false; @Input() get placeholder() { return this._placeholder; } set placeholder(placeholder) { @@ -198,6 +202,16 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp if (event.keyCode !== ENTER && (event.keyCode < 37 || event.keyCode > 40)) { this._inputSubject.next(this.inputValue); } + + // if (event.keyCode !== ENTER && (event.keyCode < 37 || event.keyCode > 40) && event.keyCode !== COMMA) { + // this._inputSubject.next(this.inputValue); + // } + } + + public onKeyDown(event) { + if (event.keyCode === BACKSPACE && event.target.value.length === 0 && this._selectedItems.size > 0) { + this._removeSelectedItem(Array.from(this._selectedItems.values()).pop(), event); + } } private _setValue(value: any) { @@ -223,6 +237,12 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp if (this.inputValue && this.inputValue.length > 1 && this.autocomplete.options && this.autocomplete.options.length > 0 && this.autoSelectFirstOptionOnBlur) { this.optionSelectedInternal(this.autocomplete.options.first.value); } + + // Clear text if not an option + if (this.inputValue && this.inputValue.length > 1) { + this.inputValue = ''; + document.getElementById(($event.target).id).focus(); + } } @@ -301,6 +321,10 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp return this.configuration.autoSelectFirstOptionOnBlur != null ? this.configuration.autoSelectFirstOptionOnBlur : false; } + get popupItemActionIcon(): string { + return this.configuration.popupItemActionIcon != null ? this.configuration.popupItemActionIcon : ''; + } + //Chip Functions _addItem(event: MatChipInputEvent): void { @@ -329,4 +353,9 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp this.autocompleteInput.nativeElement.focus(); this.pushChanges(this.value); } + + _optionActionClick(item: any, event: MouseEvent): void { + if (event != null) { event.stopPropagation(); } + this.optionActionClicked.emit(item); + } } diff --git a/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete-configuration.ts b/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete-configuration.ts index dad7fcf59..fbb609e3c 100644 --- a/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete-configuration.ts +++ b/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete-configuration.ts @@ -32,6 +32,8 @@ export interface SingleAutoCompleteConfiguration { // Selected value formating template selectedValueTemplate?: TemplateRef; + // To revert: "We set the items observable on focus to avoid the request being executed on component load." + forceFocus?: boolean; autoSelectFirstOptionOnBlur?: boolean; } diff --git a/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete.component.ts b/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete.component.ts index ea1b767e3..bbcd65fa8 100644 --- a/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete.component.ts +++ b/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete.component.ts @@ -220,7 +220,7 @@ export class SingleAutoCompleteComponent extends _CustomComponentMixinBase imple _onInputFocus() { // We set the items observable on focus to avoid the request being executed on component load. - if (!this._items) { + if (!this._items || this.forceFocus) { this._items = this._inputSubject.pipe( startWith(null), debounceTime(this.requestDelay), @@ -242,6 +242,12 @@ export class SingleAutoCompleteComponent extends _CustomComponentMixinBase imple this.inputValue = this._displayFn(this.autocomplete.options.first.value); this.optionSelectedInternal(this.autocomplete.options.first.value); } + + // Clear text if not an option + else if (this.inputValue && this.inputValue.length > 1) { + this.inputValue = ''; + document.getElementById(($event.target).id).focus(); + } } onChange = (_: any) => { }; @@ -321,4 +327,8 @@ export class SingleAutoCompleteComponent extends _CustomComponentMixinBase imple get autoSelectFirstOptionOnBlur(): boolean { return this.configuration.autoSelectFirstOptionOnBlur != null ? this.configuration.autoSelectFirstOptionOnBlur : false; } + + get forceFocus(): boolean { + return this.configuration.forceFocus != null ? this.configuration.forceFocus : false; + } } diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.component.scss b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.scss index ae71afc9d..71232d8f8 100644 --- a/dmp-frontend/src/app/library/guided-tour/guided-tour.component.scss +++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.scss @@ -1,246 +1,260 @@ ngx-guided-tour { - .guided-tour-user-input-mask { - position: fixed; - top: 0; - left: 0; - display: block; - height: 100%; - width: 100%; - max-height: 100vh; - text-align: center; - opacity: 0; - } + .guided-tour-user-input-mask { + position: fixed; + top: 0; + left: 0; + display: block; + height: 100%; + width: 100%; + max-height: 100vh; + text-align: center; + opacity: 0; + } - .guided-tour-spotlight-overlay { - position: fixed; - box-shadow: 0 0 0 9999px rgba(0,0,0,.7), 0 0 1.5rem rgba(0,0,0,.5); - border-radius: 44px; /*custom add*/ - } + .guided-tour-spotlight-overlay { + position: fixed; + box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.7), 0 0 1.5rem rgba(0, 0, 0, 0.5); + border-radius: 44px; /*custom add*/ + } - .tour-orb { - position: fixed; - width: 20px; - height: 20px; - border-radius: 50%; + .tour-orb { + position: fixed; + width: 20px; + height: 20px; + border-radius: 50%; - .tour-orb-ring { - width: 35px; - height: 35px; - position: relative; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - animation: pulse 2s linear infinite; + .tour-orb-ring { + width: 35px; + height: 35px; + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + animation: pulse 2s linear infinite; - &:after { - content: ''; - display: inline-block; - height: 100%; - width: 100%; - border-radius: 50%; - } - } + &:after { + content: ""; + display: inline-block; + height: 100%; + width: 100%; + border-radius: 50%; + } + } - @keyframes pulse { - from { - transform: translate(-50%, -50%) scale(0.45); - opacity: 1.0; - } - to { - transform: translate(-50%, -50%) scale(1); - opacity: 0.0; - } - } - } + @keyframes pulse { + from { + transform: translate(-50%, -50%) scale(0.45); + opacity: 1; + } + to { + transform: translate(-50%, -50%) scale(1); + opacity: 0; + } + } + } - .tour-step { - position: fixed; - &.page-tour-step { - // max-width: 400px; - max-width: 1043px; /*custom add*/ - width: 50%; - left: 50%; - top: 50%; - border-radius: 5px; - transform: translate(-50%, -50%) - } - &.tour-bottom, &.tour-bottom-right, &.tour-bottom-left { - .tour-arrow::before { - position: absolute; - } - .tour-block { - margin-top: 10px; - } - } + .tour-step { + position: fixed; + &.page-tour-step { + // max-width: 400px; + max-width: 1043px; /*custom add*/ + width: 50%; + left: 50%; + top: 50%; + border-radius: 5px; + transform: translate(-50%, -50%); + } + &.tour-bottom, + &.tour-bottom-right, + &.tour-bottom-left { + .tour-arrow::before { + position: absolute; + } + .tour-block { + margin-top: 10px; + } + } - &.tour-top, &.tour-top-right, &.tour-top-left { - margin-bottom: 10px; + &.tour-top, + &.tour-top-right, + &.tour-top-left { + margin-bottom: 10px; - .tour-arrow::before { - position: absolute; - bottom: 0; - } - .tour-block { - margin-bottom: 10px; - } - } + .tour-arrow::before { + position: absolute; + bottom: 0; + } + .tour-block { + margin-bottom: 10px; + } + } - &.tour-bottom , &.tour-top { - .tour-arrow::before { - // transform: translateX(-50%); - // left: 50%; - /*custom add*/ - transform: scale(2); - left: 494px; - } - /*custom add*/ - margin-top: 20px; - margin-left: 10px; - } + &.tour-bottom, + &.tour-top { + .tour-arrow::before { + // transform: translateX(-50%); + // left: 50%; + /*custom add*/ + transform: scale(2); + left: 494px; + } + /*custom add*/ + margin-top: 20px; + margin-left: 10px; + } - &.tour-bottom-right, &.tour-top-right { - .tour-arrow::before { - transform: translateX(-100%); - left: calc(100% - 5px); - } - } + &.tour-bottom-right, + &.tour-top-right { + .tour-arrow::before { + transform: translateX(-100%); + left: calc(100% - 5px); + } + } - &.tour-bottom-left, &.tour-top-left { - .tour-arrow::before { - left: 5px; - } - } + &.tour-bottom-left, + &.tour-top-left { + .tour-arrow::before { + left: 5px; + } + } - &.tour-left { - .tour-arrow::before { - position: absolute; - left: 100%; - transform: translateX(-100%); - top: 5px; - } - .tour-block { - margin-right: 10px; - } - } + &.tour-left { + .tour-arrow::before { + position: absolute; + left: 100%; + transform: translateX(-100%); + top: 5px; + } + .tour-block { + margin-right: 10px; + } + } - &.tour-right { - margin-left: 10px; /*custom add*/ - .tour-arrow::before { - transform: scale(1.5); /*custom add*/ - position: absolute; - left: 0; - top: 5px; - } - .tour-block { - margin-top: -15px; /*custom add*/ - margin-left: 10px; - } - } + &.tour-right { + margin-left: 10px; /*custom add*/ + .tour-arrow::before { + transform: scale(1.5); /*custom add*/ + position: absolute; + left: 0; + top: 5px; + } + .tour-block { + margin-top: -15px; /*custom add*/ + margin-left: 10px; + } + } - .tour-block { - // padding: 15px 25px; + .tour-block { + // padding: 15px 25px; - /*custom add*/ - padding: 15px 25px 15px 0px; - max-height: 348px; - border-radius: 5px; - } + /*custom add*/ + padding: 15px 25px 15px 0px; + max-height: 348px; + border-radius: 5px; + } - .tour-title { - // font-weight: bold !important; - // padding-bottom: 20px; + .tour-title { + // font-weight: bold !important; + // padding-bottom: 20px; - /*custom add*/ - font-weight: lighter !important; - font-size: 16px !important; - padding: 28px 5px 0px 65px; - overflow: auto; - text-align: left; - color: #212121; - line-height: 26px; - white-space:pre-line; - height: 210px; - } + /*custom add*/ + font-weight: lighter !important; + font-size: 16px !important; + padding: 28px 5px 25px 65px; + overflow: auto; + text-align: left; + color: #212121; + line-height: 26px; + white-space: pre-line; + // height: 210px; + } - h3.tour-title { - font-size: 20px; - } - h2.tour-title { - font-size: 30px; - } + h3.tour-title { + font-size: 20px; + } + h2.tour-title { + font-size: 30px; + } - .tour-buttons { - overflow: hidden; // clearfix - padding: 10px 70px 30px 65px; /*custom add*/ + .tour-buttons { + overflow: hidden; // clearfix + padding: 10px 70px 18px 65px; /*custom add*/ + display: flex; + justify-content: space-between; - button.link-button { - padding-left: 0; - font-size: 15px; - font-weight: bold; - max-width: none !important; - cursor: pointer; - text-align: center; - white-space: nowrap; - vertical-align: middle; - border: 1px solid transparent; - line-height: 1.5; - background-color: transparent; - position: relative; - outline: none; - padding: 0 15px; - -webkit-appearance: button; - } + button.link-button { + padding-left: 0; + font-size: 15px; + font-weight: bold; + max-width: none !important; + cursor: pointer; + text-align: center; + white-space: nowrap; + vertical-align: middle; + border: 1px solid transparent; + line-height: 1.5; + background-color: transparent; + position: relative; + outline: none; + padding: 0 15px; + -webkit-appearance: button; + } - button.skip-button.link-button { - // padding-left: 0; - border-left: 0; + button.skip-button.link-button { + // padding-left: 0; + border-left: 0; - /*custom add*/ - padding: 0; - float: right; - width: 133px; - height: 40px; - border: 1px solid #129D99; - background: #FFFFFF 0% 0% no-repeat padding-box; - color: #129D99; - } + /*custom add*/ + // padding: 0; + padding-right: 1em; + padding-left: 1em; + // float: right; + min-width: 133px; + height: 40px; + border: 1px solid #129d99; + background: #ffffff 0% 0% no-repeat padding-box; + color: #129d99; + } - .next-button { - cursor: pointer; - // border-radius: 1px; - // float: right; - border: none; - outline: none; - // padding-left: 10px; - // padding-right: 10px; + .next-button { + cursor: pointer; + // border-radius: 1px; + // float: right; + border: none; + outline: none; + // padding-left: 10px; + // padding-right: 10px; - /*custom add*/ - float: left; - padding: 10px 0px;; - width: 101px; - background: #129D99 0% 0% no-repeat padding-box; - } + /*custom add*/ + // float: left; + // padding: 10px 0px; + padding-left: 1em; + padding-right: 1em; + min-width: 101px; + background: #129d99 0% 0% no-repeat padding-box; + } - /*custom add*/ - button.skip-button.link-button, .next-button { - font-size: 14px; - font-weight: bold; - letter-spacing: 0.35px; - height: 40px; - box-shadow: 0px 3px 6px #1E202029; - border-radius: 30px; - } - } + /*custom add*/ + button.skip-button.link-button, + .next-button { + font-size: 14px; + font-weight: bold; + letter-spacing: 0.35px; + height: 40px; + box-shadow: 0px 3px 6px #1e202029; + border-radius: 30px; + } + } - /*custom add*/ - .argos-present-img { - background: url("../../../assets/splash/assets/img/2_Sign_in/Svg/Sign\ In.svg") no-repeat; - min-width: 176px; - height: 220px; - position: relative; - top: 110px; - left: -85px; - border-top: none; - } - } + /*custom add*/ + .argos-present-img { + background: url("../../../assets/splash/assets/img/2_Sign_in/Svg/Sign\ In.svg") no-repeat; + min-width: 176px; + height: 220px; + position: relative; + top: 110px; + left: -85px; + border-top: none; + } + } } diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.component.ts b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.ts index 913ab6d1b..e093b543b 100644 --- a/dmp-frontend/src/app/library/guided-tour/guided-tour.component.ts +++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.component.ts @@ -388,7 +388,10 @@ export class GuidedTourComponent implements AfterViewInit, OnDestroy { } const scrollAdjustment = this.currentTourStep.scrollAdjustment ? this.currentTourStep.scrollAdjustment : 0; - const tourStepHeight = typeof this.tourStep.nativeElement.getBoundingClientRect === 'function' ? this.tourStep.nativeElement.getBoundingClientRect().height : 0; + let tourStepHeight = 0; + if (this.tourStep != null && this.tourStep.nativeElement != null && typeof this.tourStep.nativeElement.getBoundingClientRect === 'function') { + tourStepHeight = this.tourStep.nativeElement.getBoundingClientRect().height; + } const elementHeight = this.selectedElementRect.height + scrollAdjustment + tourStepHeight; if ((this.windowRef.nativeWindow.innerHeight - this.topOfPageAdjustment) < elementHeight) { diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.service.ts b/dmp-frontend/src/app/library/guided-tour/guided-tour.service.ts index 2823f24d0..1250ceac9 100644 --- a/dmp-frontend/src/app/library/guided-tour/guided-tour.service.ts +++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.service.ts @@ -1,4 +1,4 @@ -import { debounceTime } from 'rxjs/operators'; +import { debounceTime, delay } from 'rxjs/operators'; import { ErrorHandler, Inject, Injectable } from '@angular/core'; import { Observable, Subject, fromEvent } from 'rxjs'; import { GuidedTour, TourStep, Orientation, OrientationConfiguration } from './guided-tour.constants'; @@ -8,222 +8,224 @@ import { WindowRefService } from "./windowref.service"; @Injectable() export class GuidedTourService { - public guidedTourCurrentStepStream: Observable; - public guidedTourOrbShowingStream: Observable; + public guidedTourCurrentStepStream: Observable; + public guidedTourOrbShowingStream: Observable; - private _guidedTourCurrentStepSubject = new Subject(); - private _guidedTourOrbShowingSubject = new Subject(); - private _currentTourStepIndex = 0; - private _currentTour: GuidedTour = null; - private _onFirstStep = true; - private _onLastStep = true; - private _onResizeMessage = false; + private _guidedTourCurrentStepSubject = new Subject(); + private _guidedTourOrbShowingSubject = new Subject(); + private _currentTourStepIndex = 0; + private _currentTour: GuidedTour = null; + private _onFirstStep = true; + private _onLastStep = true; + private _onResizeMessage = false; - constructor( - public errorHandler: ErrorHandler, - private windowRef: WindowRefService, - @Inject(DOCUMENT) private dom - ) { - this.guidedTourCurrentStepStream = this._guidedTourCurrentStepSubject.asObservable(); - this.guidedTourOrbShowingStream = this._guidedTourOrbShowingSubject.asObservable(); + constructor( + public errorHandler: ErrorHandler, + private windowRef: WindowRefService, + @Inject(DOCUMENT) private dom + ) { + this.guidedTourCurrentStepStream = this._guidedTourCurrentStepSubject.asObservable(); + this.guidedTourOrbShowingStream = this._guidedTourOrbShowingSubject.asObservable(); - fromEvent(this.windowRef.nativeWindow, 'resize').pipe(debounceTime(200)).subscribe(() => { - if (this._currentTour && this._currentTourStepIndex > -1) { - if (this._currentTour.minimumScreenSize && this._currentTour.minimumScreenSize >= this.windowRef.nativeWindow.innerWidth) { - this._onResizeMessage = true; - const dialog = this._currentTour.resizeDialog || { - title: 'Please resize', - content: 'You have resized the tour to a size that is too small to continue. Please resize the browser to a larger size to continue the tour or close the tour.' - }; + fromEvent(this.windowRef.nativeWindow, 'resize').pipe(debounceTime(200)).subscribe(() => { + if (this._currentTour && this._currentTourStepIndex > -1) { + if (this._currentTour.minimumScreenSize && this._currentTour.minimumScreenSize >= this.windowRef.nativeWindow.innerWidth) { + this._onResizeMessage = true; + const dialog = this._currentTour.resizeDialog || { + title: 'Please resize', + content: 'You have resized the tour to a size that is too small to continue. Please resize the browser to a larger size to continue the tour or close the tour.' + }; - this._guidedTourCurrentStepSubject.next(dialog); - } else { - this._onResizeMessage = false; - this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); - } + this._guidedTourCurrentStepSubject.next(dialog); + } else { + this._onResizeMessage = false; + this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); + } + } + }); + } + + public nextStep(): void { + if (this._currentTour.steps[this._currentTourStepIndex].closeAction) { + this._currentTour.steps[this._currentTourStepIndex].closeAction(); + } + if (this._currentTour.steps[this._currentTourStepIndex + 1]) { + this._currentTourStepIndex++; + this._setFirstAndLast(); + if (this._currentTour.steps[this._currentTourStepIndex].action) { + this._currentTour.steps[this._currentTourStepIndex].action(); + // Usually an action is opening something so we need to give it time to render. + setTimeout(() => { + if (this._checkSelectorValidity()) { + this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); + } else { + this.nextStep(); + } + }); + } else { + setTimeout(() => { + if (this._checkSelectorValidity()) { + this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); + } else { + this.nextStep(); + } + }, 500); } - }); - } + } else { + if (this._currentTour.completeCallback) { + this._currentTour.completeCallback(); + } + this.resetTour(); + } + } - public nextStep(): void { - if (this._currentTour.steps[this._currentTourStepIndex].closeAction) { - this._currentTour.steps[this._currentTourStepIndex].closeAction(); - } - if (this._currentTour.steps[this._currentTourStepIndex + 1]) { - this._currentTourStepIndex++; - this._setFirstAndLast(); - if (this._currentTour.steps[this._currentTourStepIndex].action) { - this._currentTour.steps[this._currentTourStepIndex].action(); - // Usually an action is opening something so we need to give it time to render. - setTimeout(() => { - if (this._checkSelectorValidity()) { - this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); - } else { - this.nextStep(); - } - }); - } else { - if (this._checkSelectorValidity()) { - this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); - } else { - this.nextStep(); - } - } - } else { - if (this._currentTour.completeCallback) { - this._currentTour.completeCallback(); - } - this.resetTour(); - } - } + public backStep(): void { + if (this._currentTour.steps[this._currentTourStepIndex].closeAction) { + this._currentTour.steps[this._currentTourStepIndex].closeAction(); + } + if (this._currentTour.steps[this._currentTourStepIndex - 1]) { + this._currentTourStepIndex--; + this._setFirstAndLast(); + if (this._currentTour.steps[this._currentTourStepIndex].action) { + this._currentTour.steps[this._currentTourStepIndex].action(); + setTimeout(() => { + if (this._checkSelectorValidity()) { + this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); + } else { + this.backStep(); + } + }); + } else { + if (this._checkSelectorValidity()) { + this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); + } else { + this.backStep(); + } + } + } else { + this.resetTour(); + } + } - public backStep(): void { - if (this._currentTour.steps[this._currentTourStepIndex].closeAction) { - this._currentTour.steps[this._currentTourStepIndex].closeAction(); - } - if (this._currentTour.steps[this._currentTourStepIndex - 1]) { - this._currentTourStepIndex--; - this._setFirstAndLast(); - if (this._currentTour.steps[this._currentTourStepIndex].action) { - this._currentTour.steps[this._currentTourStepIndex].action(); - setTimeout(() => { - if (this._checkSelectorValidity()) { - this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); - } else { - this.backStep(); - } - }); - } else { - if (this._checkSelectorValidity()) { - this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); - } else { - this.backStep(); - } - } - } else { - this.resetTour(); - } - } + public skipTour(): void { + if (this._currentTour.skipCallback) { + this._currentTour.skipCallback(this._currentTourStepIndex); + } + this.resetTour(); + } - public skipTour(): void { - if (this._currentTour.skipCallback) { - this._currentTour.skipCallback(this._currentTourStepIndex); - } - this.resetTour(); - } + public resetTour(): void { + this.dom.body.classList.remove('tour-open'); + this._currentTour = null; + this._currentTourStepIndex = 0; + this._guidedTourCurrentStepSubject.next(null); + } - public resetTour(): void { - this.dom.body.classList.remove('tour-open'); - this._currentTour = null; - this._currentTourStepIndex = 0; - this._guidedTourCurrentStepSubject.next(null); - } + public startTour(tour: GuidedTour): void { + this._currentTour = cloneDeep(tour); + this._currentTour.steps = this._currentTour.steps.filter(step => !step.skipStep); + this._currentTourStepIndex = 0; + this._setFirstAndLast(); + this._guidedTourOrbShowingSubject.next(this._currentTour.useOrb); + if ( + this._currentTour.steps.length > 0 + && (!this._currentTour.minimumScreenSize + || (this.windowRef.nativeWindow.innerWidth >= this._currentTour.minimumScreenSize)) + ) { + if (!this._currentTour.useOrb) { + this.dom.body.classList.add('tour-open'); + } + if (this._currentTour.steps[this._currentTourStepIndex].action) { + this._currentTour.steps[this._currentTourStepIndex].action(); + } + if (this._checkSelectorValidity()) { + this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); + } else { + this.nextStep(); + } + } + } - public startTour(tour: GuidedTour): void { - this._currentTour = cloneDeep(tour); - this._currentTour.steps = this._currentTour.steps.filter(step => !step.skipStep); - this._currentTourStepIndex = 0; - this._setFirstAndLast(); - this._guidedTourOrbShowingSubject.next(this._currentTour.useOrb); - if ( - this._currentTour.steps.length > 0 - && (!this._currentTour.minimumScreenSize - || (this.windowRef.nativeWindow.innerWidth >= this._currentTour.minimumScreenSize)) - ) { - if (!this._currentTour.useOrb) { - this.dom.body.classList.add('tour-open'); - } - if (this._currentTour.steps[this._currentTourStepIndex].action) { - this._currentTour.steps[this._currentTourStepIndex].action(); - } - if (this._checkSelectorValidity()) { - this._guidedTourCurrentStepSubject.next(this.getPreparedTourStep(this._currentTourStepIndex)); - } else { - this.nextStep(); - } - } - } + public activateOrb(): void { + this._guidedTourOrbShowingSubject.next(false); + this.dom.body.classList.add('tour-open'); + } - public activateOrb(): void { - this._guidedTourOrbShowingSubject.next(false); - this.dom.body.classList.add('tour-open'); - } + private _setFirstAndLast(): void { + this._onLastStep = (this._currentTour.steps.length - 1) === this._currentTourStepIndex; + this._onFirstStep = this._currentTourStepIndex === 0; + } - private _setFirstAndLast(): void { - this._onLastStep = (this._currentTour.steps.length - 1) === this._currentTourStepIndex; - this._onFirstStep = this._currentTourStepIndex === 0; - } + private _checkSelectorValidity(): boolean { + if (this._currentTour.steps[this._currentTourStepIndex].selector) { + const selectedElement = this.dom.querySelector(this._currentTour.steps[this._currentTourStepIndex].selector); + if (!selectedElement) { + this.errorHandler.handleError( + // If error handler is configured this should not block the browser. + new Error(`Error finding selector ${this._currentTour.steps[this._currentTourStepIndex].selector} on step ${this._currentTourStepIndex + 1} during guided tour: ${this._currentTour.tourId}`) + ); + return false; + } + } + return true; + } - private _checkSelectorValidity(): boolean { - if (this._currentTour.steps[this._currentTourStepIndex].selector) { - const selectedElement = this.dom.querySelector(this._currentTour.steps[this._currentTourStepIndex].selector); - if (!selectedElement) { - this.errorHandler.handleError( - // If error handler is configured this should not block the browser. - new Error(`Error finding selector ${this._currentTour.steps[this._currentTourStepIndex].selector} on step ${this._currentTourStepIndex + 1} during guided tour: ${this._currentTour.tourId}`) - ); - return false; - } - } - return true; - } + public get onLastStep(): boolean { + return this._onLastStep; + } - public get onLastStep(): boolean { - return this._onLastStep; - } + public get onFirstStep(): boolean { + return this._onFirstStep; + } - public get onFirstStep(): boolean { - return this._onFirstStep; - } + public get onResizeMessage(): boolean { + return this._onResizeMessage; + } - public get onResizeMessage(): boolean { - return this._onResizeMessage; - } + public get currentTourStepDisplay(): number { + return this._currentTourStepIndex + 1; + } - public get currentTourStepDisplay(): number { - return this._currentTourStepIndex + 1; - } + public get currentTourStepCount(): number { + return this._currentTour && this._currentTour.steps ? this._currentTour.steps.length : 0; + } - public get currentTourStepCount(): number { - return this._currentTour && this._currentTour.steps ? this._currentTour.steps.length : 0; - } + public get preventBackdropFromAdvancing(): boolean { + return this._currentTour && this._currentTour.preventBackdropFromAdvancing; + } - public get preventBackdropFromAdvancing(): boolean { - return this._currentTour && this._currentTour.preventBackdropFromAdvancing; - } + private getPreparedTourStep(index: number): TourStep { + return this.setTourOrientation(this._currentTour.steps[index]); + } - private getPreparedTourStep(index: number): TourStep { - return this.setTourOrientation(this._currentTour.steps[index]); - } + private setTourOrientation(step: TourStep): TourStep { + const convertedStep = cloneDeep(step); + if ( + convertedStep.orientation + && !(typeof convertedStep.orientation === 'string') + && (convertedStep.orientation as OrientationConfiguration[]).length + ) { + (convertedStep.orientation as OrientationConfiguration[]).sort((a: OrientationConfiguration, b: OrientationConfiguration) => { + if (!b.maximumSize) { + return 1; + } + if (!a.maximumSize) { + return -1; + } + return b.maximumSize - a.maximumSize; + }); - private setTourOrientation(step: TourStep): TourStep { - const convertedStep = cloneDeep(step); - if ( - convertedStep.orientation - && !(typeof convertedStep.orientation === 'string') - && (convertedStep.orientation as OrientationConfiguration[]).length - ) { - (convertedStep.orientation as OrientationConfiguration[]).sort((a: OrientationConfiguration, b: OrientationConfiguration) => { - if (!b.maximumSize) { - return 1; - } - if (!a.maximumSize) { - return -1; - } - return b.maximumSize - a.maximumSize; - }); + let currentOrientation: Orientation = Orientation.Top; + (convertedStep.orientation as OrientationConfiguration[]).forEach( + (orientationConfig: OrientationConfiguration) => { + if (!orientationConfig.maximumSize || this.windowRef.nativeWindow.innerWidth <= orientationConfig.maximumSize) { + currentOrientation = orientationConfig.orientationDirection; + } + } + ); - let currentOrientation: Orientation = Orientation.Top; - (convertedStep.orientation as OrientationConfiguration[]).forEach( - (orientationConfig: OrientationConfiguration) => { - if (!orientationConfig.maximumSize || this.windowRef.nativeWindow.innerWidth <= orientationConfig.maximumSize) { - currentOrientation = orientationConfig.orientationDirection; - } - } - ); - - convertedStep.orientation = currentOrientation; - } - return convertedStep; - } + convertedStep.orientation = currentOrientation; + } + return convertedStep; + } } diff --git a/dmp-frontend/src/app/ui/about/about.component.scss b/dmp-frontend/src/app/ui/about/about.component.scss index 0e23f5206..38187f826 100644 --- a/dmp-frontend/src/app/ui/about/about.component.scss +++ b/dmp-frontend/src/app/ui/about/about.component.scss @@ -8,6 +8,6 @@ img { width: 100%; } -.about-component { - margin-top: 80px; -} +// .about-component { +// margin-top: 80px; +// } diff --git a/dmp-frontend/src/app/ui/about/about.component.ts b/dmp-frontend/src/app/ui/about/about.component.ts index e659f69de..4bc1ca7b6 100644 --- a/dmp-frontend/src/app/ui/about/about.component.ts +++ b/dmp-frontend/src/app/ui/about/about.component.ts @@ -1,18 +1,22 @@ +import { HttpClient } from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; @Component({ - selector: 'app-about-componet', - templateUrl: './about.component.html', - styleUrls: ['./about.component.scss'] + selector: 'app-about-componet', + templateUrl: './about.component.html', + styleUrls: ['./about.component.scss'] }) export class AboutComponent implements OnInit { - constructor() { + constructor( + private httpClient: HttpClient, + private matomoService: MatomoService) { + } - } - - ngOnInit() { - } + ngOnInit() { + this.matomoService.trackPageView('About'); + } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/auto-complete-single-data.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/auto-complete-single-data.ts index 929edc891..3bffd86d1 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/auto-complete-single-data.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/auto-complete-single-data.ts @@ -1,7 +1,7 @@ import { FieldDataEditorModel } from './field-data-editor-model'; import { DatasetProfileComboBoxType } from '@app/core/common/enum/dataset-profile-combo-box-type'; import { FieldDataOptionEditorModel } from './field-data-option-editor-model'; -import { FormGroup } from '@angular/forms'; +import { FormGroup, Validators } from '@angular/forms'; import { AutoCompleteFieldData, AutoCompleteSingleData } from '@app/core/model/dataset-profile-definition/field-data/field-data'; export class AutoCompleteSingleDataEditorModel extends FieldDataEditorModel { @@ -16,8 +16,8 @@ export class AutoCompleteSingleDataEditorModel extends FieldDataEditorModel = []): FormGroup { const formGroup = this.formBuilder.group({ label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.label')) }], - url: [{ value: this.url, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.url')) }], - optionsRoot: [{ value: this.optionsRoot, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.optionsRoot')) }], + url: [{ value: this.url, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.url')) },[Validators.required]], + optionsRoot: [{ value: this.optionsRoot, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.optionsRoot')) }, [Validators.required]], autoCompleteType: [{ value: this.autoCompleteType, disabled: (disabled && !skipDisable.includes('AutoCompleteSingleDataEditorModel.autoCompleteType')) }] }); formGroup.addControl('autoCompleteOptions', this.autoCompleteOptions.buildForm(disabled, skipDisable)); diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/data-repositories-data-editor-models.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/data-repositories-data-editor-models.ts index 0e3e007d2..c337a5311 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/data-repositories-data-editor-models.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/data-repositories-data-editor-models.ts @@ -1,19 +1,22 @@ import { FormGroup } from '@angular/forms'; import { FieldDataEditorModel } from './field-data-editor-model'; -import { DatePickerFieldData, ExternalDatasetsFieldData, DataRepositoriesFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; +import { DataRepositoriesFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; export class DataRepositoriesDataEditorModel extends FieldDataEditorModel { public label: string; + public multiAutoComplete: boolean; buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { const formGroup = this.formBuilder.group({ - label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('DataRepositoriesDataEditorModel.label')) }] + label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('ServicesDataEditorModel.label')) }], + multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('ServicesDataEditorModel.multiAutoComplete')) }] }); return formGroup; } fromModel(item: DataRepositoriesFieldData): DataRepositoriesDataEditorModel { this.label = item.label; + this.multiAutoComplete = item.multiAutoComplete; return this; } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/dataset-identifier-data-editor-models.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/dataset-identifier-data-editor-models.ts index 0475d1c81..42492776b 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/dataset-identifier-data-editor-models.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/dataset-identifier-data-editor-models.ts @@ -1,6 +1,6 @@ import { FormGroup } from '@angular/forms'; import { FieldDataEditorModel } from './field-data-editor-model'; -import { DatePickerFieldData, ExternalDatasetsFieldData, DataRepositoriesFieldData, RegistriesFieldData, ServicesFieldData, TagsFieldData, ResearchersFieldData, OrganizationsFieldData, DatasetIdentifierFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; +import { DatasetIdentifierFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; export class DatasetIdentifierDataEditorModel extends FieldDataEditorModel { public label: string; diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/external-datasets-data-editor-models.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/external-datasets-data-editor-models.ts index a77e31902..e8d7b018e 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/external-datasets-data-editor-models.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/external-datasets-data-editor-models.ts @@ -1,19 +1,22 @@ import { FormGroup } from '@angular/forms'; import { FieldDataEditorModel } from './field-data-editor-model'; -import { DatePickerFieldData, ExternalDatasetsFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; +import { ExternalDatasetsFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; export class ExternalDatasetsDataEditorModel extends FieldDataEditorModel { public label: string; + public multiAutoComplete: boolean; buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { const formGroup = this.formBuilder.group({ - label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('ExternalDatasetsDataEditorModel.label')) }] + label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('ExternalDatasetsDataEditorModel.label')) }], + multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('ExternalDatasetsDataEditorModel.multiAutoComplete')) }] }); return formGroup; } fromModel(item: ExternalDatasetsFieldData): ExternalDatasetsDataEditorModel { this.label = item.label; + this.multiAutoComplete = item.multiAutoComplete; return this; } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/field-data-option-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/field-data-option-editor-model.ts index 5b0067954..d13491cd1 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/field-data-option-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/field-data-option-editor-model.ts @@ -1,4 +1,4 @@ -import { FormBuilder, FormGroup } from '@angular/forms'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FieldDataOption } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; import { FieldDataEditorModel } from './field-data-editor-model'; @@ -9,8 +9,8 @@ export class FieldDataOptionEditorModel extends FieldDataEditorModel = []): FormGroup { return new FormBuilder().group({ - label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('FieldDataOptionEditorModel.label')) }], - value: [{ value: this.value, disabled: (disabled && !skipDisable.includes('FieldDataOptionEditorModel.value')) }], + label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('FieldDataOptionEditorModel.label')) },[Validators.required]], + value: [{ value: this.value, disabled: (disabled && !skipDisable.includes('FieldDataOptionEditorModel.value')) },[Validators.required]], source: [{ value: this.source, disabled: (disabled && !skipDisable.includes('FieldDataOptionEditorModel.source')) }] }); } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/organizations-data-editor-models.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/organizations-data-editor-models.ts index da70c48ec..92090f3fd 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/organizations-data-editor-models.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/organizations-data-editor-models.ts @@ -1,19 +1,22 @@ import { FormGroup } from '@angular/forms'; import { FieldDataEditorModel } from './field-data-editor-model'; -import { DatePickerFieldData, ExternalDatasetsFieldData, DataRepositoriesFieldData, RegistriesFieldData, ServicesFieldData, TagsFieldData, ResearchersFieldData, OrganizationsFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; +import { OrganizationsFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; export class OrganizationsDataEditorModel extends FieldDataEditorModel { public label: string; + public multiAutoComplete: boolean = false; buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { const formGroup = this.formBuilder.group({ - label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('OrganizationsDataEditorModel.label')) }] + label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('OrganizationsDataEditorModel.label')) }], + multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('OrganizationsDataEditorModel.multiAutoComplete')) }] }); return formGroup; } fromModel(item: OrganizationsFieldData): OrganizationsDataEditorModel { this.label = item.label; + this.multiAutoComplete = item.multiAutoComplete; return this; } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/registries-data-editor-models.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/registries-data-editor-models.ts index a8891139b..f0128ea45 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/registries-data-editor-models.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/registries-data-editor-models.ts @@ -1,19 +1,22 @@ import { FormGroup } from '@angular/forms'; import { FieldDataEditorModel } from './field-data-editor-model'; -import { DatePickerFieldData, ExternalDatasetsFieldData, DataRepositoriesFieldData, RegistriesFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; +import { RegistriesFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; export class RegistriesDataEditorModel extends FieldDataEditorModel { public label: string; + public multiAutoComplete: boolean; buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { const formGroup = this.formBuilder.group({ - label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('RegistriesDataEditorModel.label')) }] + label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('RegistriesDataEditorModel.label')) }], + multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('RegistriesDataEditorModel.multiAutoComplete')) }] }); return formGroup; } fromModel(item: RegistriesFieldData): RegistriesDataEditorModel { this.label = item.label; + this.multiAutoComplete = item.multiAutoComplete; return this; } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/researchers-data-editor-models.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/researchers-data-editor-models.ts index 0b0ed0839..6141a88f2 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/researchers-data-editor-models.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/researchers-data-editor-models.ts @@ -1,6 +1,6 @@ import { FormGroup } from '@angular/forms'; import { FieldDataEditorModel } from './field-data-editor-model'; -import { DatePickerFieldData, ExternalDatasetsFieldData, DataRepositoriesFieldData, RegistriesFieldData, ServicesFieldData, TagsFieldData, ResearchersFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; +import { ResearchersFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; export class ResearchersDataEditorModel extends FieldDataEditorModel { public label: string; diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/services-data-editor-models.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/services-data-editor-models.ts index 71d759e14..033868e4f 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/services-data-editor-models.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/services-data-editor-models.ts @@ -1,19 +1,22 @@ import { FormGroup } from '@angular/forms'; import { FieldDataEditorModel } from './field-data-editor-model'; -import { DatePickerFieldData, ExternalDatasetsFieldData, DataRepositoriesFieldData, RegistriesFieldData, ServicesFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; +import { ServicesFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; export class ServicesDataEditorModel extends FieldDataEditorModel { public label: string; + public multiAutoComplete: boolean; buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { const formGroup = this.formBuilder.group({ - label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('ServicesDataEditorModel.label')) }] + label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('ServicesDataEditorModel.label')) }], + multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('ServicesDataEditorModel.multiAutoComplete')) }] }); return formGroup; } fromModel(item: ServicesFieldData): ServicesDataEditorModel { this.label = item.label; + this.multiAutoComplete = item.multiAutoComplete; return this; } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/tags-data-editor-models.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/tags-data-editor-models.ts index 4eacb9eb1..7cbbf0662 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/tags-data-editor-models.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/tags-data-editor-models.ts @@ -1,6 +1,6 @@ import { FormGroup } from '@angular/forms'; import { FieldDataEditorModel } from './field-data-editor-model'; -import { DatePickerFieldData, ExternalDatasetsFieldData, DataRepositoriesFieldData, RegistriesFieldData, ServicesFieldData, TagsFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; +import { TagsFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; export class TagsDataEditorModel extends FieldDataEditorModel { public label: string; diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/validation-data-editor-models.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/validation-data-editor-models.ts new file mode 100644 index 000000000..7fafc1486 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-data/validation-data-editor-models.ts @@ -0,0 +1,19 @@ +import { FormGroup } from '@angular/forms'; +import { FieldDataEditorModel } from './field-data-editor-model'; +import { ValidationFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; + +export class ValidationDataEditorModel extends FieldDataEditorModel { + public label: string; + + buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { + const formGroup = this.formBuilder.group({ + label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('ValidationDataEditorModel.label')) }] + }); + return formGroup; + } + + fromModel(item: ValidationFieldData): ValidationDataEditorModel { + this.label = item.label; + return this; + } +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-editor-model.ts index fcaf00324..51ebd8030 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-editor-model.ts @@ -27,6 +27,9 @@ import { ResearchersDataEditorModel } from './field-data/researchers-data-editor import { OrganizationsDataEditorModel } from './field-data/organizations-data-editor-models'; import { DatasetIdentifierDataEditorModel } from './field-data/dataset-identifier-data-editor-models'; import { CurrencyDataEditorModel } from './field-data/currency-data-editor-models'; +import { DatasetProfileComboBoxType } from '@app/core/common/enum/dataset-profile-combo-box-type'; +import { EditorCustomValidators } from '../editor/custom-validators/editor-custom-validators'; +import { ValidationDataEditorModel } from './field-data/validation-data-editor-models'; export class FieldEditorModel extends BaseFormModel { @@ -68,13 +71,14 @@ export class FieldEditorModel extends BaseFormModel { if (this.viewStyle.renderStyle === 'datePicker') { this.data = new DatePickerDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'externalDatasets') { this.data = new ExternalDatasetsDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'dataRepositories') { this.data = new DataRepositoriesDataEditorModel().fromModel(item.data); } - if (this.viewStyle.renderStyle === 'registeries') { this.data = new RegistriesDataEditorModel().fromModel(item.data); } + if (this.viewStyle.renderStyle === 'registries') { this.data = new RegistriesDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'services') { this.data = new ServicesDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'tags') { this.data = new TagsDataEditorModel().fromModel(item.data); } - if (this.viewStyle.renderStyle === 'researchers') { this.data = new ResearchersDataEditorModel().fromModel(item.data); } + if (this.viewStyle.renderStyle === 'researchers') { this.data = new ResearchersAutoCompleteFieldDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'organizations') { this.data = new OrganizationsDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'datasetIdentifier') { this.data = new DatasetIdentifierDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'currency') { this.data = new CurrencyDataEditorModel().fromModel(item.data); } + if (this.viewStyle.renderStyle === 'validation') { this.data = new ValidationDataEditorModel().fromModel(item.data); } } } return this; @@ -96,6 +100,57 @@ export class FieldEditorModel extends BaseFormModel { if (this.data) { formGroup.addControl('data', this.data.buildForm(disabled, skipDisable)); } else { formGroup.addControl('data', new WordListFieldDataEditorModel().buildForm(disabled, skipDisable)); } + + // //append validators + + this._appendCustomValidators(formGroup); + + + // //setting up listeners + // formGroup.get('viewStyle').valueChanges.subscribe(changes=>{ + // // const viewStyleChanges:{cssClass:string, renderStyle: string} = changes; + + // this._removeCustomValidators(formGroup); + // this._appendCustomValidators(formGroup); + + // }) + return formGroup; } + + + private _appendCustomValidators(formGroup: FormGroup){ + const renderStyleValue = formGroup.get('viewStyle').get('renderStyle').value; + if(renderStyleValue === 'checkBox'){ + formGroup.get('defaultValue').get('value').setValidators(Validators.required); + formGroup.get('defaultValue').get('value').updateValueAndValidity(); + }else if(renderStyleValue === 'combobox'){ + try{ + const comboType = formGroup.get('data').get('type').value; + if(comboType === DatasetProfileComboBoxType.Autocomplete){//As 'Other' in UI + formGroup.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('autoCompleteSingleDataList')); + }else if(comboType === DatasetProfileComboBoxType.WordList){ + formGroup.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); + } + }catch(e){ + console.error('Error setting validators.'); + console.error(e); + } + }else if(renderStyleValue === 'radiobox'){ + formGroup.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); + } + formGroup.get('data').updateValueAndValidity(); + } + + // private _removeCustomValidators(formGroup:FormGroup){ + // const renderStyleValue = formGroup.get('viewStyle').get('renderStyle').value; + // if(renderStyleValue != 'checkBox'){ + // formGroup.get('defaultValue').get('value').clearValidators(); + // }else if(renderStyleValue === 'combobox'){ + // formGroup.get('data').clearValidators(); + // } + // } + // private _buildData(formGroup: FormGroup){ + + // } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-set-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-set-editor-model.ts index 37e9f1fe4..052bd353b 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-set-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/field-set-editor-model.ts @@ -35,7 +35,7 @@ export class FieldSetEditorModel extends BaseFormModel { const formGroup = this.formBuilder.group({ id: [{ value: this.id, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.id')) }, [Validators.required, Validators.pattern('^[^<_>]+$')]], ordinal: [{ value: this.ordinal, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.ordinal')) }], - title: [{ value: this.title, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.title')) }], + title: [{ value: this.title, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.title')) }, [Validators.required]], description: [{ value: this.description, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.description')) }], extendedDescription: [{ value: this.extendedDescription, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.extendedDescription')) }], additionalInformation: [{ value: this.additionalInformation, disabled: (disabled && !skipDisable.includes('FieldSetEditorModel.additionalInformation')) }], diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/rule-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/rule-editor-model.ts index 6d029f80e..6e878666c 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/rule-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/rule-editor-model.ts @@ -1,4 +1,4 @@ -import { FormGroup } from "@angular/forms"; +import { FormGroup, Validators } from "@angular/forms"; import { Rule } from "../../../../core/model/admin/dataset-profile/dataset-profile"; import { BaseFormModel } from "../../../../core/model/base-form-model"; @@ -21,9 +21,9 @@ export class RuleEditorModel extends BaseFormModel { buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { const formGroup = this.formBuilder.group({ // sourceField: [this.sourceField], - target: [{ value: this.target, disabled: (disabled && !skipDisable.includes('RuleEditorModel.target')) }], + target: [{ value: this.target, disabled: (disabled && !skipDisable.includes('RuleEditorModel.target')) }, [Validators.required]], ruleStyle: [{ value: this.ruleStyle, disabled: (disabled && !skipDisable.includes('RuleEditorModel.ruleStyle')) }], - value: [{ value: this.value, disabled: (disabled && !skipDisable.includes('RuleEditorModel.value')) }], + value: [{ value: this.value, disabled: (disabled && !skipDisable.includes('RuleEditorModel.value')) }, [Validators.required]], ruleType: [{ value: this.ruleType, disabled: (disabled && !skipDisable.includes('RuleEditorModel.ruleType')) }], valueType: [{ value: this.valueType, disabled: (disabled && !skipDisable.includes('RuleEditorModel.valueType')) }] }); diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/section-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/section-editor-model.ts index 32eefaa3b..9d7c7825b 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/section-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/section-editor-model.ts @@ -1,6 +1,7 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { Section } from '../../../../core/model/admin/dataset-profile/dataset-profile'; import { BaseFormModel } from '../../../../core/model/base-form-model'; +import { EditorCustomValidators } from '../editor/custom-validators/editor-custom-validators'; import { FieldSetEditorModel } from './field-set-editor-model'; export class SectionEditorModel extends BaseFormModel { @@ -29,9 +30,9 @@ export class SectionEditorModel extends BaseFormModel { const formGroup: FormGroup = new FormBuilder().group({ id: [{ value: this.id, disabled: (disabled && !skipDisable.includes('SectionEditorModel.id')) }, [Validators.required, Validators.pattern('^[^<_>]+$')]], page: [{ value: this.page, disabled: (disabled && !skipDisable.includes('SectionEditorModel.page')) }, [Validators.required]], - title: [{ value: this.title, disabled: (disabled && !skipDisable.includes('SectionEditorModel.title')) }], + title: [{ value: this.title, disabled: (disabled && !skipDisable.includes('SectionEditorModel.title')) } , [Validators.required]], description: [{ value: this.description, disabled: (disabled && !skipDisable.includes('SectionEditorModel.description')) }], - ordinal: [{ value: this.ordinal, disabled: (disabled && !skipDisable.includes('SectionEditorModel.ordinal')) }], + ordinal: [{ value: this.ordinal, disabled: (disabled && !skipDisable.includes('SectionEditorModel.ordinal')) }, [Validators.required]], defaultVisibility: [{ value: this.defaultVisibility, disabled: (disabled && !skipDisable.includes('SectionEditorModel.defaultVisibility')) }] }); const sectionsFormArray = new Array(); @@ -53,6 +54,8 @@ export class SectionEditorModel extends BaseFormModel { if (!formGroup.controls['defaultVisibility'].value) { formGroup.controls['defaultVisibility'].setValue(true); } + formGroup.setValidators(EditorCustomValidators.sectionHasAtLeastOneChildOf('fieldSets','sections')); + formGroup.updateValueAndValidity(); return formGroup; } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/view-style-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/view-style-editor-model.ts index de61824a9..586d603f8 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/admin/view-style-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/admin/view-style-editor-model.ts @@ -1,4 +1,4 @@ -import { FormGroup } from "@angular/forms"; +import { FormGroup, Validators } from "@angular/forms"; import { ViewStyle } from "../../../../core/model/admin/dataset-profile/dataset-profile"; import { BaseFormModel } from "../../../../core/model/base-form-model"; @@ -15,7 +15,7 @@ export class ViewStyleEditorModel extends BaseFormModel { buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { const formGroup = this.formBuilder.group({ cssClass: [{ value: this.cssClass, disabled: (disabled && !skipDisable.includes('ViewStyleEditorModel.cssClass')) }], - renderStyle: [{ value: this.renderStyle, disabled: (disabled && !skipDisable.includes('ViewStyleEditorModel.renderStyle')) }] + renderStyle: [{ value: this.renderStyle, disabled: (disabled && !skipDisable.includes('ViewStyleEditorModel.renderStyle')) }, Validators.required] }); return formGroup; } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.module.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.module.ts index 538f26e0b..d270409e6 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.module.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.module.ts @@ -1,6 +1,5 @@ import { NgModule } from '@angular/core'; import { FormattingModule } from '@app/core/formatting.module'; -import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module'; import { DatasetProfileRoutingModule } from '@app/ui/admin/dataset-profile/dataset-profile.routing'; import { DatasetProfileEditorCompositeFieldComponent } from '@app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component'; import { DatasetProfileEditorDefaultValueComponent } from '@app/ui/admin/dataset-profile/editor/components/composite-profile-editor-default-value/component-profile-editor-default-value.component'; @@ -23,20 +22,39 @@ import { DatasetProfileEditorRuleComponent } from '@app/ui/admin/dataset-profile import { DatasetProfileEditorSectionComponent } from '@app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component'; import { DatasetProfileEditorComponent } from '@app/ui/admin/dataset-profile/editor/dataset-profile-editor.component'; import { DatasetProfileCriteriaComponent } from '@app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component'; -import { DialodConfirmationUploadDatasetProfiles } from '@app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component'; +import { DialogConfirmationUploadDatasetProfiles } from '@app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component'; import { DatasetProfileListingComponent } from '@app/ui/admin/dataset-profile/listing/dataset-profile-listing.component'; +import { DatasetModule } from '@app/ui/dataset/dataset.module'; +import { FormProgressIndicationModule } from '@app/ui/misc/dataset-description-form/components/form-progress-indication/form-progress-indication.module'; +import { TableOfContentsModule } from '@app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.module'; import { CommonFormsModule } from '@common/forms/common-forms.module'; +import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module'; import { CommonUiModule } from '@common/ui/common-ui.module'; -import { ParseStatus } from './listing/pipe/parse-status.pipe'; -import { DatasetProfileEditorExternalDatasetsFieldComponent } from './editor/components/field-type/external-datasets/dataset-profile-editor-external-datasets-field.component'; +import { AngularStickyThingsModule } from '@w11k/angular-sticky-things'; +import { DatasetProfileEditorCurrencyFieldComponent } from './editor/components/field-type/currency/dataset-profile-editor-currency-field.component'; import { DatasetProfileEditorDataRepositoriesFieldComponent } from './editor/components/field-type/data-repositories/dataset-profile-editor-data-repositories-field.component'; +import { DatasetProfileEditorDatasetIdentifierFieldComponent } from './editor/components/field-type/dataset-identifier/dataset-profile-editor-dataset-identifier-field.component'; +import { DatasetProfileEditorExternalDatasetsFieldComponent } from './editor/components/field-type/external-datasets/dataset-profile-editor-external-datasets-field.component'; +import { DatasetProfileEditorOrganizationsFieldComponent } from './editor/components/field-type/organizations/dataset-profile-editor-organizations-field.component'; import { DatasetProfileEditorRegistriesFieldComponent } from './editor/components/field-type/registries/dataset-profile-editor-registries-field.component'; +import { DatasetProfileEditorResearchersFieldComponent } from './editor/components/field-type/researchers/dataset-profile-editor-researchers-field.component'; import { DatasetProfileEditorServicesFieldComponent } from './editor/components/field-type/services/dataset-profile-editor-services-field.component'; import { DatasetProfileEditorTagsFieldComponent } from './editor/components/field-type/tags/dataset-profile-editor-tags-field.component'; -import { DatasetProfileEditorResearchersFieldComponent } from './editor/components/field-type/researchers/dataset-profile-editor-researchers-field.component'; -import { DatasetProfileEditorOrganizationsFieldComponent } from './editor/components/field-type/organizations/dataset-profile-editor-organizations-field.component'; -import { DatasetProfileEditorDatasetIdentifierFieldComponent } from './editor/components/field-type/dataset-identifier/dataset-profile-editor-dataset-identifier-field.component'; -import { DatasetProfileEditorCurrencyFieldComponent } from './editor/components/field-type/currency/dataset-profile-editor-currency-field.component'; +import { DatasetProfileEditorValidatorFieldComponent } from './editor/components/field-type/validator/dataset-profile-editor-validator-field.component'; +import { NgxDropzoneModule } from 'ngx-dropzone'; +import { ParseStatus } from './listing/pipe/parse-status.pipe'; +import { DatasetProfileTableOfContents } from './table-of-contents/table-of-contents'; +import { DatasetProfileTableOfContentsInternalSection } from './table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section'; +import { VisibilityRulesService } from '@app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service'; +import {DragDropModule} from '@angular/cdk/drag-drop'; +import {DragulaModule} from 'ng2-dragula'; + + +//matrial +import {MatBadgeModule} from '@angular/material/badge'; +import { DatasetProfileEditorSectionFieldSetComponent } from './editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component'; +import { FinalPreviewComponent } from './editor/components/final-preview/final-preview.component'; +import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module'; @NgModule({ imports: [ @@ -44,7 +62,15 @@ import { DatasetProfileEditorCurrencyFieldComponent } from './editor/components/ CommonFormsModule, FormattingModule, DatasetProfileRoutingModule, - ConfirmationDialogModule + ConfirmationDialogModule, + NgxDropzoneModule, + FormProgressIndicationModule, + DatasetModule, + AngularStickyThingsModule, + DragDropModule, + MatBadgeModule, + DragulaModule, + AutoCompleteModule ], declarations: [ DatasetProfileListingComponent, @@ -65,7 +91,7 @@ import { DatasetProfileEditorCurrencyFieldComponent } from './editor/components/ DatasetProfileEditorDatePickerFieldComponent, DatasetProfileEditorWordListFieldComponent, DatasetProfileEditorDefaultValueComponent, - DialodConfirmationUploadDatasetProfiles, + DialogConfirmationUploadDatasetProfiles, DatasetProfileEditorInternalDmpEntitiesFieldComponent, DatasetProfileEditorResearchersAutoCompleteFieldComponent, DatasetProfileEditorDatasetsAutoCompleteFieldComponent, @@ -79,10 +105,15 @@ import { DatasetProfileEditorCurrencyFieldComponent } from './editor/components/ DatasetProfileEditorResearchersFieldComponent, DatasetProfileEditorOrganizationsFieldComponent, DatasetProfileEditorDatasetIdentifierFieldComponent, - DatasetProfileEditorCurrencyFieldComponent + DatasetProfileEditorCurrencyFieldComponent, + DatasetProfileEditorValidatorFieldComponent, + DatasetProfileTableOfContents, + DatasetProfileTableOfContentsInternalSection, + DatasetProfileEditorSectionFieldSetComponent, + FinalPreviewComponent ], entryComponents: [ - DialodConfirmationUploadDatasetProfiles + DialogConfirmationUploadDatasetProfiles ] }) export class DatasetProfileModule { } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.routing.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.routing.ts index d36fa424c..4fb6f9404 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.routing.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/dataset-profile.routing.ts @@ -3,49 +3,73 @@ import { RouterModule, Routes } from '@angular/router'; import { DatasetProfileEditorComponent } from './editor/dataset-profile-editor.component'; import { DatasetProfileListingComponent } from './listing/dataset-profile-listing.component'; import { AdminAuthGuard } from '@app/core/admin-auth-guard.service'; +import { AppRole } from '@app/core/common/enum/app-role'; +import { SpecialAuthGuard } from '@app/core/special-auth-guard.service'; const routes: Routes = [ { path: 'new', component: DatasetProfileEditorComponent, data: { - title: 'GENERAL.TITLES.DATASET-PROFILES-NEW' + title: 'GENERAL.TITLES.DATASET-PROFILES-NEW', + authContext: { + permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] + } }, - canActivate: [AdminAuthGuard] + canActivate: [SpecialAuthGuard] }, { path: ':id', component: DatasetProfileEditorComponent, data: { - title: 'GENERAL.TITLES.DATASET-PROFILES-EDIT' + title: 'GENERAL.TITLES.DATASET-PROFILES-EDIT', + authContext: { + permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] + } }, - canActivate: [AdminAuthGuard] + canActivate: [SpecialAuthGuard] }, { path: 'clone/:cloneid', component: DatasetProfileEditorComponent, data: { - title: 'GENERAL.TITLES.DATASET-PROFILES-CLONE' + title: 'GENERAL.TITLES.DATASET-PROFILES-CLONE', + authContext: { + permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] + } }, - canActivate: [AdminAuthGuard] + canActivate: [SpecialAuthGuard] }, { path: 'newversion/:newversionid', component: DatasetProfileEditorComponent, data: { - title: 'GENERAL.TITLES.DATASET-PROFILES-NEW-VERSION' + title: 'GENERAL.TITLES.DATASET-PROFILES-NEW-VERSION', + authContext: { + permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] + } }, - canActivate: [AdminAuthGuard] + canActivate: [SpecialAuthGuard] }, { path: 'versions/:groupId', component: DatasetProfileListingComponent, - canActivate: [AdminAuthGuard] + data: { + authContext: { + permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] + } + }, + canActivate: [SpecialAuthGuard] }, { path: '', component: DatasetProfileListingComponent, - canActivate: [AdminAuthGuard] + data: { + authContext: { + permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] + } + }, + canActivate: [SpecialAuthGuard] }, ]; diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/animations/animations.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/animations/animations.ts new file mode 100644 index 000000000..c65851e63 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/animations/animations.ts @@ -0,0 +1,83 @@ +import { animate, keyframes, state, style, transition, trigger } from "@angular/animations"; + +export const STEPPER_ANIMATIONS = [ + trigger('previous_btn',[ + transition(':enter',[ + style({'transform': 'translateX(100%)', 'z-index':'9999', 'opacity':0.4}), + animate('600ms ease-out', style({ + 'transform': 'translateX(0)', + 'opacity':1 + })) + ]), + transition(':leave',[ + style({ + 'transform': 'translateX(0)', + 'opacity':'1', + 'z-index':'9999' + }), + animate('800ms ease-out', keyframes([ + style({'transform': 'translateX(120%)', offset:0.8}), + style({'opacity': '0.2', offset:0.96}) + ])) + ]) + ]), + trigger('next_btn', [ + transition(':leave',[ + style({opacity:1, position:'absolute', 'z-index':'9999'}), + animate('700ms ease-out', keyframes( [ + style({ transform:'translateX(-100%)', offset:0.6}), + style({ opacity:'0.2', offset:0.87}) + ])) + ]), + transition(':enter',[ + style({opacity:0.3, 'z-index':'9999', transform:'translateX(-100%)'}), + animate('600ms ease-out', style({ opacity:'1', transform:'translateX(0)' })) + ]) + ]), + trigger('finalize_btn',[ + transition(':enter',[ + style({opacity:0.3}), + animate('400ms ease-in', style({opacity:1})) + ]), + transition(':leave',[ + style({opacity:1, position:'absolute'}), + animate('600ms ease-in', style({opacity:0.3})) + ]) + ]) +]; +export const GENERAL_ANIMATIONS = [ + trigger('enterIn',[ + transition(':enter',[ + style({ + transform:'scale(0)', + 'transform-origin':'50% 0', + opacity:0 + }), + animate('800ms ease', style({transform:'scale(1)', opacity:1})) + ]) + ]), + trigger('fadeElement',[ + state('updated',style({opacity:0})), + transition("*=>updated", + animate('2s 1s ease-out')) + ]), + trigger('add-new-user-field', [ + state('untriggered', style({ + transform:'translateX(120%)' + })), + state('triggered', style({ + transform:'translateX(0)' + })), + transition('untriggered => triggered', animate('400ms ease')), + transition('triggered => untriggered', animate('400ms 100ms ease')) + ]), + trigger('scroll-on-top-btn',[ + transition(":enter", [style({opacity:0, transform:'scale(0)'}), animate('400ms ease', style({'opacity':1, transform:'scale(1)'}))]), + transition(":leave", [style({opacity:1,transform:'scale(1)'}), animate('400ms ease', style({'opacity':0, transform:'scale(0)'}))]) + ]), + trigger('action-btn',[ + transition(":enter", [style({opacity:0, transform:'scale(0)'}), animate('400ms ease', style({'opacity':1, transform:'scale(1)'}))]), + transition(":leave", [style({opacity:1,transform:'scale(1)'}), animate('400ms ease', style({'opacity':0, transform:'scale(0)'}))]) + ]) + +] \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.html index 6de806122..687a3b1ad 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.html @@ -1,11 +1,17 @@ -
+ + + + + + + + + + + + + + + + +
+
- - - {{i + 1}}. {{getFieldTile(field, i)}} -
- +
+ + + + + + + + +
+
+
+ + + + +
+ + +
- +
+ + + +
+
+ + + +
+ +
+ + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + +
+
+ + + +
-
- + + + +
+ + +
+ +
+ +
+ + + + + + + + +
+
+
+ + +
+
+ + + +
+
+ {{'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.PREVIEW' | translate}} + + + Preview updated! + + + ... caculating preview + + +
+
+
+ + +
+
+ +
+ + {{'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.NOT-INITIALIZED' | translate}} + +
+ +
+
+ + + +
+
+
    + + + + +
  • + Add Input icon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  • + + + +
  • + {{'DATASET-PROFILE-EDITOR.ACTIONS.FIELDSET.COMMENT-FIELD' | translate}} +
  • +
  • + + {{'DATASET-PROFILE-EDITOR.ACTIONS.FIELDSET.MULTIPLICITY' | translate}} + +
  • +
  • + + more_vert + + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.DESCRIPTION' | translate}} + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.EXTENDED-DESCRIPTION' | translate}} + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.ADDITIONAL-INFORMATION' | translate}} + +
  • +
+
+
+
+ +
+ + + + + + + diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.scss index 046126ff9..94cebbcc9 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.scss +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.scss @@ -8,4 +8,106 @@ .titleStile{ font-weight: bold; -} \ No newline at end of file +} + +//REFACTOR SECTION + + +//Palete + +$blue-color : #129D99; +$blue-color-light: #5cf7f2; + +.actions-list{ + border-radius: 7px; + box-shadow: 0 1px 0.8em $blue-color; + padding: 1em 0em; + font-size: small; +} + +.field-container{ + box-shadow: 0px 1px 2px rgb(173, 173, 173) ; + padding: 3em; + border-radius: .3em; + // border-left: 0.3em solid yellow; + padding-bottom: 2em; + margin-bottom: 2em; + +} +.field-container-active{ + box-shadow: 0px 1px 2px rgb(173, 173, 173) ; + padding: 3em; + border-radius: .3em; + border-left: 0.3em solid $blue-color; + padding-bottom: 2em; + margin-bottom: 2em; + +} +.field-id-container{ + background-color: $blue-color-light; + position: absolute; + right: 0; + top: 0; + padding: .6em 1.2em; + overflow: visible; + .field-id-container-icon{ + position: absolute; + top: -50%; + + } +} + +.main-content-page{ + padding: 0em 1.5em; +} + +.fielset-header{ + font-size: 1.5em; + font-weight: bold; + + // .numbering{ + // padding: 0.5em 0em; + // } +} + +.fieldset-actions-list{ + margin: 0; + cursor: pointer; +} + +// ::ng-deep .main-content-page .fieldset-header .field-title .mat-form-field-infix{ +// border-top: 0px; +// } + + +.numbering-label .mat-input-element:disabled{ + color: #212121; +} + +:host ::ng-deep .fieldset-checkbox-action-dataset-profile-editor +{ + .mat-checkbox-label{ + font-size: 0.8em; + color: #212121; + transform: translateY(3px); + } +} + + +.input_icon{ + width: 14px; + margin-right: 0.5em; + // display: flex; + // align-items: center; +} + +::ng-deep .mat-menu-panel{ + max-height: 32em; +} +:host ::ng-deep .fielset-header .mat-form-field-wrapper{ + padding-bottom: 0px; +} + +// ::ng-deep .underline-line-field .mat-form-field-appearance-legacy .mat-form-field-wapper{ +// padding-bottom: 1.25em !important; +// } \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts index e4fd16dfa..4e3762bec 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-field/dataset-profile-editor-composite-field.component.ts @@ -1,38 +1,271 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { FormArray, FormControl, FormGroup } from '@angular/forms'; +import { Component, Input, OnChanges, OnInit } from '@angular/core'; +import { FormArray, FormControl, FormGroup} from '@angular/forms'; import { FieldEditorModel } from '../../../admin/field-editor-model'; import { Guid } from '@common/types/guid'; +import { RuleEditorModel } from '../../../admin/rule-editor-model'; +import { ValidationType } from '@app/core/common/enum/validation-type'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { DatasetDescriptionCompositeFieldEditorModel, DatasetDescriptionFieldEditorModel, DatasetDescriptionFormEditorModel, DatasetDescriptionSectionEditorModel } from '@app/ui/misc/dataset-description-form/dataset-description-form.model'; +import { DatasetProfileFieldViewStyle } from '@app/core/common/enum/dataset-profile-field-view-style'; +import { MatDialog } from '@angular/material'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; +import { TranslateService } from '@ngx-translate/core'; +import { ViewStyleType } from '../field/view-style-enum'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { DatasetProfileService } from '@app/core/services/dataset-profile/dataset-profile.service'; +import { EditorCustomValidators } from '../../custom-validators/editor-custom-validators'; +import { Field, FieldSet } from '@app/core/model/admin/dataset-profile/dataset-profile'; +import { DatasetProfileComboBoxType } from '@app/core/common/enum/dataset-profile-combo-box-type'; +import { DatasetProfileInternalDmpEntitiesType } from '@app/core/common/enum/dataset-profile-internal-dmp-entities-type'; +import { AutoCompleteFieldData, BooleanDecisionFieldData, CheckBoxFieldData, CurrencyFieldData, DataRepositoriesFieldData, DatasetIdentifierFieldData, DatePickerFieldData, DmpsAutoCompleteFieldData, ExternalDatasetsFieldData, FieldDataOption, FreeTextFieldData, OrganizationsFieldData, RadioBoxFieldData, RegistriesFieldData, ResearchersAutoCompleteFieldData, ServicesFieldData, TagsFieldData, TextAreaFieldData, ValidationFieldData, WordListFieldData } from '@app/core/model/dataset-profile-definition/field-data/field-data'; +import { CompositeField } from '@app/core/model/dataset-profile-definition/composite-field'; +import {Field as FieldDefinition} from '@app/core/model/dataset-profile-definition/field'; +import { Subject } from 'rxjs'; +import { debounceTime } from 'rxjs/operators'; +import { GENERAL_ANIMATIONS } from '../../animations/animations'; @Component({ selector: 'app-dataset-profile-editor-composite-field-component', templateUrl: './dataset-profile-editor-composite-field.component.html', - styleUrls: ['./dataset-profile-editor-composite-field.component.scss'] + styleUrls: ['./dataset-profile-editor-composite-field.component.scss'], + animations:[GENERAL_ANIMATIONS] }) -export class DatasetProfileEditorCompositeFieldComponent implements OnInit { +export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnChanges { @Input() form: FormGroup; @Input() indexPath: string; @Input() viewOnly: boolean; - isComposite = false; - isMultiplicityEnabled = false; + @Input() numbering: string; + @Input() hasFocus: boolean = false; + + showPreview: boolean = true; + previewDirty: boolean = false; + + + showDescription: boolean = true; + showAdditionalInfo: boolean = false; + showExtendedDescription: boolean = false; + + previewForm: FormGroup = null; + // isComposite = false; + // isMultiplicityEnabled = false; + viewStyleEnum = DatasetProfileFieldViewStyle; + + viewTypeEnum = ViewStyleType; + + private myCustomValidators:EditorCustomValidators = new EditorCustomValidators(); + + constructor( + private dialog: MatDialog, + private language: TranslateService, + public enumUtils: EnumUtils, + public datasetProfileService: DatasetProfileService + ) { } + + ngOnChanges(){ + // this.setTargetField(null); + // this.showExtendedDescription = !!this.form.get('extendedDescription').value; + // this.showAdditionalInfo = !!this.form.get('additionalInformation').value; + } + + get firstField(){ + try{ + return (this.form.get('fields') as FormArray).at(0); + }catch{ + return null; + } + } + + get isMultiplicityEnabled(){ + if(!this.form.get('multiplicity')){ + return false; + } + + if (this.form.get('multiplicity').value.min > 0 || this.form.get('multiplicity').value.max > 0) { + return true; + } + + return false; + } - constructor() { } ngOnInit() { //this.addNewField(); - if (this.form.get('multiplicity')) { - if (this.form.get('multiplicity').value.min > 1 || this.form.get('multiplicity').value.max > 1) { - this.isMultiplicityEnabled = true; - } - } - this.isComposite = (this.form.get('fields') as FormArray).length > 1; + // if (this.form.get('multiplicity')) { + // if (this.form.get('multiplicity').value.min > 1 || this.form.get('multiplicity').value.max > 1) { + // this.isMultiplicityEnabled = true; + // } + // } + // this.isComposite = (this.form.get('fields') as FormArray).length > 1; if (this.viewOnly) { this.form.get('hasCommentField').disable(); } + + //SET UP TARGET FIELD + // if((this.form.get('fields') as FormArray).length>0){ + // //get the first field in list + // this.targetField = (this.form.get('fields') as FormArray).at(0) as FormGroup; + // } + + this.showExtendedDescription = !!this.form.get('extendedDescription').value; + this.showAdditionalInfo = !!this.form.get('additionalInformation').value; + + this.form.valueChanges.subscribe(changes=>{ + // this.previewForm = null; + this.previewDirty = true; + this.generatePreviewForm(); + + }); + this.previewSubject$.pipe(debounceTime(600)).subscribe(model=>{ + const updatedForm = model.buildForm(); + this.reloadPreview(updatedForm) + }) + + + this.generatePreviewForm(); } + + get updatedClass(){ + if(this.previewDirty) return ''; + else return 'updated'; + } + private reloadPreview(updatedForm: FormGroup){ + setTimeout(() => { + + const previewContainer = document.getElementById('preview_container'+ this.form.get('id').value); + // let clientHeight = -1; + if(previewContainer){ + // console.log(previewContainer); + const clientHeight = previewContainer.clientHeight; + // console.log(clientHeight); + + if(clientHeight){ + previewContainer.style.height = clientHeight.toString() + 'px'; + + // console.log('height:' ,previewContainer.style.height); + } + } + this.showPreview = false; + this.previewDirty = true; + this.previewForm = updatedForm; + + setTimeout(() => { + + + this.showPreview = true; + this.previewDirty = false; + + if(previewContainer){ + setTimeout(() => { + if(previewContainer){ + previewContainer.style.height = 'auto'; + } + }); + } + }); + }); + } + + previewSubject$: Subject = new Subject(); + + private generatePreviewForm(){ + const formValue:FieldSet = this.form.getRawValue(); + const fields:FieldDefinition[] = formValue.fields.map(editorField=>this._fieldToFieldDefinition(editorField)); + + + const compositeField: CompositeField = { + id: formValue.id, + additionalInformation: formValue.additionalInformation, + extendedDescription: formValue.extendedDescription, + numbering:'', + title: formValue.title, + ordinal: formValue.ordinal, + description: formValue.description, + hasCommentField: formValue.hasCommentField, + commentFieldValue: '', + multiplicity: {max:formValue.multiplicity.max, min : formValue.multiplicity.min}, + multiplicityItems:null, + fields: fields.map(editorField=>{ + const model = new DatasetDescriptionFieldEditorModel().fromModel(editorField); + if(model.viewStyle.renderStyle === this.viewStyleEnum.CheckBox){ + model.value = model.value?"true":"false";//patch + } + return model; + }) + } + + + const section = new DatasetDescriptionSectionEditorModel(); + section.title = ''; + section.numbering = ''; + + const compositeForm = new DatasetDescriptionCompositeFieldEditorModel().fromModel(compositeField) + section.compositeFields = [compositeForm]; + + this.previewSubject$.next(section); + } + + + private _fieldToFieldDefinition(editorField: Field): FieldDefinition{ + const field = { + id: editorField.id, + title: '', + page: editorField.page, + numbering:'', + multiplicity:null, + multiplicityItems: null, + viewStyle: editorField.viewStyle, + defaultValue:editorField.defaultValue, + value: null, + validations: editorField.validations, + } as FieldDefinition; + + field.data = editorField.data; + + // return new DatasetDescriptionFieldEditorModel().fromModel(field); + return field; + } + + + // generatePreview(){ + // const editorModel = new DatasetDescriptionCompositeFieldEditorModel(); + // editorModel.title = this.form.get('title').value; + // editorModel.description = this.form.get('description').value; + // editorModel.extendedDescription = this.form.get('extendedDescription').value; + // editorModel.additionalInformation = this.form.get('additionalInformation').value; + // editorModel.hasCommentField = this.form.get('hasCommentField').value; + // editorModel.fields = []; + + // (this.form.get('fields') as FormArray).controls.forEach(field=>{ + // const fieldEditorModel = new DatasetDescriptionFieldEditorModel(); + + // fieldEditorModel.viewStyle= { + // renderStyle: field.get('viewStyle').get('renderStyle').value, + // cssClass: null + // }; + // fieldEditorModel.defaultValue = field.get('defaultValue').value; + // switch (field.get('viewStyle').get('renderStyle').value) { + // case DatasetProfileFieldViewStyle.TextArea: + // fieldEditorModel.data = { + // label: field.get('data').get('label').value + // }; + // break; + + // default: + // break; + // } + + + // editorModel.fields.push(fieldEditorModel); + // }); + + + // this.previewForm = editorModel.buildForm(); + // } + onIsCompositeChange(isComposite: boolean) { if (!isComposite && (this.form.get('fields')).length > 1) { for (let i = 0; i < (this.form.get('fields')).length - 1; i++) { @@ -46,25 +279,601 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit { } } - onIsMultiplicityEnabledChange(isMultiplicityEnabled: boolean) { - if (!isMultiplicityEnabled) { - (this.form.get('multiplicity').get('min')).setValue(0); - (this.form.get('multiplicity').get('max')).setValue(0); + onIsMultiplicityEnabledChange(isMultiplicityEnabled: MatCheckboxChange) { + + const multiplicity = this.form.get('multiplicity') as FormGroup; + + const minControl = multiplicity.get('min'); + const maxControl = multiplicity.get('max'); + + if (isMultiplicityEnabled.checked) { + minControl.setValue(0); + maxControl.setValue(1); + }else{ + minControl.setValue(0); + maxControl.setValue(0); } + + minControl.updateValueAndValidity(); + maxControl.updateValueAndValidity(); + } addNewField() { const field: FieldEditorModel = new FieldEditorModel(); field.id=Guid.create().toString(); - (this.form.get('fields')).push(field.buildForm()); + + field.ordinal = (this.form.get('fields') as FormArray).length; + + const fieldForm = field.buildForm(); + // fieldForm.setValidators(this.customFieldValidator()); + + // fieldForm.get('viewStyle').get('renderStyle').setValidators(Validators.required); + + (this.form.get('fields')).push(fieldForm); + + this.setTargetField(fieldForm); + fieldForm.updateValueAndValidity(); } DeleteField(index) { - (this.form.get('fields')).removeAt(index); + + const fieldsForm = this.form.get('fields'); + fieldsForm.removeAt(index); + + // calculate ordinals + fieldsForm.controls.forEach((field, idx)=>{ + field.get('ordinal').setValue(idx); + field.updateValueAndValidity(); + }) } getFieldTile(formGroup: FormGroup, index: number) { if (formGroup.get('title') && formGroup.get('title').value && formGroup.get('title').value.length > 0) { return formGroup.get('title').value; } return "Field " + (index + 1); } + + + targetField:FormGroup; + validationTypeEnum = ValidationType; + + + addVisibilityRule(targetField: FormGroup){ + const rule: RuleEditorModel = new RuleEditorModel(); + (targetField.get('visible').get('rules')).push(rule.buildForm()); + } + toggleRequired(targetField: FormGroup, event:MatCheckboxChange){ + + let validationsControl = targetField.get('validations') as FormControl; + let validations: Array = validationsControl.value; + + if(event.checked){ + if(!validations.includes(ValidationType.Required)){//IS ALREADY REQUIRED + // validationsControl.setValue(validations.filter(validator=> validator != ValidationType.Required)); + // validationsControl.updateValueAndValidity(); + validations.push(ValidationType.Required); + // validationsControl.setValue(validations); + validationsControl.updateValueAndValidity(); + } + }else{ + validationsControl.setValue(validations.filter(validator=> validator != ValidationType.Required)); + validationsControl.updateValueAndValidity(); + } + + + // if(validations.includes(ValidationType.Required)){//IS ALREADY REQUIRED + // validationsControl.setValue(validations.filter(validator=> validator != ValidationType.Required)); + // validationsControl.updateValueAndValidity(); + // }else{ + // //SET REQUIRED VALIDATOR + // console.log('setting required validator'); + // validations.push(ValidationType.Required); + // validationsControl.setValue(validations); + // validationsControl.updateValueAndValidity(); + // } + } + setTargetField(field:FormGroup){ + this.targetField = field; + } + + + + + + + deleteTargetField(){ + + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + restoreFocus: false, + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + isDeleteConfirmation: true + } + }); + dialogRef.afterClosed().subscribe(result => { + if (result) { + this._deleteTargetField(); + } + }); + + } + + + private _deleteTargetField(){ + if(!this.targetField) return; + + let index = -1; + + const fields = this.form.get('fields') as FormArray; + + for(let i=0;i< fields.length; i++){ + let field = fields.at(i); + if(field.get('id').value === this.targetField.get('id').value){//index found + index = i; + break; + } + } + + if(index>=0){//target found in fields + this.DeleteField(index); + this.targetField = null; + } + + } + + deleteField(index: number){ + + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + restoreFocus: false, + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + isDeleteConfirmation: true + } + }); + dialogRef.afterClosed().subscribe(result => { + if (result) { + this.DeleteField(index); + } + }); + + } + + + + + addNewInput(type: ViewStyleType){ + + const fieldsArray = this.form.get('fields') as FormArray; + + let targetOrdinal = fieldsArray.length; + try{ + targetOrdinal = fieldsArray.controls.map(control=> control.get('ordinal').value).reduce((a,b)=>Math.max(a,b)) +1; + }catch{ + + } + + + + const field = { + id: Guid.create().toString(), + ordinal: targetOrdinal, + visible:{rules:[],style:null}, + validations:[], + viewStyle:{} + + } as Field; + + + // const field: FieldEditorModel = new FieldEditorModel(); + // field.id=Guid.create().toString(); + + // field.ordinal = (this.form.get('fields') as FormArray).length; + + // const fieldForm = field.buildForm(); + // fieldForm.setValidators(this.customFieldValidator()); + // fieldForm.get('viewStyle').get('renderStyle').setValidators(Validators.required); + + + + // if (fieldForm.get('data')) { + // fieldForm.removeControl('data'); + // } + + switch (type) { + case this.viewTypeEnum.BooleanDecision:{ + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.BooleanDecision) + // fieldForm.addControl('data', new BooleanDecisionFieldDataEditorModel().buildForm()); + + const data: BooleanDecisionFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.BooleanDecision; + field.data = data; + + break; + } + case this.viewTypeEnum.CheckBox:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.CheckBox) + // fieldForm.addControl('data', new CheckBoxFieldDataEditorModel().buildForm()); + const data: CheckBoxFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.CheckBox; + field.data = data; + + break; + } + case this.viewTypeEnum.Select:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox) + // fieldForm.addControl('data', new WordListFieldDataEditorModel().buildForm()); + + // fieldForm.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); + // fieldForm.get('data').updateValueAndValidity(); + + const firstOption = {label:'', value:''} as FieldDataOption; + + const data:WordListFieldData = { + label:'', + multiList:false, + options:[firstOption], + type:DatasetProfileComboBoxType.WordList + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.ComboBox; + field.data = data; + + break; + } + case this.viewTypeEnum.Other:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox) + // fieldForm.addControl('data', new AutoCompleteFieldDataEditorModel().buildForm()); //TODO SEE + + // fieldForm.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('autoCompleteSingleDataList')); + // fieldForm.get('data').updateValueAndValidity(); + + const data: AutoCompleteFieldData = { + autoCompleteSingleDataList:[], + multiAutoComplete: false, + label:'', + type: DatasetProfileComboBoxType.Autocomplete + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.ComboBox; + field.data = data; + + break; + }case this.viewTypeEnum.InternalDmpEntities:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.InternalDmpEntities) + // fieldForm.addControl('data', new ResearchersAutoCompleteFieldDataEditorModel().buildForm());//TODO TO SEE + + const data: DmpsAutoCompleteFieldData = { + label:'', + multiAutoComplete: false, + type: DatasetProfileInternalDmpEntitiesType.Dmps + } + + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.InternalDmpEntities; + field.data = data; + + break; + } + case this.viewTypeEnum.FreeText:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.FreeText) + // fieldForm.addControl('data', new FreeTextFieldDataEditorModel().buildForm()); + + const data: FreeTextFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.FreeText; + field.data = data; + break; + } + case this.viewTypeEnum.RadioBox:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.RadioBox) + // fieldForm.addControl('data', new RadioBoxFieldDataEditorModel().buildForm()); + // fieldForm.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); + // fieldForm.get('data').updateValueAndValidity(); + const data: RadioBoxFieldData= { + label:'', + options: [] + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.RadioBox; + field.data = data; + + break; + } + case this.viewTypeEnum.TextArea:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.TextArea) + // fieldForm.addControl('data', new TextAreaFieldDataEditorModel().buildForm()); + + const data: TextAreaFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.TextArea; + field.data = data; + + break; + } + case this.viewTypeEnum.DatePicker:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.DatePicker) + // fieldForm.addControl('data', new DatePickerDataEditorModel().buildForm()); + const data: DatePickerFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.DatePicker; + + break; + } + case this.viewTypeEnum.ExternalDatasets:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ExternalDatasets) + // fieldForm.addControl('data', new ExternalDatasetsDataEditorModel().buildForm()); + const data: ExternalDatasetsFieldData = { + label:'', + multiAutoComplete: false + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.ExternalDatasets; + field.data = data; + break; + } + case this.viewTypeEnum.DataRepositories:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.DataRepositories) + // fieldForm.addControl('data', new DataRepositoriesDataEditorModel().buildForm()); + + const data: DataRepositoriesFieldData = { + label: '', + multiAutoComplete: false + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.DataRepositories; + field.data = data; + + break; + } + case this.viewTypeEnum.Registries:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Registries) + // fieldForm.addControl('data', new RegistriesDataEditorModel().buildForm()); + + const data:RegistriesFieldData = { + label: '', + multiAutoComplete: false + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Registries; + field.data = data; + + break; + } + case this.viewTypeEnum.Services:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Services) + // fieldForm.addControl('data', new ServicesDataEditorModel().buildForm()); + + const data:ServicesFieldData = { + label:'', + multiAutoComplete: false + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Services; + field.data = data; + + break; + } + case this.viewTypeEnum.Tags:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Tags) + // fieldForm.addControl('data', new TagsDataEditorModel().buildForm()); + + const data: TagsFieldData = { + label:'' + } + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Tags; + field.data = data; + + break; + } + case this.viewTypeEnum.Researchers:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Researchers) + // this.form.addControl('data', new ResearchersDataEditorModel().buildForm()); //TODO TO ASK + // fieldForm.addControl('data', new ResearchersAutoCompleteFieldDataEditorModel().buildForm()); + + // field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Researchers; + + const data : ResearchersAutoCompleteFieldData = { + label:'', + multiAutoComplete: false, + type: DatasetProfileInternalDmpEntitiesType.Researchers + } + + // field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.InternalDmpEntities; + // field.data = {label:''} + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Researchers; + field.data = data; + + + + + break; + } + case this.viewTypeEnum.Organizations:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Organizations) + // fieldForm.addControl('data', new OrganizationsDataEditorModel().buildForm()); + // this.form.addControl('data', new OrganizationsDataEditorModel().buildForm()) + // fieldForm.addControl('data', new DatasetsAutoCompleteFieldDataEditorModel().buildForm()); //TODO + + const data = { + autoCompleteSingleDataList:[], + label:'', + multiAutoComplete: false, + + } as OrganizationsFieldData; //TODO + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Organizations; + field.data = data; + + break; + } + case this.viewTypeEnum.DatasetIdentifier:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.DatasetIdentifier) + // fieldForm.addControl('data', new DatasetIdentifierDataEditorModel().buildForm()); + + const data : DatasetIdentifierFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.DatasetIdentifier; + field.data = data; + + break; + } + case this.viewTypeEnum.Currency:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Currency) + // fieldForm.addControl('data', new CurrencyDataEditorModel().buildForm()); + + const data: CurrencyFieldData = { + label:'' + } + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Currency; + field.data = data; + + break; + } + case this.viewTypeEnum.Validation:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Validation) + // fieldForm.addControl('data', new ValidationDataEditorModel().buildForm()); + + const data:ValidationFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Validation; + field.data = data; + + break; + } + } + + (this.form.get('fields')).push(new FieldEditorModel().fromModel(field).buildForm()); + // fieldForm.get('viewStyle').get('renderStyle').updateValueAndValidity(); + // fieldForm.get('data').updateValueAndValidity(); + + + } + + // private customFieldValidator(): ValidatorFn{ + // return (control):ValidationErrors | null=>{ + // DatasetProfileFieldViewStyle + // switch(control.get('viewStyle').get('renderStyle').value){ + + // case DatasetProfileFieldViewStyle.TextArea: + // return null; + // case DatasetProfileFieldViewStyle.BooleanDecision: + // return null; + // case DatasetProfileFieldViewStyle.ComboBox: + // return null; + // case DatasetProfileFieldViewStyle.CheckBox: + // return null; + // case DatasetProfileFieldViewStyle.FreeText: + // return null; + // case DatasetProfileFieldViewStyle.RadioBox: + // return null; + // case DatasetProfileFieldViewStyle.DatePicker: + // return null; + // case DatasetProfileFieldViewStyle.InternalDmpEntities: + // return null; + // case DatasetProfileFieldViewStyle.ExternalDatasets: + // return null; + // case DatasetProfileFieldViewStyle.DataRepositories: + // return null; + // case DatasetProfileFieldViewStyle.Registries: + // return null; + // case DatasetProfileFieldViewStyle.Services: + // return null; + // case DatasetProfileFieldViewStyle.Tags: + // return null; + // case DatasetProfileFieldViewStyle.Researchers: + // return null; + // case DatasetProfileFieldViewStyle.Organizations: + // return null; + // case DatasetProfileFieldViewStyle.DatasetIdentifier: + // return null; + // case DatasetProfileFieldViewStyle.Currency: + // return null; + // case DatasetProfileFieldViewStyle.Validation: + // return null; + // default: + // return {inputTypeNotValid: true} + // } + // } + // } + + + // private _atLeastOneElementListValidator(arrayToCheck): ValidatorFn{ + // return (control: AbstractControl): ValidationErrors | null=>{ + + // const fa = control.get(arrayToCheck) as FormArray; + + // if(fa.length === 0){ + // return {emptyArray: true}; + // } + // return null; + // } + // } + + + calculateLabelWidth(numbering: string){ + + const width = numbering.split('.').reduce((acc,item)=> item+acc,'').length; + + + return {'width':width+'em'} + } } + + diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-profile-editor-default-value/component-profile-editor-default-value.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-profile-editor-default-value/component-profile-editor-default-value.component.html index b9c42bc35..31648ff9a 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-profile-editor-default-value/component-profile-editor-default-value.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-profile-editor-default-value/component-profile-editor-default-value.component.html @@ -1,7 +1,8 @@
- + {{placeHolder}} + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.DEFAULT-VALUES.NONE' | translate}} {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.DEFAULT-VALUES.BOOLEAN-DECISION.YES' @@ -16,7 +17,8 @@ - + {{placeHolder}} + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.DEFAULT-VALUES.CHECK-BOX.CHECKED' | translate}} {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.DEFAULT-VALUES.CHECK-BOX.UNCHECKED' | translate}} @@ -24,11 +26,13 @@ - + - + {{placeHolder}} + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.DEFAULT-VALUES.NONE' | translate }} {{opt.get('label').value}} @@ -36,25 +40,30 @@ - + - + {{placeHolder}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} - + {{placeHolder}} + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.DEFAULT-VALUES.NONE' | translate}} {{opt.get('label').value}} @@ -63,14 +72,16 @@ - + {{placeHolder}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} - + {{placeHolder}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-profile-editor-default-value/component-profile-editor-default-value.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-profile-editor-default-value/component-profile-editor-default-value.component.ts index aedb1fa72..a62bc0cb4 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-profile-editor-default-value/component-profile-editor-default-value.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/composite-profile-editor-default-value/component-profile-editor-default-value.component.ts @@ -17,7 +17,7 @@ export class DatasetProfileEditorDefaultValueComponent implements OnInit { @Input() comboBoxType: DatasetProfileComboBoxType; @Input() internalDmpEntitiesType: DatasetProfileInternalDmpEntitiesType; @Input() placeHolder: String; - @Input() required: Boolean; + // @Input() required: Boolean; comboBoxTypeEnum = DatasetProfileComboBoxType; internalDmpEntitiesTypeEnum = DatasetProfileInternalDmpEntitiesType; diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/auto-complete/dataset-profile-editor-auto-complete-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/auto-complete/dataset-profile-editor-auto-complete-field.component.html index 0b7c98d87..6037dbc05 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/auto-complete/dataset-profile-editor-auto-complete-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/auto-complete/dataset-profile-editor-auto-complete-field.component.html @@ -6,14 +6,36 @@ - {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-PLACEHOLDER' | translate}} + -
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-SOURCE-TITLE' | translate}}
- + +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-SOURCE-TITLE' | translate}}
+
+ + + +
+ warning_amber + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.ERROR-MESSAGES.FIELD-OTHER-SOURCES-REQUIRED'| translate}} +
+
- +
+
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-TYPE' | translate}} @@ -24,20 +46,29 @@ - + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-URL' | translate}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} - + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-OPTIONS-ROOT' | translate}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} - + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-LABEL' | translate}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} - + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-VALUE' | translate}} + - + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-AUTOCOMPLETE-SOURCE' | translate}} + + {{'GENERAL.VALIDATION.REQUIRED' | translate}}
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/boolean-decision/dataset-profile-editor-boolean-decision-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/boolean-decision/dataset-profile-editor-boolean-decision-field.component.html index 66105b839..e1f96a2e1 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/boolean-decision/dataset-profile-editor-boolean-decision-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/boolean-decision/dataset-profile-editor-boolean-decision-field.component.html @@ -1,8 +1,9 @@ -
+ diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/checkbox/dataset-profile-editor-checkbox-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/checkbox/dataset-profile-editor-checkbox-field.component.html index cb2d82d7f..56ae77ff2 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/checkbox/dataset-profile-editor-checkbox-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/checkbox/dataset-profile-editor-checkbox-field.component.html @@ -3,6 +3,7 @@
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-CHECKBOX-TITLE' | translate}}
- + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-CHECKBOX-PLACEHOLDER' | translate}} +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/combo-box/dataset-profile-editor-combo-box-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/combo-box/dataset-profile-editor-combo-box-field.component.html index 014c776f1..9f4e545ab 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/combo-box/dataset-profile-editor-combo-box-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/combo-box/dataset-profile-editor-combo-box-field.component.html @@ -1,10 +1,10 @@
- +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/combo-box/dataset-profile-editor-combo-box-field.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/combo-box/dataset-profile-editor-combo-box-field.component.ts index 86cc30d8d..862cb61f9 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/combo-box/dataset-profile-editor-combo-box-field.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/combo-box/dataset-profile-editor-combo-box-field.component.ts @@ -22,7 +22,7 @@ export class DatasetProfileEditorComboBoxFieldComponent extends BaseComponent im ) { super(); } ngOnInit() { - this.setupListeners(); + // this.setupListeners(); } setupListeners() { diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/currency/dataset-profile-editor-currency-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/currency/dataset-profile-editor-currency-field.component.html index aeb72f4ed..da6e65ae7 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/currency/dataset-profile-editor-currency-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/currency/dataset-profile-editor-currency-field.component.html @@ -1,9 +1,9 @@
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATE-PICKER-TITLE' +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-CURRENCY-TITLE' | translate}}
+ {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-CURRENCY-PLACEHOLDER' | translate}}
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/data-repositories/dataset-profile-editor-data-repositories-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/data-repositories/dataset-profile-editor-data-repositories-field.component.html index aeb72f4ed..641c26ce1 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/data-repositories/dataset-profile-editor-data-repositories-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/data-repositories/dataset-profile-editor-data-repositories-field.component.html @@ -1,9 +1,12 @@
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATE-PICKER-TITLE' +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATA-REPOSITORIES-TITLE' | translate}}
+ + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-MULTIPLE-AUTOCOMPLETE' | translate}} + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATA-REPOSITORIES-PLACEHOLDER' | translate}} -
\ No newline at end of file +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/dataset-identifier/dataset-profile-editor-dataset-identifier-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/dataset-identifier/dataset-profile-editor-dataset-identifier-field.component.html index aeb72f4ed..263601838 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/dataset-identifier/dataset-profile-editor-dataset-identifier-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/dataset-identifier/dataset-profile-editor-dataset-identifier-field.component.html @@ -1,9 +1,9 @@
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATE-PICKER-TITLE' +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATASET-IDENTIFIER-TITLE' | translate}}
+ {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATASET-IDENTIFIER-PLACEHOLDER' | translate}}
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/datasets-auto-complete/dataset-profile-editor-datasets-autocomplete-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/datasets-auto-complete/dataset-profile-editor-datasets-autocomplete-field.component.html index 85de8192e..e7c6d01dc 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/datasets-auto-complete/dataset-profile-editor-datasets-autocomplete-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/datasets-auto-complete/dataset-profile-editor-datasets-autocomplete-field.component.html @@ -6,7 +6,8 @@ - {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATASETS-PLACEHOLDER' | translate}} +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/datepicker/dataset-profile-editor-date-picker-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/datepicker/dataset-profile-editor-date-picker-field.component.html index aeb72f4ed..bf505dbea 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/datepicker/dataset-profile-editor-date-picker-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/datepicker/dataset-profile-editor-date-picker-field.component.html @@ -2,8 +2,8 @@
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATE-PICKER-TITLE' | translate}}
+ {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-CHECKBOX-PLACEHOLDER' | translate}}
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/dmps-auto-complete/dataset-profile-editor-dmps-autocomplete-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/dmps-auto-complete/dataset-profile-editor-dmps-autocomplete-field.component.html index 70d5106e7..2109b436b 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/dmps-auto-complete/dataset-profile-editor-dmps-autocomplete-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/dmps-auto-complete/dataset-profile-editor-dmps-autocomplete-field.component.html @@ -6,7 +6,8 @@ - {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DMPS-PLACEHOLDER' | translate}} +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/external-datasets/dataset-profile-editor-external-datasets-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/external-datasets/dataset-profile-editor-external-datasets-field.component.html index aeb72f4ed..a5c074147 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/external-datasets/dataset-profile-editor-external-datasets-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/external-datasets/dataset-profile-editor-external-datasets-field.component.html @@ -1,9 +1,13 @@
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATE-PICKER-TITLE' +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-EXTERNAL-DATASETS-TITLE' | translate}}
+ + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-MULTIPLE-AUTOCOMPLETE' | translate}} + + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-EXTERNAL-DATASETS-PLACEHOLDER' | translate}} -
\ No newline at end of file +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/free-text/dataset-profile-editor-free-text-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/free-text/dataset-profile-editor-free-text-field.component.html index 9509b5a57..cc1248c46 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/free-text/dataset-profile-editor-free-text-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/free-text/dataset-profile-editor-free-text-field.component.html @@ -4,8 +4,8 @@ {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-FREE-TEXT-TITLE' | translate}} + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-FREE-TEXT-PLACEHOLDER' | translate}} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/organizations/dataset-profile-editor-organizations-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/organizations/dataset-profile-editor-organizations-field.component.html index aeb72f4ed..303388518 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/organizations/dataset-profile-editor-organizations-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/organizations/dataset-profile-editor-organizations-field.component.html @@ -1,9 +1,13 @@
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATE-PICKER-TITLE' +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-ORGANIZATIONS-TITLE' | translate}}
+ + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-MULTIPLE-AUTOCOMPLETE' | translate}} + + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-ORGANIZATIONS-PLACEHOLDER' | translate}} -
\ No newline at end of file + diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/radio-box/dataset-profile-editor-radio-box-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/radio-box/dataset-profile-editor-radio-box-field.component.html index 612a43bc4..944b3e3a9 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/radio-box/dataset-profile-editor-radio-box-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/radio-box/dataset-profile-editor-radio-box-field.component.html @@ -1,24 +1,29 @@
+
+
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-RADIO-BOX-TITLE' + | translate}}
+ + warning_amber + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.ERROR-MESSAGES.FIELD-RADIO-AT-LEAST-ONE-REQUIRED'| translate}} + +
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-RADIO-BOX-TITLE' - | translate}}
- - +
+ {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-RADIO-BOX-LABEL' | translate}} + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-RADIO-BOX-VALUE' | translate}}
\ No newline at end of file +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/researchers-auto-complete/dataset-profile-editor-researchers-auto-complete-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/researchers-auto-complete/dataset-profile-editor-researchers-auto-complete-field.component.html index 7de5c855f..94260ad5a 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/researchers-auto-complete/dataset-profile-editor-researchers-auto-complete-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/researchers-auto-complete/dataset-profile-editor-researchers-auto-complete-field.component.html @@ -6,7 +6,8 @@ - {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-RESEARCHERS-PLACEHOLDER' | translate}} +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/researchers/dataset-profile-editor-researchers-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/researchers/dataset-profile-editor-researchers-field.component.html index aeb72f4ed..bc1c84e39 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/researchers/dataset-profile-editor-researchers-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/researchers/dataset-profile-editor-researchers-field.component.html @@ -1,9 +1,12 @@
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATE-PICKER-TITLE' +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-RESEARCHERS-TITLE' | translate}}
+ + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-MULTIPLE-AUTOCOMPLETE' | translate}} + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-RESEARCHERS-PLACEHOLDER' | translate}} -
\ No newline at end of file + diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/services/dataset-profile-editor-services-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/services/dataset-profile-editor-services-field.component.html index aeb72f4ed..9f0991328 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/services/dataset-profile-editor-services-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/services/dataset-profile-editor-services-field.component.html @@ -1,9 +1,12 @@
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATE-PICKER-TITLE' +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-SERVICES-TITLE' | translate}}
+ + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-MULTIPLE-AUTOCOMPLETE' | translate}} + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-SERVICES-PLACEHOLDER' | translate}} -
\ No newline at end of file + diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/tags/dataset-profile-editor-tags-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/tags/dataset-profile-editor-tags-field.component.html index aeb72f4ed..bab2b500f 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/tags/dataset-profile-editor-tags-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/tags/dataset-profile-editor-tags-field.component.html @@ -1,9 +1,9 @@
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-DATE-PICKER-TITLE' +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-TAGS-TITLE' | translate}}
+ {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-TAGS-PLACEHOLDER' | translate}}
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/textarea/dataset-profile-editor-text-area-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/textarea/dataset-profile-editor-text-area-field.component.html index bb2b107c7..f3fe6ee4a 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/textarea/dataset-profile-editor-text-area-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/textarea/dataset-profile-editor-text-area-field.component.html @@ -4,7 +4,10 @@ | translate}} - + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-TEXT-AREA-PLACEHOLDER' | translate}} + + \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/validator/dataset-profile-editor-validator-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/validator/dataset-profile-editor-validator-field.component.html new file mode 100644 index 000000000..8b28d4c4e --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/validator/dataset-profile-editor-validator-field.component.html @@ -0,0 +1,9 @@ +
+
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-VALIDATOR-TITLE' + | translate}}
+ + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-VALIDATOR-PLACEHOLDER' | translate}} + + +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/validator/dataset-profile-editor-validator-field.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/validator/dataset-profile-editor-validator-field.component.scss new file mode 100644 index 000000000..3db0dee74 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/validator/dataset-profile-editor-validator-field.component.scss @@ -0,0 +1,3 @@ +.full-width { + width: 100%; +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/validator/dataset-profile-editor-validator-field.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/validator/dataset-profile-editor-validator-field.component.ts new file mode 100644 index 000000000..021fa2633 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/validator/dataset-profile-editor-validator-field.component.ts @@ -0,0 +1,18 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { DataRepositoriesDataEditorModel } from '@app/ui/admin/dataset-profile/admin/field-data/data-repositories-data-editor-models'; + +@Component({ + selector: 'app-dataset-profile-editor-validator-field-component', + styleUrls: ['./dataset-profile-editor-validator-field.component.scss'], + templateUrl: './dataset-profile-editor-validator-field.component.html' +}) +export class DatasetProfileEditorValidatorFieldComponent implements OnInit { + + @Input() form: FormGroup; + private data: DataRepositoriesDataEditorModel = new DataRepositoriesDataEditorModel(); + + ngOnInit() { + if (!this.form.get('data')) { this.form.addControl('data', this.data.buildForm()); } + } +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/word-list/dataset-profile-editor-word-list-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/word-list/dataset-profile-editor-word-list-field.component.html index 493b890b6..12d82cae4 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/word-list/dataset-profile-editor-word-list-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/word-list/dataset-profile-editor-word-list-field.component.html @@ -1,28 +1,33 @@
+
-
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-WORD-LIST-TITLE' - | translate}}
+
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-WORD-LIST-TITLE' + | translate}}
+ + + warning_amber + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.ERROR-MESSAGES.FIELD-SELECT-AT-LEAST-ONE-REQUIRED' |translate}} + +
{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-MULTIPLE-WORDLIST' | translate}} + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-WORD-LIST-PLACEHOLDER' | translate}}
- + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-WORD-LIST-LABEL' | translate}} + - + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-WORD-LIST-VALUE' | translate}} + + +
+
+
    +
  • + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
  • +
  • + visibility +
  • +
  • + delete +
  • + +
+
-
+ + +
+ + + + + + + + + TextArea icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.TextArea)}} + + + FreeText icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.FreeText)}} + + + + Boolean icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.BooleanDecision)}} + + + RadioBox icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.RadioBox)}} + + + + Select icon + + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.Select)}} + + + CheckBox icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.CheckBox)}} + + + + + + DatePicker icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.DatePicker)}} + + + Current icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.Currency)}} + + + + + + + + Registries icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.Registries)}} + + + Services icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.Services)}} + + + Researchers icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.Researchers)}} + + + Organizations icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.Organizations)}} + + + ExternalDatasets icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.ExternalDatasets)}} + + + DataRepositories icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.DataRepositories)}} + + + Other icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.Other)}} + + + + + + + InternalDmpEntities icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.InternalDmpEntities)}} + + + Tags icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.Tags)}} + + + DatasetIdentifier icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.DatasetIdentifier)}} + + + Validation icon + {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.Validation)}} + + + + + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + + + + + + + + + + + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.COMPOSITE-FIELD.FIELDS.RDA-COMMON-STANDARDS' | translate}} + + -- + + {{property}} + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +
+
+ + + + +

{{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.RULES-TITLE' | translate}} +

+ + +
+ + + +
+ + + + + + + + + diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.scss index 3db0dee74..1c94581a8 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.scss +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.scss @@ -1,3 +1,38 @@ .full-width { width: 100%; } + + +.preview-box{ + padding: 1em; + border: 1px solid #e8dcdc; + border-radius: 0.3em; +} + +mat-radio-button{ + padding-right: 1em; +} + +li.list-inline-item{ + color: #129D99; + .mat-icon{ + vertical-align: bottom; + } +} + +:host ::ng-deep .field-toggler +{ + .mat-slide-toggle-content{ + font-size: 0.8em; + color: #212121; + } + .mat-slide-toggle.mat-checked .mat-slide-toggle-thumb { + background-color:#129D99 ; + } +} + + +.input_icon{ + width: 14px; + margin-right: 0.5em; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.ts index c2added6f..6ff1de711 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.ts @@ -1,6 +1,6 @@  -import { Component, Input, OnInit } from '@angular/core'; -import { FormArray, FormControl, FormGroup } from '@angular/forms'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { AbstractControl, AbstractControlOptions, FormArray, FormControl, FormGroup, FormGroupDirective, NgForm, ValidationErrors, ValidatorFn } from '@angular/forms'; import { DatasetProfileFieldViewStyle } from '@app/core/common/enum/dataset-profile-field-view-style'; import { ValidationType } from '@app/core/common/enum/validation-type'; import { DatasetProfileService } from '@app/core/services/dataset-profile/dataset-profile.service'; @@ -15,7 +15,7 @@ import { TextAreaFieldDataEditorModel } from '@app/ui/admin/dataset-profile/admi import { WordListFieldDataEditorModel } from '@app/ui/admin/dataset-profile/admin/field-data/word-list-field-data-editor-model'; import { RuleEditorModel } from '@app/ui/admin/dataset-profile/admin/rule-editor-model'; import { BaseComponent } from '@common/base/base.component'; -import { takeUntil } from 'rxjs/operators'; +import { debounce, debounceTime, takeUntil } from 'rxjs/operators'; import { ExternalDatasetEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; import { DataRepositoriesDataEditorModel } from '../../../admin/field-data/data-repositories-data-editor-models'; import { RegistriesDataEditorModel } from '../../../admin/field-data/registries-data-editor-models'; @@ -26,135 +26,814 @@ import { OrganizationsDataEditorModel } from '../../../admin/field-data/organiza import { DatasetIdentifierDataEditorModel } from '../../../admin/field-data/dataset-identifier-data-editor-models'; import { ExternalDatasetsDataEditorModel } from '../../../admin/field-data/external-datasets-data-editor-models'; import { CurrencyDataEditorModel } from '../../../admin/field-data/currency-data-editor-models'; +import { ValidationDataEditorModel } from '../../../admin/field-data/validation-data-editor-models'; +import { DatasetDescriptionFieldEditorModel } from '@app/ui/misc/dataset-description-form/dataset-description-form.model'; +import { Subject, Subscription } from 'rxjs'; +import { ViewStyleType } from './view-style-enum'; +import { AutoCompleteFieldDataEditorModel } from '../../../admin/field-data/auto-complete-field-data-editor-model'; +import { DatasetsAutoCompleteFieldDataEditorModel } from '../../../admin/field-data/datasets-autocomplete-field-data-editor-mode'; +import { DatasetProfileComboBoxType } from '@app/core/common/enum/dataset-profile-combo-box-type'; +import { Guid } from '@common/types/guid'; +import { ErrorStateMatcher, MatDialog, MatSlideToggleChange } from '@angular/material'; +import { DefaultValueEditorModel } from '../../../admin/default-value-editor-model'; +import { EditorCustomValidators } from '../../custom-validators/editor-custom-validators'; +import { Field } from '@app/core/model/admin/dataset-profile/dataset-profile'; +import { DatasetProfileInternalDmpEntitiesType } from '@app/core/common/enum/dataset-profile-internal-dmp-entities-type'; +import { FieldEditorModel } from '../../../admin/field-editor-model'; +import { AutoCompleteFieldData, BooleanDecisionFieldData, CheckBoxFieldData, CurrencyFieldData, DataRepositoriesFieldData, DatasetIdentifierFieldData, DatePickerFieldData, DmpsAutoCompleteFieldData, ExternalDatasetsFieldData, FieldDataOption, FreeTextFieldData, OrganizationsFieldData, RadioBoxFieldData, RegistriesFieldData, ResearchersAutoCompleteFieldData, ServicesFieldData, TagsFieldData, TextAreaFieldData, ValidationFieldData, WordListFieldData } from '@app/core/model/dataset-profile-definition/field-data/field-data'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; @Component({ selector: 'app-dataset-profile-editor-field-component', templateUrl: './dataset-profile-editor-field.component.html', styleUrls: ['./dataset-profile-editor-field.component.scss'] }) -export class DatasetProfileEditorFieldComponent extends BaseComponent implements OnInit { +export class DatasetProfileEditorFieldComponent extends BaseComponent implements OnInit, ErrorStateMatcher { @Input() viewOnly: boolean; @Input() form: FormGroup; @Input() showOrdinal = true; @Input() indexPath: string; validationTypeEnum = ValidationType; viewStyleEnum = DatasetProfileFieldViewStyle; - isFieldMultiplicityEnabled = false; + // isFieldMultiplicityEnabled = false; + viewType: ViewStyleType; + viewTypeEnum = ViewStyleType; + // private subject$:Subject = new Subject(); + + + + @Input() expandView: boolean = true; + @Input() canBeDeleted:boolean = true; + + @Output() delete = new EventEmitter(); constructor( public enumUtils: EnumUtils, - public datasetProfileService: DatasetProfileService - ) { super(); } + public datasetProfileService: DatasetProfileService, + private dialog: MatDialog + ) { super(); + + } + + + isErrorState(control: FormControl, form: FormGroupDirective | NgForm): boolean { + + if(this.form.get('viewStyle').untouched) return false; + + return this.form.get('viewStyle').invalid; + } ngOnInit() { - if (this.form.get('multiplicity')) { - if (this.form.get('multiplicity').value.min > 1 && this.form.get('multiplicity').value.max > 1) { - this.isFieldMultiplicityEnabled = true; + + // this.subject$.pipe(takeUntil(this._destroyed)).pipe(debounceTime(600)).subscribe(model=>{ + // this.previewForm = model.buildForm(); + // }); + + + + + // if (this.form.get('multiplicity')) { + // if (this.form.get('multiplicity').value.min > 1 && this.form.get('multiplicity').value.max > 1) { + // this.isFieldMultiplicityEnabled = true; + // } + // } + + const renderStyle = this.form.get('viewStyle').get('renderStyle').value; + if(renderStyle){ + + // this.matcher.setReference(this.form); + const type = this.form.get('viewStyle').get('renderStyle').value; + + switch(type){ + case DatasetProfileFieldViewStyle.BooleanDecision: + this.viewType = this.viewTypeEnum.BooleanDecision; + break; + case DatasetProfileFieldViewStyle.CheckBox: + this.viewType = this.viewTypeEnum.CheckBox; + break; + case DatasetProfileFieldViewStyle.ComboBox: + + const comboType = this.form.get('data').get('type').value; + if(comboType === DatasetProfileComboBoxType.Autocomplete){ + this.viewType = this.viewTypeEnum.Other; + }else if(comboType === DatasetProfileComboBoxType.WordList){ + this.viewType = this.viewTypeEnum.Select; + } + break; + case DatasetProfileFieldViewStyle.InternalDmpEntities: + this.viewType = this.viewTypeEnum.InternalDmpEntities; + break; + case DatasetProfileFieldViewStyle.FreeText: + this.viewType = this.viewTypeEnum.FreeText; + break; + case DatasetProfileFieldViewStyle.RadioBox: + this.viewType = this.viewTypeEnum.RadioBox; + break; + case DatasetProfileFieldViewStyle.TextArea: + this.viewType = this.viewTypeEnum.TextArea; + break; + case DatasetProfileFieldViewStyle.DatePicker: + this.viewType = this.viewTypeEnum.DatePicker; + break; + case DatasetProfileFieldViewStyle.ExternalDatasets: + this.viewType = this.viewTypeEnum.ExternalDatasets; + break; + case DatasetProfileFieldViewStyle.DataRepositories: + this.viewType = this.viewTypeEnum.DataRepositories; + break; + case DatasetProfileFieldViewStyle.Registries: + this.viewType = this.viewTypeEnum.Registries; + break; + case DatasetProfileFieldViewStyle.Services: + this.viewType = this.viewTypeEnum.Services; + break; + case DatasetProfileFieldViewStyle.Tags: + this.viewType = this.viewTypeEnum.Tags; + break; + case DatasetProfileFieldViewStyle.Researchers: + this.viewType = this.viewTypeEnum.Researchers; //TODO RESEARCHERS + break; + case DatasetProfileFieldViewStyle.Organizations: + this.viewType = this.viewTypeEnum.Organizations; + break; + case DatasetProfileFieldViewStyle.DatasetIdentifier: + this.viewType = this.viewTypeEnum.DatasetIdentifier; + break; + case DatasetProfileFieldViewStyle.Currency: + this.viewType = this.viewTypeEnum.Currency; + break; + case DatasetProfileFieldViewStyle.Validation: + this.viewType = this.viewTypeEnum.Validation; + break; } + } + + // this.showPreview = true; + + + + // this.addNewRule(); - this.form.get('viewStyle').get('renderStyle').valueChanges - .pipe(takeUntil(this._destroyed)) - .subscribe(x => { - if (this.form.get('data')) { - this.form.removeControl('data'); + // this.form.get('viewStyle').get('renderStyle').valueChanges + // .pipe(takeUntil(this._destroyed)) + // .subscribe(x => { - switch (x) { - case DatasetProfileFieldViewStyle.BooleanDecision: - this.form.addControl('data', new BooleanDecisionFieldDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.CheckBox: - this.form.addControl('data', new CheckBoxFieldDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.ComboBox: - this.form.addControl('data', new WordListFieldDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.InternalDmpEntities: - this.form.addControl('data', new ResearchersAutoCompleteFieldDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.FreeText: - this.form.addControl('data', new FreeTextFieldDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.RadioBox: - this.form.addControl('data', new RadioBoxFieldDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.TextArea: - this.form.addControl('data', new TextAreaFieldDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.DatePicker: - this.form.addControl('data', new DatePickerDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.ExternalDatasets: - this.form.addControl('data', new ExternalDatasetsDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.DataRepositories: - this.form.addControl('data', new DataRepositoriesDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.Registries: - this.form.addControl('data', new RegistriesDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.Services: - this.form.addControl('data', new ServicesDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.Tags: - this.form.addControl('data', new TagsDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.Researchers: - this.form.addControl('data', new ResearchersDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.Organizations: - this.form.addControl('data', new OrganizationsDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.DatasetIdentifier: - this.form.addControl('data', new DatasetIdentifierDataEditorModel().buildForm()); - break; - case DatasetProfileFieldViewStyle.Currency: - this.form.addControl('data', new CurrencyDataEditorModel().buildForm()); - break; - } - } - }); + + // // const previewStatus = this.showPreview; + // //!! Important to be before the if statement + // this.showPreview = false; + + // if (this.form.get('data')) { + // this.form.removeControl('data'); + + // switch (x) { + // case DatasetProfileFieldViewStyle.BooleanDecision: + // this.form.addControl('data', new BooleanDecisionFieldDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.CheckBox: + // this.form.addControl('data', new CheckBoxFieldDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.ComboBox: + // this.form.addControl('data', new WordListFieldDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.InternalDmpEntities: + // this.form.addControl('data', new ResearchersAutoCompleteFieldDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.FreeText: + // this.form.addControl('data', new FreeTextFieldDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.RadioBox: + // this.form.addControl('data', new RadioBoxFieldDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.TextArea: + // this.form.addControl('data', new TextAreaFieldDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.DatePicker: + // this.form.addControl('data', new DatePickerDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.ExternalDatasets: + // this.form.addControl('data', new ExternalDatasetsDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.DataRepositories: + // this.form.addControl('data', new DataRepositoriesDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.Registries: + // this.form.addControl('data', new RegistriesDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.Services: + // this.form.addControl('data', new ServicesDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.Tags: + // this.form.addControl('data', new TagsDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.Researchers: + // this.form.addControl('data', new ResearchersDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.Organizations: + // this.form.addControl('data', new OrganizationsDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.DatasetIdentifier: + // this.form.addControl('data', new DatasetIdentifierDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.Currency: + // this.form.addControl('data', new CurrencyDataEditorModel().buildForm()); + // break; + // case DatasetProfileFieldViewStyle.Validation: + // this.form.addControl('data', new ValidationDataEditorModel().buildForm()); + // break; + // } + // } + + + // //reset the preview status + // // this.showPreview = previewStatus; + // this.showPreview = true; + // }); } - defaulValueRequired(viewStile: DatasetProfileFieldViewStyle): boolean { - switch (viewStile) { - case DatasetProfileFieldViewStyle.CheckBox: - return true; - case DatasetProfileFieldViewStyle.RadioBox: - case DatasetProfileFieldViewStyle.TextArea: - case DatasetProfileFieldViewStyle.FreeText: - case DatasetProfileFieldViewStyle.ComboBox: - case DatasetProfileFieldViewStyle.InternalDmpEntities: - case DatasetProfileFieldViewStyle.BooleanDecision: - case DatasetProfileFieldViewStyle.DatePicker: - case DatasetProfileFieldViewStyle.ExternalDatasets: - case DatasetProfileFieldViewStyle.DataRepositories: - case DatasetProfileFieldViewStyle.Registries: - case DatasetProfileFieldViewStyle.Services: - case DatasetProfileFieldViewStyle.Tags: - case DatasetProfileFieldViewStyle.Registries: - case DatasetProfileFieldViewStyle.Organizations: - case DatasetProfileFieldViewStyle.DatasetIdentifier: - case DatasetProfileFieldViewStyle.Currency: - return false; - default: - return false; - } - } + // defaulValueRequired(viewStile: DatasetProfileFieldViewStyle): boolean { + // switch (viewStile) { + // case DatasetProfileFieldViewStyle.CheckBox: + // return true; + // case DatasetProfileFieldViewStyle.RadioBox: + // case DatasetProfileFieldViewStyle.TextArea: + // case DatasetProfileFieldViewStyle.FreeText: + // case DatasetProfileFieldViewStyle.ComboBox: + // case DatasetProfileFieldViewStyle.InternalDmpEntities: + // case DatasetProfileFieldViewStyle.BooleanDecision: + // case DatasetProfileFieldViewStyle.DatePicker: + // case DatasetProfileFieldViewStyle.ExternalDatasets: + // case DatasetProfileFieldViewStyle.DataRepositories: + // case DatasetProfileFieldViewStyle.Registries: + // case DatasetProfileFieldViewStyle.Services: + // case DatasetProfileFieldViewStyle.Tags: + // case DatasetProfileFieldViewStyle.Registries: + // case DatasetProfileFieldViewStyle.Organizations: + // case DatasetProfileFieldViewStyle.DatasetIdentifier: + // case DatasetProfileFieldViewStyle.Currency: + // case DatasetProfileFieldViewStyle.Validation: + // return false; + // default: + // return false; + // } + // } - onIsFieldMultiplicityEnabledChange(isFieldMultiplicityEnabled: boolean) { - if (!isFieldMultiplicityEnabled) { - (this.form.get('multiplicity').get('min')).setValue(0); - (this.form.get('multiplicity').get('max')).setValue(0); - } - } + // onIsFieldMultiplicityEnabledChange(isFieldMultiplicityEnabled: boolean) { + // if (!isFieldMultiplicityEnabled) { + // (this.form.get('multiplicity').get('min')).setValue(0); + // (this.form.get('multiplicity').get('max')).setValue(0); + // } + // } addNewRule() { const rule: RuleEditorModel = new RuleEditorModel(); (this.form.get('visible').get('rules')).push(rule.buildForm()); } -} + + + private _formChangesSubscription:Subscription; + private _showPreview: boolean = false;; + + // get showPreview(): boolean{ + + // return this._showPreview; + // } + + // set showPreview(value:boolean){ + // if(value == false){//hide preview + // //close subsciption + // if(this._formChangesSubscription){ + // this._formChangesSubscription.unsubscribe(); + // this._formChangesSubscription = null; + // } + // } + + // if(value == true){ + // //value is already true + // if(this._showPreview){ + // if(this._formChangesSubscription){ + // this._formChangesSubscription.unsubscribe(); + // this._formChangesSubscription = null; + // } + // } + + // //initialize + // if(this.form.get('viewStyle').get('renderStyle').value){ + // this._generatePreviewForm(); + // } + // this._formChangesSubscription = this.form.valueChanges.subscribe(()=>{ + // this._generatePreviewForm(); + // }); + // } + // this._showPreview = value; + + // } + + // previewForm: FormGroup; + + // private _generatePreviewForm(){ + + // if(!this.form.get('data')){ + // return; + // } + // this.previewForm = null; + // const fieldEditorModel = new DatasetDescriptionFieldEditorModel(); + + // fieldEditorModel.viewStyle= { + // renderStyle: this.form.get('viewStyle').get('renderStyle').value, + // cssClass: null + // }; + + // fieldEditorModel.data = (this.form.get('data') as FormGroup).getRawValue(); + // fieldEditorModel.value = this.form.get('defaultValue').get('value').value; + // fieldEditorModel.validationRequired = (this.form.get('validations').value as Array).includes(ValidationType.Required); + + // if(this.form.get('viewStyle').get('renderStyle').value == DatasetProfileFieldViewStyle.CheckBox){ + // fieldEditorModel.value = this.form.get('defaultValue').get('value').value === 'true'; + // } + // // if(this.form.get('viewStyle').get('renderStyle').value == DatasetProfileFieldViewStyle.Researchers){ + // // fieldEditorModel.data = new ResearchersAutoCompleteFieldDataEditorModel().buildForm().getRawValue(); + // // } + // if(fieldEditorModel.viewStyle.renderStyle === DatasetProfileFieldViewStyle.Validation || (fieldEditorModel.viewStyle.renderStyle === DatasetProfileFieldViewStyle.DatasetIdentifier) + // || (fieldEditorModel.viewStyle.renderStyle === DatasetProfileFieldViewStyle.Tags) + // ){ + // fieldEditorModel.value = null; + // } + + // // const myTicket = Guid.create().toString(); + // // this.validTicket = myTicket; + // // setTimeout(() => { //TODO + // // //user hasnt make any new change to inputs /show preview + // // if(myTicket === this.validTicket){ + // // this.previewForm = fieldEditorModel.buildForm(); + // // } + // // }, 600); + + // this.subject$.next(fieldEditorModel); + + + // // setTimeout(() => { + // // this.previewForm = fieldEditorModel.buildForm(); + // // }); + // }; + + get canApplyVisibility():boolean{ + + + switch(this.viewType){ + case this.viewTypeEnum.TextArea: + case this.viewTypeEnum.FreeText: + case this.viewTypeEnum.BooleanDecision: + case this.viewTypeEnum.RadioBox: + case this.viewTypeEnum.Select: + case this.viewTypeEnum.CheckBox: + case this.viewTypeEnum.DatePicker: + return true; + + } + + + return false; + + } + + // validTicket:string; + // generatePreview(){ + // const fieldEditorModel = new DatasetDescriptionFieldEditorModel(); + + // fieldEditorModel.viewStyle= { + // renderStyle: this.form.get('viewStyle').get('renderStyle').value, + // cssClass: null + // }; + // fieldEditorModel.defaultValue = this.form.get('defaultValue').value; + // switch (this.form.get('viewStyle').get('renderStyle').value) { + // case DatasetProfileFieldViewStyle.TextArea: + // fieldEditorModel.data = { + // label: this.form.get('data').get('label').value + // }; + // break; + + // default: + // break; + // } + + // // this.previewForm = fieldEditorModel.buildForm(); + + + // } + onInputTypeChange(){ + + + const x = this.viewType; + + // this.showPreview = false; + + + + const field: Field = this.form.getRawValue(); + // field.defaultValue = {type:null, value: null}; + field.defaultValue = undefined; + if(!this.canApplyVisibility){ + field.visible.rules = []; + } + + + + // if (this.form.get('data')) { + // this.form.removeControl('data'); + // } + + // this.form.removeControl('defaultValue'); + // const defaultValueModel = new DefaultValueEditorModel(); + // this.form.addControl('defaultValue',defaultValueModel.buildForm()); + + switch (x) { + case this.viewTypeEnum.BooleanDecision:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.BooleanDecision) + // this.form.addControl('data', new BooleanDecisionFieldDataEditorModel().buildForm()); + + const data: BooleanDecisionFieldData = { + label:"" + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.BooleanDecision; + field.data = data; + + break; + } + case this.viewTypeEnum.CheckBox:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.CheckBox) + // this.form.addControl('data', new CheckBoxFieldDataEditorModel().buildForm()); + + const data: CheckBoxFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.CheckBox; + field.data = data; + + break; + } + case this.viewTypeEnum.Select:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox) + // this.form.addControl('data', new WordListFieldDataEditorModel().buildForm()); + + // this.form.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); + // this.form.get('data').updateValueAndValidity(); + const option1 = {label:'', value:''} as FieldDataOption; + + const data:WordListFieldData = { + label:'', + multiList:false, + options:[option1], + type:DatasetProfileComboBoxType.WordList + } + + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.ComboBox; + field.data = data; + + break; + } + case this.viewTypeEnum.Other:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox) + // this.form.addControl('data', new AutoCompleteFieldDataEditorModel().buildForm()); //TODO SEE + + // this.form.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('autoCompleteSingleDataList')); + // this.form.get('data').updateValueAndValidity(); + + const data: AutoCompleteFieldData = { + autoCompleteSingleDataList:[], + multiAutoComplete: false, + label:'', + type: DatasetProfileComboBoxType.Autocomplete + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.ComboBox; + field.data = data; + + break; + } + case this.viewTypeEnum.InternalDmpEntities:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.InternalDmpEntities) + // this.form.addControl('data', new ResearchersAutoCompleteFieldDataEditorModel().buildForm());//TODO TO SEE + + const data: DmpsAutoCompleteFieldData = { + label:'', + multiAutoComplete: false, + type: DatasetProfileInternalDmpEntitiesType.Dmps + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.InternalDmpEntities; + field.data = data; + + break; + } + case this.viewTypeEnum.FreeText:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.FreeText) + // this.form.addControl('data', new FreeTextFieldDataEditorModel().buildForm()); + + const data: FreeTextFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.FreeText; + field.data = data; + + break; + } + case this.viewTypeEnum.RadioBox:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.RadioBox) + // this.form.addControl('data', new RadioBoxFieldDataEditorModel().buildForm()); + + // this.form.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); + // this.form.get('data').updateValueAndValidity(); + + const data: RadioBoxFieldData= { + label:'', + options: [] + } + + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.RadioBox; + field.data = data; + + break; + } + case this.viewTypeEnum.TextArea:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.TextArea) + // this.form.addControl('data', new TextAreaFieldDataEditorModel().buildForm()); + + const data: TextAreaFieldData = { + label:'' + } + + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.TextArea; + field.data = data; + break; + } + case this.viewTypeEnum.DatePicker:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.DatePicker) + // this.form.addControl('data', new DatePickerDataEditorModel().buildForm()); + + const data: DatePickerFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.DatePicker; + field.data = data; + + break; + } + case this.viewTypeEnum.ExternalDatasets:{ + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ExternalDatasets) + // this.form.addControl('data', new ExternalDatasetsDataEditorModel().buildForm()); + + const data: ExternalDatasetsFieldData = { + label:'', + multiAutoComplete: false + } + + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.ExternalDatasets; + field.data = data; + + break; + } + case this.viewTypeEnum.DataRepositories:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.DataRepositories) + // this.form.addControl('data', new DataRepositoriesDataEditorModel().buildForm()); + + const data: DataRepositoriesFieldData = { + label: '', + multiAutoComplete: false + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.DataRepositories; + field.data = data; + + break; + } + case this.viewTypeEnum.Registries:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Registries) + // this.form.addControl('data', new RegistriesDataEditorModel().buildForm()); + + const data:RegistriesFieldData = { + label: '', + multiAutoComplete: false + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Registries; + field.data = data; + + break; + } + case this.viewTypeEnum.Services:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Services) + // this.form.addControl('data', new ServicesDataEditorModel().buildForm()); + + const data:ServicesFieldData = { + label:'', + multiAutoComplete: false + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Services; + field.data = data; + + break; + } + case this.viewTypeEnum.Tags:{ + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Tags) + // this.form.addControl('data', new TagsDataEditorModel().buildForm()); + + const data: TagsFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Tags; + field.data = data; + + + break; + } + case this.viewTypeEnum.Researchers:{ + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Researchers) + // // this.form.addControl('data', new ResearchersDataEditorModel().buildForm()); //TODO TO ASK + // this.form.addControl('data', new ResearchersAutoCompleteFieldDataEditorModel().buildForm()); + + // field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Researchers; + + const data : ResearchersAutoCompleteFieldData = { + label:'', + multiAutoComplete: false, + type: DatasetProfileInternalDmpEntitiesType.Researchers + } + + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Researchers; + field.data = data; + + + break; + } + case this.viewTypeEnum.Organizations:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Organizations) + // this.form.addControl('data', new OrganizationsDataEditorModel().buildForm()); + + const data = { + autoCompleteSingleDataList:[], + label:'', + multiAutoComplete: false, + + } as OrganizationsFieldData; //TODO + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Organizations; + field.data = data; + + break; + } + case this.viewTypeEnum.DatasetIdentifier:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.DatasetIdentifier) + // this.form.addControl('data', new DatasetIdentifierDataEditorModel().buildForm()); + + const data : DatasetIdentifierFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.DatasetIdentifier; + field.data = data; + + break; + } + case this.viewTypeEnum.Currency:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Currency) + // this.form.addControl('data', new CurrencyDataEditorModel().buildForm()); + + const data: CurrencyFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Currency; + field.data = data; + break; + } + case this.viewTypeEnum.Validation:{ + + + // this.form.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Validation) + // this.form.addControl('data', new ValidationDataEditorModel().buildForm()); + + const data:ValidationFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Validation; + field.data = data; + + break; + } + } + + // this.form.get('data').updateValueAndValidity(); + // this.form.get('viewStyle').get('renderStyle').updateValueAndValidity(); + // this.form.updateValueAndValidity(); + + + const form = (new FieldEditorModel).fromModel(field).buildForm(); + + + const fields = this.form.parent as FormArray; + let index = -1; + + fields.controls.forEach((control,i)=>{ + if(this.form.get('id').value === control.get('id').value){ + index = i + } + }); + if(index>=0){ + fields.removeAt(index); + fields.insert(index, form); + this.form = form; + + + } + + // setTimeout(() => { //TODO + // this.showPreview = true; + // }); + } + + + toggleRequired(event:MatSlideToggleChange){ + + let validationsControl = this.form.get('validations') as FormControl; + let validations: Array = validationsControl.value; + + if(event.checked){ + if(!validations.includes(ValidationType.Required)){//IS ALREADY REQUIRED + // validationsControl.setValue(validations.filter(validator=> validator != ValidationType.Required)); + // validationsControl.updateValueAndValidity(); + validations.push(ValidationType.Required); + // validationsControl.setValue(validations); + validationsControl.updateValueAndValidity(); + } + }else{ + validationsControl.setValue(validations.filter(validator=> validator != ValidationType.Required)); + validationsControl.updateValueAndValidity(); + } + + } + + get isRequired(){ + let validationsControl = this.form.get('validations') as FormControl; + let validations: Array = validationsControl.value; + + if(validations.includes(ValidationType.Required)){ + return true; + } + return false; + + } + + + onDelete(){ + this.delete.emit(); + } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/view-style-enum.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/view-style-enum.ts new file mode 100644 index 000000000..f1da6d5cd --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/view-style-enum.ts @@ -0,0 +1,21 @@ +export enum ViewStyleType{ + TextArea = "textarea", + BooleanDecision = "booleanDecision", + CheckBox = "checkBox", + FreeText = "freetext", + RadioBox = "radiobox", + DatePicker = "datePicker", + InternalDmpEntities = "internalDmpEntities", + ExternalDatasets = "externalDatasets", + DataRepositories = "dataRepositories", + Registries = "registries", + Services = "services", + Tags = "tags", + Researchers = "researchers", + Organizations = "organizations", + DatasetIdentifier = "datasetIdentifier", + Currency = "currency", + Validation = 'validation', + Select ="selection", + Other ="other" +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/final-preview/final-preview.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/final-preview/final-preview.component.html new file mode 100644 index 000000000..39882e08c --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/final-preview/final-preview.component.html @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/final-preview/final-preview.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/final-preview/final-preview.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/final-preview/final-preview.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/final-preview/final-preview.component.ts new file mode 100644 index 000000000..25afc4373 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/final-preview/final-preview.component.ts @@ -0,0 +1,28 @@ +import { Component, Input, OnInit} from '@angular/core'; +import { Rule } from '@app/core/model/dataset-profile-definition/rule'; +import { VisibilityRulesService } from '@app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service'; + + +@Component({ + selector: 'app-final-preview-component', + templateUrl: './final-preview.component.html', + styleUrls: ['./final-preview.component.scss'], + providers:[VisibilityRulesService] +}) + +export class FinalPreviewComponent implements OnInit { + + + @Input() formGroup = null; + @Input() visibilityRules:Rule[] = []; + constructor(private visibilityRulesService: VisibilityRulesService){ + + } + + + + ngOnInit(): void { + this.visibilityRulesService.buildVisibilityRules(this.visibilityRules, this.formGroup); + } + +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/rule/dataset-profile-editor-rule.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/rule/dataset-profile-editor-rule.component.html index b92190104..dd2a26762 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/rule/dataset-profile-editor-rule.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/rule/dataset-profile-editor-rule.component.html @@ -1,24 +1,99 @@
- {{i + 1}} + {{i + 1}} - {{'DATASET-PROFILE-EDITOR.STEPS.FORM.RULE.FIELDS.RULE-IF'| translate}} + - --> + + + - {{'DATASET-PROFILE-EDITOR.STEPS.FORM.RULE.FIELDS.RULE-THEN'| translate}} + - + + + + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.RULE.FIELDS.RULE-THEN'| translate}} + + + + + + + + {{option.label? option.label:'<'+ ('DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.UNTITLED' | translate) + '>'}} +
+ + {{option.id}} + +
+
+ + + + + {{option.label? option.label:'<'+ ('DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.UNTITLED' | translate) + '>'}} +
+ + {{option.id}} + +
+
-
+ + + + + \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/rule/dataset-profile-editor-rule.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/rule/dataset-profile-editor-rule.component.ts index 37e753353..14963db30 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/rule/dataset-profile-editor-rule.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/rule/dataset-profile-editor-rule.component.ts @@ -1,7 +1,10 @@ -import { Component, Input } from '@angular/core'; -import { FormArray, FormControl } from '@angular/forms'; +import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { FormArray, FormControl, FormGroup } from '@angular/forms'; import { DatasetProfileFieldViewStyle } from '../../../../../../core/common/enum/dataset-profile-field-view-style'; import { DatasetProfileComboBoxType } from '../../../../../../core/common/enum/dataset-profile-combo-box-type'; +import { ToCEntryType } from '../../../table-of-contents/table-of-contents-entry'; +import { Rule } from '@app/core/model/admin/dataset-profile/dataset-profile'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'app-dataset-profile-editor-rule-component', @@ -9,21 +12,198 @@ import { DatasetProfileComboBoxType } from '../../../../../../core/common/enum/d styleUrls: ['./dataset-profile-editor-rule.component.scss'] }) -export class DatasetProfileEditorRuleComponent { - +export class DatasetProfileEditorRuleComponent implements OnInit{ + + @Input() form: FormArray; - + @Input() viewStyleForCheck: DatasetProfileFieldViewStyle; @Input() formControlForCheck: FormControl; @Input() formArrayOptionsForCheck: FormArray; @Input() comboBoxTypeForCheck: DatasetProfileComboBoxType; @Input() viewOnly: boolean; + + + options: OptionItem[]; + sectionOptions: OptionItem[]; + fieldSetOptions: OptionItem[]; + fieldOptions: OptionItem[]; + + constructor(private language: TranslateService){ + + } targetValidation() { //TODO } - + deleteRule(index) { this.form.removeAt(index); } + + ngOnInit(): void { + this._computeOptions(); + } + + private _computeOptions(){ + this.options = this.getOptions(); + + this.sectionOptions = []; + this.fieldOptions = []; + this.fieldSetOptions = []; + this.options.forEach(option=>{ + switch (option.type) { + case ToCEntryType.Field: + this.fieldOptions.push(option); + break; + case ToCEntryType.FieldSet: + this.fieldSetOptions.push(option); + break; + case ToCEntryType.Section: + this.sectionOptions.push(option); + break; + default: + break; + } + }); + //remove options to hide if given fieldset is already hidden by option + + this.fieldOptions.forEach(e=>this._buildHiddenBy(e)); + this.fieldSetOptions.forEach(e=>this._buildHiddenBy(e)); + } + + + + computeOptions(isOpened: boolean){ + if(isOpened){ + this._computeOptions(); + } + } + + private _buildHiddenBy(fo:OptionItem){ + try{ + this.fieldOptions.forEach(foption=>{ + const rules = (foption.form.get('visible').get('rules') as FormArray).controls.map(c=>(c as FormGroup).getRawValue()) as Rule[] + const targets = rules.map(rule=>rule.target); + targets.forEach(target=>{ + if(fo.parentsIds.includes(target) && !fo.hiddenBy.includes(foption.id)){ + fo.hiddenBy.push(...foption.parentsIds); + } + }) + + }); + }catch{ + console.log('error'); + } + } + + + + + + getOptions():OptionItem[]{ + const rootForm = this.form.root; + if(rootForm){ + // const parentSections = rootForm.get('sections') as FormArray; + + const result:OptionItem[] =[]; + + const sections = rootForm.get('sections') as FormArray; + + if(sections){ + sections.controls.forEach(section=>{ + const subResult = this.buildOptions(section as FormGroup, ToCEntryType.Section,[]); + result.push(...subResult); + }); + } + + //return options + return result; + + } + //nothing found + return []; + } + + private buildOptions(form: FormGroup, type: ToCEntryType, parentIds:string[]):OptionItem[]{ + + const sections = form.get('sections') as FormArray; + const fieldSets = form.get('fieldSets') as FormArray; + const fields = form.get('fields') as FormArray; + + const result:OptionItem[] = []; + + // parentIds.push(form.get('id').value); + const currentOptionItem:OptionItem = { + id: form.get('id').value, + type: type, + label: type ===ToCEntryType.Field? form.get('data').get('label').value :form.get('title').value, + // parentsIds: [form.get('id').value] + parentsIds: [...parentIds, form.get('id').value], + form: form, + hiddenBy:[] + } + result.push(currentOptionItem); + + if(sections){ + sections.controls.forEach(section=>{ + result.push( ...this.buildOptions(section as FormGroup, ToCEntryType.Section, currentOptionItem.parentsIds) ); + }); + } + if(fieldSets){ + fieldSets.controls.forEach(fieldset=>{ + result.push( ...this.buildOptions(fieldset as FormGroup, ToCEntryType.FieldSet, currentOptionItem.parentsIds) ); + }); + } + if(fields){ + fields.controls.forEach(field=>{ + result.push( ...this.buildOptions(field as FormGroup, ToCEntryType.Field, currentOptionItem.parentsIds) ); //TODO NA TO DOUME + }); + } + + return result; + } + + get parentIds(): string[]{ + + if(!this.formControlForCheck.get('id')) return []; + + const current = this.options.find(opt=> opt.id === this.formControlForCheck.get('id').value); + + if(current){ + return current.parentsIds; + } + return []; + } + get hiddenBy(): string[]{ + if(!this.formControlForCheck.get('id')) return []; + + const current = this.options.find(opt=> opt.id === this.formControlForCheck.get('id').value); + + if(current){ + return current.hiddenBy; + } + return []; + } + + getToolTipMessage(id: string){ + if(this.parentIds.includes(id)){ + // return 'Cannot hide element that contain the field'; + return this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.RULE.HINTS.ELEMENT-CHILD-OF-TARGET'); + }else if(this.hiddenBy.includes(id)){ + return this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.RULE.HINTS.ELEMENT-HIDDEN-FROM-ELEMENT'); + } + return ''; + } + } + + +interface OptionItem{ + id: string, + label: string, + type: ToCEntryType, + parentsIds: string[], + form:FormGroup, + hiddenBy:string[] +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.html new file mode 100644 index 000000000..ee9cacacd --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.html @@ -0,0 +1,154 @@ +
+
+ + +
+
+ + + + + + + +
+ +
+ +
+
+
+ +
+ + +
+ +
+ + + + + + + + + + + drag_indicator + + + + + + +
+ +
+ +
+
+ + + + +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.scss new file mode 100644 index 000000000..bb5162e77 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.scss @@ -0,0 +1,60 @@ +$blue-color : #129D99; +$blue-color-light: #5cf7f2; + + +.field-container-active{ + border-radius: .3em; + border-left: 0.3em solid $blue-color; +} +.fieldset-actions-list li{ + cursor: pointer; +} + + + +.actions-list{ + // border: 1px solid $blue-color; + // box-shadow: 0px 3px 12px #129D9999; + border-radius: 4px; + // padding-top: 1rem; + padding: 1em 0.9em; + padding-bottom: 3em; + min-width: 166px; + + .mat-list-item-content{ + padding: 0px; + } + + + .action-list-item{ + display: flex; + align-items: center; + cursor: pointer; + + + .action-list-icon{ + font-size: 1.2em; + // padding-right: 1em; + width: 14px; + margin-right: 0.5em; + margin-left: -.09em; + height: auto; + color: #129D99;; + } + .input_icon{ + width: 14px; + margin-right: .5em; + } + .action-list-text{ + font-size: 0.9em; + } + } + .action-list-label{ + color: #212121; + font-size: small; + opacity: 0.6; + } + .list-unstyled{ + margin-bottom: 0.2rem; + } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.ts new file mode 100644 index 000000000..153305388 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component.ts @@ -0,0 +1,206 @@ +import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { ChangeDetectorRef, Inject, OnDestroy } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { FormArray, FormGroup } from '@angular/forms'; +import { FieldEditorModel } from '@app/ui/admin/dataset-profile/admin/field-editor-model'; +import { FieldSetEditorModel } from '@app/ui/admin/dataset-profile/admin/field-set-editor-model'; +import { SectionEditorModel } from '@app/ui/admin/dataset-profile/admin/section-editor-model'; +import { BaseComponent } from '@common/base/base.component'; +import { Guid } from '@common/types/guid'; +import { DragulaService } from 'ng2-dragula'; +import { Subscription } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { ToCEntry, ToCEntryType } from '../../../table-of-contents/table-of-contents-entry'; + +@Component({ + selector: 'app-dataset-profile-editor-section-fieldset-component', + templateUrl: './dataset-profile-editor-section-fieldset.component.html', + styleUrls: ['./dataset-profile-editor-section-fieldset.component.scss'] +}) + +export class DatasetProfileEditorSectionFieldSetComponent implements OnInit, OnChanges, OnDestroy { + // @Input() form: FormGroup; + //@Input() dataModel: SectionEditorModel; + // @Input() indexPath: string; + @Input() viewOnly: boolean; + // @Input() numbering: string; + @Input() tocentry: ToCEntry; + + @Output() selectedEntryId = new EventEmitter(); + @Output() dataNeedsRefresh = new EventEmitter (); + @Output() removeFieldSet = new EventEmitter(); + @Output() addNewFieldSet = new EventEmitter(); + @Output() cloneFieldSet = new EventEmitter(); + + + // FIELDSET_PREFIX_ID="FIELDSET_PREFIX_ID"; + + // @Output() fieldsetAdded = new EventEmitter(); //returns the id of the fieldset added + idprefix = "id"; + private subs = new Subscription(); + private FIELDSETS = 'FIELDSETS'; + constructor( + private dragulaService: DragulaService, + private myElement: ElementRef + ) + { + if(this.dragulaService.find(this.FIELDSETS)){ + this.dragulaService.destroy(this.FIELDSETS); + } + + + this.dragulaService.createGroup(this.FIELDSETS,{ + moves:(el, container, handle)=>{ + // if(this.viewOnly) return false; //uncomment if want to unable drag n drop in viewonly mode + if(el.id != (this.idprefix+this.tocentry.id)) return false; + if(handle.className && handle.classList.contains('handle')) return true; + return false; + } + }); + this.subs.add(this.dragulaService.drop(this.FIELDSETS).subscribe(obs=>{ + + + (this.form.get('fieldSets') as FormArray).controls.forEach((e,i)=>{ + e.get('ordinal').setValue(i); + }); + + + // obs. + this.dataNeedsRefresh.emit(); + return ; + })); + + } + ngOnDestroy(){ + // console.log('elemement destroyed -->'); + + this.subs.unsubscribe(); + } + ngOnChanges(changes: SimpleChanges): void { + // console.log('---------element updated-------'); + // console.log('numbering before:', this.numbering); + + + this.initialize(); + // console.log('----post update----'); + // console.log('numbering now', this.numbering, ' changes detected:', changes); + + + } + + + form; + numbering; + + + + private _selectedFieldSetId: string = null; + get selectedFieldSetId(){ + return this._selectedFieldSetId; + } + set selectedFieldSetId(id: string){ + + if(id === this._selectedFieldSetId) return; + this._selectedFieldSetId = id; + this.selectedEntryId.emit(this._selectedFieldSetId); + } + + private _findParentSection():FormGroup{ + const parent = this.form.parent; + + return parent; + } + + + private initialize(){ + if(this.tocentry.type === ToCEntryType.Section){ + this.form = this.tocentry.form; + this.numbering = this.tocentry.numbering; + this._selectedFieldSetId = null; + + // this._scrollToElement(this.tocentry.id); + this._scrollOnTop(); + }else if(this.tocentry.type === ToCEntryType.FieldSet){ + this.form = this.tocentry.form.parent.parent; + const numberingArray = this.tocentry.numbering.split('.'); + if(numberingArray.length){ + numberingArray.splice(numberingArray.length-1); + this.numbering = numberingArray.join('.'); + }else{ + // console.warn('!not found numbering'); + } + this.selectedFieldSetId = this.tocentry.id; + + this._scrollToElement(this.selectedFieldSetId); + }else{//scroll on top + this._scrollOnTop(); + } + } + + private _scrollToElement(id: string){ + let el = this.myElement.nativeElement.querySelector("#"+this.idprefix+id); + // let el = this.myElement.nativeElement.getElementbyId (this.selectedFieldSetId); + + if(el){ + + /* + Give time to template to build itself (extending and collapsing). + In case we are dragging from one container to another, then the ui takes around 600ms to build + individual previews (dataset-profile-editor-field.component.ts); + + */ + setTimeout(() => { + const el = this.myElement.nativeElement.querySelector("#"+this.idprefix+id); + if(el){ + el.scrollIntoView({behavior: "smooth", block:'end'}); + } + }, 700); + } + + } + private _scrollOnTop(){ + setTimeout(() => { + const el = this.myElement.nativeElement.querySelector('#topofcontainer'); + if(el){ + el.scrollIntoView({behavior:'smooth'}) + } + },200); + + } + ngOnInit() { + + } + + onRemoveFieldSet(fieldsetId: string){ + this.removeFieldSet.emit(fieldsetId); + } + + onCloneFieldSet(fieldset: FormGroup){ + this.cloneFieldSet.emit(fieldset); + } + onAddFieldSet(){ + this.addNewFieldSet.emit(this.form); + } + + private _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry{ + if(!tocentries){ + return null; + } + + let tocEntryFound = tocentries.find(entry=>entry.id === id); + + if(tocEntryFound){ + return tocEntryFound; + } + + for(let entry of tocentries){ + const result = this._findTocEntryById(id, entry.subEntries); + if(result){ + tocEntryFound = result; + break; + } + } + + return tocEntryFound? tocEntryFound: null; + } +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.html index c1f35e67a..2b799f3f6 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.html @@ -1,31 +1,42 @@ 
-

{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.TITLE' | translate}}

+ + - + + +
{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-NAME' | translate}} *
+
{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-NAME-HINT' | translate}}
+ + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} - + + +
{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-DESCRIPTION' | translate}}
+
{{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-DESCRIPTION-HINT' | translate}}
+ + + + + -

{{'DATASET-PROFILE-EDITOR.STEPS.FORM.SECTION.FIELDS.FIELDS-TITLE' | + + + + + + + + + + + -

diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.scss index 02d304fc1..7645fb831 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.scss +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.scss @@ -1,4 +1,6 @@ .dataset-profile-editor { + + .field-card { margin-bottom: 1em; } @@ -6,3 +8,24 @@ .deleteBtn{ margin-right:2em; } + +.heading { + text-align: left; + font-weight: 700; + font-size: 18px; + letter-spacing: 0px; + color: #212121; + opacity: 0.81; + // margin-top: 1.625rem; + margin-bottom: 0.625rem; +} + +.hint { + text-align: left; + font-weight: 400; + font-size: 16px; + letter-spacing: 0px; + color: #212121; + opacity: 0.81; + margin-bottom: 0.125rem; +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.ts index 9f54487c6..4e6fac781 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/section/dataset-profile-editor-section.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { FormArray, FormGroup } from '@angular/forms'; import { FieldEditorModel } from '@app/ui/admin/dataset-profile/admin/field-editor-model'; import { FieldSetEditorModel } from '@app/ui/admin/dataset-profile/admin/field-set-editor-model'; @@ -15,45 +15,59 @@ import { takeUntil } from 'rxjs/operators'; export class DatasetProfileEditorSectionComponent extends BaseComponent implements OnInit { @Input() form: FormGroup; - @Input() dataModel: SectionEditorModel; + //@Input() dataModel: SectionEditorModel; @Input() indexPath: string; @Input() viewOnly: boolean; + @Output() fieldsetAdded = new EventEmitter(); //returns the id of the fieldset added constructor() { super(); } ngOnInit() { - this.form.root.get('pages').valueChanges - .pipe(takeUntil(this._destroyed)) - .subscribe(x => - this.keepPageSelectionValid(x) - ); + + //TODO + // this.form.root.get('pages').valueChanges + // .pipe(takeUntil(this._destroyed)) + // .subscribe(x => + // this.keepPageSelectionValid(x) + // ); } addField() { const fieldSet: FieldSetEditorModel = new FieldSetEditorModel(); + fieldSet.ordinal = (this.form.get('fieldSets') as FormArray).length; + const field: FieldEditorModel = new FieldEditorModel(); field.id = Guid.create().toString(); + field.ordinal = 0;//first field in fields fieldSet.fields.push(field); - if (this.dataModel.fieldSets) { + // if (this.dataModel.fieldSets) { fieldSet.id = Guid.create().toString(); - this.dataModel.fieldSets.push(fieldSet); + //this.dataModel.fieldSets.push(fieldSet); + //} + const fieldsetsArray = this.form.get('fieldSets') as FormArray; + fieldsetsArray.push(fieldSet.buildForm()); + + const fieldSetForm = fieldsetsArray.at(fieldsetsArray.length-1); + //emit id inserted + if(fieldSetForm){ + const id: string = fieldSetForm.get('id').value; + this.fieldsetAdded.emit(id); } - (this.form.get('fieldSets')).push(fieldSet.buildForm()); } addSectioninSection() { const section: SectionEditorModel = new SectionEditorModel(); - this.dataModel.sections.push(section); + //this.dataModel.sections.push(section); (this.form.get('sections')).push(section.buildForm()); } DeleteSectionInSection(index) { - this.dataModel.sections.splice(index, 1); + //this.dataModel.sections.splice(index, 1); (this.form.get('sections')).removeAt(index); } deleteFieldSet(index) { - this.dataModel.fieldSets.splice(index, 1); + //this.dataModel.fieldSets.splice(index, 1); (this.form.get('fieldSets')).removeAt(index); } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/custom-validators/editor-custom-validators.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/custom-validators/editor-custom-validators.ts new file mode 100644 index 000000000..f23d30d9f --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/custom-validators/editor-custom-validators.ts @@ -0,0 +1,67 @@ +import { Inject, Injectable } from "@angular/core"; +import { AbstractControl, FormArray, ValidationErrors, ValidatorFn } from "@angular/forms"; + + +export class EditorCustomValidators{ + + static atLeastOneElementListValidator(arrayToCheck): ValidatorFn{ + return (control: AbstractControl): ValidationErrors | null=>{ + + const fa = control.get(arrayToCheck) as FormArray; + + if(!fa || fa.length === 0){ + return {[EditorCustomValidatorsEnum.emptyArray]: true}; + } + return null; + } + } + static pagesHaveAtLeastOneSection(pagesArrayName:string,sectionsArrayName:string ): ValidatorFn{ + + return (control: AbstractControl): ValidationErrors | null=>{ + + const pages = control.get(pagesArrayName) as FormArray; + const sections = control.get(sectionsArrayName) as FormArray; + + + const pageIdsArray = pages.controls.map(page=>page.get('id').value); + const sectionsPageIds = sections.controls.map(section=> section.get('page').value); + + let invalidMessage = null; + + pageIdsArray.forEach(pageId=>{ + if(!sectionsPageIds.includes(pageId)){ + invalidMessage = {[EditorCustomValidatorsEnum.atLeastOneSectionInPage]:true}; + } + }) + + return invalidMessage; + } + } + + static sectionHasAtLeastOneChildOf(fieldsetsArrayName, sectionsArrayName): ValidatorFn{ + + return (control: AbstractControl): ValidationErrors | null=>{ + + const fieldsets = control.get(fieldsetsArrayName) as FormArray; + const sections = control.get(sectionsArrayName) as FormArray; + + + if((fieldsets && fieldsets.length) || (sections && sections.length)){ + return null; + } + + return {[EditorCustomValidatorsEnum.sectionMustHaveOneChild] : true}; + } + } + + + +} + + + +export enum EditorCustomValidatorsEnum{ + sectionMustHaveOneChild = "sectionMustHaveOneChild", + atLeastOneSectionInPage = 'atLeastOneSectionInPage', + emptyArray="emptyArray" +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor-model.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor-model.ts index 700dd4ae7..d3cf2775e 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor-model.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor-model.ts @@ -1,8 +1,10 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { UserInfoListingModel } from '@app/core/model/user/user-info-listing'; import { DatasetProfile } from '../../../../core/model/admin/dataset-profile/dataset-profile'; import { BaseFormModel } from '../../../../core/model/base-form-model'; import { PageEditorModel } from '../admin/page-editor-model'; import { SectionEditorModel } from '../admin/section-editor-model'; +import { EditorCustomValidators } from './custom-validators/editor-custom-validators'; export class DatasetProfileEditorModel extends BaseFormModel { @@ -14,6 +16,7 @@ export class DatasetProfileEditorModel extends BaseFormModel { public version: number; private description: string; private language: string; + private users: UserInfoListingModel[] = []; fromModel(item: DatasetProfile): DatasetProfileEditorModel { if (item.sections) { this.sections = item.sections.map(x => new SectionEditorModel().fromModel(x)); } @@ -23,6 +26,7 @@ export class DatasetProfileEditorModel extends BaseFormModel { this.version = item.version; this.description = item.description; this.language = item.language; + this.users = item.users; return this; } @@ -32,7 +36,8 @@ export class DatasetProfileEditorModel extends BaseFormModel { description: [{ value: this.description, disabled: (disabled && !skipDisable.includes('DatasetProfileEditorModel.description')) }, [Validators.required]], language: [{ value: this.language, disabled: (disabled && !skipDisable.includes('DatasetProfileEditorModel.language')) }, [Validators.required]], status: [{ value: this.status, disabled: (disabled && !skipDisable.includes('DatasetProfileEditorModel.status')) }], - version: [{ value: this.version, disabled: (disabled && !skipDisable.includes('DatasetProfileEditorModel.version')) }] + version: [{ value: this.version, disabled: (disabled && !skipDisable.includes('DatasetProfileEditorModel.version')) }], + users: [{ value: this.users, disabled: (disabled && !skipDisable.includes('DatasetProfileEditorModel.users')) }] }); const sectionsFormArray = new Array(); @@ -48,6 +53,10 @@ export class DatasetProfileEditorModel extends BaseFormModel { pagesFormArray.push(form); }); formGroup.addControl('pages', this.formBuilder.array(pagesFormArray)); + + formGroup.setValidators([EditorCustomValidators.atLeastOneElementListValidator('pages'), EditorCustomValidators.pagesHaveAtLeastOneSection('pages','sections')]); + formGroup.updateValueAndValidity(); + return formGroup; } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.html index c6d9198aa..515959dc0 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.html @@ -1,106 +1,481 @@ -
+ + +
-

{{'DATASET-PROFILE-EDITOR.TITLE.NEW-PROFILE' | translate}}

-

- {{'DATASET-PROFILE-EDITOR.TITLE.NEW-PROFILE-CLONE' | translate}} - {{form.get('label').value}} -

-

- {{'DATASET-PROFILE-EDITOR.TITLE.NEW-PROFILE-VERSION' | translate}} - {{form.get('label').value}} -

-

{{form.get('label').value}}

- - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - - - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - - - - - - - {{ lang.name }} - - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} - - -
- + + +
+
+ +
+

{{'DATASET-PROFILE-EDITOR.TITLE.NEW-PROFILE' | translate}}

+

+ {{'DATASET-PROFILE-EDITOR.TITLE.NEW-PROFILE-CLONE' | translate}} + {{form.get('label').value}} +

+

+ {{'DATASET-PROFILE-EDITOR.TITLE.NEW-PROFILE-VERSION' | translate}} + {{form.get('label').value}} +

+ +

{{form.get('label').value}}

+
+ + +
- - - {{'DATASET-PROFILE-EDITOR.STEPS.PAGES.TITLE' | translate}} -
- -
- + +
+ + + +
+
+
+
+
+
+ + + done + + + {{idx+1}} + + {{step.label}} + +
- - - {{'DATASET-PROFILE-EDITOR.STEPS.FORM.TITLE' | translate}} -
- - - - {{i + 1}}. {{form.get('sections').get(''+i).get('title').value}} - - - -
- - -
-
-
-
- -
-
-
- - -
- -
-
- - - -
-
- -
-
- - -
-
- -
-
- + + + + + + + + +
+ +
+
+ + +
+ + + + + + + + + +
+
+ + + +
+
+
+
1.1 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-NAME'| translate}} *
+
{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-NAME-HINT'| translate}}
+ + + {{'GENERAL.VALIDATION.REQUIRED' | + translate}} + + +
+
+
1.2 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-DESCRIPTION'| translate}} *
+ +
{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-DESCRIPTION-HINT'| translate}}
+ + + {{'GENERAL.VALIDATION.REQUIRED' + | translate}} + + +
+ +
+ +
1.3 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-LANGUAGE'| translate}} *
+ + + + + {{ lang.name }} + + + {{'GENERAL.VALIDATION.REQUIRED' | + translate}} + + +
+ +
+ + + +
+
+ + +
+ + +
+ + +
+ + + + + + +
+
+ + + +
+ +
+ +
+
+ + +
+
+
+ +
+
{{'DATASET-PROFILE-EDITOR.STEPS.PAGE-INFO.PAGE-NAME' | translate}} *
+
{{'DATASET-PROFILE-EDITOR.STEPS.PAGE-INFO.PAGE-NAME-HINT' | translate}}
+ + + {{'GENERAL.VALIDATION.REQUIRED' | translate}} + +
+ +
+ +
+ + +
+ +
+
+ +
+ + + +
+ + +
+ +
+
+
+
+ {{'DATASET-PROFILE-EDITOR.STEPS.PAGE-INFO.ACTIONS.NOTHING-HERE-HINT'| translate}} +
+
+
+
+ {{'DATASET-PROFILE-EDITOR.STEPS.PAGE-INFO.ACTIONS.START-CREATING-PAGE-START'| translate}} + + add + + {{'DATASET-PROFILE-EDITOR.STEPS.PAGE-INFO.ACTIONS.START-CREATING-PAGE-END'| translate}} + +
+
+
+
+ +
+
+
+ + +
+
+ + + + +
+
+ + + + +
+
+ + + +
+
+ + + +
+
+ +
+ + + + + +
+ +
+
+ + + + + + + + +
+
+ +
+
+ + + + +
+
+ +
+
+ +
+ +
+ +
+
+ + + + + + + + +
+ + + + +
+ + +
+
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.scss index cdba2f1cc..dd73219f6 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.scss +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.scss @@ -1,4 +1,6 @@ .dataset-profile-editor { + margin-top: 1.3rem; + .full-width { width: 100%; } @@ -7,3 +9,346 @@ .deleteBtn{ margin-right:2em; } + + + +// REFACTORED CSS + +.stepper{ + background-color: transparent; + min-height: 500px; +} +.content-displayer{ + background-color: #fff; + border-radius: 7px; + box-shadow: 0px 1px 2px #bfbfbf; + padding: 0em; + // padding-top: 0em; + overflow: visible; + // min-height: 30em; +} + +.heading { + text-align: left; + font-weight: 700; + font-size: 18px; + letter-spacing: 0px; + color: #212121; + opacity: 0.81; + // margin-top: 1.625rem; + margin-bottom: 0.625rem; +} + +.hint { + text-align: left; + font-weight: 400; + font-size: 16px; + letter-spacing: 0px; + color: #212121; + opacity: 0.81; + margin-bottom: 0.125rem; +} + +//TO CHECK +:host ::ng-deep .mat-horizontal-content-container { + overflow: visible; + // padding: 0px; +} +:host ::ng-deep .mat-form-field-outline{ + background-color: #ffffff4f; +} + +:host ::ng-deep .mat-horizontal-stepper-header-container { + display: none !important; +} + +#progress{ + position: absolute; + height: 100%; + width: 100%; + clip-path: polygon(0 0, 0 0, 0 0, 0 0); + transition-property: clip-path; + transition-duration: 600ms; + transition-timing-function: ease-out; + transition-delay: 50ms; + box-shadow: 0px 1px 2px #bfbfbf; + background-color: #F7DD72; +} +#progress-container{ + box-shadow: 1px 1px 6px #00000029;; + // border-radius: .3em; +} +// .import-btn { +// background: #ffffff 0% 0% no-repeat padding-box; +// border-radius: 30px; +// // color: #129d99; +// // border: 1px solid #129d99; +// padding-left: 2em; +// padding-right: 2em; +// color: #000; +// border: 1px solid #000; +// } +.navigate-btn { + border-radius: 30px; + background-color: #f7dd72; + border: 1px solid transparent; + padding-left: 2em; + padding-right: 2em; + box-shadow: 0px 3px 6px #1E202029; + + transition-property: background-color, color; + transition-duration: 200ms; + transition-delay: 50ms; + transition-timing-function: ease-in-out; +} +.navigate-btn-disabled{ +background-color: #CBCBCB; +color: #FFF; +} + +.create-section-btn { + border-radius: 30px; + background-color: #f7dd72; + border: 1px solid transparent; + padding-left: 1em; + padding-right: 1em; + box-shadow: 0px 3px 6px #1E202029; + line-height: 1.7em; +} + + +$blue-color : #129D99; +$blue-color-light: #5cf7f2; + +.finalize-btn { + border-radius: 30px; + border: 1px solid #129D99; + background: transparent; + padding-left: 2em; + padding-right: 2em; + box-shadow: 0px 3px 6px #1E202029; + color: #129D99; +} +.template_action_btn{ + border-radius: 30px; + border: 1px solid #129D99; + background: transparent; + padding-left: 2em; + padding-right: 2em; + height: 2.5em; + box-shadow: 0px 3px 6px #1E202029; + color: #129D99; + flex: 0 0 auto; + &.save-btn{ + background-color: #f7dd72; + border: 1px solid transparent; + padding-left: 2em; + padding-right: 2em; + box-shadow: 0px 3px 6px #1E202029; + color: #212121; + + transition-property: background-color, color; + transition-duration: 200ms; + transition-delay: 50ms; + transition-timing-function: ease-in-out; + } + &:disabled{ + background-color: #CBCBCB; + color: #FFF; + border: 0px; + } +} + + + + +.stepper-title-label{ + cursor: pointer; + transition-property: color, opacity; + transition-duration: 200ms; + transition-delay: 50ms; + transition-timing-function: ease-in-out; + color: #212121; + font-size: 0.9em; +} + +.stepper-title-label-locked{ + color: #DEDEDE; +} + +.stepper-title-label-completed{ + opacity: 0.5; +} +.page-infos{ + padding: 2em; +} +.mat-subheader{ + padding: 0px; +} + +//for the acitons list toolbar +// .mat-list-item.mat-list-item-with-avatar{ +// padding-left: 0em; +// } + + +.actions-list{ + // border: 1px solid $blue-color; + box-shadow: 0px 3px 12px #129D9999; + border-radius: 4px; + // padding-top: 1rem; + padding: 1em 0.9em; + min-width: 166px; + + .mat-list-item-content{ + padding: 0px; + } + + + .action-list-item{ + display: flex; + align-items: center; + cursor: pointer; + + + .action-list-icon{ + font-size: 1.2em; + // padding-right: 1em; + width: 14px; + margin-right: 0.5em; + margin-left: -.09em; + height: auto; + color: #129D99;; + } + .input_icon{ + width: 14px; + margin-right: .5em; + } + .action-list-text{ + font-size: 0.9em; + } + } + .action-list-label{ + color: #212121; + font-size: small; + opacity: 0.6; + } + .list-unstyled{ + margin-bottom: 0.2rem; + } +} + + +// .mli{ +// height: auto; +// } + + +:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-outline { + background: #fafafa !important; +} + +:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { + font-size: 1rem; + padding: 0.6em 0 1em 0 !important; +} + +.basic-info-input{ + margin-top: 2em; + margin-bottom: 2em; +} + +.delete-btn { + // background-color: rgba(255, 0, 0, 0.76); + // color: white; +} + +.email-input { + border-color: black; + border-style: solid; + border-width: thin; + border-radius: 4px; + margin-right: 5px; + background-color: white; + padding-top: 10px; + padding-bottom: 10px; +} + + +.user-table { + // border: thin solid rgb(179, 173, 173); + border: 1px solid rgba(0, 0, 0, 0.12); + background-color: white; + border-radius: 4px; + padding-bottom: 1em; +} + +.user-table-header { + text-align: center; + display: revert; + background-color: rgb(243 245 248); +} + +.user-table-header th { + padding-top: 10px; + padding-bottom: 10px; + // border: thin solid darkgray; +} + +.user-table-row { + // display: revert; + background-color: #fafafa; + td{ + padding-top: .5em; + padding-bottom: .5em; + } + &:hover{ + background-color: #eef5f6; + } + border-top:1px solid rgba(0, 0, 0, 0.12); +} + +.user-table-row:nth-child(even) { + // background-color: silver; +} +.user-table-body{ + text-align: center; +} +.scroll-on-top{ + position: fixed; + bottom: 2em; + right: 2em; +} + + +#stepper-navigation-wrapper{ + z-index: 99; + width: 100%; + #stepper-navigation{ + background-color: #f4f4f4; + z-index: 99; + } +} + +.actions-template{ + align-self:flex-end; + display: flex; + justify-content: space-between; + width: 100%; + padding-left: 15px; + padding-right: 15px; +} +.stepper-navigation-outer-wrapper{ + padding: 2em; + margin-bottom: 1em; + background: #f4f4f4; + position: sticky; + top: 0.01em; + z-index: 99; +} + +#title-column{ + justify-content: space-between; + align-items: baseline; + padding-right: 15px; +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts index 5d6d75a02..026434205 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/dataset-profile-editor.component.ts @@ -1,13 +1,13 @@ -import { of as observableOf, Observable } from 'rxjs'; -import { HttpErrorResponse } from '@angular/common/http'; -import { Component, OnInit, ViewChild } from '@angular/core'; -import { FormArray, FormControl, FormGroup } from '@angular/forms'; +import { of as observableOf, Observable, combineLatest, BehaviorSubject,of } from 'rxjs'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { AfterViewInit, Component, ElementRef, OnChanges, OnInit, QueryList, SimpleChanges, ViewChild } from '@angular/core'; +import { AbstractControl, Form, FormArray, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; -import { MatHorizontalStepper } from '@angular/material/stepper'; +import { MatHorizontalStepper, MatStep } from '@angular/material/stepper'; import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { map, takeUntil } from 'rxjs/operators'; +import { debounce, debounceTime, filter, map, mergeMap, takeUntil, tap } from 'rxjs/operators'; import * as FileSaver from 'file-saver'; import { BaseComponent } from '@common/base/base.component'; import { DatasetProfileEditorModel } from '@app/ui/admin/dataset-profile/editor/dataset-profile-editor-model'; @@ -25,13 +25,43 @@ import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog import { LanguageInfo } from '@app/core/model/language-info'; import { LanguageInfoService } from '@app/core/services/culture/language-info-service'; import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { Link, LinkToScroll } from '@app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents'; +import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; +import { DatasetWizardEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; +import { NewEntryType, ToCEntry, ToCEntryType } from '../table-of-contents/table-of-contents-entry'; +import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; +import { FieldSetEditorModel } from '../admin/field-set-editor-model'; +import { Guid } from '@common/types/guid'; +import { FieldEditorModel } from '../admin/field-editor-model'; +import { VisibilityRulesService } from '@app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service'; +import { CdkStep, StepperSelectionEvent } from '@angular/cdk/stepper'; +import { DatasetDescriptionCompositeFieldEditorModel, DatasetDescriptionFieldEditorModel, DatasetDescriptionFormEditorModel, DatasetDescriptionPageEditorModel, DatasetDescriptionSectionEditorModel } from '@app/ui/misc/dataset-description-form/dataset-description-form.model'; +import { Rule } from '@app/core/model/dataset-profile-definition/rule'; +import { DatasetProfileFieldViewStyle } from '@app/core/common/enum/dataset-profile-field-view-style'; +import { invalid } from '@angular/compiler/src/render3/view/util'; +import { SideNavService } from '@app/core/services/sidenav/side-nav.sevice'; +import { EditorCustomValidators, EditorCustomValidatorsEnum } from './custom-validators/editor-custom-validators'; +import { CustomErrorValidator } from '@common/forms/validation/custom-validator'; +import { GENERAL_ANIMATIONS, STEPPER_ANIMATIONS } from './animations/animations'; +import { DatasetProfileComboBoxType } from '@app/core/common/enum/dataset-profile-combo-box-type'; +import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; +import { DmpInvitationUser } from '@app/core/model/dmp/invitation/dmp-invitation-user'; +import { RequestItem } from '@app/core/query/request-item'; +import { DmpInvitationUserCriteria } from '@app/core/query/dmp/dmp-invitation-user-criteria'; +import { DmpInvitationService } from '@app/core/services/dmp/dmp-invitation.service'; +import { UserService } from '@app/core/services/user/user.service'; +import { MatInput } from '@angular/material'; + const skipDisable: any[] = require('../../../../../assets/resources/skipDisable.json'); @Component({ selector: 'app-dataset-profile-editor-component', templateUrl: './dataset-profile-editor.component.html', - styleUrls: ['./dataset-profile-editor.component.scss'] + styleUrls: ['./dataset-profile-editor.component.scss'], + animations:[...STEPPER_ANIMATIONS, ...GENERAL_ANIMATIONS], + providers:[VisibilityRulesService] }) export class DatasetProfileEditorComponent extends BaseComponent implements OnInit { @@ -51,6 +81,24 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn nestedCount: number[] = []; nestedIndex: number = 0; errorMessages: string[] = []; + tocEntryEnumValues = ToCEntryType; + public userChipList:any[] = []; + displayedColumns: String[] = ['name', 'email', 'button']; + + colorizeInvalid:boolean = false; + inputUserState: 'untriggered'| 'triggered' = 'untriggered'; + private _inputUserField$:BehaviorSubject = new BehaviorSubject(false); + private _inputUserButton$:BehaviorSubject = new BehaviorSubject(false); + showScrollOnTopButton:boolean = false; + + private stepperNavigationObserver: IntersectionObserver; + + // customEditorValidators = new EditorCustomValidators(); + + // sectionIdPreviewed:string = null; + // currentSubForm:FormGroup = null; + // currentSectionIndex: number = null; + // currentSectionEditorModel: SectionEditorModel = null; constructor( private datasetProfileService: DatasetProfileService, @@ -60,14 +108,32 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn private uiNotificationService: UiNotificationService, private language: TranslateService, private dialog: MatDialog, - private languageInfoService: LanguageInfoService + private languageInfoService: LanguageInfoService, + private httpClient: HttpClient, + private matomoService: MatomoService, + private enumUtils: EnumUtils, + private datasetWizardService: DatasetWizardService, + private visibilityRulesService: VisibilityRulesService, + private fb: FormBuilder, + private sidenavService: SideNavService, + private userService: UserService ) { super(); // this.profileID = route.snapshot.params['id']; // this.cloneId = route.snapshot.params['cloneid']; } + ngOnDestroy(){ + this.sidenavService.setStatus(true); + if(this.stepperNavigationObserver){ + this.stepperNavigationObserver.disconnect(); + this.stepperNavigationObserver = null; + } + } + ngOnInit() { + this.sidenavService.setStatus(false); + this.matomoService.trackPageView('Admin: Dataset Profile Edit'); this.route.paramMap.pipe(takeUntil(this._destroyed)).subscribe((paramMap: ParamMap) => { this.datasetProfileId = paramMap.get('id'); const cloneId = paramMap.get('cloneid'); @@ -92,7 +158,7 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn } catch (error) { this.logger.error('Could not parse MasterItem: ' + data); console.log(error) - this.uiNotificationService.snackBarNotification(this.language.instant('NOTIFICATIONS.DEFAULT.ERROR'), SnackBarNotificationLevel.Error); + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR'), SnackBarNotificationLevel.Error); } }, error => this.onCallbackError(error) @@ -114,9 +180,10 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn this.dataModel.status = DatasetProfileEnum.SAVED; this.form = this.dataModel.buildForm(); this.prepareForm(); - } catch { + } catch (error){ this.logger.error('Could not parse MasterItem: ' + data); - this.uiNotificationService.snackBarNotification(this.language.instant('NOTIFICATIONS.DEFAULT.ERROR'), SnackBarNotificationLevel.Error); + this.logger.error(error); + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR'), SnackBarNotificationLevel.Error); } }, error => this.onCallbackError(error) @@ -136,9 +203,10 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn this.form.controls['description'].disable(); this.form.controls['language'].disable(); this.prepareForm(); - } catch { + } catch (error){ this.logger.error('Could not parse MasterItem: ' + data); - this.uiNotificationService.snackBarNotification(this.language.instant('NOTIFICATIONS.DEFAULT.ERROR'), SnackBarNotificationLevel.Error); + this.logger.error(error); + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.FORM-VALIDATION-DISPLAY-DIALOG.HAS-ERROR'), SnackBarNotificationLevel.Error); } }, error => this.onCallbackError(error) @@ -146,20 +214,172 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn } else { this.dataModel = new DatasetProfileEditorModel(); this.form = this.dataModel.buildForm(); + // this.form.setValidators([EditorCustomValidators.atLeastOneElementListValidator('pages'), EditorCustomValidators.pagesHaveAtLeastOneSection('pages', 'sections')]); + if (this.dataModel.status === DatasetProfileEnum.FINALIZED) { this.form.disable(); this.viewOnly = true; } - this.addSection(); - this.addPage(); + // this.addSection(); + // this.addPage(); + this.visibilityRulesService.buildVisibilityRules([],this.form); + setTimeout(() => { + this.steps = this.stepper.steps; + }); + this._initializeToCEntries(); + } }); + + + combineLatest(this._inputUserButton$.asObservable(),this._inputUserField$.asObservable()) + .pipe(takeUntil(this._destroyed)) + .pipe(debounceTime(400)) + .pipe(filter(_=> !_[0] && !_[1])) + .subscribe(_=>{ + // if(!this.userFormDisabled) + this.inputUserState = 'untriggered'; + }); } + private _initializeToCEntries(){ + const tocentries = this.refreshToCEntries();//tocentries are sorted based on their ordinal value + + this._updateOrdinals(tocentries); + + if(tocentries && tocentries.length){ + this.selectedTocEntry = tocentries[0]; + } + setTimeout(() => { + // const stepperNavigation = document.getElementById('stepper-navigation'); + const titleColumn = document.getElementById('title-column'); + // const stepperNavigationWrapper = document.getElementById('stepper-navigation-wrapper'); + + if(titleColumn){ + if(this.stepperNavigationObserver){ + this.stepperNavigationObserver.disconnect(); + this.stepperNavigationObserver = null; + } + this.stepperNavigationObserver = new IntersectionObserver((e)=>{ + e.forEach(_=>{ + if(_.isIntersecting){ + this.showScrollOnTopButton = false; + // stepperNavigation.classList.remove('fixed-navigation'); + // stepperNavigationWrapper.classList.remove('fixed-navigation'); + }else{ + this.showScrollOnTopButton = true; + // stepperNavigation.classList.add('fixed-navigation'); + // stepperNavigationWrapper.classList.add('fixed-navigation'); + } + }) + }, {root:null, rootMargin:'0px', threshold:0}); + this.stepperNavigationObserver.observe(titleColumn); + }else{ + console.log('Could not load scroll On Top Observer') + } + }, 400); + // this._initializeFormValidity(tocentries); + + //Checking invalid visibilty RULES + const fieldsetEntries = this._getAllFieldSets(this.toCEntries); + const fieldSetHavingInvalidVisibilityRules:ToCEntry[] = fieldsetEntries + .filter(entry=>{ + const fieldsFormGroup = entry.form.get('fields'); + const invalid = (fieldsFormGroup as FormArray).controls.filter(field=>{ + return this.hasInvalidVisibilityRule(field as FormGroup); + + }); + if(invalid && invalid.length){ + return true; + } + return false; + }); + + + if(fieldSetHavingInvalidVisibilityRules.length){ + const occurences = fieldSetHavingInvalidVisibilityRules.map(record=>record.numbering).join(' , '); + this.dialog.open(ConfirmationDialogComponent, { + data:{ + message: this.language.instant('DATASET-PROFILE-EDITOR.ERRORS.INVALID-VISIBILITY-RULES.MESSAGE-START')+occurences+ this.language.instant('DATASET-PROFILE-EDITOR.ERRORS.INVALID-VISIBILITY-RULES.MESSAGE-END'), + confirmButton: this.language.instant('DATASET-PROFILE-EDITOR.ERRORS.INVALID-VISIBILITY-RULES.CONFIRM-YES'), + cancelButton: this.language.instant('DATASET-PROFILE-EDITOR.ERRORS.INVALID-VISIBILITY-RULES.CONFIRM-NO') + }, + maxWidth:'30em' + }) + .afterClosed() + .subscribe(confirm=>{ + if(confirm){ + this.removeFieldSetVisibilityRules(fieldSetHavingInvalidVisibilityRules); + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-PROFILE-EDITOR.ERRORS.INVALID-VISIBILITY-RULES.REMOVE-SUCCESS'), SnackBarNotificationLevel.Success); + + }else{ + console.log('User not confirmed'); + } + }) + } + + + } + + private removeFieldSetVisibilityRules(fieldsets:ToCEntry[]){ + + if(!fieldsets || !fieldsets.length) return; + + fieldsets.forEach(fieldset=>{ + if(fieldset.type != ToCEntryType.FieldSet){ + return; + } + const fields = fieldset.form.get('fields') as FormArray; + + fields.controls.forEach(fieldControl=>{ + if(this.hasInvalidVisibilityRule(fieldControl as FormGroup)){ + try{ + (fieldControl.get('visible').get('rules') as FormArray).clear(); + }catch{} + } + }) + + }) + + } + + private hasInvalidVisibilityRule(field: FormGroup):boolean{ + const renderStyle = field.get('viewStyle').get('renderStyle').value; + if(renderStyle && ![ + DatasetProfileFieldViewStyle.TextArea, + DatasetProfileFieldViewStyle.FreeText, + DatasetProfileFieldViewStyle.BooleanDecision, + DatasetProfileFieldViewStyle.RadioBox, + DatasetProfileFieldViewStyle.CheckBox, + DatasetProfileFieldViewStyle.DatePicker, + DatasetProfileFieldViewStyle.ComboBox, + ].includes(renderStyle)){ + if(((renderStyle === DatasetProfileFieldViewStyle) && (field.get('data').get('type').value === DatasetProfileComboBoxType.WordList))){ + return false; + } + try{ + if(field.get('visible').get('rules').value.length){ + return true; + } + return false; + + }catch{ + return false; + } + }else{ + return false; + } + } + + prepareForm() { + this.visibilityRulesService.buildVisibilityRules([],this.form); + // this.form.setValidators([EditorCustomValidators.atLeastOneElementListValidator('pages'),EditorCustomValidators.pagesHaveAtLeastOneSection('pages', 'sections')]); + // this.form.updateValueAndValidity(); this.form.valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(change => { + // this.datasetProfileService.preview(this.form.value) // .pipe(takeUntil(this._destroyed)) // .subscribe(dataset => { @@ -169,7 +389,17 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn // this.previewerFormGroup = this.dataWizardModel.buildForm().get('datasetProfileDefinition'); // }); }); - this.form.updateValueAndValidity(); + setTimeout(() => { + this.steps = this.stepper.steps; + }); + this._initializeToCEntries(); + // console.log(this.form.get('users').value); + if (this.form.get('users').value !== null && this.form.get('users').value !== undefined) { + this.userChipList = [...this.form.get('users').value]; + } else { + this.userChipList = []; + } + } onIsMultiplicityEnabledChange(isMultiplicityEnabled: boolean) { @@ -179,31 +409,33 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn } } - addSection() { - const section: SectionEditorModel = new SectionEditorModel(); - this.dataModel.sections.push(section); - (this.form.get('sections')).push(section.buildForm()); - } + // addSection() { + // const section: SectionEditorModel = new SectionEditorModel(); + // this.dataModel.sections.push(section); + // (this.form.get('sections')).push(section.buildForm()); + // } - addPage() { - const page: PageEditorModel = new PageEditorModel(this.dataModel.pages.length); - this.dataModel.pages.push(page); - (this.form.get('pages')).push(page.buildForm()); - } + // addPage() { + // const page: PageEditorModel = new PageEditorModel(this.dataModel.pages.length); + // this.dataModel.pages.push(page); + // (this.form.get('pages')).push(page.buildForm()); + // } - DeleteSection(index) { - this.dataModel.sections.splice(index, 1); - (this.form.get('sections')).removeAt(index); - } + // DeleteSection(index) { + // this.dataModel.sections.splice(index, 1); + // (this.form.get('sections')).removeAt(index); + // } onSubmit() { let data = this.form.value; + if (this.datasetProfileId) { this.datasetProfileService.updateForm(this.datasetProfileId, data) .pipe(takeUntil(this._destroyed)) .subscribe(() => { this.router.navigate(['/dataset-profiles']); - }); + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-PROFILE-EDITOR.FEEDBACK-MESSAGES.SAVE-SUCCESS'), SnackBarNotificationLevel.Success); + },error=> this.onCallbackError(error)); } else if (this.newVersionId) { data.label = this.form.get('label').value; data.description = this.form.get('description').value; @@ -211,17 +443,21 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn .pipe(takeUntil(this._destroyed)) .subscribe(() => { this.router.navigate(['/dataset-profiles']); + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-PROFILE-EDITOR.FEEDBACK-MESSAGES.SAVE-SUCCESS'), SnackBarNotificationLevel.Success); }, error => this.onCallbackErrorNewVersion(error) ); } else { - this.form.get('status').setValue(DatasetStatus.Draft); + if(this.form.get('status').value != DatasetStatus.Finalized){// forn created and finalized instantly + this.form.get('status').setValue(DatasetStatus.Draft); + } data = this.form.value; this.datasetProfileService.createForm(data) .pipe(takeUntil(this._destroyed)) .subscribe(() => { this.router.navigate(['/dataset-profiles']); - }); + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-PROFILE-EDITOR.FEEDBACK-MESSAGES.SAVE-SUCCESS'), SnackBarNotificationLevel.Success); + }, error=> this.onCallbackError(error)); } } @@ -237,21 +473,22 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn .pipe(takeUntil(this._destroyed)) .subscribe(() => { this.router.navigate(['/dataset-profiles']); - }); + this.uiNotificationService.snackBarNotification(this.language.instant('DATASET-PROFILE-EDITOR.FEEDBACK-MESSAGES.SAVE-SUCCESS'), SnackBarNotificationLevel.Success) + },error=>this.onCallbackError(error)); } showUpdateButton() { return !this.isNew && this.dataModel.status === DatasetProfileEnum.FINALIZED; } - isStepActive(step: number) { - return this.stepper && this.stepper.selectedIndex === step; - } + // isStepActive(step: number) { + // return this.stepper && this.stepper.selectedIndex === step; + // } - onCallbackSuccess(): void { - this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); - this.router.navigate(['/master-items']); - } + // onCallbackSuccess(): void { + // this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success); + // this.router.navigate(['/master-items']); + // } onCallbackErrorNewVersion(errorResponse: HttpErrorResponse) { this.uiNotificationService.snackBarNotification(errorResponse.error.message, SnackBarNotificationLevel.Error); @@ -337,66 +574,1523 @@ export class DatasetProfileEditorComponent extends BaseComponent implements OnIn } checkFormValidation() { - if (!this.form.valid) { - this.nestedIndex = -1; - this.form.markAllAsTouched(); - this.printErrors(this.form); - this.showValidationErrorsDialog(); - this.nestedCount = []; - this.nestedIndex = 0; - this.errorMessages = []; - } + this.colorizeInvalid = true; + this.printMyErrors(this.form); + + + // if (!this.form.valid) { + // this.nestedIndex = -1; + // this.form.markAllAsTouched(); + // this.printErrors(this.form); + // this.showValidationErrorsDialog(); + // this.nestedCount = []; + // this.nestedIndex = 0; + // this.errorMessages = []; + // } } - printErrors(rootform: FormGroup) { - if (!rootform.valid) { - Object.keys(rootform.controls).forEach(key => { - const errors = rootform.get(key).errors; - if (errors !== null) { - let numbering: string = ''; - for (let j = 0; j < this.nestedCount.length; j++) { - numbering += this.nestedCount[j]; - if (j < this.nestedIndex) { - numbering += '.'; - } else { + + //BEFORE REDESIGN + // printErrors(rootform: FormGroup) { + // if (!rootform.valid) { + // Object.keys(rootform.controls).forEach(key => { + // const errors = rootform.get(key).errors; + // if (errors !== null) { + // let numbering: string = ''; + // for (let j = 0; j < this.nestedCount.length; j++) { + // numbering += this.nestedCount[j]; + // if (j < this.nestedIndex) { + // numbering += '.'; + // } else { + // break; + // } + // } + // Object.keys(errors).forEach(keyError => { + // if (typeof errors[keyError] === 'boolean') { + // this.errorMessages.push(numbering + ' ' + key + ' is ' + keyError); + // } else { + // this.errorMessages.push(numbering + ' ' + key + ': ' + keyError + ': ' + JSON.stringify(errors[keyError])); + // } + // }); + // } else { + // if (rootform.get(key) instanceof FormGroup) { + // this.printErrors(rootform.get(key)); + // } else if (rootform.get(key) instanceof FormArray) { + // this.nestedIndex++; + // this.nestedCount[this.nestedIndex] = 0; + // for (let childForm of (rootform.get(key)).controls) { + // this.nestedCount[this.nestedIndex]++; + // this.printErrors(childForm); + // } + // this.nestedCount[this.nestedIndex] = 0; + // this.nestedIndex--; + + // } + // } + // }); + // } + // } + + // private showValidationErrorsDialog(projectOnly?: boolean) { + // const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { + // disableClose: true, + // autoFocus: false, + // restoreFocus: false, + // data: { + // errorMessages: this.errorMessages, + // projectOnly: projectOnly + // }, + // }); + // } + + + + + + + // links: Link[] = []; + + // getLinks(currentLinks: Link[]) { + // this.links = currentLinks; + // } + + + datasetWizardModel: DatasetWizardEditorModel; + formGroup: FormGroup; + getPreview() { + let data = this.form.getRawValue(); + this.datasetProfileService.preview(data).subscribe(x => { + this.datasetWizardModel = new DatasetWizardEditorModel().fromModel({ + datasetProfileDefinition: x + }); + this.updateVisibilityRules(); + this.formGroup = this.datasetWizardModel.buildForm().get('datasetProfileDefinition'); + }); + //this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + + //this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; + // if (this.datasetWizardModel.status === DatasetStatus.Finalized) { + // this.formGroup.disable(); + // this.viewOnly = true; + // } + //if (this.viewOnly) { this.formGroup.disable(); } // For future use, to make Dataset edit like DMP. + // this.registerFormListeners(); + // this.dmpValueChanged(null); + // this.breadCrumbs = observableOf([ + // { + // parentComponentName: null, + // label: this.language.instant('DATASET-LISTING.ACTIONS.CREATE-NEW').toUpperCase(), + // url: '/datasets/new/' + // }]); + + + + // this.datasetWizardService.updateDatasetProfile(this.profileUpdateId) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(data => { + // this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data); + // this.formGroupRawValue = JSON.parse(JSON.stringify(this.formGroup.getRawValue())); + + // this.needsUpdate(); + // this.breadCrumbs = observableOf([ + // { + // parentComponentName: null, + // label: this.language.instant('NAV-BAR.MY-DATASET-DESCRIPTIONS'), + // url: '/datasets', + // notFoundResolver: [ + // // { + // // parentComponentName: null, + // // label: this.datasetWizardModel.dmp.grant.label, + // // url: '/grants/edit/' + this.datasetWizardModel.dmp.grant.id + // // }, + // { + // parentComponentName: null, + // label: this.datasetWizardModel.dmp.label, + // url: '/plans/edit/' + this.datasetWizardModel.dmp.id, + // }, + // ] + // }]); + // this.formGroup = this.datasetWizardModel.buildForm(); + // this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; + // if (this.datasetWizardModel.status === DatasetStatus.Finalized) { + // this.formGroup.disable(); + // this.viewOnly = true; + // } + // // if (this.viewOnly) { this.formGroup.disable(); } // For future use, to make Dataset edit like DMP. + // this.loadDatasetProfiles(); + // }); + } + + private refreshToCEntries(): ToCEntry[]{ + this.toCEntries = this.getTocEntries(); + //update selected tocentry + if(this.selectedTocEntry){ + this.selectedTocEntry = this._findTocEntryById(this.selectedTocEntry.id, this.toCEntries); + } + // this.updateOrdinals(this.toCEntries); + // this._updateNumbering(this.toCEntries); + return this.toCEntries; + } + + /** + * Updates entries ordinal form value + * based on the index they have on the tocentry array. + * Tocentries that are on the same level have distinct ordinal value + * + * @param tocentries + * + */ + private _updateOrdinals(tocentries: ToCEntry[]){ + + if(!tocentries || !tocentries.length) return; + tocentries.forEach((e,idx)=>{ + const ordinalControl = e.form.get('ordinal'); + if(ordinalControl){ + ordinalControl.setValue(idx); + ordinalControl.updateValueAndValidity(); + } + this._updateOrdinals(e.subEntries); + }); + } + + //sort tocentries based on their ordinality + private _sortToCentries(entries: ToCEntry[]){ + if(!entries || !entries.length) return; + entries.sort(this._compareOrdinals); + entries.forEach(e=>{ + this._sortToCentries(e.subEntries) + }); + } + private _compareOrdinals(a, b){ + + const aValue = a.form.get('ordinal').value as number; + const bValue = b.form.get('ordinal').value as number; + + // if(!aValue || !bValue) return 0; + return aValue - bValue; + } + private _updateNumbering(entries:ToCEntry[], parentNumbering:string){ + if(!entries || !entries.length) return; + let prefix =''; + if(parentNumbering.length){ + prefix = parentNumbering + '.'; + } + entries.forEach((entry,index)=>{ + const numbering = prefix + (index+1); + entry.numbering = numbering; + this._updateNumbering(entry.subEntries, numbering); + }) + } + + toCEntries:ToCEntry[]; + + getTocEntries(): ToCEntry[] { + if (this.form == null) { return []; } + const result: ToCEntry[] = []; + + //build parent pages + (this.form.get('pages') as FormArray).controls.forEach((pageElement, i) => { + result.push({ + id: pageElement.get('id').value, + label: pageElement.get('title').value, + type: ToCEntryType.Page, + form: pageElement, + numbering: (i + 1).toString(), + subEntriesType: ToCEntryType.Section + } as ToCEntry) + }); + + // build first level sections + (this.form.get('sections') as FormArray).controls.forEach((sectionElement, i) => { + const currentSectionPageId = sectionElement.get('page').value; + const pageToAdd = result.filter(x => x.id == currentSectionPageId)[0]; + if (pageToAdd.subEntries == null) pageToAdd.subEntries = []; + + const item = { + id: sectionElement.get('id').value, + label: sectionElement.get('title').value, + type: ToCEntryType.Section, + form: sectionElement, + numbering: pageToAdd.numbering + '.' + (pageToAdd.subEntries.length +1) + } as ToCEntry; + const sectionItems = this.populateSections(sectionElement.get('sections') as FormArray, item.numbering); + const fieldSetItems = this.populateFieldSets(sectionElement.get('fieldSets') as FormArray, item.numbering); + if (sectionItems != null) { + item.subEntries = sectionItems; + item.subEntriesType = ToCEntryType.Section; + } + if (fieldSetItems != null) { + if (item.subEntries == null) { + item.subEntries = fieldSetItems; + } else { + item.subEntries.push(...fieldSetItems); + } + item.subEntriesType = ToCEntryType.FieldSet; + + } + pageToAdd.subEntries.push(item); + + }); + this._sortToCentries(result);//ordeby ordinal + this._updateNumbering(result, '');//update nubering if needed + return result; + } + + private populateSections(sections: FormArray, existingNumbering: string): ToCEntry[] { + if (sections == null || sections.controls == null || sections.controls.length == 0) { return null; } + + const result: ToCEntry[] = []; + sections.controls.forEach((sectionElement, i) => { + + const item = { + id: sectionElement.get('id').value, + label: sectionElement.get('title').value, + type: ToCEntryType.Section, + form: sectionElement, + numbering: existingNumbering + '.' + (i + 1) + } as ToCEntry; + const sectionItems = this.populateSections(sectionElement.get('sections') as FormArray, item.numbering); + const fieldSetItems = this.populateFieldSets(sectionElement.get('fieldSets') as FormArray, item.numbering); + if (sectionItems != null) { + item.subEntries = sectionItems; + item.subEntriesType = ToCEntryType.Section; + } + if (fieldSetItems != null) { + if (item.subEntries == null) { + item.subEntries = fieldSetItems; + } else { + item.subEntries.push(...fieldSetItems); + } + item.subEntriesType = ToCEntryType.FieldSet; + } + result.push(item); + }); + + return result; + } + + private populateFieldSets(fieldSets: FormArray, existingNumbering: string): ToCEntry[] { + if (fieldSets == null || fieldSets.controls == null || fieldSets.controls.length == 0) { return null; } + + const result: ToCEntry[] = []; + fieldSets.controls.forEach((fieldSetElement, i) => { + + result.push({ + id: fieldSetElement.get('id').value, + label: fieldSetElement.get('title').value, + type: ToCEntryType.FieldSet, + //subEntries: this.populateSections((fieldSetElement.get('fieldSets') as FormArray), existingNumbering + '.' + i), + form: fieldSetElement, + numbering: existingNumbering + '.' + (i + 1) + } as ToCEntry) + + }); + + return result; + } + + + + private _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry{ + if(!tocentries || !tocentries.length){ + return null; + } + + let tocEntryFound = tocentries.find(entry=>entry.id === id); + + if(tocEntryFound){ + return tocEntryFound; + } + + for(let entry of tocentries){ + const result = this._findTocEntryById(id, entry.subEntries); + if(result){ + tocEntryFound = result; + break; + } + } + + return tocEntryFound? tocEntryFound: null; + } + addNewEntry(tce: NewEntryType) { + + const parent = tce.parent; + + //define entry type + switch (tce.childType) { + case ToCEntryType.Page: + const pagesArray = (this.form.get('pages') as FormArray); + + const page: PageEditorModel = new PageEditorModel(pagesArray.length); + const pageForm = page.buildForm(); + // this.dataModel.pages.push(page); + + pagesArray.push(pageForm); + // this.form.updateValueAndValidity(); + this.refreshToCEntries(); + this.selectedTocEntry = this._findTocEntryById(pageForm.get('id').value, this.toCEntries); + + break; + case ToCEntryType.Section: + + const section: SectionEditorModel = new SectionEditorModel(); + section.id = Guid.create().toString(); + let sectionsArray:FormArray; + + if (parent.type === ToCEntryType.Page) {//FIRST LEVEL SECTION + sectionsArray = this.form.get('sections') as FormArray; + + section.page = parent.id; + + try{ + const max = sectionsArray.controls.filter(control=>control.get('page').value === parent.id) + .map(control=>control.get('ordinal').value) + .reduce((a,b)=>Math.max(a,b)); + + section.ordinal = max + 1; + }catch{ + section.ordinal = sectionsArray.length; + + } + sectionsArray.push(section.buildForm()); + // this.form.updateValueAndValidity(); + + } else if( parent.type == ToCEntryType.Section) { //SUBSECTION OF SECTION + sectionsArray = parent.form.get('sections') as FormArray; + + //adding page parent MAYBE NOT NEEDED + section.page = parent.form.get('page').value; + try{ + const maxOrdinal = sectionsArray.controls.map(control=>control.get('ordinal').value).reduce((a,b)=>Math.max(a, b)); + section.ordinal = maxOrdinal+1; + }catch{ + section.ordinal = sectionsArray.length; + } + + sectionsArray.push(section.buildForm()); + // (child.form.parent as FormArray).push(section.buildForm()); + + }else{ + console.error('Section can only be child of a page or another section'); + } + + + const sectionAdded = sectionsArray.at(sectionsArray.length -1) as FormGroup; + // sectionAdded.setValidators(this.customEditorValidators.sectionHasAtLeastOneChildOf('fieldSets','sections')); + // sectionAdded.updateValueAndValidity(); + + + this.refreshToCEntries(); + this.selectedTocEntry = this._findTocEntryById(sectionAdded.get('id').value, this.toCEntries); + + break; + case ToCEntryType.FieldSet: + + //create one field form fieldset + const field: FieldEditorModel = new FieldEditorModel(); + field.id = Guid.create().toString(); + field.ordinal = 0;//first filed in the fields list + const fieldForm = field.buildForm(); + // fieldForm.setValidators(this.customFieldValidator()); + // fieldForm.get('viewStyle').get('renderStyle').setValidators(Validators.required); + + // fieldSet.fields.push(field); + // field.ordinal = fieldSet.fields.length-1; + + const fieldSetsArray = parent.form.get('fieldSets') as FormArray + + //give fieldset id and ordinal + const fieldSet: FieldSetEditorModel = new FieldSetEditorModel(); + const fieldSetId = Guid.create().toString(); + fieldSet.id = fieldSetId; + + try{ + const maxOrdinal = fieldSetsArray.controls.map(control=>control.get('ordinal').value).reduce((a,b)=>Math.max(a, b)); + fieldSet.ordinal = maxOrdinal+1; + }catch{ + fieldSet.ordinal = fieldSetsArray.length; + } + const fieldsetForm = fieldSet.buildForm(); + + + + (fieldsetForm.get('fields') as FormArray).push(fieldForm); + fieldSetsArray.push(fieldsetForm); + + this.refreshToCEntries(); + this.selectedTocEntry = this._findTocEntryById(fieldSetId, this.toCEntries); + // fieldForm.updateValueAndValidity(); + + break; + + default: + break; + } + + this.form.updateValueAndValidity(); + } + + + onRemoveEntry(tce: ToCEntry){ + + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + restoreFocus: false, + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + isDeleteConfirmation: true + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + this._deleteEntry(tce); + } + }); + + } + + + private _deleteEntry(tce: ToCEntry) { + //define entry type + switch (tce.type) { + case ToCEntryType.Page: + const pages = this.form.get('pages') as FormArray; + + let pageIndex = -1; + //get the index + for (let i = 0; i < pages.length; i++) { + let page = pages.at(i) as FormGroup; + + if (page.controls.id.value === tce.id) { + pageIndex = i; + break; + } + } + + if (pageIndex >= 0) { + //remove page + this._updateSelectedItem(tce); + pages.removeAt(pageIndex); + //clean up sections of removed page + + const sections = (this.form.get('sections') as FormArray); + + const sectionsIndexToBeRemoved = []; + + sections.controls.forEach((section,idx)=>{ + if(section.get('page').value === tce.id){ + sectionsIndexToBeRemoved.push(idx); + } + }); + + if(sectionsIndexToBeRemoved.length){ + sectionsIndexToBeRemoved.reverse().forEach(index=>{ + sections.removeAt(index); + }); + } + + //update page ordinals + for(let i=0; i= 0) { //section found + + const sections = (this.form.get('sections') as FormArray); + + //remove section + this._updateSelectedItem(tce); + sections.removeAt(index); + + //update ordinal + for(let i=0; i< sections.length; i++){ + sections.at(i).get('ordinal').patchValue(i); + } + } else {//NOT FOUND IN FIRST LEVEL CASE + + //LOOK FOR SUBSECTION CASE + let parentFormArray = tce.form.parent as FormArray; + + for (let i = 0; i < parentFormArray.length; i++) { + let section = parentFormArray.at(i); + + if (section.get('id').value == tce.id) { + index = i; break; } } - Object.keys(errors).forEach(keyError => { - if (typeof errors[keyError] === 'boolean') { - this.errorMessages.push(numbering + ' ' + key + ' is ' + keyError); - } else { - this.errorMessages.push(numbering + ' ' + key + ': ' + keyError + ': ' + errors[keyError]); - } - }); - } else { - if (rootform.get(key) instanceof FormGroup) { - this.printErrors(rootform.get(key)); - } else if (rootform.get(key) instanceof FormArray) { - this.nestedIndex++; - this.nestedCount[this.nestedIndex] = 0; - for (let childForm of (rootform.get(key)).controls) { - this.nestedCount[this.nestedIndex]++; - this.printErrors(childForm); - } - this.nestedCount[this.nestedIndex] = 0; - this.nestedIndex--; + if (index >= 0) { + this._updateSelectedItem(tce); + parentFormArray.removeAt(index); + //update odrinal + + for(let i=0; i=0){//fieldset found + this._updateSelectedItem(tce); + parentFormArray.removeAt(idx); + + //patching order + for(let i=0; i{ + if(section.get('id').value === tce.id){ + isFirstLevel = true; + } + }); + + let parentId = null; + if(isFirstLevel){ + parentId = tce.form.get('page').value; + }else{ + parentId = tce.form.parent.parent.get('id').value + } + + // const parentId = tce.form.parent.parent.get('id').value; + if(parentId){ + const tocentries = this.getTocEntries(); + const parent = this._findTocEntryById(parentId, tocentries); + + if(parent){ + this.selectedTocEntry = parent; + }else{ + this.selectedTocEntry = null; + } + }else{ + this.selectedTocEntry = null; + } + } + } + } + } + + tocEntryIsChildOf(testingChild: ToCEntry,parent: ToCEntry): boolean{ + + if(!testingChild || !parent) return false; + + if(testingChild.id == parent.id){return true;} + + if(parent.subEntries){ + let childFound:boolean = false; + + parent.subEntries.forEach(subEntry=>{ + if(this.tocEntryIsChildOf(testingChild, subEntry)){ + childFound = true; + return true; + } + }) + + return childFound; + } + return false; + } + + selectedTocEntry: ToCEntry + displayItem(entry: ToCEntry): void { + this.selectedTocEntry = entry; + } + + get numOfPages(){ + return (this.form.get('pages')).length; + } + + // getSectionIndex(): number{ + // // if(this.sectionIdPreviewed == null) return; + // const valuesArray = this.form.get('sections').value; + // let currentVal = this.sectionIdPreviewed; + // let indexArray:string[] = valuesArray.map(element=> element.page); + // let index = indexArray.indexOf(currentVal); + // console.log(index); + // return index ? index :-1; + // } + + // getCurrentEditorModel(): SectionEditorModel{ + // let currentEditor = this.dataModel.sections.filter(section=> section.page == this.sectionIdPreviewed)[0]; + // return currentEditor; + + // } + + + // subForm(){ + // const valuesArray = this.form.get('sections').value; + // let currentVal = this.sectionIdPreviewed; + // let indexArray:string[] = valuesArray.map(element=> element.page); + // let index = indexArray.indexOf(currentVal); + // let subForm = (this.form.get('sections') as FormArray).controls[index]; + // console.log(subForm); + // return subForm; + // } + + getFieldTile(formGroup: FormGroup, index: number) { + if (formGroup.get('title') && formGroup.get('title').value && formGroup.get('title').value.length > 0) { return formGroup.get('title').value; } + return "Field " + (index + 1); + } + + deleteFieldSet(formArray: FormArray, index: number) { + formArray.removeAt(index); + } + + + + printForm(){ + // console.log(this.form.value); + console.log(this.form); + } + + get barPercentage(){ + if(!this.stepper || !this.steps){ + return 0; + } + const selectedIndex = this.stepper.selectedIndex + 1; + return (selectedIndex / this.stepper.steps.length) * 100; + } + + + get progressStyle(){ + // return {'transform': 'translateX('+this.barPercentage+'%) skewX(-25deg)'} + const diff = 3; + + return { + 'clip-path': `polygon(0 0, ${Math.round(this.barPercentage + diff)}% 0, ${Math.round(this.barPercentage)}% 100%, 0 100%)` + } + } + + steps:QueryList; + // get steps(){ + // if(!this.stepper){ + // return []; + // } + + // return this.stepper.steps; + // } + + + // generatePreviewForm(){ + + + // const model = new DatasetDescriptionFormEditorModel(); + + // const toCentries = this.getTocEntries(); + + + // //first level is always pages + // model.pages = toCentries.map((entry,idx)=>{ + // if( !(entry.type == ToCEntryType.Page)){ + // return null; + // } + // const pageModel = new DatasetDescriptionPageEditorModel(); + + // pageModel.ordinal = entry.form.get('ordinal').value; + // pageModel.title = entry.label; + + // if(entry.subEntries){ + // pageModel.sections = entry.subEntries.map(section=>{ + // const sectionModel = new DatasetDescriptionSectionEditorModel(); + + // sectionModel.id = section.id; + // sectionModel.ordinal = section.form.get('ordinal').value; + // sectionModel.description = section.form.get('description').value; + // sectionModel.page = entry.form.get('ordinal').value; + // sectionModel.title = section.label; + // sectionModel.numbering = (idx+1).toString(); + + + // if(section.subEntriesType == ToCEntryType.Section){ + // sectionModel.sections = this._buildSectionsRecursively(section.subEntries, sectionModel.numbering); + // }else{ + // sectionModel.compositeFields = this._buildFormFields(section.subEntries, sectionModel.numbering) + // } + + // return sectionModel; + // }); + // }; + // return pageModel; + + // }); + + + // //populate rules + // const rules:Rule[] =[]; + // const fieldSets = this._getFieldSets(toCentries); + + // fieldSets.forEach(fs=>{ + // const fields = fs.form.get('fields') as FormArray; + // if(fields){ + // fields.controls.forEach(field=>{ + // const rulesArray = field.get('visible').get('rules').value; + // if(rulesArray){ + // rulesArray.forEach(ruleElement => { + // const rule: Rule = new Rule(); + // rule.targetField = ruleElement.target; + // rule.sourceField = field.get('id').value; + // rule.requiredValue = ruleElement.value; + // rules.push(rule); + // }); + // } + // }); + // } + // }); + + // model.rules = rules; + // this.visibilityRules = rules; + + // this.previewForm = model.buildForm(); + // } + + + updateVisibilityRules(){ + const rules:Rule[] =[]; + const fieldSets = this._getFieldSets(this.getTocEntries()); + + fieldSets.forEach(fs=>{ + const fields = fs.form.get('fields') as FormArray; + if(fields){ + fields.controls.forEach(field=>{ + const rulesArray = field.get('visible').get('rules').value; + if(rulesArray){ + rulesArray.forEach(ruleElement => { + const rule: Rule = new Rule(); + rule.targetField = ruleElement.target; + rule.sourceField = field.get('id').value; + rule.requiredValue = ruleElement.value; + rules.push(rule); + }); + } + }); + } + }); + + this.visibilityRules = rules; + + } + + visibilityRules:Rule[]; + private _buildSectionsRecursively( tocentries: ToCEntry[], parentNumbering:string): DatasetDescriptionSectionEditorModel[]{ + + + if(!tocentries) return null; + + + const result: Array = []; + + tocentries.forEach((tocentry, idx)=>{ + + const sectionModel = new DatasetDescriptionSectionEditorModel(); + sectionModel.id = tocentry.id; + sectionModel.ordinal = tocentry.form.get('ordinal').value; + sectionModel.description = tocentry.form.get('description').value; + // sectionModel.page = entry.form.get('ordinal').value; + sectionModel.title = tocentry.label; + // sectionModel.numbering = tocentry.numbering; + sectionModel.numbering = parentNumbering+"."+(idx+1);; + + if(tocentry.subEntriesType == ToCEntryType.Section){ + sectionModel.sections = this._buildSectionsRecursively(tocentry.subEntries, sectionModel.numbering); + + }else{ + sectionModel.compositeFields = this._buildFormFields(tocentry.subEntries, sectionModel.numbering); + } + + result.push(sectionModel); + }) + + return result; + } + + private _buildFormFields(tocentries: ToCEntry[], parentNumbering: string):DatasetDescriptionCompositeFieldEditorModel[]{ + if(!tocentries) return null; + + const fieldsets:DatasetDescriptionCompositeFieldEditorModel[] = []; + + tocentries.forEach((fs, idx)=>{ + + const fieldset = new DatasetDescriptionCompositeFieldEditorModel(); + + fieldset.description = fs.form.get('description').value; + fieldset.extendedDescription = fs.form.get('extendedDescription').value; + fieldset.id = fs.form.get('id').value; + fieldset.multiplicity = fs.form.get('multiplicity').value; + fieldset.additionalInformation = fs.form.get('additionalInformation').value; + fieldset.ordinal = fs.form.get('ordinal').value; + // fieldset.numbering = fs.numbering; + fieldset.numbering = parentNumbering+"."+(idx+1); + fieldset.hasCommentField = fs.form.get('hasCommentField').value; + fieldset.title = fs.label; + // fieldset.fields = (fs.form.get('fields') as FormArray).getRawValue(); + fieldset.fields = (fs.form.get('fields') as FormArray).controls.map(field=>{ + + const fieldModel = new DatasetDescriptionFieldEditorModel(); + + fieldModel.data = (field.get('data') as FormGroup).getRawValue(); + fieldModel.id = field.get('id').value; + fieldModel.viewStyle = (field.get('viewStyle') as FormGroup).getRawValue(); + // fieldModel.defaultValue = (field.get('defaultValue') as FormGroup).getRawValue(); + fieldModel.value = (field.get('defaultValue') as FormGroup).get('value').value; + fieldModel.defaultValue = fieldModel.value; + fieldModel.page = field.get('page').value; + fieldModel.validations = field.get('validations').value; + + return fieldModel; + }); + + + + fieldsets.push(fieldset); + }); + + return fieldsets; + } + + private _getFieldSets(tocentries: ToCEntry[]):ToCEntry[]{ + + const fieldsets:ToCEntry[] = []; + + if(!tocentries) return fieldsets; + + tocentries.forEach(entry=>{ + if(entry.type == ToCEntryType.FieldSet){ + fieldsets.push(entry); + }else{ + fieldsets.push(...this._getFieldSets(entry.subEntries)); + } + }); + + return fieldsets; + } + + + + // get basicInfo(){ + + // const label = this.form.get('label'); + // const description = this.form.get('description'); + // const language = this.form.get('language'); + + + + // const fg = new FormGroup({ + // label: label, + // description: description, + // language: language + // }) + + // return fg; + // } + + + + + + onMatStepperSelectionChange(event: StepperSelectionEvent){ + + if(event.selectedIndex === (this.steps.length -1)){//preview selected + // this.generatePreviewForm();//TODO LAZY LOADING IN THE TEMPLATE + this.getPreview(); + }else{ + // this.previewForm = null; + this.formGroup = null; + } + this.form.markAsUntouched(); + + } + + // previewForm:FormGroup; + onDataNeedsRefresh(params?){ + + const tocentries = this.refreshToCEntries(); + + if(params && params.draggedItemId){ + if(params.draggedItemId){ + this.displayItem(this._findTocEntryById(params.draggedItemId, tocentries)); + } + } + } + + cloneFieldSet(fieldset: FormGroup){ + const values = fieldset.getRawValue(); + const parentArray = fieldset.parent as FormArray; + + values.id = Guid.create().toString(); + values.ordinal = parentArray.length; + + values.fields.forEach(element => { + element.id = Guid.create().toString() + }); + + + const clonedModel = new FieldSetEditorModel().fromModel(values); + const clonedForm = clonedModel.buildForm(); + parentArray.push(clonedForm); + + //update tocentries and make selected tocentry the cloedn + let entries = this.refreshToCEntries(); + + const entryfound = this._findTocEntryById(clonedForm.get('id').value, entries); + if(entryfound){ + this.selectedTocEntry = entryfound; + } + + // //create one field form fieldset + // const field: FieldEditorModel = new FieldEditorModel(); //to ask + // field.id = Guid.create().toString(); + // field.ordinal = 0;//first filed in the fields list + // fieldSet.fields.push(field); + // // field.ordinal = fieldSet.fields.length-1; + + // //give fieldset id and ordinal + // fieldSet.id = Guid.create().toString(); + // fieldSet.ordinal = (parent.form.get('fieldSets') as FormArray).length; + + // (parent.form.get('fieldSets')).push(fieldSet.buildForm()); + + // // const parentArray = parent.form.get('fieldSets') as FormArray; + // const addedFieldSet = parentArray.at(parentArray.length - 1); + + + + + } + + isStepCompleted(stepIndex: number){ + + let stepCompleted = false; + this.steps.forEach((step,index)=>{ + if(stepIndex === index){ + stepCompleted = step.completed; + } + }); + + return stepCompleted; + } + + isStepUnlocked(stepIndex: number): boolean{ + if(stepIndex === 0) return true; + if(stepIndex <0 ) return false; + //if previous step is valid then unlock + let stepUnlocked: boolean = false; + + if(!this.isStepUnlocked(stepIndex -1)) return false; + + this.steps.forEach((step,index)=>{ + + if(index+1 == stepIndex){//previous step + + if(step.completed){ + stepUnlocked = true; + } + } + }); + + return stepUnlocked; + } + + validateStep(selectedIndex){ + + if(selectedIndex === 1){//form description + if(this.form.invalid){ + this.checkFormValidation(); + } + } + + + // if(step.hasError){ + // this.printMyErrors(this.form); + // } + } + + // getFormValidationErrors() { + // Object.keys(this.form.controls).forEach(key => { + + // const controlErrors: ValidationErrors = this.form.get(key).errors; + // if (controlErrors != null) { + // Object.keys(controlErrors).forEach(keyError => { + // console.log('Key control: ' + key + ', keyError: ' + keyError + ', err value: ', controlErrors[keyError]); + // }); + // } + // }); + + // if(this.form.invalid){ + // console.log('this form is invalid!'); + // console.log(this.form.errors); + // } + // } + + + + private _buildErrorMessage(errors, numbering, key):string[]{ + const errmess: string[] = []; + + Object.keys(errors).forEach(keyError => { + + switch(keyError){ + case EditorCustomValidatorsEnum.atLeastOneSectionInPage: + errmess.push( this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.PAGE-MUST-HAVE-SECTION')); + break; + case EditorCustomValidatorsEnum.emptyArray: + errmess.push(numbering+this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.NEEDS-MORE-INFORMATION')) + break; + case EditorCustomValidatorsEnum.sectionMustHaveOneChild: + errmess.push(numbering+this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.MUST-HAVE-SECTION-OR-FIELDSET')) + break; + default: + if (typeof errors[keyError] === 'boolean') { + errmess.push(numbering + ' ' + key + ' is ' + keyError); + } else { + errmess.push(numbering + ' ' + key + ': ' + keyError + ': ' + JSON.stringify(errors[keyError])); + } + + } + + }); + + return errmess; + } + + + printMyErrors(form: AbstractControl){ + // this._printToCentriesErrrors(this.toCEntries); + const result = this._getErrors(form); + + // console.log('got errors '); + // console.log(result); + + if(result && form.invalid){ + const errmess:string[] = []; + if(result.length){ + form.markAllAsTouched(); + + let indexes:number[] = []; + ///search in pages,sections and fieldsets for the id + result.forEach((err,i)=>{ + const entry = this._findTocEntryById(err.id, this.toCEntries); + if(entry){ + + // errmess.push(`Error on ${entry.numbering} ${entry.label} . ${err.key}`); + errmess.push(...this._buildErrorMessage(err.errors, entry.numbering, err.key)); + indexes.push(i); + } + }); + indexes.reverse().forEach(index=>{ + result.splice(index,1); + }); + + indexes = []; + //searching in fields + const fieldsets = this._getAllFieldSets(this.toCEntries); + result.forEach((err,i)=>{ + fieldsets.filter(fs=>{ + let fieldFound = false; + (fs.form.get('fields') as FormArray).controls.forEach(field=>{ + if(field.get('id').value === err.id){ + fieldFound = true; + indexes.push(i); + } + }); + return fieldFound; + }) + //printing fieldsets that the field missing + .forEach(fs=>{ + // errmess.push(`Missing input in ${fs.numbering} ${fs.label} . ${err.key}`); + errmess.push(...this._buildErrorMessage(err.errors, fs.numbering, err.key)); + }); + }); + + indexes.reverse().forEach(index=>{ + result.splice(index,1); + }); + + result.forEach(err=>{ + // console.log(err); + if(err.key){ + errmess.push(`${this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.MISSING')} ${err.key}` ); + }else{ + + errmess.push(this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.FORM-VALIDATION.ERROR-MESSAGES.PROVIDE-PAGE-AND-SECTION')); + } + // errmess.push(...this._buildErrorMessage(err.errors,"", err.key) ); + + }) + } + + const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { + disableClose: true, + autoFocus: false, + restoreFocus: false, + data: { + errorMessages: errmess, + projectOnly: false + }, }); } } - private showValidationErrorsDialog(projectOnly?: boolean) { - const dialogRef = this.dialog.open(FormValidationErrorsDialogComponent, { - disableClose: true, - autoFocus: false, - restoreFocus: false, - data: { - errorMessages: this.errorMessages, - projectOnly: projectOnly + /** + * Get all filedsets in a tocentry array; + * @param entries Tocentries to search in + * @returns The tocentries that are Fieldsets provided in the entries + */ + private _getAllFieldSets(entries: ToCEntry[]):ToCEntry[]{ + + const fieldsets:ToCEntry[] = []; + if(!entries || !entries.length) return fieldsets; + + + entries.forEach(e=>{ + if(e.type === ToCEntryType.FieldSet){ + fieldsets.push(e); + }else{ + fieldsets.push(...this._getAllFieldSets(e.subEntries)); + } + }); + return fieldsets; + } + + + private _getErrors(aControl: AbstractControl):InvalidControl[]{ + + + if(aControl.valid) return; + + let controlType = 'control'; + + if(aControl instanceof FormGroup) controlType="fg" + if(aControl instanceof FormControl) controlType="fc"; + if(aControl instanceof FormArray) controlType="fa"; + + + const invalidControls:InvalidControl[] = []; + //invalid + switch (controlType){ + case 'fg': + + const controls = (aControl as FormGroup).controls; + const keys = Object.keys(controls); + keys.forEach(key=>{ + const control = controls[key]; + if(!control.invalid) return; //// !!!!! Important to be !invalid. (In case the template is finalized) + + if(control instanceof FormControl){ + const ctrl = control as FormControl; + + invalidControls.push({ + errors:ctrl.errors, + id: ctrl.get('id')? ctrl.get('id').value: null, + invalidSubControls: [], + key: key + }); + + }else{ + // if(aControl.errors){ + // invalidControls.push({ + // id: aControl.get('id')? aControl.get('id').value: null, + // errors:aControl.errors, + // key: aControl.get('title')? aControl.get('title').value: 'unspecified', + // invalidSubControls:[]//TODO TO CHECK + // }); + // } + + + //THE ONE WE REMOVED + // if(control.errors){ + // // invalidControls.push({ + // // id: aControl.get('id')? aControl.get('id').value: null, + // // errors:aControl.errors, + // // key: aControl.get('title')? aControl.get('title').value: 'unspecified', + // // invalidSubControls:[]//TODO TO CHECK + // // }); + + // invalidControls.push({ + // errors:control.errors, + // id: control.get('id')? control.get('id').value: null, + // invalidSubControls: [], + // key: key + // }); + // } + invalidControls.push(...this._getErrors(control)) ; + + } + }); + + /**In case there is an error in a formgroup then the validator probably is custom */ + if(aControl.errors){ + invalidControls.push({ + errors:aControl.errors, + id: aControl.get('id')? aControl.get('id').value: null, + invalidSubControls: [], + key: aControl.get('title')?aControl.get('title').value: null + }); + } + + break; + case 'fa': + // const fa = (aControl as FormArray); + const ctrls = (aControl as FormArray).controls; + const keys_ = Object.keys(ctrls); + keys_.forEach(key=>{ + const control = ctrls[key]; + if(control.valid) return; + + + if(control instanceof FormControl){ //for completion purposes. should never run this case + const ctrl = control as FormControl; + invalidControls.push({ + errors:ctrl.errors, + id: ctrl.get('id')? ctrl.get('id').value: null, + invalidSubControls: [], + key: key + }); + + }else{ + invalidControls.push(... this._getErrors(control)); + } + }); + + break; + } + + invalidControls.forEach(e=>{ + if(!e.id){ + e.id = aControl.get('id')? aControl.get('id').value : null; + } + }) + return invalidControls; + + } + userFormDisabled:boolean = false; + addUser(email:MatInput){ + + // email.focus(); + if(this.userFormDisabled) return; + + // console.log('user add'); + if(this.inputUserState === 'triggered'){ + // this.checkAndAdd(email); + + of(email.value) + .pipe(tap(_=> {this.userFormDisabled = true; email.focus()})) + .pipe(mergeMap(email=>this.userService.getFromEmail(email))) + .pipe(takeUntil(this._destroyed)) + .subscribe((result) => { + this.userChipList.push(result); + this.form.patchValue({'users': this.userChipList}); + + email.value = ''; + this.userFormDisabled = false; + // email.focus(); + // this.inputUserState = 'triggered'; + + }, + error=>{ + // console.log(error.message); + this.uiNotificationService.snackBarNotification(error.message, SnackBarNotificationLevel.Error); + this.userFormDisabled = false; + // this.inputUserState = 'triggered';//when it loses focus(when disabled) it becomes untriggered + // email.focus(); + }); + // this.inputUserState = 'untriggered'; + }else{ + this.inputUserState = 'triggered'; + email.focus(); + + } + } + onUserButtonFocus(){ + this._inputUserButton$.next(true); + } + onUserButtonBlur(){ + this._inputUserButton$.next(false); + } + onUserFieldFocus(){ + this._inputUserField$.next(true); + } + onUserFieldBlur(){ + this._inputUserField$.next(false); + } + + + + //Temporary patch + /** + * Append custom validators to fields. Some validators are applied on template. In case they are never rendereed, + * he form might be valid when it shouldnt. + * @param + */ + private _initializeFormValidity(tocentries: ToCEntry[]) { + const fieldsets = this._getAllFieldSets(tocentries); + + try{ + fieldsets.forEach(fs=>{ + fs.form.get('title').setValidators(Validators.required); + const fieldsF = fs.form.get('fields') as FormArray; + if(fieldsF){ + fieldsF.controls.forEach(field=>{ + const renderStyleValue = field.get('viewStyle').get('renderStyle').value; + + if(renderStyleValue === DatasetProfileFieldViewStyle.CheckBox){ + field.get('defaultValue').get('value').setValidators(Validators.required); + }else if(renderStyleValue === 'combobox'){ + + const comboType = field.get('data').get('type').value; + if(comboType === DatasetProfileComboBoxType.Autocomplete){//As 'Other' in UI + field.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('autoCompleteSingleDataList')); + }else if(comboType === DatasetProfileComboBoxType.WordList){ + field.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); + } + + }else if(renderStyleValue === DatasetProfileFieldViewStyle.RadioBox){ + field.get('data').setValidators(EditorCustomValidators.atLeastOneElementListValidator('options')); + } + }); + } + }); + }catch(e){ + console.error('Error initializing validators.'); + console.error(e); + } + + } + + checkAndAdd(ev: any) { + this.userService.getFromEmail(ev).pipe(takeUntil(this._destroyed)).subscribe((result) => { + this.userChipList.push(result); + this.form.patchValue({'users': this.userChipList}); }); } + + removeUser(user: any) { + this.userChipList.splice(this.userChipList.indexOf(user), 1); + this.form.patchValue({'users': this.userChipList}); + } + + + verifyAndRemoveUser(user:any){ + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + restoreFocus: false, + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + isDeleteConfirmation: true + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(approve => { + if (approve) { + this.removeUser(user); + } + }); + } + + scrollOnTop(){ + try{ + const topPage = document.getElementById('main-content'); + topPage.scrollIntoView({behavior:'smooth'}); + }catch{ + console.log('coulnd not scroll'); + } + } + + updateAndFinalize(){ + if(this.form.get('status').value == DatasetProfileEnum.FINALIZED ){ + //UPDATE FORM + if(this.newVersionId){ + this.onSubmit(); + }else{ + this.updateFinalized(); + } + }else{ + //finalize + this.finalize(); + } + + } + discardChanges(){ + this.router.navigate([ + 'dataset-profiles' + ]); + } + +} + +interface InvalidControl{ + key: string, + errors: any, + id: string, + invalidSubControls: InvalidControl[] } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.html index c303267ef..45a016601 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.html @@ -1,23 +1,23 @@ -
- -
- - - - - - {{'DATASET-PROFILE-STATUS.NONE' | translate}} - {{'DATASET-PROFILE-STATUS.DRAFT' | translate}} - {{'DATASET-PROFILE-STATUS.FINALIZED' | translate}} - - -
-
- - -
+
+
+
+ {{'CRITERIA.USERS.SHOW' | translate}}:
- + + + + {{'DATASET-PROFILE-STATUS.NONE' | translate}} + {{'DATASET-PROFILE-STATUS.DRAFT' | translate}} + {{'DATASET-PROFILE-STATUS.FINALIZED' | translate}} + + + +
+
+ + + search + +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.scss index e69de29bb..9b64c055a 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.scss +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.scss @@ -0,0 +1,17 @@ +.mat-form-field{ + display: inline-block !important; +} + +:host ::ng-deep .status-form-field .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} + +:host ::ng-deep .search-form-field .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} + +:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { + padding: 0.3rem 0rem 0.6rem 0rem !important; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.ts index f0b1b3e17..7cf625f1f 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria'; import { DatasetProfileService } from '@app/core/services/dataset-profile/dataset-profile.service'; -import { DialodConfirmationUploadDatasetProfiles } from '@app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component'; +import { DialogConfirmationUploadDatasetProfiles } from '@app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component'; import { BaseCriteriaComponent } from '@app/ui/misc/criteria/base-criteria.component'; import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; import { TranslateService } from '@ngx-translate/core'; @@ -11,7 +11,7 @@ import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-dataset-profile-criteria-component', templateUrl: './dataset-profile.component.html', - styleUrls: ['./dataset-profile.component.scss'], + styleUrls: ['./dataset-profile.component.scss'] }) export class DatasetProfileCriteriaComponent extends BaseCriteriaComponent implements OnInit { @@ -47,26 +47,4 @@ export class DatasetProfileCriteriaComponent extends BaseCriteriaComponent imple } } - openDialog(): void { - const dialogRef = this.dialog.open(DialodConfirmationUploadDatasetProfiles, { - width: '500px', - restoreFocus: false, - data: { - message: this.language.instant('DATASET-WIZARD.UPLOAD.UPLOAD-XML-FILE-TITLE'), - confirmButton: this.language.instant('DATASET-WIZARD.UPLOAD.UPLOAD-XML'), - cancelButton: this.language.instant('DATASET-WIZARD.UPLOAD.UPLOAD-XML-FILE-CANCEL'), - name: "", - file: FileList, - sucsess: false - } - }); - dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(data => { - if (data && data.sucsess && data.name != null && data.file != null) { - this.datasetService.uploadFile(data.file, data.name) - .pipe(takeUntil(this._destroyed)) - .subscribe(); - } - }); - } - } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.html index dec3649ac..d071e4cb2 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.html @@ -4,22 +4,41 @@

{{ data.message }}

- - + close
+ +
+
+ + + {{ selectedFileName }} + + +
+
+ +
+
+ + +
+
+
- +
- +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.scss index 226db6ce8..63c42f81e 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.scss +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.scss @@ -1,3 +1,102 @@ .hidden { display: none; +} + + +.cancel-btn { + background: #ffffff 0% 0% no-repeat padding-box; + border: 1px solid #b5b5b5; + border-radius: 30px; + width: 101px; + height: 43px; + color: #212121; + font-weight: 500; +} + +.next-btn { + background: #ffffff 0% 0% no-repeat padding-box; + border: 1px solid #129d99; + border-radius: 30px; + opacity: 1; + width: 101px; + height: 43px; + color: #129d99; + font-weight: 500; +} + +.next-btn[disabled] { + width: 100px; + height: 43px; + background: #ffffff 0% 0% no-repeat padding-box; + border: 1px solid #b5b5b5; + border-radius: 30px; + opacity: 1; +} + +.next-btn:not([disabled]):hover { + background-color: #129d99; + color: #ffffff; +} + +//ngx dropzone +.drop-file { + background-color: #fafafa; + border: 1px dashed #d1d1d1; + border-radius: 4px; + max-width: 480px; + height: 98px; + margin-top: 0.5rem; +} + +.file-preview { + height: auto !important; + width: auto !important; + max-width: 500px !important; + min-height: 1rem !important; + + background-color: #e0e0e0 !important; + background-image: none !important; + color: rgba(0, 0, 0, 0.87) !important; + font-weight: 500 !important; + border-radius: 24px !important; + line-height: 1.25 !important; +} + +.file-label { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-size: 14px !important; +} + +::ng-deep ngx-dropzone-remove-badge { + opacity: 1 !important; + margin-left: .5rem !important; + position: initial !important; +} + + +//attach file + +.attach-btn { + top: -20px; +} + +.attach-file { + width: 156px; + height: 44px; + color: #ffffff; + background: #129d99 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #1e202029; + border-radius: 30px; +} + +.attach-file:hover { + background-color: #ffffff; + border: 1px solid #129d99; + color: #129d99; +} + +.close-btn:hover{ + cursor: pointer; } \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.ts index 8db9791c4..2fe4b0343 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.ts @@ -7,30 +7,35 @@ import { Inject, Component } from '@angular/core'; templateUrl: './dialog-confirmation-upload-profiles.component.html', styleUrls: ['./dialog-confirmation-upload-profiles.component.scss'] }) -export class DialodConfirmationUploadDatasetProfiles { +export class DialogConfirmationUploadDatasetProfiles { sizeError = false; - btnColore:String="primary"; + selectedFileName= ""; selectFile =false; maxFileSize: number = 1048576; constructor( - public dialogRef: MatDialogRef, + public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: any ) { } selectXML(event) { - const file: FileList = event.target.files; + let file: FileList = null; + + if(event.target && event.target.files){ + file = event.target.files; + }else if(event.addedFiles && event.addedFiles.length){ + file = event.addedFiles; + } + if(!file) return;//no select closed with cancel . no file selected const size: number = file[0].size; // Get file size. this.sizeError = size > this.maxFileSize; // Checks if file size is valid. const formdata: FormData = new FormData(); if (!this.sizeError) { this.data.file = file; this.selectFile=true; - this.btnColore="primary"; - }else{ - this.btnColore="warn"; + this.selectedFileName = file[0].name; } this.data.name = file[0].name; } @@ -41,7 +46,6 @@ export class DialodConfirmationUploadDatasetProfiles { } confirm() { - this.data.name = this.data.name; this.data.sucsess = true; this.dialogRef.close(this.data); } @@ -50,4 +54,10 @@ export class DialodConfirmationUploadDatasetProfiles { return (this.selectFile && !this.sizeError); } + //remove selected file + onRemove(){ + this.data.name=""; + this.selectFile = false; + this.selectedFileName = ""; + } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.html index b0e01d2c1..6414e32fb 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.html @@ -1,9 +1,26 @@ -
+
-

{{titlePrefix}} {{'DATASET-PROFILE-LISTING.TITLE' | translate}}

- - +
+
+

{{titlePrefix}} {{'DATASET-PROFILE-LISTING.TITLE' | translate}}

+
+
+
+ + +
+
+
+
+
+ +
+
+ +
+ + @@ -31,12 +48,12 @@ {{'DATASET-PROFILE-LISTING.COLUMNS.STATUS' | translate}} - {{ (row.status | parseStatus) | translate}} +
{{ (row.status | parseStatus) | translate}}
- {{'DATASET-PROFILE-LISTING.COLUMNS.ACTIONS' | translate}} + @@ -50,10 +67,18 @@ library_books {{'DATASET-PROFILE-LISTING.ACTIONS.VIEW-VERSIONS' | translate}} + + @@ -63,14 +88,10 @@
+ - - -
- +
diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.scss index 2eb955561..456be7372 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.scss +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.scss @@ -1,5 +1,14 @@ +.dataset-profile-listing { + margin-top: 1rem; + margin-left: 1rem; + margin-right: 2rem; +} + .mat-table { - margin: 24px; + margin-top: 47px; + // margin-bottom: 20px; + border-radius: 4px; + } .mat-fab-bottom-right { @@ -12,6 +21,10 @@ z-index: 5; } +.mat-header-row{ + background: #f3f5f8; +} + .full-width { width: 100%; } @@ -22,48 +35,85 @@ .mat-row { cursor: pointer; + min-height: 4.5em; } mat-row:hover { - background-color: lightgray; + background-color: #eef5f6; } +.status-chip{ + + border-radius: 20px; + padding-left: 1em; + padding-right: 1em; + padding-top: 0.2em; + font-size: .8em; +} + +.status-chip-finalized{ + color: #568b5a; + background: #9dd1a1 0% 0% no-repeat padding-box; +} + +.status-chip-draft{ + color: #00c4ff; + background: #d3f5ff 0% 0% no-repeat padding-box; +} // mat-row:nth-child(even){ // background-color:red; // } -mat-row:nth-child(odd) { - background-color: #0c748914; - // background-color: #eef0fb; -} +// mat-row:nth-child(odd) { +// background-color: #0c748914; +// // background-color: #eef0fb; +// } -::ng-deep .mat-paginator-container { +:host ::ng-deep .mat-paginator-container { flex-direction: row-reverse !important; justify-content: space-between !important; background-color: #f6f6f6; - height: 30px; - min-height: 30px !important; + align-items: center; + // height: 30px; + // min-height: 30px !important; } -::ng-deep .mat-paginator-page-size { - height: 43px; -} +// ::ng-deep .mat-paginator-page-size { +// height: 43px; +// } -::ng-deep .mat-paginator-range-label { - margin: 15px 32px 0 24px !important; -} +// ::ng-deep .mat-paginator-range-label { +// margin: 15px 32px 0 24px !important; +// } -::ng-deep .mat-paginator-range-actions { - width: 55% !important; - min-height: 43px !important; - justify-content: space-between; -} +// ::ng-deep .mat-paginator-range-actions { +// width: 55% !important; +// min-height: 43px !important; +// justify-content: space-between; +// } -::ng-deep .mat-paginator-navigation-previous { - margin-left: auto !important; -} +// ::ng-deep .mat-paginator-navigation-previous { +// margin-left: auto !important; +// } -::ng-deep .mat-icon-button { - height: 30px !important; - font-size: 12px !important; +// ::ng-deep .mat-icon-button { +// height: 30px !important; +// font-size: 12px !important; +// } + +.import-btn { + background: #ffffff 0% 0% no-repeat padding-box; + border-radius: 30px; + // color: #129d99; + // border: 1px solid #129d99; + padding-left: 2em; + padding-right: 2em; + color: #000; + border: 1px solid #000; } +.create-btn { + border-radius: 30px; + background-color: #f7dd72; + padding-left: 2em; + padding-right: 2em; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.ts index 230ebf16f..4a9246688 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/dataset-profile-listing.component.ts @@ -1,21 +1,28 @@ import { DataSource } from '@angular/cdk/table'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Component, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { MatSort } from '@angular/material/sort'; import { ActivatedRoute, Params, Router } from '@angular/router'; +import { DatasetStatus } from '@app/core/common/enum/dataset-status'; import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; import { DatasetListingModel } from '@app/core/model/dataset/dataset-listing'; import { DmpModel } from '@app/core/model/dmp/dmp'; import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria'; import { DatasetProfileService } from '@app/core/services/dataset-profile/dataset-profile.service'; import { DmpService } from '@app/core/services/dmp/dmp.service'; -import { UiNotificationService } from '@app/core/services/notification/ui-notification-service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { DatasetProfileCriteriaComponent } from '@app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component'; import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; import { BaseComponent } from '@common/base/base.component'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { TranslateService } from '@ngx-translate/core'; +import * as FileSaver from 'file-saver'; import { merge as observableMerge, Observable, of as observableOf } from 'rxjs'; import { map, startWith, switchMap, takeUntil } from 'rxjs/operators'; +import { DialogConfirmationUploadDatasetProfiles } from './criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component'; @Component({ selector: 'app-dataset-profile-listing-component', @@ -35,6 +42,7 @@ export class DatasetProfileListingComponent extends BaseComponent implements OnI titlePrefix: String; dmpId: String; itemId: string; + datasetStatusEnum = DatasetStatus; constructor( private datasetService: DatasetProfileService, @@ -42,12 +50,17 @@ export class DatasetProfileListingComponent extends BaseComponent implements OnI private route: ActivatedRoute, private dmpService: DmpService, private language: TranslateService, - private uiNotificationService: UiNotificationService + private uiNotificationService: UiNotificationService, + private httpClient: HttpClient, + private matomoService: MatomoService, + private dialog: MatDialog, + private datasetProfileService: DatasetProfileService, ) { super(); } ngOnInit() { + this.matomoService.trackPageView('Admin: Dataset Templates'); this.route.params .pipe(takeUntil(this._destroyed)) .subscribe((params: Params) => { @@ -90,6 +103,7 @@ export class DatasetProfileListingComponent extends BaseComponent implements OnI refresh() { this.dataSource = new DatasetDataSource(this.datasetService, this._paginator, this.sort, this.criteria, this.itemId); + this._paginator.pageIndex = 0; } rowClick(rowId: String) { @@ -112,11 +126,121 @@ export class DatasetProfileListingComponent extends BaseComponent implements OnI viewVersions(rowId, rowLabel) { this.router.navigate(['/dataset-profiles/versions/' + rowId], { queryParams: { groupLabel: rowLabel } }); } + downloadXML(datasetProfileId: string): void { + this.datasetProfileService.downloadXML(datasetProfileId) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/xml' }); + const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + FileSaver.saveAs(blob, filename); + }); + } + getFilenameFromContentDispositionHeader(header: string): string { + const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); + + const matches = header.match(regex); + let filename: string; + for (let i = 0; i < matches.length; i++) { + const match = matches[i]; + if (match.includes('filename="')) { + filename = match.substring(10, match.length - 1); + break; + } else if (match.includes('filename=')) { + filename = match.substring(9); + break; + } + } + return filename; + } + + deleteTemplate(id: string){ + if(id){ + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + restoreFocus: false, + data: { + message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'), + confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'), + isDeleteConfirmation: true + } + }); + + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + if (result) { + //this.form.get('status').setValue(DatasetProfileEnum.DELETED); + this.datasetProfileService.delete(id, null) + .pipe(takeUntil(this._destroyed)) + .subscribe( + complete => { + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DATASET-PROFILE-DELETE'), SnackBarNotificationLevel.Success); + this.refresh(); + }, + error => { + this.onCallbackError(error); + if (error.error.statusCode == 674) { + this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DATASET-PROFILE-DELETE'), SnackBarNotificationLevel.Error); + } else { + this.uiNotificationService.snackBarNotification(this.language.instant(error.message), SnackBarNotificationLevel.Error); + } + } + ); + } + }); + + + } + } + + onCallbackError(errorResponse: HttpErrorResponse) { + this.uiNotificationService.snackBarNotification(errorResponse.message, SnackBarNotificationLevel.Warning); + } // makeItPublic(id: String) { // debugger; // this.datasetService.makeDatasetPublic(id).pipe(takeUntil(this._destroyed)).subscribe(); // } + + openDialog(): void { + const dialogRef = this.dialog.open(DialogConfirmationUploadDatasetProfiles, { + width: '500px', + restoreFocus: false, + data: { + message: this.language.instant('DATASET-WIZARD.UPLOAD.UPLOAD-XML-FILE-TITLE'), + confirmButton: this.language.instant('DATASET-WIZARD.UPLOAD.UPLOAD-XML'), + cancelButton: this.language.instant('DATASET-WIZARD.UPLOAD.UPLOAD-XML-FILE-CANCEL'), + name: "", + file: FileList, + sucsess: false + } + }); + dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(data => { + if (data && data.sucsess && data.name != null && data.file != null) { + this.datasetService.uploadFile(data.file, data.name) + .pipe(takeUntil(this._destroyed)) + .subscribe(_=>{ + this.uiNotificationService.snackBarNotification('Template successfully uploaded', SnackBarNotificationLevel.Success); + this.refresh(); + }, + error=>{ + this.uiNotificationService.snackBarNotification(error.message, SnackBarNotificationLevel.Error); + } + ); + } + }); + } + + /** + * gets as a a pameter a number representing the status and returns the class that is applied + * to status-chip */ + getStatusClass(status: DatasetStatus):string{ + + if(status == DatasetStatus.Finalized){ + return 'status-chip-finalized' + } + + return 'status-chip-draft'; + } + } export class DatasetDataSource extends DataSource { @@ -169,7 +293,8 @@ export class DatasetDataSource extends DataSource { }), map(result => { if (!result) { return []; } - if (this._paginator.pageIndex === 0) { this.totalCount = result.totalCount; } + // if (this._paginator.pageIndex === 0) { this.totalCount = result.totalCount; } + this.totalCount = result.totalCount; return result.data; })); } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/pipe/parse-status.pipe.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/pipe/parse-status.pipe.ts index 33cbd4b8c..82b75d2ab 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/listing/pipe/parse-status.pipe.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/listing/pipe/parse-status.pipe.ts @@ -1,4 +1,5 @@ import { Pipe, PipeTransform } from '@angular/core'; +import { DatasetStatus } from '@app/core/common/enum/dataset-status'; @Pipe({ name: 'parseStatus', @@ -10,7 +11,17 @@ export class ParseStatus implements PipeTransform { } - parseStatus(status: number): string { - return status != 0 ? 'DATASET-PROFILE-STATUS.FINALIZED' : 'DATASET-PROFILE-STATUS.DRAFT'; + parseStatus(status: DatasetStatus): string { + + switch (status) { + case DatasetStatus.Finalized: + return 'DATASET-PROFILE-STATUS.FINALIZED'; + case DatasetStatus.Draft: + return 'DATASET-PROFILE-STATUS.DRAFT'; + case DatasetStatus.Deleted: + return 'DATASET-PROFILE-STATUS.DRAFT.DELETED'; + default: + return 'DATASET-PROFILE-STATUS.DRAFT.NONE'; + } } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-entry.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-entry.ts new file mode 100644 index 000000000..b1ddf7e9a --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-entry.ts @@ -0,0 +1,29 @@ +import { AbstractControl } from "@angular/forms"; + +export interface ToCEntry { + id: string; + label: string; + subEntriesType: ToCEntryType; + subEntries: ToCEntry[]; + type: ToCEntryType; + form: AbstractControl; + numbering: string; +} + + +export enum ToCEntryType { + Page = 0, + Section = 1, + FieldSet = 2, + Field = 3 +} + +export interface NewEntryType { + childType: ToCEntryType, + parent: ToCEntry +} + +export interface TableUpdateInfo{ + draggedItemId?: string; + data?:any; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section.html b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section.html new file mode 100644 index 000000000..72a3d7179 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section.html @@ -0,0 +1,249 @@ + + +
+ +
+ + + + + {{parentLink.subEntries?.length}} + + + + + + delete + +
+
+ + + + +
+ + +
+ + +
+ +
+ +
+ + + + + + + + + + + + + + + +
+ + +
+ +
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section.scss new file mode 100644 index 000000000..03cd37891 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section.scss @@ -0,0 +1,113 @@ +// .docs-toc-container { +// width: 100%; +// padding: 5px 0 10px 0px; +// // cursor: pointer; +// // border-left: solid 4px #0c7489; + +// .scroll-container { +// overflow-y: auto; +// // calc(100vh - 250px) +// // height: calc(100vh - 250px); +// } + +// .docs-link { +// color: rgba(0, 0, 0, 0.54); +// // color: mat-color($app-blue-theme-foreground, secondary-text); +// transition: color 100ms; + +// &:hover, +// &.docs-active { +// .link-name { +// background-color: #ececec; +// border-radius: 6px; +// cursor: pointer;; +// // color: #0c7489; +// } +// // color: mat-color($primary, if($is-dark-theme, 200, default)); +// } +// } +// } + +// .docs-toc-heading { +// margin: 0; +// padding: 0; +// font-size: 13px; +// font-weight: bold; +// } + +// .table-item-actions{ +// // display: none; +// display: inline-block; +// visibility: hidden; +// } + +// .table-item:hover { +// .table-item-actions{ +// // display: inline-block; +// visibility: visible; +// } +// } + +// .table-item col{ +// text-overflow: ellipsis; +// overflow: hidden; +// white-space: nowrap; +// } + +.link-info{ + // display: inline-block; cursor: pointer; + // padding-top: .6em; + // padding-left: .6em; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + + +.border-left-active{ + border-left: 1px solid #000; +} + + +.side-bolder{ + border-left: 1px solid red; +} + + +.cdk-link-list { + + display: block; + // background: white; + overflow: hidden; +} + + + +$blue-color : #129D99; +$blue-color-light: #5cf7f2; +$yellow: #f7dd72; +.badge-ball{ + display: inline-block; + border-radius: 50%; + background-color: #FFF; + font-size: small; + font-weight: bold; + min-width: 2em; + text-align: center; +} + +.table-label-element{ + cursor: pointer; + // font-weight: normal; + + // transition-property: font-weight; + // transition-duration: 160ms; + // transition-delay: 50ms; + // transition-timing-function: ease-in-out; +} + + +.table-label-element-active{ + font-weight: bold; + // color: red; +} diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section.ts new file mode 100644 index 000000000..922f1d050 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section.ts @@ -0,0 +1,281 @@ +import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; +import { DOCUMENT } from '@angular/common'; +import { Component, EventEmitter, Inject, Input, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms'; +import { BaseComponent } from '@common/base/base.component'; +import { NewEntryType, ToCEntry, ToCEntryType } from '../table-of-contents-entry'; + +@Component({ + selector: 'app-dataset-profile-table-of-contents-internal-section', + styleUrls: ['./table-of-contents-internal-section.scss'], + templateUrl: './table-of-contents-internal-section.html' +}) +export class DatasetProfileTableOfContentsInternalSection extends BaseComponent implements OnInit { + + @Input() links: ToCEntry[]; + @Output() itemClick = new EventEmitter(); + @Output() removeEntry = new EventEmitter(); + + @Output() createFooEntry = new EventEmitter(); + + @Output() dataNeedsRefresh = new EventEmitter(); + + + + @Input() parentLink: ToCEntry; + @Input() itemSelected: ToCEntry; + @Input() DRAGULA_ITEM_ID_PREFIX; + @Input() overContainerId: string; + @Input() isDragging; + @Input() draggingItemId: string; + @Input() parentRootId: string; + + @Input() colorizeInvalid:boolean = false; + + @Input() viewOnly: boolean; + // @Input() dropListGroup: Set = new Set(); + // @Input() dropListGroup: string[]; + + // @Input() dragHoveringOver: boolean = false; + // @Input() depth: number = 0; + + // @Input() dropListStruct: { id: string, depth: number}[] = []; + + constructor( + @Inject(DOCUMENT) private _document: Document) { + super(); + } + + tocEntryType = ToCEntryType; + + + // compareFn(a, b){ + // if(a.depth> b.depth) return -1; + // if(a.depth< b.depth) return 1; + + // return 0; + // } + + ngOnInit(): void { + } + + // hoveroverEnter(){ + // // console.log('user hovering drag over', this.parentLink.id, this.parentLink.label); + // this.dragHoveringOver = true; + // } + // hoveroverLeft(){ + // this.dragHoveringOver = false; + // } + + ngOnChanges(changes: SimpleChanges) { + + } + + // get grouListIds(){ + // return Array.from(this.dropListGroup); + // } + itemClicked(item: ToCEntry) { + //leaf node + this.itemClick.emit(item); + } + + deleteEntry(currentLink: ToCEntry){ + this.removeEntry.emit(currentLink); + } + + createNewEntry(foo: NewEntryType){ + this.createFooEntry.emit(foo); + } + + // tocEntryIsChildOf(testingChild: ToCEntry,parent: ToCEntry): boolean{ + + // if(!testingChild || !parent) return false; + + // if(testingChild.id == parent.id){return true;} + + // if(parent.subEntries){ + // let childFound:boolean = false; + + // parent.subEntries.forEach(subEntry=>{ + // if(this.tocEntryIsChildOf(testingChild, subEntry)){ + // childFound = true; + // return true; + // } + // }) + + // return childFound; + // } + // return false; + // } + + get selectedItemInLinks(){ + if(!this.links || !this.itemSelected) return false; + + const link = this.links.find(l=>l.id === this.itemSelected.id); + + if(link) return true; + return false; + } + + // appendAndGetDroplists(dropList: CdkDropList){ + // this.dropListGroup.push(dropList); + // return this.dropListGroup; + // } + + // drop(event: CdkDragDrop) { + // // if(!this.links || !this.links.length) return; + + // if(event.container === event.previousContainer){ + // moveItemInArray(this.links, event.previousIndex, event.currentIndex); + + // let arrayToUpdate: FormArray = this.links[0].form.parent as FormArray; + // // if(this.parentLink && this.parentLink.form){ + // // switch(this.parentLink.subEntriesType){ + // // case this.tocEntryType.Field: + // // arrayToUpdate = (this.parentLink.form.get('fields') as FormArray); + // // break; + // // case this.tocEntryType.FieldSet: + // // arrayToUpdate = (this.parentLink.form.get('fieldSets') as FormArray); + // // break; + // // case this.tocEntryType.Section: + // // arrayToUpdate = (this.parentLink.form.get('sections') as FormArray); + // // break + // // } + + // // } + // if(arrayToUpdate.controls){ + // moveItemInArray(arrayToUpdate.controls, event.previousIndex, event.currentIndex); + // //update ordinality + // arrayToUpdate.controls.forEach((element,idx ) => { + // element.get('ordinal').setValue(idx); + // element.updateValueAndValidity(); + // }); + // } + + // this.dataNeedsRefresh.emit(); + // }else{ + // console.log('not same container'); + // } + + // console.log(event.container.id); + + // } + + onDataNeedsRefresh(){ + this.dataNeedsRefresh.emit(); + } + + + // get hoveringOverParent(){ + // if(!this.overContainerId) return false; + // const child = this._findTocEntryById(this.overContainerId, this.parentLink.subEntries); + // if(!child) return false; + // return true; + // } + + + public _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry{ + if(!tocentries){ + return null; + } + + let tocEntryFound = tocentries.find(entry=>entry.id === id); + + if(tocEntryFound){ + return tocEntryFound; + } + + for(let entry of tocentries){ + const result = this._findTocEntryById(id, entry.subEntries); + if(result){ + tocEntryFound = result; + break; + } + } + + return tocEntryFound? tocEntryFound: null; + } + + + + colorError():boolean{ + + if(!this.colorizeInvalid) return false; + + const form = this.parentLink.form; + if((!form || form.valid || !form.touched) && this.parentLink.type !== this.tocEntryType.Page) return false; + + const allFieldsAreTouched = this.allFieldsAreTouched(form); + + //fieldset may have errros that are inside its controls and not in the fieldsetFormGroup + if(this.parentLink.type === this.tocEntryType.FieldSet && allFieldsAreTouched) return true; + + if(form.errors && allFieldsAreTouched) return true; + + //check if page has sections + if(this.parentLink.type === this.tocEntryType.Page && allFieldsAreTouched){ + const rootForm = form.root; + if(rootForm){ + const sections = rootForm.get('sections') as FormArray; + if(!sections.controls.find(section=>section.get('page').value === this.parentLink.id)){ + return true; + } + } + } + + + //checking first child form controls if have errors + let hasErrors = false; + if(allFieldsAreTouched){ + if(form instanceof FormGroup){ + const formGroup = form as FormGroup; + + const controls = Object.keys(formGroup.controls); + + controls.forEach(control=>{ + if(formGroup.get(control).errors){ + hasErrors = true; + } + }) + + } + } + + return hasErrors; + } + + + allFieldsAreTouched(aControl:AbstractControl){//auto na testaroume + + if(!aControl|| aControl.untouched) return false; + + if(aControl instanceof FormControl){ + return aControl.touched; + }else if(aControl instanceof FormGroup){ + const controlKeys = Object.keys((aControl as FormGroup).controls); + let areAllTouched = true; + controlKeys.forEach(key=>{ + if(!this.allFieldsAreTouched(aControl.get(key))){ + areAllTouched = false; + } + }) + // const areAllTouched = controlKeys.reduce((acc, key)=>acc && this._allFieldsAreTouched(aControl.get(key)), true); + return areAllTouched; + + }else if(aControl instanceof FormArray){ + const controls = (aControl as FormArray).controls; + // const areAllTouched = controls.reduce((acc, control)=>acc && this._allFieldsAreTouched(control), true); + let areAllTouched = true; + // controls.reduce((acc, control)=>acc && this._allFieldsAreTouched(control), true); + controls.forEach(control=>{ + if(!this.allFieldsAreTouched(control)){ + areAllTouched = false; + } + }); + return areAllTouched; + } + + + return false; + } +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.html b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.html new file mode 100644 index 000000000..c88f0a71b --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.html @@ -0,0 +1,23 @@ +
+

{{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.TEMPLATE-OUTLINE' | translate}}

+
+ + + + +
+
\ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.scss new file mode 100644 index 000000000..ebd142270 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.scss @@ -0,0 +1,62 @@ + +.scroll-container { + // overflow-y: auto; + max-height: 60vh; + overflow-y: scroll; + padding-left: .2em; + padding-right: 1em; +} + +// #style-6::-webkit-scrollbar-track +// { +// -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); +// background-color: #F5F5F5; +// } + +// #style-6::-webkit-scrollbar +// { +// width: 6px; +// background-color: #F5F5F5; +// } + +// #style-6::-webkit-scrollbar-thumb +// { +// background-color: rgb(162, 163, 163); +// background-image: -webkit-linear-gradient(45deg, +// rgba(255, 255, 255, .2) 25%, +// transparent 25%, +// transparent 50%, +// rgba(255, 255, 255, .2) 50%, +// rgba(255, 255, 255, .2) 75%, +// transparent 75%, +// transparent) +// } + + +#tocentrytable::-webkit-scrollbar-track +{ + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); + border-radius: 10px; + background-color: #F5F5F5; +} + +#tocentrytable::-webkit-scrollbar +{ + width: 4px; + background-color: #F5F5F5; +} + +#tocentrytable::-webkit-scrollbar-thumb +{ + border-radius: 2px; + -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); + background-color: rgb(158, 158, 158);// #FFF;//$blue-color-light;// rgb(162, 163, 163);// #D62929; +} + + +#guide-steps{ + color: #212121; + opacity: 0.6; + font-size: 1.6em; + margin-top: 0px; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.ts new file mode 100644 index 000000000..69717e782 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/table-of-contents/table-of-contents.ts @@ -0,0 +1,559 @@ +import { DOCUMENT } from '@angular/common'; +import { Component, EventEmitter, Inject, OnInit, Output, Input } from '@angular/core'; +import { BaseComponent } from '@common/base/base.component'; +import { interval, Subject, Subscription } from 'rxjs'; +import { distinctUntilChanged } from 'rxjs/operators'; +import { type } from 'os'; +import { SimpleChanges } from '@angular/core'; +import { NewEntryType, TableUpdateInfo, ToCEntry, ToCEntryType } from './table-of-contents-entry'; +import { DragulaService } from 'ng2-dragula'; +import { FormArray } from '@angular/forms'; +import { MatSnackBar, MatSnackBarConfig } from '@angular/material'; +import { TranslateService } from '@ngx-translate/core'; +import { ContentObserver } from '@angular/cdk/observers'; + + +@Component({ + selector: 'dataset-profile-table-of-contents', + styleUrls: ['./table-of-contents.scss'], + templateUrl: './table-of-contents.html' +}) +export class DatasetProfileTableOfContents extends BaseComponent implements OnInit { + + @Input() links: ToCEntry[]; + @Input() itemSelected: ToCEntry; + @Input() colorizeInvalid: boolean = false; + @Input() viewOnly: boolean; + + + @Output() itemClick = new EventEmitter(); + // @Output() newEntry = new EventEmitter(); + @Output() removeEntry = new EventEmitter(); + @Output() createEntry = new EventEmitter(); + @Output() dataNeedsRefresh = new EventEmitter(); + + isDragging: boolean = false; + draggingItemId: string = null; + tocEntryType = ToCEntryType; + + DRAGULA_ITEM_ID_PREFIX="table_item_id_"; + ROOT_ID: string = "ROOT_ID";//no special meaning + private _dragStartedAt; + private VALID_DROP_TIME = 500;//ms + overcontainer: string = null; + + constructor( + @Inject(DOCUMENT) private _document: Document, + private dragulaService: DragulaService, + private snackbar: MatSnackBar, + private language: TranslateService + ) { + super(); + + if(this.dragulaService.find('TABLEDRAG')){ + this.dragulaService.destroy('TABLEDRAG'); + } + + const dragula = this.dragulaService.createGroup('TABLEDRAG', {}); + + const drake = dragula.drake; + + drake.on('drop', (el, target, source,sibling)=>{ + + if(this._dragStartedAt){ + const timeNow = new Date().getTime(); + + if(timeNow - this._dragStartedAt> this.VALID_DROP_TIME){ + // console.log('timenow: ', timeNow); + // console.log('timestarted', this._dragStartedAt); + this._dragStartedAt = null; + + }else{ + this.dataNeedsRefresh.emit();// even though the data is not changed the TABLE DRAG may has changed + return; + } + }else{ + this.dataNeedsRefresh.emit();// even though the data is not changed the TABLE DRAG may has changed + return; + + } + + const elementId = (el.id as string).replace(this.DRAGULA_ITEM_ID_PREFIX,''); + const targetId = target.id as string; + const sourceId = source.id as string; + + + if(!(elementId && targetId && sourceId)){ + console.info('Elements do not have an id'); + this.dataNeedsRefresh.emit(); + return; + } + + + const element:ToCEntry = this._findTocEntryById(elementId, this.links); + const targetContainer:ToCEntry = this._findTocEntryById(targetId , this.links); + const sourceContainer:ToCEntry = this._findTocEntryById(sourceId, this.links); + + if(!(element && (targetContainer ||((element.type===ToCEntryType.Page) && (targetId === this.ROOT_ID))) && (sourceContainer||((element.type===ToCEntryType.Page) && (sourceId === this.ROOT_ID))))){ + // console.info('Could not find elements'); + this.dataNeedsRefresh.emit(); + drake.cancel(true); + return; + } + + + switch(element.type){ + case ToCEntryType.FieldSet:{ + if(targetContainer.type != this.tocEntryType.Section){ + // const message = 'Fieldset can only be child of Subsections'; + const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.FIELDSET-MUST-HAVE-PARENT-SECTION'); + // console.error(message); + this.notifyUser(message) + this.dataNeedsRefresh.emit(); + return; + } + + //check if target container has no sections + if((targetContainer.form.get('sections') as FormArray).length){ + // const message = 'Cannot have inputs and sections on the same level'; + const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.INPUT-SECTION-SAME-LEVEL'); + this.notifyUser(message); + // console.error(message); + this.dataNeedsRefresh.emit(); + return; + } + + const fieldsetForm = element.form; + const targetFieldsets = targetContainer.form.get('fieldSets') as FormArray; + const sourceFieldsets = sourceContainer.form.get('fieldSets') as FormArray; + + if(!targetFieldsets){ + console.info('Not target fieldsets container found'); + this.dataNeedsRefresh.emit(); + return; + } + + let sourceOrdinal=-1; + let idx = -1; + sourceFieldsets.controls.forEach((elem,index)=>{ + if(elem.get('id').value === elementId){ + sourceOrdinal = elem.get('ordinal').value; + idx = index + } + }); + + if(sourceOrdinal>=0 && idx>=0){ + sourceFieldsets.removeAt(idx); + + sourceFieldsets.controls.forEach(control=>{ + const ordinal = control.get('ordinal'); + if((ordinal.value>= sourceOrdinal) && sourceOrdinal>0){ + const updatedOrdinalVal = ordinal.value -1; + ordinal.setValue(updatedOrdinalVal); + } + }); + sourceFieldsets.controls.sort(this._compareOrdinals); + } + + let position:number = targetFieldsets.length; + + if(!sibling ||!sibling.id){ + console.info('No sibling Id found'); + }else{ + const siblingId = (sibling.id as string).replace(this.DRAGULA_ITEM_ID_PREFIX,''); + let siblingIndex = -1; + targetFieldsets.controls.forEach((e,idx)=>{ + if(e.get('id').value === siblingId){ + siblingIndex = idx; + position = e.get('ordinal').value; + } + + }); + + if(siblingIndex>=0){ //sibling found + + targetFieldsets.controls.filter(control=> control.get('ordinal').value >= position).forEach(control=>{ + const ordinal = control.get('ordinal'); + const updatedOrdinalVal = ordinal.value +1; + ordinal.setValue(updatedOrdinalVal); + }) + } + + } + + + fieldsetForm.get('ordinal').setValue(position); + targetFieldsets.insert(position,fieldsetForm); + targetFieldsets.controls.sort(this._compareOrdinals); + this.dataNeedsRefresh.emit({draggedItemId: elementId}); + + break; + } + case ToCEntryType.Section:{ + + if(targetContainer.type == ToCEntryType.Section){ + if((targetContainer.form.get('fieldSets')as FormArray).length){ + // const message = 'Cannot have inputs and sections on the same level'; + const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.INPUT-SECTION-SAME-LEVEL');; + this.notifyUser(message); + // console.info(message); + this.dataNeedsRefresh.emit(); + return; + } + + const targetSections = targetContainer.form.get('sections') as FormArray; + const elementSectionForm = element.form; + const sourceSections = elementSectionForm.parent as FormArray; + + if(!(targetSections && sourceSections && elementSectionForm)){ + console.info('Could not load sections'); + this.dataNeedsRefresh.emit(); + return; + } + + let idx = -1; + sourceSections.controls.forEach((section,i)=>{ + if(section.get('id').value === elementId){ + idx = i; + } + }); + + if(!(idx>=0)){ + console.info('Could not find element in Parent container'); + this.dataNeedsRefresh.emit(); + return; + } + + sourceSections.controls.filter(control=>control.get('ordinal').value >= elementSectionForm.get('ordinal').value).forEach(control=>{ + const ordinal = control.get('ordinal'); + const updatedOrdinalVal = ordinal.value? ordinal.value -1: 0; + ordinal.setValue(updatedOrdinalVal); + }); + + + sourceSections.removeAt(idx); + + let targetOrdinal = targetSections.length; + + if(sibling && sibling.id){ + const siblingId = sibling.id.replace(this.DRAGULA_ITEM_ID_PREFIX,''); + + targetSections.controls.forEach((section,i)=>{ + if(section.get('id').value === siblingId){ + targetOrdinal = section.get('ordinal').value; + } + }) + + // if(targetOrdinal!=targetSections.length){//mporei na einai idio + // section.get('ordinal').setValue(i+1); + targetSections.controls.filter(control=> control.get('ordinal').value>=targetOrdinal).forEach(control=>{ + const ordinal = control.get('ordinal'); + const updatedOrdinalVal = ordinal.value+1; + ordinal.setValue(updatedOrdinalVal); + }); + // } + + }else{ + console.info('no siblings found'); + } + elementSectionForm.get('ordinal').setValue(targetOrdinal); + targetSections.insert(targetOrdinal, elementSectionForm); + + }else if(targetContainer.type === ToCEntryType.Page){ + // const pageId = targetContainer.form.get('id').value; + + const rootform = targetContainer.form.root; + const sectionForm = element.form; + const parentSections = sectionForm.parent as FormArray; + + let parentIndex = -1; + parentSections.controls.forEach((section,i )=>{ + if(section.get('id').value === elementId){ + parentIndex = i + } + }) + + + if(parentIndex<0){ + console.info('could not locate section in parents array'); + this.dataNeedsRefresh.emit(); + return; + } + + //update parent sections ordinal + parentSections.controls.filter(section=>section.get('ordinal').value >= sectionForm.get('ordinal').value).forEach(section=>{ + const ordinal = section.get('ordinal'); + const updatedOrdinalVal = ordinal.value?ordinal.value -1: 0; + ordinal.setValue(updatedOrdinalVal); + }) + + parentSections.removeAt(parentIndex); + + + + let position = 0; + if(targetContainer.subEntries){ + position = targetContainer.subEntries.length; + } + //populate sections + const targetSectionsArray = rootform.get('sections') as FormArray; + + + if(sibling && sibling.id){ + const siblingId= sibling.id.replace(this.DRAGULA_ITEM_ID_PREFIX, ''); + let indx = -1; + + targetContainer.subEntries.forEach((e,i)=>{ + if(e.form.get('id').value === siblingId){ + indx = i; + position = e.form.get('ordinal').value; + } + }); + if(indx>=0) { + + // e.form.get('ordinal').setValue(i+1); + targetContainer.subEntries.filter(e=>e.form.get('ordinal').value >= position).forEach(e=>{ + const ordinal = e.form.get('ordinal'); + const updatedOrdinalVal = ordinal.value +1; + ordinal.setValue(updatedOrdinalVal); + }); + } + + }else{ + console.info('No sibling found'); + } + + sectionForm.get('ordinal').setValue(position); + sectionForm.get('page').setValue(targetContainer.id); + targetSectionsArray.push(sectionForm); + + }else{ + // const message = 'Drag not support to specific container'; + const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.DRAG-NOT-SUPPORTED'); + this.notifyUser(message); + // console.info(message); + this.dataNeedsRefresh.emit(); + return; + } + + + + this.dataNeedsRefresh.emit({draggedItemId: elementId}); + break; + } + case ToCEntryType.Page:{ + if(targetId != this.ROOT_ID){ + // const message = 'A page element can only be at top level'; + const message = this.language.instant('DATASET-PROFILE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.PAGE-ELEMENT-ONLY-TOP-LEVEL'); + this.notifyUser(message); + // console.info(message); + this.dataNeedsRefresh.emit(); + return; + } + + const rootForm = element.form.root; + if(!rootForm){ + console.info('Could not find root!') + this.dataNeedsRefresh.emit(); + return; + } + + + const pages = rootForm.get('pages') as FormArray; + const pageForm = element.form; + + let index = -1; + + pages.controls.forEach((page,i)=>{ + if(page.get('id').value === elementId){ + index =i; + } + }); + + if(index<0){ + console.info('Could not locate page on pages'); + this.dataNeedsRefresh.emit(); + return; + } + + + //ordinality + pages.controls.filter(page=> page.get('ordinal').value>= pageForm.get('ordinal').value).forEach(page=>{ + const ordinal = page.get('ordinal'); + const ordinalVal = ordinal.value? ordinal.value - 1: 0; + ordinal.setValue(ordinalVal); + }); + + pages.removeAt(index); + + let targetPosition = pages.length; + + if(sibling){ + const siblingId = sibling.id.replace(this.DRAGULA_ITEM_ID_PREFIX, ''); + + pages.controls.forEach((page,i)=>{ + if(page.get('id').value === siblingId){ + targetPosition = page.get('ordinal').value; + } + }); + } + pageForm.get('ordinal').setValue(targetPosition); + + //update ordinality + pages.controls.filter(page=> page.get('ordinal').value>= targetPosition).forEach(page=>{ + const ordinal = page.get('ordinal'); + const ordinalVal = ordinal.value +1; + ordinal.setValue(ordinalVal); + }); + + + pages.insert(targetPosition, pageForm); + this.dataNeedsRefresh.emit({draggedItemId:elementId}); + break; + } + default: + + console.info('Could not support moving objects for specific type of element'); + this.dataNeedsRefresh.emit(); + return; + + } + }); + + + drake.on('drag',(el,source)=>{ + this._dragStartedAt = new Date().getTime(); + // console.log('drag fired'); + this.isDragging = true; + this.draggingItemId = (el.id as string).replace(this.DRAGULA_ITEM_ID_PREFIX, ''); + + // setTimeout(() => { + // if(this.isDragging){ + // this._scrollIntoDragginItem(this.draggingItemId); + // } + // }, this.VALID_DROP_TIME); + }); + drake.on('over',(el, container, source)=>{ + try { + this.overcontainer = container.id; + } catch (error) { + this.overcontainer = null; + } + }); + drake.on('dragend',(el)=>{ + this.isDragging = false; + this.draggingItemId = null; + this.overcontainer = null; + }); + + + + + } + + private _scrollIntoDragginItem(id: string){ + + // const table = document.getElementById('tocentrytable'); + // if(table){ + // // const element = document.getElementById('TABLE_ENTRY'+id); + // console.log('Table found!'); + // const element = document.getElementById('TABLE_ENTRY' + id); + // const elementFromTable = table.closest('#TABLE_ENTRY'+ id); + + + // if(elementFromTable){ + // console.log('found from table:', elementFromTable); + // } + // if(element){ + // console.log('Element found!'); + // // element.classList.add('text-danger'); + // // console.log(element); + + // const tableRect = table.getBoundingClientRect(); + // const elementRect = element.getBoundingClientRect(); + + + // console.log('tablerect :',tableRect); + // console.log('elementRect :',elementRect); + + // const dY = elementRect.top - tableRect.top; + // console.log('Distance from table is ', dY); + // // table.scroll({top:dY,behavior:'smooth'}); + // console.log('found from document ', element); + + // // element.scrollIntoView(); + // } + // // element.scrollIntoView(); + // } + } + + private _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry{ + if(!tocentries){ + return null; + } + + let tocEntryFound = tocentries.find(entry=>entry.id === id); + + if(tocEntryFound){ + return tocEntryFound; + } + + for(let entry of tocentries){ + const result = this._findTocEntryById(id, entry.subEntries); + if(result){ + tocEntryFound = result; + break; + } + } + + return tocEntryFound? tocEntryFound: null; + } + + ngOnInit(): void { + + + } + + ngOnChanges(changes: SimpleChanges) { + + } + + + itemClicked(item: ToCEntry){ + //leaf node + this.itemClick.emit(item); + } + + // addNewEntry(tce: ToCEntry){ + // this.newEntry.emit(tce); + // } + deleteEntry(currentLink: ToCEntry){ + this.removeEntry.emit(currentLink); + } + + createNewEntry(newEntry: NewEntryType){ + this.createEntry.emit(newEntry); + } + onDataNeedsRefresh(){ + this.dataNeedsRefresh.emit(); + } + + notifyUser(message:string){ + this.snackbar.open(message, null, this._snackBarConfig); + } + + private _snackBarConfig: MatSnackBarConfig = { + duration:2000 + } + + private _compareOrdinals(a, b){ + + const aValue = a.get('ordinal').value as number; + const bValue = b.get('ordinal').value as number; + + // if(!aValue || !bValue) return 0; + return aValue - bValue; + } + + +} diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.scss b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.scss index b0459dd25..972e19d7b 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.scss +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.scss @@ -1,4 +1,6 @@ .dmp-profile-editor { + margin-top: 1.3rem; + .centered-row-item { align-items: center; display: flex; diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.ts b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.ts index 6ee859cf1..90cc566e0 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.ts @@ -21,6 +21,8 @@ import * as FileSaver from 'file-saver'; import { Observable, of as observableOf } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { HttpClient } from '@angular/common/http'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; @Component({ @@ -46,13 +48,16 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie private enumUtils: EnumUtils, private uiNotificationService: UiNotificationService, private formService: FormService, - private configurationService: ConfigurationService + private configurationService: ConfigurationService, + private httpClient: HttpClient, + private matomoService: MatomoService ) { super(); this.host = configurationService.server; } ngAfterViewInit() { + this.matomoService.trackPageView('Admin: DMP Profile Edit'); this.route.params .pipe(takeUntil(this._destroyed)) .subscribe((params: Params) => { diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.scss b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.scss index fe822c9c8..7720847f5 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.scss +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.scss @@ -3,6 +3,8 @@ } .dmp-profile-listing { + margin-top: 1.3rem; + .mat-row { cursor: pointer; } @@ -31,7 +33,7 @@ } } -::ng-deep .mat-paginator-container { +:host ::ng-deep .mat-paginator-container { flex-direction: row-reverse !important; justify-content: space-between !important; background-color: #f6f6f6; @@ -39,25 +41,25 @@ min-height: 30px !important; } -::ng-deep .mat-paginator-page-size { +:host ::ng-deep .mat-paginator-page-size { height: 43px; } -::ng-deep .mat-paginator-range-label { +:host ::ng-deep .mat-paginator-range-label { margin: 15px 32px 0 24px !important; } -::ng-deep .mat-paginator-range-actions { +:host ::ng-deep .mat-paginator-range-actions { width: 55% !important; min-height: 43px !important; justify-content: space-between; } -::ng-deep .mat-paginator-navigation-previous { +:host ::ng-deep .mat-paginator-navigation-previous { margin-left: auto !important; } -::ng-deep .mat-icon-button { +:host ::ng-deep .mat-icon-button { height: 30px !important; font-size: 12px !important; } diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.ts b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.ts index 6a954910d..2bcd93061 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.ts @@ -1,5 +1,6 @@ import { DataSource } from '@angular/cdk/table'; +import { HttpClient } from '@angular/common/http'; import { Component, OnInit, ViewChild } from '@angular/core'; import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -9,6 +10,7 @@ import { DataTableRequest } from '@app/core/model/data-table/data-table-request' import { DmpProfileListing } from '@app/core/model/dmp-profile/dmp-profile-listing'; import { DmpProfileCriteria } from '@app/core/query/dmp/dmp-profile-criteria'; import { DmpProfileService } from '@app/core/services/dmp/dmp-profile.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { DmpProfileCriteriaComponent } from '@app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component'; import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; import { BaseComponent } from '@common/base/base.component'; @@ -45,12 +47,15 @@ export class DmpProfileListingComponent extends BaseComponent implements OnInit private languageService: TranslateService, public snackBar: MatSnackBar, public route: ActivatedRoute, - public dmpProfileService: DmpProfileService + public dmpProfileService: DmpProfileService, + private httpClient: HttpClient, + private matomoService: MatomoService ) { super(); } ngOnInit() { + this.matomoService.trackPageView('Admin: DMP Templates'); this.route.params .pipe(takeUntil(this._destroyed)) .subscribe((params: Params) => { diff --git a/dmp-frontend/src/app/ui/admin/index-managment/index-managment.component.scss b/dmp-frontend/src/app/ui/admin/index-managment/index-managment.component.scss index 4ec878d3b..780978d9c 100644 --- a/dmp-frontend/src/app/ui/admin/index-managment/index-managment.component.scss +++ b/dmp-frontend/src/app/ui/admin/index-managment/index-managment.component.scss @@ -1,5 +1,4 @@ .root { - padding-top: 5em; padding-bottom: 2em; .button { diff --git a/dmp-frontend/src/app/ui/admin/user/listing/criteria/user-criteria.component.html b/dmp-frontend/src/app/ui/admin/user/listing/criteria/user-criteria.component.html index 672a39da2..8b30f9472 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/criteria/user-criteria.component.html +++ b/dmp-frontend/src/app/ui/admin/user/listing/criteria/user-criteria.component.html @@ -1,14 +1,18 @@ 
- +
- - - - + {{'CRITERIA.USERS.SHOW' | translate}}: + {{enumUtils.convertFromPrincipalAppRole(role)}} + + + search + + +
- +
diff --git a/dmp-frontend/src/app/ui/admin/user/listing/criteria/user-criteria.component.scss b/dmp-frontend/src/app/ui/admin/user/listing/criteria/user-criteria.component.scss index af2a32097..9b54a5539 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/criteria/user-criteria.component.scss +++ b/dmp-frontend/src/app/ui/admin/user/listing/criteria/user-criteria.component.scss @@ -1,2 +1,33 @@ .user-roles-criteria { + + .container-fluid { + margin-top: 5rem; + } + + .show { + color: #A8A8A8; + } + + .search-form { + text-align: left; + width: 20rem; + } + + .search-form mat-icon { + color: #129d99; + } +} + +::ng-deep .search-form .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} + +::ng-deep .sort-form .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} + +::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { + padding: 0.3rem 0rem 0.6rem 0rem !important; } diff --git a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html index 01994092d..a29097e1d 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html @@ -1,15 +1,28 @@ -
- + +
+ +
+ + {{getPrincipalAppRoleWithLanguage(role)}} + +
+
+
+ - {{getPrincipalAppRoleWithLanguage(role)}} + + {{getPrincipalAppRoleWithLanguage(role)}} + {{'GENERAL.VALIDATION.REQUIRED' | translate}} - - diff --git a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss index e69de29bb..ced1b45aa 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss +++ b/dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss @@ -0,0 +1,114 @@ +.user-role-editor { + align-items: center; + margin-top: 1.125rem; + justify-content: space-between; + padding-left: 15px; + padding-right: 15px; + .select-role { + // margin-top: 1rem; + } + + .roles { + min-width: 90px; + display: flex; + flex-direction: column; + } + + .roles-width-180 { + width: 180px; + margin-right: 8px; + } + + .row-action { + color: #129d99; + cursor: pointer; + } + .user-role{ + text-align: center; + width: auto; + padding-top: 0.2em; + padding-left: 1em; + padding-right: 1em; + display: inline-block; + } + .user { + // display: flex; + // justify-content: center; + // align-items: center; + min-width: 67px; + min-height: 28px; + color: #00c4ff; + background: #d3f5ff 0% 0% no-repeat padding-box; + border-radius: 44px; + letter-spacing: 0.11px; + font-weight: 400; + opacity: 1; + margin-top: 0.5em; + margin-bottom: 0.5em; + padding-left: 10px; + padding-right: 10px; + } + + .manager { + // display: flex; + // justify-content: center; + // align-items: center; + min-width: 90px; + height: 28px; + color: #568b5a; + background: #9dd1a1 0% 0% no-repeat padding-box; + border-radius: 44px; + letter-spacing: 0.11px; + font-weight: 400; + opacity: 1; + margin-top: 0.5em; + margin-bottom: 0.5em; + padding-left: 10px; + padding-right: 10px; + } + + .admin { + // display: flex; + // justify-content: center; + // align-items: center; + min-width: 77px; + min-height: 28px; + color: #ff3d33; + background: #ffd5d3 0% 0% no-repeat padding-box; + border-radius: 44px; + letter-spacing: 0.11px; + font-weight: 400; + opacity: 1; + margin-top: 0.5em; + margin-bottom: 0.5em; + padding-left: 10px; + padding-right: 10px; + } + + .dataset-template-editor { + // display: flex; + // justify-content: center; + // align-items: center; + min-width: 77px; + min-height: 28px; + color: #d633ff; + background: #fad3ff 0% 0% no-repeat padding-box; + border-radius: 44px; + letter-spacing: 0.11px; + font-weight: 400; + opacity: 1; + margin-top: 0.5em; + margin-bottom: 0.5em; + padding-left: 10px; + padding-right: 10px; + } +} + +:host ::ng-deep .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} + +:host ::ng-deep .mat-form-field-appearance-outline .mat-form-field-infix { + padding: 0.3rem 0rem 0.6rem 0rem !important; +} diff --git a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.html b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.html index b136f7f07..150d31164 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.html @@ -1,15 +1,31 @@ 
-

{{'USERS.LISTING.TITLE' | translate}}

- +
+

{{'USERS.LISTING.TITLE' | translate}}

+
+ +
+
- + +
+ + + + + + + + - {{'USERS.LISTING.LABEL' | translate}} - {{row.name}} + {{'USERS.LISTING.NAME' | translate}} + + + {{row.name}} + @@ -26,7 +42,7 @@ {{'USERS.LISTING.ROLES' | translate}} - + @@ -34,8 +50,8 @@ - - - + + +
diff --git a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.scss b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.scss index 4f28a0670..f9e30031c 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.scss +++ b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.scss @@ -1,49 +1,91 @@ .mat-table { - margin: 24px; + margin-top: 47px; + margin-bottom: 20px; + background: #fafafa 0% 0% no-repeat padding-box; + // box-shadow: 0px 5px 12px #00000038; + border-radius: 4px; + + .mat-header-row { + background: #f3f5f8; + } + + mat-header-cell:first-of-type { + padding-left: 46px; + max-width: 120px; + } + + mat-cell:first-of-type { + padding-left: 46px; + max-width: 120px; + } + + .my-mat-card-avatar { + height: 40px; + width: 40px; + border-radius: 50%; + flex-shrink: 0; + margin-right: 2.5rem; + } } .user-listing { - .mat-card { - margin: 1em 0; - } + margin-top: 2rem; + margin-left: 1rem; + margin-right: 2rem; - mat-row:hover { - background-color: lightgray; - } + .mat-card { + margin: 1em 0; + } - mat-row:nth-child(odd) { - background-color: #0c748914; - // background-color: #eef0fb; - } + mat-row:hover { + background-color: #eef5f6; + } + + mat-row:nth-child(odd) { + // background-color: #0c748914; + // background-color: #eef0fb; + } + + .export-btn { + background: #ffffff 0% 0% no-repeat padding-box; + border-radius: 30px; + width: 145px; + color: #129d99; + border: 1px solid #129d99; + } + + .export-icon { + font-size: 20px; + } } -::ng-deep .mat-paginator-container { - flex-direction: row-reverse !important; - justify-content: space-between !important; - background-color: #f6f6f6; - height: 30px; - min-height: 30px !important; +:host ::ng-deep .mat-paginator-container { + // flex-direction: row-reverse !important; + // justify-content: space-between !important; + // height: 30px; + // min-height: 30px !important; + background-color: #f6f6f6; } -::ng-deep .mat-paginator-page-size { - height: 43px; -} +// ::ng-deep .mat-paginator-page-size { +// height: 43px; +// } -::ng-deep .mat-paginator-range-label { - margin: 15px 32px 0 24px !important; -} +// ::ng-deep .mat-paginator-range-label { +// margin: 15px 32px 0 24px !important; +// } -::ng-deep .mat-paginator-range-actions { - width: 55% !important; - min-height: 43px !important; - justify-content: space-between; -} +// ::ng-deep .mat-paginator-range-actions { +// width: 55% !important; +// min-height: 43px !important; +// justify-content: space-between; +// } -::ng-deep .mat-paginator-navigation-previous { - margin-left: auto !important; -} +// ::ng-deep .mat-paginator-navigation-previous { +// margin-left: auto !important; +// } -::ng-deep .mat-icon-button { - height: 30px !important; - font-size: 12px !important; -} +// ::ng-deep .mat-icon-button { +// height: 30px !important; +// font-size: 12px !important; +// } diff --git a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts index 36e12bdad..f756eb622 100644 --- a/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts +++ b/dmp-frontend/src/app/ui/admin/user/listing/user-listing.component.ts @@ -1,7 +1,7 @@ import {of as observableOf, merge as observableMerge, Observable } from 'rxjs'; -import {map, catchError, switchMap, startWith} from 'rxjs/operators'; +import {map, catchError, switchMap, startWith, takeUntil} from 'rxjs/operators'; import { DataSource } from '@angular/cdk/table'; import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core'; import { MatPaginator } from '@angular/material/paginator'; @@ -15,6 +15,11 @@ import { SnackBarNotificationComponent } from '../../../../library/notification/ import { DataTableRequest } from '../../../../core/model/data-table/data-table-request'; import { UserCriteriaComponent } from './criteria/user-criteria.component'; import { BreadcrumbItem } from '../../../misc/breadcrumb/definition/breadcrumb-item'; +import { BaseComponent } from '@common/base/base.component'; +import * as FileSaver from 'file-saver'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { HttpClient } from '@angular/common/http'; +import { MatTableDataSource } from '@angular/material'; export class UsersDataSource extends DataSource { @@ -29,7 +34,6 @@ export class UsersDataSource extends DataSource { private _criteria: UserCriteriaComponent ) { super(); - //this._paginator.page.pipe(takeUntil(this._destroyed)).subscribe((pageEvent: PageEvent) => { // this.store.dispatch(new LoadPhotosRequestAction(pageEvent.pageIndex, pageEvent.pageSize)) //}) @@ -93,7 +97,7 @@ export class UsersDataSource extends DataSource { templateUrl: './user-listing.component.html', styleUrls: ['./user-listing.component.scss'] }) -export class UserListingComponent implements OnInit, AfterViewInit { +export class UserListingComponent extends BaseComponent implements OnInit, AfterViewInit { @ViewChild(MatPaginator, { static: true }) _paginator: MatPaginator; @ViewChild(MatSort, { static: true }) sort: MatSort; @@ -101,17 +105,20 @@ export class UserListingComponent implements OnInit, AfterViewInit { breadCrumbs: Observable; dataSource: UsersDataSource | null; - displayedColumns: String[] = ['name', 'email', 'lastloggedin', 'roles']; + displayedColumns: String[] = ['avatar', 'name', 'email', 'lastloggedin', 'roles']; constructor( private userService: UserService, private languageService: TranslateService, - public snackBar: MatSnackBar + public snackBar: MatSnackBar, + private httpClient: HttpClient, + private matomoService: MatomoService ) { - + super(); } ngOnInit() { + this.matomoService.trackPageView('Admin: Users'); this.breadCrumbs = observableOf([{ parentComponentName: null, label: this.languageService.instant('NAV-BAR.USERS-BREADCRUMB'), @@ -138,4 +145,45 @@ export class UserListingComponent implements OnInit, AfterViewInit { const defaultCriteria = new UserCriteria(); return defaultCriteria; } + + // Export user mails + exportUsers(){ + this.userService.downloadCSV() + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { + const blob = new Blob([response.body], { type: 'application/csv' }); + const filename = this.getFilenameFromContentDispositionHeader(response.headers.get('Content-Disposition')); + FileSaver.saveAs(blob, filename); + }); + } + + getFilenameFromContentDispositionHeader(header: string): string { + const regex: RegExp = new RegExp(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/g); + + const matches = header.match(regex); + let filename: string; + for (let i = 0; i < matches.length; i++) { + const match = matches[i]; + if (match.includes('filename="')) { + filename = match.substring(10, match.length - 1); + break; + } else if (match.includes('filename=')) { + filename = match.substring(9); + break; + } + } + return filename; + } + + public setDefaultAvatar(ev: Event) { + (ev.target as HTMLImageElement).src = 'assets/images/profile-placeholder.png'; + } + + // public principalHasAvatar(): boolean { + // return this.authentication.current().avatarUrl != null && this.authentication.current().avatarUrl.length > 0; + // } + + // public getPrincipalAvatar(): string { + // return this.authentication.current().avatarUrl; + // } } diff --git a/dmp-frontend/src/app/ui/auth/login/b2access/b2access-login.component.ts b/dmp-frontend/src/app/ui/auth/login/b2access/b2access-login.component.ts index a5e2948f8..e60c04ae8 100644 --- a/dmp-frontend/src/app/ui/auth/login/b2access/b2access-login.component.ts +++ b/dmp-frontend/src/app/ui/auth/login/b2access/b2access-login.component.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Params } from '@angular/router'; +import { ActivatedRoute, Params, Router } from '@angular/router'; import { AuthProvider } from '@app/core/common/enum/auth-provider'; import { AuthService } from '@app/core/services/auth/auth.service'; import { LoginService } from '@app/ui/auth/login/utilities/login.service'; @@ -22,7 +22,8 @@ export class B2AccessLoginComponent extends BaseComponent implements OnInit { private authService: AuthService, private loginService: LoginService, private httpClient: HttpClient, - private configurationService: ConfigurationService + private configurationService: ConfigurationService, + private router: Router ) { super(); } @@ -31,9 +32,10 @@ export class B2AccessLoginComponent extends BaseComponent implements OnInit { this.route.queryParams .pipe(takeUntil(this._destroyed)) .subscribe((params: Params) => { - const returnUrl = params['returnUrl']; - if (returnUrl) { this.returnUrl = returnUrl; } - if (!params['code']) { this.b2AccessGetAuthCode(); } else { this.b2AccessLogin(params['code']); } + // const returnUrl = params['returnUrl']; + // if (returnUrl) { this.returnUrl = returnUrl; } + // if (!params['code']) { this.b2AccessGetAuthCode(); } else { this.b2AccessLogin(params['code']); } + this.router.navigate(['/oauth2'], {queryParams: params}); }); } diff --git a/dmp-frontend/src/app/ui/auth/login/linkedin-login/linkedin-login.component.ts b/dmp-frontend/src/app/ui/auth/login/linkedin-login/linkedin-login.component.ts index 6b87652c8..e2285ea4e 100644 --- a/dmp-frontend/src/app/ui/auth/login/linkedin-login/linkedin-login.component.ts +++ b/dmp-frontend/src/app/ui/auth/login/linkedin-login/linkedin-login.component.ts @@ -32,9 +32,10 @@ export class LinkedInLoginComponent extends BaseComponent implements OnInit { this.route.queryParams .pipe(takeUntil(this._destroyed)) .subscribe((params: Params) => { - const returnUrl = params['returnUrl']; - if (returnUrl) { this.returnUrl = returnUrl; } - if (!params['code']) { this.linkedinAuthorize(); } else { this.linkedInLoginUser(params['code'], params['state']); } + // const returnUrl = params['returnUrl']; + // if (returnUrl) { this.returnUrl = returnUrl; } + // if (!params['code']) { this.linkedinAuthorize(); } else { this.linkedInLoginUser(params['code'], params['state']); } + this.router.navigate(['/oauth2'], {queryParams: params}); }); } diff --git a/dmp-frontend/src/app/ui/auth/login/login.component.html b/dmp-frontend/src/app/ui/auth/login/login.component.html index ba900fc39..13ef44581 100644 --- a/dmp-frontend/src/app/ui/auth/login/login.component.html +++ b/dmp-frontend/src/app/ui/auth/login/login.component.html @@ -1,4 +1,4 @@ -