description template type and dmp blueprint frontend changes

This commit is contained in:
Diamantis Tziotzios 2023-10-20 18:01:09 +03:00
parent 0b3178177a
commit 4d3098860d
232 changed files with 7547 additions and 2576 deletions

View File

@ -20,4 +20,9 @@ public class AuditableAction {
public static final EventId EntityDoi_Delete = new EventId(2003, "EntityDoi_Delete");
public static final EventId User_Settings_Query = new EventId(3000, "User_Settings_Query");
public static final EventId User_Settings_Lookup = new EventId(3001, "User_Settings_Lookup");
public static final EventId User_Settings_Persist = new EventId(3002, "User_Settings_Persist");
public static final EventId User_Settings_Delete = new EventId(3003, "User_Settings_Delete");
}

View File

@ -33,6 +33,12 @@ public final class Permission {
public static String EditEntityDoi = "EditEntityDoi";
public static String DeleteEntityDoi = "DeleteEntityDoi";
//UserSettings
public static String BrowseUserSettings = "BrowseUserSettings";
public static String EditUserSettings = "EditUserSettings";
public static String DeleteUserSettings = "DeleteUserSettings";
// UI Pages
public static String ViewDescriptionTemplateTypePage = "ViewDescriptionTemplateTypePage";

View File

@ -0,0 +1,28 @@
package eu.eudat.commons.enums;
import eu.eudat.data.converters.enums.DatabaseEnum;
import java.util.Map;
public enum UserSettingsType implements DatabaseEnum<Short> {
Settings((short) 0),
Config((short) 1);
private final Short value;
UserSettingsType(Short value) {
this.value = value;
}
public Short getValue() {
return value;
}
private static final Map<Short, UserSettingsType> map = EnumUtils.getEnumValueMap(UserSettingsType.class);
public static UserSettingsType of(Short i) {
return map.get(i);
}
}

View File

@ -0,0 +1,112 @@
package eu.eudat.data;
import eu.eudat.commons.enums.UserSettingsType;
import eu.eudat.data.converters.enums.UserSettingsTypeConverter;
import jakarta.persistence.*;
import java.time.Instant;
import java.util.UUID;
@Entity
@Table(name = "UserSettings")
public class UserSettingsEntity {
@Id
@Column(name = "id", columnDefinition = "uuid", updatable = false, nullable = false)
private UUID id;
public static final String _id = "id";
@Column(name = "key", length = 500, nullable = false)
private String key;
public static final String _key = "key";
@Column(name = "value", nullable = false)
private String value;
public static final String _value = "value";
@Column(name = "name", nullable = false)
private String name;
public static final String _name = "name";
@Column(name = "entity_id", columnDefinition = "uuid", nullable = true)
private UUID entityId;
public static final String _entityId = "entityId";
@Column(name = "created_at", nullable = false)
private Instant createdAt;
public static final String _createdAt = "createdAt";
@Column(name = "updated_at", nullable = false)
private Instant updatedAt;
public static final String _updatedAt = "updatedAt";
@Column(name = "type", nullable = false)
@Convert(converter = UserSettingsTypeConverter.class)
private UserSettingsType type;
public static final String _type = "type";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public Instant getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
public Instant getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}
public UserSettingsType getType() {
return type;
}
public void setType(UserSettingsType type) {
this.type = type;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,11 @@
package eu.eudat.data.converters.enums;
import eu.eudat.commons.enums.UserSettingsType;
import jakarta.persistence.Converter;
@Converter
public class UserSettingsTypeConverter extends DatabaseEnumConverter<UserSettingsType, Short> {
public UserSettingsType of(Short i) {
return UserSettingsType.of(i);
}
}

View File

@ -0,0 +1,97 @@
package eu.eudat.model;
import eu.eudat.commons.enums.UserSettingsType;
import java.time.Instant;
import java.util.UUID;
public class UserSettings {
private UUID id;
public static final String _id = "id";
private String key;
public static final String _key = "key";
private String value;
public static final String _value = "value";
private UUID entityId;
public static final String _entityId = "entityId";
private Instant createdAt;
public static final String _createdAt = "createdAt";
private Instant updatedAt;
public static final String _updatedAt = "updatedAt";
private UserSettingsType type;
public static final String _type = "type";
private String hash;
public static final String _hash = "hash";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public Instant getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Instant createdAt) {
this.createdAt = createdAt;
}
public Instant getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(Instant updatedAt) {
this.updatedAt = updatedAt;
}
public UserSettingsType getType() {
return type;
}
public void setType(UserSettingsType type) {
this.type = type;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@ -0,0 +1,65 @@
package eu.eudat.model.builder;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.UserSettingsEntity;
import eu.eudat.model.UserSettings;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.*;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserSettingsBuilder extends BaseBuilder<UserSettings, UserSettingsEntity> {
private final BuilderFactory builderFactory;
private final QueryFactory queryFactory;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
public UserSettingsBuilder(ConventionService conventionService, BuilderFactory builderFactory, QueryFactory queryFactory) {
super(conventionService, new LoggerService(LoggerFactory.getLogger(UserSettingsBuilder.class)));
this.builderFactory = builderFactory;
this.queryFactory = queryFactory;
}
public UserSettingsBuilder authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
public List<UserSettings> build(FieldSet fields, List<UserSettingsEntity> data) throws MyApplicationException {
this.logger.debug("building for {} items requesting {} fields", Optional.ofNullable(data).map(List::size).orElse(0), Optional.ofNullable(fields).map(FieldSet::getFields).map(Set::size).orElse(0));
this.logger.trace(new DataLogEntry("requested fields", fields));
if (fields == null || data == null || fields.isEmpty()) return new ArrayList<>();
List<UserSettings> models = new ArrayList<>();
for (UserSettingsEntity d : data) {
UserSettings m = new UserSettings();
if (fields.hasField(this.asIndexer(UserSettings._id))) m.setId(d.getId());
if (fields.hasField(this.asIndexer(UserSettings._value))) m.setValue(d.getValue());
if (fields.hasField(this.asIndexer(UserSettings._key))) m.setKey(d.getKey());
if (fields.hasField(this.asIndexer(UserSettings._type))) m.setType(d.getType());
if (fields.hasField(this.asIndexer(UserSettings._entityId))) m.setEntityId(d.getEntityId());
if (fields.hasField(this.asIndexer(UserSettings._createdAt))) m.setCreatedAt(d.getCreatedAt());
if (fields.hasField(this.asIndexer(UserSettings._updatedAt))) m.setUpdatedAt(d.getUpdatedAt());
if (fields.hasField(this.asIndexer(UserSettings._hash))) m.setHash(this.hashValue(Instant.ofEpochMilli(d.getUpdatedAt().toEpochMilli())));
models.add(m);
}
this.logger.debug("build {} items", Optional.of(models).map(List::size).orElse(0));
return models;
}
}

View File

@ -0,0 +1,41 @@
package eu.eudat.model.censorship;
import eu.eudat.authorization.OwnedResource;
import eu.eudat.authorization.Permission;
import eu.eudat.convention.ConventionService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.censor.CensorFactory;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.DataLogEntry;
import gr.cite.tools.logging.LoggerService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.UUID;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserSettingsCensor extends BaseCensor {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserSettingsCensor.class));
protected final AuthorizationService authService;
protected final CensorFactory censorFactory;
public UserSettingsCensor(ConventionService conventionService, AuthorizationService authService, CensorFactory censorFactory) {
super(conventionService);
this.authService = authService;
this.censorFactory = censorFactory;
}
public void censor(FieldSet fields, UUID userId) throws MyForbiddenException {
logger.debug(new DataLogEntry("censoring fields", fields));
if (this.isEmpty(fields)) return;
this.authService.authorizeAtLeastOneForce(userId != null ? List.of(new OwnedResource(userId)) : null, Permission.BrowseUserSettings);
}
}

View File

@ -0,0 +1,80 @@
package eu.eudat.model.persist;
import eu.eudat.commons.enums.UserSettingsType;
import eu.eudat.commons.validation.FieldNotNullIfOtherSet;
import eu.eudat.commons.validation.ValidId;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.UUID;
@FieldNotNullIfOtherSet(message = "{validation.hashempty}")
public class UserSettingsPersist {
@ValidId(message = "{validation.invalidid}")
private UUID id;
@NotNull(message = "{validation.empty}")
@NotEmpty(message = "{validation.empty}")
private String key;
@NotNull(message = "{validation.empty}")
@NotEmpty(message = "{validation.empty}")
private String value;
@ValidId(message = "{validation.invalidid}")
private UUID entityId;
@NotNull(message = "{validation.empty}")
private UserSettingsType type;
private String hash;
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public UserSettingsType getType() {
return type;
}
public void setType(UserSettingsType type) {
this.type = type;
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}

View File

@ -0,0 +1,234 @@
package eu.eudat.query;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.enums.UserSettingsType;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.data.UserSettingsEntity;
import eu.eudat.model.UserSettings;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.query.FieldResolver;
import gr.cite.tools.data.query.QueryBase;
import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.*;
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class UserSettingsQuery extends QueryBase<UserSettingsEntity> {
private String like;
private Collection<UUID> ids;
private Collection<UserSettingsType> userSettingsTypes;
private Collection<String> keys;
private Collection<UserSettingsType> types;
private Collection<UUID> entityIds;
private EnumSet<AuthorizationFlags> authorize = EnumSet.of(AuthorizationFlags.None);
private final UserScope userScope;
private final AuthorizationService authService;
public UserSettingsQuery(
UserScope userScope,
AuthorizationService authService
) {
this.userScope = userScope;
this.authService = authService;
}
public UserSettingsQuery like(String like) {
this.like = like;
return this;
}
public UserSettingsQuery ids(UUID value) {
this.ids = List.of(value);
return this;
}
public UserSettingsQuery ids(UUID... value) {
this.ids = Arrays.asList(value);
return this;
}
public UserSettingsQuery ids(Collection<UUID> values) {
this.ids = values;
return this;
}
public UserSettingsQuery userSettingsTypes(UserSettingsType value) {
this.userSettingsTypes = List.of(value);
return this;
}
public UserSettingsQuery userSettingsTypes(UserSettingsType... value) {
this.userSettingsTypes = Arrays.asList(value);
return this;
}
public UserSettingsQuery userSettingsTypes(Collection<UserSettingsType> values) {
this.userSettingsTypes = values;
return this;
}
public UserSettingsQuery keys(String value) {
this.keys = List.of(value);
return this;
}
public UserSettingsQuery keys(String... value) {
this.keys = Arrays.asList(value);
return this;
}
public UserSettingsQuery keys(Collection<String> values) {
this.keys = values;
return this;
}
public UserSettingsQuery types(UserSettingsType value) {
this.types = List.of(value);
return this;
}
public UserSettingsQuery types(UserSettingsType... value) {
this.types = Arrays.asList(value);
return this;
}
public UserSettingsQuery types(Collection<UserSettingsType> values) {
this.types = values;
return this;
}
public UserSettingsQuery entityIds(UUID value) {
this.entityIds = List.of(value);
return this;
}
public UserSettingsQuery entityIds(UUID... value) {
this.entityIds = Arrays.asList(value);
return this;
}
public UserSettingsQuery entityIds(Collection<UUID> values) {
this.entityIds = values;
return this;
}
public UserSettingsQuery authorize(EnumSet<AuthorizationFlags> values) {
this.authorize = values;
return this;
}
@Override
protected Boolean isFalseQuery() {
return this.isEmpty(this.ids) || this.isEmpty(this.keys) || this.isEmpty(this.types) || this.isEmpty(this.entityIds);
}
@Override
protected Class<UserSettingsEntity> entityClass() {
return UserSettingsEntity.class;
}
@Override
protected <X, Y> Predicate applyAuthZ(QueryContext<X, Y> queryContext) {
if (this.authorize.contains(AuthorizationFlags.None)) return null;
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseUserSettings)) return null;
UUID ownerId = null;
if (this.authorize.contains(AuthorizationFlags.Owner)) ownerId = this.userScope.getUserIdSafe();
List<Predicate> predicates = new ArrayList<>();
if (ownerId != null) {
predicates.add(queryContext.CriteriaBuilder.equal(queryContext.Root.get(UserSettingsEntity._entityId), ownerId));
}
if (predicates.size() > 0) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return queryContext.CriteriaBuilder.or(); //Creates a false query
}
}
@Override
protected <X, Y> Predicate applyFilters(QueryContext<X, Y> queryContext) {
List<Predicate> predicates = new ArrayList<>();
if (this.like != null && !this.like.isEmpty()) {
predicates.add(queryContext.CriteriaBuilder.or(queryContext.CriteriaBuilder.like(queryContext.Root.get(UserSettingsEntity._key), this.like),
queryContext.CriteriaBuilder.like(queryContext.Root.get(UserSettingsEntity._value), this.like)
));
}
if (this.ids != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserSettingsEntity._id));
for (UUID item : this.ids) inClause.value(item);
predicates.add(inClause);
}
if (this.userSettingsTypes != null) {
CriteriaBuilder.In<UserSettingsType> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserSettingsEntity._type));
for (UserSettingsType item : this.userSettingsTypes) inClause.value(item);
predicates.add(inClause);
}
if (this.keys != null) {
CriteriaBuilder.In<String> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserSettingsEntity._key));
for (String item : this.keys) inClause.value(item);
predicates.add(inClause);
}
if (this.entityIds != null) {
CriteriaBuilder.In<UUID> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserSettingsEntity._entityId));
for (UUID item : this.entityIds) inClause.value(item);
predicates.add(inClause);
}
if (this.types != null) {
CriteriaBuilder.In<UserSettingsType> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserSettingsEntity._type));
for (UserSettingsType item : this.types) inClause.value(item);
predicates.add(inClause);
}
if (predicates.size() > 0) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
return queryContext.CriteriaBuilder.and(predicatesArray);
} else {
return null;
}
}
@Override
protected String fieldNameOf(FieldResolver item) {
if (item.match(UserSettings._id)) return UserSettingsEntity._id;
else if (item.match(UserSettings._entityId)) return UserSettingsEntity._entityId;
else if (item.match(UserSettings._value)) return UserSettingsEntity._value;
else if (item.match(UserSettings._key)) return UserSettingsEntity._key;
else if (item.match(UserSettings._type)) return UserSettingsEntity._type;
else if (item.match(UserSettings._createdAt)) return UserSettingsEntity._createdAt;
else if (item.match(UserSettings._updatedAt)) return UserSettingsEntity._updatedAt;
else if (item.match(UserSettings._hash)) return UserSettingsEntity._updatedAt;
else return null;
}
@Override
protected UserSettingsEntity convert(Tuple tuple, Set<String> columns) {
UserSettingsEntity item = new UserSettingsEntity();
item.setId(QueryBase.convertSafe(tuple, columns, UserSettingsEntity._id, UUID.class));
item.setEntityId(QueryBase.convertSafe(tuple, columns, UserSettingsEntity._entityId, UUID.class));
item.setKey(QueryBase.convertSafe(tuple, columns, UserSettingsEntity._key, String.class));
item.setValue(QueryBase.convertSafe(tuple, columns, UserSettingsEntity._value, String.class));
item.setType(QueryBase.convertSafe(tuple, columns, UserSettingsEntity._type, UserSettingsType.class));
item.setCreatedAt(QueryBase.convertSafe(tuple, columns, UserSettingsEntity._createdAt, Instant.class));
item.setUpdatedAt(QueryBase.convertSafe(tuple, columns, UserSettingsEntity._updatedAt, Instant.class));
return item;
}
}

View File

@ -0,0 +1,52 @@
package eu.eudat.query.lookup;
import eu.eudat.commons.enums.UserSettingsType;
import eu.eudat.query.UserSettingsQuery;
import gr.cite.tools.data.query.Lookup;
import gr.cite.tools.data.query.QueryFactory;
import java.util.List;
import java.util.UUID;
public class UserSettingsLookup extends Lookup {
private String like;
private List<UUID> ids;
private List<UserSettingsType> userSettingsTypes;
public String getLike() {
return like;
}
public void setLike(String like) {
this.like = like;
}
public List<UUID> getIds() {
return ids;
}
public void setIds(List<UUID> ids) {
this.ids = ids;
}
public List<UserSettingsType> getUserSettingsTypes() {
return userSettingsTypes;
}
public void setUserSettingsTypes(List<UserSettingsType> userSettingsTypes) {
this.userSettingsTypes = userSettingsTypes;
}
public UserSettingsQuery enrich(QueryFactory queryFactory) {
UserSettingsQuery query = queryFactory.query(UserSettingsQuery.class);
if (this.like != null) query.like(this.like);
if (this.ids != null) query.ids(this.ids);
if (this.userSettingsTypes != null) query.userSettingsTypes(this.userSettingsTypes);
this.enrichCommon(query);
return query;
}
}

View File

@ -39,7 +39,6 @@ import java.util.List;
import java.util.UUID;
@Service
@RequestScope
public class DescriptionTemplateTypeServiceImpl implements DescriptionTemplateTypeService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(DescriptionTemplateTypeServiceImpl.class));

View File

@ -0,0 +1,15 @@
package eu.eudat.service.user.settings;
import eu.eudat.model.UserSettings;
import eu.eudat.model.persist.UserSettingsPersist;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.FieldSet;
import javax.management.InvalidApplicationException;
public interface UserSettingsService {
UserSettings persist(UserSettingsPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException;
}

View File

@ -0,0 +1,103 @@
package eu.eudat.service.user.settings;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.UserSettingsEntity;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.model.UserSettings;
import eu.eudat.model.builder.UserSettingsBuilder;
import eu.eudat.model.persist.UserSettingsPersist;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import jakarta.persistence.EntityManager;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.context.annotation.RequestScope;
import javax.management.InvalidApplicationException;
import java.time.Instant;
import java.util.UUID;
@Service
public class UserSettingsServiceImpl implements UserSettingsService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserSettingsServiceImpl.class));
private final EntityManager entityManager;
private final AuthorizationService authorizationService;
private final DeleterFactory deleterFactory;
private final BuilderFactory builderFactory;
private final ConventionService conventionService;
private final ErrorThesaurusProperties errors;
private final MessageSource messageSource;
private final UserScope userScope;
@Autowired
public UserSettingsServiceImpl(
EntityManager entityManager,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
BuilderFactory builderFactory,
ConventionService conventionService,
ErrorThesaurusProperties errors,
MessageSource messageSource,
UserScope userScope) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.builderFactory = builderFactory;
this.conventionService = conventionService;
this.errors = errors;
this.messageSource = messageSource;
this.userScope = userScope;
}
@Override
public UserSettings persist(UserSettingsPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException {
logger.debug(new MapLogEntry("persisting data access request").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditUserSettings);
boolean isUpdate = this.conventionService.isValidGuid(model.getId());
UserSettingsEntity data;
if (isUpdate) {
data = this.entityManager.find(UserSettingsEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), UserSettings.class.getSimpleName()}, LocaleContextHolder.getLocale()));
} else {
data = new UserSettingsEntity();
data.setCreatedAt(Instant.now());
data.setId(UUID.randomUUID());
}
data.setName(model.getKey());
data.setType(model.getType());
data.setKey(model.getKey());
data.setValue(model.getValue());
data.setEntityId(model.getEntityId());
data.setUpdatedAt(Instant.now());
if (isUpdate) this.entityManager.merge(data);
else this.entityManager.persist(data);
this.entityManager.flush();
return this.builderFactory.builder(UserSettingsBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(BaseFieldSet.build(fields, UserSettings._id, UserSettings._key), data);
}
}

View File

@ -0,0 +1,120 @@
package eu.eudat.controllers.v2;
import eu.eudat.audit.AuditableAction;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.data.UserSettingsEntity;
import eu.eudat.model.UserSettings;
import eu.eudat.model.builder.UserSettingsBuilder;
import eu.eudat.model.censorship.UserSettingsCensor;
import eu.eudat.model.persist.UserSettingsPersist;
import eu.eudat.model.result.QueryResult;
import eu.eudat.query.UserSettingsQuery;
import eu.eudat.query.lookup.UserSettingsLookup;
import eu.eudat.service.user.settings.UserSettingsService;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.censor.CensorFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import gr.cite.tools.validation.MyValidate;
import jakarta.transaction.Transactional;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.bind.annotation.*;
import javax.management.InvalidApplicationException;
import java.util.*;
@RestController
@CrossOrigin
@RequestMapping(path = "api/user-settings")
public class UserSettingsController {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(UserSettingsController.class));
private final BuilderFactory builderFactory;
private final AuditService auditService;
private final UserSettingsService settingsService;
private final CensorFactory censorFactory;
private final QueryFactory queryFactory;
private final MessageSource messageSource;
@Autowired
public UserSettingsController(
BuilderFactory builderFactory,
AuditService auditService,
UserSettingsService settingsService,
CensorFactory censorFactory,
QueryFactory queryFactory,
MessageSource messageSource) {
this.builderFactory = builderFactory;
this.auditService = auditService;
this.settingsService = settingsService;
this.censorFactory = censorFactory;
this.queryFactory = queryFactory;
this.messageSource = messageSource;
}
@PostMapping("query")
public QueryResult<UserSettings> Query(@RequestBody UserSettingsLookup lookup) throws MyApplicationException, MyForbiddenException {
logger.debug("querying {}", UserSettings.class.getSimpleName());
this.censorFactory.censor(UserSettingsCensor.class).censor(lookup.getProject(), null);
UserSettingsQuery query = lookup.enrich(this.queryFactory).authorize(AuthorizationFlags.OwnerOrPermission);
List<UserSettingsEntity> data = query.collectAs(lookup.getProject());
List<UserSettings> models = this.builderFactory.builder(UserSettingsBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(lookup.getProject(), data);
long count = (lookup.getMetadata() != null && lookup.getMetadata().getCountAll()) ? query.count() : models.size();
this.auditService.track(AuditableAction.User_Settings_Query, "lookup", lookup);
//this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action);
return new QueryResult<>(models, count);
}
@GetMapping("{key}")
@Transactional
public UserSettings Get(@PathVariable("key") String key) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
logger.debug(new MapLogEntry("retrieving" + UserSettings.class.getSimpleName()).And("key", key));
BaseFieldSet fieldSet = new BaseFieldSet();
fieldSet.setFields(Set.of(
UserSettings._id,
UserSettings._key,
UserSettings._value,
UserSettings._entityId,
UserSettings._createdAt,
UserSettings._updatedAt,
UserSettings._type
));
UserSettingsQuery query = this.queryFactory.query(UserSettingsQuery.class).authorize(AuthorizationFlags.OwnerOrPermission).keys(key);
UserSettings model = this.builderFactory.builder(UserSettingsBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(fieldSet, query.firstAs(fieldSet));
this.auditService.track(AuditableAction.User_Settings_Lookup, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("key", key)
));
return model;
}
@PostMapping("persist")
@Transactional
public UserSettings Persist(@MyValidate @RequestBody UserSettingsPersist model, FieldSet fieldSet) throws MyApplicationException, MyForbiddenException, MyNotFoundException, InvalidApplicationException {
logger.debug(new MapLogEntry("persisting" + UserSettings.class.getSimpleName()).And("model", model).And("fieldSet", fieldSet));
UserSettings persisted = this.settingsService.persist(model, fieldSet);
this.auditService.track(AuditableAction.User_Settings_Persist, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model),
new AbstractMap.SimpleEntry<String, Object>("fields", fieldSet)
));
//this.auditService.trackIdentity(AuditableAction.IdentityTracking_Action);
return persisted;
}
}

View File

