diff --git a/.gitignore b/.gitignore index 670c07133..b6c15ce08 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,9 @@ dmp-frontend/.vscode/ dmp-frontend/package-lock.json dmp-backend/logging/target/ ELK.Docker/shared/data-elk/ + +# Eclipse +.project +.settings/ +bin/ +*.classpath diff --git a/dmp-backend/Dockerfile.CI b/dmp-backend/Dockerfile.CI index 5b8884b7d..866a3a692 100644 --- a/dmp-backend/Dockerfile.CI +++ b/dmp-backend/Dockerfile.CI @@ -1,4 +1,4 @@ -FROM maven:3-jdk-8-alpine AS MAVEN_BUILD +FROM maven:3-openjdk-11 AS MAVEN_BUILD COPY pom.xml /build/ COPY data /build/data/ @@ -9,7 +9,7 @@ COPY web /build/web/ WORKDIR /build/ RUN mvn package -FROM openjdk:8-jre-alpine +FROM adoptopenjdk/openjdk11:alpine-jre 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}", "-Dspring.config.location=/files/config/", "-jar","/app/app.jar"] +ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Dspring.profiles.active=${PROF}", "-Dspring.config.additional-location=/files/config/", "-jar","/app/app.jar"] 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 8aa20acc2..de7119428 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 @@ -2,6 +2,8 @@ package eu.eudat.data.dao.criteria; import eu.eudat.data.entities.DatasetProfile; + +import java.util.Date; import java.util.List; import java.util.UUID; @@ -35,6 +37,8 @@ public class DatasetProfileCriteria extends Criteria { private boolean finalized; private Integer status; private Integer role; + private List ids; + private Date periodStart; public boolean getAllVersions() { return allVersions; } public void setAllVersions(boolean allVersions) { this.allVersions = allVersions; } @@ -78,4 +82,20 @@ public class DatasetProfileCriteria extends Criteria { public void setRole(Integer role) { this.role = role; } + + public List getIds() { + return ids; + } + + public void setIds(List ids) { + this.ids = ids; + } + + public Date getPeriodStart() { + return periodStart; + } + + public void setPeriodStart(Date periodStart) { + this.periodStart = periodStart; + } } 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 40f12491c..b67983aa0 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 @@ -2,9 +2,12 @@ package eu.eudat.data.dao.criteria; import eu.eudat.data.entities.Funder; +import java.util.Date; + public class FunderCriteria extends Criteria { private String reference; private String exactReference; + private Date periodStart; public String getReference() { return reference; @@ -20,4 +23,12 @@ public class FunderCriteria extends Criteria { public void setExactReference(String exactReference) { this.exactReference = exactReference; } + + public Date getPeriodStart() { + return periodStart; + } + + public void setPeriodStart(Date periodStart) { + this.periodStart = periodStart; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/OrganisationCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/OrganisationCriteria.java index 5cc857a24..30e437df0 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/OrganisationCriteria.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/OrganisationCriteria.java @@ -5,6 +5,7 @@ import eu.eudat.data.entities.Organisation; public class OrganisationCriteria extends Criteria { private String labelLike; private Boolean isPublic; + private boolean isActive; public String getLabelLike() { return labelLike; @@ -19,4 +20,12 @@ public class OrganisationCriteria extends Criteria { public void setPublic(Boolean aPublic) { isPublic = aPublic; } + + 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/ProjectCriteria.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/criteria/ProjectCriteria.java index 1ac0bbae5..a9d266ea6 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 @@ -2,9 +2,12 @@ package eu.eudat.data.dao.criteria; import eu.eudat.data.entities.Project; +import java.util.Date; + public class ProjectCriteria extends Criteria { private String reference; private String exactReference; + private Date periodStart; public String getReference() { return reference; @@ -20,4 +23,12 @@ public class ProjectCriteria extends Criteria { public void setExactReference(String exactReference) { this.exactReference = exactReference; } + + public Date getPeriodStart() { + return periodStart; + } + + public void setPeriodStart(Date periodStart) { + this.periodStart = periodStart; + } } 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 c2184547a..21cb1e5d2 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 @@ -2,9 +2,12 @@ package eu.eudat.data.dao.criteria; import eu.eudat.data.entities.Researcher; +import java.util.Date; + public class ResearcherCriteria extends Criteria { private String name; private String reference; + private Date periodStart; public String getName() { return name; @@ -21,4 +24,12 @@ public class ResearcherCriteria extends Criteria { public void setReference(String reference) { this.reference = reference; } + + public Date getPeriodStart() { + return periodStart; + } + + public void setPeriodStart(Date periodStart) { + this.periodStart = periodStart; + } } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/databaselayer/service/DatabaseService.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/databaselayer/service/DatabaseService.java index 8f3487a40..6355da0e2 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/databaselayer/service/DatabaseService.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/databaselayer/service/DatabaseService.java @@ -7,6 +7,7 @@ import eu.eudat.queryable.queryableentity.DataEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.transaction.Transactional; import java.util.Set; @@ -28,6 +29,7 @@ public class DatabaseService { return this.databaseCtx.getQueryable(tClass); } + @Transactional public T createOrUpdate(T item, Class tClass) { return this.databaseCtx.createOrUpdate(item, tClass); } 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 378112713..ca33c0830 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 @@ -16,6 +16,7 @@ import org.springframework.stereotype.Component; import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; +import javax.transaction.Transactional; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -106,6 +107,7 @@ public class DMPDaoImpl extends DatabaseAccess implements DMPDao { } @Override + @Transactional public DMP createOrUpdate(DMP item) { return this.getDatabaseService().createOrUpdate(item, DMP.class); } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetDao.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetDao.java index bcf5d134f..126958e11 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetDao.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/DatasetDao.java @@ -13,6 +13,8 @@ public interface DatasetDao extends DatabaseAccessLayer { QueryableList getWithCriteria(DatasetCriteria criteria); + QueryableList filterFromElastic(DatasetCriteria criteria, List ids); + QueryableList getAuthenticated(QueryableList query, UserInfo principal, List roles); Dataset isPublicDataset(UUID id); 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 3c5b3d3bc..50462c163 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 @@ -87,6 +87,16 @@ public class DatasetDaoImpl extends DatabaseAccess implements DatasetDa return query; } + public QueryableList filterFromElastic(DatasetCriteria criteria, List ids) { + QueryableList query = getDatabaseService().getQueryable(Dataset.getHints(), Dataset.class); + + query.where(((builder, root) -> root.get("id").in(ids))); + if (!criteria.getAllVersions()) + query.initSubQuery(String.class).where((builder, root) -> builder.equal(root.get("dmp").get("version"), query.subQueryMax((builder1, externalRoot, nestedRoot) -> builder1.and(builder1.equal(externalRoot.get("dmp").get("groupId"), nestedRoot.get("dmp").get("groupId")), builder1.notEqual(nestedRoot.get("dmp").get("status"), DMP.DMPStatus.DELETED.getValue())), Arrays.asList(new SelectionField(FieldSelectionType.COMPOSITE_FIELD, "dmp:version")), String.class))); + + return query; + } + @Override public Dataset createOrUpdate(Dataset item) { return getDatabaseService().createOrUpdate(item, Dataset.class); @@ -121,6 +131,7 @@ public class DatasetDaoImpl extends DatabaseAccess implements DatasetDa }); } else { query.where((builder, root) -> builder.equal(root.join("dmp", JoinType.LEFT).join("users", JoinType.LEFT).join("user", JoinType.LEFT).get("id"), principal.getId())); + } return query; } 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 c704f4fba..0822ce6f4 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 @@ -58,11 +58,16 @@ public class DatasetProfileDaoImpl extends DatabaseAccess implem if (criteria.getStatus() != null) { query.where(((builder, root) -> builder.equal(root.get("status"), criteria.getStatus()))); } + if (criteria.getIds() != null) { + query.where(((builder, root) -> root.get("id").in(criteria.getIds()))); + } if (criteria.getFinalized()) { query.where(((builder, root) -> builder.equal(root.get("status"), DatasetProfile.Status.FINALIZED.getValue()))); } else { query.where(((builder, root) -> builder.notEqual(root.get("status"), DatasetProfile.Status.DELETED.getValue()))); } + if (criteria.getPeriodStart() != null) + query.where((builder, root) -> builder.greaterThanOrEqualTo(root.get("created"), criteria.getPeriodStart())); return query; } 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 39d54385c..c5a146c47 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 @@ -31,6 +31,8 @@ public class FunderDaoImpl extends DatabaseAccess implements FunderDao { 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())); + if (criteria.getPeriodStart() != null) + query.where((builder, root) -> builder.greaterThanOrEqualTo(root.get("created"), criteria.getPeriodStart())); 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/OrganisationDaoImpl.java b/dmp-backend/data/src/main/java/eu/eudat/data/dao/entities/OrganisationDaoImpl.java index 6d2e63ee2..3892e7a04 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 @@ -38,6 +38,11 @@ public class OrganisationDaoImpl extends DatabaseAccess implements query.where((builder, root) -> builder.equal(root.join("dmps", JoinType.LEFT).get("status"), DMP.DMPStatus.FINALISED.getValue())); } } + + if (criteria.isActive()) { + query.where((builder, root) -> builder.notEqual(root.join("dmps").get("status"), DMP.DMPStatus.DELETED.getValue())).distinct(); + } + 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 8b69f372a..935bfe9de 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 @@ -30,6 +30,8 @@ public class ProjectDaoImpl extends DatabaseAccess implements ProjectDa 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.getPeriodStart() != null) + query.where((builder, root) -> builder.greaterThanOrEqualTo(root.get("startdate"), criteria.getPeriodStart())); 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 374fb5681..441ed9318 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 @@ -29,6 +29,8 @@ public class ResearcherDaoImpl extends DatabaseAccess implements Res query.where((builder, root) ->builder.or(builder.like(builder.lower(root.get("label")), "%" + criteria.getName().toLowerCase() + "%"))); if (criteria.getReference() != null && !criteria.getReference().isEmpty()) query.where((builder, root) ->builder.or(builder.like(root.get("reference"), criteria.getReference()))); + if (criteria.getPeriodStart() != null) + query.where((builder, root) -> builder.greaterThanOrEqualTo(root.get("created"), criteria.getPeriodStart())); return query; } diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/entities/Dataset.java b/dmp-backend/data/src/main/java/eu/eudat/data/entities/Dataset.java index 66c917cb0..41081cefb 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/entities/Dataset.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/entities/Dataset.java @@ -16,13 +16,14 @@ import java.util.stream.Collectors; @NamedEntityGraphs({ @NamedEntityGraph( name = "datasetListingModel", - attributeNodes = {@NamedAttributeNode("services"), @NamedAttributeNode(value = "datasetDataRepositories", subgraph = "datasetDataRepositories"), - @NamedAttributeNode(value = "datasetExternalDatasets", subgraph = "datasetExternalDatasets"), @NamedAttributeNode("registries"), - @NamedAttributeNode(value = "dmp", subgraph = "dmp"), @NamedAttributeNode("profile"), @NamedAttributeNode("creator")}, + attributeNodes = {/*@NamedAttributeNode("services"), @NamedAttributeNode(value = "datasetDataRepositories", subgraph = "datasetDataRepositories"), + @NamedAttributeNode(value = "datasetExternalDatasets", subgraph = "datasetExternalDatasets"), @NamedAttributeNode("registries"),*/ + @NamedAttributeNode(value = "dmp", subgraph = "dmp"), @NamedAttributeNode(value = "profile", subgraph = "profile"), @NamedAttributeNode("creator")}, subgraphs = { @NamedSubgraph(name = "dmp", attributeNodes = {@NamedAttributeNode("creator"), @NamedAttributeNode("users"), @NamedAttributeNode("grant"), @NamedAttributeNode("organisations")}), @NamedSubgraph(name = "datasetDataRepositories", attributeNodes = {@NamedAttributeNode("dataRepository")}), - @NamedSubgraph(name = "datasetExternalDatasets", attributeNodes = {@NamedAttributeNode("externalDataset")}) + @NamedSubgraph(name = "datasetExternalDatasets", attributeNodes = {@NamedAttributeNode("externalDataset")}), + @NamedSubgraph(name = "profile", attributeNodes = {@NamedAttributeNode("label")}) }), @NamedEntityGraph( diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/entities/Grant.java b/dmp-backend/data/src/main/java/eu/eudat/data/entities/Grant.java index 9809f0953..fd5c77164 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/entities/Grant.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/entities/Grant.java @@ -106,11 +106,11 @@ public class Grant implements DataEntity { @Column(name = "\"Definition\"", columnDefinition = "xml", nullable = true) private String definition; - @Column(name = "\"StartDate\"", nullable = false) + @Column(name = "\"StartDate\"", nullable = true) @Convert(converter = DateToUTCConverter.class) private Date startdate = null; - @Column(name = "\"EndDate\"", nullable = false) + @Column(name = "\"EndDate\"", nullable = true) @Convert(converter = DateToUTCConverter.class) private Date enddate = null; diff --git a/dmp-backend/data/src/main/java/eu/eudat/data/query/definition/helpers/ColumnOrderings.java b/dmp-backend/data/src/main/java/eu/eudat/data/query/definition/helpers/ColumnOrderings.java index 7ab879b59..808cd20e2 100644 --- a/dmp-backend/data/src/main/java/eu/eudat/data/query/definition/helpers/ColumnOrderings.java +++ b/dmp-backend/data/src/main/java/eu/eudat/data/query/definition/helpers/ColumnOrderings.java @@ -6,22 +6,22 @@ import java.util.List; public class ColumnOrderings { - private String[] fields; + private List fields; - public String[] getFields() { + public List getFields() { return fields; } - public void setFields(String[] fields) { + public void setFields(List fields) { this.fields = fields; } - public Ordering[] getFieldOrderings() { + public List getFieldOrderings() { List orderings = new LinkedList<>(); for (String field : fields) { orderings.add(this.orderingFromString(field)); } - return orderings.toArray(new Ordering[orderings.size()]); + return orderings; } private Ordering orderingFromString(String field) { diff --git a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/criteria/DatasetCriteria.java b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/criteria/DatasetCriteria.java index 705093384..b00fec7df 100644 --- a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/criteria/DatasetCriteria.java +++ b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/criteria/DatasetCriteria.java @@ -18,6 +18,7 @@ public class DatasetCriteria extends Criteria { private List collaborators; private Boolean allowAllVersions; private List organiztions; + private Boolean hasTags; private List tags; private boolean isPublic; private Short grantStatus; @@ -144,4 +145,12 @@ public class DatasetCriteria extends Criteria { public void setSortCriteria(List sortCriteria) { this.sortCriteria = sortCriteria; } + + public Boolean getHasTags() { + return hasTags; + } + + public void setHasTags(Boolean hasTags) { + this.hasTags = hasTags; + } } diff --git a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/entities/Dataset.java b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/entities/Dataset.java index c54088f18..c53f08d29 100644 --- a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/entities/Dataset.java +++ b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/entities/Dataset.java @@ -283,38 +283,46 @@ public class Dataset implements ElasticEntity { @Override public Dataset fromElasticEntity(Map fields) { if (fields != null) { - this.id = (String) fields.get("id"); - if (fields.get("tags") != null) { - this.tags = ((List) fields.get("tags")).stream().map(hashMap -> new Tag().fromElasticEntity(hashMap)).collect(Collectors.toList()); + if (fields.size() == 1) { + if (fields.containsKey("id")) { + this.id = (String) fields.get("id"); + } else if (fields.containsKey("tags")) { + this.tags = ((List) fields.get("tags")).stream().map(hashMap -> new Tag().fromElasticEntity(hashMap)).collect(Collectors.toList()); + } + }else if (fields.size() > 1) { + this.id = (String) fields.get("id"); + if (fields.get("tags") != null) { + this.tags = ((List) fields.get("tags")).stream().map(hashMap -> new Tag().fromElasticEntity(hashMap)).collect(Collectors.toList()); + } + this.label = (String) fields.get("label"); + this.description = (String) fields.get("description"); + this.template = UUID.fromString((String) fields.get("template")); + this.status = Short.valueOf((String) fields.get("status")); + this.dmp = UUID.fromString((String) fields.get("dmp")); + this.group = UUID.fromString((String) fields.get("group")); + this.grant = UUID.fromString((String) fields.get("grant")); + if (fields.get("created") != null) + this.created = Date.from(Instant.parse((String) fields.get("created"))); + if (fields.get("modified") != null) + this.modified = Date.from(Instant.parse((String) fields.get("modified"))); + if (fields.get("finalizedAt") != null) + this.finalizedAt = Date.from(Instant.parse((String) fields.get("finalizedAt"))); + if (fields.get("collaborators") != null) { + this.collaborators = ((List) fields.get("collaborators")).stream().map(hashMap -> new Collaborator().fromElasticEntity(hashMap)).collect(Collectors.toList()); + } + this.lastVersion = Boolean.parseBoolean((String) fields.get("lastVersion")); + this.lastPublicVersion = Boolean.parseBoolean((String) fields.get("lastPublicVersion")); + if (fields.get("organizations") != null) { + this.organizations = ((List) fields.get("organizations")).stream().map(hashMap -> new Organization().fromElasticEntity(hashMap)).collect(Collectors.toList()); + } + if (fields.get("public") != null) { + this.isPublic = Boolean.valueOf((String) fields.get("public")); + } + if (fields.get("grantStatus") != null) { + this.grantStatus = Short.valueOf((String) fields.get("grantStatus")); + } + this.formData = (String) fields.get("formData"); } - this.label = (String) fields.get("label"); - this.description = (String) fields.get("description"); - this.template = UUID.fromString((String) fields.get("template")); - this.status = Short.valueOf((String) fields.get("status")); - this.dmp = UUID.fromString((String) fields.get("dmp")); - this.group = UUID.fromString((String) fields.get("group")); - this.grant = UUID.fromString((String) fields.get("grant")); - if (fields.get("created") != null) - this.created = Date.from(Instant.parse((String) fields.get("created"))); - if (fields.get("modified") != null) - this.modified = Date.from(Instant.parse((String) fields.get("modified"))); - if (fields.get("finalizedAt") != null) - this.finalizedAt = Date.from(Instant.parse((String) fields.get("finalizedAt"))); - if (fields.get("collaborators") != null) { - this.collaborators = ((List) fields.get("collaborators")).stream().map(hashMap -> new Collaborator().fromElasticEntity(hashMap)).collect(Collectors.toList()); - } - this.lastVersion = Boolean.parseBoolean((String) fields.get("lastVersion")); - this.lastPublicVersion = Boolean.parseBoolean((String) fields.get("lastPublicVersion")); - if (fields.get("organizations") != null) { - this.organizations = ((List) fields.get("organizations")).stream().map(hashMap -> new Organization().fromElasticEntity(hashMap)).collect(Collectors.toList()); - } - if (fields.get("public") != null) { - this.isPublic = Boolean.valueOf((String) fields.get("public")); - } - if (fields.get("grantStatus") != null) { - this.grantStatus = Short.valueOf((String) fields.get("grantStatus")); - } - this.formData = (String) fields.get("formData"); } return this; } diff --git a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/entities/Dmp.java b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/entities/Dmp.java index 8faea3233..547c23e25 100644 --- a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/entities/Dmp.java +++ b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/entities/Dmp.java @@ -5,6 +5,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; +import java.time.Instant; import java.util.*; import java.util.stream.Collectors; @@ -286,31 +287,47 @@ public class Dmp implements ElasticEntity { @Override public Dmp fromElasticEntity(Map fields) { + if (fields == null || fields.isEmpty()) { + return null; + } this.id = UUID.fromString((String) fields.get(MapKey.ID.getName())); - this.label = (String) fields.get(MapKey.LABEL.getName()); - this.description = (String) fields.get(MapKey.DESCRIPTION.getName()); - if (fields.get(MapKey.GROUPID.getName()) != null) { - this.groupId = UUID.fromString((String) fields.get(MapKey.GROUPID.getName())); - } - this.status = Short.valueOf(fields.get(MapKey.STATUS.getName()).toString()); - if (fields.get(MapKey.TEMPLATES.getName()) != null) { - this.templates = ((List>) fields.get(MapKey.TEMPLATES.getName())).stream().map(hashMap -> new DatasetTempalate().fromElasticEntity(hashMap)).collect(Collectors.toList()); - } - if (fields.get(MapKey.COLLABORATORS.getName()) != null) { - this.collaborators = ((List>) fields.get(MapKey.COLLABORATORS.getName())).stream().map(map -> new Collaborator().fromElasticEntity(map)).collect(Collectors.toList()); - } - if (fields.get(MapKey.ORGANIZATIONS.getName()) != null) { - this.organizations = ((List>) fields.get(MapKey.ORGANIZATIONS.getName())).stream().map(map -> new Organization().fromElasticEntity(map)).collect(Collectors.toList()); - } - this.lastVersion = (Boolean) fields.get(MapKey.LASTVERSION.getName()); - this.lastPublicVersion = (Boolean) fields.get(MapKey.LASTPUBLICVERSION.getName()); - this.isPublic = (Boolean) fields.get(MapKey.ISPUBLIC.getName()); - if (fields.get(MapKey.DATASETS.getName()) != null) { - this.datasets = ((List>) fields.get(MapKey.DATASETS.getName())).stream().map(map -> new Dataset().fromElasticEntity(map)).collect(Collectors.toList()); - } - this.grant = UUID.fromString((String) fields.get(MapKey.GRANT.getName())); - if (fields.get(MapKey.GRANTSTATUS.getName()) != null) { - this.grantStatus = Short.valueOf(fields.get(MapKey.GRANTSTATUS.getName()).toString()); + if (fields.size() > 1) { + this.label = (String) fields.get(MapKey.LABEL.getName()); + this.description = (String) fields.get(MapKey.DESCRIPTION.getName()); + if (fields.get(MapKey.GROUPID.getName()) != null) { + this.groupId = UUID.fromString((String) fields.get(MapKey.GROUPID.getName())); + } + this.status = Short.valueOf(fields.get(MapKey.STATUS.getName()).toString()); + if (fields.get(MapKey.TEMPLATES.getName()) != null) { + this.templates = ((List>) fields.get(MapKey.TEMPLATES.getName())).stream().map(hashMap -> new DatasetTempalate().fromElasticEntity(hashMap)).collect(Collectors.toList()); + } + if (fields.get(MapKey.COLLABORATORS.getName()) != null) { + this.collaborators = ((List>) fields.get(MapKey.COLLABORATORS.getName())).stream().map(map -> new Collaborator().fromElasticEntity(map)).collect(Collectors.toList()); + } + if (fields.get(MapKey.ORGANIZATIONS.getName()) != null) { + this.organizations = ((List>) fields.get(MapKey.ORGANIZATIONS.getName())).stream().map(map -> new Organization().fromElasticEntity(map)).collect(Collectors.toList()); + } + this.lastVersion = (Boolean) fields.get(MapKey.LASTVERSION.getName()); + this.lastPublicVersion = (Boolean) fields.get(MapKey.LASTPUBLICVERSION.getName()); + this.isPublic = (Boolean) fields.get(MapKey.ISPUBLIC.getName()); + if (fields.get(MapKey.DATASETS.getName()) != null) { + this.datasets = ((List>) fields.get(MapKey.DATASETS.getName())).stream().map(map -> new Dataset().fromElasticEntity(map)).collect(Collectors.toList()); + } + this.grant = UUID.fromString((String) fields.get(MapKey.GRANT.getName())); + if (fields.get(MapKey.GRANTSTATUS.getName()) != null) { + this.grantStatus = Short.valueOf(fields.get(MapKey.GRANTSTATUS.getName()).toString()); + } + this.created = Date.from(Instant.parse(fields.get(MapKey.CREATED.getName()).toString())); + this.modified = Date.from(Instant.parse(fields.get(MapKey.MODIFIED.getName()).toString())); + if (fields.get(MapKey.FINALIZEDAT.getName()) != null) { + this.finalizedAt = Date.from(Instant.parse(fields.get(MapKey.FINALIZEDAT.getName()).toString())); + } + if (fields.get(MapKey.PUBLISHEDAT.getName()) != null) { + this.publishedAt = Date.from(Instant.parse(fields.get(MapKey.PUBLISHEDAT.getName()).toString())); + } + if (fields.get(MapKey.DOI.getName()) != null) { + this.doi = fields.get(MapKey.DOI.getName()).toString(); + } } return this; } 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 33ff68253..dcc158405 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 @@ -24,6 +24,7 @@ import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilters; import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder; import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.fetch.subphase.FetchSourceContext; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; @@ -54,24 +55,26 @@ public class DatasetRepository extends ElasticRepository()); + if (!found) { + if (dmp.getDatasets() == null) { + dmp.setDatasets(new ArrayList<>()); + } + dmp.getDatasets().add(entity); } - dmp.getDatasets().add(entity); + IndexRequest request = new IndexRequest(this.environment.getProperty("elasticsearch.index")).id(dmp.getId().toString()).source(dmp.toElasticEntity(builder));//new IndexRequest("datasets", "doc", entity.getId()).source(entity.toElasticEntity(builder)); + this.getClient().index(request, RequestOptions.DEFAULT); } - IndexRequest request = new IndexRequest(this.environment.getProperty("elasticsearch.index")).id(dmp.getId().toString()).source(dmp.toElasticEntity(builder));//new IndexRequest("datasets", "doc", entity.getId()).source(entity.toElasticEntity(builder)); - this.getClient().index(request, RequestOptions.DEFAULT); return entity; } return null; @@ -122,74 +125,15 @@ public class DatasetRepository extends ElasticRepository sortBuilders = new ArrayList<>(); - if (criteria.isPublic()) { - boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.public", "true")); - boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.status", Dataset.Status.FINALISED.getValue())); - boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.lastPublicVersion", "true")); - } - if (criteria.getLike() != null && !criteria.getLike().isEmpty()) { - boolQuery = boolQuery.should(QueryBuilders.queryStringQuery(criteria.getLike()).allowLeadingWildcard(true).fields(Stream.of(new Object[][]{ - {"datasets.label", 1.0f}, - {"datasets.description", 1.0f}, - {"datasets.formData", 1.0f} - }).collect(Collectors.toMap(data -> (String) data[0], data -> (Float) data[1])))); - } + BoolQueryBuilder boolQuery = createBoolQuery(criteria); - if (criteria.getDatasetTemplates() != null && criteria.getDatasetTemplates().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.template", criteria.getDatasetTemplates().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getStatus() != null) { - boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.status", criteria.getStatus().toString())); - } - - if (criteria.getDmps() != null && criteria.getDmps().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.dmp", criteria.getDmps().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getGroupIds() != null && criteria.getGroupIds().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.group", criteria.getGroupIds().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getGrants() != null && criteria.getGrants().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.grant", criteria.getGrants().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getGrantStatus() != null) { - boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.grantStatus", criteria.getGrantStatus().toString())); - } - - if (criteria.getCollaborators() != null && criteria.getCollaborators().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.collaborators.id", criteria.getCollaborators().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (!criteria.isPublic()) { - if (criteria.getAllowAllVersions() != null && !criteria.getAllowAllVersions()) { - boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.lastVersion", "true")); - } - } - - if (criteria.getOrganiztions() != null && criteria.getOrganiztions().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.organizations.id", criteria.getOrganiztions())); - } - - if (criteria.getTags() != null && criteria.getTags().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.tags.name", criteria.getTags().stream().map(Tag::getName).collect(Collectors.toList()))); - } - - if (boolQuery.should().isEmpty() && boolQuery.mustNot().isEmpty()) { - boolQuery.should(QueryBuilders.matchAllQuery()); - } else { - boolQuery.minimumShouldMatch(boolQuery.should().size()); - } if (criteria.getSortCriteria() != null && !criteria.getSortCriteria().isEmpty()) { criteria.getSortCriteria().forEach(sortCriteria -> { switch(sortCriteria.getColumnType()) { case COLUMN: - sortBuilders.add(SortBuilders.fieldSort(sortCriteria.getFieldName()).order(SortOrder.fromString(sortCriteria.getOrderByType().name()))); + sortBuilders.add(SortBuilders.fieldSort("datasets." + sortCriteria.getFieldName()).order(SortOrder.fromString(sortCriteria.getOrderByType().name()))); break; case JOIN_COLUMN: List fields = Arrays.asList(sortCriteria.getFieldName().split(":")); @@ -202,8 +146,11 @@ public class DatasetRepository extends ElasticRepository 0) { + searchSourceBuilder.size(criteria.getSize()); + }*/ sortBuilders.forEach(searchSourceBuilder::sort); searchRequest.source(searchSourceBuilder); SearchResponse response = this.getClient().search(searchRequest, RequestOptions.DEFAULT); @@ -215,6 +162,161 @@ public class DatasetRepository extends ElasticRepository queryIds(DatasetCriteria criteria) throws IOException { + if (this.getClient() != null) { + SearchRequest searchRequest = new SearchRequest(this.environment.getProperty("elasticsearch.index")); + SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); + + /*CountRequest countRequest = new CountRequest("dmps").routing("datasets").routing("id"); + countRequest.query(QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery("datasets.status.keyword", Stream.of(Dataset.Status.DELETED.getValue(), Dataset.Status.CANCELED.getValue()).collect(Collectors.toList())))); + CountResponse countResponse = getClient().count(countRequest, RequestOptions.DEFAULT); + Long count = countResponse.getCount();*/ + + SearchRequest countRequest = new SearchRequest(this.environment.getProperty("elasticsearch.index")); + NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("by_dataset", "datasets"); + FiltersAggregationBuilder filtersAggregationBuilder = AggregationBuilders.filters("dataset_query", QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery("datasets.status.keyword", Stream.of(Dataset.Status.DELETED.getValue(), Dataset.Status.CANCELED.getValue()).collect(Collectors.toList())))); + nestedAggregationBuilder.subAggregation(filtersAggregationBuilder); + SearchSourceBuilder countSourceBuilder = new SearchSourceBuilder(); + countSourceBuilder.aggregation(nestedAggregationBuilder); + countRequest.source(countSourceBuilder); + SearchResponse countResponse = getClient().search(countRequest, RequestOptions.DEFAULT); + Long count = ((ParsedFilters)((ParsedNested)countResponse.getAggregations().asMap().get("by_dataset")).getAggregations().get("dataset_query")).getBuckets().get(0).getDocCount(); + + + searchSourceBuilder.size(count.intValue()); + + List sortBuilders = new ArrayList<>(); + BoolQueryBuilder boolQuery = createBoolQuery(criteria); + + + if (criteria.getSortCriteria() != null && !criteria.getSortCriteria().isEmpty()) { + criteria.getSortCriteria().forEach(sortCriteria -> { + switch(sortCriteria.getColumnType()) { + case COLUMN: + sortBuilders.add(SortBuilders.fieldSort("datasets." + sortCriteria.getFieldName()).order(SortOrder.fromString(sortCriteria.getOrderByType().name()))); + break; + case JOIN_COLUMN: + List fields = Arrays.asList(sortCriteria.getFieldName().split(":")); + fields.stream().filter(name -> !name.startsWith("dmp")).forEach(field -> { + sortBuilders.add(SortBuilders.fieldSort(field).order(SortOrder.fromString(sortCriteria.getOrderByType().name()))); + }); + break; + } + }); + + } + + NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("datasets", boolQuery, ScoreMode.None).innerHit(new InnerHitBuilder().setFetchSourceContext(new FetchSourceContext(true, new String[]{"datasets.id"}, null))); + searchSourceBuilder.query(nestedQueryBuilder)/*.from(criteria.getOffset()).size(criteria.getSize())*/.fetchSource("datasets.id", null); + sortBuilders.forEach(searchSourceBuilder::sort); + searchRequest.source(searchSourceBuilder); + SearchResponse response = this.getClient().search(searchRequest, RequestOptions.DEFAULT); + return ((Stream)Arrays.stream(response.getHits().getHits()) + .map(hit -> hit.getInnerHits().values()).flatMap(Collection::stream) + .map(SearchHits::getHits).flatMap(Arrays::stream) + .map(x -> new Dataset().fromElasticEntity(this.transformFromString(x.getSourceAsString(), Map.class)))).collect(Collectors.toList()); + } + return null; + } + + @Override + public Long count(DatasetCriteria criteria) throws IOException { + if (this.getClient() != null) { + //CountRequest countRequest = new CountRequest(this.environment.getProperty("elasticsearch.index")); + + SearchRequest countRequest = new SearchRequest(this.environment.getProperty("elasticsearch.index")); + BoolQueryBuilder boolQuery = createBoolQuery(criteria); + NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("by_dataset", "datasets"); + FiltersAggregationBuilder filtersAggregationBuilder = AggregationBuilders.filters("dataset_query", boolQuery); + nestedAggregationBuilder.subAggregation(filtersAggregationBuilder); + SearchSourceBuilder countSourceBuilder = new SearchSourceBuilder(); + countSourceBuilder.aggregation(nestedAggregationBuilder); + countRequest.source(countSourceBuilder); + SearchResponse countResponse = getClient().search(countRequest, RequestOptions.DEFAULT); + return ((ParsedFilters)((ParsedNested)countResponse.getAggregations().asMap().get("by_dataset")).getAggregations().get("dataset_query")).getBuckets().get(0).getDocCount(); + + + + /*NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("datasets", boolQuery, ScoreMode.None).innerHit(new InnerHitBuilder()); + countRequest.query(nestedQueryBuilder); + CountResponse response = this.getClient().count(countRequest, RequestOptions.DEFAULT); + return response.getCount();*/ + } + return null; + } + + private BoolQueryBuilder createBoolQuery(DatasetCriteria criteria) { + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery("datasets.status", Stream.of(Dataset.Status.DELETED.getValue(), Dataset.Status.CANCELED.getValue()).collect(Collectors.toList()))); + if (criteria.isPublic()) { + boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.public", "true")); + boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.status", Dataset.Status.FINALISED.getValue())); + boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.lastPublicVersion", "true")); + } + if (criteria.getLike() != null && !criteria.getLike().isEmpty()) { + boolQuery = boolQuery.should(QueryBuilders.queryStringQuery(criteria.getLike()).allowLeadingWildcard(true).fields(Stream.of(new Object[][]{ + {"datasets.label", 1.0f}, + {"datasets.description", 1.0f}, + {"datasets.formData", 1.0f} + }).collect(Collectors.toMap(data -> (String) data[0], data -> (Float) data[1])))); + } + + if (criteria.getDatasetTemplates() != null && criteria.getDatasetTemplates().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.template", criteria.getDatasetTemplates().stream().map(UUID::toString).collect(Collectors.toList()))); + } + + if (criteria.getStatus() != null) { + boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.status", criteria.getStatus().toString())); + } + + if (criteria.getDmps() != null && criteria.getDmps().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.dmp", criteria.getDmps().stream().map(UUID::toString).collect(Collectors.toList()))); + } + + if (criteria.getGroupIds() != null && criteria.getGroupIds().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.group", criteria.getGroupIds().stream().map(UUID::toString).collect(Collectors.toList()))); + } + + if (criteria.getGrants() != null && criteria.getGrants().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.grant", criteria.getGrants().stream().map(UUID::toString).collect(Collectors.toList()))); + } + + if (criteria.getGrantStatus() != null) { + boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.grantStatus", criteria.getGrantStatus().toString())); + } + + if (criteria.getCollaborators() != null && criteria.getCollaborators().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.collaborators.id.keyword", criteria.getCollaborators().stream().map(UUID::toString).collect(Collectors.toList()))); + } + + if (!criteria.isPublic()) { + if (criteria.getAllowAllVersions() != null && !criteria.getAllowAllVersions()) { + boolQuery = boolQuery.should(QueryBuilders.termQuery("datasets.lastVersion", "true")); + } + } + + if (criteria.getOrganiztions() != null && criteria.getOrganiztions().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.organizations.id", criteria.getOrganiztions())); + } + + if (criteria.getTags() != null && criteria.getTags().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery("datasets.tags.name", criteria.getTags().stream().map(Tag::getName).collect(Collectors.toList()))); + } + + if (criteria.getHasTags() != null) { + boolQuery = criteria.getHasTags() == true ? boolQuery.should(QueryBuilders.existsQuery("datasets.tags.id")) : boolQuery.mustNot(QueryBuilders.existsQuery("datasets.tags.id")); + } + + if (boolQuery.should().isEmpty() && boolQuery.mustNot().isEmpty()) { + boolQuery.should(QueryBuilders.matchAllQuery()); + } else { + boolQuery.minimumShouldMatch(boolQuery.should().size()); + } + + + + return boolQuery; + } + @Override public boolean exists() throws IOException { if (this.getClient() != null) { diff --git a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/DmpRepository.java b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/DmpRepository.java index 9121a0027..6e18b0f88 100644 --- a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/DmpRepository.java +++ b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/DmpRepository.java @@ -99,60 +99,8 @@ public class DmpRepository extends ElasticRepository { searchSourceBuilder.size(count.intValue()); - BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery(Dmp.MapKey.STATUS.getName(), Collections.singletonList(Dmp.DMPStatus.DELETED.getValue()))); List sortBuilders = new ArrayList<>(); - if (criteria.isPublic()) { - boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.ISPUBLIC.getName(), true)); - boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.STATUS.getName(), Dmp.DMPStatus.FINALISED.getValue())); - } - if (criteria.getLike() != null && !criteria.getLike().isEmpty()) { - boolQuery = boolQuery.should(QueryBuilders.queryStringQuery(criteria.getLike()).fields(Stream.of(new Object[][]{ - {Dmp.MapKey.LABEL.getName(), 1.0f}, - {Dmp.MapKey.DESCRIPTION.getName(), 1.0f} - }).collect(Collectors.toMap(data -> (String) data[0], data -> (Float) data[1])))); - } - - if (criteria.getTemplates() != null && criteria.getTemplates().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.TEMPLATES.getName() + ".id.keyword", criteria.getTemplates().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getStatus() != null) { - boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.STATUS.getName(), criteria.getStatus().intValue())); - } - - if (criteria.getGroupIds() != null && criteria.getGroupIds().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.GROUPID.getName(), criteria.getGroupIds().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getGrants() != null && criteria.getGrants().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.GRANT.getName() + ".keyword", criteria.getGrants().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getCollaborators() != null && criteria.getCollaborators().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.COLLABORATORS.getName() + ".id.keyword", criteria.getCollaborators().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getRoles() != null && criteria.getRoles().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.COLLABORATORS.getName() + ".role.keyword", criteria.getRoles())); - } - - if (!criteria.isAllowAllVersions()) { - boolQuery = boolQuery.should(QueryBuilders.termQuery(criteria.isPublic() ? Dmp.MapKey.LASTPUBLICVERSION.getName() : Dmp.MapKey.LASTVERSION.getName(), true)); - } - - if (criteria.getOrganizations() != null && criteria.getOrganizations().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.ORGANIZATIONS.getName() + ".id.keyword", criteria.getOrganizations().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getGrantStatus() != null) { - boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.GRANTSTATUS.getName(), criteria.getGrantStatus())); - } - - if (boolQuery.should().isEmpty() && boolQuery.mustNot().isEmpty()) { - boolQuery = boolQuery.should(QueryBuilders.matchAllQuery()); - } else { - boolQuery.minimumShouldMatch(boolQuery.should().size()); - } + BoolQueryBuilder boolQuery = createBoolQuery(criteria); if (criteria.getSortCriteria() != null && !criteria.getSortCriteria().isEmpty()) { criteria.getSortCriteria().forEach(sortCriteria -> { @@ -170,8 +118,8 @@ public class DmpRepository extends ElasticRepository { }); } - searchSourceBuilder.query(boolQuery).from(criteria.getOffset()); - if (criteria.getSize() != null) { + searchSourceBuilder.query(boolQuery).from(criteria.getOffset()).fetchSource("id", null); + if (criteria.getSize() != null && criteria.getSize() > 0) { searchSourceBuilder.size(criteria.getSize()); } sortBuilders.forEach(searchSourceBuilder::sort); @@ -182,59 +130,12 @@ public class DmpRepository extends ElasticRepository { return null; } + @Override public Long count(DmpCriteria criteria) throws IOException { if (this.getClient() != null) { CountRequest countRequest = new CountRequest(this.environment.getProperty("elasticsearch.index")); - BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery(Dmp.MapKey.STATUS.getName(), Collections.singletonList(Dmp.DMPStatus.DELETED.getValue()))); - if (criteria.isPublic()) { - boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.ISPUBLIC.getName(), true)); - boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.STATUS.getName(), Dmp.DMPStatus.FINALISED.getValue())); - } - if (criteria.getLike() != null && !criteria.getLike().isEmpty()) { - boolQuery = boolQuery.should(QueryBuilders.queryStringQuery(criteria.getLike()).fields(Stream.of(new Object[][]{ - {Dmp.MapKey.LABEL.getName(), 1.0f}, - {Dmp.MapKey.DESCRIPTION.getName(), 1.0f} - }).collect(Collectors.toMap(data -> (String) data[0], data -> (Float) data[1])))); - } - - if (criteria.getTemplates() != null && criteria.getTemplates().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.TEMPLATES.getName() + ".id.keyword", criteria.getTemplates().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getStatus() != null) { - boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.STATUS.getName(), criteria.getStatus().intValue())); - } - - if (criteria.getGroupIds() != null && criteria.getGroupIds().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.GROUPID.getName(), criteria.getGroupIds().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getGrants() != null && criteria.getGrants().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.GRANT.getName() + ".keyword", criteria.getGrants().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getCollaborators() != null && criteria.getCollaborators().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.COLLABORATORS.getName() + ".id.keyword", criteria.getCollaborators().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (!criteria.isAllowAllVersions()) { - boolQuery = boolQuery.should(QueryBuilders.termQuery(criteria.isPublic() ? Dmp.MapKey.LASTPUBLICVERSION.getName() : Dmp.MapKey.LASTVERSION.getName(), true)); - } - - if (criteria.getOrganizations() != null && criteria.getOrganizations().size() > 0) { - boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.ORGANIZATIONS.getName() + ".id.keyword", criteria.getOrganizations().stream().map(UUID::toString).collect(Collectors.toList()))); - } - - if (criteria.getGrantStatus() != null) { - boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.GRANTSTATUS.getName(), criteria.getGrantStatus())); - } - - if (boolQuery.should().isEmpty() && boolQuery.mustNot().isEmpty()) { - boolQuery = boolQuery.should(QueryBuilders.matchAllQuery()); - } else { - boolQuery.minimumShouldMatch(boolQuery.should().size()); - } + BoolQueryBuilder boolQuery = createBoolQuery(criteria); countRequest.query(boolQuery); CountResponse response = this.getClient().count(countRequest, RequestOptions.DEFAULT); @@ -243,6 +144,59 @@ public class DmpRepository extends ElasticRepository { return null; } + private BoolQueryBuilder createBoolQuery(DmpCriteria criteria) { + BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery(Dmp.MapKey.STATUS.getName(), Collections.singletonList(Dmp.DMPStatus.DELETED.getValue()))); + if (criteria.isPublic()) { + boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.ISPUBLIC.getName(), true)); + boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.STATUS.getName(), Dmp.DMPStatus.FINALISED.getValue())); + } + if (criteria.getLike() != null && !criteria.getLike().isEmpty()) { + boolQuery = boolQuery.should(QueryBuilders.queryStringQuery(criteria.getLike()).fields(Stream.of(new Object[][]{ + {Dmp.MapKey.LABEL.getName(), 1.0f}, + {Dmp.MapKey.DESCRIPTION.getName(), 1.0f} + }).collect(Collectors.toMap(data -> (String) data[0], data -> (Float) data[1])))); + } + + if (criteria.getTemplates() != null && criteria.getTemplates().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.TEMPLATES.getName() + ".id.keyword", criteria.getTemplates().stream().map(UUID::toString).collect(Collectors.toList()))); + } + + if (criteria.getStatus() != null) { + boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.STATUS.getName(), criteria.getStatus().intValue())); + } + + if (criteria.getGroupIds() != null && criteria.getGroupIds().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.GROUPID.getName(), criteria.getGroupIds().stream().map(UUID::toString).collect(Collectors.toList()))); + } + + if (criteria.getGrants() != null && criteria.getGrants().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.GRANT.getName() + ".keyword", criteria.getGrants().stream().map(UUID::toString).collect(Collectors.toList()))); + } + + if (criteria.getCollaborators() != null && criteria.getCollaborators().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.COLLABORATORS.getName() + ".id.keyword", criteria.getCollaborators().stream().filter(Objects::nonNull).map(UUID::toString).collect(Collectors.toList()))); + } + + if (!criteria.isAllowAllVersions()) { + boolQuery = boolQuery.should(QueryBuilders.termQuery(criteria.isPublic() ? Dmp.MapKey.LASTPUBLICVERSION.getName() : Dmp.MapKey.LASTVERSION.getName(), true)); + } + + if (criteria.getOrganizations() != null && criteria.getOrganizations().size() > 0) { + boolQuery = boolQuery.should(QueryBuilders.termsQuery(Dmp.MapKey.ORGANIZATIONS.getName() + ".id.keyword", criteria.getOrganizations().stream().map(UUID::toString).collect(Collectors.toList()))); + } + + if (criteria.getGrantStatus() != null) { + boolQuery = boolQuery.should(QueryBuilders.termQuery(Dmp.MapKey.GRANTSTATUS.getName(), criteria.getGrantStatus())); + } + + if (boolQuery.should().isEmpty() && boolQuery.mustNot().isEmpty()) { + boolQuery = boolQuery.should(QueryBuilders.matchAllQuery()); + } else { + boolQuery.minimumShouldMatch(boolQuery.should().size()); + } + return boolQuery; + } + public boolean createIndex() { try { if (!this.exists()) { diff --git a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/Repository.java b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/Repository.java index 36d7be4a2..71cf4c209 100644 --- a/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/Repository.java +++ b/dmp-backend/elastic/src/main/java/eu/eudat/elastic/repository/Repository.java @@ -18,6 +18,8 @@ public interface Repository { List query(C criteria) throws ExecutionException, InterruptedException, IOException; + Long count(C criteria) throws ExecutionException, InterruptedException, IOException; + boolean exists() throws IOException; void clear() throws IOException; diff --git a/dmp-backend/pom.xml b/dmp-backend/pom.xml index 254577ace..914c54b41 100644 --- a/dmp-backend/pom.xml +++ b/dmp-backend/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-starter-parent - 1.5.9.RELEASE + 2.5.2 @@ -27,25 +27,26 @@ 1.8 0.0.1-SNAPSHOT - 4.3.8.RELEASE + 5.3.8 - 4.2.3.RELEASE - 1.19.1 + 5.3.10.RELEASE + + + - 5.2.11.Final + 5.5.3.Final 1.9 4.11 1.2.17 1.7.12 - 9.0.7.v20131107 - + 1.2.3 - 1 - 3.0.1 - ikalyvas + + @@ -58,7 +59,7 @@ org.springframework spring-context-support - 4.0.0.RELEASE + ${org.springframework.version} org.springframework.boot @@ -139,7 +140,7 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml - 2.9.3 + 2.12.3 @@ -147,13 +148,13 @@ com.fasterxml.jackson.core jackson-core - 2.9.4 + 2.12.3 com.fasterxml.jackson.core jackson-databind - 2.9.4 + 2.12.3 @@ -186,6 +187,12 @@ fop 2.3 + + + org.jsoup + jsoup + 1.14.3 + fr.opensagres.xdocreport @@ -257,36 +264,53 @@ 1.3.1 + + + javax.validation + validation-api + 2.0.1.Final + + + io.prometheus simpleclient - 0.10.0 + 0.11.0 io.prometheus simpleclient_hotspot - 0.10.0 + 0.11.0 io.prometheus simpleclient_httpserver - 0.10.0 + 0.11.0 io.prometheus simpleclient_pushgateway - 0.10.0 + 0.11.0 - + - io.prometheus - simpleclient_spring_boot - 0.10.0 + io.micrometer + micrometer-registry-prometheus + 1.7.1 + + + org.springframework.boot + spring-boot-starter-actuator + + + + io.micrometer + micrometer-core diff --git a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/QueryableList.java b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/QueryableList.java index f11c15876..ac457b28a 100644 --- a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/QueryableList.java +++ b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/QueryableList.java @@ -44,6 +44,8 @@ public interface QueryableList { QueryableList orderBy(OrderByPredicate predicate); + QueryableList groupBy(GroupByPredicate predicate); + QueryableList withHint(String hint); Long count(); diff --git a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/collector/Collector.java b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/collector/Collector.java index 5de816bab..a6d710b11 100644 --- a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/collector/Collector.java +++ b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/collector/Collector.java @@ -1,6 +1,9 @@ package eu.eudat.queryable.collector; +import com.fasterxml.jackson.databind.ObjectMapper; + import javax.persistence.Tuple; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -9,9 +12,13 @@ import java.util.stream.Collectors; public class Collector { public List buildFromTuple(List results, Map> groupedResults, List fields, String key) { - return groupedResults.keySet().stream() + return results.stream().map(tuple -> { + Map parsedResult = new HashMap<>(); + tuple.getElements().forEach(tupleElement -> parsedResult.put(tupleElement.getAlias(), tuple.get(tupleElement.getAlias()))); + return parsedResult; + }).collect(Collectors.toList());/*groupedResults.keySet().stream() .map(x -> buildOne(groupedResults.get(x), fields, key)) - .collect(Collectors.toList()); + .collect(Collectors.toList());*/ } private Map buildOne(List tuples, List fields, String key) { diff --git a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/hibernatequeryablelist/QueryableHibernateList.java b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/hibernatequeryablelist/QueryableHibernateList.java index 9c461f295..75cad534f 100644 --- a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/hibernatequeryablelist/QueryableHibernateList.java +++ b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/hibernatequeryablelist/QueryableHibernateList.java @@ -1,5 +1,6 @@ package eu.eudat.queryable.jpa.hibernatequeryablelist; +import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.queryable.QueryableList; import eu.eudat.queryable.collector.Collector; import eu.eudat.queryable.exceptions.NotSingleResultException; @@ -33,6 +34,7 @@ public class QueryableHibernateList implements QueryableLi private List> nestedPredicates = new LinkedList<>(); private boolean distinct = false; private List> orderings = new LinkedList<>(); + private List> groupings = new LinkedList<>(); private List fields = new LinkedList<>(); private Integer length; private Integer offset; @@ -150,6 +152,11 @@ public class QueryableHibernateList implements QueryableLi return this; } + public QueryableList groupBy(GroupByPredicate predicate) { + this.groupings.add(predicate); + return this; + } + public Long count() { CriteriaBuilder criteriaBuilder = this.manager.getCriteriaBuilder(); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Long.class); @@ -157,8 +164,16 @@ public class QueryableHibernateList implements QueryableLi if (distinct) criteriaQuery.select(criteriaBuilder.countDistinct(this.root.get("id"))); else criteriaQuery.select(criteriaBuilder.count(this.root)); criteriaQuery.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); + if (!this.groupings.isEmpty()) criteriaQuery.groupBy(this.generateGroupPredicates(this.groupings, this.root)); //if (distinct) criteriaQuery.distinct(true); - return this.manager.createQuery(criteriaQuery).getSingleResult(); + + //GK: Group By special case. When group by is used, since it will result in a list of how many elements have the grouped field common + // then it will instead return the number of the distinct values of the grouped field + if (this.groupings.isEmpty()) { + return this.manager.createQuery(criteriaQuery).getSingleResult(); + } else { + return (long) this.manager.createQuery(criteriaQuery).getResultList().size(); + } } @Async @@ -169,8 +184,15 @@ public class QueryableHibernateList implements QueryableLi if (distinct) criteriaQuery.select(criteriaBuilder.countDistinct(this.root.get("id"))); else criteriaQuery.select(criteriaBuilder.count(this.root)); criteriaQuery.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); + if (!this.groupings.isEmpty()) criteriaQuery.groupBy(this.generateGroupPredicates(this.groupings, this.root)); //if (distinct) criteriaQuery.distinct(true); - return CompletableFuture.supplyAsync(() -> this.manager.createQuery(criteriaQuery).getSingleResult()); + return CompletableFuture.supplyAsync(() -> { + if (this.groupings.isEmpty()) { + return this.manager.createQuery(criteriaQuery).getSingleResult(); + } else { + return (long) this.manager.createQuery(criteriaQuery).getResultList().size(); + } + }); } @@ -205,6 +227,14 @@ public class QueryableHibernateList implements QueryableLi return predicates.toArray(new Order[predicates.size()]); } + private Expression[] generateGroupPredicates(List> groupByPredicates, Root root) { + List predicates = new LinkedList<>(); + for (GroupByPredicate groupPredicate : groupByPredicates) { + predicates.add(groupPredicate.applyPredicate(this.manager.getCriteriaBuilder(), root)); + } + return predicates.toArray(new Expression[predicates.size()]); + } + public List toList() { CriteriaBuilder builder = this.manager.getCriteriaBuilder(); if (!this.fields.isEmpty()) this.query = builder.createTupleQuery(); @@ -212,22 +242,20 @@ public class QueryableHibernateList implements QueryableLi this.root = this.query.from(this.tClass); this.query.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); if (!this.orderings.isEmpty()) this.query.orderBy(this.generateOrderPredicates(this.orderings, this.root)); + if (!this.groupings.isEmpty()) this.query.groupBy(this.generateGroupPredicates(this.groupings, this.root)); if (!this.fields.isEmpty()) this.selectFields(); if (distinct) this.query.distinct(true); - //if (!this.fields.isEmpty()) return this.toListWithFields(); + //if (!this.fields.isEmpty()) this.query.multiselect(this.parseFields(this.fields)); + ObjectMapper mapper = new ObjectMapper(); + if (!this.fields.isEmpty()) return this.toListWithFields().stream().map(m -> mapper.convertValue(m, this.tClass)).collect(Collectors.toList()); return this.toListWithOutFields(); } public List toListWithFields() { - CriteriaBuilder builder = this.manager.getCriteriaBuilder(); - if (!this.fields.isEmpty()) this.query = builder.createTupleQuery(); - else this.query = builder.createQuery(this.tClass); - this.root = this.query.from(this.tClass); - this.query.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); - if (!this.orderings.isEmpty()) this.query.orderBy(this.generateOrderPredicates(this.orderings, this.root)); - if (!this.fields.isEmpty()) this.selectFields(); - if (distinct) this.query.distinct(true); - List results = this.manager.createQuery(query).getResultList(); + TypedQuery typedQuery = this.manager.createQuery(this.query); + if (this.offset != null) typedQuery.setFirstResult(this.offset); + if (this.length != null) typedQuery.setMaxResults(this.length); + List results = typedQuery.getResultList(); Map> groupedResults = results.stream() .collect(Collectors.groupingBy(x -> x.get("id"))); return this.collector.buildFromTuple(results, groupedResults, this.fields, "id"); @@ -252,6 +280,7 @@ public class QueryableHibernateList implements QueryableLi this.root = this.query.from(this.tClass); this.query.where(this.generateWherePredicates(this.singlePredicates, this.root, this.nestedPredicates, this.nestedQueryRoot)); if (!this.orderings.isEmpty()) this.query.orderBy(this.generateOrderPredicates(this.orderings, this.root)); + if (!this.groupings.isEmpty()) this.query.groupBy(this.generateGroupPredicates(this.groupings, this.root)); if (!this.fields.isEmpty()) this.selectFields(); if (distinct) this.query.distinct(true); if (!this.fields.isEmpty()) return this.toListAsyncWithFields(); @@ -356,6 +385,8 @@ public class QueryableHibernateList implements QueryableLi if (!this.orderings.isEmpty()) criteriaQuery.orderBy(this.generateOrderPredicates(this.orderings, criteriaRoot)); + if (!this.groupings.isEmpty()) criteriaQuery.groupBy(this.generateGroupPredicates(this.groupings, this.root)); + TypedQuery typedQuery = this.manager.createQuery(criteriaQuery); typedQuery.setHint("javax.persistence.fetchgraph", this.manager.getEntityGraph(this.hint)); return typedQuery; @@ -466,4 +497,11 @@ public class QueryableHibernateList implements QueryableLi .createQuery(update) .executeUpdate(); } + + private Path[] parseFields(List selectedFields) { + List paths = new ArrayList<>(); + selectedFields.forEach(s -> paths.add(root.get(s))); + + return paths.toArray(new Path[0]); + } } diff --git a/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/predicates/GroupByPredicate.java b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/predicates/GroupByPredicate.java new file mode 100644 index 000000000..849878e4e --- /dev/null +++ b/dmp-backend/queryable/src/main/java/eu/eudat/queryable/jpa/predicates/GroupByPredicate.java @@ -0,0 +1,10 @@ +package eu.eudat.queryable.jpa.predicates; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.Root; + +public interface GroupByPredicate { + Expression applyPredicate(CriteriaBuilder builder, Root root); + +} diff --git a/dmp-backend/web/pom.xml b/dmp-backend/web/pom.xml index d3cfd31e6..87df79bc1 100644 --- a/dmp-backend/web/pom.xml +++ b/dmp-backend/web/pom.xml @@ -50,31 +50,37 @@ com.fasterxml.jackson.core jackson-annotations - 2.9.0 + 2.12.3 - + - + + + com.github.ben-manes.caffeine + caffeine + 2.9.0 + + - junit - junit - 4.12 + org.junit.jupiter + junit-jupiter-api + 5.7.2 test @@ -87,7 +93,7 @@ io.springfox springfox-swagger-ui - 2.7.0 + 3.0.0 @@ -101,7 +107,7 @@ org.springframework.social spring-social-linkedin - 1.0.0.RELEASE + 1.0.2.RELEASE diff --git a/dmp-backend/web/src/main/java/eu/eudat/EuDatApplication.java b/dmp-backend/web/src/main/java/eu/eudat/EuDatApplication.java index d9c2b65d6..31e87ddf5 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/EuDatApplication.java +++ b/dmp-backend/web/src/main/java/eu/eudat/EuDatApplication.java @@ -5,7 +5,7 @@ import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.web.support.SpringBootServletInitializer; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication diff --git a/dmp-backend/web/src/main/java/eu/eudat/cache/ExternalUrlsKeyGenerator.java b/dmp-backend/web/src/main/java/eu/eudat/cache/ExternalUrlsKeyGenerator.java new file mode 100644 index 000000000..544b8ffad --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/cache/ExternalUrlsKeyGenerator.java @@ -0,0 +1,22 @@ +package eu.eudat.cache; + +import eu.eudat.logic.proxy.config.ExternalUrlCriteria; +import org.springframework.cache.interceptor.KeyGenerator; + +import java.lang.reflect.Method; + +public class ExternalUrlsKeyGenerator implements KeyGenerator { + @Override + public Object generate(Object o, Method method, Object... params) { + StringBuffer stringBuffer = new StringBuffer(); + stringBuffer.append(o.getClass().getSimpleName()).append("_"); + stringBuffer.append(method.getName()).append("_"); + for (Object param: params) { + if (param instanceof ExternalUrlCriteria) { + ExternalUrlCriteria externalUrlCriteria = (ExternalUrlCriteria) param; + stringBuffer.append(externalUrlCriteria); + } + } + return stringBuffer.toString(); + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/cache/ResponsesCache.java b/dmp-backend/web/src/main/java/eu/eudat/cache/ResponsesCache.java index 0a09a6731..80da105bc 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/cache/ResponsesCache.java +++ b/dmp-backend/web/src/main/java/eu/eudat/cache/ResponsesCache.java @@ -1,12 +1,13 @@ package eu.eudat.cache; -import com.google.common.cache.CacheBuilder; +import com.github.benmanes.caffeine.cache.Caffeine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.guava.GuavaCache; +import org.springframework.cache.caffeine.CaffeineCache; +import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.cache.support.SimpleCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; @@ -29,22 +30,27 @@ public class ResponsesCache { public CacheManager cacheManager() { logger.info("Loading ResponsesCache..."); SimpleCacheManager simpleCacheManager = new SimpleCacheManager(); - List caches = new ArrayList(); - caches.add(new GuavaCache("repositories", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("grants", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("projects", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("funders", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("organisations", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("registries", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("services", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("tags", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("researchers", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("externalDatasets", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("currencies", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); - caches.add(new GuavaCache("licenses", CacheBuilder.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + List caches = new ArrayList(); + caches.add(new CaffeineCache("repositories", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("grants", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("projects", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("funders", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("organisations", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("registries", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("services", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("tags", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("researchers", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("externalDatasets", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("currencies", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); + caches.add(new CaffeineCache("licenses", Caffeine.newBuilder().expireAfterAccess(HOW_MANY, TIME_UNIT).build())); simpleCacheManager.setCaches(caches); logger.info("OK"); return simpleCacheManager; } + @Bean(name = "externalUrlsKeyGenerator") + private KeyGenerator externalUrlsKeyGenerator() { + return new ExternalUrlsKeyGenerator(); + } + } diff --git a/dmp-backend/web/src/main/java/eu/eudat/configurations/DevelDatabaseConfiguration.java b/dmp-backend/web/src/main/java/eu/eudat/configurations/DevelDatabaseConfiguration.java index 1c5c48bed..a1311f1f1 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/configurations/DevelDatabaseConfiguration.java +++ b/dmp-backend/web/src/main/java/eu/eudat/configurations/DevelDatabaseConfiguration.java @@ -2,8 +2,8 @@ package eu.eudat.configurations; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.*; import org.springframework.core.env.Environment; import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor; 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 2c64aa8d1..36d9c502c 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,7 +4,6 @@ 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; @@ -18,7 +17,6 @@ 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 6a89b4429..e27a21053 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 @@ -20,6 +20,7 @@ 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; +import eu.eudat.types.MetricNames; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -44,13 +45,15 @@ public class Admin extends BaseController { private DatasetProfileManager datasetProfileManager; private UserManager userManager; private ConfigLoader configLoader; + private final MetricsManager metricsManager; @Autowired - public Admin(ApiContext apiContext, DatasetProfileManager datasetProfileManager, UserManager userManager/*, Logger logger*/, ConfigLoader configLoader) { + public Admin(ApiContext apiContext, DatasetProfileManager datasetProfileManager, UserManager userManager/*, Logger logger*/, ConfigLoader configLoader, MetricsManager metricsManager) { super(apiContext); this.datasetProfileManager = datasetProfileManager; this.userManager = userManager; this.configLoader = configLoader; + this.metricsManager = metricsManager; } @Transactional @@ -70,7 +73,7 @@ public class Admin extends BaseController { userDatasetProfile.setRole(0); getApiContext().getOperationsContext().getDatabaseRepository().getUserDatasetProfileDao().createOrUpdate(userDatasetProfile); datasetProfileManager.storeDatasetProfileUsers(datasetProfile, profile); - MetricsManager.increaseValue("argos_dataset_templates", 1, "total"); + metricsManager.increaseValue(MetricNames.DATASET_TEMPLATE, 1, MetricNames.TOTAL); return ResponseEntity.status(HttpStatus.OK).body(modelDefinition.getId()); } @@ -89,7 +92,7 @@ public class Admin extends BaseController { 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"); + metricsManager.increaseValue(MetricNames.DATASET_TEMPLATE, 1, MetricNames.ACTIVE); } return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.NO_MESSAGE)); } 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 b01dc68c9..73b596822 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 @@ -20,6 +20,7 @@ import eu.eudat.logic.security.claims.ClaimedAuthorities; import eu.eudat.logic.services.ApiContext; import eu.eudat.logic.services.operations.DatabaseRepository; import eu.eudat.logic.utilities.documents.helpers.FileEnvelope; +import eu.eudat.logic.utilities.documents.pdf.PDFUtils; import eu.eudat.models.data.datasetprofile.DatasetProfileListingModel; import eu.eudat.models.data.datasetwizard.DatasetsToBeFinalized; import eu.eudat.models.data.dmp.DataManagementPlan; @@ -69,17 +70,15 @@ public class DMPs extends BaseController { private DynamicGrantConfiguration dynamicGrantConfiguration; private Environment environment; private DataManagementPlanManager dataManagementPlanManager; - private DatasetManager datasetManager; private ConfigLoader configLoader; @Autowired public DMPs(ApiContext apiContext, DynamicGrantConfiguration dynamicGrantConfiguration, Environment environment, - DataManagementPlanManager dataManagementPlanManager, DatasetManager datasetManager, ConfigLoader configLoader) { + DataManagementPlanManager dataManagementPlanManager, ConfigLoader configLoader) { super(apiContext); this.dynamicGrantConfiguration = dynamicGrantConfiguration; this.environment = environment; this.dataManagementPlanManager = dataManagementPlanManager; - this.datasetManager = datasetManager; this.configLoader = configLoader; } @@ -175,7 +174,7 @@ public class DMPs extends BaseController { public @ResponseBody ResponseEntity getRDAJsonDocument(@PathVariable String id, @ClaimedAuthorities(claims = {Authorities.ADMIN, Authorities.MANAGER, Authorities.USER, Authorities.ANONYMOUS}) Principal principal) { try { - return this.dataManagementPlanManager.getRDAJsonDocument(id, datasetManager, principal); + return this.dataManagementPlanManager.getRDAJsonDocument(id, principal); } catch (Exception e) { return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseItem<>().message(e.getMessage()).status(ApiMessageCode.ERROR_MESSAGE)); } @@ -186,8 +185,8 @@ public class DMPs extends BaseController { ResponseEntity getPDFDocument(@PathVariable String id, @RequestHeader("Content-Type") String contentType, @ClaimedAuthorities(claims = {Authorities.ADMIN, Authorities.MANAGER, Authorities.USER, Authorities.ANONYMOUS}) Principal principal) throws IllegalAccessException, IOException, InstantiationException, InterruptedException { FileEnvelope file = this.dataManagementPlanManager.getWordDocument(id, principal, configLoader); - String name = file.getFilename().substring(0, file.getFilename().length() - 5); - File pdffile = datasetManager.convertToPDF(file, environment); + String name = file.getFilename().substring(0, file.getFilename().length() - 5).replace(" ", "_").replace(",", "_"); + File pdffile = PDFUtils.convertToPDF(file, environment); InputStream resource = new FileInputStream(pdffile); logger.info("Mime Type of " + name + " is " + new MimetypesFileTypeMap().getContentType(file.getFile())); diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/DashBoardController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/DashBoardController.java index 0e3e7d936..6041f865c 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/DashBoardController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/DashBoardController.java @@ -54,6 +54,7 @@ public class DashBoardController extends BaseController { return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().status(ApiMessageCode.NO_MESSAGE).payload(statistics)); } + @Deprecated @RequestMapping(method = RequestMethod.GET, value = {"/user/recentActivity"}, produces = "application/json") public ResponseEntity> getRecentActivity(@RequestParam(name = "numOfActivities", required = false, defaultValue = "5") Integer numberOfActivities, Principal principal) { RecentActivity statistics = dashBoardManager.getRecentActivity(principal, numberOfActivities); 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 d79ec0a64..c1ffd67f1 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 @@ -16,6 +16,7 @@ import eu.eudat.logic.security.claims.ClaimedAuthorities; import eu.eudat.logic.services.ApiContext; import eu.eudat.logic.services.forms.VisibilityRuleService; import eu.eudat.logic.utilities.documents.helpers.FileEnvelope; +import eu.eudat.logic.utilities.documents.pdf.PDFUtils; import eu.eudat.models.data.dataset.DatasetOverviewModel; import eu.eudat.models.data.datasetprofile.DatasetProfileListingModel; import eu.eudat.models.data.datasetwizard.DataManagentPlanListingModel; @@ -38,11 +39,11 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.persistence.NoResultException; +import javax.transaction.Transactional; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -120,7 +121,7 @@ public class Datasets extends BaseController { // } } - @javax.transaction.Transactional + @Transactional @RequestMapping(method = RequestMethod.GET, value = {"{id}"}, produces = "application/json") public @ResponseBody ResponseEntity getSingle(@PathVariable String id, @RequestHeader("Content-Type") String contentType, @ClaimedAuthorities(claims = {Authorities.ADMIN, Authorities.MANAGER, Authorities.USER, Authorities.ANONYMOUS}) Principal principal) throws IllegalAccessException, IOException, InstantiationException { @@ -134,7 +135,8 @@ public class Datasets extends BaseController { HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentLength(file.getFile().length()); responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); - responseHeaders.set("Content-Disposition", "attachment;filename=" + file.getFilename()); + String fileName = file.getFilename().replace(" ", "_").replace(",", "_"); + responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName); responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition"); responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type"); @@ -177,7 +179,7 @@ public class Datasets extends BaseController { @RequestMapping(method = RequestMethod.POST, value = {"/getAvailableProfiles"}, produces = "application/json") public @ResponseBody ResponseEntity>> getAvailableProfiles(@RequestBody DatasetProfileWizardAutocompleteRequest datasetProfileWizardAutocompleteRequest, @ClaimedAuthorities(claims = {ANONYMOUS}) Principal principal) throws IllegalAccessException, InstantiationException { - List dataManagementPlans = DatasetWizardManager.getAvailableProfiles(this.getApiContext().getOperationsContext().getDatabaseRepository().getDmpDao(), datasetProfileWizardAutocompleteRequest); + List dataManagementPlans = DatasetWizardManager.getAvailableProfiles(this.getApiContext().getOperationsContext().getDatabaseRepository().getDmpDao(), this.getApiContext().getOperationsContext().getDatabaseRepository().getDatasetProfileDao(), datasetProfileWizardAutocompleteRequest); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().status(ApiMessageCode.NO_MESSAGE).payload(dataManagementPlans)); } @@ -217,11 +219,11 @@ public class Datasets extends BaseController { public @ResponseBody ResponseEntity getPDFDocument(@PathVariable String id, @ClaimedAuthorities(claims = {Authorities.ADMIN, Authorities.MANAGER, Authorities.USER, Authorities.ANONYMOUS}) Principal principal) throws IllegalAccessException, IOException, InstantiationException, InterruptedException { FileEnvelope file = datasetManager.getWordDocumentFile(this.configLoader, id, this.getApiContext().getUtilitiesService().getVisibilityRuleService(), principal); - String fileName = file.getFilename(); + String fileName = file.getFilename().replace(" ", "_").replace(",", "_"); if (fileName.endsWith(".docx")){ fileName = fileName.substring(0, fileName.length() - 5); } - File pdffile = datasetManager.convertToPDF(file, environment); + File pdffile = PDFUtils.convertToPDF(file, environment); InputStream resource = new FileInputStream(pdffile); HttpHeaders responseHeaders = new HttpHeaders(); @@ -244,7 +246,7 @@ public class Datasets extends BaseController { * Data Management * */ - @javax.transaction.Transactional + @Transactional @RequestMapping(method = RequestMethod.POST, consumes = "application/json", produces = "application/json") public @ResponseBody ResponseEntity> createOrUpdate(@RequestBody DatasetWizardModel profile, Principal principal) throws Exception { @@ -260,7 +262,7 @@ public class Datasets extends BaseController { return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message(this.getApiContext().getHelpersService().getMessageSource().getMessage("dataset.public", new Object[]{}, locale))); } - @javax.transaction.Transactional + @Transactional @RequestMapping(method = RequestMethod.DELETE, value = {"/delete/{id}"}, produces = "application/json") public @ResponseBody ResponseEntity> delete(@PathVariable(value = "id") UUID id, Principal principal) throws Exception { @@ -268,7 +270,7 @@ public class Datasets extends BaseController { return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message("Deleted")); } - @javax.transaction.Transactional + @Transactional @RequestMapping(method = RequestMethod.GET, value = {"/{id}/unlock"}, produces = "application/json") public @ResponseBody ResponseEntity> unlock(@PathVariable(value = "id") UUID id, Principal principal) throws Exception { @@ -316,7 +318,7 @@ public class Datasets extends BaseController { * Data Index * */ - @javax.transaction.Transactional + @Transactional @RequestMapping(method = RequestMethod.POST, value = {"/index"}) public @ResponseBody ResponseEntity> generateIndex(Principal principal) throws Exception { @@ -324,7 +326,7 @@ public class Datasets extends BaseController { return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).message("Generated").payload(null)); } - @javax.transaction.Transactional + @Transactional @RequestMapping(method = RequestMethod.DELETE, value = {"/index"}) public @ResponseBody ResponseEntity> clearIndex(Principal principal) throws Exception { 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 ac75578d9..4821a2b8a 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 @@ -35,6 +35,7 @@ import eu.eudat.models.data.login.LoginInfo; import eu.eudat.models.data.principal.PrincipalModel; import eu.eudat.models.data.security.Principal; import eu.eudat.types.ApiMessageCode; +import eu.eudat.types.MetricNames; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -64,6 +65,7 @@ public class Login { private ConfigurableProviderTokenValidator configurableProviderTokenValidator; private ZenodoTokenValidator zenodoTokenValidator; private ConfigLoader configLoader; + private final MetricsManager metricsManager; // private Logger logger; @@ -75,7 +77,8 @@ public class Login { B2AccessTokenValidator b2AccessTokenValidator, ORCIDTokenValidator orcidTokenValidator, LinkedInTokenValidator linkedInTokenValidator, OpenAIRETokenValidator openAIRETokenValidator, ConfigurableProviderTokenValidator configurableProviderTokenValidator, ZenodoTokenValidator zenodoTokenValidator, - ConfigLoader configLoader, UserManager userManager) { + ConfigLoader configLoader, UserManager userManager, + MetricsManager metricsManager) { this.customAuthenticationProvider = customAuthenticationProvider; this.nonVerifiedUserAuthenticationService = nonVerifiedUserAuthenticationService; this.twitterTokenValidator = twitterTokenValidator; @@ -87,6 +90,7 @@ public class Login { this.zenodoTokenValidator = zenodoTokenValidator; this.configLoader = configLoader; this.userManager = userManager; + this.metricsManager = metricsManager; } @@ -95,7 +99,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"); + metricsManager.increaseValue(MetricNames.USERS, 1, MetricNames.LOGGEDIN); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(customAuthenticationProvider.authenticate(credentials)).status(ApiMessageCode.SUCCESS_MESSAGE)); } @@ -104,7 +108,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"); + metricsManager.increaseValue(MetricNames.USERS, 1, MetricNames.LOGGEDIN); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().payload(userManager.authenticate(this.nonVerifiedUserAuthenticationService, credentials)).status(ApiMessageCode.SUCCESS_MESSAGE)); } @@ -165,7 +169,7 @@ public class Login { ResponseEntity> logout(Principal principal) { this.nonVerifiedUserAuthenticationService.Logout(principal.getToken()); logger.info(principal + " Logged Out"); - MetricsManager.decreaseValue("argos_users", 1, "loggedin"); + metricsManager.decreaseValue(MetricNames.USERS, 1, MetricNames.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/TagController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/TagController.java index e7acb4f09..eb53c5cb3 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/TagController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/TagController.java @@ -1,10 +1,8 @@ package eu.eudat.controllers; import eu.eudat.elastic.criteria.DatasetCriteria; -import eu.eudat.elastic.criteria.TagCriteria; import eu.eudat.elastic.entities.Dataset; import eu.eudat.elastic.entities.Tag; -import eu.eudat.elastic.repository.Repository; import eu.eudat.logic.proxy.config.exceptions.HugeResultSet; import eu.eudat.logic.proxy.config.exceptions.NoURLFound; import eu.eudat.logic.services.ApiContext; @@ -49,7 +47,9 @@ public class TagController extends BaseController { /*List> remoteRepos = this.getApiContext().getOperationsContext().getRemoteFetcher().getTags(externalUrlCriteria, type); TagExternalSourcesModel researchersExternalSourcesModel = new TagExternalSourcesModel().fromExternalItem(remoteRepos);*/ if (this.getApiContext().getOperationsContext().getElasticRepository().getDatasetRepository().exists()) { - List tags = this.getApiContext().getOperationsContext().getElasticRepository().getDatasetRepository().query(new DatasetCriteria()).stream().map(Dataset::getTags).flatMap(Collection::stream).filter(StreamDistinctBy.distinctByKey(Tag::getId)).collect(Collectors.toList()); + DatasetCriteria criteria = new DatasetCriteria(); + criteria.setHasTags(true); + List tags = this.getApiContext().getOperationsContext().getElasticRepository().getDatasetRepository().query(criteria).stream().map(Dataset::getTags).flatMap(Collection::stream).filter(StreamDistinctBy.distinctByKey(Tag::getId)).filter(tag -> tag.getName().toLowerCase().startsWith(query.toLowerCase())).collect(Collectors.toList()); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().payload(tags).status(ApiMessageCode.NO_MESSAGE)); } else { 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 6cb588eef..2a5dca57a 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 @@ -6,6 +6,7 @@ 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 eu.eudat.types.MetricNames; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.http.HttpHeaders; @@ -30,16 +31,18 @@ import static eu.eudat.types.Authorities.ADMIN; public class UserGuideController { private Environment environment; + private final MetricsManager metricsManager; @Autowired - public UserGuideController(Environment environment) { + public UserGuideController(Environment environment, MetricsManager metricsManager) { this.environment = environment; + this.metricsManager = metricsManager; } @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); + metricsManager.calculateValue(MetricNames.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()); @@ -50,13 +53,12 @@ public class UserGuideController { } InputStream is = new FileInputStream(fileName); - String[] filepath = fileName.split("\\.")[0].split("\\\\"); - String simplename = filepath[filepath.length - 1]; + Path path = Paths.get(fileName); HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentLength(is.available()); responseHeaders.setContentType(MediaType.TEXT_HTML); - responseHeaders.set("Content-Disposition", "attachment;filename=" + simplename); + responseHeaders.set("Content-Disposition", "attachment;filename=" + path.getFileName().toString()); responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition"); responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type"); @@ -71,7 +73,7 @@ public class UserGuideController { @RequestMapping(value = "current", method = RequestMethod.POST) public @ResponseBody ResponseEntity> updateGuide(@RequestBody UserGuide guide, @ClaimedAuthorities(claims = {ADMIN}) Principal principal) throws Exception { - String fileName = this.environment.getProperty("userguide.path") + guide.getName() + ".html"; + String fileName = this.environment.getProperty("userguide.path") + guide.getName(); OutputStream os = new FileOutputStream(fileName); os.write(guide.getHtml().getBytes()); os.close(); diff --git a/dmp-backend/web/src/main/java/eu/eudat/controllers/UserInvitationController.java b/dmp-backend/web/src/main/java/eu/eudat/controllers/UserInvitationController.java index ecb46650e..d9633d312 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/controllers/UserInvitationController.java +++ b/dmp-backend/web/src/main/java/eu/eudat/controllers/UserInvitationController.java @@ -15,6 +15,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import javax.xml.bind.JAXBException; +import java.io.IOException; import java.util.List; import java.util.UUID; @@ -42,15 +43,18 @@ public class UserInvitationController extends BaseController { @Transactional @RequestMapping(method = RequestMethod.GET, value = {"/exchange/{invitationID}"}, produces = "application/json") public @ResponseBody - ResponseEntity> exchange(@PathVariable UUID invitationID, Principal principal) throws JAXBException { + ResponseEntity> exchange(@PathVariable UUID invitationID, Principal principal) throws JAXBException, IOException { UUID dmpId = invitationsManager.assignUserAcceptedInvitation(invitationID, principal); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem().status(ApiMessageCode.SUCCESS_MESSAGE).payload(dmpId)); } @RequestMapping(method = RequestMethod.POST, value = {"/getUsers"}, consumes = "application/json", produces = "application/json") public @ResponseBody - ResponseEntity>> getUsers(Principal principal) throws IllegalAccessException, InstantiationException { - List users = invitationsManager.getUsers(principal); +// ResponseEntity>> getUsers(Principal principal) throws IllegalAccessException, InstantiationException { + ResponseEntity>> getUsers(Principal principal, @RequestBody UserInfoRequestItem userInfoRequestItem) throws IllegalAccessException, InstantiationException { + System.out.println(userInfoRequestItem.getCriteria().getLike()); +// List users = invitationsManager.getUsers(principal); + List users = invitationsManager.getUsersWithCriteria(principal, userInfoRequestItem); return ResponseEntity.status(HttpStatus.OK).body(new ResponseItem>().status(ApiMessageCode.SUCCESS_MESSAGE).payload(users)); } } 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 a01ef19ef..37600f7eb 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 @@ -8,13 +8,13 @@ import eu.eudat.data.dao.entities.DMPDao; import eu.eudat.data.dao.entities.DatasetDao; import eu.eudat.data.dao.entities.GrantDao; import eu.eudat.data.dao.entities.OrganisationDao; -import eu.eudat.data.entities.DMP; -import eu.eudat.data.entities.Dataset; -import eu.eudat.data.entities.Grant; -import eu.eudat.data.entities.UserInfo; +import eu.eudat.data.entities.*; import eu.eudat.data.query.PaginationService; +import eu.eudat.data.query.items.table.dataset.DatasetTableRequest; +import eu.eudat.elastic.criteria.SortCriteria; import eu.eudat.elastic.entities.Dmp; import eu.eudat.logic.builders.model.models.RecentActivityDataBuilder; +import eu.eudat.logic.mapper.elastic.criteria.DmpCriteriaMapper; import eu.eudat.logic.services.ApiContext; import eu.eudat.logic.services.operations.DatabaseRepository; import eu.eudat.models.HintedModelFactory; @@ -26,6 +26,8 @@ import eu.eudat.models.data.dashboard.recent.model.RecentDmpModel; import eu.eudat.models.data.dashboard.recent.tablerequest.RecentActivityTableRequest; import eu.eudat.models.data.dashboard.searchbar.SearchBarItem; import eu.eudat.models.data.dashboard.statistics.DashBoardStatistics; +import eu.eudat.models.data.listingmodels.DataManagementPlanListingModel; +import eu.eudat.models.data.listingmodels.DatasetListingModel; import eu.eudat.models.data.security.Principal; import eu.eudat.queryable.QueryableList; import eu.eudat.types.searchbar.SearchBarItemType; @@ -36,9 +38,11 @@ import org.springframework.stereotype.Component; import javax.transaction.Transactional; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; @Component @@ -97,8 +101,8 @@ public class DashBoardManager { } public DashBoardStatistics getMeStatistics(Principal principal) throws IOException { - List datasets = null; - List dmps = null; + Long datasets = 0L; + Long dmps = 0L; DashBoardStatistics statistics = new DashBoardStatistics(); DMPDao dataManagementPlanRepository = databaseRepository.getDmpDao(); DatasetDao datasetRepository = databaseRepository.getDatasetDao(); @@ -112,7 +116,8 @@ public class DashBoardManager { eu.eudat.elastic.criteria.DatasetCriteria datasetElasticCriteria = new eu.eudat.elastic.criteria.DatasetCriteria(); datasetElasticCriteria.setAllowAllVersions(false); datasetElasticCriteria.setPublic(false); - datasets = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().query(datasetElasticCriteria); + datasetElasticCriteria.setCollaborators(Collections.singletonList(principal.getId())); + datasets = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().count(datasetElasticCriteria); }catch (Exception e) { logger.warn(e.getMessage(), e); datasets = null; @@ -126,33 +131,44 @@ public class DashBoardManager { eu.eudat.elastic.criteria.DmpCriteria dmpElasticCriteria = new eu.eudat.elastic.criteria.DmpCriteria(); dmpElasticCriteria.setAllowAllVersions(false); dmpElasticCriteria.setPublic(false); - dmps = apiContext.getOperationsContext().getElasticRepository().getDmpRepository().query(dmpElasticCriteria); + dmpElasticCriteria.setCollaborators(Collections.singletonList(principal.getId())); + dmps = apiContext.getOperationsContext().getElasticRepository().getDmpRepository().count(dmpElasticCriteria); }catch (Exception e) { logger.warn(e.getMessage(), e); dmps = null; } } dataManagementPlanCriteria.setAllVersions(false); - GrantCriteria grantCriteria = new GrantCriteria(); + dataManagementPlanCriteria.setOnlyPublic(false); + dataManagementPlanCriteria.setIsPublic(false); + GrantCriteria grantCriteria = new GrantCriteria(); grantCriteria.setActive(true); + OrganisationCriteria organisationCriteria = new OrganisationCriteria(); + organisationCriteria.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() - .whenComplete((dmpsStats, throwable) -> statistics.setTotalDataManagementPlanCount(dmpsStats)); - List finalDatasets = datasets; - CompletableFuture datasetFuture = datasetRepository.getAuthenticated((datasets != null && !datasets.isEmpty()) ? datasetRepository.asQueryable().where((builder, root) -> root.get("id").in(finalDatasets.stream().map(x -> UUID.fromString(x.getId())).collect(Collectors.toList()))) : datasetRepository.getWithCriteria(datasetCriteria), user, roles).countAsync() - .whenComplete((datasetsStats, throwable) -> statistics.setTotalDataSetCount(datasetsStats)); + if ((dmps == null || dmps == 0L) && (datasets == null || datasets == 0L)) { + CompletableFuture dmpFuture = dataManagementPlanRepository.getAuthenticated(dataManagementPlanRepository.getWithCriteria(dataManagementPlanCriteria), principal.getId(), roles).distinct().countAsync() + .whenComplete((dmpsStats, throwable) -> statistics.setTotalDataManagementPlanCount(dmpsStats)); + CompletableFuture datasetFuture = datasetRepository.getAuthenticated( datasetRepository.getWithCriteria(datasetCriteria), user, roles).distinct().countAsync() + .whenComplete((datasetsStats, throwable) -> statistics.setTotalDataSetCount(datasetsStats)); + CompletableFuture.allOf(dmpFuture, datasetFuture).join(); + } else { + statistics.setTotalDataManagementPlanCount(dmps); + statistics.setTotalDataSetCount(datasets); + } CompletableFuture grantFuture = grantRepository.getAuthenticated(grantRepository.getWithCriteria(grantCriteria), user).countAsync() .whenComplete((grantsStats, throwable) -> statistics.setTotalGrantCount(grantsStats)); - CompletableFuture orgnanisationFuture = organisationRepository.getAuthenticated(organisationRepository.asQueryable().withHint("organisationRecentActivity"), user).countAsync() + CompletableFuture orgnanisationFuture = organisationRepository.getAuthenticated(organisationRepository.getWithCriteria(organisationCriteria).withHint("organisationRecentActivity"), user).countAsync() .whenComplete((organisationStats, throwable) -> statistics.setTotalOrganisationCount(organisationStats)); - CompletableFuture.allOf(dmpFuture, datasetFuture, grantFuture, orgnanisationFuture).join(); + CompletableFuture.allOf( grantFuture, orgnanisationFuture).join(); return statistics; } + @Deprecated public RecentActivity getRecentActivity(Principal principal, Integer numberofactivities) { RecentActivity activity = new RecentActivity(); DMPDao dataManagementPlanRepository = databaseRepository.getDmpDao(); @@ -175,7 +191,7 @@ public class DashBoardManager { .selectAsync(item -> recentActivityDataBuilder.label(item.getLabel()).timestamp(item.getModified()).id(item.getId().toString()).build()) .whenComplete((dmpActivities, throwable) -> activity.setRecentDmpActivities(dmpActivities)); - CompletableFuture> datasets = datasetRepository.getAuthenticated(datasetRepository.getWithCriteria(datasetCriteria), user, roles) + CompletableFuture> datasets = datasetRepository.getAuthenticated(datasetRepository.getWithCriteria(datasetCriteria), user, roles).distinct() .withHint("datasetRecentActivity") .orderBy((builder, root) -> builder.desc(root.get("modified"))) .take(numberofactivities) @@ -225,7 +241,17 @@ public class DashBoardManager { datasetElasticCriteria.setLike(tableRequest.getCriteria().getLike()); datasetElasticCriteria.setAllowAllVersions(false); datasetElasticCriteria.setPublic(!isAuthenticated); - datasets = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().query(datasetElasticCriteria); + datasetElasticCriteria.setOffset(tableRequest.getDatasetOffset()); + datasetElasticCriteria.setSize(tableRequest.getLength()); + if (isAuthenticated) { + datasetElasticCriteria.setCollaborators(Collections.singletonList(principal.getId())); + } + datasetElasticCriteria.setSortCriteria(DmpCriteriaMapper.toElasticSorting(tableRequest.getOrderings())); + datasetElasticCriteria.getSortCriteria().stream().filter(sortCriteria -> sortCriteria.getFieldName().equals("publishedAt")).forEach(sortCriteria -> { + sortCriteria.setFieldName("dmp:" + sortCriteria.getFieldName()); + sortCriteria.setColumnType(SortCriteria.ColumnType.JOIN_COLUMN); + }); + datasets = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().queryIds(datasetElasticCriteria); }catch (Exception e) { logger.warn(e.getMessage(), e); datasets = null; @@ -238,10 +264,16 @@ public class DashBoardManager { dmpElasticCriteria.setLike(tableRequest.getCriteria().getLike()); dmpElasticCriteria.setAllowAllVersions(false); dmpElasticCriteria.setPublic(!isAuthenticated); + dmpElasticCriteria.setOffset(tableRequest.getDmpOffset()); + dmpElasticCriteria.setSize(tableRequest.getLength()); + if (isAuthenticated) { + dmpElasticCriteria.setCollaborators(Collections.singletonList(principal.getId())); + } + dmpElasticCriteria.setSortCriteria(DmpCriteriaMapper.toElasticSorting(tableRequest.getOrderings())); dmps = apiContext.getOperationsContext().getElasticRepository().getDmpRepository().query(dmpElasticCriteria); }catch (Exception e) { logger.warn(e.getMessage(), e); - datasets = null; + dmps = null; } } @@ -250,6 +282,8 @@ public class DashBoardManager { dmpList = dataManagementPlanRepository.asQueryable().where((builder, root) -> root.get("id").in(finalDmps.stream().map(Dmp::getId).collect(Collectors.toList()))).distinct(); } else { dmpList = dataManagementPlanRepository.getWithCriteria(dataManagementPlanCriteria).distinct(); + PaginationService.applyOrder(dmpList, tableRequest.getOrderings()); + dmpList.skip(tableRequest.getDmpOffset()).take(tableRequest.getLength()); } if (datasets != null && !datasets.isEmpty()) { @@ -258,53 +292,57 @@ public class DashBoardManager { } else { datasetList = datasetRepository.getWithCriteria(datasetCriteria); } + IntStream.range(0, tableRequest.getOrderings().getFields().size()).filter(i -> tableRequest.getOrderings().getFields().get(i).contains("publishedAt")) + .forEach(i -> tableRequest.getOrderings().getFields().set(i, tableRequest.getOrderings().getFields().get(i).toCharArray()[0] + "dmp:publishedAt|join|")); + // tableRequest.getOrderings().getFields().stream().filter(s -> s.contains("publishedAt")).forEach(s -> s = s.toCharArray()[0] + "dmp:publishedAt|join|" ); + /*for (int i = 0; i< tableRequest.getOrderings().getFields().size(); i++) { + if (tableRequest.getOrderings().getFields().get(i).contains("publishedAt")) { + String newField = tableRequest.getOrderings().getFields().get(i).toCharArray()[0] + "dmp:publishedAt|join|"; + tableRequest.getOrderings().getFields().set(i, newField); + } + }*/ + /*if (tableRequest.getOrderings().getFields().get(0).contains("publishedAt")) { + tableRequest.getOrderings().getFields().set(0, tableRequest.getOrderings().getFields().get(0).charAt(0) + "dmp:" + tableRequest.getOrderings().getFields().get(0).substring(1) + "|join|"); + }*/ + PaginationService.applyOrder(datasetList, tableRequest.getOrderings()); + datasetList.skip(tableRequest.getDatasetOffset()).take(tableRequest.getLength()); if (isAuthenticated) { List roles = new LinkedList<>(); + roles.add(UserDMP.UserDMPRoles.USER.getValue()); + roles.add(UserDMP.UserDMPRoles.OWNER.getValue()); dmpList = dataManagementPlanRepository.getAuthenticated(dmpList, principal.getId(), roles); - datasetList = datasetRepository.getAuthenticated(datasetList, user, roles); + datasetList = datasetRepository.getAuthenticated(datasetList, user, roles).distinct(); } - PaginationService.applyOrder(dmpList, tableRequest.getOrderings()); - for (int i = 0; i< tableRequest.getOrderings().getFields().length; i++) { - if (tableRequest.getOrderings().getFields()[i].contains("publishedAt")) { - String newField = tableRequest.getOrderings().getFields()[i].toCharArray()[0] + "dmp:publishedAt|join|"; - tableRequest.getOrderings().getFields()[i] = newField; - } - } - PaginationService.applyOrder(datasetList, tableRequest.getOrderings()); + /*CompletableFuture future = CompletableFuture.runAsync(() -> */{ - recentActivityModels.addAll(dmpList - .withHint(HintedModelFactory.getHint(RecentDmpModel.class)) - // .orderBy((builder, root) -> builder.desc(root.get(tableRequest.getCriteria().getOrder()))) - .skip(tableRequest.getDmpOffset()) - .take(tableRequest.getLength()) - .select(item -> { - item.setDataset( - item.getDataset().stream() - .filter(dataset -> !dataset.getStatus().equals(Dataset.Status.DELETED.getValue()) && !dataset.getStatus().equals(Dataset.Status.CANCELED.getValue())).collect(Collectors.toList()).stream() - .filter(dataset -> dataset.getDmp().getUsers().stream() - .filter(x -> x.getUser().getId().equals(principal.getId())) - .collect(Collectors.toList()).size() > 0) - .collect(Collectors.toSet())); - return new RecentDmpModel().fromEntity(item); - })); + List dmps1 = dmpList.withHint(HintedModelFactory.getHint(DataManagementPlanListingModel.class)) + .distinct().toList(); + recentActivityModels.addAll(dmps1.stream().map(dmp -> { + DatasetCriteria datasetCriteria1 = new DatasetCriteria(); + datasetCriteria1.setDmpIds(Collections.singletonList(dmp.getId())); + datasetCriteria1.setAllVersions(false); + if (isAuthenticated) { + dmp.setDataset(retrieveRelevantDatasets(datasetCriteria1, principal.getId())); + } else { + datasetCriteria1.setIsPublic(true); + dmp.setDataset(retrieveRelevantDatasets(datasetCriteria1)); + } + return new RecentDmpModel().fromDataModel(dmp); + }).collect(Collectors.toList())); - recentActivityModels.addAll(datasetList - .withHint(HintedModelFactory.getHint(RecentDatasetModel.class)) - // .orderBy((builder, root) -> builder.desc(root.get(tableRequest.getCriteria().getOrder()))) - .skip(tableRequest.getDatasetOffset()) - .take(tableRequest.getLength()) - .select(item -> { - return new RecentDatasetModel().fromEntity(item); - })); + List recentDatasetModels = datasetList + .withHint(HintedModelFactory.getHint(DatasetListingModel.class)) + .select(item -> new RecentDatasetModel().fromEntity(item)); + recentActivityModels.addAll(recentDatasetModels); }/*);*/ //GK: Shuffle the deck otherwise we will summon the DMPodia when sorting with status - int pos = -1; + /*int pos = -1; for (int i = (recentActivityModels.size() / 2); i < recentActivityModels.size(); i++) { RecentActivityModel recentActivityModel = recentActivityModels.remove(i); while (pos < recentActivityModels.size()) { @@ -314,12 +352,29 @@ public class DashBoardManager { } } recentActivityModels.add(pos, recentActivityModel); - } + }*/ + //GK: No one likes to play shuffle with the recent activities. So just re-sort them based on how they have been sorted already + + recentActivityModels = recentActivityModels.stream().sorted((o1, o2) -> { + try { + String order = tableRequest.getOrderings().getFields().get(0).toCharArray()[0] + ""; + String field = tableRequest.getOrderings().getFields().get(0).substring(1); + if (field.contains(":") && field.contains("|")) { + field = field.substring(field.lastIndexOf(":") + 1, field.indexOf("|")); + } + field = field.equals("label") ? "title" : field; + field = field.substring(0, 1).toUpperCase() + field.substring(1); + return (order.equals("+") ? 1 : -1 ) * ((Comparable)o1.getClass().getMethod("get" + field).invoke(o1)).compareTo(o2.getClass().getMethod("get" + field).invoke(o2)); + } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + logger.error(e.getLocalizedMessage(), e); + } + return 0; + }).collect(Collectors.toList()); //CompletableFuture.allOf(future).join(); // CompletableFuture.allOf(dmps, datasets).join(); - return recentActivityModels.stream().sorted(this.comparators.get(tableRequest.getCriteria().getOrder())).limit(tableRequest.getLength()).collect(Collectors.toList()); + return recentActivityModels; } public List searchUserData(String like, Principal principal) { @@ -380,4 +435,38 @@ public class DashBoardManager { return searchBarItems; } + + private Set retrieveRelevantDatasets(DatasetCriteria datasetCriteria) { + return retrieveRelevantDatasets(datasetCriteria, null); + } + + private Set retrieveRelevantDatasets (DatasetCriteria datasetCriteria, UUID principal) { + QueryableList datasetItems = apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(datasetCriteria); + if (principal != null) { + UserInfo userInfo = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal); + List roles = new ArrayList<>(); + roles.add(0); + roles.add(1); + datasetItems = apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getAuthenticated(datasetItems, userInfo, roles); + } + Long maxDatasets = datasetItems.distinct().count(); + DatasetTableRequest datasetTableRequest = new DatasetTableRequest(); + datasetTableRequest.setOffset(0); + datasetTableRequest.setLength(3); + Set datasetsSet = new LinkedHashSet<>(); + try { + datasetItems = PaginationManager.applyPaging(datasetItems, datasetTableRequest); + List datasets = datasetItems.distinct().toList(); + datasetsSet.addAll(datasets); + for (int i = 0; i < maxDatasets - datasets.size(); i++) { + Dataset fakedataset = new Dataset(); + fakedataset.setId(UUID.randomUUID()); + datasetsSet.add(fakedataset); + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + + return datasetsSet; + } } 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 2ab542edd..b8c4ccf0d 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 @@ -37,6 +37,7 @@ import eu.eudat.logic.services.operations.DatabaseRepository; import eu.eudat.logic.services.utilities.UtilitiesService; import eu.eudat.logic.utilities.builders.XmlBuilder; import eu.eudat.logic.utilities.documents.helpers.FileEnvelope; +import eu.eudat.logic.utilities.documents.pdf.PDFUtils; import eu.eudat.logic.utilities.documents.types.ParagraphStyle; import eu.eudat.logic.utilities.documents.word.WordBuilder; import eu.eudat.logic.utilities.documents.xml.ExportXmlBuilder; @@ -59,6 +60,7 @@ import eu.eudat.models.data.user.composite.PagedDatasetProfile; import eu.eudat.models.data.userinfo.UserListingModel; import eu.eudat.queryable.QueryableList; import eu.eudat.types.Authorities; +import eu.eudat.types.MetricNames; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; @@ -77,6 +79,7 @@ import org.springframework.web.multipart.MultipartFile; import org.w3c.dom.Document; import org.w3c.dom.Element; +import javax.transaction.Transactional; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; @@ -108,9 +111,10 @@ public class DataManagementPlanManager { private Environment environment; private RDAManager rdaManager; private UserManager userManager; + private final MetricsManager metricsManager; @Autowired - public DataManagementPlanManager(ApiContext apiContext, DatasetManager datasetManager, Environment environment, RDAManager rdaManager, UserManager userManager) { + public DataManagementPlanManager(ApiContext apiContext, DatasetManager datasetManager, Environment environment, RDAManager rdaManager, UserManager userManager, MetricsManager metricsManager) { this.apiContext = apiContext; this.datasetManager = datasetManager; this.utilitiesService = apiContext.getUtilitiesService(); @@ -118,6 +122,7 @@ public class DataManagementPlanManager { this.environment = environment; this.rdaManager = rdaManager; this.userManager = userManager; + this.metricsManager = metricsManager; } /* @@ -149,8 +154,10 @@ public class DataManagementPlanManager { items = null; } } + if (items == null) { items = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(dataManagementPlanTableRequest.getCriteria()); + } List roles = new LinkedList<>(); if (!dataManagementPlanTableRequest.getCriteria().isOnlyPublic()) { if (dataManagementPlanTableRequest.getCriteria().getRole() != null) @@ -159,8 +166,11 @@ public class DataManagementPlanManager { } else { authItems = items; } + if (dmps == null) { totalData = authItems.count(); items = PaginationManager.applyPaging(authItems, dataManagementPlanTableRequest); + } else { + items = authItems; } @@ -175,6 +185,8 @@ public class DataManagementPlanManager { dataTable.setData(dmps1.stream().map(dmp -> { DatasetCriteria datasetCriteria = new DatasetCriteria(); datasetCriteria.setDmpIds(Collections.singletonList(dmp.getId())); + datasetCriteria.setAllVersions(false); + datasetCriteria.setIsPublic(dataManagementPlanTableRequest.getCriteria().getIsPublic()); dmp.setDataset(retrieveRelevantDatasets(datasetCriteria, principalID)); @@ -240,14 +252,14 @@ public class DataManagementPlanManager { roles.add(1); datasetItems = apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getAuthenticated(datasetItems, userInfo, roles); } - Long maxDatasets = datasetItems.count(); + Long maxDatasets = datasetItems.distinct().count(); DatasetTableRequest datasetTableRequest = new DatasetTableRequest(); datasetTableRequest.setOffset(0); datasetTableRequest.setLength(3); Set datasetsSet = new LinkedHashSet<>(); try { datasetItems = PaginationManager.applyPaging(datasetItems, datasetTableRequest); - List datasets = datasetItems.toList(); + List datasets = datasetItems.distinct().toList(); datasetsSet.addAll(datasets); for (int i = 0; i < maxDatasets - datasets.size(); i++) { Dataset fakedataset = new Dataset(); @@ -441,7 +453,7 @@ public class DataManagementPlanManager { setNotification = true; } else { - MetricsManager.increaseValue("argos_managed_dmps", 1, "draft"); + metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.DRAFT); } DMP newDmp = dataManagementPlan.toDataModel(); @@ -532,6 +544,9 @@ public class DataManagementPlanManager { if (dataManagementPlan.getAssociatedUsers().size() == 0) assignUser(newDmp, user); + UUID dmpId = newDmp.getId(); + newDmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); + this.updateIndex(newDmp); if (setNotification) { @@ -565,7 +580,7 @@ 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"); + metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.DRAFT); } List datasets = new ArrayList<>(); DMP tempDMP = dataManagementPlan.toDataModel(); @@ -586,6 +601,11 @@ public class DataManagementPlanManager { datasets.add(dataset); } + UUID dmpId = result.getId(); + result.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); + + this.updateIndex(result); + return result; } @@ -623,15 +643,31 @@ public class DataManagementPlanManager { assignGrandUserIfInternal(newDmp, user); assignFunderUserIfInternal(newDmp, user); assignProjectUserIfInternal(newDmp, user); + if (newDmp.getGrant().getStartdate() == null) { + newDmp.getGrant().setStartdate(new Date()); + } + if (newDmp.getGrant().getEnddate() == null) { + newDmp.getGrant().setEnddate(Date.from(Instant.now().plus(365, ChronoUnit.DAYS))); + } databaseRepository.getGrantDao().createOrUpdate(newDmp.getGrant()); DMP tempDmp = databaseRepository.getDmpDao().createOrUpdate(newDmp); newDmp.setId(tempDmp.getId()); // Assign creator. - assignUser(newDmp, user); + //assignUser(newDmp, user); copyDatasets(newDmp, databaseRepository.getDatasetDao()); + databaseRepository + .getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), oldDmp.getId())) + .toList().stream().forEach(userDMP -> { + UserDMP temp = new UserDMP(); + temp.setUser(userDMP.getUser()); + temp.setRole(userDMP.getRole()); + temp.setDmp(newDmp); + apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().createOrUpdate(temp); + }); + newDmp.setUsers(new HashSet<>(databaseRepository.getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), newDmp.getId())).toList())); DatasetCriteria criteria1 = new DatasetCriteria(); @@ -679,9 +715,11 @@ public class DataManagementPlanManager { DatasetCriteria criteria1 = new DatasetCriteria(); criteria1.setDmpIds(Collections.singletonList(newDmp.getId())); newDmp.setDataset(new HashSet<>(databaseRepository.getDatasetDao().getWithCriteria(criteria1).toList())); + UUID dmpId = newDmp.getId(); + newDmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); this.updateIndex(newDmp); - MetricsManager.increaseValue("argos_managed_dmps", 1, "draft"); + metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.DRAFT); return newDmp.getId(); } @@ -695,26 +733,30 @@ public class DataManagementPlanManager { DMP oldDmp = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(uuid); switch (oldDmp.getStatus()) { case 0: - MetricsManager.decreaseValue("argos_managed_dmps", 1, "draft"); + metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.DRAFT); break; case 1: if (oldDmp.getDoi() != null) { - MetricsManager.decreaseValue("argos_managed_dmps", 1, "doied"); + metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.DOIED); } if (oldDmp.isPublic()) { - MetricsManager.decreaseValue("argos_managed_dmps", 1, "published"); + metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.PUBLISHED); } - MetricsManager.decreaseValue("argos_managed_dmps", 1, "finalized"); + metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.FINALIZED); break; } oldDmp.setStatus(DMP.DMPStatus.DELETED.getValue()); apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(oldDmp); + UUID dmpId = oldDmp.getId(); + oldDmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); this.updateIndex(oldDmp); DataManagementPlanCriteria criteria1 = new DataManagementPlanCriteria(); criteria1.setAllVersions(true); criteria1.setGroupIds(Collections.singletonList(oldDmp.getGroupId())); apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria1).toList().forEach(dmp -> { try { + UUID tdmpId = dmp.getId(); + dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), tdmpId)).toList())); this.updateIndex(dmp); } catch (IOException e) { logger.error(e.getMessage(), e); @@ -726,6 +768,7 @@ public class DataManagementPlanManager { if (elastic != null) { tags = elastic.getTags(); } + dataset.setDmp(dmp); this.datasetManager.updateTags(dataset, tags); } catch (Exception e) { logger.error(e.getMessage(), e); @@ -744,7 +787,7 @@ public class DataManagementPlanManager { else { researcher.setCreationUser(user); researcherRepository.createOrUpdate(researcher); - MetricsManager.increaseValue("argos_researchers", 1, null); + metricsManager.increaseValue(MetricNames.RESEARCHER, 1, null); } } } @@ -785,6 +828,12 @@ public class DataManagementPlanManager { else { grant.setType(Grant.GrantType.EXTERNAL.getValue()); grant.setCreationUser(null); + if (grant.getStartdate() == null) { + grant.setStartdate(new Date()); + } + if (grant.getEnddate() == null) { + grant.setEnddate(Date.from(Instant.now().plus(365, ChronoUnit.DAYS))); + } grantDao.createOrUpdate(grant); } } @@ -823,21 +872,24 @@ public class DataManagementPlanManager { eu.eudat.data.entities.Project projectEntity = projectDao.getWithCritetia(criteria).toList().stream().max(Comparator.comparing(project1 -> project1.getModified().getTime())).orElse(null); if (projectEntity != null) project.setId(projectEntity.getId()); else { - project.setType(Project.ProjectType.EXTERNAL.getValue()); - if (project.getId() == null) project.setId(UUID.randomUUID()); - projectDao.createOrUpdate(project); - MetricsManager.increaseValue("argos_projects", 1, null); + createExternalProject(project, projectDao); } } else { - project.setType(Project.ProjectType.EXTERNAL.getValue()); - if (project.getId() == null) project.setId(UUID.randomUUID()); - projectDao.createOrUpdate(project); - MetricsManager.increaseValue("argos_projects", 1, null); + createExternalProject(project, projectDao); } } } + private void createExternalProject(Project project, ProjectDao projectDao) { + if (project.getStartdate() == null) project.setStartdate(new Date()); + if (project.getEnddate() == null) project.setEnddate(new Date()); + project.setType(Project.ProjectType.EXTERNAL.getValue()); + if (project.getId() == null) project.setId(UUID.randomUUID()); + projectDao.createOrUpdate(project); + metricsManager.increaseValue(MetricNames.PROJECT, 1, null); + } + private void copyDatasets(DMP newDmp, DatasetDao datasetDao) { List> futures = new LinkedList<>(); for (Dataset dataset : newDmp.getDataset()) { @@ -851,7 +903,8 @@ public class DataManagementPlanManager { if (elastic != null) { tags = elastic.getTags(); } - + UUID dmpId = tempDataset.getDmp().getId(); + tempDataset.getDmp().setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); this.datasetManager.updateTags(tempDataset, tags); } catch (Exception e) { logger.error(e.getMessage(), e); @@ -910,7 +963,7 @@ public class DataManagementPlanManager { return newDataset; }).thenApplyAsync(item -> { futures.add(datasetDao.createOrUpdateAsync(item).whenComplete(((dataset1, throwable) -> { - MetricsManager.increaseValue("argos_managed_dataset_descriptions", 1, "draft"); + metricsManager.increaseValue(MetricNames.DATASET, 1, MetricNames.DRAFT); eu.eudat.elastic.entities.Dataset datasetElastic = new eu.eudat.elastic.entities.Dataset(); datasetElastic.setId(dataset1.getId().toString()); datasetElastic.setLabel(dataset1.getLabel()); @@ -968,8 +1021,10 @@ public class DataManagementPlanManager { throw new Exception("DMP is not finalized"); dmp.setPublic(true); apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp); + UUID dmpId = dmp.getId(); + dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); this.updateIndex(dmp); - MetricsManager.increaseValue("argos_managed_dmps", 1, "published"); + metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.PUBLISHED); DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); criteria.setGroupIds(Collections.singletonList(dmp.getGroupId())); criteria.setAllVersions(true); @@ -981,8 +1036,10 @@ public class DataManagementPlanManager { if (elastic != null) { tags = elastic.getTags(); } + UUID tmdmpId = dataset.getDmp().getId(); + dataset.getDmp().setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), tmdmpId)).toList())); this.datasetManager.updateTags(dataset, tags); - MetricsManager.increaseValue("argos_managed_dataset_descriptions", 1, "published"); + metricsManager.increaseValue(MetricNames.DATASET, 1, MetricNames.PUBLISHED); } catch (Exception e) { logger.error(e.getMessage(), e); } @@ -995,6 +1052,7 @@ public class DataManagementPlanManager { } } + @Transactional public void makeFinalize(UUID id, Principal principal, DatasetsToBeFinalized datasetsToBeFinalized) throws Exception { DMP dmp = this.apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().find(id); if (!isUserOwnerOfDmp(dmp, principal)) @@ -1065,14 +1123,16 @@ public class DataManagementPlanManager { } dmp.setStatus(DMP.DMPStatus.FINALISED.getValue()); apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp); + UUID dmpId = dmp.getId(); + dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); 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"); + metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.DRAFT); + metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.FINALIZED); this.updateDatasetsIndex(indexDatasets); - MetricsManager.decreaseValue("argos_managed_dataset_descriptions", indexDatasets.size(), "draft"); - MetricsManager.increaseValue("argos_managed_dataset_descriptions", indexDatasets.size(), "finalized"); + metricsManager.decreaseValue(MetricNames.DATASET, indexDatasets.size(), MetricNames.DRAFT); + metricsManager.increaseValue(MetricNames.DATASET, indexDatasets.size(), MetricNames.FINALIZED); } public void undoFinalize(UUID id, Principal principal) throws Exception { @@ -1083,9 +1143,11 @@ public class DataManagementPlanManager { throw new Exception("DMP is already Active"); dmp.setStatus(DMP.DMPStatus.ACTIVE.getValue()); apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(dmp); + UUID dmpId = dmp.getId(); + dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); this.updateIndex(dmp); - MetricsManager.decreaseValue("argos_managed_dmps", 1, "finalized"); - MetricsManager.increaseValue("argos_managed_dmps", 1, "draft"); + metricsManager.decreaseValue(MetricNames.DMP, 1, MetricNames.FINALIZED); + metricsManager.increaseValue(MetricNames.DMP, 1, MetricNames.DRAFT); } public void updateUsers(UUID id, List users, Principal principal) throws Exception { @@ -1122,7 +1184,7 @@ public class DataManagementPlanManager { wordBuilder.addParagraphContent("Data Management Plan Information", document, ParagraphStyle.HEADER1, BigInteger.ZERO); // DMP title custom style. wordBuilder.addParagraphContent(dmpEntity.getLabel(), document, ParagraphStyle.HEADER2, BigInteger.ZERO); - wordBuilder.addParagraphContent(dmpEntity.getDescription(), document, ParagraphStyle.TEXT, BigInteger.ZERO); + wordBuilder.addParagraphContent(dmpEntity.getDescription(), document, ParagraphStyle.HTML, BigInteger.ZERO); wordBuilder.addParagraphContent("Funder", document, ParagraphStyle.HEADER3, BigInteger.ZERO); if (dmpEntity.getGrant().getFunder() != null) @@ -1161,8 +1223,14 @@ public class DataManagementPlanManager { .forEach(datasetEntity -> { Map properties = new HashMap<>(); if (datasetEntity.getProperties() != null) { - JSONObject jObject = new JSONObject(datasetEntity.getProperties()); - properties = jObject.toMap(); + ObjectMapper objectMapper = new ObjectMapper(); + try { + properties = objectMapper.readValue(datasetEntity.getProperties(), LinkedHashMap.class); + } catch (IOException e) { + logger.error(e.getLocalizedMessage(), e); + } + /*JSONObject jObject = new JSONObject(datasetEntity.getProperties()); + properties = jObject.toMap();*/ } // Custom style for the Dataset title. @@ -1226,7 +1294,7 @@ public class DataManagementPlanManager { }*/ - wordBuilder.addParagraphContent(datasetEntity.getDescription(), document, ParagraphStyle.TEXT, BigInteger.ZERO); + wordBuilder.addParagraphContent(datasetEntity.getDescription(), document, ParagraphStyle.HTML, BigInteger.ZERO); // Dataset Description custom style. XWPFParagraph datasetDescriptionParagraph = document.createParagraph(); @@ -1277,7 +1345,7 @@ public class DataManagementPlanManager { eu.eudat.data.entities.DMP dmp = databaseRepository.getDmpDao().find(UUID.fromString(id)); if (!dmp.isPublic() && dmp.getUsers().stream().filter(userInfo -> userInfo.getUser().getId() == principal.getId()).collect(Collectors.toList()).size() == 0) throw new UnauthorisedException(); - List datasets = dmp.getDataset().stream().collect(Collectors.toList()); + List datasets = dmp.getDataset().stream().filter(dataset -> dataset.getStatus() != Dmp.DMPStatus.DELETED.getValue()).collect(Collectors.toList()); /*String fileName = dmp.getLabel(); fileName = fileName.replaceAll("[^a-zA-Z0-9+ ]", "");*/ String uuid = UUID.randomUUID().toString(); @@ -1292,6 +1360,22 @@ public class DataManagementPlanManager { dmpName.setTextContent(dmp.getLabel()); dmpElement.appendChild(dmpName); + if (dmp.getExtraProperties() != null && !dmp.getExtraProperties().isEmpty()) { + Map extraProperties = new ObjectMapper().readValue(dmp.getExtraProperties(), HashMap.class); + Element language = xmlDoc.createElement("language"); + language.setTextContent(extraProperties.get("language") != null ? extraProperties.get("language").toString() : null); + dmpElement.appendChild(language); + Element visibility = xmlDoc.createElement("visibility"); + visibility.setTextContent(extraProperties.get("visible") != null ? extraProperties.get("visible").toString() : null); + dmpElement.appendChild(visibility); + Element publicDate = xmlDoc.createElement("publicDate"); + publicDate.setTextContent(extraProperties.get("publicDate") != null ? extraProperties.get("publicDate").toString() : null); + dmpElement.appendChild(publicDate); + Element costs = xmlDoc.createElement("costs"); + costs.setTextContent(extraProperties.get("costs") != null ? extraProperties.get("costs").toString() : null); + dmpElement.appendChild(costs); + } + DMPProfile dmpProfile = dmp.getProfile(); Element dmpProfileElement = xmlDoc.createElement("dmpProfile"); Element dmpProfileName = xmlDoc.createElement("dmpProfileName"); @@ -1407,7 +1491,7 @@ public class DataManagementPlanManager { return fileEnvelope; } - public ResponseEntity getRDAJsonDocument(String id, DatasetManager datasetManager, Principal principal) throws Exception { + public ResponseEntity getRDAJsonDocument(String id, Principal principal) throws Exception { eu.eudat.data.entities.DMP dmp = databaseRepository.getDmpDao().find(UUID.fromString(id)); if (!dmp.isPublic() && dmp.getUsers().stream().noneMatch(userInfo -> userInfo.getUser().getId() == principal.getId())) throw new UnauthorisedException(); @@ -1417,7 +1501,7 @@ public class DataManagementPlanManager { /*ObjectMapper mapper = new ObjectMapper(); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);*/ String fileName = "DMP_" + dmp.getGrant().getLabel() + "_" + dmp.getVersion();//dmp.getLabel(); - fileName = fileName.replaceAll("[^a-zA-Z0-9+ ]", ""); + fileName = fileName.replaceAll("[^a-zA-Z0-9+ ]", "").replace(" ", "_").replace(",", "_"); String uuid = UUID.randomUUID().toString(); File file = new File(this.environment.getProperty("temp.temp") + uuid + ".json"); OutputStream output = new FileOutputStream(file); @@ -1456,11 +1540,12 @@ public class DataManagementPlanManager { default: file = getXmlDocument(id, principal); } + String fileName = file.getFilename().replace(" ", "_").replace(",", "_"); InputStream resource = new FileInputStream(file.getFile()); HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentLength(file.getFile().length()); responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); - responseHeaders.set("Content-Disposition", "attachment;filename=" + file.getFilename()); + responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName); responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition"); responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type"); @@ -1580,6 +1665,21 @@ public class DataManagementPlanManager { dm.setAssociatedUsers(associatedUsers); // Sets associatedUsers property. dm.setDynamicFields(dynamicFields); // Sets dynamicFields property. dm.setDefinition(dmpProfile); + ObjectMapper mapper = new ObjectMapper(); + Map extraPropertiesMap = new HashMap<>(); + if (dataManagementPlans.get(0).getLanguage() != null) { + extraPropertiesMap.put("language", dataManagementPlans.get(0).getLanguage()); + } + if (dataManagementPlans.get(0).getVisibility() != null) { + extraPropertiesMap.put("visible", dataManagementPlans.get(0).getVisibility()); + } + if (dataManagementPlans.get(0).getPublicDate() != null) { + extraPropertiesMap.put("publicDate", dataManagementPlans.get(0).getPublicDate()); + } + if (dataManagementPlans.get(0).getCosts() != null) { + extraPropertiesMap.put("costs", mapper.readValue(dataManagementPlans.get(0).getCosts(), ArrayList.class)); + } + dm.setExtraProperties(extraPropertiesMap); //createOrUpdate(apiContext, dm, principal); DMP dmp = this.createOrUpdate(dm, principal); @@ -1622,7 +1722,7 @@ public class DataManagementPlanManager { // datasets.add(new DatasetListingModel().fromDataModel(dataset)); } - logger.info(dm.toString()); + //logger.info(dm.toString()); } return dataManagementPlans; @@ -1654,6 +1754,8 @@ public class DataManagementPlanManager { databaseRepository.getDmpDao().createOrUpdate(dmp); assignUser(dmp, me); if (this.apiContext.getOperationsContext().getElasticRepository().getDmpRepository().getClient() != null) { + UUID dmpId = dmp.getId(); + dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); this.updateIndex(dmp); } dmp.getDataset().forEach(dataset -> { @@ -1676,13 +1778,13 @@ public class DataManagementPlanManager { try { List tags = new ArrayList<>(); eu.eudat.elastic.entities.Dataset elastic = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().findDocument(dataset.getId().toString()); + DatasetWizardModel datasetWizardModel = new DatasetWizardModel().fromDataModel(dataset); if (elastic != null) { tags = elastic.getTags(); - DatasetWizardModel datasetWizardModel = new DatasetWizardModel().fromDataModel(dataset); datasetWizardModel.setTags(tags); - datasetManager.getTagsFromProfile(datasetWizardModel, dataset); - datasetManager.updateTags(dataset, datasetWizardModel.getTags()); } + datasetManager.getTagsFromProfile(datasetWizardModel, dataset); + datasetManager.updateTags(dataset, datasetWizardModel.getTags()); } catch (Exception e) { logger.error(e.getMessage(), e); } @@ -1745,6 +1847,9 @@ public class DataManagementPlanManager { try { if (dmp.getUsers() != null) { logger.info(dmp.getUsers().toString()); + } else { + UUID dmpId = dmp.getId(); + dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); } this.updateIndex(dmp); } catch (IOException e) { @@ -1762,6 +1867,8 @@ public class DataManagementPlanManager { if (elastic != null) { tags = elastic.getTags(); } + UUID dmpId = dataset.getDmp().getId(); + dataset.getDmp().setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); this.datasetManager.updateTags(dataset, tags); } catch (Exception e) { logger.error(e.getMessage(), e); @@ -1770,7 +1877,7 @@ public class DataManagementPlanManager { }); } - private void updateIndex(DMP dmp) throws IOException { + public void updateIndex(DMP dmp) throws IOException { DmpMapper mapper = new DmpMapper(apiContext, datasetManager); Dmp elastic = mapper.toElastic(dmp); apiContext.getOperationsContext().getElasticRepository().getDmpRepository().createOrUpdate(elastic); @@ -1782,6 +1889,8 @@ public class DataManagementPlanManager { List dmps = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().asQueryable().toList(); dmps.forEach(dmp -> { try { + UUID dmpId = dmp.getId(); + dmp.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); this.updateIndex(dmp); } catch (IOException e) { logger.error(e.getMessage(), e); @@ -2016,10 +2125,10 @@ public class DataManagementPlanManager { } dataBuilder.append(" }\n").append("}"); createData = dataBuilder.toString(); - ObjectMapper mapper = new ObjectMapper(); + /*ObjectMapper mapper = new ObjectMapper(); mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true); - JsonNode createDataJSON = mapper.readTree(createData); - HttpEntity request = new HttpEntity<>(createDataJSON, headers); + JsonNode createDataJSON = mapper.readTree(createData);*/ + HttpEntity request = new HttpEntity<>(createData, headers); Map createResponse = null; LinkedHashMap links = null; String previousDOI = this.getPreviousDOI(dmp.getGroupId(), dmp.getId()); @@ -2081,19 +2190,19 @@ 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); - File pdfFile = datasetManager.convertToPDF(file, environment); - String fileName = name + ".pdf";*/ - FileSystemResource fileSystemResource = new FileSystemResource(file.getFile()); + String name = file.getFilename().substring(0, file.getFilename().length() - 5); + File pdfFile = PDFUtils.convertToPDF(file, environment); + String fileName = name + ".pdf"; + FileSystemResource fileSystemResource = new FileSystemResource(pdfFile); HttpEntity addFileMapRequest = new HttpEntity<>(fileSystemResource, null); - String addFileUrl = links.get("bucket") + "/" + file.getFilename() + "?access_token=" + zenodoToken; + String addFileUrl = links.get("bucket") + "/" + fileName + "?access_token=" + zenodoToken; restTemplate.put(addFileUrl, addFileMapRequest); Files.deleteIfExists(file.getFile().toPath()); ResponseEntity jsonFile; try { - jsonFile = getRDAJsonDocument(id.toString(), datasetManager, principal); + jsonFile = getRDAJsonDocument(id.toString(), principal); } catch (Exception e) { throw e; } @@ -2104,7 +2213,9 @@ public class DataManagementPlanManager { jsonFos.flush(); } fileSystemResource = new FileSystemResource(tempJsonFile); - addFileMapRequest = new HttpEntity<>(fileSystemResource, null); + HttpHeaders jsonHeaders = new HttpHeaders(); + jsonHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE); + addFileMapRequest = new HttpEntity<>(fileSystemResource, jsonHeaders); 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); @@ -2168,51 +2279,6 @@ 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 f8f25fc3a..6f1797776 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 @@ -104,7 +104,8 @@ public class DataManagementProfileManager { HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentLength(envelope.getFile().length()); responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); - responseHeaders.set("Content-Disposition", "attachment;filename=" + envelope.getFilename() + ".xml"); + String fileName = envelope.getFilename().replace(" ", "_").replace(",", "_"); + responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName + ".xml"); 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); 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 069e381e3..4cb98b59b 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 @@ -48,6 +48,7 @@ import eu.eudat.models.data.security.Principal; import eu.eudat.models.data.user.composite.PagedDatasetProfile; import eu.eudat.queryable.QueryableList; import eu.eudat.types.Authorities; +import eu.eudat.types.MetricNames; import org.apache.commons.io.IOUtils; import org.apache.poi.xwpf.extractor.XWPFWordExtractor; import org.apache.poi.xwpf.usermodel.XWPFDocument; @@ -66,6 +67,7 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -87,7 +89,6 @@ 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; import java.util.stream.Stream; import java.util.zip.ZipEntry; @@ -109,9 +110,10 @@ public class DatasetManager { private UserManager userManager; private ConfigLoader configLoader; private Environment environment; + private final MetricsManager metricsManager; @Autowired - public DatasetManager(ApiContext apiContext, UserManager userManager, ConfigLoader configLoader, Environment environment) { + public DatasetManager(ApiContext apiContext, UserManager userManager, ConfigLoader configLoader, Environment environment, MetricsManager metricsManager) { this.apiContext = apiContext; this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository(); this.datasetRepository = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository(); @@ -119,6 +121,7 @@ public class DatasetManager { this.userManager = userManager; this.configLoader = configLoader; this.environment = environment; + this.metricsManager = metricsManager; } public DataTableData getPaged(DatasetTableRequest datasetTableRequest, Principal principal) throws Exception { @@ -156,7 +159,7 @@ public class DatasetManager { List datasets; try { datasets = datasetRepository.exists() ? - datasetRepository.query(datasetCriteria) : null; + datasetRepository.queryIds(datasetCriteria) : null; } catch (Exception ex) { logger.warn(ex.getMessage(), ex); datasets = null; @@ -168,15 +171,16 @@ public class DatasetManager { if (datasets != null) { if (!datasets.isEmpty()) { - items = databaseRepository.getDatasetDao().asQueryable().withHint(HintedModelFactory.getHint(DatasetListingModel.class)); - List finalDatasets = datasets; - items.where((builder, root) -> root.get("id").in(finalDatasets.stream().map(x -> UUID.fromString(x.getId())).collect(Collectors.toList()))); + //items = databaseRepository.getDatasetDao().asQueryable().withHint(HintedModelFactory.getHint(DatasetListingModel.class)); + final List datasetIds = datasets.stream().map(datasetE -> UUID.fromString(datasetE.getId())).distinct().collect(Collectors.toList()); + items = databaseRepository.getDatasetDao().filterFromElastic(datasetTableRequest.getCriteria(), datasetIds).withHint(HintedModelFactory.getHint(DatasetListingModel.class));//.withFields(Collections.singletonList("id")); + //items.where((builder, root) -> root.get("id").in(datasetIds)); } else { - items = databaseRepository.getDatasetDao().getWithCriteria(datasetTableRequest.getCriteria()).withHint(HintedModelFactory.getHint(DatasetListingModel.class)); + items = databaseRepository.getDatasetDao().getWithCriteria(datasetTableRequest.getCriteria()).withHint(HintedModelFactory.getHint(DatasetListingModel.class));//.withFields(Collections.singletonList("id")); //items.where((builder, root) -> root.get("id").in(new UUID[]{UUID.randomUUID()})); } } else { - items = databaseRepository.getDatasetDao().getWithCriteria(datasetTableRequest.getCriteria()).withHint(HintedModelFactory.getHint(DatasetListingModel.class)); + items = databaseRepository.getDatasetDao().getWithCriteria(datasetTableRequest.getCriteria()).withHint(HintedModelFactory.getHint(DatasetListingModel.class));//.withFields(Collections.singletonList("id")); } List roles = new LinkedList<>(); QueryableList pagedItems; @@ -185,9 +189,10 @@ public class DatasetManager { if (principal.getId() == null) { throw new UnauthorisedException("You are not allowed to access those datasets"); } - if (datasetTableRequest.getCriteria().getRole() != null) + if (datasetTableRequest.getCriteria().getRole() != null) { roles.add(datasetTableRequest.getCriteria().getRole()); - authItems = databaseRepository.getDatasetDao().getAuthenticated(items, userInfo, roles); + } + authItems = databaseRepository.getDatasetDao().getAuthenticated(items, userInfo, roles).distinct(); pagedItems = PaginationManager.applyPaging(authItems, datasetTableRequest); } else { if (principal.getId() != null && datasetTableRequest.getCriteria().getRole() != null) { @@ -214,6 +219,7 @@ public class DatasetManager { } public DataTableData getPaged(DatasetPublicTableRequest datasetTableRequest, Principal principal) throws Exception { + Long count = 0L; DatasetCriteria datasetCriteria = new DatasetCriteria(); datasetCriteria.setLike(datasetTableRequest.getCriteria().getLike()); datasetCriteria.setDatasetTemplates(datasetTableRequest.getCriteria().getDatasetProfile()); @@ -227,7 +233,8 @@ public class DatasetManager { List datasets; try { datasets = datasetRepository.exists() ? - datasetRepository.query(datasetCriteria) : new LinkedList<>(); + datasetRepository.queryIds(datasetCriteria) : new LinkedList<>(); + count = datasetRepository.exists() ? datasetRepository.count(datasetCriteria) : 0L; } catch (Exception ex) { logger.warn(ex.getMessage()); datasets = null; @@ -253,22 +260,22 @@ public class DatasetManager { return builder.and(builder.equal(userJoin.join("user", JoinType.LEFT).get("id"), principal.getId()), builder.equal(userJoin.get("role"), datasetTableRequest.getCriteria().getRole())); }); } - String[] strings = new String[1]; - strings[0] = "-dmp:publishedAt|join|"; + List strings = new ArrayList<>(); + strings.add("-dmp:publishedAt|join|"); datasetTableRequest.getOrderings().setFields(strings); + if (count == 0L) { + count = items.count(); + } QueryableList pagedItems = PaginationManager.applyPaging(items, datasetTableRequest); DataTableData dataTable = new DataTableData<>(); - CompletableFuture> itemsFuture = pagedItems. - selectAsync(this::mapModel).whenComplete((resultList, throwable) -> { - dataTable.setData(resultList.stream().filter(Objects::nonNull).collect(Collectors.toList())); - }); + List datasetListis = pagedItems. + select(this::mapModel); - CompletableFuture countFuture = pagedItems.countAsync().whenComplete((count, throwable) -> { - dataTable.setTotalCount(count); - }); + dataTable.setData(datasetListis.stream().filter(Objects::nonNull).collect(Collectors.toList())); - CompletableFuture.allOf(itemsFuture, countFuture).join(); + dataTable.setTotalCount(count); + //CompletableFuture.allOf(countFuture).join(); return dataTable; } @@ -549,54 +556,6 @@ public class DatasetManager { return fileEnvelope; } - public File convertToPDF(FileEnvelope file, Environment environment) throws IOException, InterruptedException { - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - String uuid = UUID.randomUUID().toString(); - map.add("files", new FileSystemResource(file.getFile())); - map.add("filename", uuid + ".pdf"); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - headers.add("Content-disposition", "attachment; filename=" + uuid + ".pdf"); - headers.add("Content-type", "application/pdf"); - - HttpEntity> requestEntity = new HttpEntity>( - map, headers); - - byte[] queueResult = new RestTemplate().postForObject(environment.getProperty("pdf.converter.url") + "convert/office" - , requestEntity, byte[].class); - - File resultPdf = new File(environment.getProperty("temp.temp") + uuid + ".pdf"); - FileOutputStream output = new FileOutputStream(resultPdf); - IOUtils.write(queueResult, output); - output.close(); - Files.deleteIfExists(file.getFile().toPath()); - - return resultPdf; - } - - private File extractFromZip(File file, String filename) throws IOException { - byte[] buffer = new byte[1024]; - File newFile = new File(filename); - ZipInputStream zis = new ZipInputStream(new FileInputStream(file)); - ZipEntry zipEntry = zis.getNextEntry(); - while (zipEntry != null) { - String zippedFileName = zipEntry.getName(); - if (zippedFileName.equals("pdf")) { - - FileOutputStream fos = new FileOutputStream(newFile); - int len; - while ((len = zis.read(buffer)) > 0) { - fos.write(buffer, 0, len); - } - fos.close(); - zipEntry = zis.getNextEntry(); - } - } - zis.closeEntry(); - zis.close(); - return newFile; - } - public eu.eudat.data.entities.Dataset createOrUpdate(DatasetWizardModel datasetWizardModel, Principal principal) throws Exception { Boolean sendNotification = false; Dataset tempDataset = null; @@ -613,7 +572,7 @@ public class DatasetManager { sendNotification = true; } } else { - MetricsManager.increaseValue("argos_managed_dataset_descriptions", 1, "draft"); + metricsManager.increaseValue(MetricNames.DATASET, 1, MetricNames.DRAFT); } if (dmp.getStatus().equals(DMP.DMPStatus.FINALISED.getValue()) && datasetWizardModel.getId() != null) throw new Exception("DMP is finalized, therefore Dataset cannot be edited."); @@ -639,6 +598,8 @@ public class DatasetManager { } dataset1.setProfile(this.apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().find(datasetWizardModel.getProfile().getId())); // datasetWizardModel.setDatasetProfileDefinition(getPagedProfile(datasetWizardModel, dataset1)); + UUID dmpId = dataset1.getDmp().getId(); + dataset1.getDmp().setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); updateTags(dataset1, datasetWizardModel.getTags()); if (sendNotification) { if (dataset1.getStatus() != Dataset.Status.FINALISED.getValue()) { @@ -691,6 +652,9 @@ public class DatasetManager { datasetProfileValidators.add(node.getNodeValue()); } + expression = "//validation/@type[.=1]/ancestor::fieldSet"; + nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET); + JSONObject obj = new JSONObject(dataset.getProperties()); VisibilityRuleService visibilityRuleService = this.apiContext.getUtilitiesService().getVisibilityRuleService(); @@ -702,14 +666,37 @@ public class DatasetManager { for (String validator : datasetProfileValidators) { - if ((obj.getString(validator) == null || obj.getString(validator).trim().isEmpty()) && visibilityRuleService.isElementVisible(validator)) { + if ((obj.has(validator) && (obj.getString(validator) == null || obj.getString(validator).trim().isEmpty())) && isElementVisible(nodeList, validator, visibilityRuleService)) { throw new Exception("Field value of " + validator + " must be filled."); } } } + private boolean isElementVisible(NodeList nodeList, String id, VisibilityRuleService visibilityRuleService) { + Element fieldSet = null; + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + for (int j = 0; j < node.getChildNodes().getLength(); j++) { + Node fcnode = node.getChildNodes().item(j); + if (fcnode.getNodeName().equals("fields")) { + for(int k = 0; k < fcnode.getChildNodes().getLength(); k++) { + Node scnode = fcnode.getChildNodes().item(k); + if (scnode.getNodeName().equals("field") && scnode.getAttributes().getNamedItem("id").getNodeValue().equals(id)) { + fieldSet = (Element) node; + } + } + } + } + } + if (fieldSet != null) { + return visibilityRuleService.isElementVisible(id) && visibilityRuleService.isElementVisible(fieldSet.getAttribute("id")); + } else { + return visibilityRuleService.isElementVisible(id); + } + } + private String propertiesModelToString(PagedDatasetProfile pagedDatasetProfile) { - Map values = new HashMap(); + Map values = new LinkedHashMap<>(); pagedDatasetProfile.toMap(values); JSONObject jobject = new JSONObject(values); return jobject.toString(); @@ -857,7 +844,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"); + metricsManager.increaseValue(MetricNames.DATASET, 1, MetricNames.PUBLISHED); } public ResponseEntity getDocument(String id, VisibilityRuleService visibilityRuleService, String contentType, Principal principal) throws IllegalAccessException, IOException, InstantiationException { @@ -868,7 +855,8 @@ public class DatasetManager { HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentLength(envelope.getFile().length()); responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); - responseHeaders.set("Content-Disposition", "attachment;filename=" + envelope.getFilename() + ".xml"); + String fileName = envelope.getFilename().replace(" ", "_").replace(",", "_"); + responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName + ".xml"); responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition"); responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type"); @@ -946,7 +934,7 @@ public class DatasetManager { createServicesIfTheyDontExist(entity); createExternalDatasetsIfTheyDontExist(entity); - MetricsManager.increaseValue("argos_managed_dataset_descriptions", 1, "draft"); + metricsManager.increaseValue(MetricNames.DATASET, 1, MetricNames.DRAFT); return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().createOrUpdate(entity); } @@ -1028,6 +1016,8 @@ public class DatasetManager { datasetEntities.forEach(datasetEntity -> { try { eu.eudat.elastic.entities.Dataset dataset = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().findDocument(datasetEntity.getId().toString()); + UUID dmpId = datasetEntity.getDmp().getId(); + datasetEntity.getDmp().setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), dmpId)).toList())); updateTags(datasetEntity, dataset != null ? dataset.getTags() : null); } catch (Exception e) { logger.error(e.getMessage(), e); @@ -1052,22 +1042,26 @@ public class DatasetManager { ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(wizardModel.getDatasetProfileDefinition()); JsonNode propertiesJson = mapper.readTree(json); - List tags = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().query(new DatasetCriteria()).stream().map(eu.eudat.elastic.entities.Dataset::getTags).flatMap(Collection::stream).filter(StreamDistinctBy.distinctByKey(Tag::getId)).collect(Collectors.toList()); + DatasetCriteria criteria = new DatasetCriteria(); + criteria.setHasTags(true); + List tags = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().query(criteria).stream().map(eu.eudat.elastic.entities.Dataset::getTags).flatMap(Collection::stream).filter(StreamDistinctBy.distinctByKey(Tag::getId)).collect(Collectors.toList()); Set tagNodes = new HashSet<>(); tagNodes.addAll(JsonSearcher.findNodes(propertiesJson, "renderStyle", "tags", true)); tagNodes.addAll(JsonSearcher.findNodes(propertiesJson, "rdaProperty", "dataset.keyword")); if (!tagNodes.isEmpty()) { tagNodes.forEach(node -> { JsonNode value = node.get("value"); - String stringValue = value.asText().replaceAll("=", ":"); - JSONArray values = new JSONArray(stringValue); - if (values != null) { - values.iterator().forEachRemaining(element -> { - Map data = ((JSONObject) element).toMap(); - this.addTag(tags, wizardModel.getTags(), data.get("id").toString(), data.get("name").toString()); - }); - } else { - this.addTag(tags, wizardModel.getTags(), "", value.asText()); + if (!value.toString().equals("\"\"")) { + String stringValue = value.toString().replaceAll("=", ":"); + JSONArray values = new JSONArray(stringValue); + if (values != null) { + values.iterator().forEachRemaining(element -> { + Map data = ((JSONObject) element).toMap(); + this.addTag(tags, wizardModel.getTags(), data.get("id").toString(), data.get("name").toString()); + }); + } else { + this.addTag(tags, wizardModel.getTags(), "", value.asText()); + } } }); } @@ -1088,10 +1082,10 @@ public class DatasetManager { @Transactional private DatasetListingModel mapModel(Dataset item) { - if (item.getProfile() == null) - return null; + /*if (item.getProfile() == null) + return null;*/ DatasetListingModel listingModel = new DatasetListingModel().fromDataModel(item); - DatasetProfileCriteria criteria = new DatasetProfileCriteria(); + /*DatasetProfileCriteria criteria = new DatasetProfileCriteria(); criteria.setGroupIds(Collections.singletonList(item.getProfile().getGroupId())); List profiles = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(criteria).toList(); boolean islast = false; @@ -1099,31 +1093,7 @@ public class DatasetManager { profiles = profiles.stream().sorted(Comparator.comparing(DatasetProfile::getVersion)).collect(Collectors.toList()); islast = profiles.get(0).getId().equals(item.getProfile().getId()); } - listingModel.setProfileLatestVersion(islast); + 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 56e97cfb4..ee4bbd793 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 @@ -24,9 +24,11 @@ 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.mail.SimpleMail; import eu.eudat.models.data.security.Principal; import eu.eudat.queryable.QueryableList; import eu.eudat.types.Authorities; +import eu.eudat.types.MetricNames; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -48,6 +50,7 @@ import javax.xml.xpath.*; import java.io.*; import java.nio.file.Files; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -59,12 +62,14 @@ public class DatasetProfileManager { private DatabaseRepository databaseRepository; private Environment environment; private List cache; + private final MetricsManager metricsManager; @Autowired - public DatasetProfileManager(ApiContext apiContext, Environment environment) { + public DatasetProfileManager(ApiContext apiContext, Environment environment, MetricsManager metricsManager) { this.apiContext = apiContext; this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository(); this.environment = environment; + this.metricsManager = metricsManager; this.cache = new ArrayList<>(); } @@ -92,7 +97,7 @@ 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"); + metricsManager.increaseValue(MetricNames.DATASET_TEMPLATE, 1, MetricNames.TOTAL); return profile; } @@ -102,7 +107,8 @@ public class DatasetProfileManager { 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); + List roles = Collections.singletonList(1); + authItems = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getAuthenticated(items, principal.getId(), roles); } QueryableList pagedItems = PaginationManager.applyPaging(authItems, datasetProfileTableRequestItem); List datasetProfiles = pagedItems.select(item -> new DatasetProfileListingModel().fromDataModel(item)); @@ -170,7 +176,7 @@ public class DatasetProfileManager { response = restTemplate.exchange(url, HttpMethod.GET, entity, Object.class); jsonContext = JsonPath.parse(response.getBody()); jsonItems = jsonContext.read(singleData.getOptionsRoot() + "['" + singleData.getAutoCompleteOptions().getLabel() + "','" + singleData.getAutoCompleteOptions().getValue() + "','" + singleData.getAutoCompleteOptions().getSource() + "','" + "uri" + "']"); - jsonItems.forEach(item -> result.add(new ExternalAutocompleteFieldModel(item.get(singleData.getAutoCompleteOptions().getValue()), item.get(singleData.getAutoCompleteOptions().getLabel()), item.get(singleData.getAutoCompleteOptions().getSource()) != null ? item.get(singleData.getAutoCompleteOptions().getSource()) : singleData.getAutoCompleteOptions().getSource(), item.get("uri")))); + jsonItems.forEach(item -> result.add(new ExternalAutocompleteFieldModel(parseItem(item.get(singleData.getAutoCompleteOptions().getValue())), parseItem(item.get(singleData.getAutoCompleteOptions().getLabel())), item.get(singleData.getAutoCompleteOptions().getSource()) != null ? parseItem(item.get(singleData.getAutoCompleteOptions().getSource())) : singleData.getAutoCompleteOptions().getSource(), parseItem(item.get("uri"))))); break; case CACHED: headers.setAccept(Collections.singletonList(MediaType.valueOf("text/plain; charset=utf-8"))); @@ -196,6 +202,20 @@ public class DatasetProfileManager { //return result; } + private String parseItem(Object item) { + if (item instanceof String) { + return (String) item; + } + if (item instanceof List) { + List listedItems = (List) item; + return parseItem(listedItems.get(0)); + } + if (item instanceof Map) { + return (String) ((Map)item).get("$"); + } + return item.toString(); + } + public ResponseEntity getDocument(eu.eudat.models.data.user.composite.DatasetProfile datasetProfile, String label) throws IllegalAccessException, IOException, InstantiationException { FileEnvelope envelope = getXmlDocument(datasetProfile, label); InputStream resource = new FileInputStream(envelope.getFile()); @@ -204,7 +224,8 @@ public class DatasetProfileManager { HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setContentLength(envelope.getFile().length()); responseHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM); - responseHeaders.set("Content-Disposition", "attachment;filename=" + envelope.getFilename() + ".xml"); + String fileName = envelope.getFilename().replace(" ", "_").replace(",", "_"); + responseHeaders.set("Content-Disposition", "attachment;filename=" + fileName + ".xml"); responseHeaders.set("Access-Control-Expose-Headers", "Content-Disposition"); responseHeaders.get("Access-Control-Expose-Headers").add("Content-Type"); @@ -232,7 +253,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"); + metricsManager.increaseValue(MetricNames.DATASET_TEMPLATE, 1, MetricNames.TOTAL); return profile; } catch (IOException e) { logger.error(e.getMessage(), e); @@ -281,17 +302,29 @@ public class DatasetProfileManager { 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 -> { + 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); + sendJoinMail(userDatasetProfile1); + }); + entity.getUsers().stream().filter(userDatasetProfile -> model.getUsers().stream() + .filter(userInfoListingModel -> userDatasetProfile.getUser().getId().equals(userInfoListingModel.getId())).count() > 0 + && userDatasetProfile.getRole() == 2).forEach(userDatasetProfile -> { + userDatasetProfile.setRole(1); + apiContext.getOperationsContext().getDatabaseRepository().getUserDatasetProfileDao().createOrUpdate(userDatasetProfile); + sendJoinMail(userDatasetProfile); }); } 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 -> { + 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); }); @@ -312,24 +345,21 @@ public class DatasetProfileManager { } } - public long countAlldraft() { - DatasetProfileCriteria criteria = new DatasetProfileCriteria(); - criteria.setStatus(0); - return apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(criteria).count(); - } + private void sendJoinMail(UserDatasetProfile userDatasetProfile) { + SimpleMail mail = new SimpleMail(); + mail.setSubject(environment.getProperty("admin.mail.subject").replace( "{templateName}", userDatasetProfile.getDatasetProfile().getLabel())); + String content = apiContext.getUtilitiesService().getMailService().getMailTemplateContent(environment.getProperty("email.dataset.template")); + content = content.replace("{recipient}", userDatasetProfile.getUser().getName()); + content = content.replace("{templateName}", userDatasetProfile.getDatasetProfile().getLabel()); + content = content.replace("{host}", this.environment.getProperty("dmp.domain")); + content = content.replace("{templateID}", userDatasetProfile.getDatasetProfile().getId().toString()); + mail.setContent(content); + mail.setTo(userDatasetProfile.getUser().getEmail()); + try { + apiContext.getUtilitiesService().getMailService().sendSimpleMail(mail); + } catch (Exception ex) { + logger.error(ex.getMessage(), ex); + } - 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/DatasetWizardManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetWizardManager.java index b989d0153..c3c347bcb 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetWizardManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DatasetWizardManager.java @@ -1,15 +1,17 @@ package eu.eudat.logic.managers; +import eu.eudat.data.dao.criteria.DatasetProfileCriteria; import eu.eudat.data.dao.entities.DMPDao; - +import eu.eudat.data.dao.entities.DatasetProfileDao; import eu.eudat.data.entities.DMP; import eu.eudat.data.entities.Dataset; +import eu.eudat.data.entities.DatasetProfile; import eu.eudat.data.entities.UserInfo; +import eu.eudat.data.query.items.item.dataset.DatasetWizardAutocompleteRequest; +import eu.eudat.data.query.items.item.datasetprofile.DatasetProfileWizardAutocompleteRequest; import eu.eudat.exceptions.datasetwizard.DatasetWizardCannotUnlockException; import eu.eudat.logic.services.ApiContext; import eu.eudat.models.data.datasetwizard.DataManagentPlanListingModel; -import eu.eudat.data.query.items.item.datasetprofile.DatasetProfileWizardAutocompleteRequest; -import eu.eudat.data.query.items.item.dataset.DatasetWizardAutocompleteRequest; import eu.eudat.models.data.dmp.AssociatedProfile; import eu.eudat.models.data.dmp.DataManagementPlan; import eu.eudat.models.data.security.Principal; @@ -19,6 +21,7 @@ import java.io.IOException; import java.util.LinkedList; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; public class DatasetWizardManager { @@ -31,12 +34,18 @@ public class DatasetWizardManager { return dataManagementPlans; } - public static List getAvailableProfiles(DMPDao dmpRepository, DatasetProfileWizardAutocompleteRequest datasetProfileWizardAutocompleteRequest) throws InstantiationException, IllegalAccessException { + public static List getAvailableProfiles(DMPDao dmpRepository, DatasetProfileDao profileDao, DatasetProfileWizardAutocompleteRequest datasetProfileWizardAutocompleteRequest) throws InstantiationException, IllegalAccessException { DataManagementPlan dataManagementPlan = new DataManagementPlan().fromDataModel(dmpRepository.find(datasetProfileWizardAutocompleteRequest.getCriteria().getId())); if (dataManagementPlan.getProfiles() == null || dataManagementPlan.getProfiles().isEmpty()) { return new LinkedList<>(); } - List profiles = dataManagementPlan.getProfiles(); + DatasetProfileCriteria criteria = new DatasetProfileCriteria(); + criteria.setIds(dataManagementPlan.getProfiles().stream().map(AssociatedProfile::getId).collect(Collectors.toList())); + List datasetProfiles = profileDao.getWithCriteria(criteria).toList(); + criteria.setIds(null); + criteria.setGroupIds(datasetProfiles.stream().map(DatasetProfile::getGroupId).collect(Collectors.toList())); + datasetProfiles = profileDao.getWithCriteria(criteria).toList(); + List profiles = datasetProfiles.stream().map(profile -> new AssociatedProfile().fromData(profile)).collect(Collectors.toList()); return profiles; } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DocumentManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DocumentManager.java deleted file mode 100644 index c9419512d..000000000 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/DocumentManager.java +++ /dev/null @@ -1,159 +0,0 @@ -package eu.eudat.logic.managers; - -import eu.eudat.data.dao.entities.DatasetDao; -import eu.eudat.logic.proxy.config.configloaders.ConfigLoader; -import eu.eudat.logic.services.ApiContext; -import eu.eudat.logic.services.forms.VisibilityRuleService; -import eu.eudat.logic.utilities.documents.helpers.FileEnvelope; -import eu.eudat.logic.utilities.documents.word.WordBuilder; -import eu.eudat.logic.utilities.documents.xml.ExportXmlBuilder; -import eu.eudat.models.HintedModelFactory; -import eu.eudat.models.data.datasetwizard.DatasetWizardModel; -import eu.eudat.models.data.user.composite.PagedDatasetProfile; -import org.apache.commons.io.IOUtils; -import org.apache.poi.xwpf.usermodel.XWPFDocument; -import org.json.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.core.env.Environment; -import org.springframework.core.io.FileSystemResource; -import org.springframework.http.*; -import org.springframework.http.converter.ByteArrayHttpMessageConverter; -import org.springframework.stereotype.Service; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.web.client.RestTemplate; - -import java.io.*; -import java.net.URL; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -/** - * Created by ikalyvas on 10/16/2018. - */ -@Service -public class DocumentManager { - private static final Logger logger = LoggerFactory.getLogger(DocumentManager.class); - - private ApiContext context; - private DatasetManager datasetManager; - private ConfigLoader configLoader; - private Environment environment; - - public DocumentManager(ApiContext context, DatasetManager datasetManager, ConfigLoader configLoader, Environment environment) { - this.context = context; - this.datasetManager = datasetManager; - this.configLoader = configLoader; - this.environment = environment; - } - - public File getWordDocument(ConfigLoader configLoader, DatasetDao datatasetRepository, String id, VisibilityRuleService visibilityRuleService) throws InstantiationException, IllegalAccessException, IOException { - WordBuilder wordBuilder = new WordBuilder(); - DatasetWizardModel dataset = new DatasetWizardModel(); - XWPFDocument document = configLoader.getDocument(); - eu.eudat.data.entities.Dataset datasetEntity = datatasetRepository.find(UUID.fromString(id), HintedModelFactory.getHint(DatasetWizardModel.class)); - Map properties = new HashMap<>(); - if (datasetEntity.getProperties() != null) { - JSONObject jobject = new JSONObject(datasetEntity.getProperties()); - properties = jobject.toMap(); - } - PagedDatasetProfile pagedDatasetProfile = datasetManager.getPagedProfile(dataset, datasetEntity); - visibilityRuleService.setProperties(properties); - visibilityRuleService.buildVisibilityContext(pagedDatasetProfile.getRules()); - wordBuilder.build(document, pagedDatasetProfile, visibilityRuleService); - File exportFile = new File(dataset.getLabel() + ".docx"); - FileOutputStream out = new FileOutputStream(exportFile); - document.write(out); - out.close(); - return exportFile; - } - - public FileEnvelope getXmlDocument(eu.eudat.data.entities.Dataset datasetEntity, String id, VisibilityRuleService visibilityRuleService) throws InstantiationException, IllegalAccessException, IOException { - ExportXmlBuilder xmlBuilder = new ExportXmlBuilder(); - DatasetWizardModel dataset = new DatasetWizardModel(); - Map properties = new HashMap<>(); - if (datasetEntity.getProperties() != null) { - JSONObject jobject = new JSONObject(datasetEntity.getProperties()); - properties = jobject.toMap(); - } - PagedDatasetProfile pagedDatasetProfile = datasetManager.getPagedProfile(dataset, datasetEntity); - visibilityRuleService.setProperties(properties); - visibilityRuleService.buildVisibilityContext(pagedDatasetProfile.getRules()); - File file = xmlBuilder.build(pagedDatasetProfile, UUID.fromString(id), visibilityRuleService, environment); - FileEnvelope fileEnvelope = new FileEnvelope(); - fileEnvelope.setFile(file); - fileEnvelope.setFilename(datasetEntity.getLabel()); - return fileEnvelope; - } - - public File convertToPDF(File file, Environment environment, String label) throws IOException, InterruptedException { - LinkedMultiValueMap map = new LinkedMultiValueMap<>(); - map.add("file", new FileSystemResource(file)); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.MULTIPART_FORM_DATA); - headers.add("Content-disposition", "attachment; filename=" + label + ".docx"); - - headers.add("Content-type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); - - HttpEntity> requestEntity = new HttpEntity>( - map, headers); - - Map queueResult = new RestTemplate().postForObject( - environment.getProperty("pdf.converter.url") + - "api/v1/", requestEntity, Map.class); - - Map mediaResult = new RestTemplate().getForObject(environment.getProperty("pdf.converter.url") + - "/api/v1/" + queueResult.get("id"), Map.class); - logger.info("Status: " + mediaResult.get("status")); - while (!mediaResult.get("status").equals("finished")) { - Thread.sleep(500); - mediaResult = new RestTemplate().getForObject(environment.getProperty("pdf.converter.url") + - "api/v1/" + queueResult.get("id"), Map.class); - logger.info("Polling"); - } - RestTemplate restTemplate = new RestTemplate(); - restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter()); - HttpHeaders headers2 = new HttpHeaders(); - headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM)); - HttpEntity entity = new HttpEntity(headers2); - - ResponseEntity response = restTemplate.exchange(environment.getProperty("pdf.converter.url") + - mediaResult.get("result_url"), HttpMethod.GET, entity, byte[].class, "1"); - - UUID uuid = UUID.randomUUID(); - File zip = new File(uuid + ".zip"); - if (response.getStatusCode().equals(HttpStatus.OK)) { - FileOutputStream output = new FileOutputStream(zip); - IOUtils.write(response.getBody(), output); - } - return extractFromZip(zip, label + ".pdf"); - } - - private File extractFromZip(File file, String filename) throws IOException { - byte[] buffer = new byte[1024]; - File newFile = new File(filename); - ZipInputStream zis = new ZipInputStream(new FileInputStream(file)); - ZipEntry zipEntry = zis.getNextEntry(); - while (zipEntry != null) { - String zippedFileName = zipEntry.getName(); - if (zippedFileName.equals("pdf")) { - - FileOutputStream fos = new FileOutputStream(newFile); - int len; - while ((len = zis.read(buffer)) > 0) { - fos.write(buffer, 0, len); - } - fos.close(); - zipEntry = zis.getNextEntry(); - } - } - zis.closeEntry(); - zis.close(); - return newFile; - } -} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/InvitationsManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/InvitationsManager.java index 25a660855..8b11369d7 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/InvitationsManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/InvitationsManager.java @@ -3,6 +3,7 @@ package eu.eudat.logic.managers; import eu.eudat.data.entities.DMP; import eu.eudat.data.entities.UserDMP; import eu.eudat.data.entities.UserInfo; +import eu.eudat.data.query.items.item.userinfo.UserInfoRequestItem; import eu.eudat.exceptions.security.UnauthorisedException; import eu.eudat.logic.services.ApiContext; import eu.eudat.logic.utilities.helpers.StreamDistinctBy; @@ -17,6 +18,7 @@ import org.springframework.stereotype.Component; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; +import java.io.IOException; import java.io.StringReader; import java.util.*; import java.util.stream.Collectors; @@ -26,10 +28,12 @@ import java.util.stream.Stream; public class InvitationsManager { private ApiContext apiContext; + private DataManagementPlanManager dataManagementPlanManager; @Autowired - public InvitationsManager(ApiContext apiContext) { + public InvitationsManager(ApiContext apiContext, DataManagementPlanManager dataManagementPlanManager) { this.apiContext = apiContext; + this.dataManagementPlanManager = dataManagementPlanManager; } public void inviteUsers(Invitation invitation, Principal principal) throws Exception { @@ -79,7 +83,24 @@ public class InvitationsManager { return userModels; } - public UUID assignUserAcceptedInvitation(UUID invitationID, Principal principal) throws UnauthorisedException, JAXBException { + public List getUsersWithCriteria(Principal principal, UserInfoRequestItem userInfoRequestItem) throws IllegalAccessException, InstantiationException { + List users = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao() + .getAuthenticated(apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().asQueryable() + .where(((builder, root) -> + builder.notEqual(root.get("status"), DMP.DMPStatus.DELETED.getValue()))), principal.getId(), Stream.of(0, 1).collect(Collectors.toList())) + .toList().stream().map(DMP::getUsers).flatMap(Collection::stream).map(UserDMP::getUser) + .filter(userInfo -> !userInfo.getId().equals(principal.getId())).filter(StreamDistinctBy.distinctByKey(UserInfo::getId)) + .filter(userInfo -> (userInfoRequestItem == null || userInfoRequestItem.getCriteria() == null || userInfoRequestItem.getCriteria().getLike() == null + || userInfo.getName().toLowerCase().contains(userInfoRequestItem.getCriteria().getLike().toLowerCase()) + || (userInfo.getEmail().toLowerCase().contains(userInfoRequestItem.getCriteria().getLike().toLowerCase())))) + .collect(Collectors.toList()); +// .where((builder, root) -> builder.like(builder.upper(root.get("name")), "%" + userInfoRequestItem.getCriteria().getLike().toUpperCase() + "%")) + + List userModels = users.stream().map(userInfo -> new UserInfoInvitationModel().fromDataModel(userInfo)).collect(Collectors.toList()); + return userModels; + } + + public UUID assignUserAcceptedInvitation(UUID invitationID, Principal principal) throws UnauthorisedException, JAXBException, IOException { eu.eudat.data.entities.Invitation invitation = apiContext.getOperationsContext().getDatabaseRepository().getInvitationDao().find(invitationID); if (invitation == null) throw new UnauthorisedException("There is no Data Management Plan assigned to this Link"); @@ -98,6 +119,8 @@ public class InvitationsManager { apiContext.getUtilitiesService().getInvitationService().assignToDmp(apiContext.getOperationsContext().getDatabaseRepository().getDmpDao(), existingUserDMP, datamanagementPlan); invitation.setAcceptedInvitation(true); apiContext.getOperationsContext().getDatabaseRepository().getInvitationDao().createOrUpdate(invitation); + datamanagementPlan.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), datamanagementPlan.getId())).toList())); + dataManagementPlanManager.updateIndex(datamanagementPlan); return datamanagementPlan.getId(); } } else { @@ -121,6 +144,8 @@ public class InvitationsManager { apiContext.getUtilitiesService().getInvitationService().assignToDmp(apiContext.getOperationsContext().getDatabaseRepository().getDmpDao(), userDMP, datamanagementPlan); invitation.setAcceptedInvitation(true); apiContext.getOperationsContext().getDatabaseRepository().getInvitationDao().createOrUpdate(invitation); + datamanagementPlan.setUsers(new HashSet<>(apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().asQueryable().where((builder, root) -> builder.equal(root.get("dmp").get("id"), datamanagementPlan.getId())).toList())); + dataManagementPlanManager.updateIndex(datamanagementPlan); return datamanagementPlan.getId(); } return invitation.getDmp().getId(); 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 da06aedd3..39da2a9bf 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 @@ -42,7 +42,7 @@ public class LicenseManager { ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); licenseModels.addAll(remoteRepos.stream().map(item -> mapper.convertValue(item, LicenseModel.class)).collect(Collectors.toList())); - licenseModels = licenseModels.stream().filter(licenseModel -> licenseModel.getName().contains(query)).collect(Collectors.toList()); + licenseModels = licenseModels.stream().filter(licenseModel -> licenseModel.getName().toLowerCase().contains(query.toLowerCase())).collect(Collectors.toList()); return licenseModels; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LocalFetchManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LocalFetchManager.java index fa135debe..b60a73108 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LocalFetchManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/LocalFetchManager.java @@ -22,7 +22,7 @@ public class LocalFetchManager { public List getCurrency(String query) throws Exception { List> data = localFetcher.retrieveCurrency(); List result = data.stream().map(entry -> new LocalFetchModel(entry.get("name"), entry.get("value"))).collect(Collectors.toList()); - result = result.stream().filter(localFetchModel -> localFetchModel.getValue() != null).filter(StreamDistinctBy.distinctByKey(LocalFetchModel::getValue)).filter(localFetchModel -> localFetchModel.getName().contains(query)).collect(Collectors.toList()); + result = result.stream().filter(localFetchModel -> localFetchModel.getValue() != null).filter(StreamDistinctBy.distinctByKey(LocalFetchModel::getValue)).filter(localFetchModel -> localFetchModel.getName().toLowerCase().contains(query.toLowerCase())).collect(Collectors.toList()); return result; } } 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 index 353171093..aa356d1a0 100644 --- 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 @@ -1,5 +1,11 @@ package eu.eudat.logic.managers; +import eu.eudat.data.dao.criteria.*; +import eu.eudat.data.entities.DatasetProfile; +import eu.eudat.logic.services.ApiContext; +import eu.eudat.logic.utilities.helpers.StreamDistinctBy; +import eu.eudat.types.MetricNames; +import io.micrometer.prometheus.PrometheusMeterRegistry; import io.prometheus.client.Gauge; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,32 +19,20 @@ import javax.transaction.Transactional; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; -import java.util.Map; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.*; 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()}, + private final Map gauges; - {"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) { + public void increaseValue(String name, int amount, String label) { if(label != null) { gauges.get(name).labels(label).inc(amount); @@ -47,7 +41,7 @@ public class MetricsManager { } } - public static void decreaseValue(String name, int amount, String label) { + public void decreaseValue(String name, int amount, String label) { if(label != null) { gauges.get(name).labels(label).dec(amount); } else { @@ -55,7 +49,7 @@ public class MetricsManager { } } - public static Integer getValue(String name, String label) { + public Integer getValue(String name, String label) { if(label != null) { return Double.valueOf(gauges.get(name).labels(label).get()).intValue(); } else { @@ -63,7 +57,7 @@ public class MetricsManager { } } - public static void calculateValue(String name, int amount, String label) { + public void calculateValue(String name, int amount, String label) { Integer orig = getValue(name, label); int diff = orig - amount; if (diff != 0) { @@ -75,19 +69,51 @@ public class MetricsManager { } } - private final DatasetManager datasetManager; - private final DataManagementPlanManager dataManagementPlanManager; - private final DatasetProfileManager datasetProfileManager; + private final ApiContext apiContext; 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; + public MetricsManager(ApiContext apiContext, UserManager userManager, Environment environment, PrometheusMeterRegistry registry) { + this.apiContext = apiContext; this.userManager = userManager; this.environment = environment; + registry.clear(); + this.gauges = Stream.of( new Object[][]{ + {MetricNames.DMP, Gauge.build().name(MetricNames.DMP).help("Number of managed DMPs").labelNames("status").register(registry.getPrometheusRegistry())}, + {MetricNames.NEXUS + MetricNames.DMP, Gauge.build().name(MetricNames.NEXUS + MetricNames.DMP).help("Number of managed DMPs during Nexus").labelNames("status").register(registry.getPrometheusRegistry())}, + + {MetricNames.FUNDERS, Gauge.build().name(MetricNames.FUNDERS).help("Number of registered Funders").register(registry.getPrometheusRegistry())}, + {MetricNames.GRANTS, Gauge.build().name(MetricNames.GRANTS).help("Number of registered Grants").register(registry.getPrometheusRegistry())}, + {MetricNames.PROJECT, Gauge.build().name(MetricNames.PROJECT).help("Number of registered Projects").register(registry.getPrometheusRegistry())}, + {MetricNames.RESEARCHER, Gauge.build().name(MetricNames.RESEARCHER).help("Number of Colaborators/Researchers").register(registry.getPrometheusRegistry())}, + + {MetricNames.NEXUS + MetricNames.FUNDERS, Gauge.build().name(MetricNames.NEXUS + MetricNames.FUNDERS).help("Number of registered Funders during Nexus").register(registry.getPrometheusRegistry())}, + {MetricNames.NEXUS + MetricNames.GRANTS, Gauge.build().name(MetricNames.NEXUS + MetricNames.GRANTS).help("Number of registered Grants during Nexus").register(registry.getPrometheusRegistry())}, + {MetricNames.NEXUS + MetricNames.PROJECT, Gauge.build().name(MetricNames.NEXUS + MetricNames.PROJECT).help("Number of registered Projects during Nexus").register(registry.getPrometheusRegistry())}, + {MetricNames.NEXUS + MetricNames.RESEARCHER, Gauge.build().name(MetricNames.NEXUS + MetricNames.RESEARCHER).help("Number of Colaborators/Researchers during Nexus").register(registry.getPrometheusRegistry())}, + + {MetricNames.DATASET, Gauge.build().name(MetricNames.DATASET).help("Number of managed Dataset Descriptions").labelNames("status").register(registry.getPrometheusRegistry())}, + + {MetricNames.NEXUS + MetricNames.DATASET, Gauge.build().name(MetricNames.NEXUS + MetricNames.DATASET).help("Number of managed Dataset Descriptions during Nexus").labelNames("status").register(registry.getPrometheusRegistry())}, + + {MetricNames.DATASET_TEMPLATE, Gauge.build().name(MetricNames.DATASET_TEMPLATE).help("Number of dataset Templates").labelNames("status").register(registry.getPrometheusRegistry())}, + + {MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE, Gauge.build().name(MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE).help("Number of dataset Templates during Nexus").labelNames("status").register(registry.getPrometheusRegistry())}, + + {MetricNames.USERS, Gauge.build().name(MetricNames.USERS).help("Number of users").labelNames("type").register(registry.getPrometheusRegistry())}, + + {MetricNames.LANGUAGES, Gauge.build().name(MetricNames.LANGUAGES).help("Number of Languages").register(registry.getPrometheusRegistry())}, + + {MetricNames.DMP_WITH_GRANT, Gauge.build().name(MetricNames.DMP_WITH_GRANT).help("Number of Grants based on the status of the DMP that is using them").labelNames("status").register(registry.getPrometheusRegistry())}, + + {MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT, Gauge.build().name(MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT).help("Number of Grants based on the status of the DMP that is using them during Nexus").labelNames("status").register(registry.getPrometheusRegistry())}, + + {MetricNames.INSTALLATIONS, Gauge.build().name(MetricNames.INSTALLATIONS).help("Number of Installations").register(registry.getPrometheusRegistry())}, + {MetricNames.NEXUS + MetricNames.INSTALLATIONS, Gauge.build().name(MetricNames.NEXUS + MetricNames.INSTALLATIONS).help("Number of Installations").register(registry.getPrometheusRegistry())}, + + }).collect(Collectors.toMap(data -> (String)data[0], data -> (Gauge) data[1])); + } @PostConstruct @@ -95,32 +121,293 @@ public class MetricsManager { @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(MetricNames.DMP, (int) countAllDraftDMPs(), MetricNames.DRAFT); + calculateValue(MetricNames.DMP, (int) countAllFinalizedDMPs(), MetricNames.FINALIZED); + calculateValue(MetricNames.DMP, (int) countAllPublishedDMPs(), MetricNames.PUBLISHED); + calculateValue(MetricNames.DMP, (int) countAllDoiedDMPs(), MetricNames.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(MetricNames.NEXUS + MetricNames.DMP, (int) countAllDraftDMPs(true), MetricNames.DRAFT); + calculateValue(MetricNames.NEXUS + MetricNames.DMP, (int) countAllFinalizedDMPs(true), MetricNames.FINALIZED); + calculateValue(MetricNames.NEXUS + MetricNames.DMP, (int) countAllPublishedDMPs(true), MetricNames.PUBLISHED); + calculateValue(MetricNames.NEXUS + MetricNames.DMP, (int) countAllDoiedDMPs(true), MetricNames.DOIED); - 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(MetricNames.DMP_WITH_GRANT, (int) countAllDraftDMPsWithGrantId(), MetricNames.DRAFT); + calculateValue(MetricNames.DMP_WITH_GRANT, (int) countAllFinalizedDMPsWithGrantId(), MetricNames.FINALIZED); + calculateValue(MetricNames.DMP_WITH_GRANT, (int) countAllPublishedDMPsWithGrantId(), MetricNames.PUBLISHED); + calculateValue(MetricNames.DMP_WITH_GRANT, (int) countAllDoiedDMPsWithGrantId(), MetricNames.DOIED); - calculateValue("argos_dataset_templates", (int) datasetProfileManager.countAlldraft(), "draft"); - calculateValue("argos_dataset_templates", (int) datasetProfileManager.countAllFinalized(), "active"); - calculateValue("argos_dataset_templates", (int) datasetProfileManager.countAllUsed(), "used"); + calculateValue(MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT, (int) countAllDraftDMPsWithGrantId(true), MetricNames.DRAFT); + calculateValue(MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT, (int) countAllFinalizedDMPsWithGrantId(true), MetricNames.FINALIZED); + calculateValue(MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT, (int) countAllPublishedDMPsWithGrantId(true), MetricNames.PUBLISHED); + calculateValue(MetricNames.NEXUS + MetricNames.DMP_WITH_GRANT, (int) countAllDoiedDMPsWithGrantId(true), MetricNames.DOIED); - calculateValue("argos_users", (int) userManager.countActiveUsers().intValue(), "loggedin"); - calculateValue("argos_users", (int) userManager.countAllUsers().intValue(), "total"); + calculateValue(MetricNames.FUNDERS, (int) countAllFunders(), null); + calculateValue(MetricNames.GRANTS, (int) countAllGrants(), null); + calculateValue(MetricNames.PROJECT, (int) countAllProjects(), null); + calculateValue(MetricNames.RESEARCHER, (int) countAllResearchers(), null); + + calculateValue(MetricNames.NEXUS + MetricNames.FUNDERS, (int) countAllFunders(true), null); + calculateValue(MetricNames.NEXUS + MetricNames.GRANTS, (int) countAllGrants(true), null); + calculateValue(MetricNames.NEXUS + MetricNames.PROJECT, (int) countAllProjects(true), null); + calculateValue(MetricNames.NEXUS + MetricNames.RESEARCHER, (int) countAllResearchers(true), null); + + calculateValue(MetricNames.DATASET, (int) countAllDraftDatasets(), MetricNames.DRAFT); + calculateValue(MetricNames.DATASET, (int) countAllFinalizedDatasets(), MetricNames.FINALIZED); + calculateValue(MetricNames.DATASET, (int) countAllPublicDatasets(), MetricNames.PUBLISHED); + calculateValue(MetricNames.DATASET, (int) countAllDatasetsWithDoi(), MetricNames.DOIED); + + calculateValue(MetricNames.NEXUS + MetricNames.DATASET, (int) countAllDraftDatasets(true), MetricNames.DRAFT); + calculateValue(MetricNames.NEXUS + MetricNames.DATASET, (int) countAllFinalizedDatasets(true), MetricNames.FINALIZED); + calculateValue(MetricNames.NEXUS + MetricNames.DATASET, (int) countAllPublicDatasets(true), MetricNames.PUBLISHED); + calculateValue(MetricNames.NEXUS + MetricNames.DATASET, (int) countAllDatasetsWithDoi(true), MetricNames.DOIED); + + calculateValue(MetricNames.DATASET_TEMPLATE, (int) countAllDraftTemplates(), MetricNames.DRAFT); + calculateValue(MetricNames.DATASET_TEMPLATE, (int) countAllFinalizedTemplates(), MetricNames.ACTIVE); + calculateValue(MetricNames.DATASET_TEMPLATE, (int) countAllUsedTemplates(), MetricNames.USED); + + calculateValue(MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE, (int) countAllDraftTemplates(true), MetricNames.DRAFT); + calculateValue(MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE, (int) countAllFinalizedTemplates(true), MetricNames.ACTIVE); + calculateValue(MetricNames.NEXUS + MetricNames.DATASET_TEMPLATE, (int) countAllUsedTemplates(true), MetricNames.USED); + + calculateValue(MetricNames.USERS, (int) userManager.countActiveUsers().intValue(), MetricNames.LOGGEDIN); + calculateValue(MetricNames.USERS, (int) userManager.countAllUsers().intValue(), MetricNames.TOTAL); long files = Files.list(Paths.get(this.environment.getProperty("userguide.path"))).count(); - calculateValue("argos_languages", (int) files, null); + calculateValue(MetricNames.LANGUAGES, (int) files, null); + + calculateValue(MetricNames.INSTALLATIONS, 1, null); + calculateValue(MetricNames.NEXUS + MetricNames.INSTALLATIONS, 1, null); logger.info("Metrics calculation Completed"); } + private Date getNexusDate() { + try { + return new SimpleDateFormat("yyyy-MM-dd").parse("2021-01-01"); + } catch (ParseException e) { + logger.error(e.getLocalizedMessage(), e); + } + return Date.from(LocalDate.of(2021, 1, 1).atStartOfDay(ZoneId.systemDefault()).toInstant()); + } + + private long countAllDraftDMPs(){ + return countAllDraftDMPs(false); + } + + private long countAllDraftDMPs(boolean countNexus) { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setStatus(0); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).count(); + } + + private long countAllFinalizedDMPs() { + return countAllFinalizedDMPs(false); + } + + private long countAllFinalizedDMPs(boolean countNexus) { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setStatus(1); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).count(); + } + + private long countAllPublishedDMPs() { + return countAllPublishedDMPs(false); + } + + private long countAllPublishedDMPs(boolean countNexus) { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setIsPublic(true); + criteria.setOnlyPublic(true); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).count(); + } + + private long countAllDoiedDMPs() { + return countAllDoiedDMPs(false); + } + + private long countAllDoiedDMPs(boolean countNexus) { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setHasDoi(true); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).count(); + } + + private long countAllDraftDMPsWithGrantId() { + return countAllDraftDMPsWithGrantId(false); + } + + private long countAllDraftDMPsWithGrantId(boolean countNexus) { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setStatus(0); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).groupBy((builder, root) -> root.get("grant")).count(); + } + + private long countAllFinalizedDMPsWithGrantId() { + return countAllFinalizedDMPsWithGrantId(false); + } + + private long countAllFinalizedDMPsWithGrantId(boolean countNexus) { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setStatus(1); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).groupBy((builder, root) -> root.get("grant")).count(); + } + + private long countAllPublishedDMPsWithGrantId() { + return countAllPublishedDMPsWithGrantId(false); + } + + private long countAllPublishedDMPsWithGrantId(boolean countNexus) { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setIsPublic(true); + criteria.setOnlyPublic(true); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).groupBy((builder, root) -> root.get("grant")).count(); + } + + private long countAllDoiedDMPsWithGrantId() { + return countAllDoiedDMPsWithGrantId(false); + } + + private long countAllDoiedDMPsWithGrantId(boolean countNexus) { + DataManagementPlanCriteria criteria = new DataManagementPlanCriteria(); + criteria.setHasDoi(true); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().getWithCriteria(criteria).groupBy((builder, root) -> root.get("grant")).count(); + } + + private long countAllResearchers() { + return countAllResearchers(false); + } + + private long countAllResearchers(boolean countNexus) { + ResearcherCriteria criteria = new ResearcherCriteria(); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getResearcherDao().getWithCriteria(criteria).count(); + } + + private long countAllProjects() { + return countAllProjects(false); + } + + private long countAllProjects(boolean countNexus) { + ProjectCriteria criteria = new ProjectCriteria(); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getProjectDao().getWithCritetia(criteria).count(); + } + + private long countAllFunders() { + return countAllFunders(false); + } + + private long countAllFunders(boolean countNexus) { + FunderCriteria criteria = new FunderCriteria(); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getFunderDao().getWithCritetia(criteria).count(); + } + + private long countAllGrants() { + return countAllGrants(false); + } + + private long countAllGrants(boolean countNexus) { + GrantCriteria criteria = new GrantCriteria(); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getGrantDao().getWithCriteria(criteria).count(); + } + + public long countAllDraftDatasets() { + return countAllDraftDatasets(false); + } + + public long countAllDraftDatasets(boolean countNexus) { + eu.eudat.data.dao.criteria.DatasetCriteria criteria = new eu.eudat.data.dao.criteria.DatasetCriteria(); + criteria.setStatus(0); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).count(); + } + + public long countAllFinalizedDatasets() { + return countAllFinalizedDatasets(false); + } + + public long countAllFinalizedDatasets(boolean countNexus) { + eu.eudat.data.dao.criteria.DatasetCriteria criteria = new eu.eudat.data.dao.criteria.DatasetCriteria(); + criteria.setStatus(1); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).count(); + } + + public long countAllPublicDatasets() { + return countAllPublicDatasets(false); + } + + public long countAllPublicDatasets(boolean countNexus) { + eu.eudat.data.dao.criteria.DatasetCriteria criteria = new eu.eudat.data.dao.criteria.DatasetCriteria(); + criteria.setIsPublic(true); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).count(); + } + + public long countAllDatasetsWithDoi() { + return countAllDatasetsWithDoi(false); + } + + public long countAllDatasetsWithDoi(boolean countNexus) { + eu.eudat.data.dao.criteria.DatasetCriteria criteria = new eu.eudat.data.dao.criteria.DatasetCriteria(); + criteria.setHasDoi(true); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(criteria).count(); + } + + public long countAllDraftTemplates() { + return countAllDraftTemplates(false); + } + + public long countAllDraftTemplates(boolean countNexus) { + DatasetProfileCriteria criteria = new DatasetProfileCriteria(); + criteria.setStatus(0); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(criteria).count(); + } + + public long countAllFinalizedTemplates() { + return countAllFinalizedTemplates(false); + } + + public long countAllFinalizedTemplates(boolean countNexus) { + DatasetProfileCriteria criteria = new DatasetProfileCriteria(); + criteria.setStatus(1); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + return apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(criteria).count(); + } + + @Transactional + public long countAllUsedTemplates() { + return countAllUsedTemplates(false); + } + + @Transactional + public long countAllUsedTemplates(boolean countNexus) { + DatasetProfileCriteria criteria = new DatasetProfileCriteria(); + criteria.setStatus(1); + criteria.setAllVersions(false); + if (countNexus) criteria.setPeriodStart(getNexusDate()); + List datasetProfiles = apiContext.getOperationsContext().getDatabaseRepository().getDatasetProfileDao().getWithCriteria(criteria).toList(); + List filteredProfiles = new ArrayList<>(); + for (DatasetProfile datasetProfile : datasetProfiles) { + DatasetCriteria datasetCriteria = new DatasetCriteria(); + datasetCriteria.setDatasetTemplates(Collections.singletonList(datasetProfile.getId())); + Long datasetCount = apiContext.getOperationsContext().getDatabaseRepository().getDatasetDao().getWithCriteria(datasetCriteria).count(); + if (datasetCount > 0) { + filteredProfiles.add(datasetProfile); + } + } + return filteredProfiles.stream().filter(StreamDistinctBy.distinctByKey(DatasetProfile::getId)).count(); + } + } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/RDAManager.java b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/RDAManager.java index 242a365c9..f277d98ac 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/managers/RDAManager.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/managers/RDAManager.java @@ -1,6 +1,5 @@ package eu.eudat.logic.managers; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.data.entities.DMP; @@ -12,7 +11,6 @@ import org.springframework.stereotype.Component; import javax.transaction.Transactional; import java.io.IOException; -import java.io.Serializable; import java.text.SimpleDateFormat; @Component @@ -43,9 +41,8 @@ public class RDAManager { public DMP convertToEntity(String json, String[] profiles) throws IOException { ObjectMapper mapper = new ObjectMapper(); - mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")); - Dmp rda = mapper.readValue(json, RDAModel.class).getDmp(); + Dmp rda = mapper.readValue(json, RDAModel.class).getDmp(); return dmpRDAMapper.toEntity(rda, profiles); } } 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 4dfe09d9a..e7b00d0e6 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 @@ -167,7 +167,7 @@ public class UserManager { try { this.updateDOIToken(ZenodoAccessType.REFRESH_TOKEN, principal.getZenodoRefresh(), this.environment.getProperty("zenodo.login.redirect_uri"), principal); return true; - }catch (IOException e) { + }catch (Exception e) { this.deleteDOIToken(principal); throw new ExpiredTokenException("Zenodo Token is expired."); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/DatasetMapper.java b/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/DatasetMapper.java index dad3924d9..96eae1dca 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/DatasetMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/DatasetMapper.java @@ -31,6 +31,7 @@ public class DatasetMapper { if (tags != null && !tags.isEmpty()) { DatasetCriteria criteria = new DatasetCriteria(); criteria.setTags(tags); + criteria.setHasTags(true); List tags1 = apiContext.getOperationsContext().getElasticRepository().getDatasetRepository().query(criteria).stream().map(eu.eudat.elastic.entities.Dataset::getTags).flatMap(Collection::stream) .filter(StreamDistinctBy.distinctByKey(Tag::getId)).filter(tag -> tags.stream().anyMatch(tag1 -> tag1.getName().equals(tag.getName()))).collect(Collectors.toList()); if (tags1.isEmpty()) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/DmpMapper.java b/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/DmpMapper.java index 92eb02bfe..cd0961ae5 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/DmpMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/DmpMapper.java @@ -67,6 +67,7 @@ public class DmpMapper { if (dataset1 != null) { tags = dataset1.getTags(); } + dataset.setDmp(dmp); return datasetMapper.toElastic(dataset, tags); } catch (Exception e) { logger.error(e.getMessage(), e); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/criteria/DmpCriteriaMapper.java b/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/criteria/DmpCriteriaMapper.java index 0fbf92ce8..d2345621f 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/criteria/DmpCriteriaMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/mapper/elastic/criteria/DmpCriteriaMapper.java @@ -45,7 +45,7 @@ public class DmpCriteriaMapper { public static List toElasticSorting(ColumnOrderings columnOrderings) { List sortCriteria = new ArrayList<>(); - if (columnOrderings.getFieldOrderings() != null && columnOrderings.getFieldOrderings().length > 0) { + if (columnOrderings.getFieldOrderings() != null && !columnOrderings.getFieldOrderings().isEmpty()) { for (Ordering ordering: columnOrderings.getFieldOrderings()) { SortCriteria sortCriteria1 = new SortCriteria(); sortCriteria1.setFieldName(ordering.getFieldName() + (ordering.getFieldName().contains("label") ?".keyword" : "")); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrlCriteria.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrlCriteria.java index 5d837eea2..a982067c1 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrlCriteria.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/ExternalUrlCriteria.java @@ -58,4 +58,16 @@ public class ExternalUrlCriteria { public ExternalUrlCriteria() { } + + @Override + public String toString() { + return "{" + + "like='" + like + '\'' + + ", page='" + page + '\'' + + ", pageSize='" + pageSize + '\'' + + ", funderId='" + funderId + '\'' + + ", path='" + path + '\'' + + ", host='" + host + '\'' + + '}'; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DevelConfigLoader.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DefaultConfigLoader.java similarity index 73% rename from dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DevelConfigLoader.java rename to dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DefaultConfigLoader.java index 14d5099f0..50ec81046 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DevelConfigLoader.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/DefaultConfigLoader.java @@ -8,7 +8,6 @@ import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Profile; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import org.w3c.dom.Document; @@ -26,16 +25,13 @@ import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import java.io.*; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; @Service("configLoader") -@Profile("devel") -public class DevelConfigLoader implements ConfigLoader { - private static final Logger logger = LoggerFactory.getLogger(DevelConfigLoader.class); +//@Profile("devel") +public class DefaultConfigLoader implements ConfigLoader { + private static final Logger logger = LoggerFactory.getLogger(DefaultConfigLoader.class); private ExternalUrls externalUrls; private List rdaProperties; @@ -53,10 +49,10 @@ public class DevelConfigLoader implements ConfigLoader { try { JAXBContext jaxbContext = JAXBContext.newInstance(ExternalUrls.class); Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); - is = getClass().getClassLoader().getResource(fileUrl).openStream(); + is = getStreamFromPath(fileUrl); externalUrls = (ExternalUrls) jaxbUnmarshaller.unmarshal(is); } catch (Exception ex) { - logger.error("Cannot find resource in classpath", ex); + logger.error("Cannot find resource", ex); } finally { try { if (is != null) is.close(); @@ -68,10 +64,11 @@ public class DevelConfigLoader implements ConfigLoader { private void setRdaProperties() { String filePath = environment.getProperty("configuration.rda"); + logger.info("Loaded also config file: " + filePath); BufferedReader reader; List rdaList = new LinkedList<>(); try { - reader = new BufferedReader(new FileReader(getClass().getClassLoader().getResource(filePath).getFile())); + reader = new BufferedReader(new InputStreamReader(getStreamFromPath(filePath))); String line = reader.readLine(); while (line != null) { rdaList.add(line); @@ -87,9 +84,10 @@ public class DevelConfigLoader implements ConfigLoader { private void setDocument() { String filePath = environment.getProperty("configuration.h2020template"); + logger.info("Loaded also config file: " + filePath); InputStream is = null; try { - is = getClass().getClassLoader().getResource(filePath).openStream(); + is = getStreamFromPath(filePath); this.document = new XWPFDocument(is); } catch (IOException | NullPointerException e) { logger.error(e.getMessage(), e); @@ -104,9 +102,10 @@ public class DevelConfigLoader implements ConfigLoader { private void setConfigurableProviders() { String filePath = environment.getProperty("configuration.configurable_login_providers"); + logger.info("Loaded also config file: " + filePath); InputStream is = null; try { - is = getClass().getClassLoader().getResource(filePath).openStream(); + is = getStreamFromPath(filePath); ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); this.configurableProviders = mapper.readValue(is, ConfigurableProviders.class); } catch (IOException | NullPointerException e) { @@ -145,12 +144,34 @@ public class DevelConfigLoader implements ConfigLoader { public ExternalUrls getExternalUrls() { - this.setExternalUrls(); + if (externalUrls == null) { + externalUrls = new ExternalUrls(); + this.setExternalUrls(); + } else { + while (externalUrls.getMaxresults() == null) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + logger.error(e.getMessage(), e); + } + } + } return externalUrls; } public List getRdaProperties() { - this.setRdaProperties(); + if (rdaProperties == null) { + rdaProperties = new ArrayList<>(); + this.setRdaProperties(); + } else { + while (rdaProperties.size() == 0) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + logger.error(e.getMessage(), e); + } + } + } return rdaProperties; } @@ -160,12 +181,34 @@ public class DevelConfigLoader implements ConfigLoader { } public ConfigurableProviders getConfigurableProviders() { - this.setConfigurableProviders(); + if (configurableProviders == null) { + configurableProviders = new ConfigurableProviders(); + this.setConfigurableProviders(); + } else { + while (configurableProviders.getProviders().size() == 0) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + logger.error(e.getMessage(), e); + } + } + } return configurableProviders; } public Map getKeyToSourceMap() { - this.setKeyToSourceMap(); + if (keyToSourceMap == null) { + keyToSourceMap = new HashMap<>(); + this.setKeyToSourceMap(); + } else { + while (keyToSourceMap.size() == 0) { + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + logger.error(e.getMessage(), e); + } + } + } return keyToSourceMap; } @@ -173,7 +216,7 @@ public class DevelConfigLoader implements ConfigLoader { InputStream is = null; Document doc; try { - is = getClass().getClassLoader().getResource(filePath).openStream(); + is = getStreamFromPath(filePath); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); doc = documentBuilder.parse(is); @@ -211,4 +254,13 @@ public class DevelConfigLoader implements ConfigLoader { } return values; } + + private InputStream getStreamFromPath(String filePath) { + try { + return new FileInputStream(filePath); + } catch (FileNotFoundException e) { + logger.info("loading from classpath"); + return getClass().getClassLoader().getResourceAsStream(filePath); + } + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ProductionConfigLoader.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ProductionConfigLoader.java index 1e2d36e3b..7f3c48109 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ProductionConfigLoader.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/config/configloaders/ProductionConfigLoader.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +/* @Service("configLoader") @Profile({ "production", "staging" }) public class ProductionConfigLoader implements ConfigLoader { @@ -236,4 +237,4 @@ public class ProductionConfigLoader implements ConfigLoader { } return values; } -} \ No newline at end of file +}*/ diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/LocalFetcher.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/LocalFetcher.java index 5562f1f89..12514ad40 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/LocalFetcher.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/LocalFetcher.java @@ -8,7 +8,6 @@ import org.springframework.stereotype.Component; import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; -import java.beans.PropertyDescriptor; import java.io.InputStream; import java.lang.reflect.Method; import java.util.*; @@ -22,6 +21,7 @@ public class LocalFetcher { JAXBContext context = JAXBContext.newInstance(Config.class); Unmarshaller unmarshaller = context.createUnmarshaller(); Config config = (Config) unmarshaller.unmarshal(is); + is.close(); ConfigSingle currencyConfig = config.getConfigs().stream().filter(configSingle -> configSingle.getType().equals("currency")).findFirst().get(); return retrieveData(currencyConfig); @@ -39,10 +39,11 @@ public class LocalFetcher { Unmarshaller unmarshaller = context.createUnmarshaller(); Object object = unmarshaller.unmarshal(is); - + is.close(); Method reader = null; if (configSingle.getParseField() != null && !configSingle.getParseField().isEmpty()) { - reader = new PropertyDescriptor(configSingle.getParseField(), aClass).getReadMethod(); + String camelCaseGetter =configSingle.getParseField() != null && !configSingle.getParseField().isEmpty() ? "get" + configSingle.getParseField().substring(0, 1).toUpperCase() + configSingle.getParseField().substring(1) : ""; + reader = aClass.getMethod(camelCaseGetter); } ObjectMapper objectMapper = new ObjectMapper(); List> values = new ArrayList<>(); 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 7b1bb2ac9..54fc21aee 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 @@ -12,29 +12,20 @@ import eu.eudat.logic.proxy.config.UrlConfiguration; import eu.eudat.logic.proxy.config.configloaders.ConfigLoader; import eu.eudat.logic.proxy.config.exceptions.HugeResultSet; import eu.eudat.logic.proxy.config.exceptions.NoURLFound; -import net.minidev.json.JSONArray; +import eu.eudat.logic.proxy.fetching.entities.Results; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; +import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import javax.xml.bind.JAXBContext; import javax.xml.bind.Unmarshaller; -import java.beans.PropertyDescriptor; -import java.io.*; +import java.io.File; +import java.io.StringReader; import java.lang.reflect.Method; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLEncoder; import java.nio.file.Paths; import java.util.*; import java.util.stream.Collectors; @@ -50,7 +41,7 @@ public class RemoteFetcher { this.configLoader = configLoader; } - @Cacheable("repositories") + @Cacheable(value = "repositories", keyGenerator = "externalUrlsKeyGenerator") public List> getRepositories(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet { List urlConfigs = key != null && !key.isEmpty() ? configLoader.getExternalUrls().getRepositories().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList()) @@ -59,28 +50,28 @@ public class RemoteFetcher { return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } - @Cacheable("grants") + @Cacheable(value = "grants", keyGenerator = "externalUrlsKeyGenerator") public List> getGrants(ExternalUrlCriteria externalUrlCriteria) throws NoURLFound, HugeResultSet { List urlConfigs = configLoader.getExternalUrls().getGrants().getUrls(); FetchStrategy fetchStrategy = configLoader.getExternalUrls().getGrants().getFetchMode(); return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } - @Cacheable("projects") + @Cacheable(value = "projects", keyGenerator = "externalUrlsKeyGenerator") public List> getProjects(ExternalUrlCriteria externalUrlCriteria) throws NoURLFound, HugeResultSet { List urlConfigs = configLoader.getExternalUrls().getProjects().getUrls(); FetchStrategy fetchStrategy = configLoader.getExternalUrls().getProjects().getFetchMode(); return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } - @Cacheable("funders") + @Cacheable(value = "funders", keyGenerator = "externalUrlsKeyGenerator") public List> getFunders(ExternalUrlCriteria externalUrlCriteria) throws NoURLFound, HugeResultSet { List urlConfigs = configLoader.getExternalUrls().getFunders().getUrls(); FetchStrategy fetchStrategy = configLoader.getExternalUrls().getFunders().getFetchMode(); return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } - @Cacheable("organisations") + @Cacheable(value = "organisations", keyGenerator = "externalUrlsKeyGenerator") public List> getOrganisations(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet { List urlConfigs = key != null && !key.isEmpty() ? configLoader.getExternalUrls().getOrganisations().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList()) @@ -89,7 +80,7 @@ public class RemoteFetcher { return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } - @Cacheable("registries") + @Cacheable(value = "registries", keyGenerator = "externalUrlsKeyGenerator") public List> getRegistries(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet { List urlConfigs = key != null && !key.isEmpty() ? configLoader.getExternalUrls().getRegistries().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList()) @@ -98,7 +89,7 @@ public class RemoteFetcher { return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } - @Cacheable("services") + @Cacheable(value = "services", keyGenerator = "externalUrlsKeyGenerator") public List> getServices(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet { List urlConfigs = key != null && !key.isEmpty() ? configLoader.getExternalUrls().getServices().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList()) @@ -107,7 +98,7 @@ public class RemoteFetcher { return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } - @Cacheable("researchers") + @Cacheable(value = "researchers", keyGenerator = "externalUrlsKeyGenerator") public List> getResearchers(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet { List urlConfigs = key != null && !key.isEmpty() ? configLoader.getExternalUrls().getResearchers().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList()) @@ -125,7 +116,7 @@ public class RemoteFetcher { return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); }*/ - @Cacheable("externalDatasets") + @Cacheable(value = "externalDatasets", keyGenerator = "externalUrlsKeyGenerator") public List> getDatasets(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet { List urlConfigs = key != null && !key.isEmpty() ? configLoader.getExternalUrls().getDatasets().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList()) @@ -134,7 +125,7 @@ public class RemoteFetcher { return getAll(urlConfigs, fetchStrategy, externalUrlCriteria); } - @Cacheable("licenses") + @Cacheable(value = "licenses", keyGenerator = "externalUrlsKeyGenerator") public List> getlicenses(ExternalUrlCriteria externalUrlCriteria, String key) throws NoURLFound, HugeResultSet { List urlConfigs = key != null && !key.isEmpty() ? configLoader.getExternalUrls().getLicenses().getUrls().stream().filter(item -> item.getKey().equals(key)).collect(Collectors.toList()) @@ -153,22 +144,36 @@ public class RemoteFetcher { } - private List> getAll(List urlConfigs, FetchStrategy fetchStrategy, ExternalUrlCriteria externalUrlCriteria) throws NoURLFound, HugeResultSet { + private List> getAll(List urlConfigs, FetchStrategy fetchStrategy, ExternalUrlCriteria externalUrlCriteria) { List> results = new LinkedList<>(); - if (urlConfigs == null || urlConfigs.isEmpty()) return results; + if (urlConfigs == null || urlConfigs.isEmpty()) { + return results; + } // throw new NoURLFound("No Repository urls found in configuration"); urlConfigs.sort(Comparator.comparing(UrlConfiguration::getOrdinal)); - for (UrlConfiguration urlConfig : urlConfigs) { + urlConfigs.forEach(urlConfiguration -> { + ifFunderQueryExist(urlConfiguration, externalUrlCriteria); + if (urlConfiguration.getType() == null || urlConfiguration.getType().equals("External")) { + try { + results.addAll(getAllResultsFromUrl(urlConfiguration.getUrl(), fetchStrategy, urlConfiguration.getData(), urlConfiguration.getPaginationPath(), externalUrlCriteria, urlConfiguration.getLabel(), urlConfiguration.getKey(), urlConfiguration.getContentType(), urlConfiguration.getFirstpage(), urlConfiguration.getRequestBody(), urlConfiguration.getRequestType())); + } catch (Exception e) { + logger.error(e.getLocalizedMessage(), e); + } + } else if (urlConfiguration.getType() != null && urlConfiguration.getType().equals("Internal")) { + results.addAll(getAllResultsFromMockUpJson(urlConfiguration.getUrl(), externalUrlCriteria.getLike())); + } + }); + /* 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(), urlConfig.getRequestBody(), urlConfig.getRequestType())); } else if (urlConfig.getType() != null && urlConfig.getType().equals("Internal")) { results.addAll(getAllResultsFromMockUpJson(urlConfig.getUrl(), externalUrlCriteria.getLike())); } - } + }*/ return results; } @@ -183,7 +188,7 @@ public class RemoteFetcher { } } - private String replaceCriteriaOnUrl(String path, ExternalUrlCriteria externalUrlCriteria, String firstPage) { + protected String replaceCriteriaOnUrl(String path, ExternalUrlCriteria externalUrlCriteria, String firstPage) { String completedPath = path; if (externalUrlCriteria.getLike() != null) { if ((path.contains("openaire") || path.contains("orcid") || path.contains("ror")) && externalUrlCriteria.getLike().equals("")) { @@ -233,7 +238,7 @@ 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, String requestBody, String requestType) 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 Exception { Set pages = new HashSet<>(); String replacedPath = replaceCriteriaOnUrl(path, externalUrlCriteria, firstPage); @@ -253,6 +258,7 @@ public class RemoteFetcher { Optional optionalResults = pages.parallelStream() .map(page -> getResultsFromUrl(path + "&page=" + page, jsonDataPath, jsonPaginationPath, contentType, replacedBody, requestType)) + .filter(Objects::nonNull) .reduce((result1, result2) -> { result1.getResults().addAll(result2.getResults()); return result1; @@ -264,7 +270,7 @@ public class RemoteFetcher { } - private Results getResultsFromUrl(String urlString, DataUrlConfiguration jsonDataPath, String jsonPaginationPath, String contentType, String requestBody, String requestType) { + protected Results getResultsFromUrl(String urlString, DataUrlConfiguration jsonDataPath, String jsonPaginationPath, String contentType, String requestBody, String requestType) { try { RestTemplate restTemplate = new RestTemplate(); @@ -292,103 +298,23 @@ public class RemoteFetcher { DocumentContext jsonContext = JsonPath.parse(response.getBody()); if (jsonDataPath.getFieldsUrlConfiguration().getSource() != null) { - results = new Results(jsonContext.read(jsonDataPath.getPath() - + "[" + jsonDataPath.getFieldsUrlConfiguration().getName() + "," + jsonDataPath.getFieldsUrlConfiguration().getDescription() - + "," + jsonDataPath.getFieldsUrlConfiguration().getUri() + "," + jsonDataPath.getFieldsUrlConfiguration().getId() - + "," + jsonDataPath.getFieldsUrlConfiguration().getSource() + "]"), - new HashMap<>(1, 1)); + results = RemoteFetcherUtils.getFromJsonWithSource(jsonContext, jsonDataPath); } else if (jsonDataPath.getFieldsUrlConfiguration().getCount() != null) { // parsing services.openaire.eu - results = new Results(jsonContext.read(jsonDataPath.getPath() - + "[" + jsonDataPath.getFieldsUrlConfiguration().getName() - + "," + jsonDataPath.getFieldsUrlConfiguration().getId() + "]"), - new HashMap<>(1, 1)); - List> fixedResults = results.getResults().stream().map(item -> { - for (int i = 0; i < 2; i++) { - String id; - if (i == 0) { - id = jsonDataPath.getFieldsUrlConfiguration().getId().replace("'", ""); - } else { - id = jsonDataPath.getFieldsUrlConfiguration().getName().replace("'", ""); - } - if (!(item.get(id) instanceof String)) { - Object obj = item.get(id); - if (obj instanceof JSONArray) { - JSONArray jarr = (JSONArray) obj; - if (jarr.get(0) instanceof String) { - item.put(id, jarr.get(0).toString()); - } else { - for (int j = 0; j < jarr.size(); j++) { - mapToMap(id, (Map)jarr.get(j), item, i == 1); - } - } - } else { - if (obj instanceof Map) { - mapToMap(id, (Map) obj, item, i == 1); - } else if (obj != null){ - item.put(id, obj.toString()); - } - } - } - } - return item; - }).collect(Collectors.toList()); - - results = new Results(fixedResults, new HashMap<>(1, 1)); + results = RemoteFetcherUtils.getFromJsonWithParsedData(jsonContext, jsonDataPath); } else if (jsonDataPath.getFieldsUrlConfiguration().getPath() != null) { - results = new Results(jsonContext.read(jsonDataPath.getPath() - + "[" + jsonDataPath.getFieldsUrlConfiguration().getPath() - + "," + jsonDataPath.getFieldsUrlConfiguration().getHost() + "]"), - new HashMap<>(1, 1)); - - List> multiResults = results.results.stream().map(result -> { - ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(); - 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(), requestBody, requestType); - }).filter(Objects::nonNull).map(results1 -> results1.results.get(0)).collect(Collectors.toList()); - results = new Results(multiResults, new HashMap<>(1, 1)); + results = RemoteFetcherUtils.getFromJsonWithRecursiveFetching(jsonContext, jsonDataPath, this, requestBody, requestType); } else if (jsonDataPath.getFieldsUrlConfiguration().getTypes() != null) { - List> tempRes = jsonContext.read(jsonDataPath.getPath() - + "[" + jsonDataPath.getFieldsUrlConfiguration().getId() + "," + jsonDataPath.getFieldsUrlConfiguration().getName() - + "," + jsonDataPath.getFieldsUrlConfiguration().getTypes() + "," + jsonDataPath.getFieldsUrlConfiguration().getUri() + "]"); - List> finalRes = new ArrayList<>(); - tempRes.forEach(map -> { - Map resMap = new HashMap<>(); - map.forEach((key, value) -> { - if (key.equals(jsonDataPath.getFieldsUrlConfiguration().getTypes().substring(1, jsonDataPath.getFieldsUrlConfiguration().getTypes().length() - 1))) { - resMap.put("tags", ((JSONArray) value).toJSONString()); - } else if (key.equals(jsonDataPath.getFieldsUrlConfiguration().getUri().substring(1, jsonDataPath.getFieldsUrlConfiguration().getTypes().length() - 1))) { - resMap.put(key, ((JSONArray) value).toJSONString()); - } else { - resMap.put(key, (String) value); - } - }); - - finalRes.add(resMap); - }); - - results = new Results(finalRes, - new HashMap<>(1, 1)); + results = RemoteFetcherUtils.getFromJsonWithType(jsonContext, jsonDataPath); } else if (jsonDataPath.getFieldsUrlConfiguration().getFirstName() != null) { - results = new Results(jsonContext.read(jsonDataPath.getPath() - + "[" + jsonDataPath.getFieldsUrlConfiguration().getId() + "," + jsonDataPath.getFieldsUrlConfiguration().getFirstName() - + "," + jsonDataPath.getFieldsUrlConfiguration().getLastName() + "]"), - new HashMap<>(1, 1)); - results.getResults().stream().forEach(entry -> { - String name = entry.get(jsonDataPath.getFieldsUrlConfiguration().getFirstName().replace("'", "")) + " " + entry.get(jsonDataPath.getFieldsUrlConfiguration().getLastName().replace("'", "")); - entry.put("name", name); - entry.remove(jsonDataPath.getFieldsUrlConfiguration().getFirstName().replace("'", "")); - entry.remove(jsonDataPath.getFieldsUrlConfiguration().getLastName().replace("'", "")); - }); + results = RemoteFetcherUtils.getFromJsonWithFirstAndLastName(jsonContext, jsonDataPath); } else { results = new Results(jsonContext.read(jsonDataPath.getPath() + "[" + jsonDataPath.getFieldsUrlConfiguration().getName() + "," + jsonDataPath.getFieldsUrlConfiguration().getDescription() + "," + jsonDataPath.getFieldsUrlConfiguration().getUri() + "," + jsonDataPath.getFieldsUrlConfiguration().getId() + "]"), new HashMap<>(1, 1)); } - results.results = results.results.stream().map(e -> e.entrySet().stream().collect(Collectors.toMap(x -> this.transformKey(jsonDataPath,x.getKey()), Map.Entry::getValue))) - .collect(Collectors.toList()); + results.setResults(results.getResults().stream().map(e -> e.entrySet().stream().collect(Collectors.toMap(x -> this.transformKey(jsonDataPath,x.getKey()), Map.Entry::getValue))) + .collect(Collectors.toList())); } else if (response.getHeaders().get("Content-Type").get(0).contains("xml")) { Class aClass = Class.forName(jsonDataPath.getParseClass()); @@ -398,7 +324,8 @@ public class RemoteFetcher { Object data = unmarshaller.unmarshal(stringReader); Method reader = null; if (jsonDataPath.getParseField() != null && !jsonDataPath.getParseField().isEmpty()) { - reader = new PropertyDescriptor(jsonDataPath.getParseField(), aClass).getReadMethod(); + String camelCaseGetter = jsonDataPath.getParseField() != null && !jsonDataPath.getParseField().isEmpty() ? "get" + jsonDataPath.getParseField().substring(0, 1).toUpperCase() + jsonDataPath.getParseField().substring(1) : ""; + reader = aClass.getMethod(camelCaseGetter); } ObjectMapper objectMapper = new ObjectMapper(); List> values = new ArrayList<>(); @@ -448,8 +375,6 @@ public class RemoteFetcher { } catch (Exception exception) { logger.error(exception.getMessage(), exception); } //maybe print smth... - finally { - } return null; } @@ -459,7 +384,7 @@ public class RemoteFetcher { try { String filePath = Paths.get(path).toUri().toURL().toString(); ObjectMapper mapper = new ObjectMapper(); - internalResults = mapper.readValue(new File(filePath), new TypeReference>>(){}); + internalResults = mapper.readValue(new File(filePath), new TypeReference>>(){}); return searchListMap(internalResults, query); } catch (Exception e) { logger.error(e.getMessage(), e); @@ -494,48 +419,7 @@ public class RemoteFetcher { } - static class Results { - List> results; - Map pagination; - Results() { - this.results = new ArrayList<>(); - this.pagination = new HashMap<>(); - } - Results(List> results, Map pagination) { - this.results = results; - this.pagination = pagination; - } - List> getResults() { - return results; - } - - public void setResults(List> results) { - this.results = results; - } - - Map getPagination() { - return pagination; - } - - public void setPagination(Map pagination) { - this.pagination = pagination; - } - } - - private void mapToMap(String key, Map source, Map destination, boolean isTitle) { - if (source != null) { - String content = source.get("content"); - if (isTitle) { - String classId = source.get("classid"); - if (classId.equals("main title")) { - destination.put(key, content); - } - } else { - destination.put(key, content); - } - } - } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcherUtils.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcherUtils.java new file mode 100644 index 000000000..a518ca2f7 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/RemoteFetcherUtils.java @@ -0,0 +1,128 @@ +package eu.eudat.logic.proxy.fetching; + +import com.jayway.jsonpath.DocumentContext; +import eu.eudat.logic.proxy.config.DataUrlConfiguration; +import eu.eudat.logic.proxy.config.ExternalUrlCriteria; +import eu.eudat.logic.proxy.fetching.entities.Results; +import net.minidev.json.JSONArray; + +import java.util.*; +import java.util.stream.Collectors; + +public class RemoteFetcherUtils { + + public static Results getFromJsonWithSource(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath) { + return new Results(jsonContext.read(jsonDataPath.getPath() + + "[" + jsonDataPath.getFieldsUrlConfiguration().getName() + "," + jsonDataPath.getFieldsUrlConfiguration().getDescription() + + "," + jsonDataPath.getFieldsUrlConfiguration().getUri() + "," + jsonDataPath.getFieldsUrlConfiguration().getId() + + "," + jsonDataPath.getFieldsUrlConfiguration().getSource() + "]"), + new HashMap<>(1, 1)); + } + + public static Results getFromJsonWithParsedData(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath) { + Results results = new Results(jsonContext.read(jsonDataPath.getPath() + + "[" + jsonDataPath.getFieldsUrlConfiguration().getName() + + "," + jsonDataPath.getFieldsUrlConfiguration().getId() + "]"), + new HashMap<>(1, 1)); + List> fixedResults = results.getResults().stream().map(item -> { + for (int i = 0; i < 2; i++) { + String id; + if (i == 0) { + id = jsonDataPath.getFieldsUrlConfiguration().getId().replace("'", ""); + } else { + id = jsonDataPath.getFieldsUrlConfiguration().getName().replace("'", ""); + } + if (!(item.get(id) instanceof String)) { + Object obj = item.get(id); + if (obj instanceof JSONArray) { + JSONArray jarr = (JSONArray) obj; + if (jarr.get(0) instanceof String) { + item.put(id, jarr.get(0).toString()); + } else { + for (int j = 0; j < jarr.size(); j++) { + mapToMap(id, (Map)jarr.get(j), item, i == 1); + } + } + } else { + if (obj instanceof Map) { + mapToMap(id, (Map) obj, item, i == 1); + } else if (obj != null){ + item.put(id, obj.toString()); + } + } + } + } + return item; + }).collect(Collectors.toList()); + + return new Results(fixedResults, new HashMap<>(1, 1)); + } + + public static Results getFromJsonWithRecursiveFetching(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath, RemoteFetcher remoteFetcher, String requestBody, String requestType) { + Results results = new Results(jsonContext.read(jsonDataPath.getPath() + + "[" + jsonDataPath.getFieldsUrlConfiguration().getPath() + + "," + jsonDataPath.getFieldsUrlConfiguration().getHost() + "]"), + new HashMap<>(1, 1)); + + List> multiResults = results.getResults().stream().map(result -> { + ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(); + externalUrlCriteria.setPath(result.get("path")); + externalUrlCriteria.setHost(result.get("host")); + String replacedPath = remoteFetcher.replaceCriteriaOnUrl(jsonDataPath.getUrlConfiguration().getUrl(), externalUrlCriteria, jsonDataPath.getUrlConfiguration().getFirstpage()); + return remoteFetcher.getResultsFromUrl(replacedPath, jsonDataPath.getUrlConfiguration().getData(), jsonDataPath.getUrlConfiguration().getData().getPath(), jsonDataPath.getUrlConfiguration().getContentType(), requestBody, requestType); + }).filter(Objects::nonNull).map(results1 -> results1.getResults().get(0)).collect(Collectors.toList()); + return new Results(multiResults, new HashMap<>(1, 1)); + } + + public static Results getFromJsonWithType(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath) { + List> tempRes = jsonContext.read(jsonDataPath.getPath() + + "[" + jsonDataPath.getFieldsUrlConfiguration().getId() + "," + jsonDataPath.getFieldsUrlConfiguration().getName() + + "," + jsonDataPath.getFieldsUrlConfiguration().getTypes() + "," + jsonDataPath.getFieldsUrlConfiguration().getUri() + "]"); + List> finalRes = new ArrayList<>(); + tempRes.forEach(map -> { + Map resMap = new HashMap<>(); + map.forEach((key, value) -> { + if (key.equals(jsonDataPath.getFieldsUrlConfiguration().getTypes().substring(1, jsonDataPath.getFieldsUrlConfiguration().getTypes().length() - 1))) { + resMap.put("tags", ((JSONArray) value).toJSONString()); + } else if (key.equals(jsonDataPath.getFieldsUrlConfiguration().getUri().substring(1, jsonDataPath.getFieldsUrlConfiguration().getTypes().length() - 1))) { + resMap.put(key, ((JSONArray) value).toJSONString()); + } else { + resMap.put(key, (String) value); + } + }); + + finalRes.add(resMap); + }); + + return new Results(finalRes, + new HashMap<>(1, 1)); + } + + public static Results getFromJsonWithFirstAndLastName(DocumentContext jsonContext, DataUrlConfiguration jsonDataPath) { + Results results = new Results(jsonContext.read(jsonDataPath.getPath() + + "[" + jsonDataPath.getFieldsUrlConfiguration().getId() + "," + jsonDataPath.getFieldsUrlConfiguration().getFirstName() + + "," + jsonDataPath.getFieldsUrlConfiguration().getLastName() + "]"), + new HashMap<>(1, 1)); + results.getResults().stream().forEach(entry -> { + String name = entry.get(jsonDataPath.getFieldsUrlConfiguration().getFirstName().replace("'", "")) + " " + entry.get(jsonDataPath.getFieldsUrlConfiguration().getLastName().replace("'", "")); + entry.put("name", name); + entry.remove(jsonDataPath.getFieldsUrlConfiguration().getFirstName().replace("'", "")); + entry.remove(jsonDataPath.getFieldsUrlConfiguration().getLastName().replace("'", "")); + }); + return results; + } + + private static void mapToMap(String key, Map source, Map destination, boolean isTitle) { + if (source != null) { + String content = source.get("content"); + /*if (isTitle) { + String classId = source.get("classid"); + if (classId.equals("main title")) { + destination.put(key, content); + } + } else {*/ + destination.put(key, content); +// } + } + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/entities/Results.java b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/entities/Results.java new file mode 100644 index 000000000..7cd0067e7 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/proxy/fetching/entities/Results.java @@ -0,0 +1,37 @@ +package eu.eudat.logic.proxy.fetching.entities; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Results { + List> results; + Map pagination; + + public Results() { + this.results = new ArrayList<>(); + this.pagination = new HashMap<>(); + } + + public Results(List> results, Map pagination) { + this.results = results; + this.pagination = pagination; + } + + public List> getResults() { + return results; + } + + public void setResults(List> results) { + this.results = results; + } + + public Map getPagination() { + return pagination; + } + + public void setPagination(Map pagination) { + this.pagination = pagination; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/forms/VisibilityRuleService.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/forms/VisibilityRuleService.java index cb2c2d0ee..5b664b36a 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/forms/VisibilityRuleService.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/forms/VisibilityRuleService.java @@ -9,7 +9,7 @@ import java.util.Map; * Created by ikalyvas on 3/5/2018. */ public interface VisibilityRuleService { - Boolean isElementVisible(String id); + boolean isElementVisible(String id); void buildVisibilityContext(List sources); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/forms/VisibilityRuleServiceImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/forms/VisibilityRuleServiceImpl.java index 61435c407..d46e138de 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/forms/VisibilityRuleServiceImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/forms/VisibilityRuleServiceImpl.java @@ -3,9 +3,7 @@ package eu.eudat.logic.services.forms; import eu.eudat.models.data.user.components.commons.Rule; import org.springframework.stereotype.Service; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Created by ikalyvas on 3/5/2018. @@ -16,13 +14,22 @@ public class VisibilityRuleServiceImpl implements VisibilityRuleService { private VisibilityContext visibilityContext; private Map properties; - public Boolean isElementVisible(String id) { + public boolean isElementVisible(String id) { if (!this.elementVisibility.containsKey(id) || this.elementVisibility.get(id)) return true; return false; } public void setProperties(Map properties) { this.properties = properties; + this.properties.entrySet().stream() + .filter(stringObjectEntry -> stringObjectEntry.getValue() instanceof String && ((String) stringObjectEntry.getValue()).startsWith("[") && ((String) stringObjectEntry.getValue()).endsWith("]")).forEach(stringObjectEntry -> { + stringObjectEntry.setValue(parseArray((String) stringObjectEntry.getValue())); + }); + } + + private List parseArray(String original) { + String parsed = original.replace("[", "").replace("\"", "").replace("]", ""); + return Arrays.asList(parsed.split(",")); } public void buildVisibilityContext(List sources) { @@ -34,12 +41,19 @@ public class VisibilityRuleServiceImpl implements VisibilityRuleService { private void evaluateVisibility(VisibilityRule rule) { List sources = rule.getVisibilityRuleSources(); for(int i = 0; i < sources.size(); i++){ - if (!properties.containsKey(sources.get(i).getVisibilityRuleSourceId()) || !properties.get(sources.get(i).getVisibilityRuleSourceId()).equals(sources.get(i).getVisibilityRuleSourceValue())) { + if (properties.containsKey(sources.get(i).getVisibilityRuleSourceId()) && (isContained(properties.get(sources.get(i).getVisibilityRuleSourceId()), sources.get(i).getVisibilityRuleSourceValue()) || properties.get(sources.get(i).getVisibilityRuleSourceId()).equals(sources.get(i).getVisibilityRuleSourceValue()))) { + this.elementVisibility.put(rule.getVisibilityRuleTargetId(), true); + }else{ this.elementVisibility.put(rule.getVisibilityRuleTargetId(), false); return; - }else{ - this.elementVisibility.put(rule.getVisibilityRuleTargetId(), true); } } } + + private Boolean isContained(Object values, String source) { + if (values instanceof List) { + return ((Collection) values).contains(source); + } + return false; + } } 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 e9f347342..2983bfa1d 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 @@ -100,7 +100,7 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer UserToken userToken = this.apiContext.getOperationsContext().getBuilderFactory().getBuilder(UserTokenBuilder.class) .issuedAt(new Date()).user(credential.getUserInfo()) - .token(UUID.randomUUID()).expiresAt(Timestamp.valueOf(LocalDateTime.now().plusDays(1))) + .token(UUID.randomUUID()).expiresAt(Timestamp.valueOf(LocalDateTime.now().plusDays(10))) .build(); userToken = apiContext.getOperationsContext().getDatabaseRepository().getUserTokenDao().createOrUpdate(userToken); @@ -200,7 +200,7 @@ public abstract class AbstractAuthenticationService implements AuthenticationSer UserToken userToken = this.apiContext.getOperationsContext().getBuilderFactory().getBuilder(UserTokenBuilder.class) .token(UUID.randomUUID()).user(userInfo) - .expiresAt(Timestamp.valueOf(LocalDateTime.now().plusDays(1))).issuedAt(new Date()) + .expiresAt(Timestamp.valueOf(LocalDateTime.now().plusDays(10))).issuedAt(new Date()) .build(); apiContext.getOperationsContext().getDatabaseRepository().getUserTokenDao().createOrUpdate(userToken); 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 61ced9d27..f089d15c0 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 @@ -61,7 +61,7 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { } private String createContent(UUID confirmationToken, MailService mailService) { - String content = mailService.getMailTemplateContent("classpath:templates/email/emailConfirmation.html"); + String content = mailService.getMailTemplateContent(this.environment.getProperty("email.confirmation")); 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")); @@ -85,7 +85,7 @@ public class ConfirmationEmailServiceImpl implements ConfirmationEmailService { } private String createMergeContent(UUID confirmationToken, MailService mailService, String userName) { - String content = mailService.getMailTemplateContent("classpath:templates/email/emailMergeConfirmation.html"); + String content = mailService.getMailTemplateContent(this.environment.getProperty("email.merge")); 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")))); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/InvitationServiceImpl.java b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/InvitationServiceImpl.java index d2d893470..1fdca59fb 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/InvitationServiceImpl.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/services/utilities/InvitationServiceImpl.java @@ -80,7 +80,7 @@ public class InvitationServiceImpl implements InvitationService { return CompletableFuture.runAsync(() -> { SimpleMail mail = new SimpleMail(); mail.setSubject(createSubject(dmp, mailService.getMailTemplateSubject())); - mail.setContent(createContent(invitation.getId(), dmp, recipient, mailService.getMailTemplateContent("classpath:templates/email/email.html"), role)); + mail.setContent(createContent(invitation.getId(), dmp, recipient, mailService.getMailTemplateContent(this.environment.getProperty("email.invite")), role)); mail.setTo(invitation.getInvitationEmail()); try { mailService.sendSimpleMail(mail); 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 0a782c6f2..3f73d4996 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 @@ -86,6 +86,7 @@ public class MailServiceImpl implements MailService { InputStream inputStream = resource.getInputStream(); StringWriter writer = new StringWriter(); IOUtils.copy(inputStream, writer, "UTF-8"); + inputStream.close(); return writer.toString(); } catch (IOException e) { logger.error(e.getMessage(), e); 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 90b1b806e..31c4727b2 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 @@ -84,6 +84,7 @@ public class ModelBuilder { if (type.equals("checkBox")) return (FieldData) new CheckBoxData().fromData(data); if (type.equals("freetext")) return (FieldData) new FreeTextData().fromData(data); if (type.equals("textarea")) return (FieldData) new TextAreaData().fromData(data); + if (type.equals("richTextarea")) return (FieldData) new RichTextAreaData().fromData(data); if (type.equals("datePicker")) return (FieldData) new DatePickerData().fromData(data); if (type.equals("externalDatasets")) return (FieldData) new ExternalDatasetsData().fromData(data); if (type.equals("dataRepositories")) return (FieldData) new DataRepositoriesData().fromData(data); @@ -123,6 +124,7 @@ public class ModelBuilder { if (type.equals("checkBox")) return (FieldData) new CheckBoxData().fromData(data); if (type.equals("freetext")) return (FieldData) new FreeTextData().fromData(data); if (type.equals("textarea")) return (FieldData) new TextAreaData().fromData(data); + if (type.equals("richTextarea")) return (FieldData) new RichTextAreaData().fromData(data); if (type.equals("datePicker")) return (FieldData) new DatePickerData().fromData(data); if (type.equals("externalDatasets")) return (FieldData) new ExternalDatasetsData().fromData(data); if (type.equals("dataRepositories")) return (FieldData) new DataRepositoriesData().fromData(data); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/pdf/PDFUtils.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/pdf/PDFUtils.java new file mode 100644 index 000000000..6f842b1f3 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/pdf/PDFUtils.java @@ -0,0 +1,45 @@ +package eu.eudat.logic.utilities.documents.pdf; + +import eu.eudat.logic.utilities.documents.helpers.FileEnvelope; +import org.apache.commons.io.IOUtils; +import org.springframework.core.env.Environment; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.util.UUID; + +public class PDFUtils { + + public static File convertToPDF(FileEnvelope file, Environment environment) throws IOException { + LinkedMultiValueMap map = new LinkedMultiValueMap<>(); + String uuid = UUID.randomUUID().toString(); + map.add("files", new FileSystemResource(file.getFile())); + map.add("filename", uuid + ".pdf"); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + headers.add("Content-disposition", "attachment; filename=" + uuid + ".pdf"); + headers.add("Content-type", "application/pdf"); + + HttpEntity> requestEntity = new HttpEntity>( + map, headers); + + byte[] queueResult = new RestTemplate().postForObject(environment.getProperty("pdf.converter.url") + "forms/libreoffice/convert" + , requestEntity, byte[].class); + + File resultPdf = new File(environment.getProperty("temp.temp") + uuid + ".pdf"); + FileOutputStream output = new FileOutputStream(resultPdf); + IOUtils.write(queueResult, output); + output.close(); + Files.deleteIfExists(file.getFile().toPath()); + + return resultPdf; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/types/ParagraphStyle.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/types/ParagraphStyle.java index ba104013d..db6e0e207 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/types/ParagraphStyle.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/types/ParagraphStyle.java @@ -4,7 +4,7 @@ package eu.eudat.logic.utilities.documents.types; * Created by ikalyvas on 2/26/2018. */ public enum ParagraphStyle { - TEXT(0), HEADER1(1), HEADER2(2), HEADER3(3), HEADER4(4), TITLE(5), FOOTER(6), COMMENT(7), HEADER5(8), HEADER6(9); + TEXT(0), HEADER1(1), HEADER2(2), HEADER3(3), HEADER4(4), TITLE(5), FOOTER(6), COMMENT(7), HEADER5(8), HEADER6(9), HTML(10); private Integer value; @@ -38,6 +38,8 @@ public enum ParagraphStyle { return HEADER5; case 9: return HEADER6; + case 10: + return HTML; default: throw new RuntimeException("Unsupported ParagraphStyle Code"); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/HtmlToWorldBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/HtmlToWorldBuilder.java new file mode 100644 index 000000000..063ce4deb --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/HtmlToWorldBuilder.java @@ -0,0 +1,249 @@ +package eu.eudat.logic.utilities.documents.word; + +import org.apache.poi.xwpf.usermodel.*; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Node; +import org.jsoup.nodes.TextNode; +import org.jsoup.select.NodeTraversor; +import org.jsoup.select.NodeVisitor; +import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; + +import java.math.BigInteger; +import java.util.*; + +public class HtmlToWorldBuilder implements NodeVisitor { + + private final Map properties = new LinkedHashMap<>(); + private XWPFParagraph paragraph; + private XWPFRun run; + private Boolean dumpRun; + private final float indentation; + private Boolean isIdentationUsed; + private XWPFNumbering numbering; + private Queue abstractNumId; + private BigInteger numberingLevel; + + public static HtmlToWorldBuilder convert(XWPFDocument document, Document htmlDocument, float indentation) { + XWPFParagraph paragraph = document.createParagraph(); + HtmlToWorldBuilder htmlToWorldBuilder = new HtmlToWorldBuilder(paragraph, indentation); + NodeTraversor.traverse(htmlToWorldBuilder, htmlDocument); + return htmlToWorldBuilder; + } + + public HtmlToWorldBuilder(XWPFParagraph paragraph, float indentation) { + this.paragraph = paragraph; + this.run = this.paragraph.createRun(); + this.dumpRun = false; + this.indentation = indentation; + this.isIdentationUsed = false; + this.run.setFontSize(11); + this.abstractNumId = new ArrayDeque<>(); + this.numberingLevel = BigInteger.valueOf(-1); + this.setDefaultIndentation(); + } + + @Override + public void head(Node node, int i) { + String name = node.nodeName(); + if (name.equals("#text")) { + String text = ((TextNode)node).text(); + this.run.setText(text); + this.dumpRun = true; + } else { + properties.put(name, true); + } + if (dumpRun) { + this.run = this.paragraph.createRun(); + this.run.setFontSize(11); + this.dumpRun = false; + } + parseProperties(node); + properties.clear(); + } + + private void parseProperties(Node node) { + properties.entrySet().forEach(stringBooleanEntry -> { + switch (stringBooleanEntry.getKey()) { + case "i" : + case "em": + this.run.setItalic(stringBooleanEntry.getValue()); + break; + case "b": + case "strong": + this.run.setBold(stringBooleanEntry.getValue()); + break; + case "u": + case "ins": + this.run.setUnderline(stringBooleanEntry.getValue() ? UnderlinePatterns.SINGLE : UnderlinePatterns.NONE); + break; + case "small": + this.run.setFontSize(stringBooleanEntry.getValue() ? 8 : 11); + break; + case "del": + case "strike": + case "strikethrough": + case "s": + this.run.setStrikeThrough(stringBooleanEntry.getValue()); + break; + case "mark": + this.run.setTextHighlightColor(stringBooleanEntry.getValue() ? STHighlightColor.YELLOW.toString() : STHighlightColor.NONE.toString()); + break; + case "sub": + this.run.setSubscript(stringBooleanEntry.getValue() ? VerticalAlign.SUBSCRIPT : VerticalAlign.BASELINE); + break; + case "sup": + this.run.setSubscript(stringBooleanEntry.getValue() ? VerticalAlign.SUPERSCRIPT : VerticalAlign.BASELINE); + break; + case "div": + case "p": + this.paragraph = this.paragraph.getDocument().createParagraph(); + this.run = this.paragraph.createRun(); + this.isIdentationUsed = false; + this.setDefaultIndentation(); + if (stringBooleanEntry.getValue()) { + if (node.hasAttr("align")) { + String alignment = node.attr("align"); + this.paragraph.setAlignment(ParagraphAlignment.valueOf(alignment.toUpperCase(Locale.ROOT))); + } + } + break; + case "blockquote": + this.paragraph = this.paragraph.getDocument().createParagraph(); + this.run = this.paragraph.createRun(); + if (stringBooleanEntry.getValue()) { + this.paragraph.setIndentationLeft(720); + } else { + this.isIdentationUsed = false; + this.setDefaultIndentation(); + } + break; + case "ul": + if (stringBooleanEntry.getValue()) { + createNumbering(STNumberFormat.BULLET); + } else { + this.paragraph = this.paragraph.getDocument().createParagraph(); + this.run = this.paragraph.createRun(); + this.isIdentationUsed = false; + this.setDefaultIndentation(); + this.numberingLevel = this.numberingLevel.subtract(BigInteger.ONE); + ((ArrayDeque)this.abstractNumId).removeLast(); + } + break; + case "ol": + if (stringBooleanEntry.getValue()) { + createNumbering(STNumberFormat.DECIMAL); + } else { + this.paragraph = this.paragraph.getDocument().createParagraph(); + this.run = this.paragraph.createRun(); + this.isIdentationUsed = false; + this.setDefaultIndentation(); + this.numberingLevel = this.numberingLevel.subtract(BigInteger.ONE); + ((ArrayDeque)this.abstractNumId).removeLast(); + } + break; + case "li": + if (stringBooleanEntry.getValue()) { + this.paragraph = this.paragraph.getDocument().createParagraph(); + this.paragraph.setIndentationLeft(Math.round(indentation * 720) * (numberingLevel.intValue() + 1)); + this.run = this.paragraph.createRun(); + this.paragraph.setNumID(((ArrayDeque)abstractNumId).getLast()); + } + break; + case "font": + if (stringBooleanEntry.getValue()) { + if (node.hasAttr("color")) { + this.run.setColor(node.attr("color").substring(1)); + } + } else { + this.run.setColor("000000"); + } + break; + case "a": + if (stringBooleanEntry.getValue()) { + if (node.hasAttr("href")) { + this.run = createHyperLinkRun(node.attr("href")); + this.run.setColor("0000FF"); + this.run.setUnderline(UnderlinePatterns.SINGLE); + } + } else { + this.run = paragraph.createRun(); + } + break; + case "br": + if (stringBooleanEntry.getValue()) { + this.run.addBreak(); + } + break; + } + }); + } + + @Override + public void tail(Node node, int i) { + String name = node.nodeName(); + properties.put(name, false); + parseProperties(node); + properties.clear(); + } + + //GK: This function creates one numbering.xml for the word document and adds a specific format. + //It imitates the numbering.xml that is usually generated by word editors like LibreOffice + private void createNumbering(STNumberFormat.Enum format) { + CTAbstractNum ctAbstractNum = CTAbstractNum.Factory.newInstance(); + if (this.numbering == null) this.numbering = this.paragraph.getDocument().createNumbering(); + BigInteger tempNumId = BigInteger.ONE; + boolean found = false; + while (!found) { + Object o = numbering.getAbstractNum(tempNumId); + found = (o == null); + if (!found) tempNumId = tempNumId.add(BigInteger.ONE); + } + ctAbstractNum.setAbstractNumId(tempNumId); + CTLvl ctLvl = ctAbstractNum.addNewLvl(); + this.numberingLevel = numberingLevel.add(BigInteger.ONE); + ctLvl.setIlvl(numberingLevel); + ctLvl.addNewNumFmt().setVal(format); + ctLvl.addNewStart().setVal(BigInteger.ONE); + if (format == STNumberFormat.BULLET) { + ctLvl.addNewLvlJc().setVal(STJc.LEFT); + ctLvl.addNewLvlText().setVal("\u2022"); + ctLvl.addNewRPr(); //Set the Symbol font + CTFonts f = ctLvl.getRPr().addNewRFonts(); + f.setAscii("Symbol"); + f.setHAnsi("Symbol"); + f.setCs("Symbol"); + f.setHint(STHint.DEFAULT); + } else { + ctLvl.addNewLvlText().setVal("%1."); + } + XWPFAbstractNum xwpfAbstractNum = new XWPFAbstractNum(ctAbstractNum); + + this.abstractNumId.add(this.numbering.addAbstractNum(xwpfAbstractNum)); + this.numbering.addNum(((ArrayDeque)abstractNumId).getLast()); + } + + private XWPFHyperlinkRun createHyperLinkRun(String uri) { + String rId = this.paragraph.getDocument().getPackagePart().addExternalRelationship(uri, XWPFRelation.HYPERLINK.getRelation()).getId(); + + CTHyperlink cthyperLink=paragraph.getCTP().addNewHyperlink(); + cthyperLink.setId(rId); + cthyperLink.addNewR(); + + return new XWPFHyperlinkRun( + cthyperLink, + cthyperLink.getRArray(0), + paragraph + ); + } + + private void setDefaultIndentation() { + if (!isIdentationUsed) { + this.paragraph.setIndentationLeft(Math.round(indentation * 720.0F)); + this.isIdentationUsed = true; + } + } + + public XWPFParagraph getParagraph() { + return paragraph; + } +} 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 764ac5a6f..5e8ed45e7 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,6 +1,5 @@ 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; @@ -15,6 +14,8 @@ import eu.eudat.models.data.user.components.datasetprofile.Section; import eu.eudat.models.data.user.composite.DatasetProfilePage; import eu.eudat.models.data.user.composite.PagedDatasetProfile; import org.apache.poi.xwpf.usermodel.*; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTAbstractNum; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDecimalNumber; import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTLvl; @@ -24,7 +25,14 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.math.BigInteger; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.TemporalAccessor; import java.util.*; +import java.util.stream.Collectors; public class WordBuilder { private static final Logger logger = LoggerFactory.getLogger(WordBuilder.class); @@ -32,10 +40,12 @@ public class WordBuilder { private Map> options = new HashMap<>(); private CTAbstractNum cTAbstractNum; private BigInteger numId; + private Integer indent; public WordBuilder() { this.cTAbstractNum = CTAbstractNum.Factory.newInstance(); this.cTAbstractNum.setAbstractNumId(BigInteger.valueOf(1)); + this.indent = 0; this.buildOptions(); } @@ -48,6 +58,11 @@ public class WordBuilder { run.setFontSize(11); return paragraph; }); + this.options.put(ParagraphStyle.HTML, (mainDocumentPart, item) -> { + Document htmlDoc = Jsoup.parse(item.replaceAll("\n", "
")); + HtmlToWorldBuilder htmlToWorldBuilder = HtmlToWorldBuilder.convert(mainDocumentPart, htmlDoc, indent > 0 ? (indent/2.0F) * 0.8F : 0.8F); + return htmlToWorldBuilder.getParagraph(); + }); this.options.put(ParagraphStyle.TITLE, (mainDocumentPart, item) -> { XWPFParagraph paragraph = mainDocumentPart.createParagraph(); paragraph.setStyle("Title"); @@ -124,8 +139,16 @@ public class WordBuilder { public XWPFDocument build(XWPFDocument document, PagedDatasetProfile pagedDatasetProfile, VisibilityRuleService visibilityRuleService) throws IOException { createPages(pagedDatasetProfile.getPages(), document, true, visibilityRuleService); - XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum); XWPFNumbering numbering = document.createNumbering(); + BigInteger tempNumId = BigInteger.ONE; + boolean found = false; + while (!found) { + Object o = numbering.getAbstractNum(tempNumId); + found = (o == null); + if (!found) tempNumId = tempNumId.add(BigInteger.ONE); + } + cTAbstractNum.setAbstractNumId(tempNumId); + XWPFAbstractNum abstractNum = new XWPFAbstractNum(cTAbstractNum); BigInteger abstractNumID = numbering.addAbstractNum(abstractNum); this.numId = numbering.addNum(abstractNumID); createPages(pagedDatasetProfile.getPages(), document, false, visibilityRuleService); @@ -179,7 +202,8 @@ public class WordBuilder { } hasValue = createFields(compositeField.getFields(), mainDocumentPart, 3, createListing, visibilityRuleService); if (compositeField.getMultiplicityItems() != null && !compositeField.getMultiplicityItems().isEmpty()) { - for (FieldSet multiplicityFieldset : compositeField.getMultiplicityItems()) { + List
list = compositeField.getMultiplicityItems().stream().sorted(Comparator.comparingInt(FieldSet::getOrdinal)).collect(Collectors.toList()); + for (FieldSet multiplicityFieldset : list) { hasValue = createFields(multiplicityFieldset.getFields(), mainDocumentPart, 3, createListing, visibilityRuleService); } } @@ -199,15 +223,19 @@ public class WordBuilder { private Boolean createFields(List fields, XWPFDocument mainDocumentPart, Integer indent, Boolean createListing, VisibilityRuleService visibilityRuleService) { if (createListing) this.addListing(mainDocumentPart, indent, false, false); boolean hasValue = false; - for (Field field: fields) { + List tempFields = fields.stream().sorted(Comparator.comparingInt(Field::getOrdinal)).collect(Collectors.toList()); + for (Field field: tempFields) { if (visibilityRuleService.isElementVisible(field.getId())) { if (!createListing) { try { - XWPFParagraph paragraph = addParagraphContent(this.formatter(field), mainDocumentPart, ParagraphStyle.TEXT, numId); - if (paragraph != null) { - CTDecimalNumber number = paragraph.getCTP().getPPr().getNumPr().addNewIlvl(); - number.setVal(BigInteger.valueOf(indent)); - hasValue = true; + if (field.getValue() != null && !field.getValue().toString().isEmpty()) { + this.indent = indent; + XWPFParagraph paragraph = addParagraphContent(this.formatter(field), mainDocumentPart, field.getViewStyle().getRenderStyle().equals("richTextarea") ? ParagraphStyle.HTML : ParagraphStyle.TEXT, numId); + if (paragraph != null) { + CTDecimalNumber number = paragraph.getCTP().getPPr().getNumPr().addNewIlvl(); + number.setVal(BigInteger.valueOf(indent)); + hasValue = true; + } } } catch (IOException e) { logger.error(e.getMessage(), e); @@ -221,10 +249,12 @@ public class WordBuilder { public XWPFParagraph addParagraphContent(String text, XWPFDocument mainDocumentPart, ParagraphStyle style, BigInteger numId) { if (text != null && !text.isEmpty()) { XWPFParagraph paragraph = this.options.get(style).apply(mainDocumentPart, text); - if (numId != null) { - paragraph.setNumID(numId); + if (paragraph != null) { + if (numId != null) { + paragraph.setNumID(numId); + } + return paragraph; } - return paragraph; } return null; } @@ -238,13 +268,16 @@ public class WordBuilder { } if (question) { cTLvl.addNewNumFmt().setVal(STNumberFormat.DECIMAL); + cTLvl.addNewLvlText().setVal(""); cTLvl.setIlvl(BigInteger.valueOf(indent)); } else if (!question && hasIndication) { cTLvl.addNewNumFmt().setVal(STNumberFormat.DECIMAL); + cTLvl.addNewLvlText().setVal(""); cTLvl.setIlvl(BigInteger.valueOf(indent)); } if (!question && !hasIndication) { cTLvl.addNewNumFmt().setVal(STNumberFormat.NONE); + cTLvl.addNewLvlText().setVal(""); cTLvl.setIlvl(BigInteger.valueOf(indent)); } } @@ -275,44 +308,16 @@ public class WordBuilder { try { mapList = Arrays.asList(mapper.readValue(field.getValue().toString(), HashMap[].class)); }catch (Exception e) { - logger.warn(e.getMessage(), e); - logger.info("Moving to fallback parsing"); + // logger.warn(e.getMessage(), e); + // logger.info("Moving to fallback parsing"); Map map = new HashMap<>(); map.put("label", field.getValue().toString()); mapList.add(map); } - /*try { - if (field.getValue().toString().startsWith("[")) { - JSONArray jsonarray = new JSONArray(field.getValue().toString()); - for (int i = 0; i < jsonarray.length(); i++) { - JSONObject jsonObject = jsonarray.getJSONObject(i); - String id = jsonObject.get("id").toString(); - String label = jsonObject.getString("label"); - if (id != null && label != null) { - map.put(id, label); - } - } - } else if (field.getValue().toString().startsWith("{")) { - JSONObject jsonObject = new JSONObject(field.getValue().toString()); - String id = jsonObject.get("id").toString(); - String label = jsonObject.getString("label"); - if (id != null && label != null) { - map.put(id, label); - } - } - } catch (Exception e) { - Map exMap = mapper.readValue(field.getValue().toString(), new TypeReference>() { - }); - return exMap.get("label"); - }*/ } StringBuilder sb = new StringBuilder(); int index = 0; for (Map map: mapList) { - /*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") || entry.getKey().equals("name"))) { sb.append(entry.getValue().toString()); @@ -325,15 +330,13 @@ public class WordBuilder { return sb.toString(); } else if (comboboxType.equals("wordlist")) { WordListData wordListData = (WordListData) field.getData(); - if (wordListData.getOptions().isEmpty() && field.getValue() != null) { - logger.warn("World List has no values but the field has"); - logger.info("Return value as is"); - return field.getValue().toString(); - } else if (field.getValue() != null){ + if (field.getValue() != null){ ComboBoxData.Option selectedOption = null; - for (ComboBoxData.Option option: wordListData.getOptions()) { - if (option.getValue().equals(field.getValue())) { - selectedOption = option; + if (!wordListData.getOptions().isEmpty()) { + for (ComboBoxData.Option option : wordListData.getOptions()) { + if (option.getValue().equals(field.getValue())) { + selectedOption = option; + } } } return selectedOption != null ? selectedOption.getLabel() : field.getValue().toString(); @@ -343,33 +346,39 @@ public class WordBuilder { } case "booleanDecision": if (field.getValue() != null && field.getValue().equals("true")) return "Yes"; - else return "No"; + if (field.getValue() != null && field.getValue().equals("false")) return "No"; + return null; case "radiobox": return field.getValue() != null ? field.getValue().toString() : null; case "checkBox": CheckBoxData data = (CheckBoxData) field.getData(); if (field.getValue() == null || field.getValue().equals("false")) return null; return data.getLabel(); - case "freetext": case "datepicker": + case "datePicker":{ + Instant instant; + try { + instant = Instant.parse((String) field.getValue()); + } catch (DateTimeParseException ex) { + instant = (Instant) DateTimeFormatter.ofPattern("yyyy-MM-dd").parse((String)field.getValue()); + } + return field.getValue() != null ? DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault()).format(instant) : ""; + } + case "freetext": case "textarea": + case "richTextarea": return field.getValue() != null ? field.getValue().toString(): ""; case "datasetIdentifier": case "validation": - if (field.getValue() != null) { + if (field.getValue() != null && !field.getValue().toString().isEmpty()) { Map identifierData; + ObjectMapper mapper = new ObjectMapper(); 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()); - } + } catch (Exception ex) { + // logger.warn(ex.getLocalizedMessage(), ex); + // logger.info("Reverting to custom parsing"); + identifierData = customParse(field.getValue().toString()); } return "id: " + identifierData.get("identifier") + ", Validation Type: " + identifierData.get("type"); } @@ -381,4 +390,17 @@ public class WordBuilder { private boolean hasVisibleFields(FieldSet compositeFields, VisibilityRuleService visibilityRuleService) { return compositeFields.getFields().stream().anyMatch(field -> visibilityRuleService.isElementVisible(field.getId())); } + + private Map customParse(String value) { + Map result = new LinkedHashMap<>(); + String parsedValue = value.replaceAll("[^a-zA-Z0-9\\s:=,]", ""); + StringTokenizer commaTokens = new StringTokenizer(parsedValue, ", "); + String delimeter = parsedValue.contains("=") ? "=" : ":"; + while (commaTokens.hasMoreTokens()) { + String token = commaTokens.nextToken(); + StringTokenizer delimiterTokens = new StringTokenizer(token, delimeter); + result.put(delimiterTokens.nextToken(), delimiterTokens.nextToken()); + } + return result; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/XWPFHtmlDocument.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/XWPFHtmlDocument.java new file mode 100644 index 000000000..526813e10 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/XWPFHtmlDocument.java @@ -0,0 +1,63 @@ +package eu.eudat.logic.utilities.documents.word; + +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.xwpf.usermodel.XWPFDocument; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.UUID; + +public class XWPFHtmlDocument extends POIXMLDocumentPart { + + private String html; + private String id; + + public XWPFHtmlDocument(PackagePart pkg, String id) { + super(pkg); + this.html = "HTML import

"; + this.id = id; + } + + public String getHtml() { + return html; + } + + public void setHtml(String html) { + this.html = this.html.replace("

", html); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + protected void commit() throws IOException { + PackagePart packagePart = getPackagePart(); + OutputStream outputStream = packagePart.getOutputStream(); + Writer writer = new OutputStreamWriter(outputStream, "UTF-8"); + writer.write(html); + writer.close(); + outputStream.close(); + } + + public static XWPFHtmlDocument addHtmlDocument(XWPFDocument document) throws InvalidFormatException { + OPCPackage oPCPackage = document.getPackage(); + String id = UUID.randomUUID().toString(); + PackagePartName partName = PackagingURIHelper.createPartName("/word/" + id + ".html"); + PackagePart part = oPCPackage.createPart(partName, "text/html"); + XWPFHtmlDocument xWPFHtmlDocument = new XWPFHtmlDocument(part, id); + document.addRelation(xWPFHtmlDocument.getId(), new XWPFHtmlRelation(), xWPFHtmlDocument); + return xWPFHtmlDocument; + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/XWPFHtmlRelation.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/XWPFHtmlRelation.java new file mode 100644 index 000000000..669dc47c9 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/word/XWPFHtmlRelation.java @@ -0,0 +1,11 @@ +package eu.eudat.logic.utilities.documents.word; + +import org.apache.poi.ooxml.POIXMLRelation; + +public class XWPFHtmlRelation extends POIXMLRelation { + public XWPFHtmlRelation() { + super("text/html", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk", + "/word/htmlDoc#.html"); + } +} diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/ExportXmlBuilder.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/ExportXmlBuilder.java index 14b7b30b2..9c8a591b4 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/ExportXmlBuilder.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/ExportXmlBuilder.java @@ -1,7 +1,10 @@ package eu.eudat.logic.utilities.documents.xml; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.exc.MismatchedInputException; import eu.eudat.logic.services.forms.VisibilityRuleService; import eu.eudat.logic.utilities.builders.XmlBuilder; +import eu.eudat.models.data.components.commons.datafield.ExternalDatasetsData; import eu.eudat.models.data.user.components.datasetprofile.Field; import eu.eudat.models.data.user.components.datasetprofile.FieldSet; import eu.eudat.models.data.user.components.datasetprofile.Section; @@ -17,7 +20,11 @@ import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Map; import java.util.UUID; public class ExportXmlBuilder { @@ -92,18 +99,42 @@ public class ExportXmlBuilder { if (visibilityRuleService.isElementVisible(field.getId())) { Element elementField = element.createElement("field"); elementField.setAttribute("id", field.getId()); + if (field.getViewStyle().getRenderStyle().equals("externalDatasets")) { + elementField.setAttribute("type", ((ExternalDatasetsData)field.getData()).getType()); + } if (field.getValue() != null) { Element valueField = element.createElement("value"); + ObjectMapper mapper = new ObjectMapper(); try { - JSONArray jsonArray = new JSONArray(field.getValue().toString()); + + List> jsonArray = mapper.readValue(field.getValue().toString(), List.class); +// JSONArray jsonArray = new JSONArray(field.getValue().toString()); StringBuilder sb = new StringBuilder(); - for (int i = 0; i < jsonArray.length(); i++) { + boolean firstTime = true; + for (Map jsonElement: jsonArray) { + if (!firstTime) { + sb.append(", "); + } + sb.append(jsonElement.get("label") != null ? jsonElement.get("label") : jsonElement.get("name")); + firstTime = false; + + } + /*for (int i = 0; i < jsonArray.length(); i++) { sb.append(jsonArray.getJSONObject(i).get("label").toString()); if (i != jsonArray.length() - 1) sb.append(", "); - } + }*/ valueField.setTextContent(sb.toString()); - } catch (JSONException ex) { - valueField.setTextContent(field.getValue().toString()); + } catch (IOException ex) { + try { + Map jsonElement = mapper.readValue(field.getValue().toString(), Map.class); + valueField.setTextContent((jsonElement.get("label") != null ? jsonElement.get("label").toString() : jsonElement.get("name") != null ? jsonElement.get("name").toString() : "")); + } catch (IOException e) { + try { + valueField.setTextContent(DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault()).format(Instant.parse(field.getValue().toString()))); + } catch (Exception exc) { + valueField.setTextContent(field.getValue().toString()); + } + } } elementField.appendChild(valueField); } 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 21d1aa402..334921e2a 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 @@ -260,6 +260,7 @@ public class ExportXmlBuilderDatasetProfile { case CHECK_BOX: case FREE_TEXT: case TEXT_AREA: + case RICH_TEXT_AREA: case DATE_PICKER: case DATASET_IDENTIFIER: case CURRENCY: @@ -290,6 +291,7 @@ public class ExportXmlBuilderDatasetProfile { ExternalDatasetsData externalDatasetsData = (ExternalDatasetsData) field.getData(); dataOut.setAttribute("label", externalDatasetsData.getLabel()); dataOut.setAttribute("multiAutocomplete", externalDatasetsData.getMultiAutoComplete().toString()); + dataOut.setAttribute("type", externalDatasetsData.getType()); break; case DATA_REPOSITORIES: DataRepositoriesData dataRepositoriesData = (DataRepositoriesData) field.getData(); 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 cec5df6f1..2ae1a2d34 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 @@ -52,7 +52,9 @@ public class DatasetProfile { List sectionDatasetEntity = new LinkedList<>(); for (Page xmlPage: page) { pagesDatasetEntity.add(xmlPage.toAdminCompositeModelPage()); - sectionDatasetEntity.add(xmlPage.toAdminCompositeModelSection()); + for (int i = 0; i < xmlPage.getSections().size(); i++) { + sectionDatasetEntity.add(xmlPage.toAdminCompositeModelSection(i)); + } } newDatasetEntityProfile.setPages(pagesDatasetEntity); newDatasetEntityProfile.setSections(sectionDatasetEntity); diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/Page.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/Page.java index b30f82d29..5ef139834 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/Page.java +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/documents/xml/datasetProfileXml/datasetProfileModel/Page.java @@ -3,13 +3,14 @@ package eu.eudat.logic.utilities.documents.xml.datasetProfileXml.datasetProfileM import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; @XmlRootElement(name = "page") public class Page { private String id; private int ordinal; private String title; - private Sections sections; + private List sections; @XmlAttribute(name = "id") public String getId() { @@ -39,11 +40,11 @@ public class Page { } @XmlElement(name = "sections") - public Sections getSections() { + public List getSections() { return sections; } - public void setSections(Sections sections) { + public void setSections(List sections) { this.sections = sections; } @@ -55,7 +56,7 @@ public class Page { return pageEntity; } - public eu.eudat.models.data.admin.components.datasetprofile.Section toAdminCompositeModelSection(){ + public eu.eudat.models.data.admin.components.datasetprofile.Section toAdminCompositeModelSection(int i){ /* eu.eudat.models.data.admin.components.datasetprofile.Section sectionEntity =new eu.eudat.models.data.admin.components.datasetprofile.Section(); // List sectionsListEntity = new LinkedList<>(); // for (Section xmlsection:this.sections.section) { @@ -68,6 +69,6 @@ public class Page { sectionEntity.setId(this.id); sectionEntity.setOrdinal(this.ordinal); sectionEntity.setTitle(this.title);*/ - return sections.toAdminCompositeModelSection(); + return sections.get(i).toAdminCompositeModelSection(); } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/json/MultiDateDeserializer.java b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/json/MultiDateDeserializer.java new file mode 100644 index 000000000..636b8faa2 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/logic/utilities/json/MultiDateDeserializer.java @@ -0,0 +1,41 @@ +package eu.eudat.logic.utilities.json; + +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +public class MultiDateDeserializer extends StdDeserializer { + + private static final List DATE_FORMATS = Arrays.asList("yyyy-MM-dd'T'HH:mm:ss'Z'", "yyyy-MM-dd'T'HH:mm:ss.S"); + + + public MultiDateDeserializer() { + super(Date.class); + } + + protected MultiDateDeserializer(Class vc) { + super(vc); + } + + @Override + public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + String text = p.getText(); + + for (String dateFormat: DATE_FORMATS) { + try { + return new SimpleDateFormat(dateFormat).parse(text); + } catch (ParseException ignored) { + } + } + throw new JsonParseException(p, "No supported Date format"); + } +} 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 f7dba3eb2..96181212a 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 @@ -28,6 +28,7 @@ public class ViewStyle { CHECK_BOX("checkBox"), FREE_TEXT("freetext"), TEXT_AREA("textarea"), + RICH_TEXT_AREA("richTextarea"), DATE_PICKER("datePicker"), EXTERNAL_DATASETS("externalDatasets"), DATA_REPOSITORIES("dataRepositories"), 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 ca430e294..e4e5fcf94 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/ExternalDatasetsData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ExternalDatasetsData.java index bc43f2d6a..cad4e4f19 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ExternalDatasetsData.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/ExternalDatasetsData.java @@ -8,6 +8,7 @@ import java.util.Map; public class ExternalDatasetsData extends FieldData { private Boolean multiAutoComplete; + private String type; public Boolean getMultiAutoComplete() { return multiAutoComplete; @@ -17,11 +18,20 @@ public class ExternalDatasetsData extends FieldData { this.multiAutoComplete = multiAutoComplete; } + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + @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); + this.setType(((Map) data).get("type") != null && !((Map) data).get("type").toString().isEmpty()? ((Map) data).get("type").toString() : "other"); } return this; } @@ -38,6 +48,9 @@ public class ExternalDatasetsData extends FieldData { if (this.getMultiAutoComplete() != null) { root.setAttribute("multiAutoComplete", this.getMultiAutoComplete().toString()); } + if (this.getType() != null) { + root.setAttribute("type", this.getType()); + } return root; } @@ -45,6 +58,7 @@ public class ExternalDatasetsData extends FieldData { public ExternalDatasetsData fromXml(Element item) { this.setLabel(item != null ? item.getAttribute("label") : ""); this.setMultiAutoComplete(Boolean.parseBoolean(item.getAttribute("multiAutoComplete"))); + this.setType(item.getAttribute("type") != null ? item.getAttribute("type"): "other"); return this; } @@ -53,6 +67,7 @@ public class ExternalDatasetsData extends FieldData { HashMap dataMap = new HashMap(); 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); + dataMap.put("type", item != null && item.getAttributes().getLength() > 0? item.getAttribute("type") : "other"); return dataMap; } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RichTextAreaData.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RichTextAreaData.java new file mode 100644 index 000000000..f787079e9 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/components/commons/datafield/RichTextAreaData.java @@ -0,0 +1,43 @@ +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 RichTextAreaData extends FieldData { + @Override + public RichTextAreaData fromData(Object data) { + if (data != null) { + this.setLabel(((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 RichTextAreaData fromXml(Element item) { + this.setLabel(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/dmp/DataManagementPlanNewVersionModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlanNewVersionModel.java index 46c193154..eae784aeb 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlanNewVersionModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/dmp/DataManagementPlanNewVersionModel.java @@ -7,7 +7,10 @@ import eu.eudat.models.data.funder.FunderDMPEditorModel; import eu.eudat.models.data.grant.GrantDMPEditorModel; import eu.eudat.models.data.project.ProjectDMPEditorModel; import eu.eudat.models.data.userinfo.UserInfo; +import eu.eudat.models.data.userinfo.UserListingModel; import net.minidev.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.*; import java.util.stream.Collectors; @@ -16,6 +19,8 @@ import java.util.stream.Collectors; * Created by ikalyvas on 2/5/2018. */ public class DataManagementPlanNewVersionModel implements DataModel { + private static final Logger logger = LoggerFactory.getLogger(DataManagementPlanNewVersionModel.class); + private UUID id; private String label; private UUID groupId; @@ -26,7 +31,7 @@ public class DataManagementPlanNewVersionModel implements DataModel organisations; private List researchers; - private List associatedUsers; + private List associatedUsers; private eu.eudat.models.data.userinfo.UserInfo creator; private Date created; private List datasets; @@ -104,10 +109,10 @@ public class DataManagementPlanNewVersionModel implements DataModel getAssociatedUsers() { + public List getAssociatedUsers() { return associatedUsers; } - public void setAssociatedUsers(List associatedUsers) { + public void setAssociatedUsers(List associatedUsers) { this.associatedUsers = associatedUsers; } @@ -163,7 +168,7 @@ public class DataManagementPlanNewVersionModel implements DataModel(new ArrayList<>(this.associatedUsers))); + // entity.setUsers(new HashSet<>(new ArrayList<>(this.associatedUsers))); entity.setDescription(this.description); entity.setStatus((short) this.status); entity.setGroupId(this.groupId == null ? UUID.randomUUID() : this.groupId); @@ -227,7 +232,7 @@ public class DataManagementPlanNewVersionModel implements DataModel associatedUsersImportModels; private List dynamicFieldsImportModels; private List datasetImportModels; + private String language; + private Boolean visibility; + private String publicDate; + private String costs; @XmlElement(name = "description") public String getDescriptionImport() { @@ -113,4 +117,37 @@ public class DmpImportModel { @XmlElement(name = "dataset") public List getDatasetImportModels() { return datasetImportModels; } public void setDatasetImportModels(List datasetImportModels) { this.datasetImportModels = datasetImportModels; } + + @XmlElement(name = "language") + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + @XmlElement(name = "visibility") + public Boolean getVisibility() { + return visibility; + } + + public void setVisibility(Boolean visibility) { + this.visibility = visibility; + } + @XmlElement(name = "publicDate") + public String getPublicDate() { + return publicDate; + } + + public void setPublicDate(String publicDate) { + this.publicDate = publicDate; + } + @XmlElement(name = "costs") + public String getCosts() { + return costs; + } + + public void setCosts(String costs) { + this.costs = costs; + } } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/listingmodels/DataManagementPlanListingModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/listingmodels/DataManagementPlanListingModel.java index ad8bcda51..dd5c82499 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/listingmodels/DataManagementPlanListingModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/listingmodels/DataManagementPlanListingModel.java @@ -25,7 +25,7 @@ public class DataManagementPlanListingModel implements DataModel new AssociatedProfile().fromData(item)).collect(Collectors.toList()); return this; } @@ -196,7 +196,7 @@ public class DataManagementPlanListingModel implements DataModel new Organisation().fromDataModel(item)).collect(Collectors.toList())); this.datasets = entity.getDataset().stream().map(x-> new DatasetUrlListing().fromDataModel(x)).collect(Collectors.toList()); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/listingmodels/DatasetListingModel.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/listingmodels/DatasetListingModel.java index c0bb5a74f..45e751963 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/listingmodels/DatasetListingModel.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/listingmodels/DatasetListingModel.java @@ -199,11 +199,11 @@ public class DatasetListingModel implements DataModel new eu.eudat.models.data.dataset.Registry().fromDataModel(item)).collect(Collectors.toList())); - this.dataRepositories = LabelBuilder.getLabel(entity.getDatasetDataRepositories().stream().map(item -> new DataRepository().fromDataModel(item.getDataRepository())).collect(Collectors.toList())); - this.services = LabelBuilder.getLabel(entity.getServices().stream().map(item -> new Service().fromDataModel(item.getService())).collect(Collectors.toList())); + //this.grantAbbreviation = entity.getDmp() != null ? entity.getDmp().getGrant().getAbbreviation() : ""; + // this.grantId = entity.getDmp() != null ? entity.getDmp().getGrant().getId().toString() : ""; + //this.registries = LabelBuilder.getLabel(entity.getRegistries().stream().map(item -> new eu.eudat.models.data.dataset.Registry().fromDataModel(item)).collect(Collectors.toList())); + // this.dataRepositories = LabelBuilder.getLabel(entity.getDatasetDataRepositories().stream().map(item -> new DataRepository().fromDataModel(item.getDataRepository())).collect(Collectors.toList())); + //this.services = LabelBuilder.getLabel(entity.getServices().stream().map(item -> new Service().fromDataModel(item.getService())).collect(Collectors.toList())); if (entity.getFinalizedAt() == null && entity.getStatus() == Dataset.Status.FINALISED.getValue()) { this.finalizedAt = entity.getDmp().getFinalizedAt(); } else { diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/Field.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/Field.java index 917d85542..6fe3c7741 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/Field.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/Field.java @@ -17,6 +17,7 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; import java.util.Collection; import java.util.LinkedList; import java.util.List; @@ -201,13 +202,10 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin @Override public void fromJsonObject(Map properties) { try { - JSONArray jsonArray = new JSONArray(properties.get(this.id).toString()); - List stringList = new LinkedList<>(); - for (int i = 0; i < jsonArray.length(); i++) { - stringList.add(jsonArray.getJSONObject(i).toString()); - } + ObjectMapper mapper = new ObjectMapper(); + List stringList = mapper.readValue(properties.get(this.id).toString(), LinkedList.class); this.value = stringList; - } catch (JSONException | NullPointerException e) { + } catch (JSONException | NullPointerException | IOException e) { this.value = (String) properties.get(this.id); } this.multiplicityItems = new LinkedList<>(); @@ -231,7 +229,7 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin @Override public void toMap(Map fieldValues) { if (this.value != null) { - if (this.viewStyle != null && this.viewStyle.getRenderStyle().equals("datasetIdentifier")) { + if ((this.viewStyle != null && this.viewStyle.getRenderStyle().equals("datasetIdentifier") || this.value instanceof Collection)) { ObjectMapper mapper = new ObjectMapper(); String valueString = null; try { @@ -240,7 +238,7 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin } catch (JsonProcessingException e) { logger.error(e.getMessage(), e); } - } else if (this.value instanceof Collection) { + } /*else if (this.value instanceof Collection) { Collection valueCollection = (Collection) this.value; StringBuilder valueBuilder = new StringBuilder(); valueBuilder.append("["); @@ -252,7 +250,7 @@ public class Field implements Comparable, PropertiesModelBuilder, ViewStyleDefin } valueBuilder.append("]"); fieldValues.put(this.id, valueBuilder.toString()); - } else { + }*/ else { fieldValues.put(this.id, this.value.toString()); } } else { diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/FieldSet.java b/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/FieldSet.java index 2dfd6020b..80ece4c30 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/FieldSet.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/data/user/components/datasetprofile/FieldSet.java @@ -175,12 +175,12 @@ public class FieldSet implements Comparable, PropertiesModelBuilder, ViewStyleDe private FieldSet CloneForMultiplicity2(List key, Map properties,String[] ids, int index){ FieldSet newFieldSet = new FieldSet(); - newFieldSet.id = ids[0]+"_"+ids[1]+"_"+ids[2]; + newFieldSet.id = ids[0]+"_"+ids[1]+"_"+ids[2] + (ids.length > 4 ? "_" + ids[3] : ""); newFieldSet.description = this.description; newFieldSet.extendedDescription = this.extendedDescription; newFieldSet.additionalInformation=this.additionalInformation; newFieldSet.title = this.title; - newFieldSet.ordinal = this.ordinal; + newFieldSet.ordinal = ids.length > 4 ? Integer.valueOf(ids[3]) : this.ordinal; newFieldSet.fields = new LinkedList(); for (Field field: this.fields) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/Dmp.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/Dmp.java index 773ac5f72..a6dfb29d0 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/Dmp.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/Dmp.java @@ -9,6 +9,8 @@ import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.*; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import eu.eudat.logic.utilities.json.MultiDateDeserializer; /** @@ -71,6 +73,7 @@ public class Dmp implements Serializable * */ @JsonProperty("created") + @JsonDeserialize(using = MultiDateDeserializer.class) @JsonPropertyDescription("") private Date created; /** @@ -147,6 +150,7 @@ public class Dmp implements Serializable * */ @JsonProperty("modified") + @JsonDeserialize(using = MultiDateDeserializer.class) @JsonPropertyDescription("Must be set each time DMP is modified. Indicates DMP version.") private Date modified; /** 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 3473fec6c..67858a14b 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 @@ -20,6 +20,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.transaction.Transactional; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -152,7 +156,12 @@ public class DatasetRDAMapper { templateIdsToValues.entrySet().forEach(entry -> { boolean isFound = foundNodes.stream().anyMatch(node -> node.get("id").asText().equals(entry.getKey())); if (!isFound && entry.getValue() != null && !entry.getValue().toString().isEmpty()) { - rda.setAdditionalProperty(entry.getKey(), entry.getValue()); + try { + Instant time = Instant.parse(entry.getValue().toString()); + rda.setAdditionalProperty(entry.getKey(), DateTimeFormatter.ofPattern("yyyy-MM-dd").withZone(ZoneId.systemDefault()).format(time)); + } catch (DateTimeParseException e) { + rda.setAdditionalProperty(entry.getKey(), entry.getValue()); + } } }); 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 075b3796a..700f0bfda 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 @@ -39,9 +39,9 @@ public class DmpRDAMapper { throw new IllegalArgumentException("DMP is missing language and contact properties"); } else { extraProperties = new org.json.JSONObject(dmp.getExtraProperties()).toMap(); - if (extraProperties.get("language") == null) { + /*if (extraProperties.get("language") == null) { throw new IllegalArgumentException("DMP must have it's language property defined"); - } + }*/ if (extraProperties.get("contact") == null) { throw new IllegalArgumentException("DMP must have it's contact property defined"); } @@ -72,7 +72,7 @@ public class DmpRDAMapper { } else { rda.setEthicalIssuesExist(Dmp.EthicalIssuesExist.UNKNOWN); } - rda.setLanguage(LanguageRDAMapper.mapLanguageIsoToRDAIso(extraProperties.get("language").toString())); + rda.setLanguage(LanguageRDAMapper.mapLanguageIsoToRDAIso(extraProperties.get("language") != null ? extraProperties.get("language").toString() : "en")); if (extraProperties.get("costs") != null) { rda.setCost(new ArrayList<>()); ((List) extraProperties.get("costs")).forEach(costl -> { @@ -95,7 +95,7 @@ public class DmpRDAMapper { rda.getContributor().addAll(dmp.getResearchers().stream().map(ContributorRDAMapper::toRDA).collect(Collectors.toList())); } // rda.getContributor().addAll(dmp.getUsers().stream().map(ContributorRDAMapper::toRDA).collect(Collectors.toList())); - rda.setDataset(dmp.getDataset().stream().map(dataset -> datasetRDAMapper.toRDA(dataset, rda.getContributor())).collect(Collectors.toList())); + rda.setDataset(dmp.getDataset().stream().filter(dataset -> dataset.getStatus() != eu.eudat.elastic.entities.Dmp.DMPStatus.DELETED.getValue()).map(dataset -> datasetRDAMapper.toRDA(dataset, rda.getContributor())).collect(Collectors.toList())); rda.setProject(Collections.singletonList(ProjectRDAMapper.toRDA(dmp.getProject(), dmp.getGrant()))); rda.setAdditionalProperty("templates", dmp.getAssociatedDmps().stream().map(datasetProfile -> datasetProfile.getId().toString()).toArray()); return rda; @@ -127,9 +127,11 @@ public class DmpRDAMapper { entity.setDescription(rda.getDescription()); DatasetProfile defaultProfile = ((DatasetProfile)entity.getAssociatedDmps().toArray()[0]); entity.setDataset(rda.getDataset().stream().map(rda1 -> datasetRDAMapper.toEntity(rda1, defaultProfile)).collect(Collectors.toSet())); - Map result = ProjectRDAMapper.toEntity(rda.getProject().get(0), apiContext); - entity.setProject((Project) result.get("project")); - result.entrySet().stream().filter(entry -> entry.getKey().startsWith("grant")).forEach(entry -> entity.setGrant((Grant) entry.getValue())); + if (rda.getProject().size() > 0) { + Map result = ProjectRDAMapper.toEntity(rda.getProject().get(0), apiContext); + entity.setProject((Project) result.get("project")); + result.entrySet().stream().filter(entry -> entry.getKey().startsWith("grant")).forEach(entry -> entity.setGrant((Grant) entry.getValue())); + } Map extraProperties = new HashMap<>(); extraProperties.put("language", LanguageRDAMapper.mapRDAIsoToLanguageIso(rda.getLanguage())); diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/KeywordRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/KeywordRDAMapper.java index 1c7cb8a31..2e8a93d37 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/KeywordRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/KeywordRDAMapper.java @@ -17,9 +17,11 @@ public class KeywordRDAMapper { ObjectMapper mapper = new ObjectMapper(); try { value = JavaToJson.objectStringToJson(value); - List tags = Arrays.asList(mapper.readValue(value, Tag[].class)); - List keywordNames = tags.stream().map(Tag::getName).collect(Collectors.toList()); - return keywordNames; + if (!value.isEmpty()) { + List tags = Arrays.asList(mapper.readValue(value, Tag[].class)); + List keywordNames = tags.stream().map(Tag::getName).collect(Collectors.toList()); + return keywordNames; + } } catch (IOException e) { logger.error(e.getMessage(), e); } diff --git a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/LanguageRDAMapper.java b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/LanguageRDAMapper.java index 0dfe20c0e..dd2343a2d 100644 --- a/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/LanguageRDAMapper.java +++ b/dmp-backend/web/src/main/java/eu/eudat/models/rda/mapper/LanguageRDAMapper.java @@ -1,28 +1,29 @@ package eu.eudat.models.rda.mapper; +import com.fasterxml.jackson.databind.ObjectMapper; import eu.eudat.models.rda.Language; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; -import java.util.stream.Collectors; public class LanguageRDAMapper { private final static Map langMap = new HashMap<>(); private static final Logger logger = LoggerFactory.getLogger(LanguageRDAMapper.class); static { - String json = null; try { - json = new BufferedReader(new InputStreamReader(LanguageRDAMapper.class.getClassLoader().getResource("internal/rda-lang-map.json").openStream(), StandardCharsets.UTF_8)) - .lines().collect(Collectors.joining("\n")); - langMap.putAll(new org.json.JSONObject(json).toMap()); + ObjectMapper mapper = new ObjectMapper(); + InputStreamReader isr = new InputStreamReader(LanguageRDAMapper.class.getClassLoader().getResource("internal/rda-lang-map.json").openStream(), StandardCharsets.UTF_8); + langMap.putAll(mapper.readValue(isr, LinkedHashMap.class)); + isr.close(); + } catch (IOException e) { logger.error(e.getMessage(), e); } 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 0d33cda2b..add67c68e 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 @@ -47,6 +47,8 @@ public class ProjectRDAMapper { ((eu.eudat.data.entities.Project) entities.get("project")).setStatus((short)1); ((eu.eudat.data.entities.Project) entities.get("project")).setCreated(new Date()); ((eu.eudat.data.entities.Project) entities.get("project")).setModified(new Date()); + ((eu.eudat.data.entities.Project) entities.get("project")).setStartdate(new Date()); + ((eu.eudat.data.entities.Project) entities.get("project")).setEnddate(new Date()); ((eu.eudat.data.entities.Project) entities.get("project")).setType(0); apiContext.getOperationsContext().getDatabaseRepository().getProjectDao().createOrUpdate(((eu.eudat.data.entities.Project) entities.get("project"))); for (int i = 0; i < rda.getFunding().size(); i++) { diff --git a/dmp-backend/web/src/main/java/eu/eudat/types/MetricNames.java b/dmp-backend/web/src/main/java/eu/eudat/types/MetricNames.java new file mode 100644 index 000000000..a1d7dab89 --- /dev/null +++ b/dmp-backend/web/src/main/java/eu/eudat/types/MetricNames.java @@ -0,0 +1,24 @@ +package eu.eudat.types; + +public class MetricNames { + public static final String DATASET_TEMPLATE = "argos_dataset_templates"; + public static final String INSTALLATIONS = "installations"; + public static final String USERS = "argos_users"; + public static final String DMP = "argos_managed_dmps"; + public static final String DATASET = "argos_managed_dataset_descriptions"; + public static final String RESEARCHER = "argos_researchers"; + public static final String PROJECT = "argos_projects"; + public static final String FUNDERS = "argos_funders"; + public static final String GRANTS = "argos_grants"; + public static final String LANGUAGES = "argos_languages"; + public static final String DMP_WITH_GRANT = "argos_managed_dmps_with_grantid"; + public static final String DRAFT = "draft"; + public static final String FINALIZED = "finalized"; + public static final String PUBLISHED = "published"; + public static final String DOIED = "doied"; + public static final String ACTIVE = "active"; + public static final String USED = "used"; + public static final String LOGGEDIN = "loggedin"; + public static final String TOTAL = "total"; + public static final String NEXUS = "nexus_"; +} 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 599d404c3..9b70fe237 100644 --- a/dmp-backend/web/src/main/resources/config/application-devel.properties +++ b/dmp-backend/web/src/main/resources/config/application-devel.properties @@ -27,6 +27,11 @@ configuration.rda=RDACommonStandards.txt configuration.h2020template=documents/h2020.docx configuration.configurable_login_providers=configurableLoginProviders.json +####################EMAIL FILE TEMPLATES OVERRIDES CONFIGURATIONS########## +email.invite=classpath:templates/email/email.html +email.confirmation=classpath:templates/email/emailConfirmation.html +email.merge=classpath:templates/email/emailMergeConfirmation.html + #############FACEBOOK LOGIN CONFIGURATIONS######### facebook.login.clientId= facebook.login.clientSecret= diff --git a/dmp-backend/web/src/main/resources/config/application.properties b/dmp-backend/web/src/main/resources/config/application.properties index 34fd880db..9ae11ab94 100644 --- a/dmp-backend/web/src/main/resources/config/application.properties +++ b/dmp-backend/web/src/main/resources/config/application.properties @@ -5,11 +5,28 @@ logging.file=/logs/spring-boot-logging.log spring.profiles.active=devel eu.eudat.logic.proxy.allowed.host=https://eestore.paas2.uninett.no +####################Metrics############## +management.endpoint.metrics.enabled=false +management.metrics.enable.http=false +management.metrics.enable.jvm=false +management.metrics.enable.jdbc=false +management.metrics.enable.tomcat=false +management.metrics.enable.logback=false +management.metrics.enable.hikaricp=false +management.metrics.enable.cache=false +management.endpoints.web.base-path=/ +management.endpoints.web.exposure.include=prometheus +management.endpoint.prometheus.enabled=true +management.metrics.export.prometheus.enabled=true + ####################INVITATION MAIL CONFIGURATIONS############## ####################GENERIC MAIL CONFIGURATIONS################# mail.subject=Invitation to DMP Plan {dmpname} mail.from=opendmp-dev@cite.gr +####################DATASET TEMPLATE MAIL CONFIGURATIONS################# +admin.mail.subject=You have been invited to the Dataset Template {templateName} + ####################SPRING MAIL CONFIGURATIONS################# spring.mail.default-encoding=UTF-8 spring.mail.host=hermes.local.cite.gr @@ -25,6 +42,12 @@ configuration.rda=RDACommonStandards.txt configuration.h2020template=documents/h2020.docx configuration.configurable_login_providers=ConfigurableLoginProviders.json +####################EMAIL FILE TEMPLATES OVERRIDES CONFIGURATIONS########## +email.invite=file:templates/email/email.html +email.confirmation=file:templates/email/emailConfirmation.html +email.merge=file:templates/email/emailMergeConfirmation.html +email.dataset.template=file:templates/email/emailAdmin.html + #############LOGIN CONFIGURATIONS######### #############GENERIC LOGIN CONFIGURATIONS######### autouser.root.email= diff --git a/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml b/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml index 74f4f8726..4bb95d5c9 100644 --- a/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml +++ b/dmp-backend/web/src/main/resources/externalUrls/ExternalUrls.xml @@ -806,7 +806,7 @@ External https://api.ror.org/organizations?query={like}&page={page} 1 - application/vnd.api+json; charset=utf-8 + application/json; charset=utf-8 $['items'][*] diff --git a/dmp-backend/web/src/main/resources/templates/email/emailAdmin.html b/dmp-backend/web/src/main/resources/templates/email/emailAdmin.html new file mode 100644 index 000000000..7214de36b --- /dev/null +++ b/dmp-backend/web/src/main/resources/templates/email/emailAdmin.html @@ -0,0 +1,305 @@ + + + + + + Simple Transactional Email + + + + + + + + + +
  +
+ + + This is preheader text. Some clients will show this text as a preview. + + + + + + + + +
+ + + + +
+

Dear {recipient},

+

You have been invited to co-develop the Template {templateName}.

+

Click the button to redirect to {templateName}.

+ + + + + + + +
+ + + + + + +
{templateName}
+
+ +
+
+ + + + + + +
+
 
+ + \ No newline at end of file diff --git a/dmp-db-scema/createDB/createdb.sh b/dmp-db-scema/createDB/createdb.sh index 747cdc5df..cf2859aa2 100644 --- a/dmp-db-scema/createDB/createdb.sh +++ b/dmp-db-scema/createDB/createdb.sh @@ -2,4 +2,5 @@ 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 +PGPASSWORD=$POSTGRES_PASSWORD psql -d $POSTGRES_DB -U $POSTGRES_USER --set=POSTGRES_USER="$POSTGRES_USER" -f main/dmp-dump.sql; +PGPASSWORD=$POSTGRES_PASSWORD psql --set=ADMIN_USERNAME="$ADMIN_USERNAME" --set=ADMIN_PASSWORD="$ADMIN_PASSWORD" -d $POSTGRES_DB -U $POSTGRES_USER -f main/data-dump.sql; diff --git a/dmp-db-scema/main/data-dump.sql b/dmp-db-scema/main/data-dump.sql index a6b354637..1ddeee509 100644 --- a/dmp-db-scema/main/data-dump.sql +++ b/dmp-db-scema/main/data-dump.sql @@ -1,6 +1,6 @@ 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'); +INSERT INTO public."Credential" VALUES (uuid_generate_v4(), 0, 5, :'ADMIN_USERNAME', 'fake@email.org', :'ADMIN_PASSWORD', now(), now(), (SELECT public."UserInfo"."id" FROM public."UserInfo" WHERE name = :'ADMIN_USERNAME'), 'dmp'); INSERT INTO public."UserRole"("Role", "UserId") VALUES (2, (SELECT public."UserInfo"."id" FROM public."UserInfo" WHERE name = :'ADMIN_USERNAME')); diff --git a/dmp-db-scema/main/dmp-dump.sql b/dmp-db-scema/main/dmp-dump.sql index 6da64c270..b340e568f 100644 --- a/dmp-db-scema/main/dmp-dump.sql +++ b/dmp-db-scema/main/dmp-dump.sql @@ -583,7 +583,7 @@ CREATE TABLE public."EmailConfirmation" ( ); -ALTER TABLE public."LoginConfirmationEmail" OWNER TO :POSTGRES_USER; +ALTER TABLE public."EmailConfirmation" OWNER TO :POSTGRES_USER; -- -- Name: Notification; Type: TABLE; Schema: public; Owner: :POSTGRES_USER @@ -1060,8 +1060,8 @@ ALTER TABLE ONLY public."Lock" -- Name: LoginConfirmationEmail LoginConfirmationEmail_pkey; Type: CONSTRAINT; Schema: public; Owner: :POSTGRES_USER -- -ALTER TABLE ONLY public."LoginConfirmationEmail" - ADD CONSTRAINT "LoginConfirmationEmail_pkey" PRIMARY KEY ("ID"); +ALTER TABLE ONLY public."EmailConfirmation" + ADD CONSTRAINT "EmailConfirmation_pkey" PRIMARY KEY ("ID"); -- diff --git a/dmp-frontend/browserslist b/dmp-frontend/.browserslistrc similarity index 100% rename from dmp-frontend/browserslist rename to dmp-frontend/.browserslistrc diff --git a/dmp-frontend/angular.json b/dmp-frontend/angular.json index 208b96dd7..c1a3f7752 100644 --- a/dmp-frontend/angular.json +++ b/dmp-frontend/angular.json @@ -31,16 +31,26 @@ "node_modules/cookieconsent/build/cookieconsent.min.js", "node_modules/tinymce/tinymce.min.js" - ] + ], + "vendorChunk": true, + "extractLicenses": false, + "buildOptimizer": false, + "sourceMap": true, + "optimization": false, + "namedChunks": true }, "configurations": { "production": { + "budgets": [ + { + "type": "anyComponentStyle", + "maximumWarning": "6kb" + } + ], "optimization": true, "outputHashing": "all", "sourceMap": false, - "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, @@ -52,12 +62,16 @@ ] }, "staging": { + "budgets": [ + { + "type": "anyComponentStyle", + "maximumWarning": "6kb" + } + ], "optimization": true, "outputHashing": "all", "sourceMap": false, - "extractCss": true, "namedChunks": false, - "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true, @@ -68,7 +82,8 @@ } ] } - } + }, + "defaultConfiguration": "" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", @@ -154,7 +169,7 @@ "schematics": { "@schematics/angular:component": { "prefix": "app", - "styleext": "scss" + "style": "scss" }, "@schematics/angular:directive": { "prefix": "app" diff --git a/dmp-frontend/package.json b/dmp-frontend/package.json index ed4ad77f2..12b85564e 100644 --- a/dmp-frontend/package.json +++ b/dmp-frontend/package.json @@ -5,64 +5,65 @@ "scripts": { "ng": "ng", "start": "ng serve", - "build": "ng build --prod", + "build": "ng build --configuration production", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, "private": true, "dependencies": { - "@angular/animations": "^8.2.7", - "@angular/common": "^8.2.7", - "@angular/compiler": "^8.2.7", - "@angular/core": "^8.2.7", - "@angular/forms": "^8.2.7", - "@angular/material-moment-adapter": "^8.2.0", - "@angular/platform-browser": "^8.2.7", - "@ngx-translate/core": "^11.0.1", - "@ngx-translate/http-loader": "^4.0.0", - "@swimlane/ngx-datatable": "^16.0.2", - "@tinymce/tinymce-angular": "^3.6.1", - "@w11k/angular-sticky-things": "^1.1.2", + "@angular/animations": "^12.2.7", + "@angular/common": "^12.2.7", + "@angular/compiler": "^12.2.7", + "@angular/core": "^12.2.7", + "@angular/forms": "^12.2.7", + "@angular/material-moment-adapter": "^12.2.7", + "@angular/platform-browser": "^12.2.7", + "@kolkov/angular-editor": "^1.2.0", + "@ngx-translate/core": "^13.0.0", + "@ngx-translate/http-loader": "^6.0.0", + "@swimlane/ngx-datatable": "^20.0.0", + "@tinymce/tinymce-angular": "^4.2.4", + "@w11k/angular-sticky-things": "^1.3.2", "bootstrap": "^4.3.1", "cookieconsent": "^3.1.1", "core-js": "^2.5.5", - "file-saver": "^2.0.2", - "moment": "^2.24.0", - "moment-timezone": "^0.5.26", - "ng-dialog-animation": "^9.0.3", + "file-saver": "^2.0.5", + "moment": "^2.29.1", + "moment-timezone": "^0.5.33", + "ng-dialog-animation": "^9.0.4", "ng2-dragula": "^2.1.1", - "ngx-cookie-service": "^2.2.0", + "ngx-cookie-service": "^12.0.3", "ngx-cookieconsent": "^2.2.3", - "ngx-dropzone": "^2.2.2", - "ngx-guided-tour": "^1.1.10", + "ngx-dropzone": "^3.0.0", + "ngx-guided-tour": "^1.1.11", "ngx-matomo": "^0.1.4", "rxjs": "^6.3.2", - "tinymce": "^5.4.2", - "tslib": "^1.10.0", + "tinymce": "^5.9.2", + "tslib": "^2.0.0", "web-animations-js": "^2.3.2", - "zone.js": "~0.9.1" + "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.803.5", - "@angular/cdk": "^8.2.0", - "@angular/material": "^8.2.0", - "@angular/cli": "8.3.5", - "@angular/compiler-cli": "^8.2.7", - "@angular/platform-browser-dynamic": "^8.2.7", - "@angular/router": "^8.2.7", - "@angular/language-service": "^8.2.7", - "@types/facebook-js-sdk": "^3.3.0", - "@types/file-saver": "^2.0.1", - "@types/gapi": "^0.0.39", - "@types/gapi.auth2": "^0.0.50", - "@types/jasmine": "~3.4.0", - "@types/jasminewd2": "~2.0.6", - "@types/moment-timezone": "^0.5.12", - "@types/node": "^10.11.7", - "codelyzer": "^5.1.1", - "ts-node": "~8.4.1", - "tslint": "~5.20.0", - "typescript": "3.5.3" + "@angular-devkit/build-angular": "~12.2.7", + "@angular/cdk": "^12.2.7", + "@angular/material": "^12.2.7", + "@angular/cli": "12.2.7", + "@angular/compiler-cli": "^12.2.7", + "@angular/platform-browser-dynamic": "^12.2.7", + "@angular/router": "^12.2.7", + "@angular/language-service": "^12.2.7", + "@types/facebook-js-sdk": "^3.3.5", + "@types/file-saver": "^2.0.3", + "@types/gapi": "^0.0.41", + "@types/gapi.auth2": "^0.0.55", + "@types/jasmine": "~3.9.1", + "@types/jasminewd2": "~2.0.10", + "@types/moment-timezone": "^0.5.13", + "@types/node": "^12.11.1", + "codelyzer": "^6.0.2", + "ts-node": "~10.2.1", + "tslint": "~6.1.0", + "typescript": "4.3.5" } } diff --git a/dmp-frontend/src/app/app-routing.module.ts b/dmp-frontend/src/app/app-routing.module.ts index 078f3a7bc..120a88ec1 100644 --- a/dmp-frontend/src/app/app-routing.module.ts +++ b/dmp-frontend/src/app/app-routing.module.ts @@ -258,7 +258,7 @@ const appRoutes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forRoot(appRoutes)], + imports: [RouterModule.forRoot(appRoutes, { relativeLinkResolution: 'legacy' })], exports: [RouterModule], }) export class AppRoutingModule { } diff --git a/dmp-frontend/src/app/app.component.ts b/dmp-frontend/src/app/app.component.ts index 1d17cb1ae..e514f7614 100644 --- a/dmp-frontend/src/app/app.component.ts +++ b/dmp-frontend/src/app/app.component.ts @@ -19,7 +19,7 @@ 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'; +import { MatSidenav } from '@angular/material/sidenav'; declare const gapi: any; @@ -39,7 +39,7 @@ export class AppComponent implements OnInit, AfterViewInit { private statusChangeSubscription: Subscription; onlySplash = true; - @ViewChild('sidenav', {static:false}) sidenav:MatSidenav; + @ViewChild('sidenav') sidenav:MatSidenav; constructor( private router: Router, diff --git a/dmp-frontend/src/app/app.module.ts b/dmp-frontend/src/app/app.module.ts index d5ad06c43..58c45d346 100644 --- a/dmp-frontend/src/app/app.module.ts +++ b/dmp-frontend/src/app/app.module.ts @@ -2,15 +2,14 @@ import { OverlayModule } from '@angular/cdk/overlay'; import { HttpClient, HttpClientModule } from '@angular/common/http'; 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 { MatFormFieldDefaultOptions, MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field'; import { BrowserModule, Title } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AppRoutingModule } from '@app/app-routing.module'; import { AppComponent } from '@app/app.component'; import { CoreServiceModule } from '@app/core/core-service.module'; -import { CultureService } from '@app/core/services/culture/culture-service'; import { NotificationModule } from '@app/library/notification/notification.module'; import { LoginModule } from '@app/ui/auth/login/login.module'; import { DatasetCreateWizardModule } from '@app/ui/dataset-create-wizard/dataset-create-wizard.module'; @@ -24,15 +23,16 @@ 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 { DragulaModule } from 'ng2-dragula'; import { CookieService } from 'ngx-cookie-service'; import { NgcCookieConsentConfig, NgcCookieConsentModule } from 'ngx-cookieconsent'; import { MatomoModule } from 'ngx-matomo'; import { ConfigurationService } from './core/services/configuration/configuration.service'; +import { CultureService } from './core/services/culture/culture-service'; 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) { 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 a82869dec..4344a5eea 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 @@ -1,5 +1,6 @@ export enum DatasetProfileFieldViewStyle { TextArea = "textarea", + RichTextArea = "richTextarea", BooleanDecision = "booleanDecision", ComboBox = "combobox", CheckBox = "checkBox", diff --git a/dmp-frontend/src/app/core/common/enum/external-dataset-type-enum.ts b/dmp-frontend/src/app/core/common/enum/external-dataset-type-enum.ts new file mode 100644 index 000000000..241b00ab1 --- /dev/null +++ b/dmp-frontend/src/app/core/common/enum/external-dataset-type-enum.ts @@ -0,0 +1,5 @@ +export enum ExternalDatasetTypeEnum{ + ReusedDataset = "reused_dataset", + ProducedDataset = "produced_dataset", + Other = "other" +} \ No newline at end of file diff --git a/dmp-frontend/src/app/core/common/enum/save-type.ts b/dmp-frontend/src/app/core/common/enum/save-type.ts index 897d86dd7..855a3109a 100644 --- a/dmp-frontend/src/app/core/common/enum/save-type.ts +++ b/dmp-frontend/src/app/core/common/enum/save-type.ts @@ -1,4 +1,5 @@ export enum SaveType { close = 0, - addNew = 1 + addNew = 1, + finalize = 2 } diff --git a/dmp-frontend/src/app/core/core-service.module.ts b/dmp-frontend/src/app/core/core-service.module.ts index 0f8656770..e2f71e495 100644 --- a/dmp-frontend/src/app/core/core-service.module.ts +++ b/dmp-frontend/src/app/core/core-service.module.ts @@ -64,7 +64,7 @@ export class CoreServiceModule { 'CoreModule is already loaded. Import it in the AppModule only'); } } - static forRoot(): ModuleWithProviders { + static forRoot(): ModuleWithProviders { return { ngModule: CoreServiceModule, providers: [ 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 47adf26c8..c1714c52b 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 @@ -1,3 +1,4 @@ +import { ExternalDatasetTypeEnum } from "@app/core/common/enum/external-dataset-type-enum"; import { DatasetProfileComboBoxType } from "../../../common/enum/dataset-profile-combo-box-type"; import { DatasetProfileInternalDmpEntitiesType } from "../../../common/enum/dataset-profile-internal-dmp-entities-type"; @@ -38,6 +39,10 @@ export interface TextAreaFieldData extends FieldData { } +export interface RichTextAreaFieldData extends FieldData { + +} + export interface WordListFieldData extends FieldData { type: DatasetProfileComboBoxType; options: Array; @@ -72,6 +77,7 @@ export interface DmpsAutoCompleteFieldData extends FieldData { export interface ExternalDatasetsFieldData extends FieldData { multiAutoComplete: boolean; + type?: ExternalDatasetTypeEnum; } export interface DataRepositoriesFieldData 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 1063a3cbf..9724ad28e 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 @@ -1,5 +1,5 @@ import { FormGroup, FormBuilder } from '@angular/forms'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; export class DatasetIdModel { identifier: string; 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 998978a2c..0cc4f67d0 100644 --- a/dmp-frontend/src/app/core/services/organisation/organisation.service.ts +++ b/dmp-frontend/src/app/core/services/organisation/organisation.service.ts @@ -1,15 +1,14 @@ -import { Injectable } from "@angular/core"; import { HttpHeaders } from "@angular/common/http"; -import { BaseHttpService } from "../http/base-http.service"; -import { environment } from "../../../../environments/environment"; -import { Observable } from "rxjs/internal/Observable"; -import { DataTableData } from "../../model/data-table/data-table-data"; -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 { Injectable } from "@angular/core"; import { ExternalSourceItemModel } from "@app/core/model/external-sources/external-source-item"; import { RequestItem } from "@app/core/query/request-item"; +import { Observable } from "rxjs"; +import { DataTableData } from "../../model/data-table/data-table-data"; +import { DataTableRequest } from "../../model/data-table/data-table-request"; +import { OrganizationModel } from "../../model/organisation/organization"; +import { OrganisationCriteria } from "../../query/organisation/organisation-criteria"; +import { ConfigurationService } from '../configuration/configuration.service'; +import { BaseHttpService } from "../http/base-http.service"; @Injectable() export class OrganisationService { @@ -27,14 +26,14 @@ export class OrganisationService { } public searchInternalOrganisations(dataTableRequest: DataTableRequest): Observable> { - return this.http.post>(this.actionUrl + 'internal/organisations', dataTableRequest , { headers: this.headers }); + 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 }); + 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 }); + return this.http.post>(this.actionUrl + 'public/organisations', dataTableRequest, { headers: this.headers }); } } 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 a7dada1f9..985806d78 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 @@ -79,6 +79,7 @@ export class EnumUtils { case DatasetProfileFieldViewStyle.FreeText: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.FREE-TEXT'); case DatasetProfileFieldViewStyle.RadioBox: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.RADIO-BOX'); case DatasetProfileFieldViewStyle.TextArea: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.TEXT-AREA'); + case DatasetProfileFieldViewStyle.RichTextArea: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.RICH-TEXT-AREA'); case DatasetProfileFieldViewStyle.DatePicker: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.DATE-PICKER'); case DatasetProfileFieldViewStyle.ExternalDatasets: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.EXTERNAL-DATASETS'); case DatasetProfileFieldViewStyle.DataRepositories: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.DATA-REPOSITORIES'); @@ -101,6 +102,7 @@ export class EnumUtils { 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.RichTextArea: return this.language.instant('TYPES.DATASET-PROFILE-FIELD-VIEW-STYLE.RICH-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'); 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 32b99b46c..6c88bba4a 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 @@ -36,4 +36,6 @@ export interface MultipleAutoCompleteConfiguration { autoSelectFirstOptionOnBlur?: boolean; + appendClassToItem?: {class: string, applyFunc: (item:any) => 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 3c89cd3ff..c09a78590 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 @@ -1,7 +1,7 @@
- + - +
{{_displayFn(selectedItem)}}
@@ -9,11 +9,23 @@
+ + + + arrow_drop_down +
- - - arrow_drop_down + @@ -23,10 +35,17 @@ item: item }">
-
- {{_titleFn(item)}} -
- {{_subtitleFn(item)}} +
+
+
+ {{_titleFn(item)}} +
+
+
+
+ +
+
{{popupItemActionIcon}}
@@ -34,27 +53,34 @@ -
- + +
-
- {{_titleFn(item)}} -
- {{_subtitleFn(item)}} +
+
+
+ {{_titleFn(item)}} +
+
+
+
+ +
+
{{popupItemActionIcon}}
- + No results found! - -
+
+
loading... 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 76cfcb18e..611710a88 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 @@ -19,12 +19,45 @@ } +.title-subtitle-fn { + height: auto; + width: calc(100% - 46px); +} + +.title-fn-inner { + width: inherit; + overflow: unset; + white-space: normal; +} + .title-fn { - flex-grow: 1; - white-space: nowrap; - width: calc(100% - 16px); - overflow: hidden; - text-overflow: ellipsis; + width: 100%; + overflow: hidden; + text-overflow: unset !important; + white-space: normal; +} + + +.subtitle-fn-inner { + width: inherit; + height: inherit; + overflow: hidden; + text-overflow: ellipsis; +} + +.subtitle-fn { + width: 100%; + overflow: hidden; + white-space: nowrap; + display: -webkit-box; + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -ms-box-orient: vertical; + box-orient: vertical; + -webkit-line-clamp: 1; + -moz-line-clamp: 1; + -ms-line-clamp: 1; + line-clamp: 1; } .option-icon { @@ -64,4 +97,4 @@ &::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 fd9854690..e08ffe914 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 @@ -2,14 +2,16 @@ import { FocusMonitor } from '@angular/cdk/a11y'; 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'; +import { MatChipInputEvent } from '@angular/material/chips'; +import { ErrorStateMatcher, mixinErrorState } from '@angular/material/core'; import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { MatFormFieldControl } from '@angular/material/form-field'; import { AutoCompleteGroup } from '@app/library/auto-complete/auto-complete-group'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; import { BaseComponent } from '@common/base/base.component'; -import { Observable, of as observableOf, Subject } from 'rxjs'; -import { debounceTime, distinctUntilChanged, map, mergeMap, startWith, takeUntil, switchMap } from 'rxjs/operators'; +import { isNullOrUndefined } from '@swimlane/ngx-datatable'; +import { BehaviorSubject, combineLatest, Observable, of as observableOf, Subject, Subscription } from 'rxjs'; +import {debounceTime, distinctUntilChanged, map, mergeMap, startWith, takeUntil, switchMap, tap} from 'rxjs/operators'; export class CustomComponentBase extends BaseComponent { constructor( @@ -50,6 +52,13 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp id = `multiple-autocomplete-${MultipleAutoCompleteComponent.nextId++}`; stateChanges = new Subject(); + + valueOnBlur = new BehaviorSubject(null); + onSelectAutoCompleteValue = new BehaviorSubject(null); + valueAssignSubscription: Subscription; + + queryValue: string = ""; + focused = false; controlType = 'multiple-autocomplete'; describedBy = ''; @@ -66,6 +75,8 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp get shouldLabelFloat() { return this.focused || !this.empty; } + @Input() minLength: number = 0; + @Input() showNoResultsLabel: boolean = true; @Input() hidePlaceholder: boolean = false; @Input() get placeholder() { return this._placeholder; } @@ -123,7 +134,36 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp } } - ngOnInit() { } + ngOnInit() { + this.valueAssignSubscription = combineLatest(this.valueOnBlur.asObservable(), this.onSelectAutoCompleteValue.asObservable()) + .pipe(debounceTime(100)) + .subscribe(latest =>{ + const fromBlur = latest[0]; + const fromAutoComplete = latest[1]; + + if(isNullOrUndefined(fromBlur) && isNullOrUndefined(fromAutoComplete)){ + return; + } + //higher precedence + if(!isNullOrUndefined(fromAutoComplete)){ + this.optionSelectedInternal(fromAutoComplete); + + // consumed and flush + this.onSelectAutoCompleteValue.next(null); + this.valueOnBlur.next(null); + return; + } + + if(!isNullOrUndefined(fromBlur)){ + this.optionSelectedInternal(fromBlur); + + // consumed and flush + this.onSelectAutoCompleteValue.next(null); + this.valueOnBlur.next(null); + return; + } + }); + } ngDoCheck(): void { if (this.ngControl) { @@ -178,7 +218,8 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp } _optionSelected(event: MatAutocompleteSelectedEvent) { - this.optionSelectedInternal(event.option.value); + // this.optionSelectedInternal(event.option.value); + this.onSelectAutoCompleteValue.next(event.option.value); this.autocompleteInput.nativeElement.value = ''; } @@ -227,7 +268,8 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp debounceTime(this.requestDelay), distinctUntilChanged(), distinctUntilChanged(), - switchMap(query => this.filter(query))); + tap(query => this.queryValue = query), + switchMap(query => (!this.minLength || (query && query.length >= this.minLength)) ? this.filter(query) : observableOf([]))); if (this.configuration.groupingFn) { this._groupedItems = this._items.pipe(map(items => this.configuration.groupingFn(items))); } } @@ -235,7 +277,8 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp public onBlur($event: MouseEvent) { if (this.inputValue && this.inputValue.length > 1 && this.autocomplete.options && this.autocomplete.options.length > 0 && this.autoSelectFirstOptionOnBlur) { - this.optionSelectedInternal(this.autocomplete.options.first.value); + // this.optionSelectedInternal(this.autocomplete.options.first.value); + this.valueOnBlur.next(this.autocomplete.options.first.value); } // Clear text if not an option @@ -271,6 +314,10 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp ngOnDestroy() { this.stateChanges.complete(); this.fm.stopMonitoring(this.elRef.nativeElement); + if(this.valueAssignSubscription){ + this.valueAssignSubscription.unsubscribe(); + this.valueAssignSubscription = null; + } } //Configuration getters @@ -306,7 +353,7 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp } get requestDelay(): number { - return this.configuration.requestDelay != null ? this.configuration.requestDelay : 400; + return this.configuration.requestDelay != null ? this.configuration.requestDelay : 600; } get minFilteringChars(): number { @@ -324,6 +371,9 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp get popupItemActionIcon(): string { return this.configuration.popupItemActionIcon != null ? this.configuration.popupItemActionIcon : ''; } + get appendClassToItem(): {class: string, applyFunc: (item: any) => boolean}[]{ + return this.configuration.appendClassToItem !== null ? this.configuration.appendClassToItem : null; + } //Chip Functions _addItem(event: MatChipInputEvent): void { @@ -332,7 +382,8 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp const value = event.value; // Add our fruit if ((value || '').trim()) { - this.optionSelectedInternal(value); + // this.optionSelectedInternal(value); + this.valueOnBlur.next(value); } // Reset the input value if (input) { @@ -358,4 +409,19 @@ export class MultipleAutoCompleteComponent extends _CustomComponentMixinBase imp if (event != null) { event.stopPropagation(); } this.optionActionClicked.emit(item); } + + + private readonly empyObj = {}; + computeClass(value: any){ + if(!(this.appendClassToItem && this.appendClassToItem.length)){ + return this.empyObj; + } + + const classes = this.appendClassToItem.filter(e=> e.applyFunc(value)); + return classes.reduce((r, current) => { + r[current.class] = true; + return r; + } , {}); + + } } diff --git a/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete.component.html b/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete.component.html index 65ac69493..4d74c6c63 100644 --- a/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete.component.html +++ b/dmp-frontend/src/app/library/auto-complete/single/single-auto-complete.component.html @@ -35,7 +35,7 @@
- No results found! + No results found!
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 4c40f4748..c63215f13 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 @@ -9,7 +9,7 @@ import { AutoCompleteGroup } from '@app/library/auto-complete/auto-complete-grou import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { BaseComponent } from '@common/base/base.component'; import { Observable, of as observableOf, Subject } from 'rxjs'; -import { debounceTime, distinctUntilChanged, map, mergeMap, startWith, takeUntil, switchMap } from 'rxjs/operators'; +import {debounceTime, distinctUntilChanged, map, mergeMap, startWith, takeUntil, switchMap, tap} from 'rxjs/operators'; export class CustomComponentBase extends BaseComponent { @@ -62,6 +62,7 @@ export class SingleAutoCompleteComponent extends _CustomComponentMixinBase imple _groupedItems: Observable; _selectedItems: Map = new Map(); + queryValue: string = ""; get empty() { return (this.value == null) && (!this.inputValue || this.inputValue.length === 0); } @@ -224,6 +225,7 @@ export class SingleAutoCompleteComponent extends _CustomComponentMixinBase imple startWith(null), debounceTime(this.requestDelay), distinctUntilChanged(), + tap(query => this.queryValue = query), switchMap(query => this.filter(query))); if (this.configuration.groupingFn) { this._groupedItems = this._items.pipe(map(items => this.configuration.groupingFn(items))); } @@ -313,7 +315,7 @@ export class SingleAutoCompleteComponent extends _CustomComponentMixinBase imple } get requestDelay(): number { - return this.configuration.requestDelay != null ? this.configuration.requestDelay : 400; + return this.configuration.requestDelay != null ? this.configuration.requestDelay : 600; } get minFilteringChars(): number { diff --git a/dmp-frontend/src/app/library/deactivate/can-deactivate.guard.ts b/dmp-frontend/src/app/library/deactivate/can-deactivate.guard.ts index 75d89ac2d..8e6df7ff9 100644 --- a/dmp-frontend/src/app/library/deactivate/can-deactivate.guard.ts +++ b/dmp-frontend/src/app/library/deactivate/can-deactivate.guard.ts @@ -30,7 +30,7 @@ export class CanDeactivateGuard extends BaseComponent implements CanDeactivate x ? true : false)); diff --git a/dmp-frontend/src/app/library/deactivate/deactivate.component.ts b/dmp-frontend/src/app/library/deactivate/deactivate.component.ts index 2b854f125..bfe39b109 100644 --- a/dmp-frontend/src/app/library/deactivate/deactivate.component.ts +++ b/dmp-frontend/src/app/library/deactivate/deactivate.component.ts @@ -1,6 +1,7 @@ -import { HostListener } from '@angular/core'; +import { HostListener, Directive } from '@angular/core'; import { BaseComponent } from '@common/base/base.component'; +@Directive() export abstract class CheckDeactivateBaseComponent extends BaseComponent { protected constructor() { super(); } 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 e093b543b..651975b98 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 @@ -17,7 +17,7 @@ export class GuidedTourComponent implements AfterViewInit, OnDestroy { @Input() public minimalTourStepWidth?= 500; @Input() public skipText?= 'Leave Tour'; @Input() public nextText?= 'Got it!'; - @ViewChild('tourStep', { static: false }) public tourStep: ElementRef; + @ViewChild('tourStep') public tourStep: ElementRef; public highlightPadding = 4; public currentTourStep: TourStep = null; public selectedElementRect: DOMRect = null; diff --git a/dmp-frontend/src/app/library/guided-tour/guided-tour.module.ts b/dmp-frontend/src/app/library/guided-tour/guided-tour.module.ts index d51266431..6bcb422ab 100644 --- a/dmp-frontend/src/app/library/guided-tour/guided-tour.module.ts +++ b/dmp-frontend/src/app/library/guided-tour/guided-tour.module.ts @@ -12,7 +12,7 @@ import { WindowRefService } from './windowref.service'; entryComponents: [GuidedTourComponent], }) export class GuidedTourModule { - public static forRoot(): ModuleWithProviders { + public static forRoot(): ModuleWithProviders { return { ngModule: GuidedTourModule, providers: [ErrorHandler, GuidedTourService], diff --git a/dmp-frontend/src/app/library/rich-text-editor/rich-text-editor.component.scss b/dmp-frontend/src/app/library/rich-text-editor/rich-text-editor.component.scss new file mode 100644 index 000000000..721812d6a --- /dev/null +++ b/dmp-frontend/src/app/library/rich-text-editor/rich-text-editor.component.scss @@ -0,0 +1,45 @@ +::ng-deep .angular-editor-textarea { + min-height: 150px !important; +} + +//.form-field-subscript-wrapper { +// font-size: 75%; +// padding-left: 12px; +// margin-top: 8px; +//} + +.editor-wrapper { + border: 1px solid transparent !important; + border-radius: 5px; +} + +::ng-deep .angular-editor-toolbar { + margin-left: 1px; + margin-right: 1px; +} + +::ng-deep .angular-editor-textarea, ::ng-deep .angular-editor-toolbar { + border: none !important; +} + +::ng-deep .angular-editor { + border: 1px solid #ddd !important; + border-radius: 5px; + background-color: #fff; +} + +.editor-wrapper:hover, ::ng-deep .angular-editor:hover { + border: 1px solid #000 !important; +} + +.editor-wrapper:focus-within, ::ng-deep .angular-editor:focus-within { + border: 1px solid #034459 !important; +} + +.required.editor-wrapper, .required .editor ::ng-deep .angular-editor { + border: 1px solid #f44336 !important; +} + +.full-width { + width: 100%; +} diff --git a/dmp-frontend/src/app/library/rich-text-editor/rich-text-editor.component.ts b/dmp-frontend/src/app/library/rich-text-editor/rich-text-editor.component.ts new file mode 100644 index 000000000..0a820532b --- /dev/null +++ b/dmp-frontend/src/app/library/rich-text-editor/rich-text-editor.component.ts @@ -0,0 +1,58 @@ +import {Component, Input} from "@angular/core"; +import {AngularEditorConfig} from "@kolkov/angular-editor"; + +@Component({ + selector: 'rich-text-editor-component', + template: ` +
+ +
+ `, + styleUrls: ['./rich-text-editor.component.scss'] +}) +export class RichTextEditorComponent { + @Input() parentFormGroup; + @Input() controlName; + @Input() id: string = "editor1"; + @Input() placeholder: string = "Enter text"; + @Input() required: boolean = false; + @Input() wrapperClasses: string = ""; + + editorConfig: AngularEditorConfig = { + editable: true, + spellcheck: true, + height: 'auto', + minHeight: '0', + maxHeight: 'auto', + width: '100%', + minWidth: '0', + translate: 'yes', + enableToolbar: true, + showToolbar: true, + placeholder: '', + defaultParagraphSeparator: '', + defaultFontName: '', + defaultFontSize: '', + sanitize: true, + toolbarPosition: 'top', + toolbarHiddenButtons: [ + [ + 'heading', + 'fontName' + ], + [ + 'fontSize', + 'backgroundColor', + 'customClasses', + 'insertImage', + 'insertVideo', + 'removeFormat', + 'toggleEditorMode' + ] + ] + }; + + constructor() { + } +} diff --git a/dmp-frontend/src/app/library/rich-text-editor/rich-text-editor.module.ts b/dmp-frontend/src/app/library/rich-text-editor/rich-text-editor.module.ts new file mode 100644 index 000000000..a4470d71b --- /dev/null +++ b/dmp-frontend/src/app/library/rich-text-editor/rich-text-editor.module.ts @@ -0,0 +1,21 @@ +import {NgModule} from "@angular/core"; +import {HttpClientModule} from "@angular/common/http"; +import {CommonUiModule} from "@common/ui/common-ui.module"; +import {CommonFormsModule} from "@common/forms/common-forms.module"; +import {AngularEditorModule} from "@kolkov/angular-editor"; +import {RichTextEditorComponent} from "@app/library/rich-text-editor/rich-text-editor.component"; + +@NgModule({ + imports: [ + CommonUiModule, + CommonFormsModule, + HttpClientModule, AngularEditorModule + ], + declarations: [ + RichTextEditorComponent + ], + exports: [ + RichTextEditorComponent + ] +}) +export class RichTextEditorModule { } 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 e8d7b018e..473c4a822 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,15 +1,18 @@ import { FormGroup } from '@angular/forms'; import { FieldDataEditorModel } from './field-data-editor-model'; import { ExternalDatasetsFieldData } from '../../../../../core/model/dataset-profile-definition/field-data/field-data'; +import { ExternalDatasetTypeEnum } from '@app/core/common/enum/external-dataset-type-enum'; export class ExternalDatasetsDataEditorModel extends FieldDataEditorModel { public label: string; public multiAutoComplete: boolean; + public type: ExternalDatasetTypeEnum; buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { const formGroup = this.formBuilder.group({ label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('ExternalDatasetsDataEditorModel.label')) }], - multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('ExternalDatasetsDataEditorModel.multiAutoComplete')) }] + multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('ExternalDatasetsDataEditorModel.multiAutoComplete')) }], + type: [{ value: this.type, disabled: (disabled && !skipDisable.includes('ExternalDatasetsDataEditorModel.type')) }] }); return formGroup; } @@ -17,6 +20,7 @@ export class ExternalDatasetsDataEditorModel extends FieldDataEditorModel { + + buildForm(disabled: boolean = false, skipDisable: Array = []): FormGroup { + const formGroup = this.formBuilder.group({ + label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('RichTextAreaFieldDataEditorModel.label')) }] + }); + return formGroup; + } + + fromModel(item: RichTextAreaFieldData): RichTextAreaFieldDataEditorModel { + 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 51ebd8030..ad245c0c1 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 @@ -10,6 +10,7 @@ import { FieldDataEditorModel } from './field-data/field-data-editor-model'; import { FreeTextFieldDataEditorModel } from './field-data/free-text-field-data-editor-model'; import { RadioBoxFieldDataEditorModel } from './field-data/radio-box-field-data-editor-model'; import { TextAreaFieldDataEditorModel } from './field-data/text-area-field-data-editor-model'; +import { RichTextAreaFieldDataEditorModel } from './field-data/rich-text-area-field-data-editor-model'; import { WordListFieldDataEditorModel } from './field-data/word-list-field-data-editor-model'; import { ViewStyleEditorModel } from './view-style-editor-model'; import { VisibilityEditorModel } from './visibility-editor-model'; @@ -66,6 +67,7 @@ export class FieldEditorModel extends BaseFormModel { if (this.viewStyle.renderStyle === 'radiobox') { this.data = new RadioBoxFieldDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'checkBox') { this.data = new CheckBoxFieldDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'textarea') { this.data = new TextAreaFieldDataEditorModel().fromModel(item.data); } + if (this.viewStyle.renderStyle === 'richTextarea') { this.data = new RichTextAreaFieldDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'freetext') { this.data = new FreeTextFieldDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'booleanDecision') { this.data = new BooleanDecisionFieldDataEditorModel().fromModel(item.data); } if (this.viewStyle.renderStyle === 'datePicker') { this.data = new DatePickerDataEditorModel().fromModel(item.data); } 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 d270409e6..b5fd6b6b0 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,5 +1,9 @@ +import { DragDropModule } from '@angular/cdk/drag-drop'; import { NgModule } from '@angular/core'; +//matrial +import { MatBadgeModule } from '@angular/material/badge'; import { FormattingModule } from '@app/core/formatting.module'; +import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.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'; @@ -26,11 +30,12 @@ import { DialogConfirmationUploadDatasetProfiles } from '@app/ui/admin/dataset-p 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 { AngularStickyThingsModule } from '@w11k/angular-sticky-things'; +import { DragulaModule } from 'ng2-dragula'; +import { NgxDropzoneModule } from 'ngx-dropzone'; 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'; @@ -41,37 +46,35 @@ import { DatasetProfileEditorResearchersFieldComponent } from './editor/componen 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 { DatasetProfileEditorValidatorFieldComponent } from './editor/components/field-type/validator/dataset-profile-editor-validator-field.component'; -import { NgxDropzoneModule } from 'ngx-dropzone'; +import { FinalPreviewComponent } from './editor/components/final-preview/final-preview.component'; +import { DatasetProfileEditorSectionFieldSetComponent } from './editor/components/section-fieldset/dataset-profile-editor-section-fieldset.component'; 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'; +import {TransitionGroupModule} from "@app/ui/transition-group/transition-group.module"; +import { RichTextEditorModule } from "@app/library/rich-text-editor/rich-text-editor.module"; +import {DatasetProfileEditorRichTextAreaFieldComponent} from "@app/ui/admin/dataset-profile/editor/components/field-type/rich-textarea/dataset-profile-editor-rich-text-area-field.component"; -//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: [ - CommonUiModule, - CommonFormsModule, - FormattingModule, - DatasetProfileRoutingModule, - ConfirmationDialogModule, - NgxDropzoneModule, - FormProgressIndicationModule, - DatasetModule, - AngularStickyThingsModule, - DragDropModule, - MatBadgeModule, - DragulaModule, - AutoCompleteModule - ], + imports: [ + CommonUiModule, + CommonFormsModule, + FormattingModule, + DatasetProfileRoutingModule, + ConfirmationDialogModule, + NgxDropzoneModule, + FormProgressIndicationModule, + DatasetModule, + AngularStickyThingsModule, + DragDropModule, + MatBadgeModule, + DragulaModule, + AutoCompleteModule, + TransitionGroupModule, + RichTextEditorModule + ], declarations: [ DatasetProfileListingComponent, DatasetProfileCriteriaComponent, @@ -88,6 +91,7 @@ import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.mod DatasetProfileEditorFreeTextFieldComponent, DatasetProfileEditorRadioBoxFieldComponent, DatasetProfileEditorTextAreaFieldComponent, + DatasetProfileEditorRichTextAreaFieldComponent, DatasetProfileEditorDatePickerFieldComponent, DatasetProfileEditorWordListFieldComponent, DatasetProfileEditorDefaultValueComponent, 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 4fb6f9404..1bf042f25 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 @@ -5,6 +5,7 @@ import { DatasetProfileListingComponent } from './listing/dataset-profile-listin 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'; +import { CanDeactivateGuard } from '@app/library/deactivate/can-deactivate.guard'; const routes: Routes = [ { @@ -16,7 +17,8 @@ const routes: Routes = [ permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] } }, - canActivate: [SpecialAuthGuard] + canActivate: [SpecialAuthGuard], + canDeactivate:[CanDeactivateGuard] }, { path: ':id', @@ -27,7 +29,8 @@ const routes: Routes = [ permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] } }, - canActivate: [SpecialAuthGuard] + canActivate: [SpecialAuthGuard], + canDeactivate:[CanDeactivateGuard] }, { path: 'clone/:cloneid', @@ -38,7 +41,8 @@ const routes: Routes = [ permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] } }, - canActivate: [SpecialAuthGuard] + canActivate: [SpecialAuthGuard], + canDeactivate:[CanDeactivateGuard] }, { path: 'newversion/:newversionid', @@ -49,7 +53,8 @@ const routes: Routes = [ permissions: [AppRole.Admin, AppRole.DatasetTemplateEditor] } }, - canActivate: [SpecialAuthGuard] + canActivate: [SpecialAuthGuard], + canDeactivate:[CanDeactivateGuard] }, { path: 'versions/:groupId', 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 index c65851e63..5b4cd484a 100644 --- 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 @@ -78,6 +78,13 @@ export const GENERAL_ANIMATIONS = [ 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)'}))]) - ]) + ]), + trigger('fade-in',[ + transition(":enter", [style({opacity:0}), animate('1000ms 800ms ease', style({'opacity':1}))]), + ]), + trigger('fade-in-fast',[ + transition(":enter", [style({opacity:0}), animate('800ms 100ms ease', style({'opacity':1}))]), + ]), + ] \ 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 2f205b4ef..e56e4ce9f 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,4 +1,3 @@ - - -
- -
- -
- - - - - - - - +
+
+ + + + + + + +
+
    +
  • + keyboard_arrow_up +
  • +
  • + keyboard_arrow_down +
  • +
-
- - - +
+
+
- {{'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.PREVIEW' | translate}} + {{'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.PREVIEW' | translate}} {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.STATUS.PREVIEW-UPDATED' | translate}} @@ -193,18 +191,18 @@
-
+
- +
{{'DATASET-PROFILE-EDITOR.ACTIONS.FIELD.NOT-INITIALIZED' | translate}}
- +

@@ -219,7 +217,7 @@ - +
  • @@ -228,7 +226,7 @@ - + - + + - + - + - + - + - - + + @@ -381,7 +385,7 @@ {{'DATASET-PROFILE-EDITOR.ACTIONS.FIELDSET.COMMENT-FIELD' | translate}}
  • - + {{'DATASET-PROFILE-EDITOR.ACTIONS.FIELDSET.MULTIPLICITY' | translate}}
  • @@ -390,9 +394,9 @@ 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}} + {{'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}} @@ -400,21 +404,21 @@
    - +
    - - - 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 bae52298c..a53d303c5 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 @@ -53,7 +53,7 @@ $blue-color-light: #5cf7f2; .field-id-container-icon{ position: absolute; top: -50%; - + } } @@ -64,7 +64,7 @@ $blue-color-light: #5cf7f2; .fielset-header{ font-size: 1.5em; font-weight: bold; - + // .numbering{ // padding: 0.5em 0em; // } @@ -84,7 +84,7 @@ $blue-color-light: #5cf7f2; color: #212121; } -:host ::ng-deep .fieldset-checkbox-action-dataset-profile-editor +:host ::ng-deep .fieldset-checkbox-action-dataset-profile-editor { .mat-checkbox-label{ font-size: 0.8em; @@ -129,6 +129,18 @@ $blue-color-light: #5cf7f2; 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 +.previewer-text{ + font-weight: bold; + font-style: italic; +} + +.field-input { + position: relative; +} + +.field-input .arrows { + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); +} 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 4e3762bec..84724ca99 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,28 +1,56 @@ -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 {Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild} from '@angular/core'; +import {AbstractControl, 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, + 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/dialog'; +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, + RichTextAreaFieldData, + 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'; +import {Subject} from 'rxjs'; +import {debounceTime, delay, map, takeUntil, tap} from 'rxjs/operators'; +import {GENERAL_ANIMATIONS} from '../../animations/animations'; +import {BaseComponent} from '@common/base/base.component'; +import {TransitionGroupComponent} from "@app/ui/transition-group/transition-group.component"; @Component({ selector: 'app-dataset-profile-editor-composite-field-component', @@ -30,7 +58,7 @@ import { GENERAL_ANIMATIONS } from '../../animations/animations'; styleUrls: ['./dataset-profile-editor-composite-field.component.scss'], animations:[GENERAL_ANIMATIONS] }) -export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnChanges { +export class DatasetProfileEditorCompositeFieldComponent extends BaseComponent implements OnInit, OnChanges { @Input() form: FormGroup; @Input() indexPath: string; @@ -38,7 +66,8 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh @Input() numbering: string; @Input() hasFocus: boolean = false; - + @ViewChild("inputs") inputs: TransitionGroupComponent; + showPreview: boolean = true; previewDirty: boolean = false; @@ -56,17 +85,31 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh private myCustomValidators:EditorCustomValidators = new EditorCustomValidators(); + isMultiplicityEnabled = false; constructor( private dialog: MatDialog, private language: TranslateService, public enumUtils: EnumUtils, public datasetProfileService: DatasetProfileService - ) { } + ) { + super(); + } - ngOnChanges(){ + ngOnChanges(changes: SimpleChanges){ // this.setTargetField(null); // this.showExtendedDescription = !!this.form.get('extendedDescription').value; // this.showAdditionalInfo = !!this.form.get('additionalInformation').value; + // console.log(this.form.get('fields')['controls']) + if( changes['form']){ + + try{ + const multiplicity = this.form.get('multiplicity').value; + this.isMultiplicityEnabled = multiplicity.min > 0 || multiplicity.max >0; + } catch{ + this.isMultiplicityEnabled = false; + } + } + } get firstField(){ @@ -77,17 +120,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } } - 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; - } + ngOnInit() { @@ -112,16 +145,52 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh this.showExtendedDescription = !!this.form.get('extendedDescription').value; this.showAdditionalInfo = !!this.form.get('additionalInformation').value; - this.form.valueChanges.subscribe(changes=>{ + this.form.valueChanges.pipe(takeUntil(this._destroyed)).subscribe(changes=>{ // this.previewForm = null; this.previewDirty = true; this.generatePreviewForm(); - + + }); + this.previewSubject$ + .pipe(debounceTime(600)) + .pipe( + takeUntil(this._destroyed), + map(model => model.buildForm()), + map(updatedForm =>{ + 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; + return previewContainer; + }), + delay(100), + tap( previewContainer =>{ + this.showPreview = true; + this.previewDirty = false; + }), + delay(100) + ) + .subscribe(previewContainer=>{ + + if(previewContainer){ + previewContainer.style.height = 'auto'; + } + + // const updatedForm = model.buildForm(); + // this.reloadPreview(updatedForm) }); - this.previewSubject$.pipe(debounceTime(600)).subscribe(model=>{ - const updatedForm = model.buildForm(); - this.reloadPreview(updatedForm) - }) this.generatePreviewForm(); @@ -134,7 +203,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } private reloadPreview(updatedForm: FormGroup){ setTimeout(() => { - + const previewContainer = document.getElementById('preview_container'+ this.form.get('id').value); // let clientHeight = -1; if(previewContainer){ @@ -154,7 +223,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh setTimeout(() => { - + this.showPreview = true; this.previewDirty = false; @@ -165,7 +234,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } }); } - }); + }); }); } @@ -174,7 +243,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh private generatePreviewForm(){ const formValue:FieldSet = this.form.getRawValue(); const fields:FieldDefinition[] = formValue.fields.map(editorField=>this._fieldToFieldDefinition(editorField)); - + const compositeField: CompositeField = { id: formValue.id, @@ -195,7 +264,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } return model; }) - } + } const section = new DatasetDescriptionSectionEditorModel(); @@ -241,7 +310,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh // (this.form.get('fields') as FormArray).controls.forEach(field=>{ // const fieldEditorModel = new DatasetDescriptionFieldEditorModel(); - + // fieldEditorModel.viewStyle= { // renderStyle: field.get('viewStyle').get('renderStyle').value, // cssClass: null @@ -249,15 +318,15 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh // fieldEditorModel.defaultValue = field.get('defaultValue').value; // switch (field.get('viewStyle').get('renderStyle').value) { // case DatasetProfileFieldViewStyle.TextArea: - // fieldEditorModel.data = { + // fieldEditorModel.data = { // label: field.get('data').get('label').value // }; // break; - + // default: // break; // } - + // editorModel.fields.push(fieldEditorModel); // }); @@ -280,7 +349,6 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } onIsMultiplicityEnabledChange(isMultiplicityEnabled: MatCheckboxChange) { - const multiplicity = this.form.get('multiplicity') as FormGroup; const minControl = multiplicity.get('min'); @@ -294,9 +362,10 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh maxControl.setValue(0); } + this.isMultiplicityEnabled = isMultiplicityEnabled.checked; minControl.updateValueAndValidity(); maxControl.updateValueAndValidity(); - + } addNewField() { @@ -307,11 +376,11 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh 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(); } @@ -320,12 +389,13 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh const fieldsForm = this.form.get('fields'); fieldsForm.removeAt(index); - + this.inputs.init(); // calculate ordinals fieldsForm.controls.forEach((field, idx)=>{ field.get('ordinal').setValue(idx); field.updateValueAndValidity(); - }) + }); + this.form.markAsDirty();//deactivate guard } getFieldTile(formGroup: FormGroup, index: number) { @@ -343,7 +413,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh (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; @@ -359,12 +429,12 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh 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{ + // }else{ // //SET REQUIRED VALIDATOR // console.log('setting required validator'); // validations.push(ValidationType.Required); @@ -372,17 +442,13 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh // validationsControl.updateValueAndValidity(); // } } - setTargetField(field:FormGroup){ - this.targetField = field; + setTargetField(field:AbstractControl){ + this.targetField = field; } - - - - deleteTargetField(){ - + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { restoreFocus: false, data: { @@ -397,7 +463,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh this._deleteTargetField(); } }); - + } @@ -424,7 +490,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } deleteField(index: number){ - + const dialogRef = this.dialog.open(ConfirmationDialogComponent, { restoreFocus: false, data: { @@ -439,21 +505,21 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh 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{ - + } @@ -476,8 +542,8 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh // const fieldForm = field.buildForm(); // fieldForm.setValidators(this.customFieldValidator()); // fieldForm.get('viewStyle').get('renderStyle').setValidators(Validators.required); - - + + // if (fieldForm.get('data')) { // fieldForm.removeControl('data'); @@ -499,7 +565,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.CheckBox:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.CheckBox) // fieldForm.addControl('data', new CheckBoxFieldDataEditorModel().buildForm()); const data: CheckBoxFieldData = { @@ -508,12 +574,12 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh 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()); @@ -536,7 +602,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.Other:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ComboBox) // fieldForm.addControl('data', new AutoCompleteFieldDataEditorModel().buildForm()); //TODO SEE @@ -556,10 +622,10 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh 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, @@ -574,7 +640,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.FreeText:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.FreeText) // fieldForm.addControl('data', new FreeTextFieldDataEditorModel().buildForm()); @@ -588,7 +654,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } 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')); @@ -605,7 +671,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.TextArea:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.TextArea) // fieldForm.addControl('data', new TextAreaFieldDataEditorModel().buildForm()); @@ -618,9 +684,24 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh break; } + case this.viewTypeEnum.RichTextArea:{ + + + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.TextArea) + // fieldForm.addControl('data', new TextAreaFieldDataEditorModel().buildForm()); + + const data: RichTextAreaFieldData = { + label:'' + } + + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.RichTextArea; + 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 = { @@ -633,7 +714,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.ExternalDatasets:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.ExternalDatasets) // fieldForm.addControl('data', new ExternalDatasetsDataEditorModel().buildForm()); const data: ExternalDatasetsFieldData = { @@ -647,7 +728,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.DataRepositories:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.DataRepositories) // fieldForm.addControl('data', new DataRepositoriesDataEditorModel().buildForm()); @@ -663,10 +744,10 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.Registries:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Registries) // fieldForm.addControl('data', new RegistriesDataEditorModel().buildForm()); - + const data:RegistriesFieldData = { label: '', multiAutoComplete: false @@ -679,7 +760,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.Services:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Services) // fieldForm.addControl('data', new ServicesDataEditorModel().buildForm()); @@ -695,7 +776,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.Tags:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Tags) // fieldForm.addControl('data', new TagsDataEditorModel().buildForm()); @@ -709,7 +790,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } 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()); @@ -724,10 +805,10 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh // field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.InternalDmpEntities; // field.data = {label:''} - + field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Researchers; field.data = data; - + @@ -735,7 +816,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } 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()) @@ -745,8 +826,8 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh autoCompleteSingleDataList:[], label:'', multiAutoComplete: false, - - } as OrganizationsFieldData; //TODO + + } as OrganizationsFieldData; //TODO field.viewStyle.renderStyle = DatasetProfileFieldViewStyle.Organizations; field.data = data; @@ -755,7 +836,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.DatasetIdentifier:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.DatasetIdentifier) // fieldForm.addControl('data', new DatasetIdentifierDataEditorModel().buildForm()); @@ -765,12 +846,12 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh 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()); @@ -784,7 +865,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } case this.viewTypeEnum.Validation:{ - + // fieldForm.get('viewStyle').get('renderStyle').setValue(DatasetProfileFieldViewStyle.Validation) // fieldForm.addControl('data', new ValidationDataEditorModel().buildForm()); @@ -800,9 +881,10 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh } (this.form.get('fields')).push(new FieldEditorModel().fromModel(field).buildForm()); + this.inputs.init(); // fieldForm.get('viewStyle').get('renderStyle').updateValueAndValidity(); // fieldForm.get('data').updateValueAndValidity(); - + } @@ -856,7 +938,7 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh // private _atLeastOneElementListValidator(arrayToCheck): ValidatorFn{ // return (control: AbstractControl): ValidationErrors | null=>{ - + // const fa = control.get(arrayToCheck) as FormArray; // if(fa.length === 0){ @@ -874,6 +956,37 @@ export class DatasetProfileEditorCompositeFieldComponent implements OnInit, OnCh return {'width':width+'em'} } + + get fieldsArray(): FormArray { + if(this.form && this.form.get('fields')) { + return this.form.get('fields') as FormArray; + } + return null; + } + + move(index, direction: "up" | "down" = "up") { + this.inputs.init(); + if (direction === "up" && this.canGoUp(index)) { + let temp = this.fieldsArray.at(index); + this.fieldsArray.removeAt(index); + this.fieldsArray.insert(index-1, temp); + } else if (direction === "down" && this.canGoDown(index)) { + let temp = this.fieldsArray.at(index+1); + this.fieldsArray.removeAt(index+1); + this.fieldsArray.insert(index, temp); + } + this.fieldsArray.controls.forEach((field, index) => { + field.get('ordinal').setValue(index); + }); + } + + canGoUp(index: number): boolean { + return index > 0; + } + + canGoDown(index: number): boolean { + return index < (this.fieldsArray.length - 1); + } } 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 31648ff9a..4a6fa65ec 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 @@ -77,6 +77,13 @@ {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + {{placeHolder}} + + {{'GENERAL.VALIDATION.REQUIRED' | 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 a5c074147..d8e932f46 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 @@ -10,4 +10,13 @@ + + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.EXTERNAL-DATASET-TYPE-NAME' | translate}} + + + {{type.label | 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.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/external-datasets/dataset-profile-editor-external-datasets-field.component.ts index 43ce6e6ae..e7d31f1d5 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/external-datasets/dataset-profile-editor-external-datasets-field.component.ts +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/external-datasets/dataset-profile-editor-external-datasets-field.component.ts @@ -2,6 +2,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { DatePickerDataEditorModel } from '../../../../admin/field-data/date-picker-data-editor-models'; import { ExternalDatasetsDataEditorModel } from '@app/ui/admin/dataset-profile/admin/field-data/external-datasets-data-editor-models'; +import { ExternalDatasetTypeEnum } from '@app/core/common/enum/external-dataset-type-enum'; @Component({ selector: 'app-dataset-profile-editor-external-datasets-field-component', @@ -13,7 +14,28 @@ export class DatasetProfileEditorExternalDatasetsFieldComponent implements OnIni @Input() form: FormGroup; private data: ExternalDatasetsDataEditorModel = new ExternalDatasetsDataEditorModel(); + externalDatasetTypes = [ + ... Object.keys(ExternalDatasetTypeEnum).map(key=>{ + return { + label: this.parseExtrernalDatasetTypeKey(key), + value: ExternalDatasetTypeEnum[key] + }; + }) + ]; ngOnInit() { if (!this.form.get('data')) { this.form.addControl('data', this.data.buildForm()); } } + + parseExtrernalDatasetTypeKey(key: string): string{ + if(ExternalDatasetTypeEnum[key] === ExternalDatasetTypeEnum.ProducedDataset){ + return 'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.EXTERNAL-DATASET-TYPES.PRODUCED'; + } + if(ExternalDatasetTypeEnum[key] === ExternalDatasetTypeEnum.ReusedDataset){ + return 'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.EXTERNAL-DATASET-TYPES.REUSED'; + } + if(ExternalDatasetTypeEnum[key] === ExternalDatasetTypeEnum.Other){ + return 'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.EXTERNAL-DATASET-TYPES.OTHER'; + } + return key; + } } diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/rich-textarea/dataset-profile-editor-rich-text-area-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/rich-textarea/dataset-profile-editor-rich-text-area-field.component.html new file mode 100644 index 000000000..05e06a486 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/rich-textarea/dataset-profile-editor-rich-text-area-field.component.html @@ -0,0 +1,13 @@ +
    + +
    {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-RICH-TEXT-AREA-TITLE' + | translate}}
    + + + + {{'DATASET-PROFILE-EDITOR.STEPS.FORM.FIELD.FIELDS.FIELD-RICH-TEXT-AREA-PLACEHOLDER' | translate}} + + + +
    diff --git a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/rich-textarea/dataset-profile-editor-rich-text-area-field.component.scss b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/rich-textarea/dataset-profile-editor-rich-text-area-field.component.scss new file mode 100644 index 000000000..8da39919b --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/rich-textarea/dataset-profile-editor-rich-text-area-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/rich-textarea/dataset-profile-editor-rich-text-area-field.component.ts b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/rich-textarea/dataset-profile-editor-rich-text-area-field.component.ts new file mode 100644 index 000000000..8b286cfe8 --- /dev/null +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field-type/rich-textarea/dataset-profile-editor-rich-text-area-field.component.ts @@ -0,0 +1,18 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { RichTextAreaFieldDataEditorModel } from '../../../../admin/field-data/rich-text-area-field-data-editor-model'; + +@Component({ + selector: 'app-dataset-profile-editor-rich-text-area-field-component', + styleUrls: ['./dataset-profile-editor-rich-text-area-field.component.scss'], + templateUrl: './dataset-profile-editor-rich-text-area-field.component.html' +}) +export class DatasetProfileEditorRichTextAreaFieldComponent implements OnInit { + + @Input() form: FormGroup; + private data: RichTextAreaFieldDataEditorModel = new RichTextAreaFieldDataEditorModel(); + + 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/dataset-profile-editor-field.component.html b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html index 0e73c87e1..493589d62 100644 --- a/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html +++ b/dmp-frontend/src/app/ui/admin/dataset-profile/editor/components/field/dataset-profile-editor-field.component.html @@ -1,8 +1,9 @@ -
    -
    +
    + +
    • @@ -12,15 +13,13 @@ -
    • +
    • delete
    • -
    -
    - + DatePicker icon {{enumUtils.toDatasetProfileViewTypeString(viewTypeEnum.DatePicker)}} @@ -169,13 +172,13 @@ {{'GENERAL.VALIDATION.REQUIRED' | translate}} - + - + - -
    {{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-DESCRIPTION' | translate}}
    {{'DATASET-PROFILE-EDITOR.STEPS.SECTION-INFO.SECTION-DESCRIPTION-HINT' | translate}}
    - - - - +
    + + +
    - -
    -
    +
    @@ -156,16 +156,19 @@
    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}} - - +
    + + +
    + + {{'GENERAL.VALIDATION.REQUIRED'| translate}} + +
    +
    - +
    1.3 {{'DATASET-PROFILE-EDITOR.STEPS.GENERAL-INFO.DATASET-TEMPLATE-LANGUAGE'| translate}} *
    @@ -181,58 +184,62 @@
    - -
    - - + +
    + + -
    +
    @@ -290,7 +297,7 @@
    - +
    @@ -411,7 +417,6 @@ translate}}
    -
    @@ -424,7 +429,6 @@ translate}}
    -
    - + -->
    - +
    @@ -21,7 +21,7 @@
    - + @@ -34,7 +34,7 @@ {{'DATASET-PROFILE-LISTING.COLUMNS.DESCRIPTION' | translate}} - {{row.description}} + 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 index c88f0a71b..69b4f0247 100644 --- 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 @@ -1,5 +1,5 @@ -
    -

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

    +
    +

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

    - +
    +
    + keyboard_arrow_up +
    +
    +
    +
    + keyboard_arrow_down +
    +
    \ 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 index ebd142270..dc1e64f35 100644 --- 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 @@ -1,10 +1,12 @@ - +$scroller-height: 3em; .scroll-container { // overflow-y: auto; max-height: 60vh; overflow-y: scroll; padding-left: .2em; padding-right: 1em; + // padding-top: $scroller-height; + // padding-bottom: $scroller-height; } // #style-6::-webkit-scrollbar-track @@ -59,4 +61,32 @@ opacity: 0.6; font-size: 1.6em; margin-top: 0px; +} + +.table-container{ + position: relative; +} +.table-scroller{ + // background-color: #5cf7f221; + position: absolute; + width: 95%; + height: $scroller-height; + display: flex; + align-items: center; + justify-content: center; + // z-index: -9999; +} +.top-scroller{ + top: 1px; + background: rgb(255,255,255); + background: linear-gradient(0deg, rgba(255,255,255,0) 0%, rgba(92,247,242,0.4542191876750701) 100%); +} +.bottom-scroller{ + bottom: 1px; + background: rgb(255,255,255); + background: linear-gradient(180deg, rgba(255,255,255,0) 0%, rgba(92,247,242,0.4542191876750701) 100%); +} + +.opacity-0{ + opacity: 0 !important; } \ 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 index 69717e782..2705c912d 100644 --- 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 @@ -1,14 +1,14 @@ import { DOCUMENT } from '@angular/common'; -import { Component, EventEmitter, Inject, OnInit, Output, Input } from '@angular/core'; +import { Component, EventEmitter, Inject, OnInit, Output, Input, AfterViewInit } from '@angular/core'; import { BaseComponent } from '@common/base/base.component'; import { interval, Subject, Subscription } from 'rxjs'; -import { distinctUntilChanged } from 'rxjs/operators'; +import { distinctUntilChanged, filter, takeUntil } 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 { MatSnackBar, MatSnackBarConfig } from '@angular/material/snack-bar'; import { TranslateService } from '@ngx-translate/core'; import { ContentObserver } from '@angular/cdk/observers'; @@ -18,7 +18,7 @@ import { ContentObserver } from '@angular/cdk/observers'; styleUrls: ['./table-of-contents.scss'], templateUrl: './table-of-contents.html' }) -export class DatasetProfileTableOfContents extends BaseComponent implements OnInit { +export class DatasetProfileTableOfContents extends BaseComponent implements OnInit, AfterViewInit { @Input() links: ToCEntry[]; @Input() itemSelected: ToCEntry; @@ -42,6 +42,11 @@ export class DatasetProfileTableOfContents extends BaseComponent implements OnIn private VALID_DROP_TIME = 500;//ms overcontainer: string = null; + $clock = interval(10); + scrollTableTop = false; + scrollTableBottom = false; + pxToScroll = 15; + constructor( @Inject(DOCUMENT) private _document: Document, private dragulaService: DragulaService, @@ -449,6 +454,54 @@ export class DatasetProfileTableOfContents extends BaseComponent implements OnIn + } + ngAfterViewInit(): void { + + const top = document.querySelector('.top-scroller'); + const bottom = document.querySelector('.bottom-scroller'); + const tableDiv = document.querySelector('#tocentrytable'); + + try { + top.addEventListener('mouseover', (e) => {this.scrollTableTop = true; },{ + passive: true + }); + bottom.addEventListener('mouseover', (e) => {this.scrollTableBottom = true; },{ + passive: true + }); + + top.addEventListener('mouseout', (e) => {this.scrollTableTop = false},{ + passive: true + }); + bottom.addEventListener('mouseout', (e) => {this.scrollTableBottom = false;},{ + passive: true + }); + + + this.$clock + .pipe( + takeUntil(this._destroyed), + filter(() => this.scrollTableTop) + ) + .subscribe(()=>{ + try{ + tableDiv.scrollBy(0, -this.pxToScroll); + } catch {} + }); + this.$clock + .pipe( + takeUntil(this._destroyed), + filter(() => this.scrollTableBottom) + ) + .subscribe(()=>{ + try{ + tableDiv.scrollBy(0, this.pxToScroll); + } catch {} + }); + } catch { + console.log('could not find scrolling elements'); + } + + } private _scrollIntoDragginItem(id: string){ @@ -512,10 +565,6 @@ export class DatasetProfileTableOfContents extends BaseComponent implements OnIn ngOnInit(): void { - } - - ngOnChanges(changes: SimpleChanges) { - } diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/dmp-profile.module.ts b/dmp-frontend/src/app/ui/admin/dmp-profile/dmp-profile.module.ts index 4e4e79439..5059a98b0 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/dmp-profile.module.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/dmp-profile.module.ts @@ -9,6 +9,7 @@ import { DmpProfileExternalAutocompleteFieldEditorComponent } from './editor/ext import { DialodConfirmationUploadDmpProfiles } from './listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component'; import { DmpProfileCriteriaComponent } from './listing/criteria/dmp-profile-criteria.component'; import { DmpProfileListingComponent } from './listing/dmp-profile-listing.component'; +import { NgxDropzoneModule } from "ngx-dropzone"; @NgModule({ imports: [ @@ -16,7 +17,8 @@ import { DmpProfileListingComponent } from './listing/dmp-profile-listing.compon CommonFormsModule, UrlListingModule, ConfirmationDialogModule, - DmpProfileRoutingModule + DmpProfileRoutingModule, + NgxDropzoneModule ], declarations: [ DmpProfileEditorComponent, diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.html b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.html index 1ff4715bd..e0bc6fda9 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.html +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/editor/dmp-profile-editor.component.html @@ -1,9 +1,28 @@
    +
    +
    +

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

    +

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

    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    -

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

    -

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

    - + -
    - +
    + @@ -101,12 +120,12 @@
    -
    +
    - +
    -
    + + +
    +
    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 972e19d7b..ae188ac09 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,5 +1,7 @@ .dmp-profile-editor { margin-top: 1.3rem; + margin-left: 1em; + margin-right: 3em; .centered-row-item { align-items: center; @@ -25,3 +27,37 @@ ::ng-deep .mat-checkbox-disabled.mat-checkbox-checked .mat-checkbox-background, .mat-checkbox-disabled.mat-checkbox-indeterminate .mat-checkbox-background { background-color: #b0b0b0; } + +.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; + &:disabled{ + background-color: #CBCBCB; + color: #FFF; + border: 0px; + } +} + +.action-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; + &:disabled{ + background-color: #CBCBCB; + color: #FFF; + border: 0px; + } +} \ No newline at end of file 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 90cc566e0..5cd76ceda 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 @@ -23,6 +23,8 @@ 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'; +import { MatDialog } from '@angular/material/dialog'; +import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; @Component({ @@ -50,7 +52,8 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie private formService: FormService, private configurationService: ConfigurationService, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private dialog: MatDialog ) { super(); this.host = configurationService.server; @@ -177,13 +180,27 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie } delete() { - this.formGroup.get('status').setValue(DmpProfileStatus.Deleted); - this.dmpProfileService.createDmp(this.formGroup.value) - .pipe(takeUntil(this._destroyed)) - .subscribe( - complete => this.onCallbackSuccess(), - error => this.onCallbackError(error) - ); + this.dialog.open(ConfirmationDialogComponent,{data:{ + isDeleteConfirmation: true, + confirmButton: this.language.instant('DMP-PROFILE-EDITOR.CONFIRM-DELETE-DIALOG.CONFIRM-BUTTON'), + cancelButton: this.language.instant("DMP-PROFILE-EDITOR.CONFIRM-DELETE-DIALOG.CANCEL-BUTTON"), + message: this.language.instant("DMP-PROFILE-EDITOR.CONFIRM-DELETE-DIALOG.MESSAGE") + }}) + .afterClosed() + .subscribe( + confirmed =>{ + if(confirmed){ + this.formGroup.get('status').setValue(DmpProfileStatus.Deleted); + this.dmpProfileService.createDmp(this.formGroup.value) + .pipe(takeUntil(this._destroyed)) + .subscribe( + complete => this.onCallbackSuccess(), + error => this.onCallbackError(error) + ); + } + } + ) + } finalize() { diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.html b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.html index 83c70604d..c07d4eca4 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.html +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.html @@ -4,10 +4,26 @@

    {{ data.message }}

    - -
    +
    +
    +
    + + + {{ selectedFileName }} + + +
    +
    +
    +
    + +
    @@ -15,11 +31,11 @@
    - +
    - +
    diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.scss b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.scss index 226db6ce8..63c42f81e 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.scss +++ b/dmp-frontend/src/app/ui/admin/dmp-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/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.ts b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.ts index 42a7dfa61..c8081101f 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component.ts @@ -10,9 +10,9 @@ import { Inject, Component } from '@angular/core'; export class DialodConfirmationUploadDmpProfiles { sizeError = false; - btnColore:String="primary"; selectFile =false; maxFileSize: number = 1048576; + selectedFileName: string; constructor( public dialogRef: MatDialogRef, @@ -21,16 +21,20 @@ export class DialodConfirmationUploadDmpProfiles { 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; } @@ -49,5 +53,10 @@ export class DialodConfirmationUploadDmpProfiles { hasProfile():boolean{ 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/dmp-profile/listing/criteria/dmp-profile-criteria.component.html b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.html index 5ed6cc96b..34dd7626d 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.html +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.html @@ -1,18 +1,11 @@
    - -
    - - - - - -
    -
    - - +
    +
    + + + search +
    -
    diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.scss b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.scss index e69de29bb..7389ee36f 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.scss +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.scss @@ -0,0 +1,13 @@ + +:host ::ng-deep .search-form-field .mat-form-field-wrapper { + background-color: white !important; + padding-bottom: 0 !important; +} +:host ::ng-deep .search-container .mat-form-field-appearance-outline .mat-form-field-infix { + padding: 0.3rem 0rem 0.6rem 0rem !important; +} + +.dmp-criteria{ + margin-top: 3em; + margin-bottom: 0em; +} \ No newline at end of file diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.ts b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.ts index bac44b696..9e7cc3b8c 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.ts +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/criteria/dmp-profile-criteria.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { GrantListingModel } from '@app/core/model/grant/grant-listing'; import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; import { DmpProfileCriteria } from '@app/core/query/dmp/dmp-profile-criteria'; @@ -53,24 +53,24 @@ export class DmpProfileCriteriaComponent extends BaseCriteriaComponent implement } - openDialog(): void { - const dialogRef = this.dialog.open(DialodConfirmationUploadDmpProfiles, { - restoreFocus: false, - data: { - message: this.language.instant('DMP-PROFILE-LISTING.UPLOAD.UPLOAD-XML-FILE-TITLE'), - confirmButton: this.language.instant('DMP-PROFILE-LISTING.UPLOAD.UPLOAD-XML'), - cancelButton: this.language.instant('DMP-PROFILE-LISTING.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.dmpProfileService.uploadFile(data.file, data.name) - .pipe(takeUntil(this._destroyed)) - .subscribe(); - } - }); - } + // openDialog(): void { + // const dialogRef = this.dialog.open(DialodConfirmationUploadDmpProfiles, { + // restoreFocus: false, + // data: { + // message: this.language.instant('DMP-PROFILE-LISTING.UPLOAD.UPLOAD-XML-FILE-TITLE'), + // confirmButton: this.language.instant('DMP-PROFILE-LISTING.UPLOAD.UPLOAD-XML'), + // cancelButton: this.language.instant('DMP-PROFILE-LISTING.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.dmpProfileService.uploadFile(data.file, data.name) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(); + // } + // }); + // } } diff --git a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.html b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.html index 515549df8..733ea2248 100644 --- a/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.html +++ b/dmp-frontend/src/app/ui/admin/dmp-profile/listing/dmp-profile-listing.component.html @@ -1,9 +1,26 @@
    -

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

    +
    +
    +

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

    +
    +
    +
    + + +
    +
    - +
    @@ -17,7 +34,11 @@ {{'DMP-PROFILE-LISTING.COLUMNS.STATUS' | translate}} - {{row.status}} + +
    + {{parseStatus(row.status) | translate}} +
    +
    @@ -36,12 +57,6 @@
    - - -
    -
    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 7720847f5..e797578ad 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 @@ -1,65 +1,77 @@ .mat-table { - margin: 24px; + margin-top: 47px; + border-radius: 4px; } - .dmp-profile-listing { margin-top: 1.3rem; + margin-left: 1rem; + margin-right: 2rem; + .mat-header-row{ + background: #f3f5f8; + } + .mat-card { + margin: 16px 0; + padding: 0px; + } + .mat-row { cursor: pointer; + min-height: 4.5em; } - - .mat-card { - margin: 1em 0; - } - + mat-row:hover { - background-color: lightgray; + background-color: #eef5f6; } - - mat-row:nth-child(odd) { - background-color: #0c748914; - // background-color: #eef0fb; - } - .mat-fab-bottom-right { - // top: auto !important; - // right: 20px !important; - // bottom: 10px !important; - // left: auto !important; - // position: fixed !important; float: right; z-index: 5; } } - +// PAGINATOR :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; +} +.create-btn { + border-radius: 30px; + background-color: #f7dd72; + padding-left: 2em; + padding-right: 2em; + // color: #000; + + .button-text{ + display: inline-block; + } } -:host ::ng-deep .mat-paginator-page-size { - height: 43px; +.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; } -:host ::ng-deep .mat-paginator-range-label { - margin: 15px 32px 0 24px !important; +.status-chip{ + border-radius: 20px; + padding-left: 1em; + padding-right: 1em; + padding-top: 0.2em; + font-size: .8em; } -:host ::ng-deep .mat-paginator-range-actions { - width: 55% !important; - min-height: 43px !important; - justify-content: space-between; +.status-chip-finalized{ + color: #568b5a; + background: #9dd1a1 0% 0% no-repeat padding-box; } -:host ::ng-deep .mat-paginator-navigation-previous { - margin-left: auto !important; -} - -:host ::ng-deep .mat-icon-button { - height: 30px !important; - font-size: 12px !important; -} +.status-chip-draft{ + color: #00c4ff; + background: #d3f5ff 0% 0% no-repeat padding-box; +} \ No newline at end of file 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 2bcd93061..9ffd48074 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 @@ -2,6 +2,7 @@ import { DataSource } from '@angular/cdk/table'; import { HttpClient } 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 { MatSnackBar } from '@angular/material/snack-bar'; import { MatSort } from '@angular/material/sort'; @@ -11,12 +12,15 @@ import { DmpProfileListing } from '@app/core/model/dmp-profile/dmp-profile-listi 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 { UiNotificationService } from '@app/core/services/notification/ui-notification-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'; +import { SnackBarNotificationLevel } from '@common/modules/notification/ui-notification-service'; import { TranslateService } from '@ngx-translate/core'; import { merge as observableMerge, Observable, of as observableOf } from 'rxjs'; import { map, startWith, switchMap, takeUntil } from 'rxjs/operators'; +import { DialodConfirmationUploadDmpProfiles } from './criteria/dialog-confirmation-upload-profile/dialog-confirmation-upload-profiles.component'; @Component({ @@ -38,8 +42,8 @@ export class DmpProfileListingComponent extends BaseComponent implements OnInit breadCrumbs: Observable; statuses = [ - { value: '0', viewValue: 'Active' }, - { value: '1', viewValue: 'Inactive' } + { value: '0', viewValue: 'DMP-PROFILE-LISTING.STATUS.DRAFT' },// active + { value: '1', viewValue: 'DMP-PROFILE-LISTING.STATUS.FINALIZED' }// inactive ]; constructor( @@ -49,7 +53,9 @@ export class DmpProfileListingComponent extends BaseComponent implements OnInit public route: ActivatedRoute, public dmpProfileService: DmpProfileService, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private dialog: MatDialog, + private uiNotificationService: UiNotificationService, ) { super(); } @@ -88,6 +94,52 @@ export class DmpProfileListingComponent extends BaseComponent implements OnInit // debugger; // this.datasetService.makeDatasetPublic(id).pipe(takeUntil(this._destroyed)).subscribe(); // } + + parseStatus(value: number): string{ + const stringVal = value.toString() + try{ + return this.statuses.find(status => status.value === stringVal).viewValue; + }catch{ + return stringVal; + } + } + + openDialog(): void { + const dialogRef = this.dialog.open(DialodConfirmationUploadDmpProfiles, { + restoreFocus: false, + data: { + message: this.languageService.instant('DMP-PROFILE-LISTING.UPLOAD.UPLOAD-XML-FILE-TITLE'), + confirmButton: this.languageService.instant('DMP-PROFILE-LISTING.UPLOAD.UPLOAD-XML'), + cancelButton: this.languageService.instant('DMP-PROFILE-LISTING.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.dmpProfileService.uploadFile(data.file, data.name) + .pipe(takeUntil(this._destroyed)) + .subscribe(_=>{ + this.uiNotificationService.snackBarNotification(this.languageService.instant('DMP-PROFILE-LISTING.MESSAGES.TEMPLATE-UPLOAD-SUCCESS'), SnackBarNotificationLevel.Success); + this.refresh(); + }, + error=>{ + this.uiNotificationService.snackBarNotification(error.message, SnackBarNotificationLevel.Error); + }); + } + }); + } + getStatusClass(status: number):string{ + + if(status === 1){//finalized + return 'status-chip-finalized' + } + if(status === 0){ + return 'status-chip-draft'; + } + return ''; + } } export class DatasetDataSource extends DataSource { 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 a29097e1d..65415f579 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,7 +1,7 @@ 
    -
    +
    {{getPrincipalAppRoleWithLanguage(role)}} @@ -11,7 +11,7 @@ - {{getPrincipalAppRoleWithLanguage(role)}} + {{getPrincipalAppRoleWithLanguage(role)}} {{'GENERAL.VALIDATION.REQUIRED' | translate}} 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 f756eb622..8fb1377a5 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 @@ -19,7 +19,7 @@ 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'; +import { MatTableDataSource } from '@angular/material/table'; export class UsersDataSource extends DataSource { diff --git a/dmp-frontend/src/app/ui/auth/login/login.component.ts b/dmp-frontend/src/app/ui/auth/login/login.component.ts index 4050b12c0..c001dac0c 100644 --- a/dmp-frontend/src/app/ui/auth/login/login.component.ts +++ b/dmp-frontend/src/app/ui/auth/login/login.component.ts @@ -54,10 +54,13 @@ export class LoginComponent extends BaseComponent implements OnInit, AfterViewIn private mergeLoginService: MergeLoginService, private oauth2DialogService: Oauth2DialogService, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, ) { super(); } ngOnInit(): void { + if(this.authService.current()){ + this.router.navigate(['home']); + } this.matomoService.trackPageView('loginPage'); this.route.queryParams .pipe(takeUntil(this._destroyed)) diff --git a/dmp-frontend/src/app/ui/dashboard/dashboard.component.html b/dmp-frontend/src/app/ui/dashboard/dashboard.component.html index ed8022f1a..61b9e8242 100644 --- a/dmp-frontend/src/app/ui/dashboard/dashboard.component.html +++ b/dmp-frontend/src/app/ui/dashboard/dashboard.component.html @@ -13,7 +13,7 @@ {{'DASHBOARD.NEW-QUESTION' | translate}} {{'DASHBOARD.OPEN-AIR-GUIDE' | translate}} {{'DASHBOARD.LEARN-MORE' | translate}}

    {{'DASHBOARD.DMP-ABOUT-BEG' | translate}} - {{'DASHBOARD.DATASET-DESCRIPTIONS' | translate}} + {{'DASHBOARD.DATASET-DESCRIPTIONS-DASHBOARD-TEXT' | translate}} {{'DASHBOARD.DMP-ABOUT-END' | translate}}

    diff --git a/dmp-frontend/src/app/ui/dashboard/dashboard.component.ts b/dmp-frontend/src/app/ui/dashboard/dashboard.component.ts index 35702bdbe..a6441763f 100644 --- a/dmp-frontend/src/app/ui/dashboard/dashboard.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/dashboard.component.ts @@ -25,7 +25,7 @@ import { BreadcrumbItem } from '../misc/breadcrumb/definition/breadcrumb-item'; import { IBreadCrumbComponent } from '../misc/breadcrumb/definition/IBreadCrumbComponent'; import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; import { DatasetCriteria } from '@app/core/query/dataset/dataset-criteria'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { StartNewDmpDialogComponent } from '../dmp/start-new-dmp-dialogue/start-new-dmp-dialog.component'; import { StartNewDatasetDialogComponent } from '../dmp/start-new-dataset-dialogue/start-new-dataset-dialog.component'; import { DatasetWizardEditorModel } from '../dataset/dataset-wizard/dataset-wizard-editor.model'; diff --git a/dmp-frontend/src/app/ui/dashboard/dataset-info-counter/dataset-info-counter.component.spec.ts b/dmp-frontend/src/app/ui/dashboard/dataset-info-counter/dataset-info-counter.component.spec.ts index 03b79759a..93f146d49 100644 --- a/dmp-frontend/src/app/ui/dashboard/dataset-info-counter/dataset-info-counter.component.spec.ts +++ b/dmp-frontend/src/app/ui/dashboard/dataset-info-counter/dataset-info-counter.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { DatasetInfoCounterComponent } from './dataset-info-counter.component'; @@ -6,7 +6,7 @@ describe('DatasetInfoCounterComponent', () => { let component: DatasetInfoCounterComponent; let fixture: ComponentFixture; - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ DatasetInfoCounterComponent ] }) diff --git a/dmp-frontend/src/app/ui/dashboard/drafts/drafts.component.ts b/dmp-frontend/src/app/ui/dashboard/drafts/drafts.component.ts index ecbf545bd..2cd48c20e 100644 --- a/dmp-frontend/src/app/ui/dashboard/drafts/drafts.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/drafts/drafts.component.ts @@ -14,7 +14,7 @@ import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog import { DatasetCopyDialogueComponent } from '@app/ui/dataset/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; import { FormControl, FormBuilder } from '@angular/forms'; import { BaseComponent } from '@common/base/base.component'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; import { SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service'; import * as FileSaver from 'file-saver'; @@ -78,7 +78,9 @@ export class DraftsComponent extends BaseComponent implements OnInit { const dmpDataTableRequest: DataTableRequest = new DataTableRequest(0, 5, { fields: fields }); dmpDataTableRequest.criteria = new DatasetCriteria(); dmpDataTableRequest.criteria.status = DmpStatus.Draft; - this.datasetService.getPaged(dmpDataTableRequest).subscribe(response => { + this.datasetService.getPaged(dmpDataTableRequest) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { this.datasetDrafts = response.data; this.totalCount = response.totalCount; this.totalCountDraftDatasets.emit(this.datasetDrafts.length); @@ -314,7 +316,9 @@ export class DraftsComponent extends BaseComponent implements OnInit { dmpDataTableRequest.criteria = new DatasetCriteria(); dmpDataTableRequest.criteria.status = DmpStatus.Draft; dmpDataTableRequest.criteria.like = this.formGroup.get("like").value; - this.datasetService.getPaged(dmpDataTableRequest).subscribe(response => { + this.datasetService.getPaged(dmpDataTableRequest) + .pipe(takeUntil(this._destroyed)) + .subscribe(response => { this.datasetDrafts = response.data; this.totalCount = response.totalCount; this.totalCountDraftDatasets.emit(this.datasetDrafts.length); diff --git a/dmp-frontend/src/app/ui/dashboard/recent-edited-activity/recent-edited-activity.component.ts b/dmp-frontend/src/app/ui/dashboard/recent-edited-activity/recent-edited-activity.component.ts index 84eac2e7c..a26980193 100644 --- a/dmp-frontend/src/app/ui/dashboard/recent-edited-activity/recent-edited-activity.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/recent-edited-activity/recent-edited-activity.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Output, EventEmitter } from '@angular/core'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; import { RecentActivityType } from '@app/core/common/enum/recent-activity-type'; import { Principal } from '@app/core/model/auth/principal'; @@ -100,6 +100,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn allDataTableRequest.criteria.order = this.formGroup.get('order').value; this.dashboardService .getRecentActivity(allDataTableRequest) + .pipe(takeUntil(this._destroyed)) .subscribe(response => { this.allRecentActivities = response; this.allRecentActivities.forEach(recentActivity => { @@ -123,6 +124,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn const allDataTableRequest = this.setPublicDataTableRequest(); this.dashboardService .getRecentActivity(allDataTableRequest) + .pipe(takeUntil(this._destroyed)) .subscribe(response => { this.allRecentActivities = response; this.allRecentActivities.forEach(recentActivity => { @@ -619,6 +621,7 @@ export class RecentEditedActivityComponent extends BaseComponent implements OnIn allDataTableRequest.criteria.order = this.formGroup.get("order").value; this.dashboardService .getRecentActivity(allDataTableRequest) + .pipe(takeUntil(this._destroyed)) .subscribe(response => { this.allRecentActivities = response; this.allRecentActivities.forEach(recentActivity => { diff --git a/dmp-frontend/src/app/ui/dashboard/recent-edited-dataset-activity/recent-edited-dataset-activity.component.ts b/dmp-frontend/src/app/ui/dashboard/recent-edited-dataset-activity/recent-edited-dataset-activity.component.ts index 0955ec5c3..8cd7837fb 100644 --- a/dmp-frontend/src/app/ui/dashboard/recent-edited-dataset-activity/recent-edited-dataset-activity.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/recent-edited-dataset-activity/recent-edited-dataset-activity.component.ts @@ -10,7 +10,7 @@ import { TranslateService } from '@ngx-translate/core'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; import { FormControl, FormBuilder } from '@angular/forms'; import { DatasetCopyDialogueComponent } from '@app/ui/dataset/dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { debounceTime, takeUntil } from 'rxjs/operators'; import { Router } from '@angular/router'; import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; @@ -77,6 +77,7 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen datasetDataTableRequest.criteria.like = ""; this.datasetService .getPaged(datasetDataTableRequest) + .pipe(takeUntil(this._destroyed)) .subscribe(response => { this.datasetActivities = response.data; this.totalCount = response.totalCount; @@ -130,6 +131,7 @@ export class RecentEditedDatasetActivityComponent extends BaseComponent implemen } this.datasetService .getPaged(datasetDataTableRequest) + .pipe(takeUntil(this._destroyed)) .subscribe(response => { this.datasetActivities = response.data; this.totalCount = response.totalCount; diff --git a/dmp-frontend/src/app/ui/dashboard/recent-edited-dmp-activity/recent-edited-dmp-activity.component.ts b/dmp-frontend/src/app/ui/dashboard/recent-edited-dmp-activity/recent-edited-dmp-activity.component.ts index e0492ccd8..42f1c507e 100644 --- a/dmp-frontend/src/app/ui/dashboard/recent-edited-dmp-activity/recent-edited-dmp-activity.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/recent-edited-dmp-activity/recent-edited-dmp-activity.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Output, EventEmitter } from '@angular/core'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; import { RecentActivityType } from '@app/core/common/enum/recent-activity-type'; import { Principal } from '@app/core/model/auth/principal'; @@ -90,6 +90,7 @@ export class RecentEditedDmpActivityComponent extends BaseComponent implements O dmpDataTableRequest.criteria.like = ""; this.dmpService .getPaged(dmpDataTableRequest, "listing") + .pipe(takeUntil(this._destroyed)) .subscribe(response => { this.dmpActivities = response.data; this.totalCount = response.totalCount; diff --git a/dmp-frontend/src/app/ui/dashboard/recent-visited-activity/recent-visited-activity.component.ts b/dmp-frontend/src/app/ui/dashboard/recent-visited-activity/recent-visited-activity.component.ts index 6cb7c8111..3edf9340e 100644 --- a/dmp-frontend/src/app/ui/dashboard/recent-visited-activity/recent-visited-activity.component.ts +++ b/dmp-frontend/src/app/ui/dashboard/recent-visited-activity/recent-visited-activity.component.ts @@ -8,6 +8,7 @@ import { AuthService } from '@app/core/services/auth/auth.service'; import { DmpService } from '@app/core/services/dmp/dmp.service'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; import { BaseComponent } from '@common/base/base.component'; +import { takeUntil } from "rxjs/operators"; @Component({ selector: "app-recent-visited-activity", @@ -38,6 +39,7 @@ export class RecentVisitedActivityComponent extends BaseComponent dmpDataTableRequest.criteria.like = ""; this.dmpService .getPaged(dmpDataTableRequest, "listing") + .pipe(takeUntil(this._destroyed)) .subscribe(response => { this.dmpActivities = response.data; }); diff --git a/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.component.ts b/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.component.ts index 4e39559ed..893786801 100644 --- a/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.component.ts +++ b/dmp-frontend/src/app/ui/dataset-create-wizard/dataset-create-wizard.component.ts @@ -24,7 +24,7 @@ import { takeUntil } from 'rxjs/operators'; }) export class DatasetCreateWizard extends CheckDeactivateBaseComponent implements OnInit, IBreadCrumbComponent { breadCrumbs: Observable; - @ViewChild(DatasetEditorWizardComponent, { static: false }) datasetEditorWizardComponent: DatasetEditorWizardComponent; + @ViewChild(DatasetEditorWizardComponent) datasetEditorWizardComponent: DatasetEditorWizardComponent; isLinear = false; isNew = true; isSubmitted = false; diff --git a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-editor/dataset-editor.component.html b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-editor/dataset-editor.component.html index 30d3ff105..99b1c968e 100644 --- a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-editor/dataset-editor.component.html +++ b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-editor/dataset-editor.component.html @@ -29,11 +29,15 @@ {{'DATASET-EDITOR.FIELDS.DMP' | translate}} {{'DATASET-EDITOR.HINT.TITLE-REST' | translate}} -->
    - - + + +
    {{formGroup.get('description').getError('backendError').message}} {{'GENERAL.VALIDATION.REQUIRED' | translate}} - +
    diff --git a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.html b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.html index ca513b5b7..9a0a489c8 100644 --- a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.html +++ b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.html @@ -26,17 +26,29 @@
    -
    -
    +
    +
    - - - + + + + + + + + + +
    diff --git a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.ts b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.ts index 711414023..27c543b6e 100644 --- a/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.ts +++ b/dmp-frontend/src/app/ui/dataset/dataset-wizard/dataset-wizard.component.ts @@ -31,29 +31,32 @@ import { ValidationErrorModel } from '@common/forms/validation/error-model/valid import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; import { TranslateService } from '@ngx-translate/core'; import * as FileSaver from 'file-saver'; -import { Observable, of as observableOf, interval, Subscription } from 'rxjs'; -import { catchError, debounceTime, map, takeUntil } from 'rxjs/operators'; +import { Observable, of as observableOf, interval} from 'rxjs'; +import { catchError, debounceTime, filter, map, takeUntil } from 'rxjs/operators'; import { LockService } from '@app/core/services/lock/lock.service'; import { Location } from '@angular/common'; import { LockModel } from '@app/core/model/lock/lock.model'; import { Guid } from '@common/types/guid'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { AuthService } from '@app/core/services/auth/auth.service'; -import { environment } from 'environments/environment'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { SaveType } from '@app/core/common/enum/save-type'; import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { HttpClient } from '@angular/common/http'; -import { ToCEntry, ToCEntryType } from '@app/ui/misc/dataset-description-form/dataset-description.component'; import { VisibilityRulesService } from '@app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service'; +import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; +import { CheckDeactivateBaseComponent } from '@app/library/deactivate/deactivate.component'; @Component({ selector: 'app-dataset-wizard-component', templateUrl: 'dataset-wizard.component.html', styleUrls: ['./dataset-wizard.component.scss'] }) -export class DatasetWizardComponent extends BaseComponent implements OnInit, IBreadCrumbComponent { +export class DatasetWizardComponent extends CheckDeactivateBaseComponent implements OnInit, IBreadCrumbComponent { + canDeactivate(): boolean { + return !this.isDirty(); + } breadCrumbs: Observable; viewOnly = false; @@ -94,7 +97,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr //the table seraches for elements to scroll on page with id (TOCENTRY_ID_PREFIX+fieldsetId) TOCENTRY_ID_PREFIX="TocEntRy"; showtocentriesErrors = false; - @ViewChild('table0fContents', {static: false}) table0fContents: TableOfContents; + @ViewChild('table0fContents') table0fContents: TableOfContents; hintErrors: boolean = false; datasetIsOnceSaved = false; @@ -193,6 +196,13 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr // if (this.viewOnly) { this.formGroup.disable(); } // For future use, to make Dataset edit like DMP. this.loadDatasetProfiles(); this.registerFormListeners(); + + if(lockStatus){ + this.dialog.open(PopupNotificationDialogComponent,{data:{ + title:this.language.instant('DATASET-WIZARD.LOCKED.TITLE'), + message:this.language.instant('DATASET-WIZARD.LOCKED.MESSAGE') + }, maxWidth:'30em'}); + } // this.availableProfiles = this.datasetWizardModel.dmp.profiles; }) }, @@ -255,6 +265,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr this.lockService.checkLockStatus(data.id).pipe(takeUntil(this._destroyed)).subscribe(lockStatus => { this.lockStatus = lockStatus; this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data); + this.datasetWizardModel.status = 0; this.formGroup = this.datasetWizardModel.buildForm(); this.formGroup.get('id').setValue(null); this.dmpService.getSingle(newDmpId).pipe(map(data => data as DmpModel)) @@ -398,7 +409,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr // private _listenersSubscription:Subscription = new Subscription(); registerFormListeners() { - // const dmpSubscription = + // const dmpSubscription = this.formGroup.get('dmp').valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(x => { @@ -414,32 +425,32 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr this.formChanged(); } }); - // const labelSubscription = + // const labelSubscription = this.formGroup.get('label').valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(x => { this.formChanged(); }); - // const descriptionSubscription = + // const descriptionSubscription = this.formGroup.get('description').valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(x => { this.formChanged(); }); - // const uriSubscription = + // const uriSubscription = this.formGroup.get('uri').valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(x => { this.formChanged(); }); - // const tagsSubscription = + // const tagsSubscription = this.formGroup.get('tags').valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(x => { this.formChanged(); }); if (this.formGroup.get('datasetProfileDefinition')) { - // const datasetProfileDefinitionSubscription = + // const datasetProfileDefinitionSubscription = this.formGroup.get('datasetProfileDefinition').valueChanges .pipe(takeUntil(this._destroyed)) .subscribe(x => { @@ -545,7 +556,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr this.formGroup.addControl('datasetProfileDefinition', this.datasetProfileDefinitionModel.buildForm()); // const datasetProfileDefinitionForm = this.datasetProfileDefinitionModel.buildForm(); - + // let profId = null; // try{ // profId = this.formGroup.get('profile').value.id; @@ -613,8 +624,9 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr .pipe(takeUntil(this._destroyed)) .subscribe( data => { - this.onCallbackSuccess(data, saveType); + this.hasChanges = false; this.datasetIsOnceSaved = true; + this.onCallbackSuccess(data, saveType); }, error => this.onCallbackError(error)); } @@ -636,14 +648,15 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr } private _getPlaceHolder(formControl: any): string { - if (formControl.nativeElement.localName === 'input' || formControl.nativeElement.localName === 'textarea') { + if (formControl.nativeElement.localName === 'input' || formControl.nativeElement.localName === 'textarea' + || formControl.nativeElement.localName === 'richTextarea') { return formControl.nativeElement.getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'mat-select') { - return formControl.nativeElement.getAttribute('aria-label'); + return formControl.nativeElement.getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'app-single-auto-complete') { return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'app-multiple-auto-complete') { - return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); + return (Array.from(formControl.nativeElement.firstChild.firstChild.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); } } @@ -675,7 +688,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr } } const errorMessage = this._getErrorMessage(aControl, controlName); - + errmess.push(...errorMessage); } @@ -683,7 +696,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr No need to check case of FormControl below*/ if(aControl instanceof FormGroup){ - + const fg = aControl as FormGroup; //check children Object.keys(fg.controls).forEach(controlName=>{ @@ -696,7 +709,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr fa.controls.forEach((control,index)=>{ errmess.push(... this._buildErrorMessagesForAbstractControl(control, `${controlName} --> ${index+1}`)); }); - + } } @@ -717,7 +730,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr if (!this.isSemiFormValid(this.formGroup)) { //build messages const errorMessages = this._buildSemiFormErrorMessages(); - this.showValidationErrorsDialog(undefined, errorMessages); + this.showValidationErrorsDialog(undefined, errorMessages); this.hintErrors = true; return; } @@ -767,20 +780,47 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr } reverse() { - this.viewOnly = false; - this.datasetWizardModel.status = DatasetStatus.Draft; - setTimeout(x => { this.formGroup = null; }); - setTimeout(x => { - this.formGroup = this.datasetWizardModel.buildForm(); - this.registerFormListeners(); + + this.dialog.open(ConfirmationDialogComponent, { + data:{ + message: this.language.instant('DATASET-WIZARD.ACTIONS.UNDO-FINALIZATION-QUESTION'), + confirmButton: this.language.instant('DATASET-WIZARD.ACTIONS.CONFIRM'), + cancelButton: this.language.instant('DATASET-WIZARD.ACTIONS.REJECT'), + }, + maxWidth: '30em' + }) + .afterClosed() + .pipe( + filter(x=> x), + takeUntil(this._destroyed) + ).subscribe( _ => { + this.viewOnly = false; + this.datasetWizardModel.status = DatasetStatus.Draft; + setTimeout(x => { this.formGroup = null; }); + setTimeout(x => { + this.formGroup = this.datasetWizardModel.buildForm(); + this.registerFormListeners(); + }); }); + + } saveFinalize() { - this.formService.touchAllFormFields(this.formGroup); - if (!this.isFormValid()) { - this.showValidationErrorsDialog(); + // this.formService.touchAllFormFields(this.formGroup); + + if (!this.isSemiFormValid(this.formGroup) || (this.table0fContents && this.table0fContents.hasVisibleInvalidFields())) { + // this.showValidationErrorsDialog(); + this.dialog.open(FormValidationErrorsDialogComponent, { + data:{ + errorMessages:[this.language.instant('DATASET-WIZARD.MESSAGES.MISSING-FIELDS')] + } + }) + + + this.touchForm(); + this.hintErrors = true; return; } const dialogRef = this.dialog.open(ConfirmationDialogComponent, { @@ -794,9 +834,9 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr }); dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { if (result) { - if (!this.isFormValid()) { return; } + // if (!this.isFormValid()) { return; } this.formGroup.get('status').setValue(DatasetStatus.Finalized); - this.submit(); + this.submit(SaveType.finalize); } }); } @@ -808,10 +848,11 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr this.router.navigate(['/reload']).then(() => { this.router.navigate(['/datasets', 'new', this.formGroup.get('dmp').value.id]); }) } else if (saveType === this.saveAnd.close) { this.router.navigate(['/reload']).then(() => { this.router.navigate(['/plans', 'edit', this.formGroup.get('dmp').value.id]); }); + } else if (saveType === SaveType.finalize) { + this.router.navigate(['/reload']).then(() => { this.router.navigate(['/datasets', 'edit', data.id]); }); } else { this.datasetWizardModel = new DatasetWizardEditorModel().fromModel(data); this.editMode = this.datasetWizardModel.status === DatasetStatus.Draft; - // setTimeout(() => { this.formGroup = null; }); setTimeout(() => { this.formGroup.get('id').patchValue(data.id); @@ -843,6 +884,12 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr } onCallbackError(error: any) { + const errmes = error && error.message? error.message as string : null; + let feedbackMessage = this.language.instant('DATASET-EDITOR.ERRORS.ERROR-OCCURED'); + if(errmes){ + feedbackMessage += errmes; + } + this.uiNotificationService.snackBarNotification(feedbackMessage, SnackBarNotificationLevel.Warning); this.setErrorModel(error.error); } @@ -1107,7 +1154,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr // }else{ // this.formGroup.get(key).reset(); // } - + // } // }); } else { @@ -1206,7 +1253,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr // const tempResult:ToCEntry[] = []; - + // if(sections &§ions.length){ // sections.controls.forEach(section=>{ // tempResult.push(this._buildRecursively(section as FormGroup, ToCEntryType.Section)); @@ -1242,9 +1289,9 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr // } // private _sortByOrdinal(tocentries: ToCEntry[]){ - + // if(!tocentries || !tocentries.length) return; - + // tocentries.sort(this._customCompare); // tocentries.forEach(entry=>{ // this._sortByOrdinal(entry.subEntries); @@ -1269,7 +1316,7 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr // }); // } - + // getTocEntries(form): ToCEntry[] { // if (form == null) { return []; } // const result: ToCEntry[] = []; @@ -1309,6 +1356,6 @@ export class DatasetWizardComponent extends BaseComponent implements OnInit, IBr // } - + } diff --git a/dmp-frontend/src/app/ui/dataset/dataset-wizard/external-references/dataset-external-references-editor.component.ts b/dmp-frontend/src/app/ui/dataset/dataset-wizard/external-references/dataset-external-references-editor.component.ts index c50f4b40e..fc991bc70 100644 --- a/dmp-frontend/src/app/ui/dataset/dataset-wizard/external-references/dataset-external-references-editor.component.ts +++ b/dmp-frontend/src/app/ui/dataset/dataset-wizard/external-references/dataset-external-references-editor.component.ts @@ -24,7 +24,7 @@ import { Observable } from 'rxjs'; import { takeUntil, map } from 'rxjs/operators'; import { ENTER, COMMA } from '@angular/cdk/keycodes'; import { MatChipInputEvent } from '@angular/material/chips'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { ExternalDataRepositoryService } from '@app/core/services/external-sources/data-repository/extternal-data-repository.service'; import { ExternalDatasetService } from '@app/core/services/external-sources/dataset/external-dataset.service'; import { ExternalRegistryService } from '@app/core/services/external-sources/registry/external-registry.service'; diff --git a/dmp-frontend/src/app/ui/dataset/dataset.module.ts b/dmp-frontend/src/app/ui/dataset/dataset.module.ts index ec553bef5..9a0d0ee1e 100644 --- a/dmp-frontend/src/app/ui/dataset/dataset.module.ts +++ b/dmp-frontend/src/app/ui/dataset/dataset.module.ts @@ -27,6 +27,7 @@ import { FormProgressIndicationModule } from '../misc/dataset-description-form/c import { DatasetCopyDialogModule } from './dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.module'; import { DatasetCriteriaDialogComponent } from './listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component'; import { DatasetOverviewModule } from './overview/dataset-overview.module'; +import {RichTextEditorModule} from "@app/library/rich-text-editor/rich-text-editor.module"; @NgModule({ imports: [ @@ -45,7 +46,8 @@ import { DatasetOverviewModule } from './overview/dataset-overview.module'; FormValidationErrorsDialogModule, DatasetCopyDialogModule, DatasetOverviewModule, - FormProgressIndicationModule + FormProgressIndicationModule, + RichTextEditorModule ], declarations: [ DatasetListingComponent, diff --git a/dmp-frontend/src/app/ui/dataset/dataset.routing.ts b/dmp-frontend/src/app/ui/dataset/dataset.routing.ts index b024aa044..90f46c598 100644 --- a/dmp-frontend/src/app/ui/dataset/dataset.routing.ts +++ b/dmp-frontend/src/app/ui/dataset/dataset.routing.ts @@ -1,5 +1,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { CanDeactivateGuard } from '@app/library/deactivate/can-deactivate.guard'; import { AuthGuard } from '../../core/auth-guard.service'; import { DatasetWizardComponent } from './dataset-wizard/dataset-wizard.component'; import { DatasetListingComponent } from './listing/dataset-listing.component'; @@ -14,6 +15,7 @@ const routes: Routes = [ breadcrumb: true, title: 'GENERAL.TITLES.DATASET-NEW' }, + canDeactivate:[CanDeactivateGuard] }, { path: 'edit/:id', @@ -24,6 +26,7 @@ const routes: Routes = [ public: false, title: 'GENERAL.TITLES.DATASET-EDIT' }, + canDeactivate:[CanDeactivateGuard] }, { path: 'publicEdit/:publicId', @@ -32,7 +35,8 @@ const routes: Routes = [ data: { public: true, title: 'GENERAL.TITLES.DATASET-PUBLIC-EDIT' - } + }, + canDeactivate:[CanDeactivateGuard] }, { path: 'new', @@ -42,6 +46,7 @@ const routes: Routes = [ breadcrumb: true, title: 'GENERAL.TITLES.DATASET-NEW' }, + canDeactivate:[CanDeactivateGuard] }, { path: '', @@ -67,6 +72,7 @@ const routes: Routes = [ breadcrumb: true, title: 'GENERAL.TITLES.DATASET-COPY' }, + canDeactivate:[CanDeactivateGuard] }, { path: 'profileupdate/:updateId', @@ -76,6 +82,7 @@ const routes: Routes = [ breadcrumb: true, title: 'GENERAL.TITLES.DATASET-UPDATE' }, + canDeactivate:[CanDeactivateGuard] }, { path: 'overview/:id', diff --git a/dmp-frontend/src/app/ui/dataset/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.ts b/dmp-frontend/src/app/ui/dataset/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.ts index d24cfc225..1789cb458 100644 --- a/dmp-frontend/src/app/ui/dataset/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.ts +++ b/dmp-frontend/src/app/ui/dataset/listing/criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component.ts @@ -1,5 +1,5 @@ import { Inject, Component, ViewChild, OnInit, Output, EventEmitter } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { FormGroup } from '@angular/forms'; import { DatasetCriteriaComponent } from '../dataset-criteria.component'; import { DatasetCriteria } from '@app/core/query/dataset/dataset-criteria'; diff --git a/dmp-frontend/src/app/ui/dataset/listing/criteria/dataset-criteria.component.ts b/dmp-frontend/src/app/ui/dataset/listing/criteria/dataset-criteria.component.ts index 433662496..3fa368ef9 100644 --- a/dmp-frontend/src/app/ui/dataset/listing/criteria/dataset-criteria.component.ts +++ b/dmp-frontend/src/app/ui/dataset/listing/criteria/dataset-criteria.component.ts @@ -34,7 +34,7 @@ import { TranslateService } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; import { AuthService } from '@app/core/services/auth/auth.service'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { ExploreDmpCriteriaModel } from '@app/core/query/explore-dmp/explore-dmp-criteria'; import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; diff --git a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.html b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.html index c152d1d34..f03d171f4 100644 --- a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.html +++ b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.html @@ -65,7 +65,7 @@
    -
    +
    diff --git a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.ts b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.ts index c3dfbf807..f4f90fb18 100644 --- a/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.ts +++ b/dmp-frontend/src/app/ui/dataset/listing/dataset-listing.component.ts @@ -18,9 +18,9 @@ import { Observable, of as observableOf } from 'rxjs'; import { debounceTime, takeUntil } from 'rxjs/operators'; import { ExternalTagEditorModel, DatasetWizardEditorModel } from '../dataset-wizard/dataset-wizard-editor.model'; import { AuthService } from '@app/core/services/auth/auth.service'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { DatasetCriteriaDialogComponent } from './criteria/dataset-criteria-dialogue/dataset-criteria-dialog.component'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { FormGroup, FormBuilder, FormControl } from '@angular/forms'; import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; @@ -39,7 +39,7 @@ import { HttpClient } from '@angular/common/http'; export class DatasetListingComponent extends BaseComponent implements OnInit, IBreadCrumbComponent { @ViewChild(MatPaginator, { static: true }) _paginator: MatPaginator; - @ViewChild(MatSort, { static: false }) sort: MatSort; + @ViewChild(MatSort) sort: MatSort; // @ViewChild(DatasetCriteriaComponent, { static: true }) criteria: DatasetCriteriaComponent; breadCrumbs: Observable; diff --git a/dmp-frontend/src/app/ui/dataset/listing/listing-item/dataset-listing-item.component.ts b/dmp-frontend/src/app/ui/dataset/listing/listing-item/dataset-listing-item.component.ts index 8c377cf6b..ff0ca58e7 100644 --- a/dmp-frontend/src/app/ui/dataset/listing/listing-item/dataset-listing-item.component.ts +++ b/dmp-frontend/src/app/ui/dataset/listing/listing-item/dataset-listing-item.component.ts @@ -8,7 +8,7 @@ import { BaseComponent } from '@common/base/base.component'; import { takeUntil } from 'rxjs/operators'; import * as FileSaver from 'file-saver'; import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dmp-invitation-dialog.component'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { DatasetCopyDialogueComponent } from '../../dataset-wizard/dataset-copy-dialogue/dataset-copy-dialogue.component'; import { FormControl } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; diff --git a/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.html b/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.html index a60513212..2fa0562ca 100644 --- a/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.html +++ b/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.html @@ -91,7 +91,7 @@
    {{'DATASET-LISTING.COLUMNS.DESCRIPTION' | translate}}
    -

    {{ dataset.description }}

    +

    horizontal_rule diff --git a/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.ts b/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.ts index c17692525..c665817f2 100644 --- a/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.ts +++ b/dmp-frontend/src/app/ui/dataset/overview/dataset-overview.component.ts @@ -7,12 +7,12 @@ import { ActivatedRoute, Router, Params } from '@angular/router'; import { DatasetService } from '@app/core/services/dataset/dataset.service'; import { TranslateService } from '@ngx-translate/core'; import { AuthService } from '@app/core/services/auth/auth.service'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { Oauth2DialogService } from '@app/ui/misc/oauth2-dialog/service/oauth2-dialog.service'; import { UserService } from '@app/core/services/user/user.service'; -import { takeUntil } from 'rxjs/operators'; +import { filter, takeUntil } from 'rxjs/operators'; import { Principal } from '@app/core/model/auth/principal'; import { Role } from '@app/core/common/enum/role'; import { Location } from '@angular/common'; @@ -33,6 +33,7 @@ import { DmpStatus } from '@app/core/common/enum/dmp-status'; import { DmpOverviewModel } from '@app/core/model/dmp/dmp-overview'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { HttpClient } from '@angular/common/http'; +import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; @Component({ @@ -139,7 +140,15 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { checkLockStatus(id: string) { this.lockService.checkLockStatus(id).pipe(takeUntil(this._destroyed)) - .subscribe(lockStatus => this.lockStatus = lockStatus); + .subscribe(lockStatus => { + this.lockStatus = lockStatus + if(lockStatus){ + this.dialog.open(PopupNotificationDialogComponent,{data:{ + title:this.language.instant('DATASET-OVERVIEW.LOCKED.TITLE'), + message:this.language.instant('DATASET-OVERVIEW.LOCKED.MESSAGE') + }, maxWidth:'30em'}); + } + }); } onFetchingDeletedCallbackError(redirectRoot: string) { @@ -301,7 +310,11 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { } onUpdateCallbackError(error) { - this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('DATASET-UPLOAD.SNACK-BAR.UNSUCCESSFUL'), SnackBarNotificationLevel.Error); + this.uiNotificationService.snackBarNotification(error.error.message ? this.tryTranslate( error.error.message) : this.language.instant('DATASET-UPLOAD.SNACK-BAR.UNSUCCESSFUL'), SnackBarNotificationLevel.Error); + } + tryTranslate(errorMessage: string): string{ + return errorMessage.replace('Field value of', this.language.instant('Field value of')) + .replace('must be filled', this.language.instant('must be filled')); } public getOrcidPath(): string { @@ -466,31 +479,54 @@ export class DatasetOverviewComponent extends BaseComponent implements OnInit { // } finalize(dataset: DatasetOverviewModel) { - const dialogRef = this.dialog.open(ConfirmationDialogComponent, { - restoreFocus: false, + + + this.dialog.open(ConfirmationDialogComponent, { data: { - message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.FINALIZE-ITEM'), - confirmButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.AFFIRMATIVE'), - cancelButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.NEGATIVE'), - isDeleteConfirmation: false - } - }); - dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { - if (result) { - this.datasetWizardService.getSingle(dataset.id) - .pipe(takeUntil(this._destroyed)) - .subscribe(data => { - this.datasetWizardModel = data; - this.datasetWizardModel.status = DatasetStatus.Finalized; - this.datasetWizardService.createDataset(this.datasetWizardModel) - .pipe(takeUntil(this._destroyed)) - .subscribe( - data => this.onUpdateCallbackSuccess(), - error => this.onUpdateCallbackError(error) - ); - }); - } - }); + message: this.language.instant('DATASET-OVERVIEW.FINALISE-POPUP.MESSAGE'), + confirmButton: this.language.instant('DATASET-OVERVIEW.FINALISE-POPUP.CONFIRM'), + cancelButton: this.language.instant('DATASET-OVERVIEW.FINALISE-POPUP.CANCEL'), + }, + maxWidth: '30em' + }) + .afterClosed() + .pipe( + filter(x => x), + takeUntil(this._destroyed) + ) + .subscribe( _ =>{ + this.router.navigate(['datasets','edit',dataset.id]); + }) + + + + + + // const dialogRef = this.dialog.open(ConfirmationDialogComponent, { + // restoreFocus: false, + // data: { + // message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.FINALIZE-ITEM'), + // confirmButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.AFFIRMATIVE'), + // cancelButton: this.language.instant('QUICKWIZARD.SAVE-DIALOG.ACTIONS.NEGATIVE'), + // isDeleteConfirmation: false + // } + // }); + // dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => { + // if (result) { + // this.datasetWizardService.getSingle(dataset.id) + // .pipe(takeUntil(this._destroyed)) + // .subscribe(data => { + // this.datasetWizardModel = data; + // this.datasetWizardModel.status = DatasetStatus.Finalized; + // this.datasetWizardService.createDataset(this.datasetWizardModel) + // .pipe(takeUntil(this._destroyed)) + // .subscribe( + // data => this.onUpdateCallbackSuccess(), + // error => this.onUpdateCallbackError(error) + // ); + // }); + // } + // }); } hasReversableStatus(dataset: DatasetOverviewModel): boolean { diff --git a/dmp-frontend/src/app/ui/dmp/clone/dmp-clone.component.ts b/dmp-frontend/src/app/ui/dmp/clone/dmp-clone.component.ts index 3c1092e6f..82321b4a3 100644 --- a/dmp-frontend/src/app/ui/dmp/clone/dmp-clone.component.ts +++ b/dmp-frontend/src/app/ui/dmp/clone/dmp-clone.component.ts @@ -28,7 +28,7 @@ import { DatasetService } from '@app/core/services/dataset/dataset.service'; import { DmpEditorModel } from '../editor/dmp-editor.model'; import { DatasetWizardEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; @Component({ diff --git a/dmp-frontend/src/app/ui/dmp/dataset-preview/dataset-preview-dialog.component.html b/dmp-frontend/src/app/ui/dmp/dataset-preview/dataset-preview-dialog.component.html index 4408ca279..3d1182cde 100644 --- a/dmp-frontend/src/app/ui/dmp/dataset-preview/dataset-preview-dialog.component.html +++ b/dmp-frontend/src/app/ui/dmp/dataset-preview/dataset-preview-dialog.component.html @@ -7,7 +7,8 @@
    - +
    diff --git a/dmp-frontend/src/app/ui/dmp/dmp.module.ts b/dmp-frontend/src/app/ui/dmp/dmp.module.ts index c62ff93dc..01f0355b6 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp.module.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp.module.ts @@ -49,6 +49,7 @@ import { DmpToDatasetDialogComponent } from './dmp-to-dataset/dmp-to-dataset-dia import { FormProgressIndicationComponent } from '../misc/dataset-description-form/components/form-progress-indication/form-progress-indication.component'; import { FormProgressIndicationModule } from '../misc/dataset-description-form/components/form-progress-indication/form-progress-indication.module'; import { DatasetPreviewDialogComponent } from './dataset-preview/dataset-preview-dialog.component'; +import {RichTextEditorModule} from "@app/library/rich-text-editor/rich-text-editor.module"; @NgModule({ imports: [ @@ -66,7 +67,8 @@ import { DatasetPreviewDialogComponent } from './dataset-preview/dataset-preview DatasetEditorDetailsModule, DatasetDescriptionFormModule, NgxDropzoneModule, - FormProgressIndicationModule + FormProgressIndicationModule, + RichTextEditorModule ], declarations: [ DmpListingComponent, diff --git a/dmp-frontend/src/app/ui/dmp/dmp.routing.ts b/dmp-frontend/src/app/ui/dmp/dmp.routing.ts index b3b8574f7..26295c3c0 100644 --- a/dmp-frontend/src/app/ui/dmp/dmp.routing.ts +++ b/dmp-frontend/src/app/ui/dmp/dmp.routing.ts @@ -7,6 +7,7 @@ import { DmpWizardComponent } from './wizard/dmp-wizard.component'; import { DmpOverviewComponent } from './overview/dmp-overview.component'; import { DmpCloneComponent } from './clone/dmp-clone.component'; import { AuthGuard } from '@app/core/auth-guard.service'; +import { CanDeactivateGuard } from '@app/library/deactivate/can-deactivate.guard'; const routes: Routes = [ { @@ -38,6 +39,7 @@ const routes: Routes = [ breadcrumb: true, title: 'GENERAL.TITLES.DMP-EDIT' }, + canDeactivate:[CanDeactivateGuard] }, { path: 'publicEdit/:publicId', @@ -46,6 +48,7 @@ const routes: Routes = [ breadcrumb: true, title: 'GENERAL.TITLES.DMP-PUBLIC-EDIT' }, + canDeactivate:[CanDeactivateGuard] }, { path: 'overview/:id', @@ -78,7 +81,8 @@ const routes: Routes = [ data: { breadcrumbs: 'new', title: 'GENERAL.TITLES.DMP-NEW' - } + }, + canDeactivate:[CanDeactivateGuard] }, // { // path: 'new/dataset', diff --git a/dmp-frontend/src/app/ui/dmp/editor/add-researcher/add-researcher.component.html b/dmp-frontend/src/app/ui/dmp/editor/add-researcher/add-researcher.component.html index 26d784fd5..1664c9541 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/add-researcher/add-researcher.component.html +++ b/dmp-frontend/src/app/ui/dmp/editor/add-researcher/add-researcher.component.html @@ -19,8 +19,8 @@
    -
    -
    +
    +
    diff --git a/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.html b/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.html index 9c670a435..5949af616 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.html +++ b/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.html @@ -1,8 +1,14 @@

    {{ 'DMP-EDITOR.DATASET-TEMPLATE-LIST.TITLE' | translate }}

    - - {{profile.label}} + +
    +
    + {{profile.label}} +
    + +
    +

    diff --git a/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.scss b/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.scss new file mode 100644 index 000000000..caf264353 --- /dev/null +++ b/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.scss @@ -0,0 +1,25 @@ +.list-option-inner { + flex-grow: 1; + white-space: nowrap; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; +} + +.list-option { + width: calc(100% - 16px); + flex-grow: 1; + display: -webkit-box; + -webkit-box-orient: vertical; + -moz-box-orient: vertical; + -ms-box-orient: vertical; + box-orient: vertical; + -webkit-line-clamp: 2; + -moz-line-clamp: 2; + -ms-line-clamp: 2; + line-clamp: 2; +} + +::ng-deep .mat-list-text { + max-height: 100%; +} diff --git a/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.ts b/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.ts index 165d7554c..6ee413cc2 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/available-profiles/available-profiles.component.ts @@ -9,6 +9,7 @@ import { BaseComponent } from '@common/base/base.component'; import { takeUntil } from 'rxjs/operators'; @Component({ + styleUrls: ['available-profiles.component.scss'], selector: 'app-available-profiles-component', templateUrl: 'available-profiles.component.html', }) diff --git a/dmp-frontend/src/app/ui/dmp/editor/dataset-editor-details/dataset-editor-details.component.ts b/dmp-frontend/src/app/ui/dmp/editor/dataset-editor-details/dataset-editor-details.component.ts index f4113eac9..49eba19b3 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/dataset-editor-details/dataset-editor-details.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/dataset-editor-details/dataset-editor-details.component.ts @@ -3,7 +3,9 @@ import { OnInit, Component, Input, Output, EventEmitter } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; -import { MatDialog, MatSnackBar, MatChipInputEvent } from '@angular/material'; +import { MatChipInputEvent } from '@angular/material/chips'; +import { MatDialog } from '@angular/material/dialog'; +import { MatSnackBar } from '@angular/material/snack-bar'; import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; import { ActivatedRoute, Router } from '@angular/router'; import { DmpService } from '@app/core/services/dmp/dmp.service'; @@ -19,7 +21,7 @@ import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; import { LockModel } from '@app/core/model/lock/lock.model'; import { takeUntil, map, catchError } from 'rxjs/operators'; import { RequestItem } from '@app/core/query/request-item'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { interval, Observable, of as observableOf } from 'rxjs'; import { Guid } from '@common/types/guid'; import { Location } from '@angular/common'; diff --git a/dmp-frontend/src/app/ui/dmp/editor/dataset-info/dataset-info.component.ts b/dmp-frontend/src/app/ui/dmp/editor/dataset-info/dataset-info.component.ts index 651d65560..4980c991e 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/dataset-info/dataset-info.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/dataset-info/dataset-info.component.ts @@ -7,8 +7,7 @@ import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/mu import { map, takeUntil } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; -import { isNullOrUndefined } from 'util'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria'; import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; @@ -21,8 +20,6 @@ import { Router, Params, ActivatedRoute } from '@angular/router'; import { RequestItem } from '@app/core/query/request-item'; import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; import { DatasetDescriptionFormEditorModel } from '@app/ui/misc/dataset-description-form/dataset-description-form.model'; -import { DatasetWizardEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; -import { DmpListingModel } from '@app/core/model/dmp/dmp-listing'; import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service'; import { DatasetPreviewDialogComponent } from '../../dataset-preview/dataset-preview-dialog.component'; @@ -251,6 +248,16 @@ export class DatasetInfoComponent extends BaseComponent implements OnInit { onOptionSelected(){ try{ const profiles = this.formGroup.get('profiles').value as {id:string, label:string}[]; + const profileCounts: Map = new Map(); + profiles.forEach((value) => profileCounts.set(value.id, (profileCounts.get(value.id) !== undefined ? profileCounts.get(value.id): 0 ) + 1)); + const duplicateProfiles = profiles.filter((value) => { + let isOk = profileCounts.get(value.id) > 1; + if (isOk) { + profileCounts.set(value.id, 0); + } + return isOk; + }); + duplicateProfiles.forEach((value) => profiles.splice(profiles.lastIndexOf(value), 1)); profiles.sort((a,b)=> a.label.localeCompare(b.label)); }catch{ console.info('Could not sort Dataset Templates') diff --git a/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.component.ts b/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.component.ts index 9d5773803..8baa9e0e8 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.component.ts @@ -1,11 +1,11 @@ -import { Component, OnInit, SimpleChanges } from '@angular/core'; -import { FormGroup, AbstractControl, FormControl, FormArray, FormBuilder } from '@angular/forms'; +import { Component, OnInit} from '@angular/core'; +import { FormGroup, AbstractControl, FormControl, FormArray} from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { DmpStatus } from '@app/core/common/enum/dmp-status'; import { DataTableRequest } from '@app/core/model/data-table/data-table-request'; -import { DmpProfileDefinition, DmpProfile } from '@app/core/model/dmp-profile/dmp-profile'; +import { DmpProfileDefinition } from '@app/core/model/dmp-profile/dmp-profile'; import { DmpProfileListing } from '@app/core/model/dmp-profile/dmp-profile-listing'; import { DmpModel } from '@app/core/model/dmp/dmp'; import { UserModel } from '@app/core/model/user/user'; @@ -26,14 +26,13 @@ import { GrantTabModel } from '@app/ui/dmp/editor/grant-tab/grant-tab-model'; import { ProjectFormModel } from '@app/ui/dmp/editor/grant-tab/project-form-model'; import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item'; import { IBreadCrumbComponent } from '@app/ui/misc/breadcrumb/definition/IBreadCrumbComponent'; -import { BaseComponent } from '@common/base/base.component'; import { FormService } from '@common/forms/form-service'; import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component'; import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; import { TranslateService } from '@ngx-translate/core'; import * as FileSaver from 'file-saver'; import { Observable, of as observableOf, interval } from 'rxjs'; -import { catchError, delay, map, takeUntil } from 'rxjs/operators'; +import { map, takeUntil } from 'rxjs/operators'; import { Principal } from "@app/core/model/auth/principal"; import { Role } from "@app/core/common/enum/role"; import { LockService } from '@app/core/services/lock/lock.service'; @@ -42,26 +41,23 @@ import { Guid } from '@common/types/guid'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { ExtraPropertiesFormModel } from './general-tab/extra-properties-form.model'; import { DatasetWizardEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; -import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; -import { DmpListingModel } from '@app/core/model/dmp/dmp-listing'; -import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; -import { DatasetDescriptionFormEditorModel } from '@app/ui/misc/dataset-description-form/dataset-description-form.model'; import { DatasetWizardService } from '@app/core/services/dataset-wizard/dataset-wizard.service'; -import { CloneDialogComponent } from '../clone/clone-dialog/clone-dialog.component'; -import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard'; import { DatasetService } from '@app/core/services/dataset/dataset.service'; import { DmpToDatasetDialogComponent } from '../dmp-to-dataset/dmp-to-dataset-dialog.component'; import { GrantEditorModel } from '@app/ui/grant/editor/grant-editor.model'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; -import { HttpClient } from '@angular/common/http'; import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; +import { CheckDeactivateBaseComponent } from '@app/library/deactivate/deactivate.component'; @Component({ selector: 'app-dmp-editor-component', templateUrl: 'dmp-editor.component.html', styleUrls: ['./dmp-editor.component.scss'] }) -export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadCrumbComponent { +export class DmpEditorComponent extends CheckDeactivateBaseComponent implements OnInit, IBreadCrumbComponent { + canDeactivate(): boolean { + return !this.isDirty(); + } editMode = true; // editMode = false; @@ -117,7 +113,6 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC private formService: FormService, private lockService: LockService, private configurationService: ConfigurationService, - private httpClient: HttpClient, private matomoService: MatomoService ) { super(); @@ -498,6 +493,7 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC complete => { this.formGroup.get('id').setValue(complete.id); this.formGroup.get('modified').setValue(complete.modified); + this.hasChanges = false; if (showAddDatasetDialog) { this.addDatasetOpenDialog(complete); } @@ -587,7 +583,7 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC try{ showDialog = this.isNew && !this.formGroup.get('datasets').value.length; }catch{ - + } this.editDataset(dmp.id, true, showDialog); @@ -825,14 +821,15 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC } private _getPlaceHolder(formControl: any): string { - if (formControl.nativeElement.localName === 'input' || formControl.nativeElement.localName === 'textarea') { + if (formControl.nativeElement.localName === 'input' || formControl.nativeElement.localName === 'textarea' + || formControl.nativeElement.localName === 'richTextarea') { return formControl.nativeElement.getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'mat-select') { - return formControl.nativeElement.getAttribute('aria-label'); + return formControl.nativeElement.getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'app-single-auto-complete') { return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'app-multiple-auto-complete') { - return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); + return (Array.from(formControl.nativeElement.firstChild.firstChild.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); } } @@ -864,14 +861,14 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC } } const errorMessage = this._getErrorMessage(aControl, controlName); - + errmess.push(...errorMessage); } /*in case the aControl is FormControl then the it should have provided its error messages above. No need to check case of FormControl below*/ if(aControl instanceof FormGroup){ - + const fg = aControl as FormGroup; //check children Object.keys(fg.controls).forEach(controlName=>{ @@ -884,9 +881,9 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC fa.controls.forEach((control,index)=>{ errmess.push(... this._buildErrorMessagesForAbstractControl(control, `${controlName} --> ${index+1}`)); }); - + } - + } return errmess; @@ -953,7 +950,7 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC editDataset(id: string, isNew: boolean, showModal:boolean = false) { - + if(showModal){ const dialogRef = this.dialog.open(DmpToDatasetDialogComponent, { width: '500px', @@ -975,7 +972,7 @@ export class DmpEditorComponent extends BaseComponent implements OnInit, IBreadC } else { this.router.navigate(['/datasets', 'edit', id]); } - + } diff --git a/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.model.ts b/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.model.ts index b95e0e8e0..43bcbea87 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.model.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/dmp-editor.model.ts @@ -21,7 +21,7 @@ import { BackendErrorValidator } from '@common/forms/validation/custom-validator import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; import { ValidationContext } from '@common/forms/validation/validation-context'; import { ExtraPropertiesFormModel } from './general-tab/extra-properties-form.model'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { DatasetWizardEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; import { DatasetsAutoCompleteFieldDataEditorModel } from '@app/ui/admin/dataset-profile/admin/field-data/datasets-autocomplete-field-data-editor-mode'; import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard'; diff --git a/dmp-frontend/src/app/ui/dmp/editor/general-tab/extra-properties-form.model.ts b/dmp-frontend/src/app/ui/dmp/editor/general-tab/extra-properties-form.model.ts index 6ff2f48cb..061348b2d 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/general-tab/extra-properties-form.model.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/general-tab/extra-properties-form.model.ts @@ -2,7 +2,7 @@ import { ValidationContext } from '@common/forms/validation/validation-context'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { BackendErrorValidator } from '@common/forms/validation/custom-validator'; import { CostModel } from '@app/core/model/dmp/cost'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { CostEditorModel } from '../cost-editor/add-cost/add-cost.model'; import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; @@ -55,11 +55,11 @@ export class ExtraPropertiesFormModel { createValidationContext(): ValidationContext { const baseContext: ValidationContext = new ValidationContext(); - baseContext.validation.push({ key: 'language', validators: [BackendErrorValidator(this.validationErrorModel, 'language')] }); + baseContext.validation.push({ key: 'language', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'language')] }); baseContext.validation.push({ key: 'license', validators: [BackendErrorValidator(this.validationErrorModel, 'license')] }); baseContext.validation.push({ key: 'visible', validators: [BackendErrorValidator(this.validationErrorModel, 'visible')] }); baseContext.validation.push({ key: 'publicDate', validators: [BackendErrorValidator(this.validationErrorModel, 'publicDate')] }); - baseContext.validation.push({ key: 'contact', validators: [BackendErrorValidator(this.validationErrorModel, 'contact')] }); + baseContext.validation.push({ key: 'contact', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'contact')] }); baseContext.validation.push({ key: 'costs', validators: [BackendErrorValidator(this.validationErrorModel, 'costs')] }); return baseContext; } diff --git a/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.ts b/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.ts index 038fbe007..59c3ffc95 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/general-tab/general-tab.component.ts @@ -21,7 +21,7 @@ import { TranslateService } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; import { AddOrganizationComponent } from '../add-organization/add-organization.component'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { LanguageInfoService } from '@app/core/services/culture/language-info-service'; import { LanguageInfo } from '@app/core/model/language-info'; diff --git a/dmp-frontend/src/app/ui/dmp/editor/license-info/license-info.component.ts b/dmp-frontend/src/app/ui/dmp/editor/license-info/license-info.component.ts index 05f1c97dc..cb59fa1d4 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/license-info/license-info.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/license-info/license-info.component.ts @@ -5,8 +5,8 @@ import { map, takeUntil } from 'rxjs/operators'; import { ExternalSourceItemModel } from '@app/core/model/external-sources/external-source-item'; import { Observable } from 'rxjs'; import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; -import { isNullOrUndefined } from 'util'; -import { MatDialog } from '@angular/material'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; +import { MatDialog } from '@angular/material/dialog'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; import { LanguageInfo } from '@app/core/model/language-info'; import { LanguageInfoService } from '@app/core/services/culture/language-info-service'; diff --git a/dmp-frontend/src/app/ui/dmp/editor/main-info/main-info.component.html b/dmp-frontend/src/app/ui/dmp/editor/main-info/main-info.component.html index 29c17b5a0..3f5fa53a7 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/main-info/main-info.component.html +++ b/dmp-frontend/src/app/ui/dmp/editor/main-info/main-info.component.html @@ -25,20 +25,20 @@

    1.2 {{'DMP-EDITOR.FIELDS.DESCRIPTION' | translate}}
    {{'DMP-EDITOR.FIELDS.DESCRIPTION-HINT' | translate}}
    - - - + +
    -
    1.3 {{'DMP-EDITOR.FIELDS.LANGUAGE' | translate}}
    +
    1.3 {{'DMP-EDITOR.FIELDS.LANGUAGE' | translate}}*
    {{'DMP-EDITOR.FIELDS.LANGUAGE-HINT' | translate}}
    - + {{ lang.name }} diff --git a/dmp-frontend/src/app/ui/dmp/editor/main-info/main-info.component.ts b/dmp-frontend/src/app/ui/dmp/editor/main-info/main-info.component.ts index a676a43c3..56fedce6f 100644 --- a/dmp-frontend/src/app/ui/dmp/editor/main-info/main-info.component.ts +++ b/dmp-frontend/src/app/ui/dmp/editor/main-info/main-info.component.ts @@ -8,8 +8,8 @@ import { map, takeUntil } from 'rxjs/operators'; import { ExternalSourceItemModel } from '@app/core/model/external-sources/external-source-item'; import { Observable } from 'rxjs'; import { ExternalSourcesService } from '@app/core/services/external-sources/external-sources.service'; -import { isNullOrUndefined } from 'util'; -import { MatDialog } from '@angular/material'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; +import { MatDialog } from '@angular/material/dialog'; import { AddOrganizationComponent } from '../add-organization/add-organization.component'; import { AddResearcherComponent } from '../add-researcher/add-researcher.component'; import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration'; @@ -111,6 +111,9 @@ export class MainInfoComponent extends BaseComponent implements OnInit { if (isNullOrUndefined(this.formGroup.get('extraProperties').get('contact').value)) { this.formGroup.get('extraProperties').get('contact').patchValue(associate.id); } + if (isNullOrUndefined(this.formGroup.get('extraProperties').get('language').value)) { + this.formGroup.get('extraProperties').get('language').patchValue('en'); + } } // Researchers diff --git a/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.html b/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.html index 1e0bd007e..f722893f0 100644 --- a/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.html +++ b/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.html @@ -9,7 +9,9 @@
    - +

    diff --git a/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.scss b/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.scss index 4919dc0e8..650d75379 100644 --- a/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.scss +++ b/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.scss @@ -121,3 +121,15 @@ background: #129d99; color: #ffffff; } + +@keyframes blink{ + 0% {border: 1px solid rgba(255, 0, 0, 0.2);} + 50% {border: 1px solid rgba(255, 0, 0, 0.5);} + 100% {border: 1px solid rgba(255, 0, 0, 0.2);} +} + +:host ::ng-deep .invalid-email{ + border: 1px solid red; + // animation: blink 1.4s infinite; + // animation-fill-mode: both; +} diff --git a/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.ts b/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.ts index a57ee5cb6..ee29a5ace 100644 --- a/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.ts +++ b/dmp-frontend/src/app/ui/dmp/invitation/dmp-invitation-dialog.component.ts @@ -68,7 +68,14 @@ export class DmpInvitationDialogComponent extends BaseComponent implements OnIni valueAssign: (item) => { const result = typeof(item) === 'string' ? item : item.email; return result; + }, + autoSelectFirstOptionOnBlur: true, + appendClassToItem: [{class: 'invalid-email', applyFunc: (item)=> { + const val = typeof(item) === 'string'? item : item.email; + const regexp = new RegExp(/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/); + return !regexp.test(val); } + }] }; add(event: MatChipInputEvent): void { diff --git a/dmp-frontend/src/app/ui/dmp/listing/criteria/dmp-criteria-dialog.component.ts b/dmp-frontend/src/app/ui/dmp/listing/criteria/dmp-criteria-dialog.component.ts index e7335ef87..168530b98 100644 --- a/dmp-frontend/src/app/ui/dmp/listing/criteria/dmp-criteria-dialog.component.ts +++ b/dmp-frontend/src/app/ui/dmp/listing/criteria/dmp-criteria-dialog.component.ts @@ -1,5 +1,5 @@ import { Inject, Component, ViewChild, OnInit, Output, EventEmitter } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { DmpCriteriaComponent } from './dmp-criteria.component'; import { DmpCriteria } from '@app/core/query/dmp/dmp-criteria'; import { FormGroup } from '@angular/forms'; diff --git a/dmp-frontend/src/app/ui/dmp/listing/criteria/dmp-criteria.component.ts b/dmp-frontend/src/app/ui/dmp/listing/criteria/dmp-criteria.component.ts index 54b36fd77..8ec624e36 100644 --- a/dmp-frontend/src/app/ui/dmp/listing/criteria/dmp-criteria.component.ts +++ b/dmp-frontend/src/app/ui/dmp/listing/criteria/dmp-criteria.component.ts @@ -20,7 +20,7 @@ import { ValidationErrorModel } from '@common/forms/validation/error-model/valid import { TranslateService } from '@ngx-translate/core'; import { map, takeUntil } from 'rxjs/operators'; import { AuthService } from '@app/core/services/auth/auth.service'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { DatasetService } from '@app/core/services/dataset/dataset.service'; import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile'; import { Observable } from 'rxjs'; diff --git a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts index 94342c2b0..f825230d6 100644 --- a/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts +++ b/dmp-frontend/src/app/ui/dmp/listing/dmp-listing.component.ts @@ -21,7 +21,7 @@ import { debounceTime, takeUntil } from 'rxjs/operators'; import { GrantService } from "@app/core/services/grant/grant.service"; import { DmpUploadDialogue } from './upload-dialogue/dmp-upload-dialogue.component'; import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { AuthService } from '@app/core/services/auth/auth.service'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { DmpCriteriaDialogComponent } from './criteria/dmp-criteria-dialog.component'; @@ -40,7 +40,7 @@ import { MatomoService } from '@app/core/services/matomo/matomo-service'; export class DmpListingComponent extends BaseComponent implements OnInit, IBreadCrumbComponent { @ViewChild(MatPaginator, { static: true }) _paginator: MatPaginator; - @ViewChild(MatSort, { static: false }) sort: MatSort; + @ViewChild(MatSort) sort: MatSort; // @ViewChild(DmpCriteriaComponent, { static: true }) criteria: DmpCriteriaComponent; breadCrumbs: Observable = observableOf([{ parentComponentName: null, label: 'DMPs', url: "/plans" }]); diff --git a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html index 1517c084e..d6ecd837f 100644 --- a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html +++ b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.html @@ -82,7 +82,7 @@

    {{'DATASET-LISTING.COLUMNS.DESCRIPTION' | translate}}
    -

    {{ dmp.description }}

    +

    horizontal_rule @@ -103,7 +103,7 @@ horizontal_rule
    -
    +
    add {{'DMP-LISTING.ACTIONS.ADD-DATASET-SHORT' | translate}} diff --git a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts index 5ecfee18d..8a75001ee 100644 --- a/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts +++ b/dmp-frontend/src/app/ui/dmp/overview/dmp-overview.component.ts @@ -1,5 +1,5 @@ -import { Component, OnInit, Input, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { DatasetStatus } from '@app/core/common/enum/dataset-status'; @@ -22,19 +22,13 @@ import { Observable, of as observableOf, interval } from 'rxjs'; import { takeUntil, map } from 'rxjs/operators'; import { Role } from "@app/core/common/enum/role"; import { DmpInvitationDialogComponent } from '../invitation/dmp-invitation-dialog.component'; -import { MultipleChoiceDialogModule } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.module'; import { MultipleChoiceDialogComponent } from '@common/modules/multiple-choice-dialog/multiple-choice-dialog.component'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; -import { Oauth2DialogComponent } from '@app/ui/misc/oauth2-dialog/oauth2-dialog.component'; import { Oauth2DialogService } from '@app/ui/misc/oauth2-dialog/service/oauth2-dialog.service'; -import { isNullOrUndefined } from 'util'; import { UserService } from '@app/core/services/user/user.service'; import { Location } from '@angular/common'; import { FormGroup, FormArray, FormControl } from '@angular/forms'; import { LockService } from '@app/core/services/lock/lock.service'; -import { ReturnStatement, ConditionalExpr } from '@angular/compiler'; -import { LockModel } from '@app/core/model/lock/lock.model'; -import { Guid } from '@common/types/guid'; import { VersionListingModel } from '@app/core/model/version/version-listing.model'; import { CloneDialogComponent } from '../clone/clone-dialog/clone-dialog.component'; import { DmpModel } from '@app/core/model/dmp/dmp'; @@ -43,11 +37,10 @@ import { FunderFormModel } from '../editor/grant-tab/funder-form-model'; import { ProjectFormModel } from '../editor/grant-tab/project-form-model'; import { GrantTabModel } from '../editor/grant-tab/grant-tab-model'; import { ExtraPropertiesFormModel } from '../editor/general-tab/extra-properties-form.model'; -import { DatasetWizardEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; -import { StartNewDatasetDialogComponent } from '../start-new-dataset-dialogue/start-new-dataset-dialog.component'; import { StartNewDmpDialogComponent } from '../start-new-dmp-dialogue/start-new-dmp-dialog.component'; import { HttpClient } from '@angular/common/http'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component'; @Component({ selector: 'app-dmp-overview', @@ -72,7 +65,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { version: VersionListingModel; private oauthLock: boolean; - @ViewChild('doi', { static: false }) + @ViewChild('doi') doi: ElementRef; formGroup: FormGroup; @@ -511,7 +504,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { maxWidth: '600px', restoreFocus: false, data: { - message: this.language.instant(message), + message: message ? this.language.instant(message) : this.language.instant('GENERAL.ERRORS.HTTP-REQUEST-ERROR'), titles: [this.language.instant('DMP-OVERVIEW.MULTIPLE-DIALOG.ZENODO-LOGIN'), this.language.instant('DMP-OVERVIEW.MULTIPLE-DIALOG.USE-DEFAULT')] } }); @@ -781,7 +774,15 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit { checkLockStatus(id: string) { this.lockService.checkLockStatus(id).pipe(takeUntil(this._destroyed)) - .subscribe(lockStatus => this.lockStatus = lockStatus); + .subscribe(lockStatus => { + this.lockStatus = lockStatus + if(lockStatus){ + this.dialog.open(PopupNotificationDialogComponent,{data:{ + title:this.language.instant('DMP-OVERVIEW.LOCKED-DIALOG.TITLE'), + message:this.language.instant('DMP-OVERVIEW.LOCKED-DIALOG.MESSAGE') + }, maxWidth:'30em'}); + } + }); } getUserFromDMP(): any { diff --git a/dmp-frontend/src/app/ui/dmp/start-new-dataset-dialogue/start-new-dataset-dialog.component.ts b/dmp-frontend/src/app/ui/dmp/start-new-dataset-dialogue/start-new-dataset-dialog.component.ts index 0d4d119d4..8a86bc52b 100644 --- a/dmp-frontend/src/app/ui/dmp/start-new-dataset-dialogue/start-new-dataset-dialog.component.ts +++ b/dmp-frontend/src/app/ui/dmp/start-new-dataset-dialogue/start-new-dataset-dialog.component.ts @@ -27,7 +27,7 @@ export class StartNewDatasetDialogComponent extends BaseComponent { initialItems: (extraData) => this.searchDmp(''), displayFn: (item) => this.getDatasetDisplay(item), titleFn: (item) => item['label'], - subtitleFn: (item) => this.language.instant('DATASET-WIZARD.FIRST-STEP.SUB-TITLE') + new Date(item['creationTime']).toISOString() + subtitleFn: (item) => {try{return this.language.instant('DATASET-WIZARD.FIRST-STEP.SUB-TITLE') + new Date(item['creationTime']).toISOString();}catch{return '-';}} }; constructor( diff --git a/dmp-frontend/src/app/ui/dmp/wizard/listing/dmp-wizard-dataset-listing.component.ts b/dmp-frontend/src/app/ui/dmp/wizard/listing/dmp-wizard-dataset-listing.component.ts index 351dea94f..98a5dbf7e 100644 --- a/dmp-frontend/src/app/ui/dmp/wizard/listing/dmp-wizard-dataset-listing.component.ts +++ b/dmp-frontend/src/app/ui/dmp/wizard/listing/dmp-wizard-dataset-listing.component.ts @@ -22,9 +22,9 @@ import { takeUntil } from 'rxjs/operators'; }) export class DmpWizardDatasetListingComponent extends BaseComponent implements OnInit { - @ViewChild(MatPaginator, { static: false }) _paginator: MatPaginator; - @ViewChild(MatSort, { static: false }) sort: MatSort; - @ViewChild(DatasetCriteriaComponent, { static: false }) criteria: DatasetCriteriaComponent; + @ViewChild(MatPaginator) _paginator: MatPaginator; + @ViewChild(MatSort) sort: MatSort; + @ViewChild(DatasetCriteriaComponent) criteria: DatasetCriteriaComponent; titlePrefix: String; diff --git a/dmp-frontend/src/app/ui/misc/criteria/base-criteria.component.ts b/dmp-frontend/src/app/ui/misc/criteria/base-criteria.component.ts index c40532065..ac1926486 100644 --- a/dmp-frontend/src/app/ui/misc/criteria/base-criteria.component.ts +++ b/dmp-frontend/src/app/ui/misc/criteria/base-criteria.component.ts @@ -1,8 +1,9 @@ -import { OnInit } from '@angular/core'; +import { OnInit, Directive } from '@angular/core'; import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms'; import { BaseComponent } from '@common/base/base.component'; import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; +@Directive() export class BaseCriteriaComponent extends BaseComponent implements OnInit { public refreshCallback: Function = null; diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-field/form-composite-field.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-field/form-composite-field.component.ts index 9776cf201..2cd1cc02f 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-field/form-composite-field.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-field/form-composite-field.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { FormArray, FormGroup, AbstractControl } from '@angular/forms'; +import { FormArray, FormGroup, AbstractControl, FormArrayName } from '@angular/forms'; import { DatasetDescriptionCompositeFieldEditorModel, DatasetDescriptionFieldEditorModel } from '../../dataset-description-form.model'; import { ToCEntry } from '../../dataset-description.component'; import { VisibilityRulesService } from '../../visibility-rules/visibility-rules.service'; @@ -49,18 +49,41 @@ export class FormCompositeFieldComponent { } deleteCompositeFieldFormGroup() { + + const compositeFieldId = ((this.form.get('multiplicityItems') as FormArray).get(''+0) as FormGroup).getRawValue().id; + const fieldIds = (this.form.get('fields') as FormArray).controls.map(control => control.get('id').value) as string[]; + const numberOfItems = this.form.get('multiplicityItems').get('' + 0).get('fields').value.length; for (let i = 0; i < numberOfItems; i++) { const multiplicityItem = this.form.get('multiplicityItems').get('' + 0).get('fields').get('' + i).value; this.form.get('fields').get('' + i).patchValue(multiplicityItem); } ((this.form.get('multiplicityItems'))).removeAt(0); + + + this.visibilityRulesService.removeAllIdReferences(compositeFieldId); + fieldIds.forEach( x => this.visibilityRulesService.removeAllIdReferences(x)); } deleteMultipeFieldFromCompositeFormGroup() { const parent = this.form.parent; const index = (parent as FormArray).controls.indexOf(this.form); + + const currentId = this.form.get('id').value; + const fieldIds = (this.form.get('fields') as FormArray).controls.map(control => control.get('id').value) as string[]; + + + this.visibilityRulesService.removeAllIdReferences(currentId); + fieldIds.forEach(x => this.visibilityRulesService.removeAllIdReferences(x)); + (parent as FormArray).removeAt(index); + (parent as FormArray).controls.forEach((control, i)=>{ + try{ + control.get('ordinal').setValue(i); + }catch{ + throw 'Could not find ordinal'; + } + }); // ((this.form as AbstractControl)).removeAt(fildIndex); } } diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.html b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.html index 747b63432..fc710c640 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.html +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.html @@ -12,7 +12,16 @@
    -
    {{form.get('description').value}}
    -
    - {{form.get('extendedDescription').value}} -
    +
    + +
    +
    + {{'DATASET-EDITOR.QUESTION.EXTENDED-DESCRIPTION.VIEW-MORE' | translate}} +
    +
    +
    + + {{'DATASET-EDITOR.QUESTION.EXTENDED-DESCRIPTION.VIEW-LESS' | translate}} + +
    +
    diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.scss b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.scss index 3024dfe73..b6469481f 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.scss +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.scss @@ -21,3 +21,11 @@ h6 { text-transform: none; font-weight: 400; } + +.more { + text-decoration: underline; + color: #F7DD72; + cursor: pointer; + font-size: 1rem; + font-weight: 400; +} diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.ts index 4a9776b5b..797650d47 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-composite-title/form-composite-title.component.ts @@ -13,6 +13,8 @@ export class FormCompositeTitleComponent implements OnInit { @Input() isChild: Boolean = false; @Input() tocentry:ToCEntry; + public showExtendedDescription: boolean = false; + constructor() { } ngOnInit() { diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.html b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.html index e574948cc..3f12a64f7 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.html +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.html @@ -98,15 +98,28 @@
    - - {{'GENERAL.VALIDATION.REQUIRED' | translate}} + + + +
    + {{'GENERAL.VALIDATION.REQUIRED' | translate}} +
    +
    +
    diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts index 07be0c4c8..f6e0bcbe8 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-field/form-field.component.ts @@ -27,9 +27,9 @@ import { DataRepositoryCriteria } from '@app/core/query/data-repository/data-rep import { RegistryCriteria } from '@app/core/query/registry/registry-criteria'; import { ServiceCriteria } from '@app/core/query/service/service-criteria'; import { TagCriteria } from '@app/core/query/tag/tag-criteria'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { ExternalTagEditorModel } from '@app/ui/dataset/dataset-wizard/dataset-wizard-editor.model'; -import { MatChipInputEvent } from '@angular/material'; +import { MatChipInputEvent } from '@angular/material/chips'; import { ENTER, COMMA } from '@angular/cdk/keycodes'; import { DatasetIdModel } from '@app/core/model/dataset/dataset-id.model'; import { LocalFetchModel } from '@app/core/model/local-fetch/local-fetch.model'; diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section-inner/form-section-inner.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section-inner/form-section-inner.component.ts index b6298840a..c14967308 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section-inner/form-section-inner.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section-inner/form-section-inner.component.ts @@ -58,8 +58,9 @@ export class FormSectionInnerComponent implements OnInit, OnChanges { addMultipleField(fieldsetIndex: number) { const compositeFieldToBeCloned = (this.form.get('compositeFields').get('' + fieldsetIndex) as FormGroup).getRawValue(); - const compositeField: DatasetDescriptionCompositeFieldEditorModel = new DatasetDescriptionCompositeFieldEditorModel().cloneForMultiplicity(compositeFieldToBeCloned); - ((this.form.get('compositeFields').get('' + fieldsetIndex).get('multiplicityItems'))).push(compositeField.buildForm()); + const multiplicityItemsArray = ((this.form.get('compositeFields').get('' + fieldsetIndex).get('multiplicityItems'))); + const compositeField: DatasetDescriptionCompositeFieldEditorModel = new DatasetDescriptionCompositeFieldEditorModel().cloneForMultiplicity(compositeFieldToBeCloned, multiplicityItemsArray.length); + multiplicityItemsArray.push(compositeField.buildForm()); } deleteCompositeFieldFormGroup(compositeFildIndex: number) { @@ -72,7 +73,15 @@ export class FormSectionInnerComponent implements OnInit, OnChanges { } deleteMultipeFieldFromCompositeFormGroup(compositeFildIndex: number, fildIndex: number) { - ((this.form.get('compositeFields').get('' + compositeFildIndex).get('multiplicityItems'))).removeAt(fildIndex); + const multiplicityItemsArray = ((this.form.get('compositeFields').get('' + compositeFildIndex).get('multiplicityItems'))) + multiplicityItemsArray.removeAt(fildIndex); + multiplicityItemsArray.controls.forEach((control, i)=>{ + try{ + control.get('ordinal').setValue(i); + }catch{ + throw 'Could not find ordinal'; + } + }); } // isElementVisible(fieldSet: CompositeField): boolean { diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section.component.html b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section.component.html index b9926dd1c..5408c7bb4 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section.component.html +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section.component.html @@ -7,7 +7,7 @@ -
    {{form.get('description').value}}
    +
    @@ -32,7 +32,7 @@
    -
    @@ -97,7 +97,7 @@
    -
    @@ -135,4 +135,4 @@ - \ No newline at end of file + diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section.component.ts index 33eb6c076..1b2260e2f 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/components/form-section/form-section.component.ts @@ -6,6 +6,9 @@ import { DatasetDescriptionSectionEditorModel, DatasetDescriptionCompositeFieldE import { FormCompositeFieldComponent } from '../form-composite-field/form-composite-field.component'; import { LinkToScroll } from '../../tableOfContentsMaterial/table-of-contents'; import { ToCEntry, ToCEntryType } from '../../dataset-description.component'; +import { VisibilityRuleSource } from '../../visibility-rules/models/visibility-rule-source'; +import { VisibilityRule } from '../../visibility-rules/models/visibility-rule'; +import { Rule } from '@app/core/model/dataset-profile-definition/rule'; @Component({ @@ -70,8 +73,158 @@ export class FormSectionComponent implements OnInit, OnChanges { addMultipleField(fieldsetIndex: number) { const compositeFieldToBeCloned = (this.form.get('compositeFields').get('' + fieldsetIndex) as FormGroup).getRawValue(); - const compositeField: DatasetDescriptionCompositeFieldEditorModel = new DatasetDescriptionCompositeFieldEditorModel().cloneForMultiplicity(compositeFieldToBeCloned); - ((this.form.get('compositeFields').get('' + fieldsetIndex).get('multiplicityItems'))).push(compositeField.buildForm()); + const multiplicityItemsArray = ((this.form.get('compositeFields').get('' + fieldsetIndex).get('multiplicityItems'))); + + const ordinal = multiplicityItemsArray.length? multiplicityItemsArray.controls.reduce((ordinal, currentControl)=>{ + const currentOrdinal = currentControl.get('ordinal').value as number; + + if(currentOrdinal>= ordinal){ + return currentOrdinal +1; + } + return ordinal as number; + },0) : 0; + const idMappings = [] as {old: string, new: string}[]; + const compositeField: DatasetDescriptionCompositeFieldEditorModel = new DatasetDescriptionCompositeFieldEditorModel().cloneForMultiplicity(compositeFieldToBeCloned, ordinal ,idMappings); + + + + + // ** COMPOSITE FIELD IS SHOWN OR HIDDEN FROM ANOTHER ELEMENT + const compositeFieldVisibilityDependencies = this.visibilityRulesService.getVisibilityDependency(compositeFieldToBeCloned); + if(compositeFieldVisibilityDependencies && compositeFieldVisibilityDependencies.length){ + + compositeFieldVisibilityDependencies.forEach(x =>{ + const visRule: Rule = { + targetField: compositeField.id, + sourceField: x.sourceControlId, + requiredValue: x.sourceControlValue, + type: '' + } + this.visibilityRulesService.addNewRule(visRule); + }); + } + + // console.log('idMappings ', idMappings); + compositeFieldToBeCloned.fields.forEach(element => { + // console.log(this.visibilityRulesService.getVisibilityDependency(element.id)); + const dependency = this.visibilityRulesService.getVisibilityDependency(element.id); + + + + try{ + + if(dependency && dependency.length){ + + // * INNER VISIBILITY DEPENDENCIES + // * IF INNER INPUT HIDES ANOTHER INNER INPUT + + const innerDependency = compositeFieldToBeCloned.fields.reduce((innerD, currentElement )=>{ + + const innerDependecies = dependency.filter(d => d.sourceControlId === currentElement.id); + return[...innerD, ...innerDependecies]; + },[]) as VisibilityRuleSource[]; + + if(innerDependency.length){ + //Build visibility source + const updatedRules = innerDependency.map(x => { + const newId = idMappings.find(y=> y.old === x.sourceControlId); + return {...x, sourceControlId: newId.new}; + }); + // const visRule: VisibilityRule = { + // targetControlId: idMappings.find(x => x.old === element.id).new, + // sourceVisibilityRules: updatedRules + // } + + + const rules = updatedRules.map(x => { + return { + requiredValue: x.sourceControlValue, + sourceField: x.sourceControlId, + targetField: idMappings.find(l=> l.old === element.id).new, + type: '' + } as Rule; + }); + + rules.forEach(rule =>{ + this.visibilityRulesService.addNewRule(rule); + }) + + // this.visibilityRulesService.appendVisibilityRule(visRule); + } + + } + + + // * OUTER DEPENDENCIES + + // * IF INNER INPUT HIDES OUTER INPUTS + const innerIds = idMappings.map(x => x.old) as string[]; + + const outerTargets = this.visibilityRulesService.getVisibilityTargets(element.id).filter( x=> !innerIds.includes(x)); + + outerTargets.forEach(target =>{ + + const outerRules = (this.visibilityRulesService.getVisibilityDependency(target) as VisibilityRuleSource[]).filter(x => x.sourceControlId === element.id); + const updatedRules = outerRules.map(x => { + return {...x ,sourceControlId: idMappings.find(y => y.old === element.id).new}; + }); + + // const visRule: VisibilityRule = { + // targetControlId: target, + // sourceVisibilityRules: updatedRules + // } + + + const rules = updatedRules.map(x =>{ + return { + requiredValue: x.sourceControlValue, + sourceField: x.sourceControlId, + targetField: target, + type: '' + } as Rule; + }) + + rules.forEach(rule =>{ + this.visibilityRulesService.addNewRule(rule); + }) + + // this.visibilityRulesService.appendVisibilityRule(visRule); + + }); + + + + + + // * IF INNER INPUT IS HIDDEN BY OUTER INPUT + + if(dependency && dependency.length){ + const fieldsThatHideInnerElement = dependency.filter(x => !innerIds.includes(x.sourceControlId)); + + if( fieldsThatHideInnerElement.length){ + fieldsThatHideInnerElement.forEach(x =>{ + const visRule: Rule = { + targetField: idMappings.find(l => l.old === element.id).new, + sourceField: x.sourceControlId, + requiredValue: x.sourceControlValue, + type: '' + } + const shouldBeVisibile = this.visibilityRulesService.checkTargetVisibilityProvidedBySource(x.sourceControlId, element.id); + this.visibilityRulesService.addNewRule(visRule, shouldBeVisibile); + }); + } + } + + + // console.log(`for ${element.id} targets are`, outerTargets); + } catch { + console.log('error'); + } + + }); + // console.log(this.visibilityRulesService); + // console.log(this.visibilityRulesService.getVisibilityDependency()); + multiplicityItemsArray.push(compositeField.buildForm()); } deleteCompositeFieldFormGroup(compositeFildIndex: number) { @@ -84,7 +237,15 @@ export class FormSectionComponent implements OnInit, OnChanges { } deleteMultipeFieldFromCompositeFormGroup(compositeFildIndex: number, fildIndex: number) { - ((this.form.get('compositeFields').get('' + compositeFildIndex).get('multiplicityItems'))).removeAt(fildIndex); + const multiplicityItemsArray = ((this.form.get('compositeFields').get('' + compositeFildIndex).get('multiplicityItems'))); + multiplicityItemsArray.removeAt(fildIndex); + multiplicityItemsArray.controls.forEach((control, i)=>{ + try{ + control.get('ordinal').setValue(i); + }catch{ + throw 'Could not find ordinal'; + } + }); } // isElementVisible(fieldSet: CompositeField): boolean { diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.component.ts index 2f7bac512..a3ce2ebae 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.component.ts @@ -13,7 +13,7 @@ import { BaseComponent } from '@common/base/base.component'; }) export class DatasetDescriptionFormComponent extends BaseComponent implements OnInit, AfterViewInit, OnChanges { - @ViewChild('stepper', { static: false }) stepper: MatHorizontalStepper; + @ViewChild('stepper') stepper: MatHorizontalStepper; @Input() path: string; @Input() form: FormGroup; @Input() visibilityRules: Rule[] = []; diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.model.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.model.ts index c76550e26..993b73227 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.model.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.model.ts @@ -194,13 +194,19 @@ export class DatasetDescriptionCompositeFieldEditorModel extends BaseFormModel { // return newItem; // } - cloneForMultiplicity(item: CompositeField): DatasetDescriptionCompositeFieldEditorModel { + cloneForMultiplicity(item: CompositeField, ordinal: number, idMappings:{old:string, new:string}[] = []): DatasetDescriptionCompositeFieldEditorModel { const newItem: DatasetDescriptionCompositeFieldEditorModel = new DatasetDescriptionCompositeFieldEditorModel(); - newItem.id = 'multiple_' + item.id + '_' + Guid.create(); + newItem.id = 'multiple_' + item.id + '_' + Guid.create() + '_' + ordinal; + + idMappings.push({old: item.id, new: newItem.id }); item.fields.forEach((field, index) => { - newItem.fields.push(new DatasetDescriptionFieldEditorModel().cloneForMultiplicity(field, newItem.id)); + + const clonedItem = new DatasetDescriptionFieldEditorModel().cloneForMultiplicity(field, newItem.id) + newItem.fields.push(clonedItem); + + idMappings.push({old: field.id, new: clonedItem.id}); }); - newItem.ordinal = item.ordinal; + newItem.ordinal = ordinal; return newItem; } } @@ -238,7 +244,7 @@ export class DatasetDescriptionFieldEditorModel extends BaseFormModel { if (item.multiplicity) this.multiplicity = new DatasetDescriptionMultiplicityEditorModel().fromModel(item.multiplicity); if (item.defaultValue) this.defaultValue = new DatasetDescriptionDefaultValueEditorModel().fromModel(item.defaultValue); this.value = item.value ? item.value : (this.defaultValue.value ? this.defaultValue.value : undefined); - if (this.viewStyle.renderStyle === 'checkBox') { this.value = this.value === 'true'; } + if (this.viewStyle.renderStyle === 'checkBox' && (item.value !== true)) { this.value = this.value === 'true'; } //Cover both posibilites of boolean true or false and string 'true' or 'false' if (item.multiplicityItems) { item.multiplicityItems.map(x => this.multiplicityItems.push(new DatasetDescriptionFieldEditorModel().fromModel(x))); } this.data = item.data; return this; diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.module.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.module.ts index cfc3ef0db..3fd0c411b 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.module.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description-form.module.ts @@ -14,6 +14,7 @@ import { ExternalSourcesModule } from '../external-sources/external-sources.modu import { DatasetDescriptionComponent } from './dataset-description.component'; import { FormProgressIndicationModule } from './components/form-progress-indication/form-progress-indication.module'; import { FormSectionInnerComponent } from './components/form-section/form-section-inner/form-section-inner.component'; +import {RichTextEditorModule} from "@app/library/rich-text-editor/rich-text-editor.module"; @NgModule({ @@ -22,7 +23,8 @@ import { FormSectionInnerComponent } from './components/form-section/form-sectio CommonFormsModule, AutoCompleteModule, ExternalSourcesModule, - FormProgressIndicationModule + FormProgressIndicationModule, + RichTextEditorModule ], declarations: [ DatasetDescriptionFormComponent, diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.html b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.html index 20bf08d41..94aea2f5e 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.html +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.html @@ -1,6 +1,7 @@ -
    - {{'DMP-EDITOR.DATASET-DESCRIPTION.INTRO' | translate}} -
    + + + +
    diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.ts index bcf78bf99..b3937f33a 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/dataset-description.component.ts @@ -1,7 +1,9 @@ import { AfterViewInit, Component, Input, OnChanges, OnInit, SimpleChanges, ViewChild, Output, EventEmitter } from '@angular/core'; import { AbstractControl, AbstractControlOptions, FormArray, FormGroup } from '@angular/forms'; -import { MatExpansionPanel } from '@angular/material'; +import { MatExpansionPanel } from '@angular/material/expansion'; import { MatHorizontalStepper } from '@angular/material/stepper'; +import { CompositeField } from '@app/core/model/dataset-profile-definition/composite-field'; +import { Field } from '@app/core/model/dataset-profile-definition/field'; import { Rule } from '@app/core/model/dataset-profile-definition/rule'; import { DatasetProfileTableOfContentsInternalSection } from '@app/ui/admin/dataset-profile/table-of-contents/table-of-contents-internal-section/table-of-contents-internal-section'; import { LinkToScroll } from '@app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents'; @@ -21,6 +23,7 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit @Input() path: string; @Input() visibilityRules: Rule[] = []; @Input() datasetProfileId: String; + @Input() datasetDescription: String; @Input() linkToScroll: LinkToScroll; @Output() formChanged: EventEmitter = new EventEmitter(); @Output() fieldsetFocusChange: EventEmitter = new EventEmitter(); @@ -41,7 +44,10 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit } ngOnInit() { - this.visibilityRulesService.buildVisibilityRules(this.visibilityRules, this.form); + this.tocentries = this.getTocEntries(); + const rules_to_append = this._enrichWithMultiplicityRules(this.tocentries); + + this.visibilityRulesService.buildVisibilityRules([...this.visibilityRules, ...rules_to_append ], this.form); // if (this.form) { // this.form.valueChanges @@ -52,7 +58,9 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit // } this.visibilityRulesInstance.emit(this.visibilityRulesService); - this.tocentries = this.getTocEntries(); + + + this.hiddenEntriesIds = this._findHiddenEntries(this.tocentries); this.visibilityRulesService.visibilityChange @@ -90,6 +98,161 @@ export class DatasetDescriptionComponent extends BaseComponent implements OnInit + private _enrichWithMultiplicityRules(tocentries: ToCEntry[]) : Rule[] { + if (tocentries){ + return tocentries.map(entry => { + if(entry.type === ToCEntryType.Field) return []; // * TODO Me to tora implementation den tha ftasei pote edo + + + if(entry.type === ToCEntryType.FieldSet){ + // if(multiplicity: ) + try { + // TODO OTAN KANW HIDE MULTIPLE PEDIO TOTE STO ON SHOW HANO TA VALUES (AUTO MPOREI NA EINAI KAI LEGIT) ('NA DOUME AN ONTOS DIAGRAFONTAI I APLA DEN TA DEIXNOUME') + // * UPDATE KANEI DESTROY TO COMPONENT H NGIF . PITHANOTATA NA XREIASTEI NA TO KANOUME HIDDEN AN THELOUME KATI ALLO + const multiplicity = entry.form.get('multiplicity').value; + if( (multiplicity.max > 1 ) && (multiplicity.min> 0) && (multiplicity.max >= multiplicity.min)){ // has valid multiplicity + return this._createAndAppendVisibilityRule(entry); + } + } catch { + + } + return []; + } + + if(entry.subEntries){ + return this._enrichWithMultiplicityRules(entry.subEntries); + } + }) + .reduce((r,c)=>{ return [...c, ...r]},[]); + } + return []; + } + + private _createAndAppendVisibilityRule(entry: ToCEntry): Rule[]{ + + + const rules_to_append = []; + + if(entry && (entry.type === ToCEntryType.FieldSet)){ + + //childs that are either target or source + const childIdsWithVisRules = (entry.form.get('fields') as FormArray).controls.reduce((all, s) => + { + const sval = s.value as Field; + return this.visibilityRules.find(x => (x.targetField === sval.id) || (x.sourceField === sval.id)) ? [...all, sval] : all; + },[]) as Field[]; + + + const innerCompositeFieldOriginalIds = (entry.form.get('fields') as FormArray).controls.map( x=> x.get('id').value) as string[]; + + //multiplicity items + const multiplicityItemsValue = entry.form.get('multiplicityItems').value as CompositeField[]; + + + // ********* FIELDS OF FIELDSET ARE EITHER TARGETS OR SOURCES ***** + + + if( childIdsWithVisRules.length && multiplicityItemsValue && multiplicityItemsValue.length ){ + //check each multiplicity item composite field + multiplicityItemsValue.forEach( mi =>{ + + const multiplicityCompositeFieldIds = mi.fields.map(x => x.id); + const idMappings = multiplicityCompositeFieldIds.map(x => { + return { + original: innerCompositeFieldOriginalIds.find( l => x.includes(l)), + multiplicityIdValue: x + } + }) as {original: string, multiplicityIdValue: string}[]; + + //each field of mutliplicity item + mi.fields.forEach(field =>{ + + + //get original visibility rules (original field) + + //original id + const original_id = childIdsWithVisRules.find(x=> field.id.includes(x.id)).id; + + + //get vis rules + + //as source + const original_as_source = this.visibilityRules.filter( x => x.sourceField === original_id); + const original_as_target = this.visibilityRules.filter( x => x.targetField === original_id); + + + + if(original_as_source.length){ + + //inner dependencies + const innerDep = original_as_source.filter(x => innerCompositeFieldOriginalIds.includes(x.targetField)); + innerDep.forEach(x =>{ + const newRule = {...x, sourceField: field.id, targetField: idMappings.find(l => l.original === x.targetField).multiplicityIdValue} as Rule; + rules_to_append.push(newRule); + }) + + + //outer dependencies + const outerDep = original_as_source.filter(x => !innerCompositeFieldOriginalIds.includes(x.targetField)); + outerDep.forEach(x =>{ + const newRule = {...x, sourceField: field.id}; + rules_to_append.push(newRule); + }) + } + + + + if( original_as_target.length){ + + //inner dependencies + const innerDep = original_as_target.filter( x=> innerCompositeFieldOriginalIds.includes(x.sourceField)); + innerDep.forEach(x =>{ + const newRule = {...x, targetField: field.id, sourceField: idMappings.find(l => l.original === x.sourceField).multiplicityIdValue} as Rule; + rules_to_append.push(newRule); + }) + + //outer dependencies + const outerDep = original_as_target.filter( x=> !innerCompositeFieldOriginalIds.includes(x.sourceField)); + outerDep.forEach(x=>{ + const newRule = {...x, targetField: field.id} as Rule; + rules_to_append.push(newRule); + }) + } + + }) + }); + } + + + + + // ** FIELDSET ITSELF IS TARGET + // ** source it can never be + + const compositeFieldAsTargetRules = this.visibilityRules.filter(x => x.targetField === entry.id); + const idCompositeFieldMappings = multiplicityItemsValue.map(x =>{ + return { + originalValue: entry.id, + newValue:x.id + } + }) as {originalValue: string, newValue: string}[]; + + + if(compositeFieldAsTargetRules.length){ + + compositeFieldAsTargetRules.forEach(x =>{ + idCompositeFieldMappings.forEach(l=>{ + const newRule = {...x, targetField: l.newValue}; + rules_to_append.push(newRule); + }); + }); + } + + + } + + return rules_to_append; + } private _buildRecursively(form: FormGroup,whatAmI:ToCEntryType):ToCEntry{ if(!form) return null; @@ -251,4 +414,4 @@ export enum ToCEntryType { Section = 1, FieldSet = 2, Field = 3 -} \ No newline at end of file +} diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/form-focus/form-focus.service.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/form-focus/form-focus.service.ts index 6ee8a4930..2a8491311 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/form-focus/form-focus.service.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/form-focus/form-focus.service.ts @@ -3,6 +3,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { Pair } from '../../../../../common/types/pair'; import { CompositeField } from '../../../../core/model/dataset-profile-definition/composite-field'; import { VisibilityRulesService } from '../visibility-rules/visibility-rules.service'; +import { groupBy } from 'lodash'; @Injectable() export class FormFocusService { @@ -22,7 +23,7 @@ export class FormFocusService { } focusNext(field: CompositeField) { - const flattenedCompositeFields = this.compositeFields.groupBy(x => x.right) + const flattenedCompositeFields = groupBy(this.compositeFields, x => x.right) .map(x => x.reduce((first: Pair, second: Pair) => (new Pair(first.left.concat(second.left), first.right)))); const page = flattenedCompositeFields.filter(x => x['left'].map(y => y.id).indexOf(field.id) !== -1)[0]; diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.ts index 5e65e1c16..9724daff3 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents-internal/table-of-contents-internal.ts @@ -142,8 +142,11 @@ export class TableOfContentsInternal implements OnInit { return entry.subEntries.some(_ => this.invalidChildsVisible(_)); } if(entry.type === this.tocEntryTypeEnum.FieldSet){ + const id = entry.form.get('id').value + if(!this.visibilityRulesService.checkElementVisibility(id)){ + return false; + } const fieldsArray = entry.form.get('fields') as FormArray; - const hasError = !fieldsArray.controls.every(field=>{//every invalid field should be invisible if(field.invalid){ const id = field.get('id').value; diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.html b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.html index d4ca182f3..6a10aeaac 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.html +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/tableOfContentsMaterial/table-of-contents.html @@ -27,7 +27,7 @@
    - this.internalTable.invalidChildsVisible(e)); + } + } export interface LinkToScroll { diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/visibility-rules/models/visibility-rules-context.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/visibility-rules/models/visibility-rules-context.ts index 3df90cf19..2eba9ff10 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/visibility-rules/models/visibility-rules-context.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/visibility-rules/models/visibility-rules-context.ts @@ -21,10 +21,22 @@ export class VisibilityRulesContext { }); } - private addToVisibilityRulesContext(item: Rule): void { + public addToVisibilityRulesContext(item: Rule): void { for (let i = 0; i < this.rules.length; i++) { if (this.rules[i].targetControlId === item.targetField) { - this.rules[i].sourceVisibilityRules.push({ sourceControlId: item.sourceField, sourceControlValue: item.requiredValue }); + + const newRule = { sourceControlId: item.sourceField, sourceControlValue: item.requiredValue }; + const ruleExists = this.rules[i].sourceVisibilityRules.find(x =>{ + return Object.keys(x).reduce((exact, key)=>{ + if(!exact) return false; + return x[key] === newRule[key]; + },true); + }) + + if(!ruleExists){ + this.rules[i].sourceVisibilityRules.push(newRule); + } + return; } } diff --git a/dmp-frontend/src/app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service.ts b/dmp-frontend/src/app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service.ts index e730193c7..d902e51a3 100644 --- a/dmp-frontend/src/app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service.ts +++ b/dmp-frontend/src/app/ui/misc/dataset-description-form/visibility-rules/visibility-rules.service.ts @@ -1,21 +1,26 @@ import { ApplicationRef, Injectable, NgZone } from '@angular/core'; -import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms'; +import { AbstractControl, FormArray, FormGroup } from '@angular/forms'; import { DatasetProfileFieldViewStyle } from '@app/core/common/enum/dataset-profile-field-view-style'; +import { isNumeric } from '@app/utilities/enhancers/utils'; import { Subject } from 'rxjs'; -import { isNumeric } from 'rxjs/internal/util/isNumeric'; import { Rule } from '../../../../core/model/dataset-profile-definition/rule'; import { VisibilityRule } from './models/visibility-rule'; +import { VisibilityRuleSource } from './models/visibility-rule-source'; import { VisibilityRulesContext } from './models/visibility-rules-context'; @Injectable() export class VisibilityRulesService { + private readonly VISIBILITY_RULE_LOGIC: 'OR' | 'AND' = 'OR'; + private readonly DEFAULTVISIBILITY = false; private visibilityRuleContext: VisibilityRulesContext; private form: AbstractControl; private elementVisibilityMap = new Map(); + private elementComputationalMap = new Map>(); /// keep saved the values of each form control validity value private _changeMade$ = new Subject(); + constructor( public applicationReference: ApplicationRef, public ngZone: NgZone @@ -37,48 +42,115 @@ export class VisibilityRulesService { public updateValueAndVisibility(id: string, value: any) { const visibilityRules = this.visibilityRuleContext.rules.filter(item => item.sourceVisibilityRules.filter(source => source.sourceControlId === id).length > 0); - visibilityRules.forEach(item => this.evaluateVisibility(item, value)); + visibilityRules.forEach(item => this.evaluateVisibility(item, value, id)); } - private evaluateVisibility(visibilityRule: VisibilityRule, value: any) { + private evaluateVisibility(visibilityRule: VisibilityRule, value: any, sourceId: string) {// source controlId is the same - if (value instanceof Array){ + const targetId = visibilityRule.targetControlId; + const visibilityMap = this.elementComputationalMap.get(targetId) ? this.elementComputationalMap.get(targetId) : new Map(); - const parsedSourceControlValues = visibilityRule.sourceVisibilityRules.map(e=>this.parseValue(e.sourceControlValue)); - const parsedValues = value.map(e=>this.parseValue(e)); - const isVisible = parsedValues.map(v=>parsedSourceControlValues.includes(v)).reduce((acc,current)=> acc|| current, false); + if (value instanceof Array) { - - if(isVisible){ - this._emitChangesIfNeeded(visibilityRule.targetControlId, true); - this.elementVisibilityMap.set(visibilityRule.targetControlId, true); - return; - } + const parsedSourceControlValues = visibilityRule.sourceVisibilityRules.map(e => this.parseValue(e.sourceControlValue)); + const parsedValues = value.map(e => this.parseValue(e)); + const isVisible = parsedValues.map(v => parsedSourceControlValues.includes(v)).reduce((acc, current) => acc || current, false); + + + // if(isVisible){ + // this._emitChangesIfNeeded(visibilityRule.targetControlId, true); + // this.elementVisibilityMap.set(visibilityRule.targetControlId, true); + // return; + // } + visibilityMap.set(sourceId, isVisible); + + } else { + const visibilityDependencySource = visibilityRule.sourceVisibilityRules.filter(x => x.sourceControlId === sourceId); + + const shouldBeVisible = visibilityDependencySource.reduce((isVisible, x) => { + + const shouldBeHidden = value !== null && (this.parseValue(value) !== this.parseValue(x.sourceControlValue)); + return this.VISIBILITY_RULE_LOGIC === 'OR'? (isVisible || !shouldBeHidden) : (isVisible && !shouldBeHidden); + // if(value !== null && ) + }, this.VISIBILITY_RULE_LOGIC === 'AND'); + visibilityMap.set(sourceId, shouldBeVisible); } - for (let i = 0; i < visibilityRule.sourceVisibilityRules.length; i++) { - if (value != null && (this.parseValue(value) !== this.parseValue(visibilityRule.sourceVisibilityRules[i].sourceControlValue))) { - this._emitChangesIfNeeded(visibilityRule.targetControlId, false); - this.elementVisibilityMap.set(visibilityRule.targetControlId, false); - this.resetControlWithId(this.form, visibilityRule.targetControlId); - //this.updateValueAndVisibility(visibilityRule.targetControlId, null); - // this.clearValues(targetPathKey); - return; - } + + this.elementComputationalMap.set(targetId, visibilityMap);// unnessecary + + + const isVisible = this._computeVisibility(targetId); + this._emitChangesIfNeeded(targetId, isVisible); + this.elementVisibilityMap.set(targetId, isVisible); + if (!isVisible) { + this.resetControlWithId(this.form, targetId); } - this._emitChangesIfNeeded(visibilityRule.targetControlId, true); - this.elementVisibilityMap.set(visibilityRule.targetControlId, true); - //this.updateValueAndVisibility(visibilityRule.targetControlId, null); + + + // for (let i = 0; i < visibilityRule.sourceVisibilityRules.length; i++) { + // if (value != null && (this.parseValue(value) !== this.parseValue(visibilityRule.sourceVisibilityRules[i].sourceControlValue))) { + // this._emitChangesIfNeeded(visibilityRule.targetControlId, false); + // this.elementVisibilityMap.set(visibilityRule.targetControlId, false); + // this.resetControlWithId(this.form, visibilityRule.targetControlId); + // //this.updateValueAndVisibility(visibilityRule.targetControlId, null); + // // this.clearValues(targetPathKey); + // return; + // } + // } + // this._emitChangesIfNeeded(visibilityRule.targetControlId, true); + // this.elementVisibilityMap.set(visibilityRule.targetControlId, true); + + // this.updateValueAndVisibility(visibilityRule.targetControlId, null); + } + + + private _computeVisibility(targetId: string): boolean { + const visibilityMap = this.elementComputationalMap.get(targetId); + const values = visibilityMap.values(); + let currentVal = values.next(); + let visibilityValues: boolean[] = []; + while (!currentVal.done) { + visibilityValues.push(currentVal.value); + currentVal = values.next(); + } + + + if (visibilityValues.length) { + return visibilityValues.reduce((r, c) => { + if (this.VISIBILITY_RULE_LOGIC === 'OR') { + return r || c; + } else { + return r && c; + } + }, visibilityValues[0]); + } + + return this.DEFAULTVISIBILITY; } private resetVisibilityRules() { this.elementVisibilityMap.clear(); this.elementVisibilityMap = new Map(); + this.elementComputationalMap.clear(); + this.elementComputationalMap = new Map>(); + this._populateComputationMap(); /// !IMPORTANT FOR THE AND LOGIC this._changeMade$.next(); } + private _populateComputationMap(): void { + this.visibilityRuleContext.rules.forEach(rule => { + const targetId = rule.targetControlId; + const visibilityMap = this.elementComputationalMap.get(targetId) ? this.elementComputationalMap.get(targetId) : new Map(); + rule.sourceVisibilityRules.forEach(vr => { + visibilityMap.set(vr.sourceControlId, this.DEFAULTVISIBILITY); + }); + this.elementComputationalMap.set(targetId, visibilityMap); + }); + } + parseValue(value: any) { if (typeof value === 'string') { if (isNumeric(value)) { return value; } @@ -147,9 +219,9 @@ export class VisibilityRulesService { private resetFieldFormGroup(formGroup: FormGroup) { const renderStyle = formGroup.getRawValue().viewStyle.renderStyle; - if(renderStyle ===DatasetProfileFieldViewStyle.Validation || renderStyle === DatasetProfileFieldViewStyle.DatasetIdentifier){ - formGroup.get('value').setValue({identifier:'',type:'' }); - }else{ + if (renderStyle === DatasetProfileFieldViewStyle.Validation || renderStyle === DatasetProfileFieldViewStyle.DatasetIdentifier) { + formGroup.get('value').setValue({ identifier: '', type: '' }); + } else { formGroup.get('value').setValue(formGroup.get('defaultValue').value ? this.parseValue(formGroup.get('defaultValue').value.value) : undefined); } @@ -161,16 +233,139 @@ export class VisibilityRulesService { }); (formGroup.get('multiplicityItems') as FormArray).controls.splice(0); } - private _emitChangesIfNeeded(id:string, valueToBeSet: boolean){ - if(this.elementVisibilityMap.has(id)){ - if(this.elementVisibilityMap.get(id) != valueToBeSet){ + private _emitChangesIfNeeded(id: string, valueToBeSet: boolean) { + if (this.elementVisibilityMap.has(id)) { + if (this.elementVisibilityMap.get(id) != valueToBeSet) { this._changeMade$.next(); } - }else{ + } else { this._changeMade$.next(); } } - public get visibilityChange(){ + public get visibilityChange() { return this._changeMade$.asObservable(); } + public getVisibilityDependency(targetId: string): VisibilityRuleSource[] | null { + return this.visibilityRuleContext.rules.reduce((hasDependency, rule) => { + if (hasDependency) return hasDependency; + + if (rule.targetControlId === targetId) { + return rule.sourceVisibilityRules; + } + + return null; + }, null) as VisibilityRuleSource[]; + } + + public getVisibilityTargets(sourceId: string): string[] { + return this.visibilityRuleContext.rules.filter(x => { + const result = x.sourceVisibilityRules.filter(y => y.sourceControlId === sourceId); + return result.length; + }).map(x => x.targetControlId); + } + + // public appendVisibilityRule(rule: VisibilityRule): void{ + + // const existingTargetRule = this.visibilityRuleContext.rules.find( r => r.targetControlId === rule.targetControlId); + + // if(existingTargetRule){ + // rule.sourceVisibilityRules.forEach(svr =>{ + // existingTargetRule.sourceVisibilityRules.push(svr); + // }); + // }else{ + // this.visibilityRuleContext.rules.push(rule); + // } + + + // } + + + //removes rule that has the specific id either as a source either as a target + public removeAllIdReferences(id: string): void { + + // * Remove from visibility rues and visibility rules context + + //remove as a target + const temp = this.visibilityRuleContext.rules.map((x, i) => (x.targetControlId === id) ? i : null); + const indexes = temp.filter(x => x !== null); + indexes.reverse().forEach(index => this.visibilityRuleContext.rules.splice(index, 1)); + this.elementVisibilityMap.delete(id); + + + + //remove as a source + const tbd = this.visibilityRuleContext.rules.reduce((to_be_deleted, rule, ruleIdx) => { + const idxs = rule.sourceVisibilityRules.map((x, i) => (x.sourceControlId === id) ? i : null).filter(x => x !== null); + idxs.reverse().forEach(index => rule.sourceVisibilityRules.splice(index, 1)); + + if (!rule.sourceVisibilityRules.length) { + to_be_deleted.push(ruleIdx); + } + return to_be_deleted + }, []); + + + //clean up empty + tbd.reverse().forEach(index => { + this.visibilityRuleContext.rules.splice(index, 1); + }); + + + + // * Remove from computational map + + // as a target + if (this.elementComputationalMap.get(id)) { + this.elementComputationalMap.delete(id); + } + + + // as a source + const keyIterator = this.elementComputationalMap.keys(); + let currentKey = keyIterator.next(); + + + while (!currentKey.done) { + const currentVals = this.elementComputationalMap.get(currentKey.value); + currentVals.delete(id); + currentKey = keyIterator.next(); + } + } + + + public addNewRule(rule: Rule, currentVisibility = this.DEFAULTVISIBILITY): void { + const targetId = rule.targetField; + const sourceId = rule.sourceField; + + this.visibilityRuleContext.addToVisibilityRulesContext(rule); + + + let visibilityMap = this.elementComputationalMap.get(targetId); + + if(!visibilityMap){ + visibilityMap = new Map(); + this.elementComputationalMap.set(targetId, visibilityMap); + } + + visibilityMap.set(sourceId, currentVisibility); + const isVisible = this._computeVisibility(targetId); + + this._emitChangesIfNeeded(targetId, isVisible); + this.elementVisibilityMap.set(targetId, isVisible); + } + + + /** + * Check what sourceId hides or shows the target field + * return true if no rule found + */ + public checkTargetVisibilityProvidedBySource(sourceId: string, targetId: string): boolean{ + + const computationalMap = this.elementComputationalMap.get(targetId); + if(computationalMap){ + return !!computationalMap.get(sourceId); + } + + return true; + } } diff --git a/dmp-frontend/src/app/ui/misc/navigation/navigation.component.scss b/dmp-frontend/src/app/ui/misc/navigation/navigation.component.scss index 7120dc3c1..0b4651e59 100644 --- a/dmp-frontend/src/app/ui/misc/navigation/navigation.component.scss +++ b/dmp-frontend/src/app/ui/misc/navigation/navigation.component.scss @@ -1,3 +1,4 @@ +@use '~@angular/material' as mat; @mixin navigation-component-theme($theme) { $accent: map-get($theme, accent); @@ -10,7 +11,7 @@ z-index: 1000; .active { - color: mat-color($accent); + color: mat.get-color-from-palette($accent); } .user-label { diff --git a/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.ts b/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.ts index 346afc5d6..4457a9a48 100644 --- a/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.ts +++ b/dmp-frontend/src/app/ui/misc/oauth2-dialog/oauth2-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Inject } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { ActivatedRoute, Params } from '@angular/router'; import { BaseComponent } from '@common/base/base.component'; import { takeUntil } from 'rxjs/operators'; diff --git a/dmp-frontend/src/app/ui/misc/oauth2-dialog/service/oauth2-dialog.service.ts b/dmp-frontend/src/app/ui/misc/oauth2-dialog/service/oauth2-dialog.service.ts index e0b3ee9e5..8b556b63d 100644 --- a/dmp-frontend/src/app/ui/misc/oauth2-dialog/service/oauth2-dialog.service.ts +++ b/dmp-frontend/src/app/ui/misc/oauth2-dialog/service/oauth2-dialog.service.ts @@ -2,7 +2,7 @@ import { Injectable, Inject } from '@angular/core'; import { BaseService } from '@common/base/base.service'; import { BehaviorSubject, Observable, interval } from 'rxjs'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { takeUntil } from 'rxjs/operators'; @Injectable() diff --git a/dmp-frontend/src/app/ui/misc/search/search.component.spec.ts b/dmp-frontend/src/app/ui/misc/search/search.component.spec.ts index 43729199b..be8e387b3 100644 --- a/dmp-frontend/src/app/ui/misc/search/search.component.spec.ts +++ b/dmp-frontend/src/app/ui/misc/search/search.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { SearchComponent } from './search.component'; @@ -6,7 +6,7 @@ describe('SearchComponent', () => { let component: SearchComponent; let fixture: ComponentFixture; - beforeEach(async(() => { + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ SearchComponent ] }) diff --git a/dmp-frontend/src/app/ui/navbar/navbar.component.ts b/dmp-frontend/src/app/ui/navbar/navbar.component.ts index d664c7325..aeccb0abb 100644 --- a/dmp-frontend/src/app/ui/navbar/navbar.component.ts +++ b/dmp-frontend/src/app/ui/navbar/navbar.component.ts @@ -1,6 +1,7 @@ import { Location } from '@angular/common'; import { Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; -import { MatDialog, MatMenuTrigger } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; +import { MatMenuTrigger } from '@angular/material/menu'; import { Router } from '@angular/router'; import { AppRole } from '@app/core/common/enum/app-role'; import { AuthService } from '@app/core/services/auth/auth.service'; @@ -40,7 +41,7 @@ export class NavbarComponent extends BaseComponent implements OnInit { selectedLanguage = 'en'; private user: UserListingModel; @Output() sidebarToggled: EventEmitter = new EventEmitter(); - @ViewChild(MatMenuTrigger, { static: false }) trigger: MatMenuTrigger; + @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger; constructor(location: Location, private element: ElementRef, diff --git a/dmp-frontend/src/app/ui/quick-wizard/quick-wizard-editor/quick-wizard-editor.component.ts b/dmp-frontend/src/app/ui/quick-wizard/quick-wizard-editor/quick-wizard-editor.component.ts index 473e59400..98a59d698 100644 --- a/dmp-frontend/src/app/ui/quick-wizard/quick-wizard-editor/quick-wizard-editor.component.ts +++ b/dmp-frontend/src/app/ui/quick-wizard/quick-wizard-editor/quick-wizard-editor.component.ts @@ -35,7 +35,7 @@ export class QuickWizardEditorComponent extends CheckDeactivateBaseComponent imp breadCrumbs: Observable = observableOf([]); @ViewChild('stepper', { static: true }) stepper: MatStepper; - @ViewChild(DatasetEditorWizardComponent, { static: false }) datasetEditorWizardComponent: DatasetEditorWizardComponent; + @ViewChild(DatasetEditorWizardComponent) datasetEditorWizardComponent: DatasetEditorWizardComponent; isNew = true; isSubmitted = false; quickWizard: QuickWizardEditorWizardModel; diff --git a/dmp-frontend/src/app/ui/transition-group/transition-group-item.directive.ts b/dmp-frontend/src/app/ui/transition-group/transition-group-item.directive.ts new file mode 100644 index 000000000..a9b08f970 --- /dev/null +++ b/dmp-frontend/src/app/ui/transition-group/transition-group-item.directive.ts @@ -0,0 +1,16 @@ +import {Directive, ElementRef} from "@angular/core"; + +@Directive({ + selector: '[transition-group-item]' +}) +export class TransitionGroupItemDirective { + prevPos: any; + newPos: any; + el: HTMLElement; + moved: boolean; + moveCallback: any; + + constructor(elRef: ElementRef) { + this.el = elRef.nativeElement; + } +} diff --git a/dmp-frontend/src/app/ui/transition-group/transition-group.component.ts b/dmp-frontend/src/app/ui/transition-group/transition-group.component.ts new file mode 100644 index 000000000..62d89b8e1 --- /dev/null +++ b/dmp-frontend/src/app/ui/transition-group/transition-group.component.ts @@ -0,0 +1,106 @@ +import {TransitionGroupItemDirective} from "./transition-group-item.directive"; +import {AfterViewInit, Component, ContentChildren, Input, OnDestroy, QueryList} from "@angular/core"; +import {Subscription} from "rxjs"; +import Timeout = NodeJS.Timeout; + +@Component({ + selector: '[transition-group]', + template: '' +}) +export class TransitionGroupComponent implements AfterViewInit, OnDestroy { + @ContentChildren(TransitionGroupItemDirective) items: QueryList; + private subscription: Subscription; + private timeout: Timeout; + + ngAfterViewInit() { + this.init(); + this.subscription = this.items.changes.subscribe(items => { + items.forEach(item => item.prevPos = item.newPos || item.prevPos); + items.forEach(this.runCallback); + this.refreshPosition('newPos'); + items.forEach(item => item.prevPos = item.prevPos || item.newPos); // for new items + + const animate = () => { + items.forEach(this.applyTranslation); + this['_forceReflow'] = document.body.offsetHeight; // force reflow to put everything in position + this.items.forEach(this.runTransition.bind(this)); + } + + const willMoveSome = items.some((item) => { + const dx = item.prevPos.left - item.newPos.left; + const dy = item.prevPos.top - item.newPos.top; + return dx || dy; + }); + + if (willMoveSome) { + animate(); + } else { + setTimeout(() => { // for removed items + this.refreshPosition('newPos'); + animate(); + }, 0); + } + }); + } + + ngOnDestroy() { + this.clear(); + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + clear() { + if (this.timeout) { + clearTimeout(this.timeout); + } + } + + init() { + this.clear(); + this.refreshPosition('prevPos'); + this.refreshPosition('newPos'); + } + + runCallback(item: TransitionGroupItemDirective) { + if (item.moveCallback) { + item.moveCallback(); + } + } + + runTransition(item: TransitionGroupItemDirective) { + if (!item.moved) { + return; + } + const cssClass = 'list-move'; + let el = item.el; + let style: any = el.style; + el.classList.add(cssClass); + style.transform = style.WebkitTransform = style.transitionDuration = ''; + el.addEventListener('transitionend', item.moveCallback = (e: any) => { + if (!e || /transform$/.test(e.propertyName)) { + el.removeEventListener('transitionend', item.moveCallback); + item.moveCallback = null; + el.classList.remove(cssClass); + } + }); + } + + refreshPosition(prop: string) { + this.items.forEach(item => { + item[prop] = item.el.getBoundingClientRect(); + }); + } + + applyTranslation(item: TransitionGroupItemDirective) { + item.moved = false; + const dx = item.prevPos.left - item.newPos.left; + const dy = item.prevPos.top - item.newPos.top; + if (dx || dy) { + item.moved = true; + let style: any = item.el.style; + style.transform = style.WebkitTransform = 'translate(' + dx + 'px,' + dy + 'px)'; + style.transitionDuration = '0s'; + } + } +} diff --git a/dmp-frontend/src/app/ui/transition-group/transition-group.module.ts b/dmp-frontend/src/app/ui/transition-group/transition-group.module.ts new file mode 100644 index 000000000..a1d3d0cd9 --- /dev/null +++ b/dmp-frontend/src/app/ui/transition-group/transition-group.module.ts @@ -0,0 +1,13 @@ +import {NgModule} from "@angular/core"; +import {CommonModule} from "@angular/common"; +import {TransitionGroupItemDirective} from "./transition-group-item.directive"; +import {TransitionGroupComponent} from "./transition-group.component"; + +@NgModule({ + declarations: [TransitionGroupItemDirective, TransitionGroupComponent], + imports: [CommonModule], + exports: [TransitionGroupItemDirective, TransitionGroupComponent] +}) +export class TransitionGroupModule { + +} diff --git a/dmp-frontend/src/app/ui/user-guide-editor/user-guide-editor.component.ts b/dmp-frontend/src/app/ui/user-guide-editor/user-guide-editor.component.ts index 67a53a805..74e630be4 100644 --- a/dmp-frontend/src/app/ui/user-guide-editor/user-guide-editor.component.ts +++ b/dmp-frontend/src/app/ui/user-guide-editor/user-guide-editor.component.ts @@ -6,7 +6,7 @@ import { takeUntil } from 'rxjs/operators'; import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service'; import { TranslateService } from '@ngx-translate/core'; import { Router } from '@angular/router'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { environment } from 'environments/environment'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { AuthService } from '@app/core/services/auth/auth.service'; diff --git a/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.ts b/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.ts index aa89424c3..8940213ba 100644 --- a/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.ts +++ b/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.ts @@ -1,14 +1,13 @@ -import { Component, OnInit, AfterViewChecked, ViewEncapsulation, ViewChild, AfterContentInit, AfterViewInit } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; +import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; +import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { LanguageService } from '@app/core/services/language/language.service'; +import { MatomoService } from '@app/core/services/matomo/matomo-service'; import { UserGuideService } from '@app/core/services/user-guide/user-guide.service'; import { BaseComponent } from '@common/base/base.component'; -import { takeUntil } from 'rxjs/internal/operators/takeUntil'; -import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; -import { LanguageService } from '@app/core/services/language/language.service'; -import { HttpClient } from '@angular/common/http'; -import { MatomoService } from '@app/core/services/matomo/matomo-service'; -import { ElementRef } from '@angular/core'; import { interval, Subject } from 'rxjs'; -import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'app-user-guide-content', @@ -26,7 +25,7 @@ export class UserGuideContentComponent extends BaseComponent implements OnInit { private _transformed: Subject = new Subject(); private _parsed: Subject = new Subject(); - @ViewChild('guide', {static: false}) guide: ElementRef; + @ViewChild('guide') guide: ElementRef; constructor( private userGuideService: UserGuideService, @@ -94,8 +93,8 @@ export class UserGuideContentComponent extends BaseComponent implements OnInit { private parse() { const specialElements: HTMLCollection = document.getElementsByTagName('a'); for (let i = 0; i < specialElements.length; i++) { - const element = specialElements.item(i) as HTMLAnchorElement; - if(element.href.includes('#')) { + const element = specialElements.item(i) as HTMLAnchorElement; + if (element.href.includes('#')) { this.hrefToPath(element); element.removeEventListener('click', this.scrollEvent); element.addEventListener('click', this.scrollEvent); @@ -150,9 +149,9 @@ export class UserGuideContentComponent extends BaseComponent implements OnInit { } - onIFrameLoad(iframe:HTMLIFrameElement){ - - try{ + onIFrameLoad(iframe: HTMLIFrameElement) { + + try { // const contentDocument = iframe.contentDocument; // const URI = contentDocument.URL; // const refs = contentDocument.getElementsByTagName('a'); @@ -165,9 +164,9 @@ export class UserGuideContentComponent extends BaseComponent implements OnInit { // } // navLinks.forEach(a=>a.href = URI+a.hash); - const images =iframe.contentWindow.document.getElementsByTagName('img'); + const images = iframe.contentWindow.document.getElementsByTagName('img'); - for(let i=0; i< images.length; i++){ + for (let i = 0; i < images.length; i++) { const tempDiv = document.createElement('div'); const currentImage = images.item(i); tempDiv.innerHTML = currentImage.outerHTML.trim(); @@ -178,7 +177,7 @@ export class UserGuideContentComponent extends BaseComponent implements OnInit { // console.log('eleme', elem); // firstimage.src = elem.src; - }catch{ + } catch { console.warn('Could not find contentDocument'); } } diff --git a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts index 9b090c878..db7c61a1c 100644 --- a/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts +++ b/dmp-frontend/src/app/ui/user-profile/user-profile.component.ts @@ -13,7 +13,7 @@ import * as moment from 'moment-timezone'; import { Observable, of } from 'rxjs'; import { map, takeUntil } from 'rxjs/operators'; import { LanguageService } from '@app/core/services/language/language.service'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; import { Oauth2DialogService } from '../misc/oauth2-dialog/service/oauth2-dialog.service'; import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration'; @@ -23,7 +23,7 @@ import { UserProfileEditorModel } from './user-profile-editor.model'; import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type'; import { EnumUtils } from '@app/core/services/utilities/enum-utils.service'; import { AddAccountDialogComponent } from './add-account/add-account-dialog.component'; -import { MatDialog } from '@angular/material'; +import { MatDialog } from '@angular/material/dialog'; import { UserCredentialModel } from '@app/core/model/user/user-credential'; import { AuthProvider } from '@app/core/common/enum/auth-provider'; import { MergeEmailConfirmationService } from '@app/core/services/merge-email-confirmation/merge-email-confirmation.service'; diff --git a/dmp-frontend/src/app/utilities/enhancers/utils.ts b/dmp-frontend/src/app/utilities/enhancers/utils.ts new file mode 100644 index 000000000..6749dc1be --- /dev/null +++ b/dmp-frontend/src/app/utilities/enhancers/utils.ts @@ -0,0 +1,7 @@ +export function isNullOrUndefined(object: any): boolean { + return object === null || object === undefined; +} + +export function isNumeric(val: any): val is number | string { + return !Array.isArray(val) && (val - parseFloat(val) + 1) >= 0; +} diff --git a/dmp-frontend/src/assets/i18n/de.json b/dmp-frontend/src/assets/i18n/de.json index 301b3c1f8..927a98f10 100644 --- a/dmp-frontend/src/assets/i18n/de.json +++ b/dmp-frontend/src/assets/i18n/de.json @@ -289,14 +289,18 @@ "DATASET-TEMPLATE-DESCRIPTION-HINT": "A brief description of what the Dataset is about, it's scope and objectives.", "DATASET-TEMPLATE-LANGUAGE": "Dataset template language", "DATASET-TEMPLATE-SELECT-LANGUAGE": "Select a language", - "DATASET-TEMPLATE-USERS": "Users", - "DATASET-TEMPLATE-REMOVE-USER": "Remove User", - "DATASET-TEMPLATE-NO-USERS-YET": "... No users yet", - "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add User", + "DATASET-TEMPLATE-USERS": "Editors", + "DATASET-TEMPLATE-USERS-HINT": "Add editors and save changes to notify them.", + "DATASET-TEMPLATE-REMOVE-USER": "Remove Editor", + "DATASET-TEMPLATE-NO-USERS-YET": "... No editors yet", + "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add Editor", "DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER": "Dataset template description", "UNTITLED": "Untitled", "QUESTION": "Question", - "TEMPLATE-OUTLINE": "Template outline" + "TEMPLATE-OUTLINE": "Template outline", + "ERRORS":{ + "USER-NOT-FOUND":"User not found." + } }, "PAGE-INFO": { "PAGE-NAME": "Chapter Name", @@ -310,7 +314,8 @@ "CREATE-FIRST-SECTION": "Create the first chapter", "NOTHING-HERE-HINT": "Nothing here yet.", "START-CREATING-PAGE-START": "Start by ", - "START-CREATING-PAGE-END": "creating the first chapter." + "START-CREATING-PAGE-END": "creating the first chapter.", + "CREATE-SECTION": "Create section" }, "PAGE": "Chapter" }, @@ -386,6 +391,8 @@ "MULTIPLICITY-CHECKBOX": "Multiplicity", "FIELD-TEXT-AREA-TITLE": "Text Area Data", "FIELD-TEXT-AREA-PLACEHOLDER": "Input Placeholder Text", + "FIELD-RICH-TEXT-AREA-TITLE": "Rich Text Area Data", + "FIELD-RICH-TEXT-AREA-PLACEHOLDER": "Input Placeholder Text", "FIELD-BOOLEAN-DECISION-TITLE": "Boolean Decision Data", "FIELD-BOOLEAN-DECISION-PLACEHOLDER": "Input Placeholder", "FIELD-CHECKBOX-TITLE": "Checkbox Data", @@ -441,7 +448,13 @@ "FIELD-DATASET-IDENTIFIER-TITLE": "Dataset Identifier Data", "FIELD-DATASET-IDENTIFIER-PLACEHOLDER": "Input placeholder", "FIELD-VALIDATOR-TITLE": "Validator Data", - "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text" + "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text", + "EXTERNAL-DATASET-TYPE-NAME": "Type", + "EXTERNAL-DATASET-TYPES":{ + "PRODUCED": "Produced dataset", + "REUSED": "Reused dataset", + "OTHER": "Other" + } }, "ERROR-MESSAGES": { "FIELD-OTHER-SOURCES-REQUIRED": "At least one source must be provided.", @@ -523,7 +536,9 @@ "ADD-VISIBILITY-RULE": "Add Conditional Question", "DELETE-INPUT": "Delete this input", "PREVIEW": "Preview", - "NOT-INITIALIZED": "Not initialized yet" + "NOT-INITIALIZED": "Not initialized yet", + "MOVE-UP": "Move this input above", + "MOVE-DOWN": "Move this input below" }, "FIELDSET": { "ADD-INPUT": "Add input", @@ -683,6 +698,7 @@ "GO-TO-GRANT": "Gehe zu Datansatzbeschreibung Förderung", "GO-TO-DMP": "Gehe zu Datensatzbeschreibung DMP", "SAVE": "Speichern", + "SAVE-AND-CONTINUE": "Save & Continue", "SAVE-AND-ADD": "Save & Add New", "SAVE-AND-CLOSE": "Save & Close", "SAVE-AND-FINALISE": "Speichern und Fertigstellen", @@ -695,12 +711,16 @@ "DOWNLOAD-DOCX": "DOCX herunterladen", "COPY-DATASET": "Datensatzbeschreibung kopieren", "UPDATE-DATASET-PROFILE": "Vorlage aktualisieren", - "VALIDATE":"Validate" + "VALIDATE":"Validate", + "UNDO-FINALIZATION-QUESTION" :"Undo finalization?", + "CONFIRM" : "Yes", + "REJECT": "No" }, "MESSAGES": { "DATASET-NOT-FOUND": "Datensatzbeschreibung ist nicht vorhanden", "DATASET-NOT-ALLOWED": "Sie haben keinen Zugriff auf diese Datensatzbeschreibung", - "SUCCESS-UPDATE-DATASET-PROFILE": "Vorlage der Datensatzbeschreibung erfolgreich aktualisiert" + "SUCCESS-UPDATE-DATASET-PROFILE": "Vorlage der Datensatzbeschreibung erfolgreich aktualisiert", + "MISSING-FIELDS": "There are some required fields left unfilled. Please check the form and make sure that all required fields are filled. (Missing fields are marked in red color)" }, "UPLOAD": { "UPLOAD-XML": "Importieren", @@ -718,6 +738,10 @@ "CANCEL": "Abbrechen", "NEXT": "Next", "ERROR-MESSAGE": "Diese Vorlage der Datensatzbeschreibung ist nicht enthalten" + }, + "LOCKED":{ + "TITLE":"Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. You may view the dataset but you cannot make any changes. If you would like to modify it please come back later." } }, "DMP-OVERVIEW": { @@ -744,6 +768,10 @@ "MULTIPLE-DIALOG": { "ZENODO-LOGIN": "Login with Zenodo", "USE-DEFAULT": "Use Default Token" + }, + "LOCKED-DIALOG":{ + "TITLE": "DMP is locked", + "MESSAGE":"Somebody else is modifying the DMP at this moment. If you would like to modify or view it, please come back later." } }, "DATASET-OVERVIEW": { @@ -751,6 +779,15 @@ "ERROR": { "DELETED-DATASET": "The requested dataset is deleted", "FORBIDEN-DATASET": "You are not allowed to access this dataset" + }, + "LOCKED":{ + "TITLE": "Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. If you would like to modify or view it, please come back later." + }, + "FINALISE-POPUP":{ + "MESSAGE":"You will be directed to Dataset Editor where you can finalize the selected dataset. Would you like to proceed?", + "CONFIRM":"OK", + "CANCEL":"Cancel" } }, "DATASET-LISTING": { @@ -850,7 +887,8 @@ "CLONE": "Klon", "NEW-VERSION": "Neue Version", "VIEW-VERSIONS": "Alle Vorlagenversionen für Datensatzbeschreibungen", - "DELETE": "Delete" + "DELETE": "Delete", + "CREATE-DATASET-TEMPLATE": "Create Dataset Template" } }, "DATASET-UPLOAD": { @@ -896,6 +934,11 @@ "DELETE": "Löschen", "FINALIZE": "Finalize", "DOWNLOAD-XML": "Download XML" + }, + "CONFIRM-DELETE-DIALOG":{ + "MESSAGE": "Would you like to delete this DMP template?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" } }, "GRANT-EDITOR": { @@ -1023,7 +1066,9 @@ "RESEARCHER-IDENTIFIER-EXISTS": "Researcher identifier already exists.", "ORGANISATION-IDENTIFIER-EXSTS": "Organisation identifier already exists.", "IDENTIFIER-EXISTS-RESEARCHER-LIST": "This identifier is already used by a researcher in the researchers list.", - "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list." + "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list.", + "SAVE":"Save", + "CANCEL": "Cancel" }, "FUNDING-INFO": { "INTRO": "You are using the DMP editor. Add here information about the scope, funding, actors of your DMP and decide on access and re-use issues for the DMP output that you are creating.", @@ -1060,6 +1105,7 @@ }, "DMP-PROFILE-LISTING": { "TITLE": "DMP Vorlagen", + "CREATE-DMP-TEMPLATE": "Create DMP Template", "COLUMNS": { "NAME": "Name", "STATUS": "Status", @@ -1073,6 +1119,13 @@ "UPLOAD-XML-NAME": "Name der DMP Vorlage", "UPLOAD-XML-IMPORT": "Datei", "UPLOAD-XML-FILE-CANCEL": "Abbrechen" + }, + "STATUS":{ + "DRAFT": "Draft", + "FINALIZED": "Finalized" + }, + "MESSAGES":{ + "TEMPLATE-UPLOAD-SUCCESS":"Template successfully uploaded" } }, "DYNAMIC-FORM": { @@ -1195,6 +1248,16 @@ "VERSION-DIALOG": { "ABOUT": "Die Versionierung ist automatisiert.", "QUESTION": "Es scheint, dass Ihre Vorlage der Datensatzbeschreibung veraltet ist. Möchten Sie sie auf die neueste Version aktualisieren?" + }, + "ERRORS":{ + "ERROR-OCCURED": "An error occured.", + "MESSAGE": "Message: " + }, + "QUESTION": { + "EXTENDED-DESCRIPTION": { + "VIEW-MORE": "View more", + "VIEW-LESS": "View less" + } } }, "DATASET-CREATE-WIZARD": { @@ -1294,6 +1357,7 @@ "FREE-TEXT": "Free Text", "RADIO-BOX": "Radio Box", "TEXT-AREA": "Text Area", + "RICH-TEXT-AREA": "Rich Text Area", "DATE-PICKER": "Date Picker", "EXTERNAL-DATASETS": "External Datasets", "DATA-REPOSITORIES": "Data Repositories", @@ -1508,6 +1572,7 @@ "PERSONAL-USAGE": "Personal Usage", "PUBLIC-USAGE": "Public Usage", "DATASET-DESCRIPTIONS": "Datasets", + "DATASET-DESCRIPTIONS-DASHBOARD-TEXT": "Datasets", "PUBLIC-DMPS": "Public DMPs", "PUBLIC-DATASETS": "Public Datasets", "RELATED-ORGANISATIONS": "Related Organisations", @@ -1758,4 +1823,4 @@ "FINALIZED": "Finalized", "DELETED": "Deleted" } -} \ No newline at end of file +} diff --git a/dmp-frontend/src/assets/i18n/en.json b/dmp-frontend/src/assets/i18n/en.json index 3537f2ca6..ea7c76aea 100644 --- a/dmp-frontend/src/assets/i18n/en.json +++ b/dmp-frontend/src/assets/i18n/en.json @@ -289,14 +289,18 @@ "DATASET-TEMPLATE-DESCRIPTION-HINT": "A brief description of what the Dataset is about, it's scope and objectives.", "DATASET-TEMPLATE-LANGUAGE": "Dataset template language", "DATASET-TEMPLATE-SELECT-LANGUAGE": "Select a language", - "DATASET-TEMPLATE-USERS": "Users", - "DATASET-TEMPLATE-REMOVE-USER": "Remove User", - "DATASET-TEMPLATE-NO-USERS-YET": "... No users yet", - "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add User", + "DATASET-TEMPLATE-USERS": "Editors", + "DATASET-TEMPLATE-USERS-HINT": "Add editors and save changes to notify them.", + "DATASET-TEMPLATE-REMOVE-USER": "Remove Editor", + "DATASET-TEMPLATE-NO-USERS-YET": "... No editors yet", + "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add Editor", "DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER": "Dataset template description", "UNTITLED": "Untitled", "QUESTION": "Question", - "TEMPLATE-OUTLINE": "Template outline" + "TEMPLATE-OUTLINE": "Template outline", + "ERRORS":{ + "USER-NOT-FOUND":"User not found." + } }, "PAGE-INFO": { "PAGE-NAME": "Chapter Name", @@ -310,7 +314,8 @@ "CREATE-FIRST-SECTION": "Create the first chapter", "NOTHING-HERE-HINT": "Nothing here yet.", "START-CREATING-PAGE-START": "Start by ", - "START-CREATING-PAGE-END": "creating the first chapter." + "START-CREATING-PAGE-END": "creating the first chapter.", + "CREATE-SECTION": "Create section" }, "PAGE": "Chapter" }, @@ -386,6 +391,8 @@ "MULTIPLICITY-CHECKBOX": "Multiplicity", "FIELD-TEXT-AREA-TITLE": "Text Area Data", "FIELD-TEXT-AREA-PLACEHOLDER": "Input Placeholder Text", + "FIELD-RICH-TEXT-AREA-TITLE": "Rich Text Area Data", + "FIELD-RICH-TEXT-AREA-PLACEHOLDER": "Input Placeholder Text", "FIELD-BOOLEAN-DECISION-TITLE": "Boolean Decision Data", "FIELD-BOOLEAN-DECISION-PLACEHOLDER": "Input Placeholder Text", "FIELD-CHECKBOX-TITLE": "Checkbox Data", @@ -441,7 +448,13 @@ "FIELD-DATASET-IDENTIFIER-TITLE": "Dataset Identifier Data", "FIELD-DATASET-IDENTIFIER-PLACEHOLDER": "Input placeholder Text", "FIELD-VALIDATOR-TITLE": "Validator Data", - "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text" + "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text", + "EXTERNAL-DATASET-TYPE-NAME": "Type", + "EXTERNAL-DATASET-TYPES":{ + "PRODUCED": "Produced dataset", + "REUSED": "Reused dataset", + "OTHER": "Other" + } }, "ERROR-MESSAGES": { "FIELD-OTHER-SOURCES-REQUIRED": "At least one source must be provided.", @@ -523,7 +536,9 @@ "ADD-VISIBILITY-RULE": "Make Conditional Question", "DELETE-INPUT": "Delete this input", "PREVIEW": "Preview", - "NOT-INITIALIZED": "Not initialized yet" + "NOT-INITIALIZED": "Not initialized yet", + "MOVE-UP": "Move this input above", + "MOVE-DOWN": "Move this input below" }, "FIELDSET": { "ADD-INPUT": "Add input", @@ -683,6 +698,7 @@ "GO-TO-GRANT": "Go to Dataset's Grant", "GO-TO-DMP": "Go to Dataset's DMP", "SAVE": "Save", + "SAVE-AND-CONTINUE": "Save & Continue", "SAVE-AND-ADD": "Save & Add New", "SAVE-AND-CLOSE": "Save & Close", "SAVE-AND-FINALISE": "Save and Finalize", @@ -695,12 +711,16 @@ "DOWNLOAD-DOCX": "Download DOCX", "COPY-DATASET": "Copy Dataset", "UPDATE-DATASET-PROFILE": "Update Template", - "VALIDATE":"Validate" + "VALIDATE":"Validate", + "UNDO-FINALIZATION-QUESTION" :"Undo finalization?", + "CONFIRM" : "Yes", + "REJECT": "No" }, "MESSAGES": { "DATASET-NOT-FOUND": "Dataset does not exist", "DATASET-NOT-ALLOWED": "You have no access to this Dataset", - "SUCCESS-UPDATE-DATASET-PROFILE": "Dataset Template updated successfully" + "SUCCESS-UPDATE-DATASET-PROFILE": "Dataset Template updated successfully", + "MISSING-FIELDS": "There are some required fields left unfilled. Please check the form and make sure that all required fields are filled. (Missing fields are marked in red color)" }, "UPLOAD": { "UPLOAD-XML": "Import", @@ -718,6 +738,10 @@ "CANCEL": "Cancel", "NEXT": "Next", "ERROR-MESSAGE": "Does not contain this Dataset Template" + }, + "LOCKED":{ + "TITLE":"Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. You may view the dataset but you cannot make any changes. If you would like to modify it please come back later." } }, "DMP-OVERVIEW": { @@ -744,6 +768,10 @@ "MULTIPLE-DIALOG": { "ZENODO-LOGIN": "Login with Zenodo", "USE-DEFAULT": "Use Default Token" + }, + "LOCKED-DIALOG":{ + "TITLE": "DMP is locked", + "MESSAGE":"Somebody else is modifying the DMP at this moment. If you would like to modify or view it, please come back later." } }, "DATASET-OVERVIEW": { @@ -751,6 +779,15 @@ "ERROR": { "DELETED-DATASET": "The requested dataset is deleted", "FORBIDEN-DATASET": "You are not allowed to access this dataset" + }, + "LOCKED":{ + "TITLE": "Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. If you would like to modify or view it, please come back later." + }, + "FINALISE-POPUP":{ + "MESSAGE":"You will be directed to Dataset Editor where you can finalize the selected dataset. Would you like to proceed?", + "CONFIRM":"OK", + "CANCEL":"Cancel" } }, "DATASET-LISTING": { @@ -850,7 +887,8 @@ "CLONE": "Clone", "NEW-VERSION": "New Version", "VIEW-VERSIONS": "All Dataset Template Versions", - "DELETE": "Delete" + "DELETE": "Delete", + "CREATE-DATASET-TEMPLATE": "Create Dataset Template" } }, "DATASET-UPLOAD": { @@ -896,6 +934,11 @@ "DELETE": "Delete", "FINALIZE": "Finalize", "DOWNLOAD-XML": "Download XML" + }, + "CONFIRM-DELETE-DIALOG":{ + "MESSAGE": "Would you like to delete this DMP template?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" } }, "GRANT-EDITOR": { @@ -1023,7 +1066,9 @@ "RESEARCHER-IDENTIFIER-EXISTS": "Researcher identifier already exists.", "ORGANISATION-IDENTIFIER-EXSTS": "Organisation identifier already exists.", "IDENTIFIER-EXISTS-RESEARCHER-LIST": "This identifier is already used by a researcher in the researchers list.", - "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list." + "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list.", + "SAVE":"Save", + "CANCEL": "Cancel" }, "FUNDING-INFO": { "INTRO": "You are using the DMP editor. Add here information about the scope, funding, actors of your DMP and decide on access and re-use issues for the DMP output that you are creating.", @@ -1060,6 +1105,7 @@ }, "DMP-PROFILE-LISTING": { "TITLE": "DMP Templates", + "CREATE-DMP-TEMPLATE": "Create DMP Template", "COLUMNS": { "NAME": "Name", "STATUS": "Status", @@ -1073,6 +1119,13 @@ "UPLOAD-XML-NAME": "Name Of DMP Template", "UPLOAD-XML-IMPORT": "File", "UPLOAD-XML-FILE-CANCEL": "Cancel" + }, + "STATUS":{ + "DRAFT": "Draft", + "FINALIZED": "Finalized" + }, + "MESSAGES":{ + "TEMPLATE-UPLOAD-SUCCESS":"Template successfully uploaded" } }, "DYNAMIC-FORM": { @@ -1195,6 +1248,16 @@ "VERSION-DIALOG": { "ABOUT": "Versioning is automated.", "QUESTION": "It seems your Dataset Template is outdated. Do you want to update it to the latest version?" + }, + "ERRORS":{ + "ERROR-OCCURED": "An error occured.", + "MESSAGE": "Message: " + }, + "QUESTION": { + "EXTENDED-DESCRIPTION": { + "VIEW-MORE": "View more", + "VIEW-LESS": "View less" + } } }, "DATASET-CREATE-WIZARD": { @@ -1294,6 +1357,7 @@ "FREE-TEXT": "Free Text", "RADIO-BOX": "Radio Box", "TEXT-AREA": "Text Area", + "RICH-TEXT-AREA": "Rich Text Area", "DATE-PICKER": "Date Picker", "EXTERNAL-DATASETS": "External Datasets", "DATA-REPOSITORIES": "Data Repositories", @@ -1508,6 +1572,7 @@ "PERSONAL-USAGE": "Personal Usage", "PUBLIC-USAGE": "Public Usage", "DATASET-DESCRIPTIONS": "Datasets", + "DATASET-DESCRIPTIONS-DASHBOARD-TEXT": "Datasets", "PUBLIC-DMPS": "Public DMPs", "PUBLIC-DATASETS": "Public Datasets", "RELATED-ORGANISATIONS": "Related Organisations", @@ -1758,4 +1823,4 @@ "FINALIZED": "Finalized", "DELETED": "Deleted" } -} \ No newline at end of file +} diff --git a/dmp-frontend/src/assets/i18n/es.json b/dmp-frontend/src/assets/i18n/es.json index ca04f9851..5bf81b680 100644 --- a/dmp-frontend/src/assets/i18n/es.json +++ b/dmp-frontend/src/assets/i18n/es.json @@ -79,27 +79,27 @@ } }, "START-NEW-DMP-DIALOG": { - "IMPORT": "Import", - "FUNCTION-SUPPORTS": "function supports", - "JSON-FILES": ".json files", - "PRODUCED": "produced", - "RDA-SPECIFICATIONS": "according to RDA specifications", - "MACHINE-ACTIONABLE": "for machine-actionable DMPs", - "UPLOAD-FILE": "Upload File", - "REPLACE-FILE": "Replace File" + "IMPORT": "Importar", + "FUNCTION-SUPPORTS": "función de soporte", + "JSON-FILES": "ficheros .json", + "PRODUCED": "producido", + "RDA-SPECIFICATIONS": "de acuerdo a las especificaciones RDA", + "MACHINE-ACTIONABLE": "para PGDs procesables por máquinas", + "UPLOAD-FILE": "Subir fichero", + "REPLACE-FILE": "Reemplar ichero" }, "INVITATION-DIALOG": { - "HINT": "Press comma or enter between authors", - "SUCCESS": "Invitation successfully sent", - "ERROR": "Invitation sent failed" + "HINT": "Ponga coma o enter entre los autores", + "SUCCESS": "Invitación correctamente enviada", + "ERROR": "Fallo al enviar la invitación" }, "DMP-TO-DATASET-DIALOG": { - "FROM-DMP": "You have successfully created your", - "DMP": "DMP", - "TO-DATASET": "You will be transferred to the", + "FROM-DMP": "Ha creado correctamente su", + "DMP": "PGD", + "TO-DATASET": "Será transferido al", "DATASET": "Dataset", "EDITOR": "editor", - "START": "let's start" + "START": "comencemos" }, "ACTIONS": { "VIEW-ALL": "Ver todo", @@ -107,7 +107,7 @@ "LOAD-MORE": "Cargar más", "SHOW-LESS": "Mostrar menos", "LOG-IN": "Iniciar sesión", - "TAKE-A-TOUR": "Do you need help? Take a tour.." + "TAKE-A-TOUR": "Necesita ayuda? Hagamos una visita virtual.." }, "PREPOSITIONS": { "OF": "de" @@ -171,9 +171,9 @@ "SPANISH": "Español", "GERMAN": "Alemán", "TURKISH": "Turco", - "SLOVAK": "Slovak", - "SERBIAN": "Serbian", - "PORTUGUESE": "Portuguese" + "SLOVAK": "Eslovaco", + "SERBIAN": "Serbio", + "PORTUGUESE": "Portugués" } }, "COOKIE": { @@ -282,49 +282,54 @@ }, "STEPS": { "GENERAL-INFO": { - "TITLE": "General Info", - "DATASET-TEMPLATE-NAME": "Dataset template name", - "DATASET-TEMPLATE-NAME-HINT": "A title that determines the Dataset template.", - "DATASET-TEMPLATE-DESCRIPTION": "Description", - "DATASET-TEMPLATE-DESCRIPTION-HINT": "A brief description of what the Dataset is about, it's scope and objectives.", - "DATASET-TEMPLATE-LANGUAGE": "Dataset template language", - "DATASET-TEMPLATE-SELECT-LANGUAGE": "Select a language", - "DATASET-TEMPLATE-USERS": "Users", - "DATASET-TEMPLATE-NO-USERS-YET": "... No users yet", - "DATASET-TEMPLATE-REMOVE-USER": "Remove User", - "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add User", - "DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER": "Dataset template description", - "UNTITLED": "Untitled", - "QUESTION": "Question", - "TEMPLATE-OUTLINE": "Template outline" + "TITLE": "Información General", + "DATASET-TEMPLATE-NAME": "Nombre de la plantilla del Dataset", + "DATASET-TEMPLATE-NAME-HINT": "Un título que determine la plantilla del Dataset.", + "DATASET-TEMPLATE-DESCRIPTION": "Descripción", + "DATASET-TEMPLATE-DESCRIPTION-HINT": "Una breve descripción acerca del Dataset, su alcance y objetivos.", + "DATASET-TEMPLATE-LANGUAGE": "Idioma de la plantilla del Dataset", + "DATASET-TEMPLATE-SELECT-LANGUAGE": "Seleccione un idioma", + "DATASET-TEMPLATE-USERS": "Editors", + "DATASET-TEMPLATE-USERS-HINT": "Add editors and save changes to notify them.", + "DATASET-TEMPLATE-REMOVE-USER": "Remove Editor", + "DATASET-TEMPLATE-NO-USERS-YET": "... No editors yet", + "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add Editor", + "DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER": "Descripción de la plantilla del Dataset", + "UNTITLED": "Sin título", + "QUESTION": "Pregunta", + "TEMPLATE-OUTLINE": "Modelo de plantilla", + "ERRORS":{ + "USER-NOT-FOUND":"Usuario no encontrado." + } }, "PAGE-INFO": { - "PAGE-NAME": "Chapter Name", - "PAGE-NAME-HINT": "Set a name for the dataset chapter.", - "PAGE-DESCRIPTION": "Description", - "PAGE-DESCRIPTION-HINT": "Write a brief desciption of what the chapter is about.", + "PAGE-NAME": "Nombre del capítulo", + "PAGE-NAME-HINT": "Poner un nombre al capítulo del dataset.", + "PAGE-DESCRIPTION": "Descripción", + "PAGE-DESCRIPTION-HINT": "Escribir una breve descripción de lo que trata el capítulo.", "ACTIONS": { - "CREATE-FIRST-PAGE": "Create the first chapter", - "CREATE-NEW-SUBSECTION": "Section", - "CREATE-NEW-SECTION": "Chapter", - "CREATE-FIRST-SECTION": "Create the first chapter", - "NOTHING-HERE-HINT": "Nothing here yet.", - "START-CREATING-PAGE-START": "Start by ", - "START-CREATING-PAGE-END": "creating the first chapter." + "CREATE-FIRST-PAGE": "Crear el primer capítulo", + "CREATE-NEW-SUBSECTION": "Sección", + "CREATE-NEW-SECTION": "Capítulo", + "CREATE-FIRST-SECTION": "Crear el primer capítulo", + "NOTHING-HERE-HINT": "Todavía no hay nada.", + "START-CREATING-PAGE-START": "Empieza por ", + "START-CREATING-PAGE-END": "creando el primer capítulo.", + "CREATE-SECTION": "Crear sección" }, - "PAGE": "Chapter" + "PAGE": "Capítulo" }, "SECTION-INFO": { - "SECTION-NAME": "Section Name", - "SECTION-NAME-HINT": "Set a name for the section.", - "SECTION-DESCRIPTION": "Description", - "SECTION-DESCRIPTION-HINT": "Write a brief desciption of what the section is about.", - "SECTION": "Section" + "SECTION-NAME": "Nombre de sección", + "SECTION-NAME-HINT": "Poner un nombre a la sección.", + "SECTION-DESCRIPTION": "Descripción", + "SECTION-DESCRIPTION-HINT": "Escribir una pequeña descripción de lo que trata la sección.", + "SECTION": "Sección" }, "PAGES": { - "TITLE": "Chapter Description", - "PAGE-PREFIX": "Chapter", - "PAGE-INPUT-TITLE": "Chapter Title", + "TITLE": "Descripción del capítulo", + "PAGE-PREFIX": "Capítulo", + "PAGE-INPUT-TITLE": "Título del cappítulo", "DATASET-DETAILS": "Detalles de la descripción del dataset", "EXTERNAL-REFERENCES": "Referencias externas", "DESCRIPTION": "Descripción" @@ -386,6 +391,8 @@ "MULTIPLICITY-CHECKBOX": "Multiplicidad", "FIELD-TEXT-AREA-TITLE": "Datos de área de texto", "FIELD-TEXT-AREA-PLACEHOLDER": "Marcador de entrada", + "FIELD-RICH-TEXT-AREA-TITLE": "Rich Text Area Data", + "FIELD-RICH-TEXT-AREA-PLACEHOLDER": "Input Placeholder Text", "FIELD-BOOLEAN-DECISION-TITLE": "Datos de decisión booleana", "FIELD-BOOLEAN-DECISION-PLACEHOLDER": "Marcador de entrada", "FIELD-CHECKBOX-TITLE": "Datos de la casilla de verificación", @@ -423,30 +430,36 @@ "FIELD-DATE-PICKER-LABEL": "Etiqueta", "FIELD-DATE-PICKER-VALUE": "Valor", "FIELD-MULTIPLE-AUTOCOMPLETE": "Autocompeltado múltiple", - "FIELD-MULTIPLE-WORDLIST": "Multiple Selection", - "FIELD-CURRENCY-TITLE": "Currency Data", - "FIELD-CURRENCY-PLACEHOLDER": "Input Placeholder Text", - "FIELD-REGISTRIES-TITLE": "Registries Data", - "FIELD-REGISTRIES-PLACEHOLDER": "Input Placeholder Text", - "FIELD-SERVICES-TITLE": "Services Data", - "FIELD-SERVICES-PLACEHOLDER": "Input Placeholder Text", - "FIELD-ORGANIZATIONS-TITLE": "Organizations Data", - "FIELD-ORGANIZATIONS-PLACEHOLDER": "Input placeholder Text", - "FIELD-EXTERNAL-DATASETS-TITLE": "External Datasets Data", - "FIELD-EXTERNAL-DATASETS-PLACEHOLDER": "Input placeholder Text", - "FIELD-DATA-REPOSITORIES-TITLE": "Data Repositories Data", - "FIELD-DATA-REPOSITORIES-PLACEHOLDER": "Input placeholder Text", - "FIELD-TAGS-TITLE": "Tags Data", - "FIELD-TAGS-PLACEHOLDER": "Input placeholder Text", - "FIELD-DATASET-IDENTIFIER-TITLE": "Dataset Identifier Data", - "FIELD-DATASET-IDENTIFIER-PLACEHOLDER": "Input placeholder Text", - "FIELD-VALIDATOR-TITLE": "Validator Data", - "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text" + "FIELD-MULTIPLE-WORDLIST": "Selección múltiple", + "FIELD-CURRENCY-TITLE": "Datos de moneda", + "FIELD-CURRENCY-PLACEHOLDER": "Introduzca marcador del texto", + "FIELD-REGISTRIES-TITLE": "Datos de registros", + "FIELD-REGISTRIES-PLACEHOLDER": "Introduzca marcador del texto", + "FIELD-SERVICES-TITLE": "Datos de servicios", + "FIELD-SERVICES-PLACEHOLDER": "Introduzca marcador del texto", + "FIELD-ORGANIZATIONS-TITLE": "Datos de organizaciones", + "FIELD-ORGANIZATIONS-PLACEHOLDER": "Introduzca marcador del texto", + "FIELD-EXTERNAL-DATASETS-TITLE": "Datos de Datasets externos", + "FIELD-EXTERNAL-DATASETS-PLACEHOLDER": "Introduzca marcador del texto", + "FIELD-DATA-REPOSITORIES-TITLE": "Datos de Repositorios de Datos", + "FIELD-DATA-REPOSITORIES-PLACEHOLDER": "Introduzca marcador del texto", + "FIELD-TAGS-TITLE": "Datos de etiquetas", + "FIELD-TAGS-PLACEHOLDER": "Introduzca marcador del texto", + "FIELD-DATASET-IDENTIFIER-TITLE": "Datos del identifcador del dataset", + "FIELD-DATASET-IDENTIFIER-PLACEHOLDER": "Introduzca marcador del texto", + "FIELD-VALIDATOR-TITLE": "Datos de validador", + "FIELD-VALIDATOR-PLACEHOLDER": "Introduzca marcador del texto", + "EXTERNAL-DATASET-TYPE-NAME": "Tipo", + "EXTERNAL-DATASET-TYPES":{ + "PRODUCED": "Dataset producido", + "REUSED": "Dataset reutilizado", + "OTHER": "Otro" + } }, "ERROR-MESSAGES": { - "FIELD-OTHER-SOURCES-REQUIRED": "At least one source must be provided.", - "FIELD-RADIO-AT-LEAST-ONE-REQUIRED": "There must be at least one option provided.", - "FIELD-SELECT-AT-LEAST-ONE-REQUIRED": "There must be at least one option provided." + "FIELD-OTHER-SOURCES-REQUIRED": "Debe proporcionar al menos una fuente.", + "FIELD-RADIO-AT-LEAST-ONE-REQUIRED": "Debe proporcionar al menos una opción.", + "FIELD-SELECT-AT-LEAST-ONE-REQUIRED": "Debe proporcionar al menos una opción." }, "DEFAULT-VALUES": { "NONE": "Ninguno", @@ -463,48 +476,48 @@ "ADD-RULE": "Añadir una regla de visibilidad +" }, "STATUS":{ - "CALCULATING-PREVIEW":"... calculating preview", - "PREVIEW-UPDATED":"Preview updated!" + "CALCULATING-PREVIEW":"... calculando previsualización", + "PREVIEW-UPDATED":"¡Previsualización actualizada!" } }, "RULE": { "FIELDS": { "RULE-TYPE": "Tipo de regla", - "TARGET": "Target Field Id", + "TARGET": "Identificador del campo objetivo", "VALUE": "Valor requerido", "RULE-IF": "Si el valor es", - "RULE-THEN": "then show Question", - "FIELDSETS": "Questions", - "FIELDS": "Inputs" + "RULE-THEN": "entonces muestre la pregunta", + "FIELDSETS": "Preguntas", + "FIELDS": "Entradas" }, "HINTS": { - "ELEMENT-CHILD-OF-TARGET": "This element is parent of selected input.", - "ELEMENT-HIDDEN-FROM-ELEMENT": "This element hides the element or a parent element of the input you are trying to apply visibility rule from." + "ELEMENT-CHILD-OF-TARGET": "Este elemento es padre de la entrada seleccionada.", + "ELEMENT-HIDDEN-FROM-ELEMENT": "Estos elementos esconden el elemento o un elemento padre de la entrada que está intentando para aplicar la visibilidad de la regla." } }, "FORM-VALIDATION": { "ERROR-MESSAGES": { - "PAGE-MUST-HAVE-SECTION": "Each chapter must have at least one section.", - "NEEDS-MORE-INFORMATION": " needs more information.", - "MUST-HAVE-SECTION-OR-FIELDSET": " must have either section or question.", - "MISSING": "Missing", - "PROVIDE-PAGE-AND-SECTION": "Make sure you provide a chapter and at least one section per chapter." + "PAGE-MUST-HAVE-SECTION": "Cada capítulo debe tener al menos una sección.", + "NEEDS-MORE-INFORMATION": " necesita más información.", + "MUST-HAVE-SECTION-OR-FIELDSET": " debte tener seccion o pregunta.", + "MISSING": "Perdido", + "PROVIDE-PAGE-AND-SECTION": "Asegúrese de proveer un capítulo y al menos una sección por capítulo." } }, "TABLE-OF-CONTENTS": { "ERROR-MESSAGES": { - "FIELDSET-MUST-HAVE-PARENT-SECTION": "Question can only be child of section.", - "INPUT-SECTION-SAME-LEVEL": "Cannot have question and section on the same level.", - "DRAG-NOT-SUPPORTED": "Drag n drop of section not supported to target container.", - "PAGE-ELEMENT-ONLY-TOP-LEVEL": "Chapter elements can only be at top level" + "FIELDSET-MUST-HAVE-PARENT-SECTION": "La pregunta pueden ser solo hija de una sección.", + "INPUT-SECTION-SAME-LEVEL": "No puede haber preguntas y secciones al mismo nivel.", + "DRAG-NOT-SUPPORTED": "No se puede arrastrar la sección a un contenedor destino.", + "PAGE-ELEMENT-ONLY-TOP-LEVEL": "Elementos de capítulos solo pueden estar en el nivel superior" } } }, "TOOLKIT": { - "GENERAL-TOOLS": "Questions tools", - "NEW-INPUT-SET": "Add Question", - "CLONE": "Clone", - "DELETE": "Delete" + "GENERAL-TOOLS": "Herramientas de preguntas", + "NEW-INPUT-SET": "Añadir pregunta", + "CLONE": "Clonar", + "DELETE": "Borrar" } }, "ACTIONS": { @@ -515,35 +528,37 @@ "DELETE": "Borrar", "ADD-PAGE": "Añadir página +", "ADD-SECTION": "Añadir sección +", - "VALIDATE": "Validate", - "PREVIEW-AND-FINALIZE": "Preview and finalize", - "BACK-TO-TOP": "Back to top", + "VALIDATE": "Validar", + "PREVIEW-AND-FINALIZE": "Previsualizar y finalizar", + "BACK-TO-TOP": "Volver arriba", "FIELD": { - "MAKE-IT-REQUIRED": "Make input required", - "ADD-VISIBILITY-RULE": "Add Conditional Question", - "DELETE-INPUT": "Delete this input", - "PREVIEW": "Preview", - "NOT-INITIALIZED": "Not initialized yet" + "MAKE-IT-REQUIRED": "Entrada requerida", + "ADD-VISIBILITY-RULE": "Añadir un pregunta condicional", + "DELETE-INPUT": "Borrar esta entrada", + "PREVIEW": "Previsualizar", + "NOT-INITIALIZED": "No iniciado todavía", + "MOVE-UP": "Mover esta entrada arriba", + "MOVE-DOWN": "Mover esta entrada abajo" }, "FIELDSET": { - "ADD-INPUT": "Add input", - "COMMENT-FIELD": "Comment field", - "INCLUDE-COMMENT-FIELD": "A comment field input is appended to the given question.", - "ENABLE-MULTIPLICITY": "User may provide more than one answer to the given question.", - "MULTIPLICITY": "Multiplicity", + "ADD-INPUT": "Añadir entrada", + "COMMENT-FIELD": "Campo comentario", + "INCLUDE-COMMENT-FIELD": "Se añade un comentario al final de la pregunta.", + "ENABLE-MULTIPLICITY": "El usuario debe dar más de una respuesta a la pregunta.", + "MULTIPLICITY": "Multiplicidad", "MORE": "More.." } }, "FEEDBACK-MESSAGES": { - "SAVE-SUCCESS": "Changes were saved successfully." + "SAVE-SUCCESS": "Los cambios se grabaron correctamente." }, "ERRORS": { "INVALID-VISIBILITY-RULES": { - "MESSAGE-START": "There were invalid visibilty rules detected in ", - "MESSAGE-END": ". Would you like to fix them?", - "CONFIRM-YES": "Yes, remove invalid rules", - "CONFIRM-NO": "No, keep them.", - "REMOVE-SUCCESS": "Invalid visibility rules were removed successfully." + "MESSAGE-START": "Se han detectado reglas inválidas de visibilidad ", + "MESSAGE-END": ". Le gustaría solucionarlo?", + "CONFIRM-YES": "Sí, elimine reglas inválidas", + "CONFIRM-NO": "No, manténgalo.", + "REMOVE-SUCCESS": "Las reglas de visibilidad inválida han sido eliminadas correctamente." } } }, @@ -683,6 +698,7 @@ "GO-TO-GRANT": "Ir a la subvención de la descripción del dataset", "GO-TO-DMP": "Ir al PGD de la descripción del dataset", "SAVE": "Grabar", + "SAVE-AND-CONTINUE": "Save & Continue", "SAVE-AND-ADD": "Save & Add New", "SAVE-AND-CLOSE": "Save & Close", "SAVE-AND-FINALISE": "Grabar y finalizar", @@ -695,12 +711,16 @@ "DOWNLOAD-DOCX": "Descargar DOCX", "COPY-DATASET": "Copiar la descripción del dataset", "UPDATE-DATASET-PROFILE": "Plantilla de modificación", - "VALIDATE":"Validate" + "VALIDATE":"Validar", + "UNDO-FINALIZATION-QUESTION" :"Deshacer finalización?", + "CONFIRM" : "Sí", + "REJECT": "No" }, "MESSAGES": { "DATASET-NOT-FOUND": "No existe la descripción del dataset", "DATASET-NOT-ALLOWED": "No tiene acceso a la descricipción de este dataset", - "SUCCESS-UPDATE-DATASET-PROFILE": "Plantilla de descripción del dataset actualizada correctamente" + "SUCCESS-UPDATE-DATASET-PROFILE": "Plantilla de descripción del dataset actualizada correctamente", + "MISSING-FIELDS": "Hay campos requeridos sin rellenar. Por favor, compruebe el formulario y rellene los campos obligatorios (Los campos sin rellenar se marcan en color rojo)" }, "UPLOAD": { "UPLOAD-XML": "Importar", @@ -718,6 +738,10 @@ "CANCEL": "Cancelar", "NEXT": "Next", "ERROR-MESSAGE": "No contiene esta plantilla de descripción del dataset" + }, + "LOCKED":{ + "TITLE":"Dataset bloqueado", + "MESSAGE": "Hay alguien más modificando el datase en este momento. Puede visualizar el dataset pero no editarlo. Si queiere editarlo, por favor, inténtalo más tarde." } }, "DMP-OVERVIEW": { @@ -744,6 +768,10 @@ "MULTIPLE-DIALOG": { "ZENODO-LOGIN": "Identificarse con Zenodo", "USE-DEFAULT": "Usar el token por defecto" + }, + "LOCKED-DIALOG":{ + "TITLE": "PGD bloqeuado", + "MESSAGE":"Alguien más está modificando el PGD. Si quiere modificarlo o visualizarlo, por favor, inténtelo más tarde." } }, "DATASET-OVERVIEW": { @@ -751,6 +779,15 @@ "ERROR": { "DELETED-DATASET": "El dataset pedido ha sido eliminado", "FORBIDEN-DATASET": "No tiene permisos para acceder a este Dataset" + }, + "LOCKED":{ + "TITLE": "Dataset bloquead", + "MESSAGE": "Alquien está modificando el dataset en este momento. Si quiere modificarlo o visualizarlo, por favor, inténtelo más tarde." + }, + "FINALISE-POPUP":{ + "MESSAGE":"Será redireccionado al editar del Dataset donde podrá finalizar el dataset seleccionado. ¿Quiere proceder?", + "CONFIRM":"De acuerdo", + "CANCEL":"Cancelar" } }, "DATASET-LISTING": { @@ -788,13 +825,13 @@ "CREATE-NEW": "Crear una nueva descripción del dataset", "EXPORT": "Exportar", "INVITE-COLLABORATORS": "Invitar colaboradores", - "INVITE-SHORT": "Invite" + "INVITE-SHORT": "Invitar" }, "STATES": { - "EDITED": "Edited", - "PUBLIC": "Public", - "FINALIZED": "Finalized", - "PUBLISHED": "Published" + "EDITED": "Editado", + "PUBLIC": "Público", + "FINALIZED": "Finalizado", + "PUBLISHED": "Publicado" }, "TOOLTIP": { "DATASET-STATUS": { @@ -803,10 +840,10 @@ }, "DMP": "PGD", "GRANT": "Subvención", - "TEMPLATES-INVOLVED": "Dataset Description Template", + "TEMPLATES-INVOLVED": "Plantilla de descripción del Dataset", "VERSION": "Versión del PGD", "PART-OF": "Parte de", - "TO-DMP": "To DMP", + "TO-DMP": "Al PGD", "DMP-FOR": "PGD para" }, "EMPTY-LIST": "Nada todavía por aquí." @@ -850,7 +887,8 @@ "CLONE": "Clonar", "NEW-VERSION": "Nueva versión", "VIEW-VERSIONS": "Todas las versiones de las plantillas de descripción del dataset", - "DELETE": "Delete" + "DELETE": "Borrar", + "CREATE-DATASET-TEMPLATE": "Create Dataset Template" } }, "DATASET-UPLOAD": { @@ -896,6 +934,11 @@ "DELETE": "Borrar", "FINALIZE": "Finalizar", "DOWNLOAD-XML": "Descargar XML" + }, + "CONFIRM-DELETE-DIALOG":{ + "MESSAGE": "Desea borra esta plantilla de PGD?", + "CONFIRM-BUTTON": "Sí, eliminar", + "CANCEL-BUTTON": "No" } }, "GRANT-EDITOR": { @@ -925,48 +968,48 @@ "NEW": "Nuevo Plan de Gestión de Datos", "EDIT": "Editar", "EDIT-DMP": "Editando PGD", - "ADD-DATASET": "Adding dataset", - "EDIT-DATASET": "Editing Dataset", - "CLONE-DMP": "Clone", - "NEW-VERSION": "New Version", - "CREATE-DATASET": "Creating Dataset Description", + "ADD-DATASET": "Añadiendo dataset", + "EDIT-DATASET": "Editando Dataset", + "CLONE-DMP": "Clonar", + "NEW-VERSION": "Nueva versión", + "CREATE-DATASET": "Creando una descripción del Dataset", "SUBTITLE": "DOI", - "PREVIEW-DATASET": "Previewing Dataset" + "PREVIEW-DATASET": "Previsualizando el Dataset" }, "FIELDS": { "NAME": "Título", "RELATED-GRANT": "Subvención relacionada", "DESCRIPTION": "Descripción", - "DESCRIPTION-HINT": "Briefly describe the context and purpose of the DMP", + "DESCRIPTION-HINT": "Describa brevemente el contesto y el propósito del PGD", "ORGANISATIONS": "Organizaciones", "ORGANISATIONS-HINT": "Añadir aquí los nombre de las organizaciones participantes en la creación y revisión de los PGDs", "RESEARCHERS": "Investigadores", - "RESEARCHERS-HINT": "Add here the names of people that have produced, processed, analysed the data described in the DMP. ", + "RESEARCHERS-HINT": "Añadir aquí los nombres de las personas que han producido, procesado, analizado los datos descritos en el PGD. ", "AUTHORS": "Autores", "TEMPLATES": "Plantillas", "TEMPLATE": "Plantilla del PGD", "DATASET-TEMPLATES": "Plantillas para la descripción de dataset relacionados", - "SELECT-TEMPLATE": "Select a template to describe your dataset", + "SELECT-TEMPLATE": "Seleccione una plantilla para describir su dataset", "PROFILE": "Plantilla del PGD", "PROJECT": "Proyecto", "GRANT": "Subvención", "GRANTS": "Subvenciones", - "GRANTS-HINT": "Find the grant of your research or add new", + "GRANTS-HINT": "Encuentre la convocatoria de su investigación o añada una nueva", "FUNDER": "Financiador", - "FUNDER-HINT": "Select a funder of your research or add new", + "FUNDER-HINT": "Seleccione un financiador de su investigación o añada uno nuevo", "FUNDING-ORGANIZATIONS": "Organizaciones subvencionadoras", - "PROJECT-HINT": "Projects in Argos are perceived as distinct activities falling under a grant or common activities under different grants in collaborative schemas, eg open call for contributions. Please complete it for the grant associated to your organization if your project falls under this category. In all other cases, please leave blank and it will be autocompleted.", + "PROJECT-HINT": "Los proyectos en Argos se consideran como distintas actividades dentro de una convocatoria o actividades comunes dentro de diferentes convocatorias en esquemas colaborativos, es decir, llamada a colaboradores. Por favor, rellene la convocatoria asociada a su organización si su proyecto pertenece a esta categoría. En el resto de casos, deje el campo en blanco y será autocompletado.", "STATUS": "Estado del PGD", "EXTERNAL-SOURCE-HINT": "Lista de valores proporcioador por fuente(s) externa(s)", "COLLABORATORS": "Colaboradores", "LANGUAGE": "Idioma", - "LANGUAGE-HINT": "Select the language of your DMP", + "LANGUAGE-HINT": "Seleccione el idioma de su PGD", "LICENSE": "Licencia", "VISIBILITY": "Visibilidad", - "VISIBILITY-HINT": "Choose how the DMP is displayed in Argos. By choosing Public, the DMP is automatically made available to all users from the “Public DMPs” collection.", + "VISIBILITY-HINT": "Seleccione cómo el PGD se muestra en Argos. Si selecciona Público, el PGD se hará automáticamente disponible paraa todos los usuario de la colección “PGDs Públicos ”.", "PUBLICATION": "Fecha de publicación", "CONTACT": "Contacto", - "COST": "Costs", + "COST": "Costes", "DATASETS": "Datasets" }, "ACTIONS": { @@ -976,7 +1019,7 @@ "SAVE": "Grabar", "CANCEL": "Cancelar", "DELETE": "Borrar", - "DELETE-DATASET": "Delete Dataset", + "DELETE-DATASET": "Borrar el Dataset", "DISCARD": "Descartar", "FINALISE": "Finalizar", "LOCK": "El PGD ha sido bloqueado por otro usuario", @@ -1019,47 +1062,50 @@ "INTRO": "Un PGD en Argos consiste en la información clave sobre una investigación. Para este propósito se incluyen además de objetivos e investigaciones la documentación sobre los datasets de la investigación. En concreto la descripción de los dataset que resalta los pasos seguidos y los medios usados en las actividades administrativas.", "HINT": "Una breve descripción sobre que trata el PGD, su ámbito y objetivos", "TYPING": "Escriba más letras del nombre para que sea más probable encontrar el correcto", - "UNIQUE-IDENTIFIER": "Unique Identifier", - "RESEARCHER-IDENTIFIER-EXISTS": "Researcher identifier already exists.", - "ORGANISATION-IDENTIFIER-EXSTS": "Organisation identifier already exists.", - "IDENTIFIER-EXISTS-RESEARCHER-LIST": "This identifier is already used by a researcher in the researchers list.", - "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list." + "UNIQUE-IDENTIFIER": "Identificador único", + "RESEARCHER-IDENTIFIER-EXISTS": "El identificador del investigador ya existe.", + "ORGANISATION-IDENTIFIER-EXSTS": "El identificador de la Organización ya existe.", + "IDENTIFIER-EXISTS-RESEARCHER-LIST": "Este identificador está en uso por otro investigador en la lista de investigadores.", + "IDENTIFIER-EXISTS-ORGANISATION-LIST": "Este identificador está en uso por otra organización en la lista de organizaciones.", + "SAVE":"Grabar", + "CANCEL": "Cancelar" }, "FUNDING-INFO": { - "INTRO": "You are using the DMP editor. Add here information about the scope, funding, actors of your DMP and decide on access and re-use issues for the DMP output that you are creating.", + "INTRO": "Está utilizando el editor de PGD. Añada información aquí sobre el alcance, financiadores y participantes de su PGD y decida el acceso y cuestiones de reutilización para la salida del PGD que está creando.", "FIND": "¿No se encontró el correcto?", - "UNIQUE-IDENTIFIER": "Unique Identifier", - "IDENTIFIER-FUNDER-EXISTS": "A funder with the given identifier exists.", - "IDENTIFIER-GRANT-EXISTS": "A grant with the given identifier exists.", - "IDENTIFIER-PROJECT-EXISTS": "A project with the given identifier exists." + "UNIQUE-IDENTIFIER": "Identificador único", + "IDENTIFIER-FUNDER-EXISTS": "Existe un financiador con el identificador introducido.", + "IDENTIFIER-GRANT-EXISTS": "Existe una convocatoria con el identificador introducido.", + "IDENTIFIER-PROJECT-EXISTS": "Existe un proyecto con el identificador introducido." }, "DATASET-INFO": { "INTRO": "Un PGD en Argos consiste en la información clave sobre una investigación. Para este propósito se incluyen además de objetivos e investigaciones la documentación sobre los datasets de la investigación. En concreto la descripción de los dataset que resalta los pasos seguidos y los medios usados en las actividades administrativas.", "SECOND-INTRO": "Los datasets se documentan siguiendo plantillas predefinadas con el contenido de la descripción de los datasets. En Argos un PGD puede incluir tantas descripciones de datasets como datasets se documenten.", "FIND": "¿No se encontró el correcto?", - "HINT": "Select a template to describe your datasets. You may select more than one template." + "HINT": "Seleccione una plantilla para descrbir sus datasets. Puede seleccionar una o más plantillas." }, "LICENSE-INFO": { - "INTRO": "Each DMP can contain specific license informatation over how much open and available it is, that way you can determine who can see your dataset and for how long that data will be private", - "HINT": "A brief description of what license the DMP is using, it’s type and when it will open.", - "TYPING": "Type more letters of the name so its more possible to find the correct one." + "INTRO": "Cada PGD puede contener información específica sobre licencias, su visibidad y disponibilidad, que puede determinar quien puede ver sus dataset y cuánto tiempo estará restringido el acceso a los datos", + "HINT": "Una pequeña descripción de la licencia del PGD, su tipo y cuándo será público.", + "TYPING": "Escriba más letras o el nombre, así tiene más posibilidades de encontrar el correcto." }, "DATASET-DESCRIPTION": { - "INTRO": "Ιn general terms, your research data should be 'fair', that is findable, accessible, interoperable and re-usable. these principles precede implementation choices and do not necessarily suggest any specific technology, standard, or implementation-solution. this template is not intended as a strict technical implementation of the fair principles, it is rather inspired by fair as a general concept." + "INTRO": "En términos generales, sus datos de investigación deberían ser 'fair', es decir, encontrables, accesibles, interoperables y reutilizables. Estos principios preceden la elección de la implementación, y no sugieren una tecnología específica o estándar. Esta plantilla no pretende ser una implementación técnica estricta de los principio fair, sino que está inspirado por fair como concepto general." }, - "CHANGES": "unsaved changes", + "CHANGES": "cambios no grabados", "CLONE-DIALOG": { - "CLONE": "Clone", - "SAVE": "Save", - "CANCEL": "Cancel" + "CLONE": "Clonar", + "SAVE": "Grabar", + "CANCEL": "Cancelar" }, "LOCKED":{ - "TITLE":"DMP is locked", - "MESSAGE":"Somebody else is modifying the DMP at this moment. You may view the dataset but you cannot make any changes." + "TITLE":"PGD bloqueado", + "MESSAGE":"Alguien está modificando el PGD en este momento. Puede ver el dataset pero no puede modificarlo." } }, "DMP-PROFILE-LISTING": { "TITLE": "Plantilla del PGD", + "CREATE-DMP-TEMPLATE": "Create DMP Template", "COLUMNS": { "NAME": "Nombre", "STATUS": "Estado", @@ -1073,6 +1119,13 @@ "UPLOAD-XML-NAME": "Nombre de la Plantilla del PGD", "UPLOAD-XML-IMPORT": "Fichero", "UPLOAD-XML-FILE-CANCEL": "Cancelar" + }, + "STATUS":{ + "DRAFT": "Borrador", + "FINALIZED": "Finalizado" + }, + "MESSAGES":{ + "TEMPLATE-UPLOAD-SUCCESS":"Plantilla subida correctamente" } }, "DYNAMIC-FORM": { @@ -1137,7 +1190,7 @@ "USERS": { "LABEL": "Búsqueda", "ROLE": "Función", - "SHOW": "Show" + "SHOW": "Mostrar" }, "SELECT": "Seleccionar una opción", "LIKE": "Buscar" @@ -1146,17 +1199,17 @@ "TITLE": { "NEW": "Nuevo Plan de Gestión de Datos", "EDIT": "Editar", - "INTRO": "You are using the Dataset editor. Answer here questions that describe your data management activities.", - "INTRO-TIP": "Tip: Add new datasets to describe different types of data or disciplinary data to avoid mixing information." + "INTRO": "Está usando el editor del Dataset. Responda aquí las cuestiones que describen sus acvtividades de gestión de datos.", + "INTRO-TIP": "Sugerencia: Añadir nuevos datasets para describir distintos tipos de datos o datos multidisciplinares para evitar mezclar información." }, "FIELDS": { "NAME": "Nombre de la descripción del dataset", - "TITLE": "Title of Dataset", + "TITLE": "Título del Dataset", "DESCRIPTION": "Descripción", "PROFILE": "Plantilla", "URI": "Uri", "DMP": "PGD", - "SELECT-DMP": "Select DMP", + "SELECT-DMP": "Seleccione PGD", "DATAREPOSITORIES": "Repositorios de Datos", "REGISTRIES": "Registros", "SERVICES": "Servicios", @@ -1165,7 +1218,7 @@ "EXTERNAL-DATASET-TYPE": "Tipo de dataset externos", "EXTERNAL-DATASET-INFO": "Información dataset externos", "DATAREPOSITORIES-INFO": "Información Repositorios de Datos", - "EXTERNAL-LINK": "External link", + "EXTERNAL-LINK": "Enlace externo", "TAGS": "Etiquetas", "CREATE": "Crear nuevo" }, @@ -1175,33 +1228,43 @@ "DELETE": "Borrar", "UPDATE": "Actualizar", "DISCARD": { - "DISCARD-NEW-MESSAGE": "All changes made will be discarded and you will be redirected back to DMP Editor. Would you like to proceed?", - "DISCARD-NEW-CONFIRM": "Yes, discard and remove dataset.", + "DISCARD-NEW-MESSAGE": "Todos los cambios realizados se descartarán y será redireccionado al editor de PGDs. ¿Desea contiunar?", + "DISCARD-NEW-CONFIRM": "Sí, descatar y eliminar el dataset.", "DISCARD-NEW-DENY": "No.", - "DISCARD-EDITED-MESSAGE": "All unsaved changes will be reverted to their initial state and you will be redirected back to DMP Editor. Would you like to proceed?", - "DISCARD-EDITED-CONFIRM": "Yes, revert changes and go back.", + "DISCARD-EDITED-MESSAGE": "Todos los cambios no grabados volverán a su estado inicial y será redireccionado al editor de PGD. ¿Desea continuar?", + "DISCARD-EDITED-CONFIRM": "Sí, descartar los cambios y volver.", "DISCARD-EDITED-DENY": "No." } }, "PLACEHOLDER": { "DESCRIPTION": "Rellenar con la descripción", - "EXTERNAL-LINK": "Provide an external URL link" + "EXTERNAL-LINK": "Facilite una URL" }, "HINT": { - "DESCRIPTION": "Briefly describe the context and purpose of the Dataset", - "TITLE": "A brief description of what the ", - "TITLE-REST": " is about it’s scope and objectives." + "DESCRIPTION": "Describa brevemente el contexto y propósito del Dataset", + "TITLE": "Una breve descripción sobre el ", + "TITLE-REST": " su alcance y objetivos." }, "VERSION-DIALOG": { "ABOUT": "Versionado es automático.", "QUESTION": "Parece que su plantilla de descripción del dataset está desactualizada. ¿Queire actualizarla a la última versión?" + }, + "ERRORS":{ + "ERROR-OCCURED": "Ocurrió un error.", + "MESSAGE": "Mensaje: " + }, + "QUESTION": { + "EXTENDED-DESCRIPTION": { + "VIEW-MORE": "Ver más", + "VIEW-LESS": "Ver menos" + } } }, "DATASET-CREATE-WIZARD": { "ACTIONS": { "NEXT": "Siguiente", "BACK": "Anterior", - "SAVE": "Granar" + "SAVE": "Grabar" }, "FIRST-STEP": { "TITLE": "PGD", @@ -1222,16 +1285,16 @@ "LISTING": { "TITLE": "Usuarios", "EMAIL": "Email", - "LAST-LOGGED-IN": "Last Logged In", + "LAST-LOGGED-IN": "Última visita", "LABEL": "Etiqueta", "ROLES": "Función", "NAME": "Nombre", "PERMISSIONS": "Permisos", - "EXPORT": "Export users" + "EXPORT": "Exportar usuarios" }, "ACTIONS": { - "EDIT": "Edit", - "SAVE": "Save" + "EDIT": "Editar", + "SAVE": "Grabar" } }, "TYPES": { @@ -1239,7 +1302,7 @@ "ADMIN": "Administrador", "USER": "Usuario", "MANAGER": "Director", - "DATASET-TEMPLATE-EDITOR": "Dataset Template Editor" + "DATASET-TEMPLATE-EDITOR": "Editor de planitllas de Dataset" }, "DMP-PROFILE-FIELD": { "DATA-TYPE": { @@ -1269,7 +1332,7 @@ "SOURCE:": "Fuente: ", "NO-SOURCE": "No enlazado con la fuente", "OUTPUT": "Salida", - "SELECT": "Select" + "SELECT": "Seleccionar" }, "DMP": { "FINALISED": "Finalizado", @@ -1294,6 +1357,7 @@ "FREE-TEXT": "Texto libre", "RADIO-BOX": "Casilla", "TEXT-AREA": "Area de texto", + "RICH-TEXT-AREA": "Rich Text Area", "DATE-PICKER": "Campo de entrada para fecha", "EXTERNAL-DATASETS": "Datasets externos", "DATA-REPOSITORIES": "Repositorios de datos", @@ -1508,8 +1572,9 @@ "PERSONAL-USAGE": "Uso personal", "PUBLIC-USAGE": "Public Usage", "DATASET-DESCRIPTIONS": "Descripciones de los datasets", - "PUBLIC-DMPS": "Public DMPs", - "PUBLIC-DATASETS": "Public Dataset Descriptions", + "DATASET-DESCRIPTIONS-DASHBOARD-TEXT": "Descripciones de los datasets", + "PUBLIC-DMPS": "PGDs públicos", + "PUBLIC-DATASETS": "Descripciónes de Dataset Públicos", "RELATED-ORGANISATIONS": "Organizaciones relacionadas", "DRAFTS": "Borradores", "ALL": "Todo", @@ -1524,18 +1589,18 @@ "ADD-DMP-DESCRIPTION": "Añadir la descripción de un PGD" }, "TOUR-GUIDE": { - "DMP": "This is your dashboard. You can view and edit all DMPs that you have either contributed to or created yourself.", - "START-NEW": "Create your DMP with Start new DMP.", - "IMPORT-DMP": "You can import a DMP", - "START-WIZARD": "or create new in Argos.", - "DATASET": "This is your dashboard. You can view and edit all Datasets that you have either contributed to or created yourself.", - "NEW-DATASET": "With Add Dataset you can describe new datasets anytime in the research process.", - "GOT-IT": "Got it!", - "LEAVE-TOUR": "Leave Tour" + "DMP": "Esta es su consola, puede ver y editar todos los PGD en los que ha participado o creado.", + "START-NEW": "Cree su PGD con la opción Iniciar nuevo PGD.", + "IMPORT-DMP": "Puede importar un PGD", + "START-WIZARD": "o crear uno nuevo en Argos.", + "DATASET": "Esta es su consola. Puede ver todos los Datasets en lo que ha participado o creado.", + "NEW-DATASET": "Con la opción Añadir Dataset puede describir nuevos datasets en cualquier momento del proceso de investigación.", + "GOT-IT": "¡Lo tengo!", + "LEAVE-TOUR": "Abandonar la visita virtual" }, "ADD-NEW-DATASET": { - "OPTIONS-NOT-ENOUGH": "Are those options not enough?", - "START-NEW-DMP": "Start new DMP" + "OPTIONS-NOT-ENOUGH": "¿No son estas opciones suficientes?", + "START-NEW-DMP": "Iniciar un nuevo PGD" } }, "USER-DIALOG": { @@ -1550,14 +1615,14 @@ "TIMEZONE": "Zona horaria", "CULTURE": "Cultura", "LANGUAGE": "Idioma", - "CONNECTED-WITH": "Connected with", - "NAME": "Name", - "ORGANIZATION": "Organization", - "ROLE": "Role", - "SELECT-ROLE": "Select Role", - "ACCOUNTS": "Accounts", + "CONNECTED-WITH": "Conectado con", + "NAME": "Nombre", + "ORGANIZATION": "Organización", + "ROLE": "Función", + "SELECT-ROLE": "Seleccione función", + "ACCOUNTS": "Cuentas", "EMAILS": "Emails", - "YOUR-EMAIL": "Your Email" + "YOUR-EMAIL": "Su Email" }, "ASSOCIATED-DMPS": "PGDs asociados", "DMPS": { @@ -1573,27 +1638,27 @@ "DESCRIPTION": "Enlazar una cuenta en Zenodo" }, "ROLE-ORGANIZATION": { - "FACULTY": "Faculty", - "LIBRARIAN": "Librarian", - "RESEARCHER": "Researcher", - "STUDENT": "Student (BA/BSc, MSc)", - "EARLY-CAREER-RESEARCHER": "Early Career Researcher (PhD candidate, post-graduate)", - "RESEARCH-ADMINISTRATOR": "Research Administrator", - "REPOSITORY-MANAGER": "Repository Manager", - "RESEARCH-INFRASTRUCTURE": "Research Infrastructure", - "SERVICE-PROVIDER": "Service Provider", - "PUBLISHER": "Publisher", - "RESEARCH-FUNDER": "Research Funder", - "POLICY-MAKER": "Policymaker", - "SME-INDUSTRY": "SME/ Industry", - "OTHER": "Other" + "FACULTY": "Facultad", + "LIBRARIAN": "Bibliotecario", + "RESEARCHER": "Investigador", + "STUDENT": "Estudiante", + "EARLY-CAREER-RESEARCHER": "Investigador en fase inicial (candidato doctor, post-graduado)", + "RESEARCH-ADMINISTRATOR": "Administrador de investigación", + "REPOSITORY-MANAGER": "Gestor de repositorio", + "RESEARCH-INFRASTRUCTURE": "Infrastructura de investigación", + "SERVICE-PROVIDER": "Proveedor de servicios", + "PUBLISHER": "Editorial", + "RESEARCH-FUNDER": "Financiador de investigación", + "POLICY-MAKER": "Legislador", + "SME-INDUSTRY": "PYME", + "OTHER": "Otro" }, "ACTIONS": { - "SAVE": "Save", - "LINK-NEW": "Link new", - "LINK-NEW-ACCOUNT": "Link new account", - "ADD": "Add", - "CANCEL": "Cancel" + "SAVE": "Grabar", + "LINK-NEW": "Nuevo enlace", + "LINK-NEW-ACCOUNT": "Enlazar nueva cuenta", + "ADD": "Añadir", + "CANCEL": "Cancelar" } }, "DATASET-REFERENCED-MODELS": { @@ -1662,7 +1727,7 @@ }, "IMPACT": "Esta acción finaliza su PGD, y no podrá editarla otra vez. ", "AFTER-FINALIZATION": "Después de finalizar su PGD puede publicarlo, y estará disponible públicamente en la herramienta ARGOS.", - "INVALID": "Invalid" + "INVALID": "Inválido" }, "DRAFTS": { "FOR-DMP": "Para el PGD:", @@ -1756,6 +1821,6 @@ "NONE": "Ninguno", "DRAFT": "Borrador", "FINALIZED": "Finalizado", - "DELETED": "Deleted" + "DELETED": "Eliminado" } -} \ No newline at end of file +} diff --git a/dmp-frontend/src/assets/i18n/gr.json b/dmp-frontend/src/assets/i18n/gr.json index 5990361c2..5ba205304 100644 --- a/dmp-frontend/src/assets/i18n/gr.json +++ b/dmp-frontend/src/assets/i18n/gr.json @@ -289,14 +289,18 @@ "DATASET-TEMPLATE-DESCRIPTION-HINT": "A brief description of what the Dataset is about, it's scope and objectives.", "DATASET-TEMPLATE-LANGUAGE": "Dataset template language", "DATASET-TEMPLATE-SELECT-LANGUAGE": "Select a language", - "DATASET-TEMPLATE-USERS": "Users", - "DATASET-TEMPLATE-NO-USERS-YET": "... No users yet", - "DATASET-TEMPLATE-REMOVE-USER": "Remove User", - "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add User", + "DATASET-TEMPLATE-USERS": "Editors", + "DATASET-TEMPLATE-USERS-HINT": "Add editors and save changes to notify them.", + "DATASET-TEMPLATE-REMOVE-USER": "Remove Editor", + "DATASET-TEMPLATE-NO-USERS-YET": "... No editors yet", + "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add Editor", "DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER": "Dataset template description", "UNTITLED": "Untitled", "QUESTION": "Question", - "TEMPLATE-OUTLINE": "Template outline" + "TEMPLATE-OUTLINE": "Template outline", + "ERRORS":{ + "USER-NOT-FOUND":"Ο χρήστης δεν βρέθηκε." + } }, "PAGE-INFO": { "PAGE-NAME": "Chapter Name", @@ -310,7 +314,8 @@ "CREATE-FIRST-SECTION": "Create the first chapter", "NOTHING-HERE-HINT": "Nothing here yet.", "START-CREATING-PAGE-START": "Start by ", - "START-CREATING-PAGE-END": "creating the first chapter." + "START-CREATING-PAGE-END": "creating the first chapter.", + "CREATE-SECTION": "Create section" }, "PAGE": "Chapter" }, @@ -386,6 +391,8 @@ "MULTIPLICITY-CHECKBOX": "Πολλαπλότητα", "FIELD-TEXT-AREA-TITLE": "Περιοχή Κειμένου Δεδομένων", "FIELD-TEXT-AREA-PLACEHOLDER": "Τοποθέτηση placeholder", + "FIELD-RICH-TEXT-AREA-TITLE": "Περιοχή Πλούσιου Κειμένου Δεδομένων", + "FIELD-RICH-TEXT-AREA-PLACEHOLDER": "Τοποθέτηση Placeholder", "FIELD-BOOLEAN-DECISION-TITLE": "Boolean Δεδομένα", "FIELD-BOOLEAN-DECISION-PLACEHOLDER": "Τοποθέτηση placeholder", "FIELD-CHECKBOX-TITLE": "Πλαίσιο Ελέγχου Δεδομένων", @@ -441,7 +448,13 @@ "FIELD-DATASET-IDENTIFIER-TITLE": "Dataset Identifier Data", "FIELD-DATASET-IDENTIFIER-PLACEHOLDER": "Input placeholder Text", "FIELD-VALIDATOR-TITLE": "Validator Data", - "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text" + "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text", + "EXTERNAL-DATASET-TYPE-NAME": "Type", + "EXTERNAL-DATASET-TYPES":{ + "PRODUCED": "Produced dataset", + "REUSED": "Reused dataset", + "OTHER": "Other" + } }, "ERROR-MESSAGES": { "FIELD-OTHER-SOURCES-REQUIRED": "At least one source must be provided.", @@ -523,7 +536,9 @@ "ADD-VISIBILITY-RULE": "Add Conditional Question", "DELETE-INPUT": "Delete this input", "PREVIEW": "Preview", - "NOT-INITIALIZED": "Not initialized yet" + "NOT-INITIALIZED": "Not initialized yet", + "MOVE-UP": "Move this input above", + "MOVE-DOWN": "Move this input below" }, "FIELDSET": { "ADD-INPUT": "Add input", @@ -683,6 +698,7 @@ "GO-TO-GRANT": "Μετάβαση σε Περιγραφή Συνόλου Δεδομένων της Επιχορήγησης", "GO-TO-DMP": "Μετάβαση σε Περιγραφή Συνόλου Δεδομένων του Σχεδίου Διαχείρισης Δεδομένων", "SAVE": "Save", + "SAVE-AND-CONTINUE": "Αποθήκευση & Συνέχεια", "SAVE-AND-ADD": "Αποθήκευση & Προσθήκη Νέου", "SAVE-AND-CLOSE": " Αποθήκευση & Κλείσμο", "SAVE-AND-FINALISE": "Αποθήκευση και Οριστικοποίηση", @@ -695,12 +711,16 @@ "DOWNLOAD-DOCX": "Ληψη DOCX", "COPY-DATASET": "Αντιφραφή Περιγραφής Συνόλου Δεδομένων", "UPDATE-DATASET-PROFILE": "Ενημέρωση Template", - "VALIDATE":"Επικύρωση" + "VALIDATE":"Επικύρωση", + "UNDO-FINALIZATION-QUESTION" :"Αναίρεση Οριστικοποίησης?", + "CONFIRM" : "Ναι", + "REJECT": "Oχι" }, "MESSAGES": { "DATASET-NOT-FOUND": "Η Περιγραφή Συνόλου Δεδομένων δεν υπάρχει", "DATASET-NOT-ALLOWED": "Δεν έχετε πρόσβαση σε αυτή την Περιγραφή Συνόλου Δεδομένων", - "SUCCESS-UPDATE-DATASET-PROFILE": "Επιτυχία ενημέρωσης της Περιγραφής Συνόλου Δεδομένων" + "SUCCESS-UPDATE-DATASET-PROFILE": "Επιτυχία ενημέρωσης της Περιγραφής Συνόλου Δεδομένων", + "MISSING-FIELDS": "Υπάρχουν ορισμένα υποχρεωτικά πεδία που δεν έχουν συμπληρωθεί. Ελέγξτε τη φόρμα και βεβαιωθείτε ότι έχουν συμπληρωθεί όλα τα απαιτούμενα πεδία. (Τα πεδία που λείπουν επισημαίνονται με κόκκινο χρώμα)" }, "UPLOAD": { "UPLOAD-XML": "Εισαγωγή", @@ -718,6 +738,10 @@ "CANCEL": "Ακύρωση", "NEXT": "Επόμενο", "ERROR-MESSAGE": "Αυτο το Template Περιγραφής Συνόλου Δεδομένων δεν περιέχεται" + }, + "LOCKED":{ + "TITLE":"Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. You may view the dataset but you cannot make any changes. If you would like to modify it please come back later." } }, "DMP-OVERVIEW": { @@ -744,6 +768,10 @@ "MULTIPLE-DIALOG": { "ZENODO-LOGIN": "Σύνδεση με Zenodo", "USE-DEFAULT": "Χρήση Προκαθορισμένου Token" + }, + "LOCKED-DIALOG":{ + "TITLE": "DMP is locked", + "MESSAGE":"Somebody else is modifying the DMP at this moment. If you would like to modify or view it, please come back later." } }, "DATASET-OVERVIEW": { @@ -751,6 +779,15 @@ "ERROR": { "DELETED-DATASET": "H επιλεγμένη Περιγραφή Δεδομένων θα διαγραφεί", "FORBIDEN-DATASET": "Δεν επιτρέπεται η πρόσβαση σε αυτή την Περιγραφή Δεδομένων" + }, + "LOCKED":{ + "TITLE": "Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. If you would like to modify or view it, please come back later." + }, + "FINALISE-POPUP":{ + "MESSAGE":"You will be directed to Dataset Editor where you can finalize the selected dataset. Would you like to proceed?", + "CONFIRM":"OK", + "CANCEL":"Cancel" } }, "DATASET-LISTING": { @@ -850,7 +887,8 @@ "CLONE": "Κλώνος", "NEW-VERSION": "Νέα Έκδοση", "VIEW-VERSIONS": "Όλες οι εκδόσεις των Templates Περιγραφής Συνόλου Δεδομένων", - "DELETE": "Delete" + "DELETE": "Delete", + "CREATE-DATASET-TEMPLATE": "Create Dataset Template" } }, "DATASET-UPLOAD": { @@ -896,6 +934,11 @@ "DELETE": "Διαγραφή", "FINALIZE": "Οριστικοποίηση", "DOWNLOAD-XML": "Ληψη XML" + }, + "CONFIRM-DELETE-DIALOG":{ + "MESSAGE": "Would you like to delete this DMP template?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" } }, "GRANT-EDITOR": { @@ -1023,7 +1066,9 @@ "RESEARCHER-IDENTIFIER-EXISTS": "Researcher identifier already exists.", "ORGANISATION-IDENTIFIER-EXSTS": "Organisation identifier already exists.", "IDENTIFIER-EXISTS-RESEARCHER-LIST": "This identifier is already used by a researcher in the researchers list.", - "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list." + "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list.", + "SAVE":"Αποθήκευση", + "CANCEL": "Ακύρωση" }, "FUNDING-INFO": { "INTRO": "You are using the DMP editor. Add here information about the scope, funding, actors of your DMP and decide on access and re-use issues for the DMP output that you are creating.", @@ -1060,6 +1105,7 @@ }, "DMP-PROFILE-LISTING": { "TITLE": "Templates Σχεδίων Διαχείρισης Δεδομένων", + "CREATE-DMP-TEMPLATE": "Create DMP Template", "COLUMNS": { "NAME": "Τίτλος", "STATUS": "Κατάσταση", @@ -1073,6 +1119,13 @@ "UPLOAD-XML-NAME": "Τίτλος Template Σχεδίου Διαχείρισης Δεδομένων", "UPLOAD-XML-IMPORT": "Αρχείο", "UPLOAD-XML-FILE-CANCEL": "Ακύρωση" + }, + "STATUS":{ + "DRAFT": "Draft", + "FINALIZED": "Finalized" + }, + "MESSAGES":{ + "TEMPLATE-UPLOAD-SUCCESS":"Template successfully uploaded" } }, "DYNAMIC-FORM": { @@ -1195,6 +1248,16 @@ "VERSION-DIALOG": { "ABOUT": "Η δημιουργία νέας έκδοσης συμβαίνει αυτόματα", "QUESTION": "Το Template το Συνόλου Δεδομένων που επιλέξατε χρειάζεται επικαιροποίηση. Θα θέλατε να το ενημερώσετε στην πιο πρόσφατη έκδοσή του;" + }, + "ERRORS":{ + "ERROR-OCCURED": "Προέκυψε κάποιο σφάλμα.", + "MESSAGE": "Μήνυμα: " + }, + "QUESTION": { + "EXTENDED-DESCRIPTION": { + "VIEW-MORE": "Δείτε περισσότερα", + "VIEW-LESS": "Δείτε λιγότερα" + } } }, "DATASET-CREATE-WIZARD": { @@ -1294,6 +1357,7 @@ "FREE-TEXT": "Ελεύθερο Κείμενο", "RADIO-BOX": "Πλαίσιο Επιλογής", "TEXT-AREA": "Περιοχή Κειμένου", + "RICH-TEXT-AREA": "Περιοχή Πλούσιου Κειμένου", "DATE-PICKER": "Επιλογή Ημερομηνίας", "EXTERNAL-DATASETS": "Εξωτερικά Σύνολα Δεδομένων", "DATA-REPOSITORIES": "Αποθετήρια Δεδομένων", @@ -1508,6 +1572,7 @@ "PERSONAL-USAGE": "Προσωπική Χρήση", "PUBLIC-USAGE": "Δημόσια Χρήση", "DATASET-DESCRIPTIONS": "Περιγραφές Συνόλου Δεδομένων", + "DATASET-DESCRIPTIONS-DASHBOARD-TEXT": "Περιγραφές Συνόλου Δεδομένων", "PUBLIC-DMPS": "Δημόσια Σχέδια Διαχείρισης Δεδομένων", "PUBLIC-DATASETS": "Δημόσιες Περιγραφές Συνόλου Δεδομένων", "RELATED-ORGANISATIONS": "Σχετικοί Οργανισμοί", @@ -1758,4 +1823,4 @@ "FINALIZED": "Οριστικοποιημένα", "DELETED": "Deleted" } -} \ No newline at end of file +} diff --git a/dmp-frontend/src/assets/i18n/pt.json b/dmp-frontend/src/assets/i18n/pt.json index 2323a493d..76503ef92 100644 --- a/dmp-frontend/src/assets/i18n/pt.json +++ b/dmp-frontend/src/assets/i18n/pt.json @@ -289,14 +289,18 @@ "DATASET-TEMPLATE-DESCRIPTION-HINT": "Uma breve descrição do que é o modelo de dados, o seu âmbito e objetivos.", "DATASET-TEMPLATE-LANGUAGE": "Idioma do modelo de dados", "DATASET-TEMPLATE-SELECT-LANGUAGE": "Selecione o idioma", - "DATASET-TEMPLATE-USERS": "Utilizadores", - "DATASET-TEMPLATE-REMOVE-USER": "Remover Utilizador", - "DATASET-TEMPLATE-NO-USERS-YET": "... Ainda sem utilizadores", - "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validar e adicionar utilizador", + "DATASET-TEMPLATE-USERS": "Editors", + "DATASET-TEMPLATE-USERS-HINT": "Add editors and save changes to notify them.", + "DATASET-TEMPLATE-REMOVE-USER": "Remove Editor", + "DATASET-TEMPLATE-NO-USERS-YET": "... No editors yet", + "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add Editor", "DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER": "Descrição do modelo de dados", "UNTITLED": "Sem título", "QUESTION": "Questão", - "TEMPLATE-OUTLINE": "Esquema do modelo" + "TEMPLATE-OUTLINE": "Esquema do modelo", + "ERRORS":{ + "USER-NOT-FOUND":"Usuário não encontrado." + } }, "PAGE-INFO": { "PAGE-NAME": "Nome do capítulo", @@ -310,7 +314,8 @@ "CREATE-FIRST-SECTION": "Criar o primeiro capítulo", "NOTHING-HERE-HINT": "Ainda sem informação.", "START-CREATING-PAGE-START": "Começar por ", - "START-CREATING-PAGE-END": "criar o primeiro capítulo." + "START-CREATING-PAGE-END": "criar o primeiro capítulo.", + "CREATE-SECTION": "Criar secção" }, "PAGE": "Capítulo" }, @@ -386,6 +391,8 @@ "MULTIPLICITY-CHECKBOX": "Multiplicidade", "FIELD-TEXT-AREA-TITLE": "Dados da Área de Texto", "FIELD-TEXT-AREA-PLACEHOLDER": "Sugestão de Preenchimento", + "FIELD-RICH-TEXT-AREA-TITLE": "Rich Text Area Data", + "FIELD-RICH-TEXT-AREA-PLACEHOLDER": "Input Placeholder Text", "FIELD-BOOLEAN-DECISION-TITLE": "Dados Booleanos de Decisão", "FIELD-BOOLEAN-DECISION-PLACEHOLDER": "Sugestão de Preenchimento", "FIELD-CHECKBOX-TITLE": "Dados da caixa de verificação", @@ -441,7 +448,13 @@ "FIELD-DATASET-IDENTIFIER-TITLE": "Dados de Identificador de Dataset", "FIELD-DATASET-IDENTIFIER-PLACEHOLDER": "Texto de Entrada", "FIELD-VALIDATOR-TITLE": "Dados de Validação", - "FIELD-VALIDATOR-PLACEHOLDER": "Texto de Entrada" + "FIELD-VALIDATOR-PLACEHOLDER": "Texto de Entrada", + "EXTERNAL-DATASET-TYPE-NAME": "Type", + "EXTERNAL-DATASET-TYPES":{ + "PRODUCED": "Produced dataset", + "REUSED": "Reused dataset", + "OTHER": "Other" + } }, "ERROR-MESSAGES": { "FIELD-OTHER-SOURCES-REQUIRED": "Deve ser fornecida pelo menos uma fonte.", @@ -523,7 +536,9 @@ "ADD-VISIBILITY-RULE": "Adione Questão Condicionada", "DELETE-INPUT": "Eliminar este input", "PREVIEW": "Pré-visualização", - "NOT-INITIALIZED": "Ainda não iniciado" + "NOT-INITIALIZED": "Ainda não iniciado", + "MOVE-UP": "Move this input above", + "MOVE-DOWN": "Move this input below" }, "FIELDSET": { "ADD-INPUT": "Adicionar novo input", @@ -566,8 +581,8 @@ "PUBLISHED": "Publicado", "VERSION": "Versão", "CONTAINED-DATASETS": "Contém Datasets", - "TEXT-INFO": "A informação contida no Plano de Gestão de Dados (PGD) demonstra como os Datasets foram recolhidos e/ou gerados, como foram processados e analisados, que ferramentas, normas e metodologias utilizaram, mas também fornece informação de onde e como os Datasets são alojados, publicados e preservados, incluindo quaisquer custos associados com recursos humanos dedicados a atividades de curadoria/admnistração dos dados ou custos para aquisição ou construção de serviços de gestão de dados.", - "TEXT-INFO-QUESTION": "Não tem a certeza de como um PGD é na prática? Pesquise PGD Públicos e", + "TEXT-INFO": "A informação contida no Plano de Gestão de Dados (PGD) demonstra como os dados foram recolhidos e/ou gerados, como foram processados e analisados, que ferramentas, normas e metodologias utilizaram. Fornece também informação de onde e como os dados são alojados, publicados e preservados.", + "TEXT-INFO-QUESTION": "Não tem a certeza de como um Plano de Gestão de Dados (PGD) é na prática? Pesquise “PGD Públicos” e", "LINK-ZENODO": "Comunidade LIBER no Zenodo", "GET-IDEA": "para ter uma ideia!", "SORT-BY": "Ordenar por", @@ -683,6 +698,7 @@ "GO-TO-GRANT": "Ir para o Grant do Dataset", "GO-TO-DMP": "Ir para o PGD do Dataset", "SAVE": "Guardar", + "SAVE-AND-CONTINUE": "Save & Continue", "SAVE-AND-ADD": "Guardar e Adicionar Novo", "SAVE-AND-CLOSE": "Guardar e Fechar", "SAVE-AND-FINALISE": "Guardar e Concluir", @@ -695,12 +711,16 @@ "DOWNLOAD-DOCX": "Exportar para DOCX", "COPY-DATASET": "Copiar o Dataset", "UPDATE-DATASET-PROFILE": "Atualizar o Modelo", - "VALIDATE":"Validate" + "VALIDATE":"Validate", + "UNDO-FINALIZATION-QUESTION" :"Desfazer a finalização??", + "CONFIRM" : "Sim", + "REJECT": "Não" }, "MESSAGES": { "DATASET-NOT-FOUND": "O Dataset não existe", "DATASET-NOT-ALLOWED": "Não tem acesso a este Dataset", - "SUCCESS-UPDATE-DATASET-PROFILE": "O Dataset foi atualizado com sucesso" + "SUCCESS-UPDATE-DATASET-PROFILE": "O Dataset foi atualizado com sucesso", + "MISSING-FIELDS": "Alguns campos obrigatórios não foram preenchidos. Por favor, verifique o formulário e certifique-se de que todos os campos obrigatórios foram preenchidos. (Os campos ausentes são marcados em vermelho)" }, "UPLOAD": { "UPLOAD-XML": "Importar", @@ -718,6 +738,15 @@ "CANCEL": "Cancelar", "NEXT": "Seguinte", "ERROR-MESSAGE": "Não contém este Modelo de Dados" + }, + "LOCKED":{ + "TITLE":"Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. You may view the dataset but you cannot make any changes. If you would like to modify it please come back later." + }, + "FINALISE-POPUP":{ + "MESSAGE":"You will be directed to Dataset Editor where you can finalize the selected dataset. Would you like to proceed?", + "CONFIRM":"OK", + "CANCEL":"Cancelar" } }, "DMP-OVERVIEW": { @@ -744,6 +773,10 @@ "MULTIPLE-DIALOG": { "ZENODO-LOGIN": "Entre com o Zenodo", "USE-DEFAULT": "Use o Token" + }, + "LOCKED-DIALOG":{ + "TITLE": "DMP is locked", + "MESSAGE":"Somebody else is modifying the DMP at this moment. If you would like to modify or view it, please come back later." } }, "DATASET-OVERVIEW": { @@ -751,6 +784,10 @@ "ERROR": { "DELETED-DATASET": "O Dataset requerido foi eliminado", "FORBIDEN-DATASET": "Não está autorizado o acesso a este Dataset" + }, + "LOCKED":{ + "TITLE": "Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. If you would like to modify or view it, please come back later." } }, "DATASET-LISTING": { @@ -850,7 +887,8 @@ "CLONE": "Duplicar", "NEW-VERSION": "Nova Versão", "VIEW-VERSIONS": "Ver todas as versões do Dataset", - "DELETE": "Eliminar" + "DELETE": "Eliminar", + "CREATE-DATASET-TEMPLATE": "Criar Modelo de Dados" } }, "DATASET-UPLOAD": { @@ -896,6 +934,11 @@ "DELETE": "Eliminar", "FINALIZE": "Concluir", "DOWNLOAD-XML": "Exportar para XML" + }, + "CONFIRM-DELETE-DIALOG":{ + "MESSAGE": "Would you like to delete this DMP template?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" } }, "GRANT-EDITOR": { @@ -1023,7 +1066,9 @@ "RESEARCHER-IDENTIFIER-EXISTS": "O identificador do investigador já existe.", "ORGANISATION-IDENTIFIER-EXSTS": "O identificador da organização já existe.", "IDENTIFIER-EXISTS-RESEARCHER-LIST": "Este identificador está já a ser utilizado por um investigador incluído na lista.", - "IDENTIFIER-EXISTS-ORGANISATION-LIST": "Este identificador está já a ser utilizado por uma organização incluída na lista." + "IDENTIFIER-EXISTS-ORGANISATION-LIST": "Este identificador está já a ser utilizado por uma organização incluída na lista.", + "SAVE":"Guardar", + "CANCEL": "Cancelar" }, "FUNDING-INFO": { "INTRO": "Um Plano de Gestão de Dados (PGD) permite uma maior proximidade com o local onde os seus dados são gerados, analisados e armazenados. O Argos é uma ferramenta aberta, extensível e colaborativa que disponibiliza Planos de Gestão de Dados FAIR e Abertos.", @@ -1060,6 +1105,7 @@ }, "DMP-PROFILE-LISTING": { "TITLE": "PGDs", + "CREATE-DMP-TEMPLATE": "Create DMP Template", "COLUMNS": { "NAME": "Nome", "STATUS": "Estado", @@ -1073,6 +1119,13 @@ "UPLOAD-XML-NAME": "Nome do PGD", "UPLOAD-XML-IMPORT": "Ficheiro", "UPLOAD-XML-FILE-CANCEL": "Cancelar" + }, + "STATUS":{ + "DRAFT": "Draft", + "FINALIZED": "Finalized" + }, + "MESSAGES":{ + "TEMPLATE-UPLOAD-SUCCESS":"Template successfully uploaded" } }, "DYNAMIC-FORM": { @@ -1195,6 +1248,16 @@ "VERSION-DIALOG": { "ABOUT": "O versionamento é automático.", "QUESTION": "O seu modelo de dados está desatualizado. Pretende atualizá-lo de acordo com a última versão?" + }, + "ERRORS":{ + "ERROR-OCCURED": "Um erro ocorreu.", + "MESSAGE": "Mensangem: " + }, + "QUESTION": { + "EXTENDED-DESCRIPTION": { + "VIEW-MORE": "View more", + "VIEW-LESS": "View less" + } } }, "DATASET-CREATE-WIZARD": { @@ -1294,6 +1357,7 @@ "FREE-TEXT": "Texto Livre", "RADIO-BOX": "Escolha Múltipla", "TEXT-AREA": "Área de Texto", + "RICH-TEXT-AREA": "Rich Text Area", "DATE-PICKER": "Escolha de Data", "EXTERNAL-DATASETS": "Datasets Externos", "DATA-REPOSITORIES": "Repositórios de Dados", @@ -1508,6 +1572,7 @@ "PERSONAL-USAGE": "Uso Pessoal", "PUBLIC-USAGE": "Uso Público", "DATASET-DESCRIPTIONS": "Datasets", + "DATASET-DESCRIPTIONS-DASHBOARD-TEXT": "", "PUBLIC-DMPS": "PGDs Públicos", "PUBLIC-DATASETS": "Datasets Públicos", "RELATED-ORGANISATIONS": "Organizações Relacionadas", @@ -1515,8 +1580,8 @@ "ALL": "TODOS", "EMPTY-LIST": "Sem Informação", "LATEST-ACTIVITY": "Última Atividade", - "DMP-ABOUT-BEG": "A criação de um Plano de Gestão de Dados (PGD) no Argos consiste em fornecer informação-chave sobre a investigação, tal como, a sua finalidade, os seus objetivos e quais os investigadores envolvidos, como também sobre a documentação relativa aos dados de investigação", - "DMP-ABOUT-END": "que permite evidenciar os passos dados e os meios utilizados para uma gestão mais eficaz dos dados a produzir ou que já tenham sido produzidos.", + "DMP-ABOUT-BEG": "A criação de um Plano de Gestão de Dados (PGD) no Argos consiste em fornecer informação-chave sobre a investigação, tal como, a sua finalidade, os seus objetivos e quais os investigadores envolvidos, como também sobre a documentação relativa aos dados de investigação que permite evidenciar os passos dados e os meios utilizados para uma gestão mais eficaz dos dados a produzir ou que já tenham sido produzidos.", + "DMP-ABOUT-END": "", "SELECT-DMP": "Selecione um PGD para o seu Dataset", "ACTIONS": { "ADD-DATASET-DESCRIPTION": "Adicione um Dataset", @@ -1762,5 +1827,8 @@ "File format is not supported": "Ficheiro a importar deverá ser em formato .json", "You cannot Remove Datamanagement Plan with Datasets": "Não pode remover o Plano de Gestão de Dados porque contém pelo menos um modelo de dados associado", "Failed to create DOI for the Data Management Plan.": "Não foi possível criar um DOI para o Plano de Gestão de Dados", - "No entity found for query": "Não foi possível eliminar o item pretendido" -} \ No newline at end of file + "No entity found for query": "Não foi possível eliminar o item pretendido", + "Index: 0, Size: 0" : "Ficheiro a importar deverá ser em formato .json", + "Field value of": "O campo", + "must be filled": "deve ser preenchido" +} diff --git a/dmp-frontend/src/assets/i18n/sk.json b/dmp-frontend/src/assets/i18n/sk.json index 1a0eed604..3a8726434 100644 --- a/dmp-frontend/src/assets/i18n/sk.json +++ b/dmp-frontend/src/assets/i18n/sk.json @@ -289,14 +289,18 @@ "DATASET-TEMPLATE-DESCRIPTION-HINT": "A brief description of what the Dataset is about, it's scope and objectives.", "DATASET-TEMPLATE-LANGUAGE": "Dataset template language", "DATASET-TEMPLATE-SELECT-LANGUAGE": "Select a language", - "DATASET-TEMPLATE-USERS": "Users", - "DATASET-TEMPLATE-REMOVE-USER": "Remove User", - "DATASET-TEMPLATE-NO-USERS-YET": "... No users yet", - "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add User", + "DATASET-TEMPLATE-USERS": "Editors", + "DATASET-TEMPLATE-USERS-HINT": "Add editors and save changes to notify them.", + "DATASET-TEMPLATE-REMOVE-USER": "Remove Editor", + "DATASET-TEMPLATE-NO-USERS-YET": "... No editors yet", + "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add Editor", "DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER": "Dataset template description", "UNTITLED": "Untitled", "QUESTION": "Question", - "TEMPLATE-OUTLINE": "Template outline" + "TEMPLATE-OUTLINE": "Template outline", + "ERRORS":{ + "USER-NOT-FOUND":"User not found." + } }, "PAGE-INFO": { "PAGE-NAME": "Chapter Name", @@ -310,7 +314,8 @@ "CREATE-FIRST-SECTION": "Create the first chapter", "NOTHING-HERE-HINT": "Nothing here yet.", "START-CREATING-PAGE-START": "Start by ", - "START-CREATING-PAGE-END": "creating the first chapter." + "START-CREATING-PAGE-END": "creating the first chapter.", + "CREATE-SECTION": "Create section" }, "PAGE": "Chapter" }, @@ -386,6 +391,8 @@ "MULTIPLICITY-CHECKBOX": "Multiplicity", "FIELD-TEXT-AREA-TITLE": "Text Area Data", "FIELD-TEXT-AREA-PLACEHOLDER": "Input Placeholder", + "FIELD-RICH-TEXT-AREA-TITLE": "Rich Text Area Data", + "FIELD-RICH-TEXT-AREA-PLACEHOLDER": "Input Placeholder Text", "FIELD-BOOLEAN-DECISION-TITLE": "Boolean Decision Data", "FIELD-BOOLEAN-DECISION-PLACEHOLDER": "Input Placeholder", "FIELD-CHECKBOX-TITLE": "Checkbox Data", @@ -441,7 +448,13 @@ "FIELD-DATASET-IDENTIFIER-TITLE": "Dataset Identifier Data", "FIELD-DATASET-IDENTIFIER-PLACEHOLDER": "Input placeholder Text", "FIELD-VALIDATOR-TITLE": "Validator Data", - "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text" + "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text", + "EXTERNAL-DATASET-TYPE-NAME": "Type", + "EXTERNAL-DATASET-TYPES":{ + "PRODUCED": "Produced dataset", + "REUSED": "Reused dataset", + "OTHER": "Other" + } }, "ERROR-MESSAGES": { "FIELD-OTHER-SOURCES-REQUIRED": "At least one source must be provided.", @@ -523,7 +536,9 @@ "ADD-VISIBILITY-RULE": "Add Conditional Question", "DELETE-INPUT": "Delete this input", "PREVIEW": "Preview", - "NOT-INITIALIZED": "Not initialized yet" + "NOT-INITIALIZED": "Not initialized yet", + "MOVE-UP": "Move this input above", + "MOVE-DOWN": "Move this input below" }, "FIELDSET": { "ADD-INPUT": "Add input", @@ -683,6 +698,7 @@ "GO-TO-GRANT": "Prejsť na Grant súboru dát", "GO-TO-DMP": "Prejsť na DMP súboru dát", "SAVE": "Uložiť", + "SAVE-AND-CONTINUE": "Save & Continue", "SAVE-AND-ADD": "Uložiť a pridať nový", "SAVE-AND-CLOSE": "Uložiť a zavrieť", "SAVE-AND-FINALISE": "Uložiť a dokončiť", @@ -695,12 +711,16 @@ "DOWNLOAD-DOCX": "Stiahnuť DOCX", "COPY-DATASET": "Kopírovať súbor dát", "UPDATE-DATASET-PROFILE": "Aktualizovať šablónu", - "VALIDATE":"Validate" + "VALIDATE":"Validate", + "UNDO-FINALIZATION-QUESTION" :"Undo finalization?", + "CONFIRM" : "Yes", + "REJECT": "No" }, "MESSAGES": { "DATASET-NOT-FOUND": "Súbor dát neexistuje", "DATASET-NOT-ALLOWED": "K tomuto súboru dát nemáte prístup", - "SUCCESS-UPDATE-DATASET-PROFILE": "Šablóna súboru dát bola úspešne aktualizovaná" + "SUCCESS-UPDATE-DATASET-PROFILE": "Šablóna súboru dát bola úspešne aktualizovaná", + "MISSING-FIELDS": "There are some required fields left unfilled. Please check the form and make sure that all required fields are filled. (Missing fields are marked in red color)" }, "UPLOAD": { "UPLOAD-XML": "Importovať", @@ -718,6 +738,10 @@ "CANCEL": "Zrušiť", "NEXT": "Ďalej", "ERROR-MESSAGE": "Neobsahuje túto šablónu súboru dát" + }, + "LOCKED":{ + "TITLE":"Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. You may view the dataset but you cannot make any changes. If you would like to modify it please come back later." } }, "DMP-OVERVIEW": { @@ -744,6 +768,10 @@ "MULTIPLE-DIALOG": { "ZENODO-LOGIN": "Prihlásiť sa do Zenodo", "USE-DEFAULT": "Použite predvolený token" + }, + "LOCKED-DIALOG":{ + "TITLE": "DMP is locked", + "MESSAGE":"Somebody else is modifying the DMP at this moment. If you would like to modify or view it, please come back later." } }, "DATASET-OVERVIEW": { @@ -751,6 +779,15 @@ "ERROR": { "DELETED-DATASET": "Požadovaný DMP je vymazaný", "FORBIDEN-DATASET": "K tomuto DMP nemáte povolený prístup" + }, + "LOCKED":{ + "TITLE": "Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. If you would like to modify or view it, please come back later." + }, + "FINALISE-POPUP":{ + "MESSAGE":"You will be directed to Dataset Editor where you can finalize the selected dataset. Would you like to proceed?", + "CONFIRM":"OK", + "CANCEL":"Cancel" } }, "DATASET-LISTING": { @@ -850,7 +887,8 @@ "CLONE": "Klonovať", "NEW-VERSION": "Nová verzia", "VIEW-VERSIONS": "Všetky verzie šablóny súboru dát", - "DELETE": "Delete" + "DELETE": "Delete", + "CREATE-DATASET-TEMPLATE": "Create Dataset Template" } }, "DATASET-UPLOAD": { @@ -896,6 +934,11 @@ "DELETE": "Vymazať", "FINALIZE": "Dokončiť", "DOWNLOAD-XML": "Stiahnuť XML" + }, + "CONFIRM-DELETE-DIALOG":{ + "MESSAGE": "Would you like to delete this DMP template?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" } }, "GRANT-EDITOR": { @@ -1023,7 +1066,9 @@ "RESEARCHER-IDENTIFIER-EXISTS": "Researcher identifier already exists.", "ORGANISATION-IDENTIFIER-EXSTS": "Organisation identifier already exists.", "IDENTIFIER-EXISTS-RESEARCHER-LIST": "This identifier is already used by a researcher in the researchers list.", - "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list." + "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list.", + "SAVE":"Save", + "CANCEL": "Cancel" }, "FUNDING-INFO": { "INTRO": "Plán manažmentu dát (DMP, z angl. Data Management Plan) tvoria vaše plány manažmentu dát, ktoré sú bližšie miestu, kde sú vygenerované, analyzované a uložené. Argos je otvorený, rozširovateľný, kolaboratívny nástroj, ktorý podporuje plány manažmentu otvorených dát a FAIR dát (Open and FAIR Data Management Plans).", @@ -1060,6 +1105,7 @@ }, "DMP-PROFILE-LISTING": { "TITLE": "Šablóny DMP", + "CREATE-DMP-TEMPLATE": "Create DMP Template", "COLUMNS": { "NAME": "Názov", "STATUS": "Stav", @@ -1073,6 +1119,13 @@ "UPLOAD-XML-NAME": "Názov šablóny DMP", "UPLOAD-XML-IMPORT": "Súbor", "UPLOAD-XML-FILE-CANCEL": "Zrušiť" + }, + "STATUS":{ + "DRAFT": "Draft", + "FINALIZED": "Finalized" + }, + "MESSAGES":{ + "TEMPLATE-UPLOAD-SUCCESS":"Template successfully uploaded" } }, "DYNAMIC-FORM": { @@ -1195,6 +1248,16 @@ "VERSION-DIALOG": { "ABOUT": "Verziovanie je automatizované.", "QUESTION": "Zdá sa, že Vaša šablóna súboru dát je zastaraná. Chcete ju aktualizovať na najnovšiu verziu?" + }, + "ERRORS":{ + "ERROR-OCCURED": "An error occured.", + "MESSAGE": "Message: " + }, + "QUESTION": { + "EXTENDED-DESCRIPTION": { + "VIEW-MORE": "View more", + "VIEW-LESS": "View less" + } } }, "DATASET-CREATE-WIZARD": { @@ -1294,6 +1357,7 @@ "FREE-TEXT": "Neštruktúrovaný text (Free Text)", "RADIO-BOX": "Pole výberu (Radio Box)", "TEXT-AREA": "Textová oblasť (Text Area)", + "RICH-TEXT-AREA": "Rich Text Area", "DATE-PICKER": "Výber dátumu (Date Picker) ", "EXTERNAL-DATASETS": "Externé súbory dát (External Datasets)", "DATA-REPOSITORIES": "Repozitáre dát (Data Repositories)", @@ -1508,6 +1572,7 @@ "PERSONAL-USAGE": "Osobné použitie", "PUBLIC-USAGE": "Verejné použitie", "DATASET-DESCRIPTIONS": "Súbory dát", + "DATASET-DESCRIPTIONS-DASHBOARD-TEXT": "Súbory dát", "PUBLIC-DMPS": "Verejné DMP", "PUBLIC-DATASETS": "Verejné súbory dát", "RELATED-ORGANISATIONS": "Ďalšie organizácie", @@ -1758,4 +1823,4 @@ "FINALIZED": "Dokončené", "DELETED": "Deleted" } -} \ No newline at end of file +} diff --git a/dmp-frontend/src/assets/i18n/sr.json b/dmp-frontend/src/assets/i18n/sr.json index 9f12e3745..be76fe7be 100644 --- a/dmp-frontend/src/assets/i18n/sr.json +++ b/dmp-frontend/src/assets/i18n/sr.json @@ -289,14 +289,18 @@ "DATASET-TEMPLATE-DESCRIPTION-HINT": "A brief description of what the Dataset is about, it's scope and objectives.", "DATASET-TEMPLATE-LANGUAGE": "Dataset template language", "DATASET-TEMPLATE-SELECT-LANGUAGE": "Select a language", - "DATASET-TEMPLATE-USERS": "Users", - "DATASET-TEMPLATE-NO-USERS-YET": "... No users yet", - "DATASET-TEMPLATE-REMOVE-USER": "Remove User", - "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add User", + "DATASET-TEMPLATE-USERS": "Editors", + "DATASET-TEMPLATE-USERS-HINT": "Add editors and save changes to notify them.", + "DATASET-TEMPLATE-REMOVE-USER": "Remove Editor", + "DATASET-TEMPLATE-NO-USERS-YET": "... No editors yet", + "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add Editor", "DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER": "Dataset template description", "UNTITLED": "Untitled", "QUESTION": "Question", - "TEMPLATE-OUTLINE": "Template outline" + "TEMPLATE-OUTLINE": "Template outline", + "ERRORS":{ + "USER-NOT-FOUND":"User not found." + } }, "PAGE-INFO": { "PAGE-NAME": "Chapter Name", @@ -310,7 +314,8 @@ "CREATE-FIRST-SECTION": "Create the first chapter", "NOTHING-HERE-HINT": "Nothing here yet.", "START-CREATING-PAGE-START": "Start by ", - "START-CREATING-PAGE-END": "creating the first chapter." + "START-CREATING-PAGE-END": "creating the first chapter.", + "CREATE-SECTION": "Create section" }, "PAGE": "Chapter" }, @@ -386,6 +391,8 @@ "MULTIPLICITY-CHECKBOX": "Višestrukost", "FIELD-TEXT-AREA-TITLE": "Polje za tekst", "FIELD-TEXT-AREA-PLACEHOLDER": "Polje za unos", + "FIELD-RICH-TEXT-AREA-TITLE": "Rich Text Area Data", + "FIELD-RICH-TEXT-AREA-PLACEHOLDER": "Input Placeholder Text", "FIELD-BOOLEAN-DECISION-TITLE": "Podaci za odlučivanje", "FIELD-BOOLEAN-DECISION-PLACEHOLDER": "Polje za unos", "FIELD-CHECKBOX-TITLE": "Podaci polja za potvrdu", @@ -441,7 +448,13 @@ "FIELD-DATASET-IDENTIFIER-TITLE": "Dataset Identifier Data", "FIELD-DATASET-IDENTIFIER-PLACEHOLDER": "Input placeholder Text", "FIELD-VALIDATOR-TITLE": "Validator Data", - "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text" + "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text", + "EXTERNAL-DATASET-TYPE-NAME": "Type", + "EXTERNAL-DATASET-TYPES":{ + "PRODUCED": "Produced dataset", + "REUSED": "Reused dataset", + "OTHER": "Other" + } }, "ERROR-MESSAGES": { "FIELD-OTHER-SOURCES-REQUIRED": "At least one source must be provided.", @@ -523,7 +536,9 @@ "ADD-VISIBILITY-RULE": "Add Conditional Question", "DELETE-INPUT": "Delete this input", "PREVIEW": "Preview", - "NOT-INITIALIZED": "Not initialized yet" + "NOT-INITIALIZED": "Not initialized yet", + "MOVE-UP": "Move this input above", + "MOVE-DOWN": "Move this input below" }, "FIELDSET": { "ADD-INPUT": "Add input", @@ -683,6 +698,7 @@ "GO-TO-GRANT": "Pređite na grant za skupove podataka", "GO-TO-DMP": "Pređite na Plan za skup podataka", "SAVE": "Sačuvajte", + "SAVE-AND-CONTINUE": "Save & Continue", "SAVE-AND-ADD": "Sačuvajte i dodajte novi", "SAVE-AND-CLOSE": "Sačuvajte i zatvorite", "SAVE-AND-FINALISE": "Sačuvajte i završite", @@ -695,12 +711,16 @@ "DOWNLOAD-DOCX": "Preuzmite DOCX", "COPY-DATASET": "Kopirajte skup podataka", "UPDATE-DATASET-PROFILE": "Ažurirajte obrazac", - "VALIDATE":"Validate" + "VALIDATE":"Validate", + "UNDO-FINALIZATION-QUESTION" :"Undo finalization?", + "CONFIRM" : "Yes", + "REJECT": "No" }, "MESSAGES": { "DATASET-NOT-FOUND": "Skup podataka ne postoji", "DATASET-NOT-ALLOWED": "Nemate pristup ovom skupu podataka", - "SUCCESS-UPDATE-DATASET-PROFILE": "Obrazac skupa podataka je ažuriran uspešno" + "SUCCESS-UPDATE-DATASET-PROFILE": "Obrazac skupa podataka je ažuriran uspešno", + "MISSING-FIELDS": "There are some required fields left unfilled. Please check the form and make sure that all required fields are filled. (Missing fields are marked in red color)" }, "UPLOAD": { "UPLOAD-XML": "Uvezite", @@ -718,6 +738,10 @@ "CANCEL": "Otkažite", "NEXT": "Sledeće", "ERROR-MESSAGE": "Ne sadrži obrazac za podatke" + }, + "LOCKED":{ + "TITLE":"Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. You may view the dataset but you cannot make any changes. If you would like to modify it please come back later." } }, "DMP-OVERVIEW": { @@ -744,6 +768,10 @@ "MULTIPLE-DIALOG": { "ZENODO-LOGIN": "Prijavite se pomoću Zenodo naloga", "USE-DEFAULT": "Koristite podrazumevani znak" + }, + "LOCKED-DIALOG":{ + "TITLE": "DMP is locked", + "MESSAGE":"Somebody else is modifying the DMP at this moment. If you would like to modify or view it, please come back later." } }, "DATASET-OVERVIEW": { @@ -751,6 +779,15 @@ "ERROR": { "DELETED-DATASET": "Traženi skup podataka je obrisan", "FORBIDEN-DATASET": "Nije Vam dozvoljeno da pristupite ovom skupu podataka" + }, + "LOCKED":{ + "TITLE": "Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. If you would like to modify or view it, please come back later." + }, + "FINALISE-POPUP":{ + "MESSAGE":"You will be directed to Dataset Editor where you can finalize the selected dataset. Would you like to proceed?", + "CONFIRM":"OK", + "CANCEL":"Cancel" } }, "DATASET-LISTING": { @@ -850,7 +887,8 @@ "CLONE": "Napravite kopiju", "NEW-VERSION": "Nova verzija", "VIEW-VERSIONS": "Sve verzije obrazaca za skupove podataka", - "DELETE": "Delete" + "DELETE": "Delete", + "CREATE-DATASET-TEMPLATE": "Create Dataset Template" } }, "DATASET-UPLOAD": { @@ -896,6 +934,11 @@ "DELETE": "Obrišite", "FINALIZE": "Dovršite", "DOWNLOAD-XML": "Preuzmite XML" + }, + "CONFIRM-DELETE-DIALOG":{ + "MESSAGE": "Would you like to delete this DMP template?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" } }, "GRANT-EDITOR": { @@ -1023,7 +1066,9 @@ "RESEARCHER-IDENTIFIER-EXISTS": "Researcher identifier already exists.", "ORGANISATION-IDENTIFIER-EXSTS": "Organisation identifier already exists.", "IDENTIFIER-EXISTS-RESEARCHER-LIST": "This identifier is already used by a researcher in the researchers list.", - "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list." + "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list.", + "SAVE":"Save", + "CANCEL": "Cancel" }, "FUNDING-INFO": { "INTRO": "Plan upravljanja podacima (eng. Data Management Plan) se sastoji od Vaših planova za upravljanje podacima i sadrži informacije kako su nastali, kako su analizirani i na koji način su sačuvani podaci. Argos je otvoren kolaboracioni alat sa mogućnošću nadogradnje i koji podržava otvorene FAIR principe za upravljanje podacima.", @@ -1060,6 +1105,7 @@ }, "DMP-PROFILE-LISTING": { "TITLE": "Obrasci za Planove", + "CREATE-DMP-TEMPLATE": "Create DMP Template", "COLUMNS": { "NAME": "Ime", "STATUS": "Status", @@ -1073,6 +1119,13 @@ "UPLOAD-XML-NAME": "Ime obrasca za Plan", "UPLOAD-XML-IMPORT": "Datoteka", "UPLOAD-XML-FILE-CANCEL": "Otkažite" + }, + "STATUS":{ + "DRAFT": "Draft", + "FINALIZED": "Finalized" + }, + "MESSAGES":{ + "TEMPLATE-UPLOAD-SUCCESS":"Template successfully uploaded" } }, "DYNAMIC-FORM": { @@ -1195,6 +1248,16 @@ "VERSION-DIALOG": { "ABOUT": "Verzionisanje je automatsko.", "QUESTION": "Izgleda da je obrazac za skup podataka zastareo. Da li želite da koristite najnoviju verziju?" + }, + "ERRORS":{ + "ERROR-OCCURED": "An error occured.", + "MESSAGE": "Message: " + }, + "QUESTION": { + "EXTENDED-DESCRIPTION": { + "VIEW-MORE": "View more", + "VIEW-LESS": "View less" + } } }, "DATASET-CREATE-WIZARD": { @@ -1294,6 +1357,7 @@ "FREE-TEXT": "Slobodan tekst", "RADIO-BOX": "Polje za unos", "TEXT-AREA": "Tekst", + "RICH-TEXT-AREA": "Rich Text Area", "DATE-PICKER": "Alat za odabir podataka", "EXTERNAL-DATASETS": "Spoljni skupovi podataka", "DATA-REPOSITORIES": "Repozitorijumi podataka", @@ -1508,6 +1572,7 @@ "PERSONAL-USAGE": "Lična upotreba", "PUBLIC-USAGE": "Javna upotreba", "DATASET-DESCRIPTIONS": "Skupovi podataka", + "DATASET-DESCRIPTIONS-DASHBOARD-TEXT": "Skupovi podataka", "PUBLIC-DMPS": "Javno dostupni Planovi", "PUBLIC-DATASETS": "Javno dostupni skupovi podataka", "RELATED-ORGANISATIONS": "Povezane institucije", diff --git a/dmp-frontend/src/assets/i18n/tr.json b/dmp-frontend/src/assets/i18n/tr.json index 71fde2ca2..05590c73d 100644 --- a/dmp-frontend/src/assets/i18n/tr.json +++ b/dmp-frontend/src/assets/i18n/tr.json @@ -289,14 +289,18 @@ "DATASET-TEMPLATE-DESCRIPTION-HINT": "A brief description of what the Dataset is about, it's scope and objectives.", "DATASET-TEMPLATE-LANGUAGE": "Dataset template language", "DATASET-TEMPLATE-SELECT-LANGUAGE": "Select a language", - "DATASET-TEMPLATE-USERS": "Users", - "DATASET-TEMPLATE-REMOVE-USER": "Remove User", - "DATASET-TEMPLATE-NO-USERS-YET": "... No users yet", - "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add User", + "DATASET-TEMPLATE-USERS": "Editors", + "DATASET-TEMPLATE-USERS-HINT": "Add editors and save changes to notify them.", + "DATASET-TEMPLATE-REMOVE-USER": "Remove Editor", + "DATASET-TEMPLATE-NO-USERS-YET": "... No editors yet", + "DATASET-TEMPLATE-VALIDATE-AND-ADD-USER": "Validate and Add Editor", "DATASET-TEMPLATE-DESCRIPTION-PLACEHOLDER": "Dataset template description", "UNTITLED": "Untitled", "QUESTION": "Question", - "TEMPLATE-OUTLINE": "Template outline" + "TEMPLATE-OUTLINE": "Template outline", + "ERRORS":{ + "USER-NOT-FOUND":"User not found." + } }, "PAGE-INFO": { "PAGE-NAME": "Chapter Name", @@ -310,7 +314,8 @@ "CREATE-FIRST-SECTION": "Create the first chapter", "NOTHING-HERE-HINT": "Nothing here yet.", "START-CREATING-PAGE-START": "Start by ", - "START-CREATING-PAGE-END": "creating the first chapter." + "START-CREATING-PAGE-END": "creating the first chapter.", + "CREATE-SECTION": "Create section" }, "PAGE": "Chapter" }, @@ -386,6 +391,8 @@ "MULTIPLICITY-CHECKBOX": "Çeşitlilik", "FIELD-TEXT-AREA-TITLE": "Metin Alanı Verisi", "FIELD-TEXT-AREA-PLACEHOLDER": "Girdi Yertutucu", + "FIELD-RICH-TEXT-AREA-TITLE": "Rich Text Area Data", + "FIELD-RICH-TEXT-AREA-PLACEHOLDER": "Input Placeholder Text", "FIELD-BOOLEAN-DECISION-TITLE": "Boolean Kararı Verisi", "FIELD-BOOLEAN-DECISION-PLACEHOLDER": "Girdi Yertutucu", "FIELD-CHECKBOX-TITLE": "Onay Kutusu Verisi", @@ -441,7 +448,13 @@ "FIELD-DATASET-IDENTIFIER-TITLE": "Dataset Identifier Data", "FIELD-DATASET-IDENTIFIER-PLACEHOLDER": "Input placeholder Text", "FIELD-VALIDATOR-TITLE": "Validator Data", - "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text" + "FIELD-VALIDATOR-PLACEHOLDER": "Input placeholder Text", + "EXTERNAL-DATASET-TYPE-NAME": "Type", + "EXTERNAL-DATASET-TYPES":{ + "PRODUCED": "Produced dataset", + "REUSED": "Reused dataset", + "OTHER": "Other" + } }, "ERROR-MESSAGES": { "FIELD-OTHER-SOURCES-REQUIRED": "At least one source must be provided.", @@ -523,7 +536,9 @@ "ADD-VISIBILITY-RULE": "Add Conditional Question", "DELETE-INPUT": "Delete this input", "PREVIEW": "Preview", - "NOT-INITIALIZED": "Not initialized yet" + "NOT-INITIALIZED": "Not initialized yet", + "MOVE-UP": "Move this input above", + "MOVE-DOWN": "Move this input below" }, "FIELDSET": { "ADD-INPUT": "Add input", @@ -683,6 +698,7 @@ "GO-TO-GRANT": "Veri Seti Hibesine Git", "GO-TO-DMP": "Veri Seti VYP'na Git", "SAVE": "Kaydet", + "SAVE-AND-CONTINUE": "Save & Continue", "SAVE-AND-ADD": "Kaydet & Yeni Ekle", "SAVE-AND-CLOSE": "Kaydet & Kapat", "SAVE-AND-FINALISE": "Kaydet ve Bitir", @@ -695,12 +711,16 @@ "DOWNLOAD-DOCX": "DOCX İndir", "COPY-DATASET": "Veri Seti Tanımını Kopyala", "UPDATE-DATASET-PROFILE": "Şablonu Güncelle", - "VALIDATE":"Validate" + "VALIDATE":"Validate", + "UNDO-FINALIZATION-QUESTION" :"Undo finalization?", + "CONFIRM" : "Yes", + "REJECT": "No" }, "MESSAGES": { "DATASET-NOT-FOUND": "Veri Seti mevcut değildir", "DATASET-NOT-ALLOWED": "Bu Veri Setine erişiminiz yok", - "SUCCESS-UPDATE-DATASET-PROFILE": "Veri Seti Tanımı başarılı olarak güncellendi" + "SUCCESS-UPDATE-DATASET-PROFILE": "Veri Seti Tanımı başarılı olarak güncellendi", + "MISSING-FIELDS": "There are some required fields left unfilled. Please check the form and make sure that all required fields are filled. (Missing fields are marked in red color)" }, "UPLOAD": { "UPLOAD-XML": "İçeri Aktar", @@ -718,6 +738,10 @@ "CANCEL": "İptal", "NEXT": "İleri", "ERROR-MESSAGE": "Bu Veri Seti Şablonunu İçermiyor" + }, + "LOCKED":{ + "TITLE":"Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. You may view the dataset but you cannot make any changes. If you would like to modify it please come back later." } }, "DMP-OVERVIEW": { @@ -744,6 +768,10 @@ "MULTIPLE-DIALOG": { "ZENODO-LOGIN": "Zenodo ile oturum aç", "USE-DEFAULT": "Mevcut Jetonu Kullan" + }, + "LOCKED-DIALOG":{ + "TITLE": "DMP is locked", + "MESSAGE":"Somebody else is modifying the DMP at this moment. If you would like to modify or view it, please come back later." } }, "DATASET-OVERVIEW": { @@ -751,6 +779,15 @@ "ERROR": { "DELETED-DATASET": "Seçili Veri Seti silindi", "FORBIDEN-DATASET": "Bu veri setine erişim izniniz yok" + }, + "LOCKED":{ + "TITLE": "Dataset is locked", + "MESSAGE": "Somebody else is modifying the dataset at this moment. If you would like to modify or view it, please come back later." + }, + "FINALISE-POPUP":{ + "MESSAGE":"You will be directed to Dataset Editor where you can finalize the selected dataset. Would you like to proceed?", + "CONFIRM":"OK", + "CANCEL":"Cancel" } }, "DATASET-LISTING": { @@ -850,7 +887,8 @@ "CLONE": "Çoğalt", "NEW-VERSION": "Yeni Sürüm", "VIEW-VERSIONS": "Tüm Veri Seti Tanımı Şablon Sürümleri", - "DELETE": "Sil" + "DELETE": "Sil", + "CREATE-DATASET-TEMPLATE": "Create Dataset Template" } }, "DATASET-UPLOAD": { @@ -896,6 +934,11 @@ "DELETE": "Sil", "FINALIZE": "Tamamla", "DOWNLOAD-XML": "XML İndir" + }, + "CONFIRM-DELETE-DIALOG":{ + "MESSAGE": "Would you like to delete this DMP template?", + "CONFIRM-BUTTON": "Yes, delete", + "CANCEL-BUTTON": "No" } }, "GRANT-EDITOR": { @@ -1023,7 +1066,9 @@ "RESEARCHER-IDENTIFIER-EXISTS": "Researcher identifier already exists.", "ORGANISATION-IDENTIFIER-EXSTS": "Organisation identifier already exists.", "IDENTIFIER-EXISTS-RESEARCHER-LIST": "This identifier is already used by a researcher in the researchers list.", - "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list." + "IDENTIFIER-EXISTS-ORGANISATION-LIST": "This identifier is already used by an organisation in the organisations list.", + "SAVE":"Save", + "CANCEL": "Cancel" }, "FUNDING-INFO": { "INTRO": "You are using the DMP editor. Add here information about the scope, funding, actors of your DMP and decide on access and re-use issues for the DMP output that you are creating.", @@ -1060,6 +1105,7 @@ }, "DMP-PROFILE-LISTING": { "TITLE": "VYP Şablonları", + "CREATE-DMP-TEMPLATE": "Create DMP Template", "COLUMNS": { "NAME": "İsim", "STATUS": "Durum", @@ -1073,6 +1119,13 @@ "UPLOAD-XML-NAME": "VYP Şablonu İsmi", "UPLOAD-XML-IMPORT": "Dosya", "UPLOAD-XML-FILE-CANCEL": "İptal" + }, + "STATUS":{ + "DRAFT": "Draft", + "FINALIZED": "Finalized" + }, + "MESSAGES":{ + "TEMPLATE-UPLOAD-SUCCESS":"Template successfully uploaded" } }, "DYNAMIC-FORM": { @@ -1195,6 +1248,16 @@ "VERSION-DIALOG": { "ABOUT": "Sürüm Otomatiktir.", "QUESTION": "Veri Seti Şablonunuz güncel değil. Son sürüme güncellenmesini ister misiniz?" + }, + "ERRORS":{ + "ERROR-OCCURED": "An error occured.", + "MESSAGE": "Message: " + }, + "QUESTION": { + "EXTENDED-DESCRIPTION": { + "VIEW-MORE": "View more", + "VIEW-LESS": "View less" + } } }, "DATASET-CREATE-WIZARD": { @@ -1294,6 +1357,7 @@ "FREE-TEXT": "Serbest Metin", "RADIO-BOX": "Radio Box", "TEXT-AREA": "Metin Alanı", + "RICH-TEXT-AREA": "Rich Text Area", "DATE-PICKER": "Tarih Seçici", "EXTERNAL-DATASETS": "Harici Veri Setleri", "DATA-REPOSITORIES": "Veri Depoları", @@ -1508,6 +1572,7 @@ "PERSONAL-USAGE": "Kişisel Kullanım", "PUBLIC-USAGE": "Genel Kullanım", "DATASET-DESCRIPTIONS": "Veri Setleri", + "DATASET-DESCRIPTIONS-DASHBOARD-TEXT": "Veri Setleri", "PUBLIC-DMPS": "Herkese açık VYP'ler", "PUBLIC-DATASETS": "Herkese açık Veri Setleri", "RELATED-ORGANISATIONS": "Bağlantılı Kurumlar", @@ -1758,4 +1823,4 @@ "FINALIZED": "Tamamlandı", "DELETED": "Silindi" } -} \ No newline at end of file +} diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture1.png b/dmp-frontend/src/assets/images/guide/pt/Picture1.png new file mode 100644 index 000000000..df300831d Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture1.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture10.png b/dmp-frontend/src/assets/images/guide/pt/Picture10.png new file mode 100644 index 000000000..2a12e8c67 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture10.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture11.png b/dmp-frontend/src/assets/images/guide/pt/Picture11.png new file mode 100644 index 000000000..a343e526d Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture11.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture12.png b/dmp-frontend/src/assets/images/guide/pt/Picture12.png new file mode 100644 index 000000000..bebc36145 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture12.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture13.png b/dmp-frontend/src/assets/images/guide/pt/Picture13.png new file mode 100644 index 000000000..65739d70b Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture13.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture14.png b/dmp-frontend/src/assets/images/guide/pt/Picture14.png new file mode 100644 index 000000000..247726b0a Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture14.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture15.png b/dmp-frontend/src/assets/images/guide/pt/Picture15.png new file mode 100644 index 000000000..ffa855b8f Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture15.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture16.png b/dmp-frontend/src/assets/images/guide/pt/Picture16.png new file mode 100644 index 000000000..f5a792e7b Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture16.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture17.png b/dmp-frontend/src/assets/images/guide/pt/Picture17.png new file mode 100644 index 000000000..2d6f8ad70 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture17.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture18.png b/dmp-frontend/src/assets/images/guide/pt/Picture18.png new file mode 100644 index 000000000..08f903a0c Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture18.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture19.png b/dmp-frontend/src/assets/images/guide/pt/Picture19.png new file mode 100644 index 000000000..c350bf0bd Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture19.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture2.png b/dmp-frontend/src/assets/images/guide/pt/Picture2.png new file mode 100644 index 000000000..eddb0746a Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture2.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture20.png b/dmp-frontend/src/assets/images/guide/pt/Picture20.png new file mode 100644 index 000000000..10e789cf1 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture20.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture21.png b/dmp-frontend/src/assets/images/guide/pt/Picture21.png new file mode 100644 index 000000000..08cd3c6f3 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture21.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture22.png b/dmp-frontend/src/assets/images/guide/pt/Picture22.png new file mode 100644 index 000000000..7d79d84c1 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture22.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture23.png b/dmp-frontend/src/assets/images/guide/pt/Picture23.png new file mode 100644 index 000000000..340aaeaa6 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture23.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture24.png b/dmp-frontend/src/assets/images/guide/pt/Picture24.png new file mode 100644 index 000000000..9498ad56c Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture24.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture25.png b/dmp-frontend/src/assets/images/guide/pt/Picture25.png new file mode 100644 index 000000000..b99c51900 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture25.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture26.png b/dmp-frontend/src/assets/images/guide/pt/Picture26.png new file mode 100644 index 000000000..63e1d9f14 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture26.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture27.png b/dmp-frontend/src/assets/images/guide/pt/Picture27.png new file mode 100644 index 000000000..6bf546fbc Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture27.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture28.png b/dmp-frontend/src/assets/images/guide/pt/Picture28.png new file mode 100644 index 000000000..c08ffb179 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture28.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture29.png b/dmp-frontend/src/assets/images/guide/pt/Picture29.png new file mode 100644 index 000000000..ee10c17a8 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture29.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture3.png b/dmp-frontend/src/assets/images/guide/pt/Picture3.png new file mode 100644 index 000000000..df300831d Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture3.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture30.png b/dmp-frontend/src/assets/images/guide/pt/Picture30.png new file mode 100644 index 000000000..d3fcf94bc Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture30.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture31.png b/dmp-frontend/src/assets/images/guide/pt/Picture31.png new file mode 100644 index 000000000..307e68f82 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture31.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture32.png b/dmp-frontend/src/assets/images/guide/pt/Picture32.png new file mode 100644 index 000000000..1c3683198 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture32.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture33.png b/dmp-frontend/src/assets/images/guide/pt/Picture33.png new file mode 100644 index 000000000..8a8f36227 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture33.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture34.png b/dmp-frontend/src/assets/images/guide/pt/Picture34.png new file mode 100644 index 000000000..7215a9244 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture34.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture35.png b/dmp-frontend/src/assets/images/guide/pt/Picture35.png new file mode 100644 index 000000000..18ec4223a Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture35.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture36.png b/dmp-frontend/src/assets/images/guide/pt/Picture36.png new file mode 100644 index 000000000..a9a60ce16 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture36.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture37.png b/dmp-frontend/src/assets/images/guide/pt/Picture37.png new file mode 100644 index 000000000..c8b3861fc Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture37.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture38.png b/dmp-frontend/src/assets/images/guide/pt/Picture38.png new file mode 100644 index 000000000..fd752297a Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture38.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture39.png b/dmp-frontend/src/assets/images/guide/pt/Picture39.png new file mode 100644 index 000000000..d0fc6d066 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture39.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture4.png b/dmp-frontend/src/assets/images/guide/pt/Picture4.png new file mode 100644 index 000000000..3c552cd30 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture4.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture40.png b/dmp-frontend/src/assets/images/guide/pt/Picture40.png new file mode 100644 index 000000000..31a51c618 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture40.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture41.png b/dmp-frontend/src/assets/images/guide/pt/Picture41.png new file mode 100644 index 000000000..6c4c67a90 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture41.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture5.png b/dmp-frontend/src/assets/images/guide/pt/Picture5.png new file mode 100644 index 000000000..aaca9388e Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture5.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture6.png b/dmp-frontend/src/assets/images/guide/pt/Picture6.png new file mode 100644 index 000000000..ffba11fad Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture6.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture7.png b/dmp-frontend/src/assets/images/guide/pt/Picture7.png new file mode 100644 index 000000000..c2efa98dd Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture7.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture8.png b/dmp-frontend/src/assets/images/guide/pt/Picture8.png new file mode 100644 index 000000000..fa5b410f9 Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture8.png differ diff --git a/dmp-frontend/src/assets/images/guide/pt/Picture9.png b/dmp-frontend/src/assets/images/guide/pt/Picture9.png new file mode 100644 index 000000000..8016e6b7a Binary files /dev/null and b/dmp-frontend/src/assets/images/guide/pt/Picture9.png differ diff --git a/dmp-frontend/src/assets/scss/blue-theme.scss b/dmp-frontend/src/assets/scss/blue-theme.scss index 41868c1f7..b1efe01e8 100644 --- a/dmp-frontend/src/assets/scss/blue-theme.scss +++ b/dmp-frontend/src/assets/scss/blue-theme.scss @@ -1,3 +1,4 @@ +@use '~@angular/material' as mat; @import "app/ui/misc/navigation/navigation.component.scss"; @import "~@angular/material/theming"; // @import '../../../node_modules/@angular/material/theming'; @@ -67,25 +68,25 @@ $app-blue-theme-accent-palette: ( ) ); -$app-blue-theme-primary: mat-palette($app-blue-theme-primary-palette); -$app-blue-theme-accent: mat-palette($app-blue-theme-accent-palette, A200, A100, A400); -$app-blue-theme-warn: mat-palette($mat-red); +$app-blue-theme-primary: mat.define-palette($app-blue-theme-primary-palette); +$app-blue-theme-accent: mat.define-palette($app-blue-theme-accent-palette, A200, A100, A400); +$app-blue-theme-warn: mat.define-palette(mat.$red-palette); $app-blue-theme-background: ( - status-bar: map_get($mat-grey, 300), - app-bar: map_get($mat-grey, 100), - background: map_get($mat-grey, 50), + status-bar: map_get(mat.$grey-palette, 300), + app-bar: map_get(mat.$grey-palette, 100), + background: map_get(mat.$grey-palette, 50), hover: rgba(black, 0.04), card: white, dialog: white, disabled-button: rgba(black, 0.12), raised-button: white, focused-button: $dark-focused, - selected-button: map_get($mat-grey, 300), - selected-disabled-button: map_get($mat-grey, 400), - disabled-button-toggle: map_get($mat-grey, 200), - unselected-chip: map_get($mat-grey, 300), - disabled-list-option: map_get($mat-grey, 200) + selected-button: map_get(mat.$grey-palette, 300), + selected-disabled-button: map_get(mat.$grey-palette, 400), + disabled-button-toggle: map_get(mat.$grey-palette, 200), + unselected-chip: map_get(mat.$grey-palette, 300), + disabled-list-option: map_get(mat.$grey-palette, 200) ); $app-blue-theme-foreground: ( @@ -114,28 +115,28 @@ $custom-theme: ( background: $app-blue-theme-background ); -$custom-typography: mat-typography-config( +$custom-typography: mat.define-typography-config( $font-family: "Lato, regular", - $headline: mat-typography-level(32px, 48px, 700), - $body-1: mat-typography-level(16px, 24px, 500) + $headline: mat.define-typography-level(32px, 48px, 700), + $body-1: mat.define-typography-level(16px, 24px, 500) ); .blue-theme { - @include mat-core(); + @include mat.core(); - @include angular-material-theme($custom-theme); + @include mat.all-component-themes($custom-theme); @include navigation-component-theme($custom-theme); // Override typography CSS classes (e.g., mat-h1, mat-display-1, mat-typography, etc.). - @include mat-base-typography($custom-typography); + @include mat.typography-hierarchy($custom-typography); // Override typography for a specific Angular Material components. - @include mat-checkbox-typography($custom-typography); + @include mat.checkbox-typography($custom-typography); // Override typography for all Angular Material, including mat-base-typography and all components. - @include angular-material-typography($custom-typography); + @include mat.all-component-typographies($custom-typography); //If you're using Material's theming, you can also pass in your typography config to the mat-core mixin: // Override the typography in the core CSS. - @include mat-core($custom-typography); + @include mat.core($custom-typography); } diff --git a/dmp-frontend/src/assets/scss/core/_dropdown.scss b/dmp-frontend/src/assets/scss/core/_dropdown.scss index cfe3cb561..550f34b48 100644 --- a/dmp-frontend/src/assets/scss/core/_dropdown.scss +++ b/dmp-frontend/src/assets/scss/core/_dropdown.scss @@ -90,7 +90,7 @@ text-decoration: none; font-size: .8125rem; - border-radius: $border-radius / 2; + border-radius: math.div($border-radius, 2); margin: 0 $bmd-dropdown-margin-y; @include transitions($fast-transition-time, $transition-linear); diff --git a/dmp-frontend/src/assets/scss/core/_togglebutton.scss b/dmp-frontend/src/assets/scss/core/_togglebutton.scss index 555b7c466..21ab5eeac 100644 --- a/dmp-frontend/src/assets/scss/core/_togglebutton.scss +++ b/dmp-frontend/src/assets/scss/core/_togglebutton.scss @@ -70,7 +70,7 @@ // set bg when checked input[type=checkbox]:checked { + .toggle { - background-color: rgba($brand-primary, (70/100)); // Switch bg on + background-color: rgba($brand-primary, math.div(70,100)); // Switch bg on } + .toggle:after { @@ -78,7 +78,7 @@ } + .toggle:active:after { - box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba($brand-primary, (10/100)); // Ripple on + box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.4), 0 0 0 15px rgba($brand-primary, math.div(10,100)); // Ripple on } } } diff --git a/dmp-frontend/src/assets/scss/core/mixins/_utilities.scss b/dmp-frontend/src/assets/scss/core/mixins/_utilities.scss index f598748dc..fb3809b24 100644 --- a/dmp-frontend/src/assets/scss/core/mixins/_utilities.scss +++ b/dmp-frontend/src/assets/scss/core/mixins/_utilities.scss @@ -25,7 +25,7 @@ @function brightness($color) { @if type-of($color) == color { - @return (red($color) * 0.299 + green($color) * 0.587 + blue($color) * 0.114) / 255 * 100%; + @return math.div(red($color) * 0.299 + green($color) * 0.587 + blue($color) * 0.114, 255) * 100%; } @else { @return unquote("brightness(#{$color})"); } diff --git a/dmp-frontend/src/assets/scss/core/variables/_bootstrap-material-design.scss b/dmp-frontend/src/assets/scss/core/variables/_bootstrap-material-design.scss index f9a2a0943..d5ba0c576 100644 --- a/dmp-frontend/src/assets/scss/core/variables/_bootstrap-material-design.scss +++ b/dmp-frontend/src/assets/scss/core/variables/_bootstrap-material-design.scss @@ -20,8 +20,8 @@ $mdb-input-font-size-base: 14px !default; $mdb-input-font-size-large: ceil(($font-size-base * 1.25)) !default; // ~20px $mdb-input-font-size-small: ceil(($font-size-base * 0.75)) !default; // ~12px -$bmd-bmd-label-static-size-ratio: 75 / 100 !default; -$bmd-help-size-ratio: 75 / 100 !default; +$bmd-bmd-label-static-size-ratio: math.div(75, 100) !default; +$bmd-help-size-ratio: math.div(75, 100) !default; $bmd-form-control-bg-repeat-y: no-repeat !default; $bmd-form-control-bg-position: center bottom, center calc(100% - 1px) !default; diff --git a/dmp-frontend/src/assets/scss/core/variables/_type.scss b/dmp-frontend/src/assets/scss/core/variables/_type.scss index 427c29769..f1ab356cb 100644 --- a/dmp-frontend/src/assets/scss/core/variables/_type.scss +++ b/dmp-frontend/src/assets/scss/core/variables/_type.scss @@ -43,7 +43,7 @@ $display4-size: 2.125rem !default; // md display-1 34px was 3.5rem // //$line-height-base: 1.5 !default; // -$headings-margin-bottom: ($spacer / 2) !default; +$headings-margin-bottom: math.div($spacer, 2) !default; //$headings-font-family: inherit !default; $headings-font-weight: 400 !default; // was 500 diff --git a/dmp-frontend/src/assets/scss/green-theme.scss b/dmp-frontend/src/assets/scss/green-theme.scss index bc6a17def..08463b3cf 100644 --- a/dmp-frontend/src/assets/scss/green-theme.scss +++ b/dmp-frontend/src/assets/scss/green-theme.scss @@ -1,3 +1,4 @@ +@use '~@angular/material' as mat; @import "app/ui/misc/navigation/navigation.component.scss"; @import "~@angular/material/theming"; // @import '../../../node_modules/@angular/material/theming'; @@ -67,25 +68,25 @@ $app-green-theme-accent-palette: ( ) ); -$app-green-theme-primary: mat-palette($app-green-theme-primary-palette); -$app-green-theme-accent: mat-palette($app-green-theme-accent-palette, A200, A100, A400); -$app-green-theme-warn: mat-palette($mat-red); +$app-green-theme-primary: mat.define-palette($app-green-theme-primary-palette); +$app-green-theme-accent: mat.define-palette($app-green-theme-accent-palette, A200, A100, A400); +$app-green-theme-warn: mat.define-palette(mat.$red-palette); $app-green-theme-background: ( - status-bar: map_get($mat-grey, 300), - app-bar: map_get($mat-grey, 100), - background: map_get($mat-grey, 50), + status-bar: map_get(mat.$grey-palette, 300), + app-bar: map_get(mat.$grey-palette, 100), + background: map_get(mat.$grey-palette, 50), hover: rgba(black, 0.04), card: white, dialog: white, disabled-button: rgba(black, 0.12), raised-button: white, focused-button: $dark-focused, - selected-button: map_get($mat-grey, 300), - selected-disabled-button: map_get($mat-grey, 400), - disabled-button-toggle: map_get($mat-grey, 200), - unselected-chip: map_get($mat-grey, 300), - disabled-list-option: map_get($mat-grey, 200) + selected-button: map_get(mat.$grey-palette, 300), + selected-disabled-button: map_get(mat.$grey-palette, 400), + disabled-button-toggle: map_get(mat.$grey-palette, 200), + unselected-chip: map_get(mat.$grey-palette, 300), + disabled-list-option: map_get(mat.$grey-palette, 200) ); $app-green-theme-foreground: ( @@ -114,28 +115,28 @@ $custom-theme: ( background: $app-green-theme-background ); -$custom-typography: mat-typography-config( +$custom-typography: mat.define-typography-config( $font-family: "Lato, regular", - $headline: mat-typography-level(32px, 48px, 700), - $body-1: mat-typography-level(16px, 24px, 500) + $headline: mat.define-typography-level(32px, 48px, 700), + $body-1: mat.define-typography-level(16px, 24px, 500) ); .green-theme { - @include mat-core(); + @include mat.core(); - @include angular-material-theme($custom-theme); + @include mat.all-component-themes($custom-theme); @include navigation-component-theme($custom-theme); // Override typography CSS classes (e.g., mat-h1, mat-display-1, mat-typography, etc.). - @include mat-base-typography($custom-typography); + @include mat.typography-hierarchy($custom-typography); // Override typography for a specific Angular Material components. - @include mat-checkbox-typography($custom-typography); + @include mat.checkbox-typography($custom-typography); // Override typography for all Angular Material, including mat-base-typography and all components. - @include angular-material-typography($custom-typography); + @include mat.all-component-typographies($custom-typography); //If you're using Material's theming, you can also pass in your typography config to the mat-core mixin: // Override the typography in the core CSS. - @include mat-core($custom-typography); + @include mat.core($custom-typography); } diff --git a/dmp-frontend/src/assets/scss/material-dashboard.scss b/dmp-frontend/src/assets/scss/material-dashboard.scss index fd0efab3c..0f9072667 100644 --- a/dmp-frontend/src/assets/scss/material-dashboard.scss +++ b/dmp-frontend/src/assets/scss/material-dashboard.scss @@ -14,25 +14,27 @@ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ +@use '~@angular/material' as mat; +@use 'sass:math'; @import "~@angular/material/prebuilt-themes/indigo-pink.css"; @import "~@angular/material/theming"; // @import '../../../node_modules/@angular/material/theming'; @import "./blue-theme.scss"; // @import "./green-theme.scss"; -@include mat-core(); +@include mat.core(); // // Define a blue theme. -$custom-theme-primary: mat-palette($app-blue-theme-primary-palette, 900); -$custom-theme-accent: mat-palette($app-blue-theme-accent-palette); +$custom-theme-primary: mat.define-palette($app-blue-theme-primary-palette, 900); +$custom-theme-accent: mat.define-palette($app-blue-theme-accent-palette); // Define a green theme. // $custom-theme-primary: mat-palette($app-green-theme-primary-palette, 500); // $custom-theme-accent: mat-palette($app-green-theme-accent-palette); // $custom-theme-accent: mat-palette($mat-pink, A200, A100, A400); -$custom-theme-warn: mat-palette($mat-red); -$custom-theme: mat-light-theme($custom-theme-primary, $custom-theme-accent, $custom-theme-warn); -@include angular-material-theme($custom-theme); +$custom-theme-warn: mat.define-palette(mat.$red-palette); +$custom-theme: mat.define-light-theme($custom-theme-primary, $custom-theme-accent, $custom-theme-warn); +@include mat.all-component-themes($custom-theme); @import "core/variables"; @import "core/mixins"; diff --git a/dmp-frontend/src/assets/splash/about/how-it-works.html b/dmp-frontend/src/assets/splash/about/how-it-works.html index 31beaf045..ea57d1479 100644 --- a/dmp-frontend/src/assets/splash/about/how-it-works.html +++ b/dmp-frontend/src/assets/splash/about/how-it-works.html @@ -134,7 +134,7 @@

    Argos follows a full DMP publication lifecycle. Users create their DMPs and add descriptions for their - datasets. DMPs and Datasets are by default created in private mode and can be shared with + datasets. DMPs and Datasets are created in private mode by default and can be shared with colleagues to facilitate the writing process. Datasets are added in DMPs per type and / or per scientific discipline that they concern. That way information is well-organized and it is easier to distinguish relevant information from a pool of data activities detailed in a single DMP record. Once created, DMPs @@ -201,7 +201,7 @@ Argos consists of two main editors: the DMP editor and the Dataset editor. Both editors integrate OpenAIRE and ESOC APIs to ease the writing process and help create links with other outputs. Also, the editors incorporate a mechanism - to enable compliance with the RDA DMP Common Standard. + to enable compliance with the RDA DMP Common Standard.

    @@ -225,7 +225,7 @@
    @@ -400,6 +402,18 @@
    +
    +
    +
    +
    +
    + Paper in the DaMaLOS conference: +
    + + https://repository.publisso.de/resource/frl:6423283 + +
    +
    diff --git a/dmp-frontend/src/assets/splash/assets/img/eosc_logo.png b/dmp-frontend/src/assets/splash/assets/img/eosc_logo.png new file mode 100644 index 000000000..fab95f7e4 Binary files /dev/null and b/dmp-frontend/src/assets/splash/assets/img/eosc_logo.png differ diff --git a/dmp-frontend/src/assets/splash/css/section.css b/dmp-frontend/src/assets/splash/css/section.css index 10943753b..0bec5d83f 100644 --- a/dmp-frontend/src/assets/splash/css/section.css +++ b/dmp-frontend/src/assets/splash/css/section.css @@ -254,3 +254,54 @@ section.media-kit-logo { padding-bottom: 2rem; } } + +.eosc{ + margin-top: 8.5em; + /* margin-bottom: 5em; */ +} +.eosc-row{ + justify-content: space-around; +} +.european-open-cloud{ + background: #C2EDED 0% 0% no-repeat padding-box; + height: 453px; + width: 347px; + box-shadow: 0px 3px 6px #0044941A; + transform: matrix(0.99, -0.16, 0.16, 0.99, 0, 0); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 4em; +} +.eosc_logo{ + height: 117px; +} + +.eosc-desc{ + font-size: 1rem; + color: #292747; + opacity: 1; + margin-bottom: 3.5em; +} +.eosc-header{ + font-weight: 400 !important; + font-size: 2.375em !important; + line-height: 2.66rem !important; + margin-bottom: .7em; +} +.eosc-tag{ + color: #004494; +} +.eosc-learn-more{ + max-width: 256px; + height: 40px; + cursor: pointer; + background: #004494 0% 0% no-repeat padding-box; + box-shadow: 0px 3px 6px #1E202029; + border-radius: 30px; + border: none; + color: #FFFFFF; + opacity: 1; + font-size: 0.87rem; + padding: 0.62rem 1.87rem; +} \ No newline at end of file diff --git a/dmp-frontend/src/assets/splash/index.html b/dmp-frontend/src/assets/splash/index.html index a956b1501..8b6aa48be 100644 --- a/dmp-frontend/src/assets/splash/index.html +++ b/dmp-frontend/src/assets/splash/index.html @@ -85,7 +85,7 @@ analysed and stored.

    @@ -97,33 +97,31 @@
    - -
    + +
    -
    -
    -

    Start your

    -
    -

    Argos

     

    experience

    -
    -
    -
    -

    1

     

    Select Templates

    -
    -
    -

    2

     

    Fill the information

    -
    -
    -

    3

     

    Share DMP

    -
    +
    +
    +
    +
    -
    - +
    +

    Connect with OpenAIRE & EOSC

    +

    + Link your plan directly to underlying OpenAIRE and EOSC services, + sources and semantics and trace the quality of your research. Use Argos + templates that incorporate the fullest collections of repositories, datasets, + metadata standards and other resources to choose from when completing your DMP's. +

    +

    + + + +

    -
    @@ -233,6 +231,34 @@
    + +
    +
    +
    +
    +

    Start your

    +
    +

    Argos

     

    experience

    +
    +
    +
    +

    1

     

    Select Templates

    +
    +
    +

    2

     

    Fill the information

    +
    +
    +

    3

     

    Share DMP

    +
    +
    +
    +
    + +
    +
    +
    +
    +
    diff --git a/dmp-frontend/src/common/base/base-pending-changes.component.ts b/dmp-frontend/src/common/base/base-pending-changes.component.ts index fbf4a98ea..308d42daa 100644 --- a/dmp-frontend/src/common/base/base-pending-changes.component.ts +++ b/dmp-frontend/src/common/base/base-pending-changes.component.ts @@ -1,7 +1,8 @@ -import { HostListener } from '@angular/core'; +import { HostListener, Directive } from '@angular/core'; import { BaseComponent } from '@common/base/base.component'; import { Observable } from 'rxjs'; +@Directive() export abstract class BasePendingChangesComponent extends BaseComponent { protected constructor() { super(); } diff --git a/dmp-frontend/src/common/base/base.component.ts b/dmp-frontend/src/common/base/base.component.ts index bfff59ea4..04789d808 100644 --- a/dmp-frontend/src/common/base/base.component.ts +++ b/dmp-frontend/src/common/base/base.component.ts @@ -1,6 +1,7 @@ -import { OnDestroy } from '@angular/core'; +import { Directive, OnDestroy } from '@angular/core'; import { Subject } from 'rxjs'; +@Directive() export abstract class BaseComponent implements OnDestroy { protected _destroyed: Subject = new Subject(); diff --git a/dmp-frontend/src/common/base/base.pipe.ts b/dmp-frontend/src/common/base/base.pipe.ts index a36b43efb..d5a98ba56 100644 --- a/dmp-frontend/src/common/base/base.pipe.ts +++ b/dmp-frontend/src/common/base/base.pipe.ts @@ -1,6 +1,7 @@ -import { OnDestroy } from '@angular/core'; +import { Directive, OnDestroy } from '@angular/core'; import { Subject } from 'rxjs'; +@Directive() export abstract class BasePipe implements OnDestroy { protected _destroyed: Subject = new Subject(); diff --git a/dmp-frontend/src/common/base/base.service.ts b/dmp-frontend/src/common/base/base.service.ts index 6f77bcd59..993ece5c8 100644 --- a/dmp-frontend/src/common/base/base.service.ts +++ b/dmp-frontend/src/common/base/base.service.ts @@ -1,6 +1,7 @@ -import { OnDestroy } from '@angular/core'; +import { Directive, OnDestroy } from '@angular/core'; import { Subject } from 'rxjs'; +@Directive() export abstract class BaseService implements OnDestroy { protected _destroyed: Subject = new Subject(); diff --git a/dmp-frontend/src/common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component.html b/dmp-frontend/src/common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component.html index 465324b54..e36977ed5 100644 --- a/dmp-frontend/src/common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component.html +++ b/dmp-frontend/src/common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component.html @@ -5,9 +5,14 @@
    -
      -
    • {{error}}
    • -
    + +
      +
    • {{error}}
    • +
    +
    + + {{errorMessages[0]}} +
    diff --git a/dmp-frontend/src/common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component.ts b/dmp-frontend/src/common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component.ts index b01d262f7..7d4d14a33 100644 --- a/dmp-frontend/src/common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component.ts +++ b/dmp-frontend/src/common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component.ts @@ -70,14 +70,15 @@ export class FormValidationErrorsDialogComponent { } getPlaceHolder(formControl: any): string { - if (formControl.nativeElement.localName === 'input' || formControl.nativeElement.localName === 'textarea') { + if (formControl.nativeElement.localName === 'input' || formControl.nativeElement.localName === 'textarea' + || formControl.nativeElement.localName === 'richTextarea') { return formControl.nativeElement.getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'mat-select') { - return formControl.nativeElement.getAttribute('aria-label'); + return formControl.nativeElement.getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'app-single-auto-complete') { return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); } else if (formControl.nativeElement.localName === 'app-multiple-auto-complete') { - return (Array.from(formControl.nativeElement.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); + return (Array.from(formControl.nativeElement.firstChild.firstChild.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder'); } // Needs to have in in html in order to fill results diff --git a/dmp-frontend/src/common/forms/validation/custom-validator.ts b/dmp-frontend/src/common/forms/validation/custom-validator.ts index 9854d1d4d..1ca4dec4b 100644 --- a/dmp-frontend/src/common/forms/validation/custom-validator.ts +++ b/dmp-frontend/src/common/forms/validation/custom-validator.ts @@ -1,6 +1,6 @@ import { AbstractControl, ValidatorFn, Validators } from '@angular/forms'; import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model'; -import { isNullOrUndefined } from 'util'; +import { isNullOrUndefined } from '@app/utilities/enhancers/utils'; export function BackendErrorValidator(errorModel: ValidationErrorModel, propertyName: string): ValidatorFn { return (control: AbstractControl): { [key: string]: any } => { diff --git a/dmp-frontend/src/common/http/interceptors/unauthorized-response.interceptor.ts b/dmp-frontend/src/common/http/interceptors/unauthorized-response.interceptor.ts index c33369a3b..76b56c400 100644 --- a/dmp-frontend/src/common/http/interceptors/unauthorized-response.interceptor.ts +++ b/dmp-frontend/src/common/http/interceptors/unauthorized-response.interceptor.ts @@ -19,8 +19,6 @@ export class UnauthorizedResponseInterceptor extends BaseInterceptor { get type(): InterceptorType { return InterceptorType.UnauthorizedResponse; } - private accountRefresh$: Observable = null; - interceptRequest(req: HttpRequest, next: HttpHandler): Observable | HttpUserEvent> { return next.handle(req).pipe( catchError(error => { @@ -29,7 +27,6 @@ export class UnauthorizedResponseInterceptor extends BaseInterceptor { case 401: this.logoutUser(); return throwError(error); - //return this.handle401Error(req, next); default: return throwError(error); } @@ -39,31 +36,6 @@ export class UnauthorizedResponseInterceptor extends BaseInterceptor { })); } - // private handle401Error(req: HttpRequest, next: HttpHandler) { - // if (!this.accountRefresh$) { - // this.accountRefresh$ = this.authService.refreshToken().pipe( - // tap(account => { - // this.accountRefresh$ = null; - // if (!account) { throw throwError('missing_authentication_token'); } - // }), - // catchError(error => { - // this.logoutUser(); - // return throwError(error); - // })); - // } - // return this.accountRefresh$.pipe(mergeMap(account => this.repeatRequest(account, req, next))); - // } - - private repeatRequest(account: Account, originalRequest: HttpRequest, next: HttpHandler) { - const newAuthenticationToken: String = this.authService.current().token; - const newRequest = originalRequest.clone({ - setHeaders: { - Authorization: `Bearer ${newAuthenticationToken}` - } - }); - return next.handle(newRequest); - } - private logoutUser() { //this.authService.clear(); if (!this.isLoginRoute() && !this.isSignupRoute()) { this.router.navigate(['/unauthorized']); } diff --git a/dmp-frontend/src/common/modules/confirmation-dialog/confirmation-dialog.component.html b/dmp-frontend/src/common/modules/confirmation-dialog/confirmation-dialog.component.html index 258eb7cda..199b9ed9b 100644 --- a/dmp-frontend/src/common/modules/confirmation-dialog/confirmation-dialog.component.html +++ b/dmp-frontend/src/common/modules/confirmation-dialog/confirmation-dialog.component.html @@ -1,14 +1,14 @@
    -
    -
    +
    +
    {{ data.icon }}
    -
    {{ data.warning }}
    +
    {{ data.warning }}
    close
    -
    +
    close
    diff --git a/dmp-frontend/src/common/modules/confirmation-dialog/confirmation-dialog.component.scss b/dmp-frontend/src/common/modules/confirmation-dialog/confirmation-dialog.component.scss index 5bd8b98b0..804e34258 100644 --- a/dmp-frontend/src/common/modules/confirmation-dialog/confirmation-dialog.component.scss +++ b/dmp-frontend/src/common/modules/confirmation-dialog/confirmation-dialog.component.scss @@ -14,7 +14,8 @@ } .warn-text { - color: #f44336; + // color: #f44336; + } .cancel { diff --git a/dmp-frontend/src/common/modules/multiple-choice-dialog/multiple-choice-dialog.component.ts b/dmp-frontend/src/common/modules/multiple-choice-dialog/multiple-choice-dialog.component.ts index 8abce91d7..a667ce3b3 100644 --- a/dmp-frontend/src/common/modules/multiple-choice-dialog/multiple-choice-dialog.component.ts +++ b/dmp-frontend/src/common/modules/multiple-choice-dialog/multiple-choice-dialog.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; @Component({ selector: 'app-multiple-choice-dialog', diff --git a/dmp-frontend/src/common/types/pair.ts b/dmp-frontend/src/common/types/pair.ts index 02bffbc32..f4f904e70 100644 --- a/dmp-frontend/src/common/types/pair.ts +++ b/dmp-frontend/src/common/types/pair.ts @@ -6,4 +6,4 @@ export class Pair { this.left = left; this.right = right; } -} +} \ No newline at end of file diff --git a/dmp-frontend/src/index.html b/dmp-frontend/src/index.html index 5c840236b..65da9ad6b 100644 --- a/dmp-frontend/src/index.html +++ b/dmp-frontend/src/index.html @@ -17,7 +17,6 @@ - diff --git a/dmp-frontend/src/polyfills.ts b/dmp-frontend/src/polyfills.ts index adefaa3b1..5dc0b3e49 100644 --- a/dmp-frontend/src/polyfills.ts +++ b/dmp-frontend/src/polyfills.ts @@ -38,7 +38,7 @@ import 'core-js/es6/reflect'; /*************************************************************************************************** * Zone JS is required by Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. diff --git a/dmp-frontend/src/styles.scss b/dmp-frontend/src/styles.scss index 6ca804bb1..4d24d173e 100644 --- a/dmp-frontend/src/styles.scss +++ b/dmp-frontend/src/styles.scss @@ -251,4 +251,53 @@ .translateY-3{ transform:translateY(3px); -} \ No newline at end of file +} + +// CSS for (@kolkov/angular-editor) +.form-field-subscript-wrapper { + font-size: 75%; + padding-left: 12px; + margin-top: 8px; +} + +//.angular-editor-textarea { +// min-height: 150px !important; +//} +// +//.editor-wrapper { +// border: 1px solid transparent !important; +// border-radius: 5px; +//} +// +//.angular-editor-toolbar { +// margin-left: 1px; +// margin-right: 1px; +//} +// +//.angular-editor-textarea, .angular-editor-toolbar { +// border: none !important; +//} +// +//.angular-editor { +// border: 1px solid #ddd !important; +// border-radius: 5px; +// background-color: #fff; +//} +// +//.editor-wrapper:hover, .angular-editor:hover { +// border: 1px solid #000 !important; +//} +// +//.editor-wrapper:focus-within, .angular-editor:focus-within { +// border: 1px solid #034459 !important; +//} +// +//.required.editor-wrapper, .required .editor .angular-editor { +// border: 1px solid #f44336 !important; +//} +// end of CSS for (@kolkov/angular-editor) + +/* Transition Group */ +.list-move { + transition: transform 1s; +} diff --git a/dmp-frontend/src/tsconfig.app.json b/dmp-frontend/src/tsconfig.app.json index d9b21b2b4..d1370a69b 100644 --- a/dmp-frontend/src/tsconfig.app.json +++ b/dmp-frontend/src/tsconfig.app.json @@ -6,8 +6,11 @@ "types": ["node"], "typeRoots": [ "../node_modules/@types" ] }, - "exclude": [ - "test.ts", - "**/*.spec.ts" + "files": [ + "main.ts", + "polyfills.ts" + ], + "include": [ + "src/**/*.d.ts" ] } diff --git a/dmp-frontend/tsconfig.json b/dmp-frontend/tsconfig.json index 4d05665e4..c554a09e7 100644 --- a/dmp-frontend/tsconfig.json +++ b/dmp-frontend/tsconfig.json @@ -5,9 +5,8 @@ "outDir": "./dist/out-tsc", "sourceMap": true, "declaration": false, - "module": "esnext", - "moduleResolution": "node", - "emitDecoratorMetadata": true, + "module": "es2020", + "moduleResolution": "node", "experimentalDecorators": true, "importHelpers": true, "skipLibCheck": true, diff --git a/dmp-frontend/tslint.json b/dmp-frontend/tslint.json index b82bc955a..e38acd69b 100644 --- a/dmp-frontend/tslint.json +++ b/dmp-frontend/tslint.json @@ -20,7 +20,10 @@ "class-name": true, "comment-format": [ true - ], + ], + "deprecation": { + "severity": "warning" + }, "curly": true, "eofline": true, "forin": true, @@ -79,8 +82,7 @@ "no-switch-case-fall-through": true, "no-trailing-whitespace": true, "no-unnecessary-initializer": true, - "no-unused-expression": true, - "no-use-before-declare": true, + "no-unused-expression": true, "no-var-keyword": true, "object-literal-sort-keys": false, "one-line": [ diff --git a/user-guide/UserGuide_pt.html b/user-guide/UserGuide_pt.html new file mode 100644 index 000000000..a06222f7d --- /dev/null +++ b/user-guide/UserGuide_pt.html @@ -0,0 +1,2461 @@ + + + + + + + + + + + + + + + +
    +
    +

    1.    Terminologia

    +

    1.1 Termos chave

    +
      +
    • +

      Plano de Gestão de Dados (PGD) – Corresponde a um conjunto de Datasets, associado a uma determinada atividade (grant ou projeto). O PGD pode ser versionado e exportado em vários formatos. É, atualmente, “machine readable” (xml, json) e “human readable” (pdf/openxml). Pode, igualmente, ser-lhe atribuído um DOI e proceder-se à sua publicação no Zenodo.

      +
    • +
    • +

      Dataset - Descreve os dados de acordo com um conjunto de regras, através de um modelo de dados.

      +
    • +
    • +

      Modelo de Dados – é composto por um conjunto de questões que descrevem o que os Datasets contêm e como são tratados. Estes modelos estão ligados a PGDs, de modo a que os utilizadores tenham acesso a formatos específicos consoante a instituição a que reportam. Os modelos só podem ser modificados por administradores. Um grant e/ou um projeto definem o contexto em que um ou mais PGDs podem ser criados.

      +
    • +
    • +

      Importar ficheiro - Suporta a função de importar ficheiros em formato .json, que são produzidos de acordo com as especificações da Research Data Alliance (RDA) para PGDs acionáveis por máquina.


      Paste Picture 1

      +
    • +
    +
    +

    O botão “Criar novo PGD” permite de forma simples iniciar o seu plano. Esta opção fornece um editor que percorre os elementos essenciais de um PGD, guiando o processo de criação do documento passo a passo.

    +


    A partir do "Início"


    Paste Picture 2
    Paste Picture 3

    +
    +

    A partir de “Adicione um Dataset”


    Paste Picture 4



    Adicionar um Dataset
    é uma forma simples de adicionar novos modelos de dados a PGDs pré-existentes.

    A partir do “Início”


    Paste Picture 5

    +

    A partir de “Os meus Datasets”


    Paste Picture 6


    A partir de “Os meus PGDs”


    Paste Picture 7

    +

    A partir do “Editor de Dataset”


    Paste Picture 8


    +

    1.2  Outros termos

    +
    +
      +
    • +

      Repositório - Base de dados eletrónica ou sistema de informação onde os dados são armazenados e mantidos.

      +
    • +
    • +

      Registo – Uma base de dados de entidades e descrições.

      +
    • +
    • +

      Serviço - Um software de operações e tarefas específicas, que interage com outro software e hardware.

      +
    • +
    • +

      Investigador – Um indivíduo que pratica investigação.

      +
    • +
    • +

      Financiador – Uma organização que financia projetos de investigação e/ou atividades de investigadores.

      +
    • +
    +
    +

     

    +

    2. Navegação

    +

    2.1 Página Inicial

    +
    +

    A página inicial poderá ser encontrada em https://argos.openaire.eu/splash/, também acessível através do Catálogo de Serviços do OpenAIRE e EOSC.

    +


    Paste Picture 9

    +
      +
    • +

      Sobre – Informa sobre o âmbito e principais funções da ferramenta (como funciona, Roadmap, FAQs, quais os contribuidores).

      +
    • +
    • +

      Recursos - Fornece informação útil sobre a utilização do Argos e inclui material de divulgação (Media Kit, Guia do Utilizador, Co-branding).

      +
    • +
    • +

      Contactos – Um formulário de contacto que permite a comunicação com a equipa do Argos.

      +
    • +
    • +

      Sign in – Entrar na aplicação como utilizador.

      +
    • +
    +

     

    +

    2.2    Login

    +


    Estão disponíveis diferentes opções de login, desde meios de comunicação social a canais de investigação e comunicação académica.

    +


    Paste Picture 10


    +

    Atenção! Não é necessária uma conta de utilizador.

    +

    2.3   Menu do Utilizador

    +
    +

    Espaço dedicado e que poderá ser customizado de acordo com o perfil pessoal do utilizador.

    + + + + + + + +
    Paste Picture 11 +

    Definições do Meu Perfil -  Exibe a página de perfil que contém detalhes tais como: nome, e-mail, dados da conta Zenodo, etc.

    +

    PGDs associados - Coleção dos PGDs do utilizador.

    +

    Log out - Termina a sessão e redireciona para a página de login.

    +
    +

     

    +

    2.4   Menu Principal

    +


    O menu principal está localizado no lado esquerdo do ecrã.

    +

    Dependendo da forma como os utilizadores vêem a ferramenta, ou seja, se já efetuaram o login ou não, o menu principal altera-se para incluir características que estão apenas disponíveis no painel dos respetivos  utilizadores.

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +


    Menu Principal – 
    Antes de  Iniciar a Sessão

    +
    +

     

    +
    +


    Menu Principal - Após Iniciar 
    a Sessão

    +


    Paste Picture 12 




    +

     

    +
    +

     

    +

    Paste Picture 13

    +

     

    +
    Início  - Direciona o utilizador para a página inicial/painel. +

     

    +
    Início  - Direciona o utilizador para a página inicial/painel.
    PGDs públicos – Conjunto de PGDs que estão disponíveis publicamente no Argos. +

     

    +
    Os meus PGDs - Inclui todos os PGDs dos quais o utilizador é proprietário ou colaborador.
    Datasets públicos – Conjunto de Datasets que estão disponíveis publicamente no Argos. +

     

    +
    Os meus Datasets - Inclui todos os Datasets dos quais o utilizador é proprietário ou colaborador.
    Co-Branding - Página para visualizar o software e aprender como contribuir para a aplicação. +

     

    +
    PGDs públicos - Conjunto de PGDs que estão disponíveis publicamente no Argos.
    Suporte - Formulário de contacto que permite a comunicação com a equipa do Argos. +

     

    +
    Datasets públicos - Conjunto de Datasets que estão disponíveis publicamente no Argos.
    Enviar comentários - Formulário de feedback para contribuir com sugestões e opiniões sobre a utilização de Argos. +

     

    +
    Sobre - Informa sobre o âmbito e principais funcionalidades da ferramenta.
    Sobre - Informa sobre o âmbito e principais funcionalidades da ferramenta. +

     

    +
    Condições do Serviço - Providencia o status legal para a utilização do Argos.
    Condições do Serviço - Providencia o status legal para a utilização do Argos. +

     

    +
    Glossário - Inclui os termos usados na ferramenta e explica os componentes básicos.
    Glossário - Inclui os termos usados na ferramenta e explica os componentes básicos. +

     

    +
    Guia do Utilizador - Guia para os utilizadores aprenderem a usar o Argos.
    Guia do Utilizador - Guia para os utilizadores aprenderem a usar o Argos. +

     

    +
    +

    Contacto de suporte - Formulário de contacto que permite a comunicação com a equipa do Argos.

    +
    +

     

    +

    2.5   Início

    +
    +

    O “Início” é a informação que aparece depois de entrar no Argos a partir da página inicial.

    +

    Inclui informação condensada baseada na função Argos e na sua utilização.

    +


    Início –
    Antes de iniciar a sessão


    Paste Picture 14


    + + + + + + + +
    Paste Picture 15 +


    Última Atividade
    - Exibe os PGDs e Datasets publicamente disponíveis, de acordo com a data da sua publicação no Argos e a sua etiqueta (PGDs ou Datasets).

    +
    +

    + + + + + + + +
    Paste Picture 16 +


    Uso Público
    - Mostra o número de PGDs, Datasets, Grants e Organizações publicamente disponíveis e incluídas no Argos.

    +

     

    +

    Atenção! Após iniciar a sessão no Argos, a página inicial torna-se num painel pessoal e, como tal, os números alteram-se de acordo com a atividade do utilizador.

    +
    +

     

    +

    Início - Após Iniciar a Sessão


    Paste Picture 17


    + + + + + + + +
    Paste Picture 18 +


    Última Atividade
    - Exibe os PGDs e Datasets do utilizador, de acordo com a data da sua última modificação, estado  do documento (rascunho, finalizado, publicado) e a sua etiqueta (PGDs ou Datasets).

    +
    +

     

    + + + + + + + +
    Paste Picture 19 +


    Uso Pessoal
    - Mostra a atividade do utilizador nos PGDs, Datasets, Grants e Organizações.

    +
    +

     

    +

    2.6   Os Meus PGDs / Os Meus Datasets

    +
    +

    Contém todos os PGDs e Datasets de que o utilizador é proprietário ou colaborador. Tanto os PGDs, como os Datasets, são classificados pela data da sua última modificação, o estado do documento (rascunho, finalizado, publicado) e a sua etiqueta (PGDs ou Datasets).

    +

    Os Meus PGDs

    +

    Quando a etiqueta é verde trata-se de um PGD, mostrando o papel da pessoa que vê o PGD, o estado do processo de escrita, a versão atual do PGD, o grant associado ao PGD, o número e nome dos Datasets que o PGD tem.


    Paste Picture 20

    +
      +
    • +

      Exportar - Suporta o download dos outputs do PGD nos seguintes formatos: PDF, Document, XML, RDA JSON (pode ser importado para outras ferramentas de PGD compatíveis com a RDA).

      +
    • +
    +
      +
    • +

      Adicionar Dataset - Permite adicionar mais Datasets aos PGDs existentes.

      +
    • +
    +
      +
    • +

      Convidar - Fornece direitos de edição sobre o documento.

      +
    • +
    • +

      Duplicar - Cria uma réplica exata do PGD.

      +
    • +
    + + + + + + + +
    Paste Picture 21 +


    Nova Versão
    - Inicia uma nova versão do PGD.

    +

    Ver todas as Versões do PGD  - Mostra o histórico das diferentes versões do PGD.

    +

    Eliminar - Remove permanentemente o PGD.

    +
    +
    +


    Os Meus Datasets

    +

    Quando a etiqueta é amarelo trata-se de um Dataset e mostra o papel da pessoa que visualiza o Dataset, o estado do processo de escrita, o grant associado ao PGD e o título do PGD do qual o Dataset faz parte.


    Paste Picture 22

    +
      +
    • +

      Exportar - Suporta o download dos outputs do Dataset nos seguintes formatos: PDF, Document, XML, RDA JSON (pode ser importado para outras ferramentas de PGD compatíveis com a RDA).

      +
    • +
    • +

      Convidar - Possibilidade de envio de e-mail ou pesquisa no Catálogo de Utilizadores do Argos, de modo a encontrar colegas/colaboradores. O convite enviado fornece direitos de edição sobre o documento.

      +
    • +
    • +

      Copiar o Dataset - Cria uma cópia do Dataset.

      +
    • +
    • +

      Eliminar - Remove permanentemente o Dataset.

      +
    • +
    +
    +

    2.7   PGDs Públicos / Datasets Públicos

    +


    Contém informação de PGDs que estão disponíveis em acesso aberto no Argos. Isto significa que os proprietários e membros do PGD estão a disponibilizar os seus PGDs e Datasets associados a todos os utilizadores (Argos e não Argos), que queiram consultá-los ou reutilizá-los no âmbito da licença atribuída.

    +

    Tanto os PGDs como os Datasets são organizados e apresentados pela data da sua última modificação e pela sua etiqueta (PGDs ou Datasets). Os utilizadores também podem procurar o PGD ou Dataset a partir da barra de pesquisa.

    +

    PGDs Públicos

    +

    Quando a etiqueta é verde trata-se de um PGD e exibe o título do PGD, o seu estado, a sua versão, o grant associado, o número e o nome dos Datasets que o PGD contém.

    +


    Paste Picture 23

    +
      +
    • +

      Exportar - Suporta o download dos outputs do PGD nos seguintes formatos: PDF, Document, XML, RDA JSON (pode ser importado para outras ferramentas de PGD compatíveis com a RDA).

      +
    • +
    +
      +
    • +

      Duplicar - Cria uma cópia idêntica do PGD.

      +
    • +
    +

    Datasets Públicos

    +

    Quando a etiqueta é amarelo trata-se de um Dataset Público e mostra o título do Dataset, o seu estado, o grant associado ao PGD e o papel da pessoa que visualiza o Dataset, o estado do processo de escrita, o grant associado ao PGD e o título do PGD do qual o Dataset faz parte.


    Paste Picture 24

    +
      +
    • +

      Exportar - Suporta o download dos outputs do Dataset nos seguintes formatos: PDF, Document, XML, RDA JSON (pode ser importado para outras ferramentas de PGD compatíveis com a RDA).

      +
    • +
    +

     

    + + + + + + + +
    Paste Picture 25 +

    Copiar o Dataset - Cria uma cópia do Dataset.

    +
    +



    +

    2.8   Criar novo PGD

    +


    Existem várias formas de criar um novo PGD no Argos.

    +
    Paste Picture 26


    +

    Existem 4 etapas envolvidas na criação de um PGD:

    +

    - Informação Geral;

    +

    - Informação sobre o Financiamento;

    +

    - Informação sobre a Licença;

    +

    - Informação sobre os Datasets.

    +

    Informação Geral

    +

    Paste Picture 27

    Paste Picture 28


    +
      +
    • +

      Título do PGD - Título do documento.

      +
    • +
    • +

      Descrição - Breve descrição do contexto e objetivos do PGD.

      +
    • +
    • +

      Idioma - Idioma do PGD.

      +
    • +
    • +

      Visibilidade – Informação sobre como será disponibilizado o PGD no Argos. Ao selecionar “Público” o PGD é disponibilizado a todos os utilizadores, através da opção “PGDs Públicos”.

      +
    • +
    • +

      Investigadores - As pessoas que produziram, processaram e analisaram os dados descritos no PGD.

      +
    • +
    • +

      Organizações - Nomes das organizações que contribuem para a criação e a revisão dos PGDs.

      +
    • +
    • +

      Contacto - Detalhe do proprietário do PGD.

      +
    • +
    +
    +

    Informação sobre o Financiamento

    +
    Paste Picture 29


    +
      +
    • +

      Organizações de Financiamento - Integra uma lista onde os utilizadores podem selecionar a organização através da qual a investigação é financiada. Caso o nome de uma organização financiadora não possa ser encontrado no Argos, os utilizadores podem criar um novo registo com o nome e detalhes da organização financiadora ("Inserir manualmente").

      +
    • +
    • +

      Grants - Integra um menu pendente para selecionar o grant que está associado ao projeto de investigação da organização financiadora em questão. Caso o grant não seja encontrado no Argos, os utilizadores podem criar um novo registo com o número e nome do mesmo ("Inserir manualmente").

      +
    • +
    • +

      Projeto - Este campo é para ser preenchido apenas por projetos onde múltiplos grants são aplicáveis. De outra forma, este campo não deverá ser preenchido, sendo mais tarde adicionada informação de forma automática pelos metadados do Argos.

      +
    • +
    +

    Informação sobre a Licença

    +
    Paste Picture 30


    +
      +
    • +

      Licença - Inclui uma lista de licenças a escolher e a atribuir aos PGDs.

      +
    • +
    +
    +

    Informação sobre os Datasets

    +
    Paste Picture 31

    +
      +
    • +

      Informação sobre os Datasets – Permite selecionar o modelo para descrever os seus datasets. Poderá selecionar mais do que um modelo de dados.

      +
    • +
    • +

      Guardar Salva todas as alterações efetuadas ao PGD, permitindo continuar a trabalhar na mesma página.

      +
    • +
    • +

      Guardar & Adicionar Dataset – Salva todas as alterações efetuadas ao PGD, sendo dado início ao Editor do Dataset para começar a descrever o primeiro conjunto de dados do PGD.

      +
    • +
    +

    +

    2.9  Adicionar Dataset(s)

    +
    +

    Existem duas maneiras de criar Datasets:

    +

    - Criar o primeiro Dataset do PGD;

    +

    - Adicionar Datasets aos PGDs existentes.

    +

     

    +

    No Argos, os Datasets estão ligados e associados a pelo menos um PGD. Um Dataset não pode existir como registo único.

    +

     

    +

    Primeiro Dataset

    +
    Paste Picture 32

    +


    Uma vez criado o PGD, o utilizador poderá preencher a descrição dos seus dados no “Editor de Dataset”.

    +

     

    +
      +
    • +

      Adicionar Dataset - Adiciona mais Datasets a PGDs já existentes.

      +
    • +
    +

    São três os passos a seguir para adicionar um Dataset:

    +
      +
    • +
        +
      • +

        Selecionar o PGD para o seu Dataset;

        +
      • +
      • +

        Selecionar o modelo de dados respetivo;

        +
      • +
      • +

        Editar o Dataset.

        +
      • +
      +
    • +
    +
    +

    Selecione um PGD para o seu Dataset

    +
    Paste Picture 33


    +

    Selecione um PGD já existente na lista.

    +
    +

    Selecione um Modelo

    +
    +

    Paste Picture 34

    +
    +

    Selecionar o modelo de dados para descrever o seu Dataset, de acordo com a organização que financia o seu grant.

    +


    Edite o Dataset

    +
    +

    O “Editor de Dataset” apoia na descrição de informação de acordo com o modelo selecionado.

    +
    Paste Picture 35

    +
      +
    • +

      Guardar Datasets - Existem várias formas de guardar Datasets no Argos. Todas têm a mesma finalidade, diferindo da ação de acompanhamento.

      +
    • +
    +
      +
    • +

      Ignorar – Não guarda as alterações realizadas no Dataset.

      +
    • +
    • +

      Guardar – Salva todas as alterações realizadas e permite continuar a trabalhar na mesma página.

      +
    • +
    • +

      Concluir – Salva todas as alterações realizadas, encerrando a janela do editor e redirecionando para o “Início”.

      +
      +

      Paste Picture 36

      +
    • +
    +
    +

    2.10 Registo de PGDs/Datasets

    +

    Regista os PGDs e Datasets no Argos, após a sua edição e conclusão.

    +

    Os utilizadores podem visualizar os PGDs e Datasets que criaram. Ao abrir um registo que tenham criado, são-lhes fornecidas funcionalidades adicionais que estão ligadas à conclusão do processo de escrita dos PGDs.

    +

    São aplicáveis funcionalidades diferentes consoante o estado do PGD, ou seja, antes ou depois da conclusão do PGD.

    +


    Antes de concluir o PGD


    Paste Picture 37

    +
    +

    Antes de concluir o PGD, este pode ainda ser editado, eliminado ou duplicado. Os utilizadores podem rever a informação que acrescentaram relativamente a grants, investigadores, descrição do PGD e Datasets e/ou modelos de dados utilizados.

    +

    Podem ser adicionados novos Datasets em qualquer altura, a partir desta página.

    +

    Os utilizadores podem exportar o PGD, começar a trabalhar numa nova versão e/ou convidar colegas a colaborar na conclusão do seu plano.

    +


    Convidar

    +
    Paste Picture 38


    +

    Após concluir o PGD


    Paste Picture 39

    +
    +

    Após a conclusão do PGD, este poderá ser tornado publicamente visível no Argos e depositado no Zenodo.

    +

    Os utilizadores podem exportar o PGD finalizado, começar a trabalhar numa nova versão e/ou convidar colegas a colaborar na conclusão do plano.

    +

    É possível reverter a conclusão do PGD.

    +

     

    +

    Antes de concluir os Datasets

    +
    Paste Picture 40


    +

    Antes de concluir o Dataset, este pode ainda ser editado, eliminado ou duplicado.

    +

    Os utilizadores podem aceder a todo o PGD do qual o Dataset faz parte, a partir dessa página, e rever a informação que acrescentaram relativamente ao grant, investigadores e descrição do Dataset.

    +

    Os utilizadores podem exportar a descrição do Dataset e convidar os colegas a colaborar na sua conclusão.

    +

     

    +

     Após concluir os Datasets

    +
    Paste Picture 41

    +


    Após a conclusão do Dataset, este poderá ser exportado e partilhado com colegas para revisão.


    +

     

    +
    + + + + \ No newline at end of file