@ -13,7 +13,7 @@ ADD COLUMN "Type" uuid;
INSERT INTO public."DescriptionTemplateType" ("ID", "Name", "Status")
VALUES ('709a8400-10ca-11ee-be56-0242ac120002', 'Dataset', 1);
UPDATE public."DescriptionTemplate" SET ("Type") = '709a8400-10ca-11ee-be56-0242ac120002';
UPDATE public."DescriptionTemplate" SET "Type" = '709a8400-10ca-11ee-be56-0242ac120002';
ALTER TABLE public."DescriptionTemplate"
ALTER COLUMN "Type" SET NOT NULL;

View File

@ -1,3 +1,9 @@
DO $$DECLARE
this_version CONSTANT varchar := '00.01.001';
BEGIN
PERFORM * FROM "DBVersion" WHERE version = this_version;
IF FOUND THEN RETURN; END IF;
ALTER TABLE public."DescriptionTemplateType"
RENAME "ID" TO id;
@ -31,4 +37,9 @@ UPDATE public."DescriptionTemplateType" SET is_active = 0 where status = 99;
UPDATE public."DescriptionTemplateType" SET status = 0 where is_active = 0;
ALTER TABLE public."DescriptionTemplateType"
ALTER COLUMN is_active SET NOT NULL;
ALTER COLUMN is_active SET NOT NULL;
INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.001', '2023-10-19 12:00:00.000000+02', now(), 'Align Description Template Type.');
END$$;

View File

@ -1,3 +1,9 @@
DO $$DECLARE
this_version CONSTANT varchar := '00.01.002';
BEGIN
PERFORM * FROM "DBVersion" WHERE version = this_version;
IF FOUND THEN RETURN; END IF;
ALTER TABLE public."EntityDoi"
RENAME "ID" TO id;
@ -20,4 +26,9 @@ ALTER TABLE public."EntityDoi"
RENAME "EntityId" TO entity_id;
ALTER TABLE public."EntityDoi"
ADD COLUMN is_active smallint NOT NULL DEFAULT 1
ADD COLUMN is_active smallint NOT NULL DEFAULT 1
INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.002', '2023-10-19 12:00:00.000000+02', now(), 'Align Entity Doi table.');
END$$;

View File

@ -0,0 +1,23 @@
DO $$DECLARE
this_version CONSTANT varchar := '00.01.003';
BEGIN
PERFORM * FROM "DBVersion" WHERE version = this_version;
IF FOUND THEN RETURN; END IF;
CREATE TABLE IF NOT EXISTS public.UserSettings
(
id uuid NOT NULL,
key character varying(500) COLLATE pg_catalog."default" NOT NULL,
entity_id uuid,
created_at timestamp without time zone NOT NULL,
updated_at timestamp without time zone NOT NULL,
type character varying(200) COLLATE pg_catalog."default" NOT NULL,
value text COLLATE pg_catalog."default" NOT NULL,
name character varying(500) COLLATE pg_catalog."default" NOT NULL,
CONSTRAINT user_settings_pkey PRIMARY KEY (id)
)
INSERT INTO public."DBVersion" VALUES ('DMPDB', '00.01.003', '2023-10-19 12:00:00.000000+02', now(), 'Add UserSettings table.');
END$$;

View File

@ -4,6 +4,7 @@ import { ReloadHelperComponent } from '@app/ui/misc/reload-helper/reload-helper.
import { Oauth2DialogComponent } from './ui/misc/oauth2-dialog/oauth2-dialog.component';
import { AppComponent } from './app.component';
import { AppPermission } from './core/common/enum/permission.enum';
import { BreadcrumbService } from './ui/misc/breadcrumb/breadcrumb.service';
const appRoutes: Routes = [
{
@ -11,7 +12,10 @@ const appRoutes: Routes = [
component: AppComponent,
data: {
breadcrumbs: false,
title: 'GENERAL.TITLES.GENERAL'
title: 'GENERAL.TITLES.GENERAL',
...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.HOME'
})
},
pathMatch: 'full'
},
@ -75,8 +79,8 @@ const appRoutes: Routes = [
}
},
{
path: 'dmp-profiles',
loadChildren: () => import('./ui/admin/dmp-profile/dmp-profile.module').then(m => m.DmpProfileModule),
path: 'dmp-blueprints',
loadChildren: () => import('./ui/admin/dmp-blueprint/dmp-blueprint.module').then(m => m.DmpBlueprintModule),
data: {
breadcrumb: true,
title: 'GENERAL.TITLES.DMP-BLUEPRINTS'
@ -99,14 +103,17 @@ const appRoutes: Routes = [
}
},
{
path: 'description-types',
loadChildren: () => import('./ui/admin/description-types/description-types.module').then(m => m.DescriptionTypesModule),
path: 'description-template-type',
loadChildren: () => import('./ui/admin/description-types/description-template-type.module').then(m => m.DescriptionTemplateTypesModule),
data: {
breadcrumb: true,
title: 'GENERAL.TITLES.DESCRIPTION-TYPES',
title: 'GENERAL.TITLES.DESCRIPTION-TEMPLATE-TYPES',
authContext: {
permissions: [AppPermission.ViewDescriptionTemplateTypePage]
}
},
...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.DESCRIPTION-TEMPLATE-TYPES'
})
},
},
{

View File

@ -8,7 +8,7 @@ import { TranslateService } from '@ngx-translate/core';
import { environment } from '../environments/environment';
import { AuthService } from './core/services/auth/auth.service';
import { CultureService } from './core/services/culture/culture-service';
import { BreadCrumbResolverService } from './ui/misc/breadcrumb/service/breadcrumb.service';
// import { BreadCrumbResolverService } from './ui/misc/breadcrumb/service/breadcrumb.service';
import { Title } from '@angular/platform-browser';
import { NgcCookieConsentService, NgcStatusChangeEvent } from "ngx-cookieconsent";
import { CookieService } from "ngx-cookie-service";
@ -19,6 +19,7 @@ import { Location } from '@angular/common';
import { MatomoService } from './core/services/matomo/matomo-service';
import { SideNavService } from './core/services/sidenav/side-nav.sevice';
import { MatSidenav } from '@angular/material/sidenav';
import { TimezoneService } from './core/services/timezone/timezone-service';
declare const gapi: any;
@ -45,9 +46,10 @@ export class AppComponent implements OnInit, AfterViewInit {
private route: ActivatedRoute,
private authentication: AuthService,
private translate: TranslateService,
private breadCrumbResolverService: BreadCrumbResolverService,
// private breadCrumbResolverService: BreadCrumbResolverService,
private titleService: Title,
private cultureService: CultureService,
private timezoneService: TimezoneService,
private cookieService: CookieService,
private ccService: NgcCookieConsentService,
private language: LanguageService,
@ -96,7 +98,7 @@ export class AppComponent implements OnInit, AfterViewInit {
}
onActivate(event: any) {
this.breadCrumbResolverService.push(event);
// this.breadCrumbResolverService.push(event);
}
onDeactivate(event: any) {
@ -225,6 +227,7 @@ export class AppComponent implements OnInit, AfterViewInit {
initializeServices() {
this.translate.setDefaultLang(this.configurationService.defaultLanguage || 'en');
this.authentication.currentAccountIsAuthenticated() && this.authentication.getUserProfileCulture() ? this.cultureService.cultureSelected(this.authentication.getUserProfileCulture()) : this.cultureService.cultureSelected(this.configurationService.defaultCulture);
this.authentication.currentAccountIsAuthenticated() && this.authentication.getUserProfileTimezone() ? this.timezoneService.timezoneSelected(this.authentication.getUserProfileTimezone()) : this.timezoneService.timezoneSelected(this.configurationService.defaultTimezone);
this.authentication.currentAccountIsAuthenticated() && this.authentication.getUserProfileLanguage() ? this.language.changeLanguage(this.authentication.getUserProfileLanguage()) : (this.configurationService.defaultLanguage || 'en');
}

View File

@ -13,7 +13,7 @@ import { CoreServiceModule } from '@app/core/core-service.module';
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';
import { BreadcrumbModule } from '@app/ui/misc/breadcrumb/breadcrumb.module';
// import { BreadcrumbModule } from '@app/ui/misc/breadcrumb/breadcrumb.module';
import { HelpContentModule } from '@app/ui/misc/help-content/help-content.module';
import { NavigationModule } from '@app/ui/misc/navigation/navigation.module';
import { ReloadHelperComponent } from '@app/ui/misc/reload-helper/reload-helper.component';
@ -137,7 +137,7 @@ export function InstallationConfigurationFactory(appConfig: ConfigurationService
//Ui
NotificationModule,
NavigationModule,
BreadcrumbModule,
// BreadcrumbModule,
HelpContentModule,
ReactiveFormsModule,
FormsModule,

View File

@ -1,5 +1,4 @@
export enum DescriptionTemplateTypeStatus {
Draft = 0,
Finalized = 1,
Deleted = 99
Draft = 'Draft',
Finalized = 'Finalized'
}

View File

@ -1,4 +1,4 @@
export enum DmpProfileFieldDataType {
export enum DmpBlueprintFieldDataType {
Date = 0,
Number = 1,
Text = 2,

View File

@ -0,0 +1,4 @@
export enum DmpBlueprintStatus {
Draft = 'Draft',
Finalized = 'Finalized'
}

View File

@ -0,0 +1,3 @@
export enum DmpBlueprintType {
Input = 0
}

View File

@ -1,5 +0,0 @@
export enum DmpProfileStatus {
Draft = 0,
Finalized = 1,
Deleted = 99
}

View File

@ -1,3 +0,0 @@
export enum DmpProfileType {
Input = 0
}

View File

@ -0,0 +1,4 @@
export enum IsActive {
Inactive = 0,
Active = 1
}

View File

@ -1,10 +1,9 @@
import { HttpClient } from '@angular/common/http';
import { APP_INITIALIZER, ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core';
import { PrefillingService } from "@app/core/services/prefilling.service";
import { CookieService } from 'ngx-cookie-service';
import { AdminAuthGuard } from './admin-auth-guard.service';
import { AuthGuard } from './auth-guard.service';
import { AuthService } from './services/auth/auth.service';
import { ConfigurationService } from './services/configuration/configuration.service';
import { ContactSupportService } from './services/contact-support/contact-support.service';
import { CultureService } from './services/culture/culture-service';
import { LanguageInfoService } from './services/culture/language-info-service';
@ -14,8 +13,10 @@ import { DatasetProfileService } from './services/dataset-profile/dataset-profil
import { DatasetWizardService } from './services/dataset-wizard/dataset-wizard.service';
import { DatasetExternalAutocompleteService } from './services/dataset/dataset-external-autocomplete.service';
import { DatasetService } from './services/dataset/dataset.service';
import { DepositRepositoriesService } from './services/deposit-repositories/deposit-repositories.service';
import { DescriptionTemplateTypeService } from './services/description-template-type/description-template-type.service';
import { DmpBlueprintService } from './services/dmp/dmp-blueprint.service';
import { DmpInvitationService } from './services/dmp/dmp-invitation.service';
import { DmpProfileService } from './services/dmp/dmp-profile.service';
import { DmpService } from './services/dmp/dmp.service';
import { EmailConfirmationService } from './services/email-confirmation/email-confirmation.service';
import { ExternalDataRepositoryService } from './services/external-sources/data-repository/extternal-data-repository.service';
@ -28,6 +29,7 @@ import { ExternalServiceService } from './services/external-sources/service/exte
import { FunderService } from './services/funder/funder.service';
import { GrantFileUploadService } from './services/grant/grant-file-upload.service';
import { GrantService } from './services/grant/grant.service';
import { BaseHttpV2Service } from './services/http/base-http-v2.service';
import { BaseHttpService } from './services/http/base-http.service';
import { LanguageService } from './services/language/language.service';
import { LockService } from './services/lock/lock.service';
@ -40,21 +42,19 @@ import { ProjectService } from './services/project/project.service';
import { QuickWizardService } from './services/quick-wizard/quick-wizard.service';
import { SearchBarService } from './services/search-bar/search-bar.service';
import { TimezoneService } from './services/timezone/timezone-service';
import { UnlinkAccountEmailConfirmationService } from './services/unlink-account-email-confirmation/unlink-account-email-confirmation.service';
import { UserService } from './services/user/user.service';
import { CollectionUtils } from './services/utilities/collection-utils.service';
import { TypeUtils } from './services/utilities/type-utils.service';
import { SpecialAuthGuard } from './special-auth-guard.service';
import {PrefillingService} from "@app/core/services/prefilling.service";
import { DepositRepositoriesService } from './services/deposit-repositories/deposit-repositories.service';
import { UnlinkAccountEmailConfirmationService } from './services/unlink-account-email-confirmation/unlink-account-email-confirmation.service';
import { DescriptionTemplateTypeService } from './services/description-template-type/description-template-type.service';
import { BaseHttpV2Service } from './services/http/base-http-v2.service';
//import { KeycloakService } from 'keycloak-angular';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { FilterService } from '@common/modules/text-filter/filter-service';
import { PrincipalService } from './services/http/principal.service';
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
import { BaseHttpParams } from '@common/http/base-http-params';
import { from } from 'rxjs';
import { SupportiveMaterialService } from './services/supportive-material/supportive-material.service';
import { UserSettingsHttpService } from './services/user-settings/user-settings-http.service';
import { UserSettingsService } from './services/user-settings/user-settings.service';
import { QueryParamsService } from './services/utilities/query-params.service';
//
//
// This is shared module that provides all the services. Its imported only once on the AppModule.
@ -96,7 +96,7 @@ export class CoreServiceModule {
GrantFileUploadService,
DmpService,
DepositRepositoriesService,
DmpProfileService,
DmpBlueprintService,
ExternalSourcesService,
ExternalSourcesConfigurationService,
DatasetService,
@ -123,7 +123,12 @@ export class CoreServiceModule {
UnlinkAccountEmailConfirmationService,
LanguageInfoService,
PrefillingService,
DescriptionTemplateTypeService
DescriptionTemplateTypeService,
HttpErrorHandlingService,
QueryParamsService,
UserSettingsService,
UserSettingsHttpService,
FilterService
],
};
}

View File

@ -10,6 +10,7 @@ import { DateTimeCultureFormatPipe } from './pipes/date-time-culture-format.pipe
import {FieldValuePipe} from "@app/core/pipes/field-value.pipe";
import {ColumnClassPipe} from "@app/core/pipes/column-class.pipe";
import { DatasetInSectioPipe } from './pipes/dataset-in-section.pipe';
import { PipeService } from '@common/formatting/pipe.service';
//
//
@ -27,7 +28,7 @@ import { DatasetInSectioPipe } from './pipes/dataset-in-section.pipe';
JsonParserPipe,
FieldValuePipe,
ColumnClassPipe,
DatasetInSectioPipe
DatasetInSectioPipe,
],
exports: [
NgForLimitPipe,
@ -43,6 +44,7 @@ import { DatasetInSectioPipe } from './pipes/dataset-in-section.pipe';
providers: [
EnumUtils,
DatePipe,
PipeService,
NgForLimitPipe,
TimezoneInfoDisplayPipe,
DateFormatPipe,

View File

@ -1,5 +1,12 @@
export interface DescriptionTemplateType {
id: string;
import { DescriptionTemplateTypeStatus } from "@app/core/common/enum/description-template-type-status";
import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model";
export interface DescriptionTemplateType extends BaseEntity {
name: string;
status: number;
status: DescriptionTemplateTypeStatus;
}
export interface DescriptionTemplateTypePersist extends BaseEntityPersist {
name: string;
status: DescriptionTemplateTypeStatus;
}

View File

@ -1,4 +1,4 @@
export interface DmpProfileExternalAutoCompleteField {
export interface DmpBlueprintExternalAutoCompleteField {
url: string;
optionsRoot: string;
multiAutoComplete: boolean;

View File

@ -0,0 +1,13 @@
import { DmpBlueprintFieldDataType } from '../../common/enum/dmp-blueprint-field-type';
import { DmpBlueprintType } from '../../common/enum/dmp-blueprint-type';
import { DmpBlueprintExternalAutoCompleteFieldDataEditorModel } from '../../../ui/admin/dmp-blueprint/editor/external-autocomplete/dmp-blueprint-external-autocomplete-field-editor.model';
export interface DmpBlueprintField {
id: string;
type: DmpBlueprintType;
dataType: DmpBlueprintFieldDataType;
required: boolean;
label: string;
value: any;
externalAutocomplete?: DmpBlueprintExternalAutoCompleteFieldDataEditorModel;
}

View File

@ -0,0 +1,10 @@
import { DmpBlueprintDefinition } from "../dmp/dmp-blueprint/dmp-blueprint";
export interface DmpBlueprintListing {
id: string;
label: string;
definition: DmpBlueprintDefinition;
status: number;
created: Date;
modified: Date;
}

View File

@ -0,0 +1,19 @@
import { DmpBlueprintStatus } from "@app/core/common/enum/dmp-blueprint-status";
import { BaseEntity, BaseEntityPersist } from "@common/base/base-entity.model";
import { DmpBlueprintDefinition } from "../dmp/dmp-blueprint/dmp-blueprint";
export interface DmpBlueprint extends BaseEntity {
label: string;
definition: DmpBlueprintDefinition;
status: DmpBlueprintStatus;
description: string;
}
export interface DmpBlueprintPersist extends BaseEntityPersist {
label: string;
definition: DmpBlueprintDefinition;
status: number;
description: string;
}

View File

@ -1,13 +0,0 @@
import { DmpProfileFieldDataType } from '../../common/enum/dmp-profile-field-type';
import { DmpProfileType } from '../../common/enum/dmp-profile-type';
import { DmpProfileExternalAutoCompleteFieldDataEditorModel } from '../../../ui/admin/dmp-profile/editor/external-autocomplete/dmp-profile-external-autocomplete-field-editor.model';
export interface DmpProfileField {
id: string;
type: DmpProfileType;
dataType: DmpProfileFieldDataType;
required: boolean;
label: string;
value: any;
externalAutocomplete?: DmpProfileExternalAutoCompleteFieldDataEditorModel;
}

View File

@ -1,10 +0,0 @@
import { DmpProfileDefinition } from "./dmp-profile";
export interface DmpProfileListing {
id: string;
label: string;
definition: DmpProfileDefinition;
status: number;
created: Date;
modified: Date;
}

View File

@ -1,16 +0,0 @@
import { DmpProfileField } from "./dmp-profile-field";
export interface DmpProfile {
id: string;
label: string;
definition: DmpProfileDefinition;
status: number;
created: Date;
modified: Date;
description: string;
}
export interface DmpProfileDefinition {
fields: DmpProfileField[];
}

View File

@ -1,68 +1,72 @@
import { DmpBlueprintStatus } from "@app/core/common/enum/dmp-blueprint-status";
import { DmpBlueprintField } from "../../dmp-blueprint/dmp-blueprint-field";
export interface DmpBlueprint {
id: string;
label: string;
definition: DmpBlueprintDefinition;
status: number;
status: DmpBlueprintStatus;
created: Date;
modified: Date;
description: string;
}
export interface DmpBlueprintDefinition {
sections: SectionDmpBlueprint[];
sections: SectionDmpBlueprint[];
fields: DmpBlueprintField[];
}
export interface SectionDmpBlueprint {
id: string;
label: string;
description: string;
ordinal: number;
fields: FieldInSection[];
hasTemplates: boolean;
descriptionTemplates?: DescriptionTemplatesInSection[];
id: string;
label: string;
description: string;
ordinal: number;
fields: FieldInSection[];
hasTemplates: boolean;
descriptionTemplates?: DescriptionTemplatesInSection[];
}
export interface FieldInSection {
id: string;
category: FieldCategory;
type: number;
label: string;
placeholder: string;
description: string;
required: boolean;
ordinal: number;
id: string;
category: FieldCategory;
type: number;
label: string;
placeholder: string;
description: string;
required: boolean;
ordinal: number;
}
export enum FieldCategory {
SYSTEM = 0,
EXTRA = 1
SYSTEM = 0,
EXTRA = 1
}
export enum SystemFieldType {
TEXT = 0,
HTML_TEXT = 1,
RESEARCHERS= 2,
ORGANIZATIONS = 3,
LANGUAGE = 4,
CONTACT = 5,
FUNDER = 6,
GRANT = 7,
PROJECT = 8,
LICENSE = 9,
ACCESS_RIGHTS = 10
TEXT = 0,
HTML_TEXT = 1,
RESEARCHERS = 2,
ORGANIZATIONS = 3,
LANGUAGE = 4,
CONTACT = 5,
FUNDER = 6,
GRANT = 7,
PROJECT = 8,
LICENSE = 9,
ACCESS_RIGHTS = 10
}
export interface DescriptionTemplatesInSection {
id: string;
descriptionTemplateId: string;
label: string;
minMultiplicity: number;
maxMultiplicity: number;
id: string;
descriptionTemplateId: string;
label: string;
minMultiplicity: number;
maxMultiplicity: number;
}
export enum ExtraFieldType {
TEXT = 0,
RICH_TEXT = 1,
DATE = 2,
NUMBER = 3
TEXT = 0,
RICH_TEXT = 1,
DATE = 2,
NUMBER = 3
}

View File

@ -1,5 +1,5 @@
import { DmpStatus } from "../../common/enum/dmp-status";
import { DmpAssociatedProfileModel } from '../dmp-profile/dmp-associated-profile';
import { DmpAssociatedProfileModel } from '../dmp-blueprint/dmp-associated-profile';
export interface DmpListingModel {
id: string;

View File

@ -1,6 +1,6 @@
import { OrganizationModel } from "../organisation/organization";
import { UserInfoListingModel } from "../user/user-info-listing";
import { DmpAssociatedProfileModel } from "../dmp-profile/dmp-associated-profile";
import { DmpAssociatedProfileModel } from "../dmp-blueprint/dmp-associated-profile";
import { ResearcherModel } from "../researcher/researcher";
import { GrantOverviewModel } from "../grant/grant-overview";
import { DatasetOverviewModel } from "../dataset/dataset-overview";

View File

@ -1,22 +1,21 @@
import { DmpProfileDefinition } from "../dmp-profile/dmp-profile";
import { OrganizationModel } from "../organisation/organization";
import { GrantListingModel } from "../grant/grant-listing";
import { ResearcherModel } from "../researcher/researcher";
import { UserModel } from "../user/user";
import { DmpDynamicField } from "./dmp-dynamic-field";
import { UserInfoListingModel } from "../user/user-info-listing";
import { ProjectModel } from "../project/project";
import { FunderModel } from "../funder/funder";
import { DmpStatus } from '@app/core/common/enum/dmp-status';
import { DatasetWizardModel } from '../dataset/dataset-wizard';
import { FunderModel } from "../funder/funder";
import { GrantListingModel } from "../grant/grant-listing";
import { OrganizationModel } from "../organisation/organization";
import { ProjectModel } from "../project/project";
import { ResearcherModel } from "../researcher/researcher";
import { UserModel } from "../user/user";
import { UserInfoListingModel } from "../user/user-info-listing";
import { DmpDatasetProfile } from "./dmp-dataset-profile/dmp-dataset-profile";
import { DmpDynamicField } from "./dmp-dynamic-field";
import { DmpExtraField } from "./dmp-extra-field";
export interface DmpModel {
id: string;
label: string;
groupId: String;
profile: DmpProfile;
profile: DmpBlueprint;
version: number;
status: DmpStatus;
lockable: boolean;
@ -40,7 +39,7 @@ export interface DmpModel {
}
export interface DmpProfile {
export interface DmpBlueprint {
id: string;
label: string;
}

View File

@ -1,6 +1,6 @@
import { RecentActivityModel } from './recent-activity.model';
import { RecentDatasetModel } from './recent-dataset-activity.model';
import { DmpAssociatedProfileModel } from '../dmp-profile/dmp-associated-profile';
import { DmpAssociatedProfileModel } from '../dmp-blueprint/dmp-associated-profile';
import { UserInfoListingModel } from '../user/user-info-listing';
import { DatasetUrlListing } from '../dataset/dataset-url-listing';

View File

@ -0,0 +1,47 @@
import { UserSettingsType } from '@app/core/services/user-settings/user-settings.service';
import { Guid } from '@common/types/guid';
export interface UserSetting {
id: Guid;
name: string;
type: UserSettingsType;
value: any;
createdAt: Date;
updatedAt: Date;
hash: string;
userId: Guid;
isDefault?: boolean;
}
export interface UserSettings {
key: string;
settings: UserSetting[];
defaultSetting: UserSetting;
}
export interface UserSettingPersist {
id: Guid;
name: string;
key: string;
type: UserSettingsType;
value: string;
hash: string;
isDefault: boolean;
}
export interface UserSettingsKey {
key: string;
}
//TODO possible move these
export interface UserSettingsInformation<T> {
key: string;
type: UserSettingsBuilder<T>;
}
export type UserSettingsBuilder<T> = new () => T;
export interface UserSettingsLookupBuilder<T> {
update(lookup: T);
apply(lookup: T): T;
}

View File

@ -0,0 +1,21 @@
import { Lookup } from "@common/model/lookup";
import { Guid } from "@common/types/guid";
import { IsActive } from "../common/enum/is-active.enum";
export class DescriptionTemplateTypeLookup extends Lookup implements DescriptionTemplateTypeFilter {
ids: Guid[];
excludedIds: Guid[];
like: string;
isActive: IsActive[];
constructor() {
super();
}
}
export interface DescriptionTemplateTypeFilter {
ids: Guid[];
excludedIds: Guid[];
like: string;
isActive: IsActive[];
}

View File

@ -1,14 +0,0 @@
import { Status } from "@app/core/common/enum/status";
import { Lookup } from "@common/model/lookup";
import { Guid } from "@common/types/guid";
export class DescriptionTemplateTypeLookup extends Lookup {
ids: Guid[];
excludedIds: Guid[];
like: string;
isActive: Status[];
constructor() {
super();
}
}

View File

@ -0,0 +1,23 @@
import { Lookup } from '@common/model/lookup';
import { Guid } from '@common/types/guid';
import { IsActive } from '../common/enum/is-active.enum';
export class DmpBlueprintLookup extends Lookup implements DmpBlueprintFilter {
ids: Guid[];
excludedIds: Guid[];
like: string;
isActive: IsActive[];
typeIds: Guid[];
constructor() {
super();
}
}
export interface DmpBlueprintFilter {
ids: Guid[];
excludedIds: Guid[];
like: string;
isActive: IsActive[];
typeIds: Guid[];
}

View File

@ -1,5 +1,6 @@
import { DmpBlueprintStatus } from "@app/core/common/enum/dmp-blueprint-status";
import { BaseCriteria } from "../base-criteria";
export class DmpBlueprintCriteria extends BaseCriteria {
public status?: number;
public status?: DmpBlueprintStatus;
}

View File

@ -1,5 +1,5 @@
import { BaseCriteria } from "../base-criteria";
export class DmpProfileCriteria extends BaseCriteria {
export class DmpBlueprintCriteria extends BaseCriteria {
}

View File

@ -1,6 +1,6 @@
import { BaseCriteria } from "../base-criteria";
export class DmpProfileExternalAutocompleteCriteria extends BaseCriteria {
export class DmpBlueprintExternalAutocompleteCriteria extends BaseCriteria {
public profileID: String;
public fieldID: String;
}

View File

@ -33,6 +33,7 @@ export enum LoginStatus {
@Injectable()
export class AuthService extends BaseService {
public permissionEnum = AppPermission;
public authenticationStateSubject: Subject<AuthenticationState>;
private accessToken: string;
private appAccount: AppAccount;

View File

@ -36,6 +36,11 @@ export class ConfigurationService extends BaseComponent {
return this._defaultCulture;
}
private _defaultTimezone: string;
get defaultTimezone(): string {
return this._defaultTimezone || 'UTC';
}
private _defaultLanguage: string;
get defaultLanguage(): string {
return this._defaultLanguage;
@ -102,6 +107,11 @@ export class ConfigurationService extends BaseComponent {
return this._keycloak;
}
private _userSettingsVersion: string;
get userSettingsVersion(): string {
return this._userSettingsVersion;
}
public loadConfiguration(): Promise<any> {
return new Promise((r, e) => {
@ -143,6 +153,7 @@ export class ConfigurationService extends BaseComponent {
this._app = config.App;
this._helpService = HelpService.parseValue(config.HelpService);
this._defaultCulture = config.defaultCulture;
this._defaultTimezone = config.defaultTimezone;
this._defaultLanguage = config.defaultLanguage;
this._availableLanguages = config.availableLanguages;
this._keycloak = KeycloakConfiguration.parseValue(config.keycloak);
@ -158,6 +169,7 @@ export class ConfigurationService extends BaseComponent {
this._matomoSiteId = config.matomo.siteId;
}
this._maxFileSizeInMB = config.maxFileSizeInMB;
this._userSettingsVersion = config.userSettingsVersion;
}
}

View File

@ -1,42 +1,49 @@
import { HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ConfigurationService } from '../configuration/configuration.service';
import { Observable } from 'rxjs';
import { DescriptionTemplateType } from '@app/core/model/description-template-type/description-template-type';
import { DescriptionTemplateType, DescriptionTemplateTypePersist } from '@app/core/model/description-template-type/description-template-type';
import { DescriptionTemplateTypeLookup } from '@app/core/query/description-template-type.lookup';
import { QueryResult } from '@common/model/query-result';
import { DescriptionTemplateTypeLookup } from '@app/core/query/description-template/description-template-type.lookup';
import { Guid } from '@common/types/guid';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ConfigurationService } from '../configuration/configuration.service';
import { BaseHttpV2Service } from '../http/base-http-v2.service';
@Injectable()
export class DescriptionTemplateTypeService {
private get apiBase(): string { return `${this.configurationService.server}description-template-type`; }
private headers = new HttpHeaders();
constructor(private http: BaseHttpV2Service, private configurationService: ConfigurationService) {}
constructor(private http: BaseHttpV2Service, private installationConfiguration: ConfigurationService) { }
query(lookup: DescriptionTemplateTypeLookup): Observable<QueryResult<DescriptionTemplateType>> {
const url = `${this.apiBase}/query`;
return this.http.post<QueryResult<DescriptionTemplateType>>(url, lookup, { headers: this.headers });
}
private get apiBase(): string { return `${this.installationConfiguration.server}description-template-type`; }
get(id: string): Observable<QueryResult<DescriptionTemplateType>> {
const url = `${this.apiBase}/${id}`;
return this.http.get<QueryResult<DescriptionTemplateType>>(url , { headers: this.headers });
}
query(q: DescriptionTemplateTypeLookup): Observable<QueryResult<DescriptionTemplateType>> {
const url = `${this.apiBase}/query`;
return this.http
.post<QueryResult<DescriptionTemplateType>>(url, q).pipe(
catchError((error: any) => throwError(error)));
}
persist(payload: DescriptionTemplateType): Observable<QueryResult<DescriptionTemplateType>> {
const url = `${this.apiBase}/persist`;
return this.http.post<QueryResult<DescriptionTemplateType>>(url, payload, { headers: this.headers });
}
getSingle(id: Guid, reqFields: string[] = []): Observable<DescriptionTemplateType> {
const url = `${this.apiBase}/${id}`;
const options = { params: { f: reqFields } };
update(payload: DescriptionTemplateType): Observable<QueryResult<DescriptionTemplateType>> {
const url = `${this.apiBase}/update`;
return this.http.post<QueryResult<DescriptionTemplateType>>(url, payload, { headers: this.headers });
}
return this.http
.get<DescriptionTemplateType>(url, options).pipe(
catchError((error: any) => throwError(error)));
}
delete(id: string): Observable<void> {
const url = `${this.apiBase}/delete/${id}`;
return this.http.delete<void>(url, { headers: this.headers });
}
persist(item: DescriptionTemplateTypePersist): Observable<DescriptionTemplateType> {
const url = `${this.apiBase}/persist`;
return this.http
.post<DescriptionTemplateType>(url, item).pipe(
catchError((error: any) => throwError(error)));
}
delete(id: Guid): Observable<DescriptionTemplateType> {
const url = `${this.apiBase}/${id}`;
return this.http
.delete<DescriptionTemplateType>(url).pipe(
catchError((error: any) => throwError(error)));
}
}

View File

@ -0,0 +1,107 @@
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { DataTableData } from '@app/core/model/data-table/data-table-data';
import { DataTableRequest } from '@app/core/model/data-table/data-table-request';
import { DatasetListingModel } from '@app/core/model/dataset/dataset-listing';
import { DmpBlueprint, DmpBlueprintPersist } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { DmpBlueprintListing } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint-listing';
import { DmpBlueprintLookup } from '@app/core/query/dmp-blueprint.lookup';
import { DmpBlueprintCriteria } from '@app/core/query/dmp/dmp-blueprint-criteria';
import { DmpBlueprintExternalAutocompleteCriteria } from '@app/core/query/dmp/dmp-profile-external-autocomplete-criteria';
import { RequestItem } from '@app/core/query/request-item';
import { BaseHttpParams } from '@common/http/base-http-params';
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
import { QueryResult } from '@common/model/query-result';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { ConfigurationService } from '../configuration/configuration.service';
import { BaseHttpV2Service } from '../http/base-http-v2.service';
import { Guid } from '@common/types/guid';
@Injectable()
export class DmpBlueprintService {
private actionUrl: string;
private headers = new HttpHeaders();
constructor(private http: BaseHttpV2Service, private httpClient: HttpClient, private configurationService: ConfigurationService) {
this.actionUrl = configurationService.server + 'dmpprofile/';
}
private get apiBase(): string { return `${this.configurationService.server}dmpprofile`; }
query(q: DmpBlueprintLookup): Observable<QueryResult<DmpBlueprint>> {
const url = `${this.apiBase}/query`;
return this.http.post<QueryResult<DmpBlueprint>>(url, q).pipe(catchError((error: any) => throwError(error)));
}
getSingle(id: Guid, reqFields: string[] = []): Observable<DmpBlueprint> {
const url = `${this.apiBase}/${id}`;
const options = { params: { f: reqFields } };
return this.http
.get<DmpBlueprint>(url, options).pipe(
catchError((error: any) => throwError(error)));
}
persist(item: DmpBlueprintPersist): Observable<DmpBlueprint> {
const url = `${this.apiBase}/persist`;
return this.http
.post<DmpBlueprint>(url, item).pipe(
catchError((error: any) => throwError(error)));
}
delete(id: Guid): Observable<DmpBlueprint> {
const url = `${this.apiBase}/${id}`;
return this.http
.delete<DmpBlueprint>(url).pipe(
catchError((error: any) => throwError(error)));
}
getPaged(dataTableRequest: DataTableRequest<DmpBlueprintCriteria>): Observable<DataTableData<DmpBlueprintListing>> {
return this.http.post<DataTableData<DmpBlueprintListing>>(this.actionUrl + 'getPaged', dataTableRequest, { headers: this.headers });
}
getPagedBlueprint(dataTableRequest: DataTableRequest<DmpBlueprintCriteria>): Observable<DataTableData<DmpBlueprintListing>> {
return this.http.post<DataTableData<DmpBlueprintListing>>(this.actionUrl + 'getPagedBlueprint', dataTableRequest, { headers: this.headers });
}
getSingleBlueprint(id: String): Observable<DmpBlueprintListing> {
return this.http.get<DmpBlueprintListing>(this.actionUrl + 'getSingleBlueprint/' + id, { headers: this.headers });
}
createDmp(dataManagementPlanModel: DmpBlueprint): Observable<DmpBlueprint> {
return this.http.post<DmpBlueprint>(this.actionUrl, dataManagementPlanModel, { headers: this.headers });
}
createBlueprint(dmpBlueprint: DmpBlueprint): Observable<DmpBlueprint> {
return this.http.post<DmpBlueprint>(this.actionUrl + 'blueprint', dmpBlueprint, { headers: this.headers });
}
public downloadXML(id: string): Observable<HttpResponse<Blob>> {
let headerXml: HttpHeaders = this.headers.set('Content-Type', 'application/xml')
return this.httpClient.get(this.actionUrl + 'getXml/' + id, { responseType: 'blob', observe: 'response', headers: headerXml });
}
uploadFile(file: FileList, labelSent: string): Observable<DataTableData<DatasetListingModel>> {
const params = new BaseHttpParams();
params.interceptorContext = {
excludedInterceptors: [InterceptorType.JSONContentType]
};
const formData = new FormData();
formData.append('file', file[0], labelSent);
return this.http.post(this.actionUrl + "upload", formData, { params: params });
}
clone(id: string): Observable<DmpBlueprint> {
return this.http.post<DmpBlueprint>(this.actionUrl + 'clone/' + id, { headers: this.headers });
}
externalAutocomplete(lookUpItem: RequestItem<DmpBlueprintExternalAutocompleteCriteria>): Observable<any> {
return this.httpClient.post(this.actionUrl + 'search/autocomplete', lookUpItem);
}
}

View File

@ -1,81 +0,0 @@
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { environment } from '../../../../environments/environment';
import { DataTableRequest } from '../../model/data-table/data-table-request';
import { DmpProfile } from '../../model/dmp-profile/dmp-profile';
import { BaseHttpService } from '../http/base-http.service';
import { DmpProfileListing } from '../../model/dmp-profile/dmp-profile-listing';
import { RequestItem } from '../../query/request-item';
import { DataTableData } from '../../model/data-table/data-table-data';
import { DmpProfileCriteria } from '../../query/dmp/dmp-profile-criteria';
import { DatasetListingModel } from '../../model/dataset/dataset-listing';
import { BaseHttpParams } from '../../../../common/http/base-http-params';
import { InterceptorType } from '../../../../common/http/interceptors/interceptor-type';
import { DmpProfileExternalAutocompleteCriteria } from '../../query/dmp/dmp-profile-external-autocomplete-criteria';
import { ConfigurationService } from '../configuration/configuration.service';
import { DmpBlueprintCriteria } from '@app/core/query/dmp/dmp-blueprint-criteria';
import { DmpBlueprintListing } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint-listing';
import { DmpBlueprint } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint';
@Injectable()
export class DmpProfileService {
private actionUrl: string;
private headers = new HttpHeaders();
constructor(private http: BaseHttpService, private httpClient: HttpClient, private configurationService: ConfigurationService) {
this.actionUrl = configurationService.server + 'dmpprofile/';
}
getPaged(dataTableRequest: DataTableRequest<DmpProfileCriteria>): Observable<DataTableData<DmpProfileListing>> {
return this.http.post<DataTableData<DmpProfileListing>>(this.actionUrl + 'getPaged', dataTableRequest, { headers: this.headers });
}
getPagedBlueprint(dataTableRequest: DataTableRequest<DmpBlueprintCriteria>): Observable<DataTableData<DmpBlueprintListing>> {
return this.http.post<DataTableData<DmpBlueprintListing>>(this.actionUrl + 'getPagedBlueprint', dataTableRequest, { headers: this.headers });
}
getSingle(id: String): Observable<DmpProfile> {
return this.http.get<DmpProfile>(this.actionUrl + 'getSingle/' + id, { headers: this.headers });
}
getSingleBlueprint(id: String): Observable<DmpBlueprintListing> {
return this.http.get<DmpBlueprintListing>(this.actionUrl + 'getSingleBlueprint/' + id, { headers: this.headers });
}
createDmp(dataManagementPlanModel: DmpProfile): Observable<DmpProfile> {
return this.http.post<DmpProfile>(this.actionUrl, dataManagementPlanModel, { headers: this.headers });
}
createBlueprint(dmpBlueprint: DmpBlueprint): Observable<DmpProfile> {
return this.http.post<DmpProfile>(this.actionUrl + 'blueprint', dmpBlueprint, { headers: this.headers });
}
public downloadXML(id: string): Observable<HttpResponse<Blob>> {
let headerXml: HttpHeaders = this.headers.set('Content-Type', 'application/xml')
return this.httpClient.get(this.actionUrl + 'getXml/' + id, { responseType: 'blob', observe: 'response', headers: headerXml });
}
uploadFile(file: FileList, labelSent: string): Observable<DataTableData<DatasetListingModel>> {
const params = new BaseHttpParams();
params.interceptorContext = {
excludedInterceptors: [InterceptorType.JSONContentType]
};
const formData = new FormData();
formData.append('file', file[0], labelSent);
return this.http.post(this.actionUrl + "upload", formData, { params: params });
}
clone(id: string): Observable<DmpBlueprint> {
return this.http.post<DmpBlueprint>(this.actionUrl + 'clone/' + id, { headers: this.headers });
}
delete(id: string): Observable<any> {
return this.http.delete<any>(this.actionUrl + id, { headers: this.headers });
}
externalAutocomplete(lookUpItem: RequestItem<DmpProfileExternalAutocompleteCriteria>): Observable<any> {
return this.httpClient.post(this.actionUrl + 'search/autocomplete', lookUpItem);
}
}

View File

@ -82,7 +82,7 @@ export class DmpService {
return this.http.delete<DmpModel>(this.actionUrl + 'inactivate/' + id, { headers: this.headers });
}
searchDMPProfiles(dataSetProfileRequest: RequestItem<DatasetProfileCriteria>): Observable<DatasetProfileModel[]> {
searchDmpBlueprints(dataSetProfileRequest: RequestItem<DatasetProfileCriteria>): Observable<DatasetProfileModel[]> {
return this.http.post<DatasetProfileModel[]>(this.actionUrl + 'datasetprofiles/get', dataSetProfileRequest, { headers: this.headers });
}
@ -137,7 +137,7 @@ export class DmpService {
return this.httpClient.get(this.actionUrl + 'rda/' + id, { responseType: 'blob', observe: 'response' });
}
public uploadXml(fileList: FileList, dmpTitle: string, dmpProfiles: any[]): Observable<any> {
public uploadXml(fileList: FileList, dmpTitle: string, dmpBlueprints: any[]): Observable<any> {
const formData: FormData = new FormData();
if (fileList instanceof FileList) {
for (let i = 0; i < fileList.length; i++) {
@ -148,8 +148,8 @@ export class DmpService {
} else {
formData.append('file', fileList, dmpTitle);
}
for (let j = 0; j < dmpProfiles.length; j++) {
formData.append('profiles', dmpProfiles[j].id);
for (let j = 0; j < dmpBlueprints.length; j++) {
formData.append('profiles', dmpBlueprints[j].id);
}
const params = new BaseHttpParams();
params.interceptorContext = {

View File

@ -91,7 +91,7 @@ export class ExternalSourcesService {
}
// TODO: currently not used.
public searchDMPProfiles(like: string): Observable<ExternalSourceItemModel[]> {
public searchDmpBlueprints(like: string): Observable<ExternalSourceItemModel[]> {
return this.http.get<ExternalSourceItemModel[]>(this.actionUrl + 'datasetprofiles/get' + '?query=' + like, { headers: this.headers });
}

View File

@ -35,4 +35,61 @@ export class TimezoneService {
getCurrentTimezone(): string {
return this.currentTimezone;
}
public buildDateTime(params: {
time: string,
date: moment.Moment
}): moment.Moment {
const { time, date } = params;
if (!time || !date) {
return null;
}
const momentTime = moment.duration(time);
const momentDate = moment(date.toString());
const toReturn = moment.utc({
year: momentDate.year(),
month: momentDate.month(),
date: momentDate.date(),
hour: momentTime.hours(),
minute: momentTime.minutes()
})
return toReturn.tz(this.getCurrentTimezone(), true);
}
public splitDateTime(params: {
dateTime: moment.Moment,
}): { date: moment.Moment, time: string } | null {
const { dateTime } = params
if (!dateTime) {
return null;
}
const dateTimeMoment = moment(dateTime.toString()).tz(this.getCurrentTimezone());
const date =
moment.utc({
year: dateTimeMoment.year(),
month: dateTimeMoment.month(),
date: dateTimeMoment.date(),
});
const hours = dateTimeMoment.hour();
const minutes = dateTimeMoment.minute();
const hoursString = hours > 10 ? hours.toString() : `0${hours}`
const minutesString = hours > 10 ? minutes.toString() : `0${minutes}`
return {
date,
time: `${hoursString}:${minutesString}:00`
};
}
}

View File

@ -0,0 +1,45 @@
import { Injectable } from '@angular/core';
import { Guid } from '@common/types/guid';
import { Observable } from 'rxjs';
import { UserSettingPersist, UserSettings } from '@app/core/model/user-settings/user-settings.model';
import { BaseHttpV2Service } from '../http/base-http-v2.service';
import { ConfigurationService } from '../configuration/configuration.service';
@Injectable()
export class UserSettingsHttpService {
constructor(
private installationConfiguration: ConfigurationService,
private http: BaseHttpV2Service) { }
private get apiBase(): string { return `${this.installationConfiguration.server}user-settings`; }
getSingle(key: string): Observable<UserSettings> {
const url = `${this.apiBase}/${key}`;
return this.http.get<UserSettings>(url);
}
persist(item: UserSettingPersist): Observable<UserSettings> {
const url = `${this.apiBase}/persist`;
return this.http.post<UserSettings>(url, item);
}
persistAll(items: UserSettingPersist[]): Observable<UserSettings[]> {
const url = `${this.apiBase}/persist-all-default`;
return this.http.post<UserSettings[]>(url, items);
}
delete(id: Guid): Observable<UserSettings> {
const url = `${this.apiBase}/${id}`;
return this.http
.delete<UserSettings>(url);
}
share(item: UserSettingPersist, targetId: Guid): Observable<UserSettings>{
const url = `${this.apiBase}/share/${targetId}`;
return this.http.post<UserSettings>(url, item);
}
}

View File

@ -0,0 +1,332 @@
import { Injectable } from '@angular/core';
import { UserSettingPersist, UserSettingsInformation, UserSettings as UserSettingsObject } from '@app/core/model/user-settings/user-settings.model';
import { BaseService } from '@common/base/base.service';
import { Guid } from '@common/types/guid';
import * as moment from 'moment';
import { Moment } from 'moment';
import { Observable, Subject, of as observableOf } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { AuthService, LoginStatus } from '../auth/auth.service';
import { ConfigurationService } from '../configuration/configuration.service';
import { UserSettingsHttpService } from './user-settings-http.service';
export enum UserSettingsType {
Setting = 0,
Config = 1
}
@Injectable()
export class UserSettingsService extends BaseService {
static CACHE_SETTINGS_LIFETIME = 7200; // (value is in seconds) 2 hours
public userSettingUpdated: Subject<string>;
private userSettingsMap: Map<String, any> = new Map<String, any>();
private userSettingsLastAccessMap: Map<String, Moment> = new Map<String, Moment>();
constructor(
private userSettingsHttpService: UserSettingsHttpService,
private authService: AuthService,
private installationConfigurationService: ConfigurationService
) {
super();
this.userSettingUpdated = new Subject<string>();
this.authService.getAuthenticationStateObservable().pipe(takeUntil(this._destroyed)).subscribe(authenticationState => {
if (authenticationState.loginStatus === LoginStatus.LoggedOut) {
localStorage.clear();
this.userSettingsMap = new Map<String, any>();
this.userSettingsLastAccessMap = new Map<String, Moment>();
}
});
}
public getUserSettingUpdatedObservable(): Observable<string> {
return this.userSettingUpdated.asObservable();
}
get<T>(userSettingsInformation: UserSettingsInformation<T>): Observable<UserSettings<T>> {
if (this.userSettingsLastAccessMap.has(userSettingsInformation.key)) {
const lastAccess = this.userSettingsLastAccessMap.get(userSettingsInformation.key);
const difference = moment.utc().diff(lastAccess, 'seconds');
if (difference > UserSettingsService.CACHE_SETTINGS_LIFETIME) {
this.clearSetting(userSettingsInformation.key);
}
}
this.userSettingsLastAccessMap.set(userSettingsInformation.key, moment.utc());
if (this.userSettingsMap.has(userSettingsInformation.key)) { return observableOf(this.userSettingsMap.get(userSettingsInformation.key)); }
return this.loadUserSettings<T>(userSettingsInformation);
}
set<T>(setting: UserSetting<T>, isDefault: boolean = false, userSettingsInformation: UserSettingsInformation<T> = null) {
const userSetting = this.buildUserSetting(setting, userSettingsInformation.key, isDefault);
let userSettings: UserSettings<T> = this.buildUserSettings(userSetting, userSettingsInformation.key);
if (this.userSettingsMap.has(userSettingsInformation.key)) {
userSettings = this.userSettingsMap.get(userSettingsInformation.key);
if (userSettings == null) { userSettings = this.buildUserSettings(userSetting, userSettingsInformation.key); }
const itemIndex = userSettings.settings.findIndex(item => item.id === userSetting.id);
if (itemIndex < 0) {
userSettings.settings.push(userSetting);
} else {
userSettings.settings[itemIndex] = userSetting;
}
if (isDefault) {
userSettings.defaultSetting = userSetting;
}
} else {
userSetting.isDefault = isDefault;
if (isDefault) { userSettings.defaultSetting = userSetting; }
}
userSettings.settings.forEach((element) => {
if (!(element.id === userSettings.defaultSetting.id)) {
element.isDefault = false;
}
});
this.persistUserSettings(userSettingsInformation.key, userSettings, userSettingsInformation, true);
this.userSettingsLastAccessMap.set(userSettingsInformation.key, moment.utc());
}
remove<T>(id: Guid, userSettingsInformation: UserSettingsInformation<T> = null) {
this.deleteFromWebServer(id, userSettingsInformation);
}
share(userSettings: UserSetting<any>, targetId: Guid, newName: string): Observable<UserSettingsObject> {
const userSettingsToPersist: UserSettingPersist = {
id: null,
key: userSettings.key,
isDefault: false,
name: newName,
type: userSettings.type,
value: JSON.stringify(userSettings.value),
hash: null
};
return this.userSettingsHttpService.share(userSettingsToPersist, targetId);
}
private deleteFromWebServer<T>(id: Guid, userSettingsInformation: UserSettingsInformation<T>) {
this.userSettingsHttpService.delete(id).pipe(takeUntil(this._destroyed)).subscribe(item => {
const result: UserSettings<T> = (item ? this.toTypedUserSettings<T>(item as UserSettingsObject) : null);
this.persistUserSettings(userSettingsInformation.key, result, userSettingsInformation, false);
this.userSettingUpdated.next(userSettingsInformation.key);
});
}
private buildUserSettings<T>(setting: UserSetting<T>, key: string): UserSettings<T> {
const userSettings: UserSettings<T> = {
key: key,
settings: [setting],
defaultSetting: setting
};
return userSettings;
}
private buildUserSetting<T>(setting: UserSetting<T>, key: string, isDefault: boolean): UserSetting<T> {
const userSettings: UserSetting<T> = {
id: setting.id,
key: key,
name: setting.name,
value: setting.value,
type: UserSettingsType.Setting,
isDefault: isDefault,
hash: setting.hash
};
return userSettings;
}
private loadUserSettings<T>(userSettingsInformation: UserSettingsInformation<T>): Observable<UserSettings<T>> {
const localStorageItem = this.loadFromLocalStorage(userSettingsInformation.key);
if (localStorageItem) {
const jsonLocalStorageItem = JSON.parse(localStorageItem);
if (jsonLocalStorageItem) { return observableOf((jsonLocalStorageItem as UserSettings<T>)); }
}
return this.userSettingsHttpService.getSingle(userSettingsInformation.key).pipe(
// catchError(() => {
// const result: UserSettings<T> = this.defaultValue(userSettingsInformation);
// this.persistUserSettings(userSettingsInformation.key, result, userSettingsInformation, false);
// return observableOf(result);
// }),
map(x => {
const result: UserSettings<T> = (x ? this.toTypedUserSettings<T>(x as UserSettingsObject) : null);
this.persistUserSettings(userSettingsInformation.key, result, userSettingsInformation, false);
return result;
}));
}
private persistUserSettings<T>(key: string, userSettings: UserSettings<T>, userSettingsInformation: UserSettingsInformation<T>, pushChangeToServer: boolean = true) {
this.userSettingsMap.set(key, userSettings);
this.storeToLocalStorage(key, userSettings);
if (pushChangeToServer) {
this.persistSettingsChanges(key, userSettings, userSettingsInformation);
}
}
private persistSettingsChanges<T>(key: string, userSettings: UserSettings<T>, userSettingsInformation: UserSettingsInformation<T>, updateLocalAfterPush: boolean = true) {
const changesToPush: UserSettingPersist[] = [];
const userSettingsPersist = this.prepareSettingsToPushToWebServer(userSettings);
userSettingsPersist.forEach(element => {
changesToPush.push(element);
});
this.pushToWebServer(changesToPush, userSettingsInformation, updateLocalAfterPush);
}
private buildPersistModel(element: any): UserSettingPersist {
const userSettingsToPersist: UserSettingPersist = {
id: element.id,
key: element.key,
isDefault: element.isDefault,
name: element.name,
type: element.type,
value: JSON.stringify(element.value),
hash: element.hash
};
return userSettingsToPersist;
}
private prepareSettingsToPushToWebServer(userSettings: UserSettings<any>): UserSettingPersist[] {
const userSettingsArray: UserSettingPersist[] = [];
if (userSettings && userSettings.settings && userSettings.settings.length !== 0) {
userSettings.settings.forEach(element => {
userSettingsArray.push(this.buildPersistModel(element));
});
}
return userSettingsArray;
}
private toTypedUserSettings<T>(userSettings: UserSettingsObject): UserSettings<T> {
const settingsArray: UserSetting<T>[] = [];
if (userSettings && userSettings.settings && userSettings.settings.length !== 0) {
userSettings.settings.forEach(element => {
const settingElement: UserSetting<T> = {
id: element.id,
key: userSettings.key,
name: element.name,
value: JSON.parse(element.value),
userId: element.userId,
updatedAt: element.updatedAt,
createdAt: element.createdAt,
hash: element.hash,
type: element.type,
isDefault: element.isDefault
};
settingsArray.push(settingElement);
});
}
let defaultSetting: UserSetting<T> = null;
if (userSettings.defaultSetting) {
const defaultSettingValue = userSettings.defaultSetting;
defaultSetting = {
id: defaultSettingValue.id,
key: userSettings.key,
name: defaultSettingValue.name,
value: userSettings.defaultSetting ? JSON.parse(defaultSettingValue.value) : defaultSettingValue.value,
userId: defaultSettingValue.userId,
updatedAt: defaultSettingValue.updatedAt,
createdAt: defaultSettingValue.createdAt,
hash: defaultSettingValue.hash,
type: defaultSettingValue.type,
isDefault: defaultSettingValue.isDefault
};
}
const result: UserSettings<T> = {
key: userSettings.key,
settings: settingsArray,
defaultSetting: defaultSetting
};
return result;
}
private pushToWebServer<T>(userSettingsToPersist: UserSettingPersist[], userSettingsInformation: UserSettingsInformation<T>, updateLocalAfterPush: boolean) {
if (!userSettingsToPersist || userSettingsToPersist.length === 0) { return; }
this.userSettingsHttpService.persistAll(userSettingsToPersist).pipe(takeUntil(this._destroyed)).subscribe(items => {
if (updateLocalAfterPush) {
const result: UserSettings<any>[] = items ? items.map(x => this.toTypedUserSettings<any>(x as UserSettingsObject)) : [];
result.forEach(item => {
if (item.defaultSetting != null) {
this.userSettingsMap.set(item.key, item);
this.storeToLocalStorage(item.key, item);
} else {
this.clearSetting(item.key);
}
this.userSettingUpdated.next(item.key);
});
}
});
}
private clearSetting(key: string) {
this.userSettingsMap.delete(key);
this.userSettingsLastAccessMap.delete(key);
this.deleteFromLocalStorage(key);
}
private loadFromLocalStorage(key: string) {
return localStorage.getItem(this.generateLocalStorageKey(key));
}
private storeToLocalStorage(key: string, value: any) {
return localStorage.setItem(this.generateLocalStorageKey(key), JSON.stringify(value));
}
private deleteFromLocalStorage(key: string) {
return localStorage.removeItem(this.generateLocalStorageKey(key));
}
private generateLocalStorageKey(key: string): string {
return `${this.getUserSettingsVersion()}_${this.getUserId()}_${key}`;
}
// private defaultValue<T>(userSettingsInformation: UserSettingsInformation<T>): UserSettings<T> {
// const defaultSetting: UserSetting<T> = {
// id: null,
// name: null,
// userId: this.getUserId(),
// key: userSettingsInformation.key,
// type: UserSettingsType.Config,
// isDefault: true,
// value: new userSettingsInformation.type()
// };
// const userSettings: UserSettings<T> = {
// defaultSetting: defaultSetting,
// key: userSettingsInformation.key,
// settings: [defaultSetting]
// };
// return userSettings;
// }
private getUserId(): Guid {
return this.authService.userId();
}
private getUserSettingsVersion(): string {
return this.installationConfigurationService.userSettingsVersion;
}
}
export interface UserSettings<T> {
key: string;
settings: UserSetting<T>[];
defaultSetting: UserSetting<T>;
}
export interface UserSetting<T> {
id: Guid;
name: string;
key: string;
type: UserSettingsType;
value: T;
createdAt?: Date;
updatedAt?: Date;
hash?: string;
userId?: Guid;
isDefault?: boolean;
}

View File

@ -4,8 +4,8 @@ import { AppRole } from '../../common/enum/app-role';
import { DatasetProfileComboBoxType } from '../../common/enum/dataset-profile-combo-box-type';
import { DatasetProfileFieldViewStyle } from '../../common/enum/dataset-profile-field-view-style';
import { DatasetStatus } from '../../common/enum/dataset-status';
import { DmpProfileFieldDataType } from '../../common/enum/dmp-profile-field-type';
import { DmpProfileType } from '../../common/enum/dmp-profile-type';
import { DmpBlueprintFieldDataType } from '../../common/enum/dmp-blueprint-field-type';
import { DmpBlueprintType } from '../../common/enum/dmp-blueprint-type';
import { DmpStatus } from '../../common/enum/dmp-status';
import { ValidationType } from '../../common/enum/validation-type';
import { DatasetProfileInternalDmpEntitiesType } from '../../common/enum/dataset-profile-internal-dmp-entities-type';
@ -13,6 +13,7 @@ import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order
import { Role } from '@app/core/common/enum/role';
import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type';
import { ViewStyleType } from '@app/ui/admin/dataset-profile/editor/components/field/view-style-enum';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
@Injectable()
export class EnumUtils {
@ -34,18 +35,18 @@ export class EnumUtils {
}
}
toDmpProfileFieldDataTypeString(type: DmpProfileFieldDataType): string {
toDmpBlueprintFieldDataTypeString(type: DmpBlueprintFieldDataType): string {
switch (type) {
case DmpProfileFieldDataType.Date: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.DATE');
case DmpProfileFieldDataType.Number: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.NUMBER');
case DmpProfileFieldDataType.Text: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.TEXT');
case DmpProfileFieldDataType.ExternalAutocomplete: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.EXTERNAL-AUTOCOMPLETE');
case DmpBlueprintFieldDataType.Date: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.DATE');
case DmpBlueprintFieldDataType.Number: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.NUMBER');
case DmpBlueprintFieldDataType.Text: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.TEXT');
case DmpBlueprintFieldDataType.ExternalAutocomplete: return this.language.instant('TYPES.DMP-PROFILE-FIELD.DATA-TYPE.EXTERNAL-AUTOCOMPLETE');
}
}
toDmpProfileTypeString(type: DmpProfileType): string {
toDmpBlueprintTypeString(type: DmpBlueprintType): string {
switch (type) {
case DmpProfileType.Input: return this.language.instant('TYPES.DMP-PROFILE-FIELD.TYPE.INPUT');
case DmpBlueprintType.Input: return this.language.instant('TYPES.DMP-PROFILE-FIELD.TYPE.INPUT');
}
}
@ -185,4 +186,11 @@ export class EnumUtils {
case RoleOrganizationType.Other: return this.language.instant('USER-PROFILE.ROLE-ORGANIZATION.OTHER');
}
}
toDescriptionTemplateTypeStatusString(status: DescriptionTemplateTypeStatus): string {
switch (status) {
case DescriptionTemplateTypeStatus.Draft: return this.language.instant('TYPES.DESCRIPTION-TEMPLATE-TYPE-STATUS.DRAFT');
case DescriptionTemplateTypeStatus.Finalized: return this.language.instant('TYPES.DESCRIPTION-TEMPLATE-TYPE-STATUS.FINALIZED');
}
}
}

View File

@ -0,0 +1,59 @@
import { Injectable } from '@angular/core';
import { Lookup } from '@common/model/lookup';
import { nameof } from 'ts-simple-nameof';
@Injectable()
export class QueryParamsService {
constructor() { }
serializeLookup(lookup: Lookup): string {
return JSON.stringify(lookup, (key: string, value: any) => {
switch (key) {
// case nameof<Lookup>(x => x.page):
// case nameof<Lookup>(x => x.order):
case nameof<Lookup>(x => x.metadata):
case nameof<Lookup>(x => x.project):
return undefined;
default:
return value == null ? undefined : value;
}
});
}
deSerializeLookup(serializedLookup: string): any {
const json = JSON.parse(serializedLookup);
// delete json[nameof<Lookup>(x => x.page)];
// delete json[nameof<Lookup>(x => x.order)];
delete json[nameof<Lookup>(x => x.metadata)];
delete json[nameof<Lookup>(x => x.project)];
return json;
}
serializeObject(object: Record<string,any>): string | null{
if(!object){
return null
}
return JSON.stringify(object);
}
deserializeObject<T>(object: string):T | null {
if(!object){
return null;
}
return JSON.parse(object);
}
deSerializeAndApplyLookup(serializedLookup: string, targetLookup: Lookup) {
this.applyLookup(targetLookup, this.deSerializeLookup(serializedLookup));
}
private applyLookup(target: Lookup, source: Lookup) {
Object.keys(source).forEach(key => {
target[key] = source[key];
});
}
}

View File

@ -12,7 +12,7 @@ import * as FileSaver from 'file-saver';
import { BaseComponent } from '@common/base/base.component';
import { DatasetProfileEditorModel } from '@app/ui/admin/dataset-profile/editor/dataset-profile-editor-model';
import { DatasetWizardModel } from '@app/core/model/dataset/dataset-wizard';
import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { DatasetProfileService } from '@app/core/services/dataset-profile/dataset-profile.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { UiNotificationService, SnackBarNotificationLevel } from '@app/core/services/notification/ui-notification-service';
@ -48,7 +48,7 @@ import { CheckDeactivateBaseComponent } from '@app/library/deactivate/deactivate
import { DescriptionTemplateTypeService } from '@app/core/services/description-template-type/description-template-type.service';
import { DescriptionTemplateType } from '@app/core/model/description-template-type/description-template-type';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
import { DescriptionTemplateTypeLookup } from '@app/core/query/description-template/description-template-type.lookup';
import { DescriptionTemplateTypeLookup } from '@app/core/query/description-template-type.lookup';
import { nameof } from 'ts-simple-nameof';
const skipDisable: any[] = require('../../../../../assets/resources/skipDisable.json');
@ -76,7 +76,7 @@ export class DatasetProfileEditorComponent extends CheckDeactivateBaseComponent
private datasetProfileId: string;
newVersionId: string;
dataWizardModel: DatasetWizardModel;
breadCrumbs: Observable<BreadcrumbItem[]>;
// breadCrumbs: Observable<BreadcrumbItem[]>;
@ViewChild('stepper') stepper: MatStepper;
viewOnly = false;
nestedCount: number[] = [];
@ -166,11 +166,11 @@ export class DatasetProfileEditorComponent extends CheckDeactivateBaseComponent
},
error => this.onCallbackError(error)
);
this.breadCrumbs = observableOf([{
parentComponentName: 'DatasetProfileListingComponent',
label: this.language.instant('NAV-BAR.TEMPLATE'),
url: '/dataset-profiles/' + this.datasetProfileId
}]);
// this.breadCrumbs = observableOf([{
// parentComponentName: 'DatasetProfileListingComponent',
// label: this.language.instant('NAV-BAR.TEMPLATE'),
// url: '/dataset-profiles/' + this.datasetProfileId
// }]);
} else if (cloneId != null) {
this.isClone = true;
this.datasetProfileService.clone(cloneId)

View File

@ -72,7 +72,7 @@
</button>
<button mat-menu-item (click)="downloadXML(row.id)" *ngIf="row.status === datasetStatusEnum.Finalized">
<mat-icon>download</mat-icon>
{{'DMP-PROFILE-EDITOR.ACTIONS.DOWNLOAD-XML' | translate}}
{{'DMP-BLUEPRINT-EDITOR.ACTIONS.DOWNLOAD-XML' | translate}}
</button>
<button mat-menu-item (click)="deleteTemplate(row.id)">
<mat-icon>delete</mat-icon>

View File

@ -15,7 +15,7 @@ import { DmpService } from '@app/core/services/dmp/dmp.service';
import { MatomoService } from '@app/core/services/matomo/matomo-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { DatasetProfileCriteriaComponent } from '@app/ui/admin/dataset-profile/listing/criteria/dataset-profile.component';
import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { BaseComponent } from '@common/base/base.component';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { TranslateService } from '@ngx-translate/core';
@ -35,7 +35,7 @@ export class DatasetProfileListingComponent extends BaseComponent implements OnI
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild(DatasetProfileCriteriaComponent, { static: true }) criteria: DatasetProfileCriteriaComponent;
breadCrumbs: Observable<BreadcrumbItem[]>;
// breadCrumbs: Observable<BreadcrumbItem[]>;
dataSource: DatasetDataSource | null;
displayedColumns: String[] = ['label', 'description', 'created', 'status', 'actions'];
pageEvent: PageEvent;
@ -83,11 +83,11 @@ export class DatasetProfileListingComponent extends BaseComponent implements OnI
this.criteria.setCriteria(this.getDefaultCriteria());
this.refresh();
this.criteria.setRefreshCallback(() => this.refresh());
this.breadCrumbs = observableOf([{
parentComponentName: null,
label: this.language.instant('NAV-BAR.DATASET-TEMPLATES'),
url: '/dataset-profiles'
}]);
// this.breadCrumbs = observableOf([{
// parentComponentName: null,
// label: this.language.instant('NAV-BAR.DATASET-TEMPLATES'),
// url: '/dataset-profiles'
// }]);
}
});
}

View File

@ -0,0 +1,32 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { HybridListingModule } from '@common/modules/hybrid-listing/hybrid-listing.module';
import { TextFilterModule } from '@common/modules/text-filter/text-filter.module';
import { UserSettingsModule } from '@common/modules/user-settings/user-settings.module';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { DescriptionTemplateTypesRoutingModule } from './description-template-type.routing';
import { DescriptionTemplateTypeEditorComponent } from './editor/description-template-type-editor.component';
import { DescriptionTemplateTypeListingComponent } from './listing/description-template-type-listing.component';
import { DescriptionTemplateTypeListingFiltersComponent } from './listing/filters/description-template-type-listing-filters.component';
import { FormattingModule } from '@app/core/formatting.module';
import { CommonFormattingModule } from '@common/formatting/common-formatting.module';
@NgModule({
declarations: [
DescriptionTemplateTypeListingComponent,
DescriptionTemplateTypeEditorComponent,
DescriptionTemplateTypeListingFiltersComponent
],
imports: [
CommonModule,
CommonUiModule,
CommonFormsModule,
CommonFormattingModule,
DescriptionTemplateTypesRoutingModule,
HybridListingModule,
TextFilterModule,
UserSettingsModule
]
})
export class DescriptionTemplateTypesModule { }

View File

@ -0,0 +1,57 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AuthGuard } from "@app/core/auth-guard.service";
import { AppPermission } from "@app/core/common/enum/permission.enum";
import { BreadcrumbService } from "@app/ui/misc/breadcrumb/breadcrumb.service";
import { PendingChangesGuard } from "@common/forms/pending-form-changes/pending-form-changes-guard.service";
import { DescriptionTemplateTypeEditorComponent } from './editor/description-template-type-editor.component';
import { DescriptionTemplateTypeEditorResolver } from "./editor/description-template-type-editor.resolver";
import { DescriptionTemplateTypeListingComponent } from "./listing/description-template-type-listing.component";
const routes: Routes = [
{
path: '',
component: DescriptionTemplateTypeListingComponent,
canActivate: [AuthGuard]
},
{
path: 'new',
canActivate: [AuthGuard],
data: {
authContext: {
permissions: [AppPermission.EditDescriptionTemplateType]
},
...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.NEW-DESCRIPTION-TEMPLATE-TYPE'
})
},
component: DescriptionTemplateTypeEditorComponent,
canDeactivate: [PendingChangesGuard],
},
{
path: ':id',
canActivate: [AuthGuard],
component: DescriptionTemplateTypeEditorComponent,
canDeactivate: [PendingChangesGuard],
resolve: {
'entity': DescriptionTemplateTypeEditorResolver
},
data: {
...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DESCRIPTION-TEMPLATE-TYPE'
}),
authContext: {
permissions: [AppPermission.EditDescriptionTemplateType]
}
}
},
{ path: '**', loadChildren: () => import('@common/modules/page-not-found/page-not-found.module').then(m => m.PageNotFoundModule) },
]
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [DescriptionTemplateTypeEditorResolver]
})
export class DescriptionTemplateTypesRoutingModule { }

View File

@ -1,21 +0,0 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DescriptionTypesRoutingModule } from './description-types.routing';
import { DescriptionTypesComponent } from './listing/description-types.component';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { DescriptionTypeEditorComponent } from './editor/description-type-editor.component';
import { CommonFormsModule } from '@common/forms/common-forms.module';
@NgModule({
declarations: [
DescriptionTypesComponent,
DescriptionTypeEditorComponent
],
imports: [
CommonModule,
CommonUiModule,
CommonFormsModule,
DescriptionTypesRoutingModule
]
})
export class DescriptionTypesModule { }

View File

@ -1,45 +0,0 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { AdminAuthGuard } from "@app/core/admin-auth-guard.service";
import { DescriptionTypeEditorComponent } from './editor/description-type-editor.component';
import { DescriptionTypesComponent } from "./listing/description-types.component";
import { AuthGuard } from "@app/core/auth-guard.service";
import { AppPermission } from "@app/core/common/enum/permission.enum";
const routes: Routes = [
{
path: '',
component: DescriptionTypesComponent,
canActivate: [AuthGuard],
data: {
authContext: {
permissions: [AppPermission.ViewDescriptionTemplateTypePage]
}
},
},
{
path: 'new',
component: DescriptionTypeEditorComponent,
canActivate: [AuthGuard],
data: {
title: 'GENERAL.TITLES.DESCRIPTION-TYPE-NEW',
authContext: {
permissions: [AppPermission.EditDescriptionTemplateType]
}
}
},
{
path: ':id',
component: DescriptionTypeEditorComponent,
canActivate: [AuthGuard],
data: {
title: 'GENERAL.TITLES.DESCRIPTION-TYPE-EDIT'
}
}
]
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DescriptionTypesRoutingModule { }

View File

@ -0,0 +1,99 @@
<div class="row description-template-type-editor">
<div class="col-md-8 offset-md-2 colums-gapped">
<div class="row justify-content-between align-items-center">
<div class="col">
<!-- <h3 *ngIf="isNew">{{'DESCRIPTION-TEMPLATE-TYPE-EDITOR.NEW' | translate}}</h3> -->
<app-navigation-breadcrumb />
</div>
<div class="col-auto">
<button mat-button class="action-btn" (click)="cancel()" type="button">{{'DESCRIPTION-TEMPLATE-TYPE-EDITOR.ACTIONS.CANCEL' | translate}}</button>
</div>
<div class="col-auto" *ngIf="canDelete">
<button mat-button (click)="delete()" class="action-btn" type="button">
<mat-icon>delete</mat-icon>
{{'DESCRIPTION-TEMPLATE-TYPE-EDITOR.ACTIONS.DELETE' | translate}}
</button>
</div>
<div class="col-auto" *ngIf="canFinalize">
<button mat-button class="action-btn" (click)="finalize(); formSubmit()">
<mat-icon>save</mat-icon>
{{'DESCRIPTION-TEMPLATE-TYPE-EDITOR.ACTIONS.FINALIZE' | translate}}
</button>
</div>
<div class="col-auto" *ngIf="canSave">
<button mat-button class="action-btn" (click)="save(); formSubmit()">
<mat-icon>save</mat-icon>
{{'DESCRIPTION-TEMPLATE-TYPE-EDITOR.ACTIONS.SAVE' | translate}}
</button>
</div>
</div>
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title *ngIf="isNew">{{'DESCRIPTION-TEMPLATE-TYPE-EDITOR.NEW' | translate}}</mat-card-title>
<!-- <mat-card-title *ngIf="!isNew">{{formGroup.get('name').value}}</mat-card-title> -->
</mat-card-header>
<mat-card-content>
<form (ngSubmit)="formSubmit()" [formGroup]="formGroup" *ngIf="formGroup">
<div class="info-grid">
<div class="info-grid-label">
<span>
{{'DESCRIPTION-TEMPLATE-TYPE-EDITOR.FIELDS.NAME' | translate}}
</span>
</div>
<div class="info-grid-value">
<mat-form-field class="col-lg-6">
<input matInput placeholder="{{'DESCRIPTION-TEMPLATE-TYPE-EDITOR.FIELDS.NAME' | translate}}" type="text" name="name" [formControl]="formGroup.get('name')" required>
<mat-error *ngIf="formGroup.get('name').hasError('backendError')">
{{formGroup.get('name').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('name').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
</div>
</form>
</mat-card-content>
</mat-card>
</div>
</div>
<!-- <div class="main-content">
<div class="container-fluid description-type-editor">
<div class="row align-items-center mb-4" *ngIf="formGroup">
<div class="col-auto">
<h3>{{'DESCRIPTION-TYPE-EDITOR.NEW' | translate}}</h3>
</div>
</div>
<form *ngIf="formGroup" (ngSubmit)="formSubmit()" [formGroup]="formGroup">
<mat-card style="padding: 2em;">
<mat-card-content>
<div class="row" style="gap:1em">
<mat-form-field class="col-lg-6">
<input matInput placeholder="{{'DESCRIPTION-TYPE-EDITOR.FIELDS.LABEL' | translate}}" type="text" name="name" formControlName="name"
required>
<mat-error *ngIf="formGroup.get('name').hasError('backendError')">
{{formGroup.get('name').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('name').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="row mt-4">
<div class="col-auto">
<button mat-button class="action-btn" (click)="cancel()" type="button">{{'DESCRIPTION-TYPE-EDITOR.ACTIONS.CANCEL' | translate}}</button>
</div>
<div class="col"></div>
<div class="col-auto">
<button mat-button *ngIf="formGroup.get('status').value!=1" class="action-btn" (click)="finalize()"
[disabled]="!this.isFormValid()" type="button">{{'DESCRIPTION-TYPE-EDITOR.ACTIONS.FINALIZE' | translate }}</button>
<button mat-button class="action-btn ml-3" type="submit" [disabled]="!this.isFormValid() || viewOnly">
{{'DESCRIPTION-TYPE-EDITOR.ACTIONS.SAVE' | translate}}
</button>
</div>
</div>
</mat-card-content>
</mat-card>
</form>
</div>
</div> -->

View File

@ -1,7 +1,9 @@
.description-type-editor {
margin-top: 1.3rem;
margin-left: 1em;
margin-right: 3em;
.description-template-type-editor {
padding-top: 1em;
.editor-actions {
margin-top: 30px;
}
}
.action-btn {

View File

@ -0,0 +1,173 @@
import { DatePipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionTemplateType, DescriptionTemplateTypePersist } from '@app/core/model/description-template-type/description-template-type';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DescriptionTemplateTypeService } from '@app/core/services/description-template-type/description-template-type.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { BaseEditor } from '@common/base/base-editor';
import { FormService } from '@common/forms/form-service';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { FilterService } from '@common/modules/text-filter/filter-service';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { map, takeUntil } from 'rxjs/operators';
import { DescriptionTemplateTypeEditorModel } from './description-template-type-editor.model';
import { DescriptionTemplateTypeEditorResolver } from './description-template-type-editor.resolver';
import { DescriptionTemplateTypeEditorService } from './description-template-type-editor.service';
@Component({
templateUrl: './description-template-type-editor.component.html',
styleUrls: ['./description-template-type-editor.component.scss'],
providers: [DescriptionTemplateTypeEditorService]
})
export class DescriptionTemplateTypeEditorComponent extends BaseEditor<DescriptionTemplateTypeEditorModel, DescriptionTemplateType> implements OnInit {
isNew = true;
isDeleted = false;
formGroup: UntypedFormGroup = null;
showInactiveDetails = false;
protected get canDelete(): boolean {
return !this.isDeleted && !this.isNew && this.hasPermission(this.authService.permissionEnum.DeleteDescriptionTemplateType);
}
protected get canSave(): boolean {
return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditDescriptionTemplateType);
}
protected get canFinalize(): boolean {
return !this.isDeleted && this.hasPermission(this.authService.permissionEnum.EditDescriptionTemplateType);
}
private hasPermission(permission: AppPermission): boolean {
return this.authService.hasPermission(permission) || this.editorModel?.permissions?.includes(permission);
}
constructor(
// BaseFormEditor injected dependencies
protected dialog: MatDialog,
protected language: TranslateService,
protected formService: FormService,
protected router: Router,
protected uiNotificationService: UiNotificationService,
protected httpErrorHandlingService: HttpErrorHandlingService,
protected filterService: FilterService,
protected datePipe: DatePipe,
protected route: ActivatedRoute,
protected queryParamsService: QueryParamsService,
// Rest dependencies. Inject any other needed deps here:
public authService: AuthService,
public enumUtils: EnumUtils,
private descriptionTemplateTypeService: DescriptionTemplateTypeService,
private logger: LoggingService,
private descriptionTemplateTypeEditorService: DescriptionTemplateTypeEditorService
) {
super(dialog, language, formService, router, uiNotificationService, httpErrorHandlingService, filterService, datePipe, route, queryParamsService);
}
ngOnInit(): void {
super.ngOnInit();
}
getItem(itemId: Guid, successFunction: (item: DescriptionTemplateType) => void) {
this.descriptionTemplateTypeService.getSingle(itemId, DescriptionTemplateTypeEditorResolver.lookupFields())
.pipe(map(data => data as DescriptionTemplateType), takeUntil(this._destroyed))
.subscribe(
data => successFunction(data),
error => this.onCallbackError(error)
);
}
prepareForm(data: DescriptionTemplateType) {
try {
this.editorModel = data ? new DescriptionTemplateTypeEditorModel().fromModel(data) : new DescriptionTemplateTypeEditorModel();
this.isDeleted = data ? data.isActive === IsActive.Inactive : false;
this.buildForm();
} catch (error) {
this.logger.error('Could not parse descriptionTemplateType item: ' + data + error);
this.uiNotificationService.snackBarNotification(this.language.instant('COMMONS.ERRORS.DEFAULT'), SnackBarNotificationLevel.Error);
}
}
buildForm() {
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !this.authService.hasPermission(AppPermission.EditDescriptionTemplateType));
this.descriptionTemplateTypeEditorService.setValidationErrorModel(this.editorModel.validationErrorModel);
}
refreshData(): void {
this.getItem(this.editorModel.id, (data: DescriptionTemplateType) => this.prepareForm(data));
}
refreshOnNavigateToData(id?: Guid): void {
this.formGroup.markAsPristine();
let route = [];
if (id === null) {
route.push('../..');
} else if (this.isNew) {
route.push('../' + id);
} else {
route.push('..');
}
this.router.navigate(route, { queryParams: { 'lookup': this.queryParamsService.serializeLookup(this.lookupParams), 'lv': ++this.lv }, replaceUrl: true, relativeTo: this.route });
}
persistEntity(onSuccess?: (response) => void): void {
const formData = this.formService.getValue(this.formGroup.value) as DescriptionTemplateTypePersist;
this.descriptionTemplateTypeService.persist(formData)
.pipe(takeUntil(this._destroyed)).subscribe(
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
error => this.onCallbackError(error)
);
}
formSubmit(): void {
this.formService.touchAllFormFields(this.formGroup);
if (!this.isFormValid()) {
return;
}
this.persistEntity();
}
public delete() {
const value = this.formGroup.value;
if (value.id) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
maxWidth: '300px',
data: {
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.descriptionTemplateTypeService.delete(value.id).pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
);
}
});
}
}
clearErrorModel() {
this.editorModel.validationErrorModel.clear();
this.formService.validateAllFormFields(this.formGroup);
}
}

View File

@ -0,0 +1,54 @@
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
import { DescriptionTemplateType, DescriptionTemplateTypePersist } from '@app/core/model/description-template-type/description-template-type';
import { BaseEditorModel } from '@common/base/base-form-editor-model';
import { BackendErrorValidator } from '@common/forms/validation/custom-validator';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
import { Validation, ValidationContext } from '@common/forms/validation/validation-context';
export class DescriptionTemplateTypeEditorModel extends BaseEditorModel implements DescriptionTemplateTypePersist {
name: string;
status: DescriptionTemplateTypeStatus = DescriptionTemplateTypeStatus.Draft;
permissions: string[];
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
constructor() { super(); }
public fromModel(item: DescriptionTemplateType): DescriptionTemplateTypeEditorModel {
if (item) {
this.id = item.id;
this.name = item.name;
this.status = item.status;
this.isActive = item.isActive;
this.hash = item.hash;
if (item.createdAt) { this.createdAt = item.createdAt; }
if (item.updatedAt) { this.updatedAt = item.updatedAt; }
}
return this;
}
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
if (context == null) { context = this.createValidationContext(); }
return this.formBuilder.group({
id: [{ value: this.id, disabled: disabled }, context.getValidation('id').validators],
name: [{ value: this.name, disabled: disabled }, context.getValidation('name').validators],
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators],
hash: [{ value: this.hash, disabled: disabled }, context.getValidation('hash').validators]
});
}
createValidationContext(): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
const baseValidationArray: Validation[] = new Array<Validation>();
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
baseValidationArray.push({ key: 'name', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'RenameourceId')] });
baseValidationArray.push({ key: 'status', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'status')] });
baseValidationArray.push({ key: 'hash', validators: [] });
baseContext.validation = baseValidationArray;
return baseContext;
}
}

View File

@ -0,0 +1,38 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionTemplateType } from '@app/core/model/description-template-type/description-template-type';
import { DescriptionTemplateTypeService } from '@app/core/services/description-template-type/description-template-type.service';
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
import { BaseEditorResolver } from '@common/base/base-editor.resolver';
import { Guid } from '@common/types/guid';
import { takeUntil, tap } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
@Injectable()
export class DescriptionTemplateTypeEditorResolver extends BaseEditorResolver {
constructor(private descriptionTemplateTypeService: DescriptionTemplateTypeService, private breadcrumbService: BreadcrumbService) {
super();
}
public static lookupFields(): string[] {
return [
...BaseEditorResolver.lookupFields(),
nameof<DescriptionTemplateType>(x => x.id),
nameof<DescriptionTemplateType>(x => x.name),
nameof<DescriptionTemplateType>(x => x.status),
nameof<DescriptionTemplateType>(x => x.createdAt),
nameof<DescriptionTemplateType>(x => x.hash),
nameof<DescriptionTemplateType>(x => x.isActive)
]
}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const fields = [
...DescriptionTemplateTypeEditorResolver.lookupFields()
];
return this.descriptionTemplateTypeService.getSingle(Guid.parse(route.paramMap.get('id')), fields).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.name)), takeUntil(this._destroyed));
}
}

View File

@ -0,0 +1,15 @@
import { Injectable } from "@angular/core";
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
@Injectable()
export class DescriptionTemplateTypeEditorService {
private validationErrorModel: ValidationErrorModel;
public setValidationErrorModel(validationErrorModel: ValidationErrorModel): void {
this.validationErrorModel = validationErrorModel;
}
public getValidationErrorModel(): ValidationErrorModel {
return this.validationErrorModel;
}
}

View File

@ -1,38 +0,0 @@
<div class="main-content">
<div class="container-fluid description-type-editor">
<div class="row align-items-center mb-4" *ngIf="formGroup">
<div class="col-auto">
<h3>{{'DESCRIPTION-TYPE-EDITOR.NEW' | translate}}</h3>
</div>
</div>
<form *ngIf="formGroup" (ngSubmit)="formSubmit()" [formGroup]="formGroup">
<mat-card style="padding: 2em;">
<mat-card-content>
<div class="row" style="gap:1em">
<mat-form-field class="col-lg-6">
<input matInput placeholder="{{'DESCRIPTION-TYPE-EDITOR.FIELDS.LABEL' | translate}}" type="text" name="name" formControlName="name"
required>
<mat-error *ngIf="formGroup.get('name').hasError('backendError')">
{{formGroup.get('name').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('name').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field>
</div>
<div class="row mt-4">
<div class="col-auto">
<button mat-button class="action-btn" (click)="cancel()" type="button">{{'DESCRIPTION-TYPE-EDITOR.ACTIONS.CANCEL' | translate}}</button>
</div>
<div class="col"></div>
<div class="col-auto">
<button mat-button *ngIf="formGroup.get('status').value!=1" class="action-btn" (click)="finalize()"
[disabled]="!this.isFormValid()" type="button">{{'DESCRIPTION-TYPE-EDITOR.ACTIONS.FINALIZE' | translate }}</button>
<button mat-button class="action-btn ml-3" type="submit" [disabled]="!this.isFormValid() || viewOnly">
{{'DESCRIPTION-TYPE-EDITOR.ACTIONS.SAVE' | translate}}
</button>
</div>
</div>
</mat-card-content>
</mat-card>
</form>
</div>
</div>

View File

@ -1,161 +0,0 @@
import { AfterViewInit, Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
import { DescriptionTemplateType } from '@app/core/model/description-template-type/description-template-type';
import { DescriptionTemplateTypeService } from '@app/core/services/description-template-type/description-template-type.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { BaseComponent } from '@common/base/base.component';
import { FormService } from '@common/forms/form-service';
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 { TranslateService } from '@ngx-translate/core';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-description-type-editor',
templateUrl: './description-type-editor.component.html',
styleUrls: ['./description-type-editor.component.scss']
})
export class DescriptionTypeEditorComponent extends BaseComponent implements OnInit {
formGroup: UntypedFormGroup = null;
descriptionTypeModel: DescriptionTypeEditorModel;
isNew = true;
viewOnly = false;
descriptionTemplateTypeId: string;
constructor(
private descriptionTemplateTypeService: DescriptionTemplateTypeService,
private formService: FormService,
private uiNotificationService: UiNotificationService,
private language: TranslateService,
private route: ActivatedRoute,
private router: Router
) {
super();
}
ngOnInit(): void {
this.route.paramMap.pipe(takeUntil(this._destroyed)).subscribe((paramMap: ParamMap) => {
this.descriptionTemplateTypeId = paramMap.get('id');
if (this.descriptionTemplateTypeId != null) {
this.isNew = false;
this.descriptionTemplateTypeService.get(this.descriptionTemplateTypeId)
.pipe(takeUntil(this._destroyed)).subscribe(
type => {
this.descriptionTypeModel = new DescriptionTypeEditorModel().fromModel(type.items[0]);
if (this.descriptionTypeModel.status === DescriptionTemplateTypeStatus.Finalized) {
this.formGroup = this.descriptionTypeModel.buildForm(null, true);
this.viewOnly = true;
}
else {
this.formGroup = this.descriptionTypeModel.buildForm();
}
},
error => this.uiNotificationService.snackBarNotification(error.message, SnackBarNotificationLevel.Error)
);
}
else {
this.descriptionTypeModel = new DescriptionTypeEditorModel();
this.descriptionTypeModel.status = DescriptionTemplateTypeStatus.Draft;
this.formGroup = this.descriptionTypeModel.buildForm();
}
});
}
formSubmit(): void {
this.formService.touchAllFormFields(this.formGroup);
if (!this.isFormValid()) { return; }
this.onSubmit();
}
public isFormValid() {
return this.formGroup.valid;
}
finalize() {
this.formGroup.get('status').setValue(DescriptionTemplateTypeStatus.Finalized);
this.onSubmit();
}
onSubmit(): void {
if (this.isNew) {
this.descriptionTemplateTypeService.persist(this.formGroup.value)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(true),
error => this.onCallbackError(error)
);
}
else {
this.descriptionTemplateTypeService.update(this.formGroup.value)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(false),
error => this.onCallbackError(error)
);
}
}
onCallbackSuccess(creation: boolean): void {
if (creation) {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION'), SnackBarNotificationLevel.Success);
}
else {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
}
this.router.navigate(['/description-types']);
}
onCallbackError(errorResponse: any) {
this.setErrorModel(errorResponse.error);
this.formService.validateAllFormFields(this.formGroup);
this.uiNotificationService.snackBarNotification(errorResponse.error.message, SnackBarNotificationLevel.Error);
}
public setErrorModel(validationErrorModel: ValidationErrorModel) {
Object.keys(validationErrorModel).forEach(item => {
(<any>this.descriptionTypeModel.validationErrorModel)[item] = (<any>validationErrorModel)[item];
});
}
public cancel(): void {
this.router.navigate(['/description-types']);
}
}
export class DescriptionTypeEditorModel {
public id: string;
public name: string;
public status: DescriptionTemplateTypeStatus;
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
fromModel(item: DescriptionTemplateType): DescriptionTypeEditorModel {
this.id = item.id;
this.name = item.name;
this.status = item.status;
return this;
}
buildForm(context: ValidationContext = null, disabled: boolean = false): UntypedFormGroup {
if (context == null) { context = this.createValidationContext(); }
const formGroup = new UntypedFormBuilder().group({
id: [this.id],
name: [{ value: this.name, disabled: disabled }, context.getValidation('name').validators],
status: [{ value: this.status, disabled: disabled }, context.getValidation('status').validators]
});
return formGroup;
}
createValidationContext(): ValidationContext {
const baseContext: ValidationContext = new ValidationContext();
baseContext.validation.push({ key: 'name', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'name')] });
baseContext.validation.push({ key: 'status', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'status')] });
return baseContext;
}
}

View File

@ -0,0 +1,108 @@
<div class="row description-template-type-listing-listing">
<div class="col-md-8 offset-md-2">
<div class="row mb-4 mt-3">
<div class="col">
<h4>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.TITLE' | translate}}</h4>
<app-navigation-breadcrumb />
</div>
<div class="col-auto">
<button mat-raised-button class="create-btn"
*ngIf="authService.hasPermission(authService.permissionEnum.EditDescriptionTemplateType)"
[routerLink]="['/description-template-type/new']">
<mat-icon>add</mat-icon>
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.CREATE-TYPE' | translate}}
</button>
</div>
</div>
<app-hybrid-listing [rows]="gridRows" [columns]="gridColumns" [visibleColumns]="visibleColumns"
[count]="totalElements" [offset]="currentPageNumber" [limit]="lookup.page.size"
[defaultSort]="lookup.order?.items" [externalSorting]="true" (rowActivated)="onRowActivated($event)"
(pageLoad)="alterPage($event)" (columnSort)="onColumnSort($event)"
(columnsChanged)="onColumnsChanged($event)" [listItemTemplate]="listItemTemplate">
<app-description-template-type-listing-filters hybrid-listing-filters [(filter)]="lookup"
(filterChange)="filterChanged($event)" />
<app-user-settings-picker [key]="userSettingsKey" [userPreference]="lookup"
(onSettingSelected)="changeSetting($event)" [autoSelectUserSettings]="autoSelectUserSettings"
user-preference-settings />
<button mat-icon-button download-listing-report>
<mat-icon>download</mat-icon>
</button>
</app-hybrid-listing>
</div>
</div>
<ng-template #listItemTemplate let-item="item" let-isColumnSelected="isColumnSelected">
<div class="d-flex align-items-center p-3 gap-1-rem">
<div class="row">
<ng-container *ngIf="isColumnSelected('name')">
<a class="buttonLinkClass" [routerLink]="'./' + item?.id" class="col-12"
(click)="$event.stopPropagation()">{{item?.name | nullifyValue}}</a>
<br />
</ng-container>
<ng-container *ngIf="isColumnSelected('status')">
<div class="col-auto">
<div class="status-chip"
[ngClass]="{'status-chip-finalized': item.status === descriptionTemplateTypeStatuses.Finalized, 'status-chip-draft' : item.status === descriptionTemplateTypeStatuses.Draft}">
{{enumUtils.toDescriptionTemplateTypeStatusString(item.status) | nullifyValue}}
</div>
</div>
</ng-container>
<ng-container *ngIf="isColumnSelected('createdAt')">
<span class="col-12">
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FIELDS.CREATED-AT' | translate}}:
<small>
{{item?.createdAt | dateTimeFormatter : 'short' | nullifyValue}}
</small>
</span>
<br>
</ng-container>
<ng-container *ngIf="isColumnSelected('updatedAt')">
<span class="col-12">
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FIELDS.UPDATED-AT' | translate}}:
<small>
{{item?.updatedAt | dateTimeFormatter : 'short' | nullifyValue}}
</small>
</span>
</ng-container>
</div>
</div>
</ng-template>
<ng-template #descriptionTemplateTypeStatus let-row="row" let-item>
<div class="row">
<div class="col-auto status-chip"
[ngClass]="{'status-chip-finalized': row.status === descriptionTemplateTypeStatuses.Finalized, 'status-chip-draft' : row.status === descriptionTemplateTypeStatuses.Draft}">
{{enumUtils.toDescriptionTemplateTypeStatusString(row.status) | nullifyValue}}
</div>
</div>
</ng-template>
<ng-template #actions let-row="row" let-item>
<div class="row" (click)="$event.stopPropagation()">
<div class="col-auto">
<button mat-icon-button [matMenuTriggerFor]="actionsMenu">
<mat-icon>more_horiz</mat-icon>
</button>
<mat-menu #actionsMenu="matMenu">
<button mat-menu-item [routerLink]="['/description-template-type/' + row.id]">
<mat-icon>edit</mat-icon>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.ACTIONS.EDIT' | translate}}
</button>
<button mat-menu-item (click)="deleteType(row.id)">
<mat-icon>delete</mat-icon>
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.ACTIONS.DELETE' | translate}}
</button>
<!--<button *ngIf="row.status==1" mat-menu-item (click)="makeItPublic(row.id)"><mat-icon>people_outline</mat-icon>{{'DATASET-LISTING.ACTIONS.MAKE-IT-PUBLIC' | translate}}</button> -->
</mat-menu>
</div>
</div>
</ng-template>

View File

@ -1,8 +1,4 @@
.mat-table {
margin-top: 47px;
border-radius: 4px;
}
.description-types-listing {
.description-template-type-listing {
margin-top: 1.3rem;
margin-left: 1rem;
margin-right: 2rem;
@ -28,13 +24,6 @@
z-index: 5;
}
}
// PAGINATOR
:host ::ng-deep .mat-paginator-container {
flex-direction: row-reverse !important;
justify-content: space-between !important;
background-color: #f6f6f6;
align-items: center;
}
.create-btn {
border-radius: 30px;
background-color: var(--secondary-color);
@ -68,4 +57,4 @@
.status-chip-draft{
color: #00c4ff;
background: #d3f5ff 0% 0% no-repeat padding-box;
}
}

View File

@ -0,0 +1,177 @@
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { DescriptionTemplateType } from '@app/core/model/description-template-type/description-template-type';
import { DescriptionTemplateTypeLookup } from '@app/core/query/description-template-type.lookup';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DescriptionTemplateTypeService } from '@app/core/services/description-template-type/description-template-type.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { BaseListingComponent } from '@common/base/base-listing-component';
import { PipeService } from '@common/formatting/pipe.service';
import { DataTableDateTimeFormatPipe } from '@common/formatting/pipes/date-time-format.pipe';
import { QueryResult } from '@common/model/query-result';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { ColumnDefinition, ColumnsChangedEvent, HybridListingComponent, PageLoadEvent } from '@common/modules/hybrid-listing/hybrid-listing.component';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
@Component({
templateUrl: './description-template-type-listing.component.html',
styleUrls: ['./description-template-type-listing.component.scss']
})
export class DescriptionTemplateTypeListingComponent extends BaseListingComponent<DescriptionTemplateType, DescriptionTemplateTypeLookup> implements OnInit {
publish = false;
userSettingsKey = { key: 'DescriptionTemplateTypeListingUserSettings' };
propertiesAvailableForOrder: ColumnDefinition[];
descriptionTemplateTypeStatuses = DescriptionTemplateTypeStatus;
@ViewChild('descriptionTemplateTypeStatus', { static: true }) descriptionTemplateTypeStatus?: TemplateRef<any>;
@ViewChild('actions', { static: true }) actions?: TemplateRef<any>;
@ViewChild(HybridListingComponent, { static: true }) hybridListingComponent: HybridListingComponent;
private readonly lookupFields: string[] = [
nameof<DescriptionTemplateType>(x => x.id),
nameof<DescriptionTemplateType>(x => x.name),
nameof<DescriptionTemplateType>(x => x.status),
nameof<DescriptionTemplateType>(x => x.updatedAt),
nameof<DescriptionTemplateType>(x => x.createdAt),
nameof<DescriptionTemplateType>(x => x.hash),
nameof<DescriptionTemplateType>(x => x.isActive)
];
rowIdentity = x => x.id;
constructor(
protected router: Router,
protected route: ActivatedRoute,
protected uiNotificationService: UiNotificationService,
protected httpErrorHandlingService: HttpErrorHandlingService,
protected queryParamsService: QueryParamsService,
private descriptionTemplateTypeService: DescriptionTemplateTypeService,
public authService: AuthService,
private pipeService: PipeService,
public enumUtils: EnumUtils,
private language: TranslateService,
private dialog: MatDialog
) {
super(router, route, uiNotificationService, httpErrorHandlingService, queryParamsService);
// Lookup setup
// Default lookup values are defined in the user settings class.
this.lookup = this.initializeLookup();
}
ngOnInit() {
super.ngOnInit();
}
protected initializeLookup(): DescriptionTemplateTypeLookup {
const lookup = new DescriptionTemplateTypeLookup();
lookup.metadata = { countAll: true };
lookup.page = { offset: 0, size: this.ITEMS_PER_PAGE };
lookup.isActive = [IsActive.Active];
lookup.order = { items: [this.toDescSortField(nameof<DescriptionTemplateType>(x => x.createdAt))] };
this.updateOrderUiFields(lookup.order);
lookup.project = {
fields: this.lookupFields
};
return lookup;
}
protected setupColumns() {
this.gridColumns.push(...[{
prop: nameof<DescriptionTemplateType>(x => x.name),
sortable: true,
languageName: 'DESCRIPTION-TEMPLATE-TYPE-LISTING.FIELDS.NAME'
}, {
prop: nameof<DescriptionTemplateType>(x => x.status),
sortable: true,
languageName: 'DESCRIPTION-TEMPLATE-TYPE-LISTING.FIELDS.STATUS',
cellTemplate: this.descriptionTemplateTypeStatus
},
// {
// prop: nameof<DescriptionTemplateType>(x => x.isExclude),
// languageName: 'DESCRIPTION-TEMPLATE-TYPE-LISTING.FIELDS.IS-EXCLUDE',
// pipe: this.pipeService.getPipe<IsExcludeTypePipe>(IsExcludeTypePipe)
// },
{
prop: nameof<DescriptionTemplateType>(x => x.createdAt),
sortable: true,
languageName: 'DESCRIPTION-TEMPLATE-TYPE-LISTING.FIELDS.CREATED-AT',
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
},
{
prop: nameof<DescriptionTemplateType>(x => x.updatedAt),
sortable: true,
languageName: 'DESCRIPTION-TEMPLATE-TYPE-LISTING.FIELDS.UPDATED-AT',
pipe: this.pipeService.getPipe<DataTableDateTimeFormatPipe>(DataTableDateTimeFormatPipe).withFormat('short')
},
{
alwaysShown: true,
cellTemplate: this.actions,
maxWidth: 120
}
]);
this.propertiesAvailableForOrder = this.gridColumns.filter(x => x.sortable);
}
//
// Listing Component functions
//
onColumnsChanged(event: ColumnsChangedEvent) {
super.onColumnsChanged(event);
this.onColumnsChangedInternal(event.properties.map(x => x.toString()));
}
private onColumnsChangedInternal(columns: string[]) {
// Here are defined the projection fields that always requested from the api.
const fields = new Set(this.lookupFields);
this.gridColumns.map(x => x.prop)
.filter(x => !columns?.includes(x as string))
.forEach(item => {
fields.delete(item as string)
});
this.lookup.project = { fields: [...fields] };
this.onPageLoad({ offset: 0 } as PageLoadEvent);
}
protected loadListing(): Observable<QueryResult<DescriptionTemplateType>> {
return this.descriptionTemplateTypeService.query(this.lookup);
}
public deleteType(id: Guid) {
if (id) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
maxWidth: '300px',
data: {
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.descriptionTemplateTypeService.delete(id).pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
);
}
});
}
}
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DELETE'), SnackBarNotificationLevel.Success);
this.ngOnInit();
}
}

View File

@ -1,54 +0,0 @@
<div class="main-content">
<div class="container-fluid description-types-listing">
<div class="row align-items-center">
<div class="col-auto">
<h3>{{'DESCRIPTION-TYPES-LISTING.TITLE' | translate}}</h3>
</div>
<div class="col"></div>
<div class="col-auto">
<button mat-raised-button class="create-btn ml-md-3" [routerLink]="['/description-types/new']">
<span class="button-text">
{{'DESCRIPTION-TYPES-LISTING.CREATE-TYPE' | translate}}
</span>
</button>
</div>
</div>
<!-- <app-dmp-profile-criteria-component></app-dmp-profile-criteria-component> -->
<div class="mat-elevation-z6">
<mat-table [dataSource]="dataSource" matSort (matSortChange)="refresh()">
<!-- Column Definition: Name -->
<ng-container cdkColumnDef="label">
<mat-header-cell *matHeaderCellDef mat-sort-header="label">
{{'DESCRIPTION-TYPES-LISTING.COLUMNS.NAME' | translate}}</mat-header-cell>
<mat-cell *matCellDef="let row">{{row.name}}</mat-cell>
</ng-container>
<ng-container cdkColumnDef="status">
<mat-header-cell *matHeaderCellDef mat-sort-header="status">{{'DESCRIPTION-TYPES-LISTING.COLUMNS.STATUS' |
translate}}</mat-header-cell>
<mat-cell *matCellDef="let row">
<div [ngClass]="['status-chip',getStatusClass(row.status)]">{{parseStatus(row.status) | translate}}</div>
</mat-cell>
</ng-container>
<ng-container cdkColumnDef="delete">
<mat-header-cell *matHeaderCellDef></mat-header-cell>
<mat-cell *matCellDef="let row" (click)="$event.stopPropagation()">
<button mat-icon-button (click)="deleteTemplate(row.id)">
<mat-icon [matTooltip]="('DESCRIPTION-TYPES-LISTING.ACTIONS.DELETE' | translate)"
matTooltipPosition="right" class="dlt-btn">delete</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns" (click)="rowClick(row.id)"></mat-row>
</mat-table>
<mat-paginator #paginator [pageSizeOptions]="[10, 25, 100]">
</mat-paginator>
</div>
</div>
</div>

View File

@ -1,173 +0,0 @@
import { DataSource } from '@angular/cdk/table';
import { HttpErrorResponse } from '@angular/common/http';
import { Component, EventEmitter, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { Router } from '@angular/router';
import { DescriptionTemplateType } from '@app/core/model/description-template-type/description-template-type';
import { DescriptionTemplateTypeLookup } from '@app/core/query/description-template/description-template-type.lookup';
import { DescriptionTemplateTypeService } from '@app/core/services/description-template-type/description-template-type.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { BaseComponent } from '@common/base/base.component';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { TranslateService } from '@ngx-translate/core';
import { Observable, merge } from 'rxjs';
import { map, startWith, switchMap, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
@Component({
selector: 'app-description-types',
templateUrl: './description-types.component.html',
styleUrls: ['./description-types.component.scss']
})
export class DescriptionTypesComponent extends BaseComponent implements OnInit {
@ViewChild(MatPaginator, { static: true }) _paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) _sort: MatSort;
dataSource: DescriptionTypesDataSource | null;
displayedColumns: String[] = ['label', 'status', 'delete'];
statuses = [
{ value: '0', viewValue: 'DESCRIPTION-TYPES-LISTING.STATUS.DRAFT' },
{ value: '1', viewValue: 'DESCRIPTION-TYPES-LISTING.STATUS.FINALIZED' }
];
constructor(
private descriptionTemplateTypeService: DescriptionTemplateTypeService,
private dialog: MatDialog,
private language: TranslateService,
private uiNotificationService: UiNotificationService,
private router: Router
) {
super();
}
ngOnInit(): void {
this.refresh();
}
refresh() {
this.dataSource = new DescriptionTypesDataSource(this.descriptionTemplateTypeService, this._paginator, this._sort);
}
rowClick(rowId: String) {
this.router.navigate(['description-types/' + rowId]);
}
parseStatus(value: number): string {
const stringVal = value.toString()
try {
return this.statuses.find(status => status.value === stringVal).viewValue;
} catch {
return stringVal;
}
}
deleteTemplate(id: string) {
if (id) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
restoreFocus: false,
data: {
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL'),
isDeleteConfirmation: true
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
this.descriptionTemplateTypeService.delete(id)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-DATASET-PROFILE-DELETE'), SnackBarNotificationLevel.Success);
this.refresh();
},
error => {
this.onCallbackError(error);
if (error.error.statusCode == 674) {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DATASET-PROFILE-DELETE'), SnackBarNotificationLevel.Error);
} else {
this.uiNotificationService.snackBarNotification(this.language.instant(error.message), SnackBarNotificationLevel.Error);
}
}
);
}
});
}
}
onCallbackError(errorResponse: HttpErrorResponse) {
this.uiNotificationService.snackBarNotification(errorResponse.message, SnackBarNotificationLevel.Warning);
}
getStatusClass(status: number): string {
if (status == 1) {
return 'status-chip-finalized'
}
return 'status-chip-draft';
}
}
export class DescriptionTypesDataSource extends DataSource<DescriptionTemplateType> {
data: DescriptionTemplateType[] = [];
loadData: EventEmitter<boolean> = new EventEmitter<boolean>();
constructor(
private _service: DescriptionTemplateTypeService,
private _paginator: MatPaginator,
private _sort: MatSort
) {
super();
}
connect(): Observable<DescriptionTemplateType[]> {
const dataChanges = [
this._paginator.page,
this._sort.sortChange
];
return merge(...dataChanges).pipe(
startWith(<string>null),
switchMap(() => {
let lookup: DescriptionTemplateTypeLookup = new DescriptionTemplateTypeLookup();
lookup.page = {
offset: this._paginator.pageIndex * this._paginator.pageSize,
size: this._paginator.pageSize
},
lookup.project = {
fields: [
nameof<DescriptionTemplateType>(x => x.id),
nameof<DescriptionTemplateType>(x => x.name),
nameof<DescriptionTemplateType>(x => x.status)
]
};
lookup.order = {
items: ['name']
};
return this._service.query(lookup)
}),
map(result => {
return result;
}),
map(result => {
if (!result) { return []; }
this.data = result.items;
this._paginator.length = result.count;
return result.items;
}));
}
disconnect() {
// No-op
}
}

View File

@ -0,0 +1,36 @@
<div class="d-flex align-items-center gap-1-rem">
<button mat-flat-button [matMenuTriggerFor]="filterMenu" #filterMenuTrigger="matMenuTrigger" (click)="updateFilters()" class="filter-button">
<mat-icon aria-hidden="false" [matBadgeHidden]="!appliedFilterCount" [matBadge]="appliedFilterCount" matBadgeColor="warn" matBadgeSize="small">filter_alt</mat-icon>
{{'COMMONS.LISTING-COMPONENT.SEARCH-FILTER-BTN' | translate}}
</button>
<mat-menu #filterMenu>
<div class="p-3" (click)="$event?.stopPropagation?.()">
<div class="search-listing-filters-container">
<div class="d-flex align-items-center justify-content-between">
<h4>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.TITLE' | translate}}</h4>
<button color="accent" mat-button (click)="clearFilters()">
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
</button>
</div>
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.IS-ACTIVE' | translate}}
</mat-slide-toggle>
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.CANCEL' | translate}}
</button>
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.APPLY-FILTERS' | translate}}
</button>
</div>
</div>
</div>
</mat-menu>
<app-expandable-search-field [(value)]=internalFilters.like (valueChange)="onSearchTermChange($event)" />
</div>

View File

@ -0,0 +1,25 @@
.description-template-type-listing-filters {
}
::ng-deep.mat-mdc-menu-panel {
max-width: 100% !important;
height: 100% !important;
}
:host::ng-deep.mat-mdc-menu-content:not(:empty) {
padding-top: 0 !important;
}
.filter-button{
padding-top: .6rem;
padding-bottom: .6rem;
// .mat-icon{
// font-size: 1.5em;
// width: 1.2em;
// height: 1.2em;
// }
}

View File

@ -0,0 +1,94 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { DescriptionTemplateTypeFilter } from '@app/core/query/description-template-type.lookup';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { BaseComponent } from '@common/base/base.component';
import { nameof } from 'ts-simple-nameof';
@Component({
selector: 'app-description-template-type-listing-filters',
templateUrl: './description-template-type-listing-filters.component.html',
styleUrls: ['./description-template-type-listing-filters.component.scss']
})
export class DescriptionTemplateTypeListingFiltersComponent extends BaseComponent implements OnInit, OnChanges {
@Input() readonly filter: DescriptionTemplateTypeFilter;
@Output() filterChange = new EventEmitter<DescriptionTemplateTypeFilter>();
// * State
internalFilters: DescriptionTemplateTypeListingFilters = this._getEmptyFilters();
protected appliedFilterCount: number = 0;
constructor(
public enumUtils: EnumUtils,
) { super(); }
ngOnInit() {
}
ngOnChanges(changes: SimpleChanges): void {
const filterChange = changes[nameof<DescriptionTemplateTypeListingFiltersComponent>(x => x.filter)]?.currentValue as DescriptionTemplateTypeFilter;
if (filterChange) {
this.updateFilters()
}
}
onSearchTermChange(searchTerm: string): void {
this.applyFilters()
}
protected updateFilters(): void {
this.internalFilters = this._parseToInternalFilters(this.filter);
this.appliedFilterCount = this._computeAppliedFilters(this.internalFilters);
}
protected applyFilters(): void {
const { isActive, like } = this.internalFilters ?? {}
this.filterChange.emit({
...this.filter,
like,
isActive: isActive ? [IsActive.Active] : [IsActive.Inactive]
})
}
private _parseToInternalFilters(inputFilter: DescriptionTemplateTypeFilter): DescriptionTemplateTypeListingFilters {
if (!inputFilter) {
return this._getEmptyFilters();
}
let { excludedIds, ids, isActive, like } = inputFilter;
return {
isActive: (isActive ?? [])?.includes(IsActive.Active) || !isActive?.length,
like: like
}
}
private _getEmptyFilters(): DescriptionTemplateTypeListingFilters {
return {
isActive: true,
like: null,
}
}
private _computeAppliedFilters(filters: DescriptionTemplateTypeListingFilters): number {
let count = 0;
if (filters?.isActive) {
count++
}
return count;
}
clearFilters() {
this.internalFilters = this._getEmptyFilters();
}
}
interface DescriptionTemplateTypeListingFilters {
isActive: boolean;
like: string;
}

View File

@ -0,0 +1,43 @@
import { DragDropModule } from '@angular/cdk/drag-drop';
import { NgModule } from "@angular/core";
import { AutoCompleteModule } from "@app/library/auto-complete/auto-complete.module";
import { UrlListingModule } from '@app/library/url-listing/url-listing.module';
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module';
import { HybridListingModule } from "@common/modules/hybrid-listing/hybrid-listing.module";
import { TextFilterModule } from "@common/modules/text-filter/text-filter.module";
import { UserSettingsModule } from "@common/modules/user-settings/user-settings.module";
import { CommonUiModule } from '@common/ui/common-ui.module';
import { NgxDropzoneModule } from "ngx-dropzone";
import { DmpBlueprintRoutingModule } from './dmp-blueprint.routing';
import { DmpBlueprintEditorComponent } from './editor/dmp-blueprint-editor.component';
import { DmpBlueprintExternalAutocompleteFieldEditorComponent } from './editor/external-autocomplete/dmp-blueprint-external-autocomplete-field-editor.component';
import { DialodConfirmationUploadDmpBlueprints } from './listing/criteria/dialog-confirmation-upload-blueprint/dialog-confirmation-upload-blueprints.component';
import { DmpBlueprintCriteriaComponent } from './listing/criteria/dmp-blueprint-criteria.component';
import { DmpBlueprintListingComponent } from './listing/dmp-blueprint-listing.component';
import { DmpBlueprintListingFiltersComponent } from "./listing/filters/dmp-blueprint-listing-filters.component";
@NgModule({
imports: [
CommonUiModule,
CommonFormsModule,
UrlListingModule,
ConfirmationDialogModule,
DmpBlueprintRoutingModule,
NgxDropzoneModule,
DragDropModule,
AutoCompleteModule,
HybridListingModule,
TextFilterModule,
UserSettingsModule
],
declarations: [
DmpBlueprintEditorComponent,
DmpBlueprintListingComponent,
DmpBlueprintListingFiltersComponent,
DmpBlueprintCriteriaComponent,
DialodConfirmationUploadDmpBlueprints,
DmpBlueprintExternalAutocompleteFieldEditorComponent
]
})
export class DmpBlueprintModule { }

View File

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminAuthGuard } from '@app/core/admin-auth-guard.service';
import { DmpBlueprintEditorComponent } from './editor/dmp-blueprint-editor.component';
import { DmpBlueprintListingComponent } from './listing/dmp-blueprint-listing.component';
const routes: Routes = [
{ path: '', component: DmpBlueprintListingComponent, canActivate: [AdminAuthGuard] },
{ path: 'new', component: DmpBlueprintEditorComponent, canActivate: [AdminAuthGuard], data: { title: 'GENERAL.TITLES.DMP-BLUEPRINT-NEW' } },
{ path: 'clone/:cloneid', component: DmpBlueprintEditorComponent, canActivate: [AdminAuthGuard], data: { title: 'GENERAL.TITLES.DMP-BLUEPRINT-CLONE' } },
{ path: ':id', component: DmpBlueprintEditorComponent, canActivate: [AdminAuthGuard], data: { title: 'GENERAL.TITLES.DMP-BLUEPRINT-EDIT' } },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class DmpBlueprintRoutingModule { }

View File

@ -1,10 +1,10 @@
<div class="main-content">
<div class="container-fluid dmp-profile-editor">
<div class="container-fluid dmp-blueprint-editor">
<div class="row align-items-center mb-4" *ngIf="formGroup">
<div class="col-auto">
<h3 *ngIf="isNew && !isClone">{{'DMP-PROFILE-EDITOR.TITLE.NEW' | translate}}</h3>
<h3 *ngIf="isNew && !isClone">{{'DMP-BLUEPRINT-EDITOR.TITLE.NEW' | translate}}</h3>
<h3 *ngIf="isNew && isClone">
<span>{{'DMP-PROFILE-EDITOR.TITLE.NEW-PROFILE-CLONE' | translate}}</span>
<span>{{'DMP-BLUEPRINT-EDITOR.TITLE.CLONE' | translate}}</span>
{{formGroup.get('label').value}}
</h3>
<h3 *ngIf="!isNew">{{formGroup.get('label').value}}</h3>
@ -13,23 +13,23 @@
<div class="col-auto" *ngIf="!isNew">
<button mat-button class="action-btn" type="button" (click)="delete()">
<mat-icon>delete</mat-icon>
{{'DMP-PROFILE-EDITOR.ACTIONS.DELETE' | translate}}
{{'DMP-BLUEPRINT-EDITOR.ACTIONS.DELETE' | translate}}
</button>
</div>
<div class="col-auto" *ngIf="formGroup.get('status').value==1">
<button mat-button class="finalize-btn" (click)="downloadXML()"
type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.DOWNLOAD-XML' | translate }}</button>
type="button">{{'DMP-BLUEPRINT-EDITOR.ACTIONS.DOWNLOAD-XML' | translate }}</button>
</div>
<div *ngIf="formGroup.get('status').value!=1" class="col-auto">
<button mat-button class="finalize-btn" (click)="finalize()"
[disabled]="!this.isFormValid()" type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.FINALIZE' | translate }}</button>
[disabled]="!this.isFormValid()" type="button">{{'DMP-BLUEPRINT-EDITOR.ACTIONS.FINALIZE' | translate }}</button>
</div>
</div>
<form *ngIf="formGroup" (ngSubmit)="formSubmit()" [formGroup]="formGroup">
<mat-card style="padding: 2em;">
<!-- <mat-card-header>
<mat-card-title *ngIf="isNew">
<h4>{{'DMP-PROFILE-EDITOR.TITLE.NEW' | translate}}</h4>
<h4>{{'DMP-BLUEPRINT-EDITOR.TITLE.NEW' | translate}}</h4>
</mat-card-title>
<mat-card-title *ngIf="!isNew">
<h4>{{formGroup.get('label').value}}</h4>
@ -148,7 +148,7 @@
</div>
<div [hidden]="viewOnly" class="field-delete col-1" (click)="removeSystemFieldWithIndex(sectionIndex, fieldIndex)">
<mat-icon class="field-delete-icon">delete</mat-icon>
<span class="field-delete-text">{{'DATASET-PROFILE-EDITOR.STEPS.TOOLKIT.DELETE' | translate}}</span>
<span class="field-delete-text">{{'DMP-BLUEPRINT-EDITOR.STEPS.TOOLKIT.DELETE' | translate}}</span>
</div>
</ng-container>
@ -192,7 +192,7 @@
</div>
<div [hidden]="viewOnly" class="field-delete col-1" (click)="removeExtraField(sectionIndex, fieldIndex)">
<mat-icon class="field-delete-icon">delete</mat-icon>
<span class="field-delete-text">{{'DATASET-PROFILE-EDITOR.STEPS.TOOLKIT.DELETE' | translate}}</span>
<span class="field-delete-text">{{'DMP-BLUEPRINT-EDITOR.STEPS.TOOLKIT.DELETE' | translate}}</span>
</div>
</ng-container>
@ -210,7 +210,7 @@
<div class="col-12">
<div class="row">
<div class="col-12">
<mat-checkbox formControlName="hasTemplates" (change)="checkForProfiles($event, sectionIndex)">
<mat-checkbox formControlName="hasTemplates" (change)="checkForBlueprints($event, sectionIndex)">
Description Templates
</mat-checkbox>
</div>
@ -222,9 +222,9 @@
<div class="col-12">
<mat-form-field>
<mat-label>Description Templates</mat-label>
<app-multiple-auto-complete placeholder="Description Templates" [disabled]="viewOnly" [value]="descriptionTemplatesPerSection[sectionIndex]" [hidePlaceholder]="true" required='false' [configuration]="profilesAutoCompleteConfiguration" (optionRemoved)="onRemoveTemplate($event, sectionIndex)" (optionSelected)="onOptionSelected($event, sectionIndex)">
<app-multiple-auto-complete placeholder="Description Templates" [disabled]="viewOnly" [value]="descriptionTemplatesPerSection[sectionIndex]" [hidePlaceholder]="true" required='false' [configuration]="blueprintsAutoCompleteConfiguration" (optionRemoved)="onRemoveTemplate($event, sectionIndex)" (optionSelected)="onOptionSelected($event, sectionIndex)">
</app-multiple-auto-complete>
<!-- <button matSuffix class="input-btn" (click)="allAvailableProfiles($event)">
<!-- <button matSuffix class="input-btn" (click)="allAvailableBlueprints($event)">
<mat-icon class="icon-btn">view_list</mat-icon>
</button> -->
</mat-form-field>
@ -267,7 +267,7 @@
</div> -->
<div [hidden]="viewOnly" class="action-list-item col-auto dlt-section-btn" (click)="removeSection(sectionIndex)" [disabled]="viewOnly">
<mat-icon class="action-list-icon">delete</mat-icon>
<span class="action-list-text">{{'DATASET-PROFILE-EDITOR.STEPS.TOOLKIT.DELETE' | translate}}</span>
<span class="action-list-text">{{'DMP-BLUEPRINT-EDITOR.STEPS.TOOLKIT.DELETE' | translate}}</span>
</div>
</div>
</div>
@ -285,105 +285,15 @@
</div>
</div>
</div>
<!-- <div class="col-12">
<div class="row" *ngFor="let fieldFormGroup of formGroup.get('definition').get('fields')['controls'];let i=index">
<div class="col-10">
<div class="row">
<mat-form-field class="col">
<input matInput placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.LABEL' | translate}}" type="text" name="label" [formControl]="fieldFormGroup.get('label')"
required>
<mat-error *ngIf="fieldFormGroup.get('label').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="fieldFormGroup.get('label').hasError('backendError')">
{{fieldFormGroup.get('label').getError('backendError').message}}
</mat-error>
</mat-form-field>
<mat-form-field class="col">
<mat-select placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.TYPE' | translate}}" [formControl]="fieldFormGroup.get('type')" required>
<mat-option *ngFor="let fieldType of getDMPProfileFieldTypeValues()" [value]="fieldType">
{{ getDMPProfileFieldTypeWithLanguage(fieldType) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="fieldFormGroup.get('type').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="fieldFormGroup.get('type').hasError('backendError')">
{{fieldFormGroup.get('type').getError('backendError').message}}
</mat-error>
</mat-form-field>
<mat-form-field class="col">
<mat-select placeholder="{{'DMP-PROFILE-EDITOR.FIELDS.DATATYPE' | translate}}" [formControl]="fieldFormGroup.get('dataType')"
required>
<mat-option *ngFor="let fieldDataType of getDMPProfileFieldDataTypeValues()" [value]="fieldDataType">
{{ getDMPProfileFieldDataTypeWithLanguage(fieldDataType) | translate}}
</mat-option>
</mat-select>
<mat-error *ngIf="fieldFormGroup.get('dataType').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<mat-error *ngIf="fieldFormGroup.get('dataType').hasError('backendError')">
{{fieldFormGroup.get('dataType').getError('backendError').message}}
</mat-error>
</mat-form-field>
<div class="centered-row-item col-auto">
<mat-checkbox [formControl]="fieldFormGroup.get('required')">
{{'DMP-PROFILE-EDITOR.FIELDS.REQUIRED' | translate}}
</mat-checkbox>
</div>
<div *ngIf="isExternalAutocomplete(fieldFormGroup)" class="row">
<app-dmp-profile-external-autocomplete-field-editor-component [form]="fieldFormGroup">
</app-dmp-profile-external-autocomplete-field-editor-component>
</div>
</div>
</div>
<div class="col-2">
<div class="row">
<div class="col-auto" *ngIf="!isNew">
<button mat-icon-button type="button" (click)="removeField(i)" [disabled]="viewOnly">
<mat-icon class="mat-24">delete</mat-icon>
</button>
</div>
<div class="col-auto" *ngIf="!isNew && formGroup.get('status').value==0 && i == (formGroup.get('definition').get('fields')['controls'].length - 1)">
<button mat-mini-fab color="primary" type="button" (click)="addField()" [disabled]="viewOnly">
<mat-icon class="mat-24">add</mat-icon>
</button>
</div>
<div class="col-auto" *ngIf="isNew && i != 0">
<button mat-mini-fab class="remove" type="button" (click)="removeField(i)">
<mat-icon class="mat-24">remove</mat-icon>
</button>
</div>
<div class="col-auto" *ngIf="isNew && i == (formGroup.get('definition').get('fields')['controls'].length - 1)">
<button mat-mini-fab color="primary" type="button" (click)="addField()" [disabled]="viewOnly">
<mat-icon class="mat-24">add</mat-icon>
</button>
</div>
</div>
</div>
</div>
</div> -->
</div>
<div class="row mt-4">
<div class="col-auto">
<button mat-button class="action-btn" (click)="cancel()" type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.CANCEL' | translate}}</button>
<button mat-button class="action-btn" (click)="cancel()" type="button">{{'DMP-BLUEPRINT-EDITOR.ACTIONS.CANCEL' | translate}}</button>
</div>
<div class="col"></div>
<!-- <div class="col-auto" *ngIf="!isNew">
<button mat-raised-button color="primary" type="button" (click)="delete()">{{'DMP-PROFILE-EDITOR.ACTIONS.DELETE' | translate}}</button>
</div>
<button mat-raised-button *ngIf="formGroup.get('status').value!=1" class="col-auto" color="primary" (click)="finalize()"
[disabled]="!formGroup.valid" type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.FINALIZE' | translate }}</button>
<button mat-raised-button *ngIf="formGroup.get('status').value==1" class="col-auto" color="primary" (click)="downloadXML()"
type="button">{{'DMP-PROFILE-EDITOR.ACTIONS.DOWNLOAD-XML' | translate }}</button>
<div class="col-auto" *ngIf="!viewOnly">
<button mat-raised-button color="primary" type="submit" [disabled]="!formGroup.valid">
{{'DMP-PROFILE-EDITOR.ACTIONS.SAVE' | translate}}
</button>
</div> -->
<div class="col-auto" *ngIf="!viewOnly">
<button mat-button class="action-btn" type="submit">
{{'DMP-PROFILE-EDITOR.ACTIONS.SAVE' | translate}}
{{'DMP-BLUEPRINT-EDITOR.ACTIONS.SAVE' | translate}}
</button>
</div>
</div>

View File

@ -1,4 +1,4 @@
.dmp-profile-editor {
.dmp-blueprint-editor {
margin-top: 1.3rem;
margin-left: 1em;
margin-right: 3em;

View File

@ -1,85 +1,80 @@
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { UntypedFormArray, UntypedFormBuilder, FormControl, UntypedFormGroup, Validators } from '@angular/forms';
import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
import { HttpClient } from '@angular/common/http';
import { AfterViewInit, Component } from '@angular/core';
import { UntypedFormArray, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { DmpProfileFieldDataType } from '@app/core/common/enum/dmp-profile-field-type';
import { DmpProfileStatus } from '@app/core/common/enum/dmp-profile-status';
import { DmpProfileType } from '@app/core/common/enum/dmp-profile-type';
import { DmpProfile } from '@app/core/model/dmp-profile/dmp-profile';
import { DmpProfileService } from '@app/core/services/dmp/dmp-profile.service';
import { DmpBlueprintFieldDataType } from '@app/core/common/enum/dmp-blueprint-field-type';
import { DmpBlueprintStatus } from '@app/core/common/enum/dmp-blueprint-status';
import { DmpBlueprintType } from '@app/core/common/enum/dmp-blueprint-type';
import { DataTableRequest } from '@app/core/model/data-table/data-table-request';
import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile';
import { DmpBlueprint, ExtraFieldType, FieldCategory, SystemFieldType } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint';
import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import { MatomoService } from '@app/core/services/matomo/matomo-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { DmpProfileEditorModel, DmpProfileFieldEditorModel } from '@app/ui/admin/dmp-profile/editor/dmp-profile-editor.model';
import { DmpProfileExternalAutoCompleteFieldDataEditorModel } from '@app/ui/admin/dmp-profile/editor/external-autocomplete/dmp-profile-external-autocomplete-field-editor.model';
import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { DmpBlueprintExternalAutoCompleteFieldDataEditorModel } from '@app/ui/admin/dmp-blueprint/editor/external-autocomplete/dmp-blueprint-external-autocomplete-field-editor.model';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { isNullOrUndefined } from '@app/utilities/enhancers/utils';
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 { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { environment } from 'environments/environment';
import * as FileSaver from 'file-saver';
import { Observable, of as observableOf } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { HttpClient } from '@angular/common/http';
import { MatomoService } from '@app/core/services/matomo/matomo-service';
import { MatDialog } from '@angular/material/dialog';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { DatasetProfileModel } from '@app/core/model/dataset/dataset-profile';
import { DataTableRequest } from '@app/core/model/data-table/data-table-request';
import { DatasetProfileCriteria } from '@app/core/query/dataset-profile/dataset-profile-criteria';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import { AvailableProfilesComponent } from '@app/ui/dmp/editor/available-profiles/available-profiles.component';
import { DatasetPreviewDialogComponent } from '@app/ui/dmp/dataset-preview/dataset-preview-dialog.component';
import { CdkDragDrop, CdkDropList, CdkDrag, moveItemInArray } from '@angular/cdk/drag-drop';
import { DmpBlueprint, DmpBlueprintDefinition, ExtraFieldType, FieldCategory, SystemFieldType } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint';
import { DescriptionTemplatesInSectionEditor, DmpBlueprintEditor, FieldInSectionEditor, SectionDmpBlueprintEditor } from './dmp-blueprint-editor.model';
import { Guid } from '@common/types/guid';
import { isNullOrUndefined } from '@app/utilities/enhancers/utils';
import { DmpBlueprintListing } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint-listing';
import { FormValidationErrorsDialogComponent } from '@common/forms/form-validation-errors-dialog/form-validation-errors-dialog.component';
import { DmpBlueprintEditorModel } from './dmp-profile-editor.model';
@Component({
selector: 'app-dmp-profile-editor-component',
templateUrl: 'dmp-profile-editor.component.html',
styleUrls: ['./dmp-profile-editor.component.scss']
selector: 'app-dmp-blueprint-editor-component',
templateUrl: 'dmp-blueprint-editor.component.html',
styleUrls: ['./dmp-blueprint-editor.component.scss']
})
export class DmpProfileEditorComponent extends BaseComponent implements AfterViewInit {
export class DmpBlueprintEditorComponent extends BaseComponent implements AfterViewInit {
isNew = true;
isClone = false;
viewOnly = false;
dmpProfileModel: DmpProfileEditorModel;
dmpBlueprintModel: DmpBlueprintEditor;
dmpBlueprintEditorModel = new DmpBlueprintEditorModel();
dmpBlueprintEditor = new DmpBlueprintEditor();
formGroup: UntypedFormGroup = null;
host: string;
dmpProfileId: string;
breadCrumbs: Observable<BreadcrumbItem[]>;
dmpBlueprintId: string;
// breadCrumbs: Observable<BreadcrumbItem[]>;
dmpBlueprintsFormGroup: UntypedFormGroup = null;
profilesAutoCompleteConfiguration: MultipleAutoCompleteConfiguration;
blueprintsAutoCompleteConfiguration: MultipleAutoCompleteConfiguration;
fieldList = [
{label: 'Title', type: SystemFieldType.TEXT},
{label: 'Description', type: SystemFieldType.HTML_TEXT},
{label: 'Researchers', type: SystemFieldType.RESEARCHERS},
{label: 'Organizations', type: SystemFieldType.ORGANIZATIONS},
{label: 'Language', type: SystemFieldType.LANGUAGE},
{label: 'Contact', type: SystemFieldType.CONTACT},
{label: 'Funder', type: SystemFieldType.FUNDER},
{label: 'Grant', type: SystemFieldType.GRANT},
{label: 'Project', type: SystemFieldType.PROJECT},
{label: 'License', type: SystemFieldType.LICENSE},
{label: 'Access Rights', type: SystemFieldType.ACCESS_RIGHTS}
{ label: 'Title', type: SystemFieldType.TEXT },
{ label: 'Description', type: SystemFieldType.HTML_TEXT },
{ label: 'Researchers', type: SystemFieldType.RESEARCHERS },
{ label: 'Organizations', type: SystemFieldType.ORGANIZATIONS },
{ label: 'Language', type: SystemFieldType.LANGUAGE },
{ label: 'Contact', type: SystemFieldType.CONTACT },
{ label: 'Funder', type: SystemFieldType.FUNDER },
{ label: 'Grant', type: SystemFieldType.GRANT },
{ label: 'Project', type: SystemFieldType.PROJECT },
{ label: 'License', type: SystemFieldType.LICENSE },
{ label: 'Access Rights', type: SystemFieldType.ACCESS_RIGHTS }
];
systemFieldListPerSection: Array<Array<any>> = new Array();
descriptionTemplatesPerSection: Array<Array<DatasetProfileModel>> = new Array<Array<DatasetProfileModel>>();
constructor(
private dmpProfileService: DmpProfileService,
private dmpBlueprintService: DmpBlueprintService,
private _service: DmpService,
private route: ActivatedRoute,
private router: Router,
@ -100,7 +95,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
ngAfterViewInit() {
this.matomoService.trackPageView('Admin: DMP Profile Edit');
this.profilesAutoCompleteConfiguration = {
this.blueprintsAutoCompleteConfiguration = {
filterFn: this.filterProfiles.bind(this),
initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
displayFn: (item) => item['label'],
@ -112,67 +107,67 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
this.route.params
.pipe(takeUntil(this._destroyed))
.subscribe((params: Params) => {
this.dmpProfileId = params['id'];
this.dmpBlueprintId = params['id'];
const cloneId = params['cloneid'];
if (this.dmpProfileId != null) {
if (this.dmpBlueprintId != null) {
this.isNew = false;
this.dmpProfileService.getSingleBlueprint(this.dmpProfileId).pipe(map(data => data as DmpBlueprint))
this.dmpBlueprintService.getSingleBlueprint(this.dmpBlueprintId).pipe(map(data => data as any))
.pipe(takeUntil(this._destroyed))
.subscribe(data => {
this.dmpBlueprintModel = new DmpBlueprintEditor().fromModel(data);
this.formGroup = this.dmpBlueprintModel.buildForm();
this.dmpBlueprintEditor = new DmpBlueprintEditor().fromModel(data);
this.formGroup = this.dmpBlueprintEditor.buildForm();
this.buildSystemFields();
this.fillDescriptionTemplatesInMultAutocomplete();
if (this.dmpBlueprintModel.status == DmpProfileStatus.Finalized) {
if (this.dmpBlueprintEditor.status == DmpBlueprintStatus.Finalized) {
this.formGroup.disable();
this.viewOnly = true
}
this.breadCrumbs = observableOf([{
parentComponentName: 'DmpProfileListingComponent',
label: this.language.instant('NAV-BAR.TEMPLATE'),
url: '/dmp-profiles/' + this.dmpProfileId
}]);
// this.breadCrumbs = observableOf([{
// parentComponentName: 'DmpBlueprintListingComponent',
// label: this.language.instant('NAV-BAR.TEMPLATE'),
// url: '/dmp-blueprints/' + this.dmpBlueprintId
// }]);
});
} else if (cloneId != null) {
this.isClone = true;
this.dmpProfileService.clone(cloneId).pipe(map(data => data as DmpBlueprint), takeUntil(this._destroyed))
this.dmpBlueprintService.clone(cloneId).pipe(map(data => data as any), takeUntil(this._destroyed))
.subscribe(
data => {
this.dmpBlueprintModel = new DmpBlueprintEditor().fromModel(data);
this.dmpBlueprintModel.id = null;
this.dmpBlueprintModel.status = DmpProfileStatus.Draft;
this.formGroup = this.dmpBlueprintModel.buildForm();
this.dmpBlueprintEditor = new DmpBlueprintEditor().fromModel(data);
this.dmpBlueprintEditor.id = null;
this.dmpBlueprintEditor.status = DmpBlueprintStatus.Draft;
this.formGroup = this.dmpBlueprintEditor.buildForm();
this.buildSystemFields();
this.fillDescriptionTemplatesInMultAutocomplete();
},
error => this.onCallbackError(error)
);
} else {
this.dmpProfileModel = new DmpProfileEditorModel();
this.dmpBlueprintModel = new DmpBlueprintEditor();
this.dmpBlueprintEditorModel = new DmpBlueprintEditorModel();
this.dmpBlueprintEditor = new DmpBlueprintEditor();
setTimeout(() => {
// this.formGroup = this.dmpProfileModel.buildForm();
// this.formGroup = this.dmpBlueprintModel.buildForm();
// this.addField();
this.dmpBlueprintModel.status = DmpProfileStatus.Draft;
this.formGroup = this.dmpBlueprintModel.buildForm();
this.dmpBlueprintEditor.status = DmpBlueprintStatus.Draft;
this.formGroup = this.dmpBlueprintEditor.buildForm();
});
this.breadCrumbs = observableOf([{
parentComponentName: 'DmpProfileListingComponent',
label: this.language.instant('NAV-BAR.TEMPLATE'),
url: '/dmp-profiles/' + this.dmpProfileId
}]);
// this.breadCrumbs = observableOf([{
// parentComponentName: 'DmpBlueprintListingComponent',
// label: this.language.instant('NAV-BAR.TEMPLATE'),
// url: '/dmp-blueprints/' + this.dmpBlueprintId
// }]);
}
});
}
buildSystemFields(){
buildSystemFields() {
const sections = this.sectionsArray().controls.length;
for(let i = 0; i < sections; i++){
for (let i = 0; i < sections; i++) {
let systemFieldsInSection = new Array();
this.fieldsArray(i).controls.forEach((field) => {
if((field.get('category').value == FieldCategory.SYSTEM || field.get('category').value == 'SYSTEM')){
if ((field.get('category').value == FieldCategory.SYSTEM || field.get('category').value == 'SYSTEM')) {
systemFieldsInSection.push(this.fieldList.find(f => f.type == field.get('type').value).type);
}
})
@ -180,12 +175,12 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
}
}
fillDescriptionTemplatesInMultAutocomplete(){
fillDescriptionTemplatesInMultAutocomplete() {
const sections = this.sectionsArray().controls.length;
for(let i = 0; i < sections; i++){
for (let i = 0; i < sections; i++) {
let descriptionTemplatesInSection = new Array<DatasetProfileModel>();
this.descriptionTemplatesArray(i).controls.forEach((template) => {
descriptionTemplatesInSection.push({id: template.value.descriptionTemplateId, label: template.value.label, description: ""});
descriptionTemplatesInSection.push({ id: template.value.descriptionTemplateId, label: template.value.label, description: "" });
})
this.descriptionTemplatesPerSection.push(descriptionTemplatesInSection);
}
@ -203,7 +198,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
const criteria = new DatasetProfileCriteria();
criteria.like = value;
request.criteria = criteria;
return this._service.searchDMPProfiles(request);
return this._service.searchDmpBlueprints(request);
}
sectionsArray(): UntypedFormArray {
@ -231,19 +226,19 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
fieldsArray(sectionIndex: number): UntypedFormArray {
return this.sectionsArray().at(sectionIndex).get('fields') as UntypedFormArray;
}
addField(sectionIndex: number, fieldCategory: FieldCategory, fieldType?: number): void {
const field: FieldInSectionEditor = new FieldInSectionEditor();
field.id = Guid.create().toString();
field.ordinal = this.fieldsArray(sectionIndex).length + 1;
field.category = fieldCategory;
if(!isNullOrUndefined(fieldType)){
if (!isNullOrUndefined(fieldType)) {
field.type = fieldType
}
field.required = (!isNullOrUndefined(fieldType) && (fieldType == 0 || fieldType == 1)) ? true : false;
this.fieldsArray(sectionIndex).push(field.buildForm());
}
removeField(sectionIndex: number, fieldIndex: number): void {
this.fieldsArray(sectionIndex).removeAt(fieldIndex);
}
@ -263,12 +258,12 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
ordinal: this.fb.control('')
});
}
addSystemField(sectionIndex: number, systemField?: SystemFieldType): void {
this.addField(sectionIndex, FieldCategory.SYSTEM, systemField);
}
transfromEnumToString(type: SystemFieldType): string{
transfromEnumToString(type: SystemFieldType): string {
return this.fieldList.find(f => f.type == type).label;
}
@ -277,7 +272,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
if (index == -1) {
this.systemFieldListPerSection[sectionIndex].push(type);
this.addSystemField(sectionIndex, type);
}
}
else {
this.systemFieldListPerSection[sectionIndex].splice(index, 1);
this.removeSystemField(sectionIndex, type);
@ -298,7 +293,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
}
return false;
}
removeSystemFieldWithIndex(sectionIndex: number, fieldIndex: number): void {
let type: SystemFieldType = this.fieldsArray(sectionIndex).at(fieldIndex).get('type').value;
let index = this.systemFieldListPerSection[sectionIndex].indexOf(type);
@ -308,8 +303,8 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
removeSystemField(sectionIndex: number, systemField: SystemFieldType): void {
let i = 0;
for(let f of this.fieldsArray(sectionIndex).controls){
if((f.get('category').value == FieldCategory.SYSTEM || f.get('category').value == 'SYSTEM') && f.get('type').value == systemField){
for (let f of this.fieldsArray(sectionIndex).controls) {
if ((f.get('category').value == FieldCategory.SYSTEM || f.get('category').value == 'SYSTEM') && f.get('type').value == systemField) {
this.fieldsArray(sectionIndex).removeAt(i);
return;
}
@ -326,19 +321,19 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
label: this.fb.control(descriptionTemplate.value)
}));
}
removeDescriptionTemplate(sectionIndex: number, templateIndex: number): void {
this.descriptionTemplatesArray(sectionIndex).removeAt(templateIndex);
}
extraFieldsArray(sectionIndex: number): UntypedFormArray {
return this.sectionsArray().at(sectionIndex).get('extraFields') as UntypedFormArray;
}
}
addExtraField(sectionIndex: number): void {
this.addField(sectionIndex, FieldCategory.EXTRA);
}
removeExtraField(sectionIndex: number, fieldIndex: number): void {
this.fieldsArray(sectionIndex).removeAt(fieldIndex);
}
@ -361,12 +356,12 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
drop(event: CdkDragDrop<string[]>, sectionIndex: number) {
moveItemInArray(this.fieldsArray(sectionIndex).controls, event.previousIndex, event.currentIndex);
moveItemInArray(this.fieldsArray(sectionIndex).value, event.previousIndex, event.currentIndex);
moveItemInArray(this.fieldsArray(sectionIndex).value, event.previousIndex, event.currentIndex);
}
dropSections(event: CdkDragDrop<string[]>) {
moveItemInArray(this.sectionsArray().controls, event.previousIndex, event.currentIndex);
moveItemInArray(this.sectionsArray().value, event.previousIndex, event.currentIndex);
moveItemInArray(this.sectionsArray().value, event.previousIndex, event.currentIndex);
this.sectionsArray().controls.forEach((section, index) => {
section.get('ordinal').setValue(index + 1);
});
@ -374,22 +369,22 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
moveItemInFormArray(formArray: UntypedFormArray, fromIndex: number, toIndex: number): void {
const dir = toIndex > fromIndex ? 1 : -1;
const item = formArray.at(fromIndex);
for (let i = fromIndex; i * dir < toIndex * dir; i = i + dir) {
const current = formArray.at(i + dir);
formArray.setControl(i, current);
const current = formArray.at(i + dir);
formArray.setControl(i, current);
}
formArray.setControl(toIndex, item);
}
// clearForm(): void{
// this.dmpBlueprintsFormGroup.reset();
// }
onRemoveTemplate(event, sectionIndex: number) {
const profiles = this.descriptionTemplatesArray(sectionIndex).controls;
const foundIndex = profiles.findIndex(profile => profile.get('descriptionTemplateId').value === event.id);
const blueprints = this.descriptionTemplatesArray(sectionIndex).controls;
const foundIndex = blueprints.findIndex(blueprint => blueprint.get('descriptionTemplateId').value === event.id);
foundIndex !== -1 && this.descriptionTemplatesArray(sectionIndex).removeAt(foundIndex);
}
@ -405,14 +400,14 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
// });
// dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
// if (result) {
// let profiles = this.sectionsArray().at(sectionIndex).get('descriptionTemplates').value;//this.formGroup.get('profiles').value;
// const profile: DescriptionTemplatesInSectionEditor = new DescriptionTemplatesInSectionEditor();
// profile.id = Guid.create().toString();
// profile.descriptionTemplateId = event.id;
// profile.label = event.label;
// profiles.push(profile.buildForm());
// this.sectionsArray().at(sectionIndex).get('descriptionTemplates').setValue(profiles);//this.formGroup.get('profiles').setValue(profiles);
// this.profilesAutoCompleteConfiguration = {
// let blueprints = this.sectionsArray().at(sectionIndex).get('descriptionTemplates').value;//this.formGroup.get('blueprints').value;
// const blueprint: DescriptionTemplatesInSectionEditor = new DescriptionTemplatesInSectionEditor();
// blueprint.id = Guid.create().toString();
// blueprint.descriptionTemplateId = event.id;
// blueprint.label = event.label;
// blueprints.push(blueprint.buildForm());
// this.sectionsArray().at(sectionIndex).get('descriptionTemplates').setValue(blueprints);//this.formGroup.get('blueprints').setValue(blueprints);
// this.blueprintsAutoCompleteConfiguration = {
// filterFn: this.filterProfiles.bind(this),
// initialItems: (excludedItems: any[]) => this.filterProfiles('').pipe(map(result => result.filter(resultItem => (excludedItems || []).map(x => x.id).indexOf(resultItem.id) === -1))),
// displayFn: (item) => item['label'],
@ -424,28 +419,28 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
// });
// }
onOptionSelected(item, sectionIndex){
const profile: DescriptionTemplatesInSectionEditor = new DescriptionTemplatesInSectionEditor();
profile.id = Guid.create().toString();
profile.descriptionTemplateId = item.id;
profile.label = item.label;
this.descriptionTemplatesArray(sectionIndex).push(profile.buildForm());
onOptionSelected(item, sectionIndex) {
const blueprint: DescriptionTemplatesInSectionEditor = new DescriptionTemplatesInSectionEditor();
blueprint.id = Guid.create().toString();
blueprint.descriptionTemplateId = item.id;
blueprint.label = item.label;
this.descriptionTemplatesArray(sectionIndex).push(blueprint.buildForm());
}
checkValidity() {
this.formService.touchAllFormFields(this.formGroup);
if (!this.isFormValid()) { return false; }
let errorMessages = [];
if(!this.hasTitle()) {
if (!this.hasTitle()) {
errorMessages.push("Title should be set.");
}
if(!this.hasDescription()) {
if (!this.hasDescription()) {
errorMessages.push("Description should be set.");
}
if(!this.hasDescriptionTemplates()) {
if (!this.hasDescriptionTemplates()) {
errorMessages.push("At least one section should have description templates.");
}
if(errorMessages.length > 0) {
if (errorMessages.length > 0) {
this.showValidationErrorsDialog(undefined, errorMessages);
return false;
}
@ -483,7 +478,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
autoFocus: false,
restoreFocus: false,
data: {
errorMessages:errmess,
errorMessages: errmess,
projectOnly: projectOnly
},
});
@ -491,7 +486,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
}
onSubmit(): void {
this.dmpProfileService.createBlueprint(this.formGroup.value)
this.dmpBlueprintService.createBlueprint(this.formGroup.value)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
@ -501,7 +496,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
onCallbackSuccess(): void {
this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
this.router.navigate(['/dmp-profiles']);
this.router.navigate(['/dmp-blueprints']);
}
onCallbackError(errorResponse: any) {
@ -511,32 +506,32 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
public setErrorModel(validationErrorModel: ValidationErrorModel) {
Object.keys(validationErrorModel).forEach(item => {
(<any>this.dmpProfileModel.validationErrorModel)[item] = (<any>validationErrorModel)[item];
(<any>this.dmpBlueprintEditor.validationErrorModel)[item] = (<any>validationErrorModel)[item];
});
}
public cancel(): void {
this.router.navigate(['/dmp-profiles']);
this.router.navigate(['/dmp-blueprints']);
}
// addField() {
// (<FormArray>this.formGroup.get('definition').get('fields')).push(new DmpProfileFieldEditorModel().buildForm());
// (<FormArray>this.formGroup.get('definition').get('fields')).push(new DmpBlueprintFieldEditorModel().buildForm());
// }
// removeField(index: number) {
// (<FormArray>this.formGroup.get('definition').get('fields')).controls.splice(index, 1);
// }
getDMPProfileFieldDataTypeValues(): Number[] {
let keys: string[] = Object.keys(DmpProfileFieldDataType);
getDmpBlueprintFieldDataTypeValues(): Number[] {
let keys: string[] = Object.keys(DmpBlueprintFieldDataType);
keys = keys.slice(0, keys.length / 2);
const values: Number[] = keys.map(Number);
return values;
}
getDMPProfileFieldDataTypeWithLanguage(fieldType: DmpProfileFieldDataType): string {
getDmpBlueprintFieldDataTypeWithLanguage(fieldType: DmpBlueprintFieldDataType): string {
let result = '';
this.language.get(this.enumUtils.toDmpProfileFieldDataTypeString(fieldType))
this.language.get(this.enumUtils.toDmpBlueprintFieldDataTypeString(fieldType))
.pipe(takeUntil(this._destroyed))
.subscribe((value: string) => {
result = value;
@ -544,16 +539,16 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
return result;
}
getDMPProfileFieldTypeValues(): Number[] {
let keys: string[] = Object.keys(DmpProfileType);
getDmpBlueprintFieldTypeValues(): Number[] {
let keys: string[] = Object.keys(DmpBlueprintType);
keys = keys.slice(0, keys.length / 2);
const values: Number[] = keys.map(Number);
return values;
}
getDMPProfileFieldTypeWithLanguage(profileType: DmpProfileType): string {
getDmpBlueprintFieldTypeWithLanguage(blueprintType: DmpBlueprintType): string {
let result = '';
this.language.get(this.enumUtils.toDmpProfileTypeString(profileType))
this.language.get(this.enumUtils.toDmpBlueprintTypeString(blueprintType))
.pipe(takeUntil(this._destroyed))
.subscribe((value: string) => {
result = value;
@ -562,54 +557,56 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
}
delete() {
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){
if(this.formGroup.get('status').value == DmpProfileStatus.Draft) {
this.formGroup.get('status').setValue(DmpProfileStatus.Deleted);
this.dmpProfileService.createBlueprint(this.formGroup.value)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
);
}
else {
this.dmpProfileService.delete(this.dmpProfileId)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => {
if (error.error.statusCode == 674) {
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DMP-BLUEPRINT-DELETE'), SnackBarNotificationLevel.Error);
} else {
this.uiNotificationService.snackBarNotification(this.language.instant(error.message), SnackBarNotificationLevel.Error);
}
}
);
this.dialog.open(ConfirmationDialogComponent, {
data: {
isDeleteConfirmation: true,
confirmButton: this.language.instant('DMP-BLUEPRINT-EDITOR.CONFIRM-DELETE-DIALOG.CONFIRM-BUTTON'),
cancelButton: this.language.instant("DMP-BLUEPRINT-EDITOR.CONFIRM-DELETE-DIALOG.CANCEL-BUTTON"),
message: this.language.instant("DMP-BLUEPRINT-EDITOR.CONFIRM-DELETE-DIALOG.MESSAGE")
}
})
.afterClosed()
.subscribe(
confirmed => {
if (confirmed) {
if (this.formGroup.get('status').value == DmpBlueprintStatus.Draft) {
// this.formGroup.get('status').setValue(DmpBlueprintStatus.Deleted);
this.dmpBlueprintService.createBlueprint(this.formGroup.value)
.pipe(takeUntil(this._destroyed))
.subscribe(
complete => this.onCallbackSuccess(),
error => this.onCallbackError(error)
);
}
else {
// this.dmpBlueprintService.delete(this.dmpBlueprintId)
// .pipe(takeUntil(this._destroyed))
// .subscribe(
// complete => this.onCallbackSuccess(),
// error => {
// if (error.error.statusCode == 674) {
// this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DMP-BLUEPRINT-DELETE'), SnackBarNotificationLevel.Error);
// } else {
// this.uiNotificationService.snackBarNotification(this.language.instant(error.message), SnackBarNotificationLevel.Error);
// }
// }
// );
}
}
}
}
)
)
}
finalize() {
if (this.checkValidity()) {
this.formGroup.get('status').setValue(DmpProfileStatus.Finalized);
this.formGroup.get('status').setValue(DmpBlueprintStatus.Finalized);
this.onSubmit();
}
}
}
downloadXML(): void {
this.dmpProfileService.downloadXML(this.dmpProfileId)
this.dmpBlueprintService.downloadXML(this.dmpBlueprintId)
.pipe(takeUntil(this._destroyed))
.subscribe(response => {
const blob = new Blob([response.body], { type: 'application/xml' });
@ -639,7 +636,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
}
isExternalAutocomplete(formGroup: UntypedFormGroup) {
if (formGroup.get('dataType').value == DmpProfileFieldDataType.ExternalAutocomplete) {
if (formGroup.get('dataType').value == DmpBlueprintFieldDataType.ExternalAutocomplete) {
this.addControl(formGroup);
return true;
} else {
@ -650,7 +647,7 @@ export class DmpProfileEditorComponent extends BaseComponent implements AfterVie
addControl(formGroup: UntypedFormGroup) {
if (formGroup.get('dataType').value == 3)
formGroup.addControl('externalAutocomplete', new DmpProfileExternalAutoCompleteFieldDataEditorModel().buildForm());
formGroup.addControl('externalAutocomplete', new DmpBlueprintExternalAutoCompleteFieldDataEditorModel().buildForm());
}
removeControl(formGroup: UntypedFormGroup) {

View File

@ -1,4 +1,5 @@
import { UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
import { DmpBlueprintStatus } from "@app/core/common/enum/dmp-blueprint-status";
import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, FieldCategory, FieldInSection, SectionDmpBlueprint } from "@app/core/model/dmp/dmp-blueprint/dmp-blueprint";
import { BackendErrorValidator } from "@common/forms/validation/custom-validator";
import { ValidationErrorModel } from "@common/forms/validation/error-model/validation-error-model";
@ -8,7 +9,7 @@ export class DmpBlueprintEditor {
public id: string;
public label: string;
public definition: DmpBlueprintDefinitionEditor = new DmpBlueprintDefinitionEditor();
public status: number;
public status: DmpBlueprintStatus;
public created: Date;
public modified: Date;
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();

View File

@ -1,28 +1,29 @@
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
import { DmpProfileFieldDataType } from '@app/core/common/enum/dmp-profile-field-type';
import { DmpProfileType } from '@app/core/common/enum/dmp-profile-type';
import { DmpProfile, DmpProfileDefinition } from '@app/core/model/dmp-profile/dmp-profile';
import { DmpProfileField } from '@app/core/model/dmp-profile/dmp-profile-field';
import { DmpProfileExternalAutoCompleteFieldDataEditorModel } from '@app/ui/admin/dmp-profile/editor/external-autocomplete/dmp-profile-external-autocomplete-field-editor.model';
import { DmpBlueprintFieldDataType } from '@app/core/common/enum/dmp-blueprint-field-type';
import { DmpBlueprintType } from '@app/core/common/enum/dmp-blueprint-type';
import { DmpBlueprint } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { DmpBlueprintField } from '@app/core/model/dmp-blueprint/dmp-blueprint-field';
import { DmpBlueprintDefinition } from '@app/core/model/dmp/dmp-blueprint/dmp-blueprint';
import { DmpBlueprintExternalAutoCompleteFieldDataEditorModel } from '@app/ui/admin/dmp-blueprint/editor/external-autocomplete/dmp-blueprint-external-autocomplete-field-editor.model';
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
export class DmpProfileEditorModel {
export class DmpBlueprintEditorModel {
public id: string;
public label: string;
public definition: DmpProfileDefinitionEditorModel = new DmpProfileDefinitionEditorModel();
public definition: DmpBlueprintDefinitionEditorModel = new DmpBlueprintDefinitionEditorModel();
public status: number;
public created: Date;
public modified: Date;
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel();
fromModel(item: DmpProfile): DmpProfileEditorModel {
this.id = item.id;
this.label = item.label;
this.definition = new DmpProfileDefinitionEditorModel().fromModel(item.definition);
this.status = item.status;
this.created = item.created;
this.modified = item.modified;
fromModel(item: DmpBlueprint): DmpBlueprintEditorModel {
// this.id = item.id;
// this.label = item.label;
// this.definition = new DmpBlueprintDefinitionEditorModel().fromModel(item.definition);
// this.status = item.status;
// this.created = item.created;
// this.modified = item.modified;
return this;
}
@ -39,12 +40,12 @@ export class DmpProfileEditorModel {
}
}
export class DmpProfileDefinitionEditorModel {
export class DmpBlueprintDefinitionEditorModel {
public fields: DmpProfileFieldEditorModel[] = new Array<DmpProfileFieldEditorModel>();
public fields: DmpBlueprintFieldEditorModel[] = new Array<DmpBlueprintFieldEditorModel>();
fromModel(item: DmpProfileDefinition): DmpProfileDefinitionEditorModel {
if (item.fields) { item.fields.map(x => this.fields.push(new DmpProfileFieldEditorModel().fromModel(x))); }
fromModel(item: DmpBlueprintDefinition): DmpBlueprintDefinitionEditorModel {
if (item.fields) { item.fields.map(x => this.fields.push(new DmpBlueprintFieldEditorModel().fromModel(x))); }
return this;
}
@ -61,16 +62,16 @@ export class DmpProfileDefinitionEditorModel {
}
}
export class DmpProfileFieldEditorModel {
export class DmpBlueprintFieldEditorModel {
public id: string;
public type: DmpProfileType;
public dataType: DmpProfileFieldDataType;
public type: DmpBlueprintType;
public dataType: DmpBlueprintFieldDataType;
public required = false;
public label: string;
public value: any;
public externalAutocomplete?: DmpProfileExternalAutoCompleteFieldDataEditorModel;
public externalAutocomplete?: DmpBlueprintExternalAutoCompleteFieldDataEditorModel;
fromModel(item: DmpProfileField): DmpProfileFieldEditorModel {
fromModel(item: DmpBlueprintField): DmpBlueprintFieldEditorModel {
this.type = item.type;
this.dataType = item.dataType;
this.required = item.required;
@ -78,7 +79,7 @@ export class DmpProfileFieldEditorModel {
this.id = item.id;
this.value = item.value;
if (item.externalAutocomplete)
this.externalAutocomplete = new DmpProfileExternalAutoCompleteFieldDataEditorModel().fromModel(item.externalAutocomplete);
this.externalAutocomplete = new DmpBlueprintExternalAutoCompleteFieldDataEditorModel().fromModel(item.externalAutocomplete);
return this;
}

View File

@ -0,0 +1,22 @@
<div class="container external-autocomplete">
<div class="row external-autocomplete-field" *ngIf="form.get('externalAutocomplete')">
<h5 style="font-weight: bold" class="col-auto">{{'DMP-BLUEPRINT-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.TITLE' | translate}}</h5>
<mat-checkbox class="col-auto" [formControl]="this.form.get('externalAutocomplete').get('multiAutoComplete')">
{{'DMP-BLUEPRINT-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.MULTIPLE-AUTOCOMPLETE' | translate}}
</mat-checkbox>
<mat-form-field class="col-md-12">
<input matInput placeholder="{{'DMP-BLUEPRINT-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.URL' | translate}}" [formControl]="this.form.get('externalAutocomplete').get('url')">
</mat-form-field>
<mat-form-field class="col-md-4">
<input matInput placeholder="{{'DMP-BLUEPRINT-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.OPTIONS-ROOT' | translate}}"
[formControl]="this.form.get('externalAutocomplete').get('optionsRoot')">
</mat-form-field>
<mat-form-field class="col-md-4">
<input matInput placeholder="{{'DMP-BLUEPRINT-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.LABEL' | translate}}" [formControl]="this.form.get('externalAutocomplete').get('label')">
</mat-form-field>
<mat-form-field class="col-md-4">
<input matInput placeholder="{{'DMP-BLUEPRINT-EDITOR.FIELDS.EXTERNAL-AUTOCOMPLETE.VALUE' | translate}}" [formControl]="this.form.get('externalAutocomplete').get('value')">
</mat-form-field>
</div>
</div>

View File

@ -0,0 +1,17 @@
import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { DmpBlueprintExternalAutoCompleteFieldDataEditorModel } from './dmp-blueprint-external-autocomplete-field-editor.model';
@Component({
selector: 'app-dmp-blueprint-external-autocomplete-field-editor-component',
styleUrls: ['./dmp-blueprint-external-autocomplete-field-editor.component.scss'],
templateUrl: './dmp-blueprint-external-autocomplete-field-editor.component.html'
})
export class DmpBlueprintExternalAutocompleteFieldEditorComponent implements OnInit {
@Input() form: UntypedFormGroup
private externalAutocomplete: DmpBlueprintExternalAutoCompleteFieldDataEditorModel;
ngOnInit() {
}
}

View File

@ -1,7 +1,7 @@
import { UntypedFormGroup, UntypedFormBuilder } from "@angular/forms";
import { DmpProfileExternalAutoCompleteField } from "../../../../../core/model/dmp-profile/dmp-profile-external-autocomplete";
import { DmpBlueprintExternalAutoCompleteField } from "../../../../../core/model/dmp-blueprint/dmp-blueprint-external-autocomplete";
export class DmpProfileExternalAutoCompleteFieldDataEditorModel {
export class DmpBlueprintExternalAutoCompleteFieldDataEditorModel {
public url: string;
public optionsRoot: string;
@ -11,17 +11,17 @@ export class DmpProfileExternalAutoCompleteFieldDataEditorModel {
buildForm(disabled: boolean = false, skipDisable: Array<String> = []): UntypedFormGroup {
const formGroup = new UntypedFormBuilder().group({
url: [{ value: this.url, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.url')) }],
optionsRoot: [{ value: this.optionsRoot, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.optionsRoot')) }],
multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.multiAutoComplete')) }],
label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.label')) }],
value: [{ value: this.value, disabled: (disabled && !skipDisable.includes('DmpProfileExternalAutoCompleteFieldDataEditorModel.value')) }],
url: [{ value: this.url, disabled: (disabled && !skipDisable.includes('DmpBlueprintExternalAutoCompleteFieldDataEditorModel.url')) }],
optionsRoot: [{ value: this.optionsRoot, disabled: (disabled && !skipDisable.includes('DmpBlueprintExternalAutoCompleteFieldDataEditorModel.optionsRoot')) }],
multiAutoComplete: [{ value: this.multiAutoComplete, disabled: (disabled && !skipDisable.includes('DmpBlueprintExternalAutoCompleteFieldDataEditorModel.multiAutoComplete')) }],
label: [{ value: this.label, disabled: (disabled && !skipDisable.includes('DmpBlueprintExternalAutoCompleteFieldDataEditorModel.label')) }],
value: [{ value: this.value, disabled: (disabled && !skipDisable.includes('DmpBlueprintExternalAutoCompleteFieldDataEditorModel.value')) }],
});
return formGroup;
}
fromModel(item: DmpProfileExternalAutoCompleteField): DmpProfileExternalAutoCompleteFieldDataEditorModel {
fromModel(item: DmpBlueprintExternalAutoCompleteField): DmpBlueprintExternalAutoCompleteFieldDataEditorModel {
this.url = item.url;
this.optionsRoot = item.optionsRoot;
this.multiAutoComplete = item.multiAutoComplete;

View File

@ -10,7 +10,7 @@
<div class="row">
<div class="col-12">
<ngx-dropzone class="drop-file" (change)="selectXML($event)" [accept]="'text/xml'" [multiple]="false">
<ngx-dropzone-preview class="file-preview" [removable]="true" *ngIf="hasProfile()" (removed)="onRemove()">
<ngx-dropzone-preview class="file-preview" [removable]="true" *ngIf="hasBlueprint()" (removed)="onRemove()">
<ngx-dropzone-label class="file-label">{{ selectedFileName }}</ngx-dropzone-label>
</ngx-dropzone-preview>
</ngx-dropzone>
@ -20,22 +20,22 @@
<div class="col-12 d-flex justify-content-center attach-btn">
<button mat-button type="button" class="col-auto attach-file" (click)="imgFileInput.click()">
<mat-icon class="mr-2">input</mat-icon>
<span *ngIf="!hasProfile()">{{'GENERAL.START-NEW-DMP-DIALOG.UPLOAD-FILE' | translate}}</span>
<span *ngIf="hasProfile()">{{'GENERAL.START-NEW-DMP-DIALOG.REPLACE-FILE' | translate}}</span>
<span *ngIf="!hasBlueprint()">{{'GENERAL.START-NEW-DMP-DIALOG.UPLOAD-FILE' | translate}}</span>
<span *ngIf="hasBlueprint()">{{'GENERAL.START-NEW-DMP-DIALOG.REPLACE-FILE' | translate}}</span>
</button>
<input class="hidden" type="file" #imgFileInput (change)="selectXML($event)" accept="text/xml" />
</div>
</div>
<div class="row">
<mat-form-field class="col-12">
<input matInput placeholder="{{'DMP-PROFILE-LISTING.UPLOAD.UPLOAD-XML-NAME'| translate}}" name="datasetProfileName" [(ngModel)]="data.name">
<input matInput placeholder="{{'DMP-BLUEPRINT-LISTING.UPLOAD.UPLOAD-XML-NAME'| translate}}" name="datasetBlueprintName" [(ngModel)]="data.name">
</mat-form-field>
<div class="col-auto">
<button mat-button type="button" class="cancel-btn" (click)="cancel()">{{ data.cancelButton }}</button>
</div>
<div class="col"></div>
<div class="col-auto">
<button mat-button color="primary" class="next-btn" type="button" (click)="confirm()" [disabled]="!hasProfile()">{{ data.confirmButton }}</button>
<button mat-button color="primary" class="next-btn" type="button" (click)="confirm()" [disabled]="!hasBlueprint()">{{ data.confirmButton }}</button>
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More