Merge branch 'dmp-refactoring' of code-repo.d4science.org:MaDgiK-CITE/argos into dmp-refactoring

This commit is contained in:
Efstratios Giannopoulos 2024-06-12 11:42:38 +03:00
commit 626161bb3f
82 changed files with 1482 additions and 2090 deletions

View File

@ -25,6 +25,7 @@ queue:
enable: false
options:
exchange: null
annotation-created-topic: annotation.created
rabbitmq:
enable: false
interval-seconds: 3

View File

@ -2,41 +2,56 @@ package gr.cite.annotation.common;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
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.HashMap;
import java.util.Map;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class JsonHandlingService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(JsonHandlingService.class));
private final ObjectMapper objectMapper = new ObjectMapper();
private final ObjectMapper objectMapper;
public JsonHandlingService() {
this.objectMapper = new ObjectMapper();
this.objectMapper.registerModule(new JavaTimeModule());
}
public String toJson(Object item) throws JsonProcessingException {
if (item == null) return null;
return objectMapper.writeValueAsString(item);
return this.objectMapper.writeValueAsString(item);
}
public String toJsonSafe(Object item) {
if (item == null) return null;
try {
return objectMapper.writeValueAsString(item);
return this.objectMapper.writeValueAsString(item);
} catch (Exception ex) {
logger.error("Json Parsing Error: " + ex.getLocalizedMessage(), ex);
return null;
}
}
public <T> T fromJson(Class<T> type, String json) throws JsonProcessingException {
if (json == null) return null;
return objectMapper.readValue(json, type);
return this.objectMapper.readValue(json, type);
}
public HashMap<String, String> mapFromJson(String json) throws JsonProcessingException {
ObjectReader reader = this.objectMapper.readerFor(Map.class);
return reader.readValue(json);
}
public <T> T fromJsonSafe(Class<T> type, String json) {
if (json == null) return null;
try {
return objectMapper.readValue(json, type);
return this.objectMapper.readValue(json, type);
} catch (Exception ex) {
logger.error("Json Parsing Error: " + ex.getLocalizedMessage(), ex);
return null;
}
}

View File

@ -26,9 +26,11 @@ import java.util.UUID;
@ConditionalOnProperty(prefix = "queue.task.publisher", name = "enable", matchIfMissing = false)
public class OutboxIntegrationEventConfigurer extends OutboxConfigurer {
private final ApplicationContext applicationContext;
private final OutboxProperties outboxProperties;
public OutboxIntegrationEventConfigurer(ApplicationContext applicationContext) {
public OutboxIntegrationEventConfigurer(ApplicationContext applicationContext, OutboxProperties outboxProperties) {
this.applicationContext = applicationContext;
this.outboxProperties = outboxProperties;
}
@Bean
@ -64,7 +66,7 @@ public class OutboxIntegrationEventConfigurer extends OutboxConfigurer {
@Bean
public OutboxRepository outboxRepositoryCreator() {
return new OutboxRepositoryImpl(this.applicationContext);
return new OutboxRepositoryImpl(this.applicationContext, this.outboxProperties);
}
}

View File

@ -6,6 +6,8 @@ import gr.cite.rabbitmq.IntegrationEvent;
@JsonIgnoreProperties(ignoreUnknown = true)
public class OutboxIntegrationEvent extends IntegrationEvent {
public static final String ANNOTATION_CREATED = "ANNOTATION_CREATED";
private TrackedEvent event;
public TrackedEvent getEvent() {

View File

@ -7,13 +7,20 @@ public class OutboxProperties {
private final String exchange;
private final String annotationCreatedTopic;
public OutboxProperties(String exchange
) {
public OutboxProperties(String exchange,
String annotationCreatedTopic) {
this.exchange = exchange;
this.annotationCreatedTopic = annotationCreatedTopic;
}
public String getExchange() {
return exchange;
}
public String getAnnotationCreatedTopic() {
return annotationCreatedTopic;
}
}

View File

@ -33,12 +33,16 @@ public class OutboxRepositoryImpl implements OutboxRepository {
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
private final JsonHandlingService jsonHandlingService;
private final OutboxProperties outboxProperties;
public OutboxRepositoryImpl(
ApplicationContext applicationContext
) {
ApplicationContext applicationContext,
OutboxProperties outboxProperties) {
this.applicationContext = applicationContext;
this.jsonHandlingService = this.applicationContext.getBean(JsonHandlingService.class);
this.outboxProperties = outboxProperties;
}
@Override
@ -370,10 +374,35 @@ public class OutboxRepositoryImpl implements OutboxRepository {
private QueueOutboxEntity mapEvent(OutboxIntegrationEvent event) {
String routingKey;
switch (event.getType()) {
case OutboxIntegrationEvent.ANNOTATION_CREATED: {
routingKey = this.outboxProperties.getAnnotationCreatedTopic();
break;
}
default: {
logger.error("unrecognized outgoing integration event {}. Skipping...", event.getType());
return null;
}
}
UUID correlationId = UUID.randomUUID();
if (event.getEvent() != null)
event.getEvent().setTrackingContextTag(correlationId.toString());
event.setMessage(this.jsonHandlingService.toJsonSafe(event.getEvent()));
QueueOutboxEntity queueMessage = new QueueOutboxEntity();
queueMessage.setId(UUID.randomUUID());
queueMessage.setTenantId(event.getTenantId());
queueMessage.setExchange(this.outboxProperties.getExchange());
queueMessage.setRoute(routingKey);
queueMessage.setMessageId(event.getMessageId());
queueMessage.setMessage(this.jsonHandlingService.toJsonSafe(event));
queueMessage.setIsActive(IsActive.Active);
queueMessage.setNotifyStatus(QueueOutboxNotifyStatus.PENDING);
queueMessage.setRetryCount(0);
queueMessage.setCreatedAt(Instant.now());
queueMessage.setUpdatedAt(Instant.now());
return queueMessage;
}
}

View File

@ -0,0 +1,114 @@
package gr.cite.annotation.integrationevent.outbox.annotationcreated;
import gr.cite.annotation.common.enums.AnnotationProtectionType;
import gr.cite.annotation.integrationevent.TrackedEvent;
import java.time.Instant;
import java.util.UUID;
public class AnnotationCreatedEvent extends TrackedEvent {
private UUID id;
private UUID entityId;
private String entityType;
private String anchor;
private String payload;
private UUID subjectId;
private UUID threadId;
private UUID parentId;
private AnnotationProtectionType protectionType;
private Instant timeStamp;
public AnnotationCreatedEvent() {
}
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public String getEntityType() {
return entityType;
}
public void setEntityType(String entityType) {
this.entityType = entityType;
}
public String getAnchor() {
return anchor;
}
public void setAnchor(String anchor) {
this.anchor = anchor;
}
public String getPayload() {
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
public UUID getSubjectId() {
return subjectId;
}
public void setSubjectId(UUID subjectId) {
this.subjectId = subjectId;
}
public UUID getThreadId() {
return threadId;
}
public void setThreadId(UUID threadId) {
this.threadId = threadId;
}
public UUID getParentId() {
return parentId;
}
public void setParentId(UUID parentId) {
this.parentId = parentId;
}
public AnnotationProtectionType getProtectionType() {
return protectionType;
}
public void setProtectionType(AnnotationProtectionType protectionType) {
this.protectionType = protectionType;
}
public Instant getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(Instant timeStamp) {
this.timeStamp = timeStamp;
}
}

View File

@ -0,0 +1,7 @@
package gr.cite.annotation.integrationevent.outbox.annotationcreated;
import javax.management.InvalidApplicationException;
public interface AnnotationCreatedIntegrationEventHandler {
void handle(AnnotationCreatedEvent event) throws InvalidApplicationException;
}

View File

@ -0,0 +1,36 @@
package gr.cite.annotation.integrationevent.outbox.annotationcreated;
import gr.cite.annotation.common.scope.tenant.TenantScope;
import gr.cite.annotation.integrationevent.outbox.OutboxIntegrationEvent;
import gr.cite.annotation.integrationevent.outbox.OutboxService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;
import javax.management.InvalidApplicationException;
import java.util.UUID;
@Component
@RequestScope
public class AnnotationCreatedIntegrationEventHandlerImpl implements AnnotationCreatedIntegrationEventHandler {
private final OutboxService outboxService;
private final TenantScope tenantScope;
@Autowired
public AnnotationCreatedIntegrationEventHandlerImpl(
OutboxService outboxService, TenantScope tenantScope) {
this.outboxService = outboxService;
this.tenantScope = tenantScope;
}
@Override
public void handle(AnnotationCreatedEvent event) throws InvalidApplicationException {
OutboxIntegrationEvent message = new OutboxIntegrationEvent();
message.setMessageId(UUID.randomUUID());
message.setType(OutboxIntegrationEvent.ANNOTATION_CREATED);
message.setEvent(event);
if (this.tenantScope.isSet()) message.setTenantId(this.tenantScope.getTenant());
this.outboxService.publish(message);
}
}

View File

@ -13,7 +13,7 @@ import java.util.UUID;
public interface AnnotationService {
Annotation persist(AnnotationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException;
Annotation persist(AnnotationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException;
void deleteAndSave(UUID id) throws InvalidApplicationException;

View File

@ -8,6 +8,8 @@ import gr.cite.annotation.common.enums.IsActive;
import gr.cite.annotation.common.scope.user.UserScope;
import gr.cite.annotation.data.AnnotationEntity;
import gr.cite.annotation.data.TenantEntityManager;
import gr.cite.annotation.integrationevent.outbox.annotationcreated.AnnotationCreatedEvent;
import gr.cite.annotation.integrationevent.outbox.annotationcreated.AnnotationCreatedIntegrationEventHandler;
import gr.cite.annotation.model.Annotation;
import gr.cite.annotation.model.builder.AnnotationBuilder;
import gr.cite.annotation.model.deleter.AnnotationDeleter;
@ -30,9 +32,7 @@ import org.springframework.stereotype.Service;
import javax.management.InvalidApplicationException;
import java.time.Instant;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
import java.util.*;
@Service
public class AnnotationServiceImpl implements AnnotationService {
@ -52,12 +52,14 @@ public class AnnotationServiceImpl implements AnnotationService {
private final MessageSource messageSource;
private final AnnotationCreatedIntegrationEventHandler annotationCreatedEventHandler;
public AnnotationServiceImpl(
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
TenantEntityManager entityManager,
BuilderFactory builderFactory, UserScope userScope, AuthorizationContentResolver authorizationContentResolver, MessageSource messageSource) {
BuilderFactory builderFactory, UserScope userScope, AuthorizationContentResolver authorizationContentResolver, MessageSource messageSource, AnnotationCreatedIntegrationEventHandler annotationCreatedEventHandler) {
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.entityManager = entityManager;
@ -65,10 +67,11 @@ public class AnnotationServiceImpl implements AnnotationService {
this.userScope = userScope;
this.authorizationContentResolver = authorizationContentResolver;
this.messageSource = messageSource;
this.annotationCreatedEventHandler = annotationCreatedEventHandler;
}
@Override
public Annotation persist(AnnotationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException {
public Annotation persist(AnnotationPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException {
logger.debug(new MapLogEntry("persisting annotation").And("model", model).And("fields", fields));
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.entityAffiliation(model.getEntityId())), Permission.NewAnnotation);
@ -92,9 +95,30 @@ public class AnnotationServiceImpl implements AnnotationService {
this.entityManager.flush();
this.sendAnnotationCreatedEvent(data);
return this.builderFactory.builder(AnnotationBuilder.class).authorize(EnumSet.of(AuthorizationFlags.None)).build(BaseFieldSet.build(fields, Annotation._id), data);
}
private void sendAnnotationCreatedEvent(AnnotationEntity annotation) throws InvalidApplicationException {
AnnotationCreatedEvent event = new AnnotationCreatedEvent();
event.setId(annotation.getId());
event.setSubjectId(annotation.getSubjectId());
event.setEntityId(annotation.getEntityId());
event.setEntityType(annotation.getEntityType());
event.setAnchor(annotation.getAnchor());
event.setPayload(annotation.getPayload());
event.setThreadId(annotation.getThreadId());
event.setParentId(annotation.getParentId());
event.setProtectionType(annotation.getProtectionType());
event.setTimeStamp(Instant.now());
this.annotationCreatedEventHandler.handle(event);
}
@Override
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting Annotation: {}", id);

View File

@ -173,6 +173,8 @@ public class AuditableAction {
public static final EventId TenantConfiguration_Persist = new EventId(270002, "TenantConfiguration_Persist");
public static final EventId TenantConfiguration_Delete = new EventId(270003, "TenantConfiguration_Delete");
public static final EventId TenantConfiguration_LookupByType = new EventId(270004, "TenantConfiguration_LookupByType");
public static final EventId Annotation_Created_Notify = new EventId(280000, "Annotation_Created_Notify");

View File

@ -0,0 +1,31 @@
package org.opencdmp.commons.enums.annotation;
import com.fasterxml.jackson.annotation.JsonValue;
import org.opencdmp.commons.enums.EnumUtils;
import org.opencdmp.data.converters.enums.DatabaseEnum;
import java.util.Map;
public enum AnnotationProtectionType implements DatabaseEnum<Short> {
Private((short) 0),
EntityAccessors((short) 1);
private final Short value;
AnnotationProtectionType(Short value) {
this.value = value;
}
@JsonValue
public Short getValue() {
return value;
}
private static final Map<Short, AnnotationProtectionType> map = EnumUtils.getEnumValueMap(AnnotationProtectionType.class);
public static AnnotationProtectionType of(Short i) {
return map.get(i);
}
}

View File

@ -13,6 +13,7 @@ public class NotificationProperties {
private UUID dmpFinalisedType;
private UUID descriptionModifiedType;
private UUID descriptionFinalisedType;
private UUID descriptionAnnotationCreated;
private UUID mergeAccountConfirmationType;
private UUID removeCredentialConfirmationType;
private UUID dmpDepositType;
@ -133,4 +134,12 @@ public class NotificationProperties {
public void setContactSupportEmail(String contactSupportEmail) {
this.contactSupportEmail = contactSupportEmail;
}
public UUID getDescriptionAnnotationCreated() {
return descriptionAnnotationCreated;
}
public void setDescriptionAnnotationCreated(UUID descriptionAnnotationCreated) {
this.descriptionAnnotationCreated = descriptionAnnotationCreated;
}
}

View File

@ -9,6 +9,7 @@ import gr.cite.rabbitmq.consumer.InboxCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
@ -19,21 +20,23 @@ import java.util.List;
@ConditionalOnProperty(prefix = "queue.rabbitmq", name = "listenerEnabled")
public class AppRabbitConfigurer extends RabbitConfigurer {
private ApplicationContext applicationContext;
private final ApplicationContext applicationContext;
private final InboxProperties inboxProperties;
public AppRabbitConfigurer(ApplicationContext applicationContext) {
public AppRabbitConfigurer(ApplicationContext applicationContext, InboxProperties inboxProperties) {
this.applicationContext = applicationContext;
this.inboxProperties = inboxProperties;
}
// @Bean
@Bean
public InboxBindings inboxBindingsCreator() {
List<String> bindingItems = new ArrayList<>();
bindingItems.addAll(this.inboxProperties.getAnnotationCreatedTopic());
return new InboxBindings(bindingItems);
}
// @Bean
@Bean(name = "InboxCreator")
public InboxCreator inboxCreator() {
return (params) -> {
InboxRepository inboxRepository = this.applicationContext.getBean(InboxRepository.class);

View File

@ -4,6 +4,8 @@ import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import java.util.List;
@Validated
@ConfigurationProperties(prefix = "queue.task.listener.options")
//@ConstructorBinding
@ -12,14 +14,19 @@ public class InboxProperties {
@NotNull
private final String exchange;
private final List<String> annotationCreatedTopic;
public InboxProperties(
String exchange
) {
String exchange, List<String> annotationCreatedTopic) {
this.exchange = exchange;
this.annotationCreatedTopic = annotationCreatedTopic;
}
public String getExchange() {
return exchange;
}
public List<String> getAnnotationCreatedTopic() {
return annotationCreatedTopic;
}
}

View File

@ -15,6 +15,7 @@ import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.fake.FakeRequestScope;
import org.opencdmp.data.QueueInboxEntity;
import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.integrationevent.inbox.annotationentitycreated.AnnotationEntityCreatedIntegrationEventHandler;
import org.opencdmp.query.QueueInboxQuery;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
@ -359,12 +360,12 @@ public class InboxRepositoryImpl implements InboxRepository {
private EventProcessingStatus processMessage(QueueInboxEntity queueInboxMessage) {
IntegrationEventHandler handler = null;
logger.debug("Processing message with routing key '{}'", queueInboxMessage.getRoute());
// if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getTenantRemovalTopic()))
// handler = this.applicationContext.getBean(TenantRemovalIntegrationEventHandler.class);
// else {
// logger.error("No handler found for message routing key '{}'. Discarding.", queueInboxMessage.getRoute());
// handler = null;
// }
if (this.routingKeyMatched(queueInboxMessage.getRoute(), this.inboxProperties.getAnnotationCreatedTopic()))
handler = this.applicationContext.getBean(AnnotationEntityCreatedIntegrationEventHandler.class);
else {
logger.error("No handler found for message routing key '{}'. Discarding.", queueInboxMessage.getRoute());
handler = null;
}
if (handler == null)
return EventProcessingStatus.Discard;

View File

@ -1,7 +1,9 @@
package org.opencdmp.integrationevent.inbox;
import javax.management.InvalidApplicationException;
public interface IntegrationEventHandler {
EventProcessingStatus handle(IntegrationEventProperties properties, String message);
EventProcessingStatus handle(IntegrationEventProperties properties, String message) throws InvalidApplicationException;
}

View File

@ -0,0 +1,189 @@
package org.opencdmp.integrationevent.inbox.annotationentitycreated;
import gr.cite.tools.validation.ValidatorFactory;
import gr.cite.tools.validation.specification.Specification;
import org.opencdmp.commons.enums.annotation.AnnotationProtectionType;
import org.opencdmp.commons.validation.BaseValidator;
import org.opencdmp.convention.ConventionService;
import org.opencdmp.errorcode.ErrorThesaurusProperties;
import org.opencdmp.integrationevent.TrackedEvent;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class AnnotationEntityCreatedIntegrationEvent extends TrackedEvent {
private UUID id;
public static final String _id = "id";
private UUID entityId;
public static final String _entityId = "entityId";
private String entityType;
public static final String _entityType = "entityType";
private String anchor;
public static final String _anchor = "anchor";
private String payload;
public static final String _payload = "payload";
private UUID subjectId;
public static final String _subjectId = "subjectId";
private UUID threadId;
public static final String _threadId = "threadId";
private UUID parentId;
public static final String _parentId = "parentId";
private AnnotationProtectionType protectionType;
public static final String _protectionType = "protectionType";
private Instant timeStamp;
public static final String _timeStamp = "timeStamp";
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
public UUID getEntityId() {
return entityId;
}
public void setEntityId(UUID entityId) {
this.entityId = entityId;
}
public String getEntityType() {
return entityType;
}
public void setEntityType(String entityType) {
this.entityType = entityType;
}
public String getAnchor() {
return anchor;
}
public void setAnchor(String anchor) {
this.anchor = anchor;
}
public String getPayload() {
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
public UUID getSubjectId() {
return subjectId;
}
public void setSubjectId(UUID subjectId) {
this.subjectId = subjectId;
}
public UUID getThreadId() {
return threadId;
}
public void setThreadId(UUID threadId) {
this.threadId = threadId;
}
public UUID getParentId() {
return parentId;
}
public void setParentId(UUID parentId) {
this.parentId = parentId;
}
public AnnotationProtectionType getProtectionType() {
return protectionType;
}
public void setProtectionType(AnnotationProtectionType protectionType) {
this.protectionType = protectionType;
}
public Instant getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(Instant timeStamp) {
this.timeStamp = timeStamp;
}
@Component(AnnotationEntityCreatedIntegrationEventValidator.ValidatorName)
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public static class AnnotationEntityCreatedIntegrationEventValidator extends BaseValidator<AnnotationEntityCreatedIntegrationEvent> {
public static final String ValidatorName = "AnnotationEntityCreatedIntegrationEventValidator";
private final MessageSource messageSource;
private final ValidatorFactory validatorFactory;
protected AnnotationEntityCreatedIntegrationEventValidator(ConventionService conventionService, ErrorThesaurusProperties errors, MessageSource messageSource, ValidatorFactory validatorFactory) {
super(conventionService, errors);
this.messageSource = messageSource;
this.validatorFactory = validatorFactory;
}
@Override
protected Class<AnnotationEntityCreatedIntegrationEvent> modelClass() {
return AnnotationEntityCreatedIntegrationEvent.class;
}
@Override
protected List<Specification> specifications(AnnotationEntityCreatedIntegrationEvent item) {
return Arrays.asList(
this.spec()
.must(() -> this.isValidGuid(item.getId()))
.failOn(AnnotationEntityCreatedIntegrationEvent._id).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityCreatedIntegrationEvent._id}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> this.isValidGuid(item.getSubjectId()))
.failOn(AnnotationEntityCreatedIntegrationEvent._subjectId).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityCreatedIntegrationEvent._subjectId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isNull(item.getEntityId()))
.failOn(AnnotationEntityCreatedIntegrationEvent._entityId).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityCreatedIntegrationEvent._entityId}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> !this.isNull(item.getEntityId()))
.must(() -> this.isValidGuid(item.getEntityId()))
.failOn(AnnotationEntityCreatedIntegrationEvent._entityId).failWith(messageSource.getMessage("validation.invalidid", new Object[]{AnnotationEntityCreatedIntegrationEvent._entityId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isEmpty(item.getEntityType()))
.failOn(AnnotationEntityCreatedIntegrationEvent._entityType).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityCreatedIntegrationEvent._entityType}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isEmpty(item.getPayload()))
.failOn(AnnotationEntityCreatedIntegrationEvent._payload).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityCreatedIntegrationEvent._payload}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> !this.isNull(item.getThreadId()))
.must(() -> this.isValidGuid(item.getThreadId()))
.failOn(AnnotationEntityCreatedIntegrationEvent._threadId).failWith(messageSource.getMessage("Validation_UnexpectedValue", new Object[]{AnnotationEntityCreatedIntegrationEvent._threadId}, LocaleContextHolder.getLocale())),
this.spec()
.iff(() -> !this.isNull(item.getParentId()))
.must(() -> this.isValidGuid(item.getParentId()))
.failOn(AnnotationEntityCreatedIntegrationEvent._parentId).failWith(messageSource.getMessage("Validation_UnexpectedValue", new Object[]{AnnotationEntityCreatedIntegrationEvent._parentId}, LocaleContextHolder.getLocale())),
this.spec()
.must(() -> !this.isNull(item.getProtectionType()))
.failOn(AnnotationEntityCreatedIntegrationEvent._protectionType).failWith(messageSource.getMessage("Validation_Required", new Object[]{AnnotationEntityCreatedIntegrationEvent._protectionType}, LocaleContextHolder.getLocale()))
);
}
}
}

View File

@ -0,0 +1,9 @@
package org.opencdmp.integrationevent.inbox.annotationentitycreated;
import org.opencdmp.integrationevent.inbox.IntegrationEventHandler;
import javax.management.InvalidApplicationException;
import java.util.UUID;
public interface AnnotationEntityCreatedIntegrationEventHandler extends IntegrationEventHandler {
}

View File

@ -0,0 +1,178 @@
package org.opencdmp.integrationevent.inbox.annotationentitycreated;
import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorProperties;
import gr.cite.tools.auditing.AuditService;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.validation.ValidatorFactory;
import org.opencdmp.audit.AuditableAction;
import org.opencdmp.commons.JsonHandlingService;
import org.opencdmp.commons.enums.IsActive;
import org.opencdmp.commons.notification.NotificationProperties;
import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.commons.scope.user.UserScope;
import org.opencdmp.commons.types.notification.DataType;
import org.opencdmp.commons.types.notification.FieldInfo;
import org.opencdmp.commons.types.notification.NotificationFieldData;
import org.opencdmp.data.*;
import org.opencdmp.errorcode.ErrorThesaurusProperties;
import org.opencdmp.integrationevent.inbox.EventProcessingStatus;
import org.opencdmp.integrationevent.inbox.InboxPrincipal;
import org.opencdmp.integrationevent.inbox.IntegrationEventProperties;
import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEvent;
import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import org.opencdmp.model.Tenant;
import org.opencdmp.model.description.Description;
import org.opencdmp.query.DescriptionQuery;
import org.opencdmp.query.DmpUserQuery;
import org.opencdmp.query.TenantQuery;
import org.opencdmp.query.UserQuery;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Scope;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class AnnotationEntityCreatedIntegrationEventHandlerImpl implements AnnotationEntityCreatedIntegrationEventHandler {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(AnnotationEntityCreatedIntegrationEventHandlerImpl.class));
private final QueryFactory queryFactory;
private final JsonHandlingService jsonHandlingService;
private final NotificationProperties notificationProperties;
private final TenantScope tenantScope;
private final NotifyIntegrationEventHandler notifyIntegrationEventHandler;
private final CurrentPrincipalResolver currentPrincipalResolver;
private final ClaimExtractorProperties claimExtractorProperties;
private final MessageSource messageSource;
private final UserScope userScope;
private final ErrorThesaurusProperties errors;
private final TenantEntityManager tenantEntityManager;
private final ValidatorFactory validatorFactory;
private final AuditService auditService;
public AnnotationEntityCreatedIntegrationEventHandlerImpl(QueryFactory queryFactory, JsonHandlingService jsonHandlingService, NotificationProperties notificationProperties, TenantScope tenantScope, NotifyIntegrationEventHandler notifyIntegrationEventHandler, CurrentPrincipalResolver currentPrincipalResolver, ClaimExtractorProperties claimExtractorProperties, MessageSource messageSource, UserScope userScope, ErrorThesaurusProperties errors, TenantEntityManager tenantEntityManager, ValidatorFactory validatorFactory, AuditService auditService) {
this.queryFactory = queryFactory;
this.jsonHandlingService = jsonHandlingService;
this.notificationProperties = notificationProperties;
this.tenantScope = tenantScope;
this.notifyIntegrationEventHandler = notifyIntegrationEventHandler;
this.currentPrincipalResolver = currentPrincipalResolver;
this.claimExtractorProperties = claimExtractorProperties;
this.messageSource = messageSource;
this.userScope = userScope;
this.errors = errors;
this.tenantEntityManager = tenantEntityManager;
this.validatorFactory = validatorFactory;
this.auditService = auditService;
}
@Override
public EventProcessingStatus handle(IntegrationEventProperties properties, String message) throws InvalidApplicationException {
AnnotationEntityCreatedIntegrationEvent event = this.jsonHandlingService.fromJsonSafe(AnnotationEntityCreatedIntegrationEvent.class, message);
if (event == null)
return EventProcessingStatus.Error;
logger.debug("Handling {}", AnnotationEntityCreatedIntegrationEvent.class.getSimpleName());
this.validatorFactory.validator(AnnotationEntityCreatedIntegrationEvent.AnnotationEntityCreatedIntegrationEventValidator.class).validateForce(event);
EventProcessingStatus status = EventProcessingStatus.Success;
try {
if (this.tenantScope.isMultitenant() && properties.getTenantId() != null) {
TenantEntity tenant = queryFactory.query(TenantQuery.class).disableTracking().ids(properties.getTenantId()).firstAs(new BaseFieldSet().ensure(Tenant._id).ensure(Tenant._code));
if (tenant == null) {
logger.error("missing tenant from event message");
return EventProcessingStatus.Error;
}
this.tenantScope.setTempTenant(tenantEntityManager, properties.getTenantId(), tenant.getCode());
} else if (this.tenantScope.isMultitenant()) {
this.tenantScope.setTempTenant(tenantEntityManager, null, this.tenantScope.getDefaultTenantCode());
}
currentPrincipalResolver.push(InboxPrincipal.build(properties, claimExtractorProperties));
this.sendNotification(event);
auditService.track(AuditableAction.Annotation_Created_Notify, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", event)
));
} catch (Exception ex) {
status = EventProcessingStatus.Error;
logger.error("Problem getting list of queue outbox. Skipping: {}", ex.getMessage(), ex);
} finally {
currentPrincipalResolver.pop();
try {
tenantScope.removeTempTenant(this.tenantEntityManager);
this.tenantEntityManager.reloadTenantFilters();
} catch (InvalidApplicationException e) {
}
}
return status;
}
private void sendNotification(AnnotationEntityCreatedIntegrationEvent event) throws InvalidApplicationException {
DescriptionEntity descriptionEntity = this.queryFactory.query(DescriptionQuery.class).disableTracking().ids(event.getEntityId()).first();
if (descriptionEntity == null) throw new MyNotFoundException(this.messageSource.getMessage("General_ItemNotFound", new Object[]{event.getEntityId(), Description.class.getSimpleName()}, LocaleContextHolder.getLocale()));
List<DmpUserEntity> existingUsers = this.queryFactory.query(DmpUserQuery.class).disableTracking()
.dmpIds(descriptionEntity.getDmpId())
.isActives(IsActive.Active)
.collect();
if (existingUsers == null || existingUsers.size() <= 1){
return;
}
UserEntity sender = this.queryFactory.query(UserQuery.class).disableTracking().ids(event.getSubjectId()).first();
if (sender == null) throw new MyApplicationException("Sender user not found");
for (DmpUserEntity dmpUser : existingUsers) {
if (!dmpUser.getUserId().equals(event.getSubjectId())){
UserEntity user = this.queryFactory.query(UserQuery.class).disableTracking().ids(dmpUser.getUserId()).first();
if (user == null || user.getIsActive().equals(IsActive.Inactive)) throw new MyValidationException(this.errors.getDmpInactiveUser().getCode(), this.errors.getDmpInactiveUser().getMessage());
this.createAnnotationNotificationEvent(user, descriptionEntity, sender.getName());
}
}
}
private void createAnnotationNotificationEvent(UserEntity user, DescriptionEntity description, String reasonName) throws InvalidApplicationException, InvalidApplicationException {
NotifyIntegrationEvent notifyIntegrationEvent = new NotifyIntegrationEvent();
notifyIntegrationEvent.setUserId(user.getId());
notifyIntegrationEvent.setNotificationType(notificationProperties.getDescriptionAnnotationCreated());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();
fieldInfoList.add(new FieldInfo("{recipient}", DataType.String, user.getName()));
fieldInfoList.add(new FieldInfo("{reasonName}", DataType.String, reasonName));
fieldInfoList.add(new FieldInfo("{name}", DataType.String, description.getLabel()));
fieldInfoList.add(new FieldInfo("{id}", DataType.String, description.getId().toString()));
if(this.tenantScope.getTenantCode() != null && !this.tenantScope.getTenantCode().equals(this.tenantScope.getDefaultTenantCode())){
fieldInfoList.add(new FieldInfo("{tenant-url-path}", DataType.String, String.format("/t/%s", this.tenantScope.getTenantCode())));
}
data.setFields(fieldInfoList);
notifyIntegrationEvent.setData(this.jsonHandlingService.toJsonSafe(data));
this.notifyIntegrationEventHandler.handle(notifyIntegrationEvent);
}
}

View File

@ -345,15 +345,7 @@ public class DepositServiceImpl implements DepositService {
private void createDmpDepositNotificationEvent(DmpEntity dmp, UserEntity user) throws InvalidApplicationException {
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setUserId(user.getId());
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).disableTracking().userIds(user.getId());
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
if (query.count() == 0) return;
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(this.jsonHandlingService.toJsonSafe(contactData));
event.setNotificationType(this.notificationProperties.getDmpDepositType());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();

View File

@ -388,16 +388,6 @@ public class DescriptionServiceImpl implements DescriptionService {
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setUserId(user.getId());
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).disableTracking().userIds(user.getId());
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
if (query.count() == 0) throw new MyValidationException(this.errors.getDmpMissingUserContactInfo().getCode(), this.errors.getDmpMissingUserContactInfo().getMessage());
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(this.jsonHandlingService.toJsonSafe(contactData));
this.applyNotificationType(description.getStatus(), event);
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();

View File

@ -278,14 +278,6 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
if (user.getIsActive().equals(IsActive.Inactive)) throw new MyValidationException(this.errors.getDescriptionTemplateInactiveUser().getCode(), this.errors.getDescriptionTemplateInactiveUser().getMessage());
event.setUserId(user.getId());
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).disableTracking().userIds(user.getId());
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
if (query.count() == 0) throw new MyValidationException(this.errors.getDescriptionTemplateMissingUserContactInfo().getCode(), this.errors.getDescriptionTemplateMissingUserContactInfo().getMessage());
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(this.jsonHandlingService.toJsonSafe(contactData));
event.setNotificationType(this.notificationProperties.getDescriptionTemplateInvitationType());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();

View File

@ -69,7 +69,6 @@ import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEvent;
import org.opencdmp.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
import org.opencdmp.model.DmpUser;
import org.opencdmp.model.DmpValidationResult;
import org.opencdmp.model.UserContactInfo;
import org.opencdmp.model.builder.DmpUserBuilder;
import org.opencdmp.model.builder.description.DescriptionBuilder;
import org.opencdmp.model.builder.dmp.DmpBuilder;
@ -313,15 +312,6 @@ public class DmpServiceImpl implements DmpService {
private void createDmpNotificationEvent(DmpEntity dmp, UserEntity user) throws InvalidApplicationException {
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setUserId(user.getId());
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).disableTracking().userIds(user.getId());
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
if (query.count() == 0) throw new MyValidationException(this.errors.getDmpMissingUserContactInfo().getCode(), this.errors.getDmpMissingUserContactInfo().getMessage());
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(this.jsonHandlingService.toJsonSafe(contactData));
this.applyNotificationType(dmp.getStatus(), event);
NotificationFieldData data = new NotificationFieldData();
@ -1493,10 +1483,6 @@ public class DmpServiceImpl implements DmpService {
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setUserId(recipient.getId());
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(this.jsonHandlingService.toJsonSafe(contactData));
event.setNotificationType(this.notificationProperties.getDmpInvitationExistingUserType());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();

View File

@ -525,11 +525,7 @@ public class UserServiceImpl implements UserService {
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
event.setUserId(user.getId());
List<ContactPair> contactPairs = new ArrayList<>();
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
event.setContactHint(this.jsonHandlingService.toJsonSafe(contactData));
event.setContactTypeHint(NotificationContactType.EMAIL);
event.setNotificationType(this.notificationProperties.getMergeAccountConfirmationType());
NotificationFieldData data = new NotificationFieldData();
List<FieldInfo> fieldInfoList = new ArrayList<>();

View File

@ -63,8 +63,10 @@ public class PrincipalController {
BaseFieldSet.asIndexer(Account._profile, Account.UserProfileInfo._language),
BaseFieldSet.asIndexer(Account._profile, Account.UserProfileInfo._culture),
BaseFieldSet.asIndexer(Account._profile, Account.UserProfileInfo._timezone),
Account._roles,
Account._permissions);
Account._permissions,
BaseFieldSet.asIndexer(Account._selectedTenant, Tenant._id),
BaseFieldSet.asIndexer(Account._selectedTenant, Tenant._name),
BaseFieldSet.asIndexer(Account._selectedTenant, Tenant._code));
}
MyPrincipal principal = this.currentPrincipalResolver.currentPrincipal();

View File

@ -301,9 +301,7 @@ public class UserController {
public Boolean getUserTokenPermission(@PathVariable("token") String token) throws InvalidApplicationException, IOException {
logger.debug(new MapLogEntry("allow merge account to user").And("token", token));
this.auditService.track(AuditableAction.User_AllowMergeAccount, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("token", token)
));
this.auditService.track(AuditableAction.User_AllowMergeAccount);
return this.userTypeService.doesTokenBelongToLoggedInUser(token);
}

View File

@ -1,6 +1,8 @@
package org.opencdmp.models;
import gr.cite.tools.logging.annotation.LogSensitive;
import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.model.Tenant;
import java.time.Instant;
import java.util.List;
@ -187,6 +189,9 @@ public class Account {
public final static String _permissions = "permissions";
private List<String> permissions;
public final static String _selectedTenant = "selectedTenant";
public Tenant selectedTenant;
public PrincipalInfo getPrincipal() {
return principal;
}
@ -195,22 +200,10 @@ public class Account {
this.principal = principal;
}
public final static String _roles = "roles";
private List<String> roles;
public Boolean getAuthenticated() {
return isAuthenticated;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
public void setAuthenticated(Boolean authenticated) {
isAuthenticated = authenticated;
}
@ -230,4 +223,12 @@ public class Account {
public void setProfile(UserProfileInfo profile) {
this.profile = profile;
}
public Tenant getSelectedTenant() {
return selectedTenant;
}
public void setSelectedTenant(Tenant selectedTenant) {
this.selectedTenant = selectedTenant;
}
}

View File

@ -6,19 +6,30 @@ import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver;
import gr.cite.commons.web.oidc.principal.MyPrincipal;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor;
import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractorKeys;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import org.opencdmp.commons.JsonHandlingService;
import org.opencdmp.commons.scope.tenant.TenantScope;
import org.opencdmp.commons.scope.user.UserScope;
import org.opencdmp.commons.types.user.AdditionalInfoEntity;
import org.opencdmp.data.DmpEntity;
import org.opencdmp.data.TenantEntityManager;
import org.opencdmp.data.UserEntity;
import org.opencdmp.model.builder.BaseBuilder;
import org.opencdmp.model.builder.TenantBuilder;
import org.opencdmp.model.builder.dmpreference.DmpReferenceBuilder;
import org.opencdmp.model.dmp.Dmp;
import org.opencdmp.query.DmpReferenceQuery;
import org.opencdmp.query.TenantQuery;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.management.InvalidApplicationException;
import java.util.*;
import java.util.stream.Collectors;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@ -31,13 +42,20 @@ public class AccountBuilder {
private final JsonHandlingService jsonHandlingService;
private final UserScope userScope;
private final TenantEntityManager entityManager;
public AccountBuilder(ClaimExtractor claimExtractor, CurrentPrincipalResolver currentPrincipalResolver, AuthorizationConfiguration authorizationConfiguration, JsonHandlingService jsonHandlingService, UserScope userScope, TenantEntityManager entityManager) {
private final TenantScope tenantScope;
private final QueryFactory queryFactory;
private final BuilderFactory builderFactory;
public AccountBuilder(ClaimExtractor claimExtractor, CurrentPrincipalResolver currentPrincipalResolver, AuthorizationConfiguration authorizationConfiguration, JsonHandlingService jsonHandlingService, UserScope userScope, TenantEntityManager entityManager, TenantScope tenantScope, QueryFactory queryFactory, BuilderFactory builderFactory) {
this.claimExtractor = claimExtractor;
this.currentPrincipalResolver = currentPrincipalResolver;
this.authorizationConfiguration = authorizationConfiguration;
this.jsonHandlingService = jsonHandlingService;
this.userScope = userScope;
this.entityManager = entityManager;
this.tenantScope = tenantScope;
this.queryFactory = queryFactory;
this.builderFactory = builderFactory;
this.excludeMoreClaim = Set.of(
ClaimExtractorKeys.Subject,
ClaimExtractorKeys.Name,
@ -90,10 +108,6 @@ public class AccountBuilder {
model.getPrincipal().getMore().get(key).addAll(values);
}
}
if (fields.hasField(Account._roles)) {
List<String> roles = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal());
model.setRoles(roles);
}
if (fields.hasField(Account._permissions)) {
List<String> roles = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal());
Set<String> permissions = this.authorizationConfiguration.permissionsOfRoles(roles);
@ -105,6 +119,15 @@ public class AccountBuilder {
model.setPermissions(new ArrayList<>(permissions));
}
FieldSet selectedTenantFields = fields.extractPrefixed(BaseFieldSet.asIndexerPrefix(Account._selectedTenant));
if (!selectedTenantFields.isEmpty() && this.tenantScope.isSet()) {
if (!this.tenantScope.getTenantCode().equalsIgnoreCase(this.tenantScope.getDefaultTenantCode())) {
TenantQuery query = this.queryFactory.query(TenantQuery.class).disableTracking().ids(this.tenantScope.getTenant());
model.setSelectedTenant(this.builderFactory.builder(TenantBuilder.class).build(selectedTenantFields, query.first()));
}
}
FieldSet profileFields = fields.extractPrefixed(BaseFieldSet.asIndexerPrefix(Account._profile));
if (!profileFields.isEmpty() && this.userScope.getUserIdSafe() != null){
model.setProfile(new Account.UserProfileInfo());

View File

@ -5,6 +5,7 @@ notification:
dmpFinalisedType: 90DB0B46-42DE-BD89-AEBF-6F27EFEB256E
descriptionModifiedType: 4FDBFA80-7A71-4A69-B854-67CBB70648F1
descriptionFinalisedType: 33790bad-94d4-488a-8ee2-7f6295ca18ea
descriptionAnnotationCreated: db1e99d2-a240-4e75-9bb2-ef25b234c1f0
mergeAccountConfirmationType: BFE68845-CB05-4C5A-A03D-29161A7C9660
removeCredentialConfirmationType: C9BC3F16-057E-4BBA-8A5F-36BD835E5604
dmpDepositType: 55736F7A-83AB-4190-AF43-9D031A6F9612

View File

@ -54,6 +54,7 @@ queue:
enable: true
options:
exchange: null
annotation-created-topic: annotation.created
rabbitmq:
enable: true
interval-seconds: 3

View File

@ -1,13 +1,13 @@
import { AppRole } from "@app/core/common/enum/app-role";
import { AppPermission } from "@app/core/common/enum/permission.enum";
import { Guid } from "@common/types/guid";
import { Tenant } from "../tenant/tenant";
export interface AppAccount {
isAuthenticated: boolean;
roles: AppRole[];
permissions: AppPermission[];
principal: AppPrincipalInfo;
profile: UserProfileInfo;
selectedTenant: Tenant;
}
export interface AppPrincipalInfo {

View File

@ -2,11 +2,12 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable, NgZone } from '@angular/core';
import { Router } from '@angular/router';
import { AppRole } from '@app/core/common/enum/app-role';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { AppAccount } from '@app/core/model/auth/principal';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { BaseService } from '@common/base/base.service';
import { BaseHttpParams } from '@common/http/base-http-params';
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { KeycloakEventType, KeycloakService } from 'keycloak-angular';
@ -14,12 +15,9 @@ import { Observable, Subject, forkJoin, from, of } from 'rxjs';
import { exhaustMap, map, takeUntil } from 'rxjs/operators';
import { ConfigurationService } from '../configuration/configuration.service';
import { PrincipalService } from '../http/principal.service';
import { BaseHttpParams } from '@common/http/base-http-params';
import { InterceptorType } from '@common/http/interceptors/interceptor-type';
import { TenantHandlingService } from '../tenant/tenant-handling.service';
export interface ResolutionContext {
roles: AppRole[];
permissions: AppPermission[];
}
export interface AuthenticationState {
@ -194,6 +192,7 @@ export class AuthService extends BaseService {
map(
(myTenants) => {
if (myTenants) {
if (myTenants.some(x => x.code.toLocaleLowerCase() == tenantCode.toLocaleLowerCase())) {
this.selectedTenant(tenantCode);
} else {
@ -228,12 +227,13 @@ export class AuthService extends BaseService {
return null;
}
public getRoles(): AppRole[] {
if (this.appAccount && this.appAccount.roles) {
return this.appAccount.roles;
public getSelectedTenantName(): string {
if (this.appAccount && this.appAccount.selectedTenant) {
return this.appAccount.selectedTenant.name;
}
return null;
}
public getUserProfileEmail(): string {
if (this.appAccount && this.appAccount.profile) {
return this.appAccount.profile.email;
@ -248,28 +248,6 @@ export class AuthService extends BaseService {
return null;
}
public hasAnyRole(roles: AppRole[]): boolean {
if (!roles) {
return false;
}
return roles.filter((r) => this.hasRole(r)).length > 0;
}
public hasRole(role: AppRole): boolean {
if (role === undefined) {
return false;
}
if (
!this.appAccount ||
!this.appAccount.roles ||
this.appAccount.roles.length === 0
) {
return false;
}
return this.appAccount.roles
.includes(role);
}
public getUserProfileCulture(): string {
if (this.appAccount && this.appAccount.profile) {
return this.appAccount.profile.culture;
@ -388,7 +366,6 @@ export class AuthService extends BaseService {
}
private evaluatePermission(availablePermissions: string[], permissionToCheck: string): boolean {
if (!permissionToCheck) { return false; }
// if (this.hasRole(AppRole.Admin)) { return true; }
return availablePermissions.map(x => x.toLowerCase()).includes(permissionToCheck.toLowerCase());
}
public hasAnyPermission(permissions: AppPermission[]): boolean {
@ -398,19 +375,14 @@ export class AuthService extends BaseService {
public authorize(context: ResolutionContext): boolean {
if (!context || this.hasRole(AppRole.Admin)) { return true; }
let roleAuthorized = false;
if (context.roles && context.roles.length > 0) {
roleAuthorized = this.hasAnyRole(context.roles);
}
if (!context) { return true; }
let permissionAuthorized = false;
if (context.permissions && context.permissions.length > 0) {
permissionAuthorized = this.hasAnyPermission(context.permissions);
}
if (roleAuthorized || permissionAuthorized) { return true; }
if (permissionAuthorized) { return true; }
return false;
}

View File

@ -1,15 +1,14 @@
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 { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { Guid } from '@common/types/guid';
import moment from 'moment';
import { Moment } from 'moment';
import moment, { 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';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
export enum UserSettingsType {
Setting = 0,
@ -117,7 +116,7 @@ export class UserSettingsService extends BaseService {
this.persistUserSettings(userSettingsInformation.key, result, userSettingsInformation, false);
this.userSettingUpdated.next(userSettingsInformation.key);
},
error => this.httpErrorHandlingService.handleBackedRequestError(error));
error => this.httpErrorHandlingService.handleBackedRequestError(error));
}
private buildUserSettings<T>(setting: UserSetting<T>, key: string): UserSettings<T> {
@ -264,7 +263,7 @@ export class UserSettingsService extends BaseService {
});
}
},
error => this.httpErrorHandlingService.handleBackedRequestError(error));
error => this.httpErrorHandlingService.handleBackedRequestError(error));
}
private clearSetting(key: string) {
@ -289,24 +288,6 @@ export class UserSettingsService extends BaseService {
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();
}

View File

@ -1,35 +1,34 @@
import { Injectable } from '@angular/core';
import { AnnotationProtectionType } from '@app/core/common/enum/annotation-protection-type.enum';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DescriptionTemplateExternalSelectAuthType } from '@app/core/common/enum/description-template-external-select-auth-type';
import { DescriptionTemplateExternalSelectHttpMethodType } from '@app/core/common/enum/description-template-external-select-http-method-type';
import { DescriptionTemplateFieldDataExternalDatasetType } from '@app/core/common/enum/description-template-field-data-external-dataset-type';
import { DescriptionTemplateFieldType } from '@app/core/common/enum/description-template-field-type';
import { DescriptionTemplateStatus } from '@app/core/common/enum/description-template-status';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
import { DmpAccessType } from '@app/core/common/enum/dmp-access-type';
import { DmpBlueprintFieldCategory } from '@app/core/common/enum/dmp-blueprint-field-category';
import { DmpBlueprintStatus } from '@app/core/common/enum/dmp-blueprint-status';
import { DmpBlueprintSystemFieldType } from '@app/core/common/enum/dmp-blueprint-system-field-type';
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
import { DmpUserType } from '@app/core/common/enum/dmp-user-type';
import { ExternalFetcherApiHTTPMethodType } from '@app/core/common/enum/external-fetcher-api-http-method-type';
import { ExternalFetcherSourceType } from '@app/core/common/enum/external-fetcher-source-type';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { PrefillingSourceSystemTargetType } from '@app/core/common/enum/prefilling-source-system-target-type';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type';
import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type';
import { ExternalFetcherApiHTTPMethodType } from '@app/core/common/enum/external-fetcher-api-http-method-type';
import { ExternalFetcherSourceType } from '@app/core/common/enum/external-fetcher-source-type';
import { RoleOrganizationType } from '@app/core/common/enum/role-organization-type';
import { SupportiveMaterialFieldType } from '@app/core/common/enum/supportive-material-field-type';
import { UserDescriptionTemplateRole } from '@app/core/common/enum/user-description-template-role';
import { TranslateService } from '@ngx-translate/core';
import { AppRole } from '../../common/enum/app-role';
import { DmpBlueprintExtraFieldDataType } 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 { DescriptionTemplateExternalSelectAuthType } from '@app/core/common/enum/description-template-external-select-auth-type';
import { DmpBlueprintFieldCategory } from '@app/core/common/enum/dmp-blueprint-field-category';
import { DmpUserType } from '@app/core/common/enum/dmp-user-type';
import { PrefillingSourceSystemTargetType } from '@app/core/common/enum/prefilling-source-system-target-type';
import { AnnotationProtectionType } from '@app/core/common/enum/annotation-protection-type.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
@Injectable()
export class EnumUtils {
@ -227,12 +226,8 @@ export class EnumUtils {
toRecentActivityOrderString(status: RecentActivityOrder): string {
switch (status) {
// case RecentActivityOrder.CREATED: return this.language.instant('TYPES.RECENT-ACTIVITY-ORDER.CREATED');
case RecentActivityOrder.Label: return this.language.instant('TYPES.RECENT-ACTIVITY-ORDER.LABEL');
case RecentActivityOrder.UpdatedAt: return this.language.instant('TYPES.RECENT-ACTIVITY-ORDER.MODIFIED');
// case RecentActivityOrder.FINALIZED: return this.language.instant('TYPES.RECENT-ACTIVITY-ORDER.FINALIZED');
// case RecentActivityOrder.PUBLISHED: return this.language.instant('TYPES.RECENT-ACTIVITY-ORDER.PUBLISHED');
// case RecentActivityOrder.DATASETPUBLISHED: return this.language.instant('TYPES.RECENT-ACTIVITY-ORDER.PUBLISHED');
case RecentActivityOrder.Status: return this.language.instant('TYPES.RECENT-ACTIVITY-ORDER.STATUS');
}
}

View File

@ -1,6 +1,4 @@
.multiple-auto-complete {
// margin-left: inherit;
// margin-right: inherit;
.not-loading {
display: none;
@ -8,10 +6,7 @@
.align-arrow-right {
// position: absolute;
right: 0;
// bottom: 0;
// vertical-align: middle;
cursor: pointer;
align-self: center;
color: rgba(0, 0, 0, 0.54);
@ -25,19 +20,10 @@
}
.chip-text {
// text-overflow: ellipsis;
// white-space: nowrap;
// overflow: hidden;
white-space: normal;
word-break: break-word;
}
// .chip-list {
// max-width: calc(100% - 30px);
// padding-left: 0.8em;
// }
.title-subtitle-fn {
height: auto;
width: calc(100% - 46px);

View File

@ -57,11 +57,6 @@
line-height: 1.2em;
}
// .two-line-mat-option {
// height: 3.5em;
// line-height: 1.2em;
// }
.mat-standard-chip:hover::after {
opacity: 0;
}
@ -70,11 +65,6 @@
opacity: 0;
}
// .mat-form-field-type-single-autocomplete:not(.mat-form-field-disabled) .mat-form-field-flex {
// cursor: pointer;
// }
.option-text-container {
min-width: 0;
}

View File

@ -313,9 +313,9 @@ export class SingleAutoCompleteComponent extends _CustomComponentMixinBase imple
}
ngOnDestroy() {
this.panelClosedSubscription.unsubscribe();
this.stateChanges.complete();
this.fm.stopMonitoring(this.elRef.nativeElement);
this.panelClosedSubscription?.unsubscribe();
this.stateChanges?.complete();
this.fm?.stopMonitoring(this.elRef?.nativeElement);
}
//Configuration getters

View File

@ -17,42 +17,6 @@ ngx-guided-tour {
border-radius: 44px; /*custom add*/
}
.tour-orb {
position: fixed;
width: 20px;
height: 20px;
border-radius: 50%;
.tour-orb-ring {
width: 35px;
height: 35px;
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
animation: pulse 2s linear infinite;
&:after {
content: "";
display: inline-block;
height: 100%;
width: 100%;
border-radius: 50%;
}
}
@keyframes pulse {
from {
transform: translate(-50%, -50%) scale(0.45);
opacity: 1;
}
to {
transform: translate(-50%, -50%) scale(1);
opacity: 0;
}
}
}
.tour-step {
position: fixed;
&.page-tour-step {
@ -154,9 +118,6 @@ ngx-guided-tour {
}
.tour-title {
// font-weight: bold !important;
// padding-bottom: 20px;
/*custom add*/
font-weight: lighter !important;
font-size: 16px !important;
@ -166,7 +127,6 @@ ngx-guided-tour {
color: #212121;
line-height: 26px;
white-space: pre-line;
// height: 210px;
}
h3.tour-title {
@ -201,14 +161,11 @@ ngx-guided-tour {
}
button.skip-button.link-button {
// padding-left: 0;
border-left: 0;
/*custom add*/
// padding: 0;
padding-right: 1em;
padding-left: 1em;
// float: right;
min-width: 133px;
height: 40px;
border: 1px solid var(--primary-color);
@ -218,16 +175,10 @@ ngx-guided-tour {
.next-button {
cursor: pointer;
// border-radius: 1px;
// float: right;
border: none;
outline: none;
// padding-left: 10px;
// padding-right: 10px;
/*custom add*/
// float: left;
// padding: 10px 0px;
padding-left: 1em;
padding-right: 1em;
min-width: 101px;

View File

@ -40,7 +40,6 @@ export class DescriptionTemplateEditorSectionFieldSetComponent implements OnInit
private initializer = new Subject<void>();
private scroller = new Subject<string>();
// private $selectedFieldsetId = new Subject<string>();
constructor(
private dragulaService: DragulaService,
private myElement: ElementRef
@ -52,8 +51,6 @@ export class DescriptionTemplateEditorSectionFieldSetComponent implements OnInit
this.dragulaService.createGroup(this.FIELDSETS, {
moves: (el, container, handle) => {
// if(this.viewOnly) return false; //uncomment if want to unable drag n drop in viewonly mode
// if (el.id != (this.dragula_prefix + this.tocentry.id)) return false;
if (handle.className && handle.classList.contains('handle')) return true;
return false;
}

View File

@ -1,16 +1,16 @@
import { CdkStep, StepperSelectionEvent } from '@angular/cdk/stepper';
import { DatePipe } from '@angular/common';
import { Component, OnInit, QueryList, ViewChild } from '@angular/core';
import { FormArray, FormControl, FormGroup, UntypedFormArray, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { CdkStep, StepperSelectionEvent } from '@angular/cdk/stepper';
import { DatePipe } from '@angular/common';
import { MatStepper } from '@angular/material/stepper';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { DescriptionTemplateStatus } from '@app/core/common/enum/description-template-status';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { UserDescriptionTemplateRole } from '@app/core/common/enum/user-description-template-role';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
@ -18,13 +18,19 @@ import { DescriptionTemplatePersist, NewVersionDescriptionTemplatePersist } from
import { LanguageInfo } from '@app/core/model/language-info';
import { User } from '@app/core/model/user/user';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LanguageInfoService } from '@app/core/services/culture/language-info-service';
import { DescriptionTemplateTypeService } from '@app/core/services/description-template-type/description-template-type.service';
import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { UserService } from '@app/core/services/user/user.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { BaseEditor } from '@common/base/base-editor';
import { FormService } from '@common/forms/form-service';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
@ -38,13 +44,6 @@ import { DescriptionTemplateEditorModel, DescriptionTemplateFieldEditorModel, De
import { DescriptionTemplateEditorResolver } from './description-template-editor.resolver';
import { DescriptionTemplateEditorService } from './description-template-editor.service';
import { NewEntryType, ToCEntry, ToCEntryType } from './table-of-contents/description-template-table-of-contents-entry';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { Title } from '@angular/platform-browser';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { DescriptionTemplateTypeStatus } from '@app/core/common/enum/description-template-type-status';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
@Component({
@ -79,7 +78,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
userFormControl = new FormControl();
singleAutocompleteDescriptionTemplateTypeConfiguration: SingleAutoCompleteConfiguration;
//Preview
previewFieldSet: DescriptionTemplate = null;
@ -94,7 +93,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
get fromStepperLabel(): string {
return '2 ' + this.language.instant('DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.TITLE');
}
get previewLabel(): string {
return '3 ' + this.language.instant('DESCRIPTION-TEMPLATE-EDITOR.ACTIONS.PREVIEW-AND-FINALIZE');
}
@ -143,7 +142,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
public titleService: Title,
private analyticsService: AnalyticsService
) {
const descriptionLabel:string = route.snapshot.data['entity']?.label;
const descriptionLabel: string = route.snapshot.data['entity']?.label;
if (descriptionLabel) {
titleService.setTitle(descriptionLabel);
} else {
@ -233,14 +232,14 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
} else if (this.isNew || this.isNewVersion || this.isClone) {
route.push('/description-templates/' + id);
this.router.navigate(route, { queryParams: { 'lookup': this.queryParamsService.serializeLookup(this.lookupParams), 'lv': ++this.lv }, replaceUrl: true, relativeTo: this.route });
} else{
} else {
this.refreshData();
}
}
persistEntity(onSuccess?: (response) => void): void {
if (this.isNewVersion == false){
if (this.isNewVersion == false) {
const formData = this.formService.getValue(this.formGroup.value) as DescriptionTemplatePersist;
this.descriptionTemplateService.persist(formData)
@ -248,14 +247,14 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
error => this.onCallbackError(error)
);
} else if (this.isNewVersion== true && this.isNew == false && this.isClone == false) {
const formData = this.formService.getValue(this.formGroup.value) as NewVersionDescriptionTemplatePersist;
} else if (this.isNewVersion == true && this.isNew == false && this.isClone == false) {
const formData = this.formService.getValue(this.formGroup.value) as NewVersionDescriptionTemplatePersist;
this.descriptionTemplateService.newVersion(formData)
.pipe(takeUntil(this._destroyed)).subscribe(
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
error => this.onCallbackError(error)
);
this.descriptionTemplateService.newVersion(formData)
.pipe(takeUntil(this._destroyed)).subscribe(
complete => onSuccess ? onSuccess(complete) : this.onCallbackSuccess(complete),
error => this.onCallbackError(error)
);
}
}
@ -269,10 +268,10 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
this.persistEntity(onSuccess);
}
saveWithClose(close: boolean){
saveWithClose(close: boolean) {
this.formSubmit((data) => {
this.uiNotificationService.snackBarNotification(this.isNew ? this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-CREATION') : this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
if(close){
if (close) {
this.formGroup = null;
this.router.navigate(['/description-templates']);
} else {
@ -325,7 +324,8 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
if (result) {
this.formGroup.get('status').setValue(DescriptionTemplateStatus.Finalized);
this.persistEntity();
}});
}
});
}
//
@ -592,7 +592,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
}
}
return sameLevelFields.at(tceIndex > 0 ? tceIndex-1 : tceIndex+1); //if deleting the first field, find the next one, else find the previous
return sameLevelFields.at(tceIndex > 0 ? tceIndex - 1 : tceIndex + 1); //if deleting the first field, find the next one, else find the previous
}
private populateSections(sections: UntypedFormArray, existingNumbering: string, validationRootPath: string): ToCEntry[] {
@ -803,7 +803,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
field.ordinal = 0;//first filed in the fields list
const fieldSetsArray = parent.form.get('fieldSets') as UntypedFormArray
const fieldForm = field.buildForm({ rootPath:'definition.pages[' + pageIndex + '].' + parentSectionRootPath + 'fieldSets[' + fieldSetsArray.length + '].' + 'fields[' + 0 + '].' });
const fieldForm = field.buildForm({ rootPath: 'definition.pages[' + pageIndex + '].' + parentSectionRootPath + 'fieldSets[' + fieldSetsArray.length + '].' + 'fields[' + 0 + '].' });
//give fieldset id and ordinal
const fieldSet: DescriptionTemplateFieldSetEditorModel = new DescriptionTemplateFieldSetEditorModel(this.editorModel.validationErrorModel);
@ -1016,7 +1016,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
}
cloneFieldSet(fieldset: FormGroup, validationRootPath: string){
cloneFieldSet(fieldset: FormGroup, validationRootPath: string) {
const values = fieldset.getRawValue();
const parentArray = fieldset.parent as FormArray;
@ -1029,14 +1029,14 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
const clonedModel = new DescriptionTemplateFieldSetEditorModel(this.editorModel.validationErrorModel).fromModel(values);
const clonedForm = clonedModel.buildForm({rootPath: validationRootPath + '.fieldSets[' + parentArray.length + ']' + '.fields[' + 0 + '].'});
const clonedForm = clonedModel.buildForm({ rootPath: validationRootPath + '.fieldSets[' + parentArray.length + ']' + '.fields[' + 0 + '].' });
parentArray.push(clonedForm);
//update tocentries and make selected tocentry the cloedn
let entries = this.refreshToCEntries();
const entryfound = this._findTocEntryById(clonedForm.get('id').value, entries);
if(entryfound){
if (entryfound) {
this.selectedTocEntry = entryfound;
}
}
@ -1081,23 +1081,23 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
const previousField = this.getNextFieldAfterDeletingTocentry(tce);
const tocentries = this.getTocEntries();
const previousFieldTocEntry = this._findTocEntryById(previousField?.id, tocentries);
this.selectedTocEntry = previousFieldTocEntry;
} else if (sameLevelFields?.length == 1) {
//scroll to parent
let parentId = null;
if (isFirstLevel) {
parentId = tce.form.get('page').value;
} else {
parentId = tce.form.parent.parent.get('id').value
}
if (parentId) {
const tocentries = this.getTocEntries();
const parent = this._findTocEntryById(parentId, tocentries);
if (parent) {
this.selectedTocEntry = parent;
} else {
@ -1206,33 +1206,7 @@ export class DescriptionTemplateEditorComponent extends BaseEditor<DescriptionTe
//
//
private hasInvalidVisibilityRule(field: UntypedFormGroup): boolean {
// const renderStyle = field.get('viewStyle').get('renderStyle').value;
// if (renderStyle && ![
// DatasetProfileFieldViewStyle.TextArea,
// DatasetProfileFieldViewStyle.RichTextArea,
// DatasetProfileFieldViewStyle.Upload,
// DatasetProfileFieldViewStyle.FreeText,
// DatasetProfileFieldViewStyle.BooleanDecision,
// DatasetProfileFieldViewStyle.RadioBox,
// DatasetProfileFieldViewStyle.CheckBox,
// DatasetProfileFieldViewStyle.DatePicker,
// DatasetProfileFieldViewStyle.ComboBox,
// ].includes(renderStyle)) {
// if (((renderStyle === DatasetProfileFieldViewStyle) && (field.get('data').get('type').value === DatasetProfileComboBoxType.Select))) {
// return false;
// }
// try {
// if (field.get('visible').get('rules').value.length) {
// return true;
// }
// return false;
// } catch {
// return false;
// }
// } else {
return false;
// }
}
private removeFieldSetVisibilityRules(fieldsets: ToCEntry[]) {

View File

@ -414,7 +414,7 @@ export class DescriptionTemplateTableOfContents extends BaseComponent implements
);
this.dragSubscriptions.push(
dragulaService.over(this.DRAG_GROUP).subscribe(( {el, container, source }) => {
dragulaService.over(this.DRAG_GROUP).subscribe(({ el, container, source }) => {
try {
this.overcontainer = container.id;
} catch (error) {
@ -422,7 +422,7 @@ export class DescriptionTemplateTableOfContents extends BaseComponent implements
}
})
);
this.dragSubscriptions.push(
dragulaService.dragend(this.DRAG_GROUP).subscribe(({ el }) => {
this.isDragging = false;
@ -430,394 +430,9 @@ export class DescriptionTemplateTableOfContents extends BaseComponent implements
this.overcontainer = null;
})
);
// if (this.dragulaService.find('TABLEDRAG')) {
// this.dragulaService.destroy('TABLEDRAG');
// }
// const dragula = this.dragulaService.createGroup('TABLEDRAG', {});
// const drake = dragula.drake;
// drake.on('drop', (el, target, source, sibling) => {
// if (this._dragStartedAt) {
// const timeNow = new Date().getTime();
// if (timeNow - this._dragStartedAt > this.VALID_DROP_TIME) {
// // console.log('timenow: ', timeNow);
// // console.log('timestarted', this._dragStartedAt);
// this._dragStartedAt = null;
// } else {
// this.dataNeedsRefresh.emit();// even though the data is not changed the TABLE DRAG may has changed
// return;
// }
// } else {
// this.dataNeedsRefresh.emit();// even though the data is not changed the TABLE DRAG may has changed
// return;
// }
// const elementId = (el.id as string).replace(this.DRAGULA_ITEM_ID_PREFIX, '');
// const targetId = target.id as string;
// const sourceId = source.id as string;
// if (!(elementId && targetId && sourceId)) {
// console.info('Elements do not have an id');
// this.dataNeedsRefresh.emit();
// return;
// }
// const element: ToCEntry = this._findTocEntryById(elementId, this.links);
// const targetContainer: ToCEntry = this._findTocEntryById(targetId, this.links);
// const sourceContainer: ToCEntry = this._findTocEntryById(sourceId, this.links);
// if (!(element && (targetContainer || ((element.type === ToCEntryType.Page) && (targetId === this.ROOT_ID))) && (sourceContainer || ((element.type === ToCEntryType.Page) && (sourceId === this.ROOT_ID))))) {
// // console.info('Could not find elements');
// this.dataNeedsRefresh.emit();
// //TODO: angular update //drake.cancel(true);
// return;
// }
// switch (element.type) {
// case ToCEntryType.FieldSet: {
// if (targetContainer.type != this.tocEntryType.Section) {
// // const message = 'Fieldset can only be child of Subsections';
// const message = this.language.instant('DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.FIELDSET-MUST-HAVE-PARENT-SECTION');
// // console.error(message);
// this.notifyUser(message)
// this.dataNeedsRefresh.emit();
// return;
// }
// //check if target container has no sections
// if ((targetContainer.form.get('sections') as UntypedFormArray).length) {
// // const message = 'Cannot have inputs and sections on the same level';
// const message = this.language.instant('DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.INPUT-SECTION-SAME-LEVEL');
// this.notifyUser(message);
// // console.error(message);
// this.dataNeedsRefresh.emit();
// return;
// }
// const fieldsetForm = element.form;
// const targetFieldsets = targetContainer.form.get('fieldSets') as UntypedFormArray;
// const sourceFieldsets = sourceContainer.form.get('fieldSets') as UntypedFormArray;
// if (!targetFieldsets) {
// console.info('Not target fieldsets container found');
// this.dataNeedsRefresh.emit();
// return;
// }
// let sourceOrdinal = -1;
// let idx = -1;
// sourceFieldsets.controls.forEach((elem, index) => {
// if (elem.get('id').value === elementId) {
// sourceOrdinal = elem.get('ordinal').value;
// idx = index
// }
// });
// if (sourceOrdinal >= 0 && idx >= 0) {
// sourceFieldsets.removeAt(idx);
// sourceFieldsets.controls.forEach(control => {
// const ordinal = control.get('ordinal');
// if ((ordinal.value >= sourceOrdinal) && sourceOrdinal > 0) {
// const updatedOrdinalVal = ordinal.value - 1;
// ordinal.setValue(updatedOrdinalVal);
// }
// });
// sourceFieldsets.controls.sort(this._compareOrdinals);
// }
// let position: number = targetFieldsets.length;
// if (!sibling || !sibling.id) {
// console.info('No sibling Id found');
// } else {
// const siblingId = (sibling.id as string).replace(this.DRAGULA_ITEM_ID_PREFIX, '');
// let siblingIndex = -1;
// targetFieldsets.controls.forEach((e, idx) => {
// if (e.get('id').value === siblingId) {
// siblingIndex = idx;
// position = e.get('ordinal').value;
// }
// });
// if (siblingIndex >= 0) { //sibling found
// targetFieldsets.controls.filter(control => control.get('ordinal').value >= position).forEach(control => {
// const ordinal = control.get('ordinal');
// const updatedOrdinalVal = ordinal.value + 1;
// ordinal.setValue(updatedOrdinalVal);
// })
// }
// }
// fieldsetForm.get('ordinal').setValue(position);
// targetFieldsets.insert(position, fieldsetForm);
// targetFieldsets.controls.sort(this._compareOrdinals);
// this.dataNeedsRefresh.emit({ draggedItemId: elementId });
// break;
// }
// case ToCEntryType.Section: {
// if (targetContainer.type == ToCEntryType.Section) {
// if ((targetContainer.form.get('fieldSets') as UntypedFormArray).length) {
// // const message = 'Cannot have inputs and sections on the same level';
// const message = this.language.instant('DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.INPUT-SECTION-SAME-LEVEL');;
// this.notifyUser(message);
// // console.info(message);
// this.dataNeedsRefresh.emit();
// return;
// }
// const targetSections = targetContainer.form.get('sections') as UntypedFormArray;
// const elementSectionForm = element.form;
// const sourceSections = elementSectionForm.parent as UntypedFormArray;
// if (!(targetSections && sourceSections && elementSectionForm)) {
// console.info('Could not load sections');
// this.dataNeedsRefresh.emit();
// return;
// }
// let idx = -1;
// sourceSections.controls.forEach((section, i) => {
// if (section.get('id').value === elementId) {
// idx = i;
// }
// });
// if (!(idx >= 0)) {
// console.info('Could not find element in Parent container');
// this.dataNeedsRefresh.emit();
// return;
// }
// sourceSections.controls.filter(control => control.get('ordinal').value >= elementSectionForm.get('ordinal').value).forEach(control => {
// const ordinal = control.get('ordinal');
// const updatedOrdinalVal = ordinal.value ? ordinal.value - 1 : 0;
// ordinal.setValue(updatedOrdinalVal);
// });
// sourceSections.removeAt(idx);
// let targetOrdinal = targetSections.length;
// if (sibling && sibling.id) {
// const siblingId = sibling.id.replace(this.DRAGULA_ITEM_ID_PREFIX, '');
// targetSections.controls.forEach((section, i) => {
// if (section.get('id').value === siblingId) {
// targetOrdinal = section.get('ordinal').value;
// }
// })
// targetSections.controls.filter(control => control.get('ordinal').value >= targetOrdinal).forEach(control => {
// const ordinal = control.get('ordinal');
// const updatedOrdinalVal = ordinal.value + 1;
// ordinal.setValue(updatedOrdinalVal);
// });
// } else {
// console.info('no siblings found');
// }
// elementSectionForm.get('ordinal').setValue(targetOrdinal);
// targetSections.insert(targetOrdinal, elementSectionForm);
// } else if (targetContainer.type === ToCEntryType.Page) {
// const rootform = targetContainer.form.root;
// const sectionForm = element.form;
// const parentSections = sectionForm.parent as UntypedFormArray;
// let parentIndex = -1;
// parentSections.controls.forEach((section, i) => {
// if (section.get('id').value === elementId) {
// parentIndex = i
// }
// })
// if (parentIndex < 0) {
// console.info('could not locate section in parents array');
// this.dataNeedsRefresh.emit();
// return;
// }
// //update parent sections ordinal
// parentSections.controls.filter(section => section.get('ordinal').value >= sectionForm.get('ordinal').value).forEach(section => {
// const ordinal = section.get('ordinal');
// const updatedOrdinalVal = ordinal.value ? ordinal.value - 1 : 0;
// ordinal.setValue(updatedOrdinalVal);
// })
// parentSections.removeAt(parentIndex);
// let position = 0;
// if (targetContainer.subEntries) {
// position = targetContainer.subEntries.length;
// }
// //populate sections
// const targetSectionsArray = rootform.get('sections') as UntypedFormArray;
// if (sibling && sibling.id) {
// const siblingId = sibling.id.replace(this.DRAGULA_ITEM_ID_PREFIX, '');
// let indx = -1;
// targetContainer.subEntries.forEach((e, i) => {
// if (e.form.get('id').value === siblingId) {
// indx = i;
// position = e.form.get('ordinal').value;
// }
// });
// if (indx >= 0) {
// // e.form.get('ordinal').setValue(i+1);
// targetContainer.subEntries.filter(e => e.form.get('ordinal').value >= position).forEach(e => {
// const ordinal = e.form.get('ordinal');
// const updatedOrdinalVal = ordinal.value + 1;
// ordinal.setValue(updatedOrdinalVal);
// });
// }
// } else {
// console.info('No sibling found');
// }
// sectionForm.get('ordinal').setValue(position);
// sectionForm.get('page').setValue(targetContainer.id);
// targetSectionsArray.push(sectionForm);
// } else {
// // const message = 'Drag not support to specific container';
// const message = this.language.instant('DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.DRAG-NOT-SUPPORTED');
// this.notifyUser(message);
// // console.info(message);
// this.dataNeedsRefresh.emit();
// return;
// }
// this.dataNeedsRefresh.emit({ draggedItemId: elementId });
// break;
// }
// case ToCEntryType.Page: {
// if (targetId != this.ROOT_ID) {
// // const message = 'A page element can only be at top level';
// const message = this.language.instant('DESCRIPTION-TEMPLATE-EDITOR.STEPS.FORM.TABLE-OF-CONTENTS.ERROR-MESSAGES.PAGE-ELEMENT-ONLY-TOP-LEVEL');
// this.notifyUser(message);
// // console.info(message);
// this.dataNeedsRefresh.emit();
// return;
// }
// const rootForm = element.form.root;
// if (!rootForm) {
// console.info('Could not find root!')
// this.dataNeedsRefresh.emit();
// return;
// }
// const pages = rootForm.get('pages') as UntypedFormArray;
// const pageForm = element.form;
// let index = -1;
// pages.controls.forEach((page, i) => {
// if (page.get('id').value === elementId) {
// index = i;
// }
// });
// if (index < 0) {
// console.info('Could not locate page on pages');
// this.dataNeedsRefresh.emit();
// return;
// }
// //ordinality
// pages.controls.filter(page => page.get('ordinal').value >= pageForm.get('ordinal').value).forEach(page => {
// const ordinal = page.get('ordinal');
// const ordinalVal = ordinal.value ? ordinal.value - 1 : 0;
// ordinal.setValue(ordinalVal);
// });
// pages.removeAt(index);
// let targetPosition = pages.length;
// if (sibling) {
// const siblingId = sibling.id.replace(this.DRAGULA_ITEM_ID_PREFIX, '');
// pages.controls.forEach((page, i) => {
// if (page.get('id').value === siblingId) {
// targetPosition = page.get('ordinal').value;
// }
// });
// }
// pageForm.get('ordinal').setValue(targetPosition);
// //update ordinality
// pages.controls.filter(page => page.get('ordinal').value >= targetPosition).forEach(page => {
// const ordinal = page.get('ordinal');
// const ordinalVal = ordinal.value + 1;
// ordinal.setValue(ordinalVal);
// });
// pages.insert(targetPosition, pageForm);
// this.dataNeedsRefresh.emit({ draggedItemId: elementId });
// break;
// }
// default:
// console.info('Could not support moving objects for specific type of element');
// this.dataNeedsRefresh.emit();
// return;
// }
// });
// drake.on('drag', (el, source) => {
// this._dragStartedAt = new Date().getTime();
// // console.log('drag fired');
// this.isDragging = true;
// this.draggingItemId = (el.id as string).replace(this.DRAGULA_ITEM_ID_PREFIX, '');
// });
// drake.on('over', (el, container, source) => {
// try {
// this.overcontainer = container.id;
// } catch (error) {
// this.overcontainer = null;
// }
// });
// drake.on('dragend', (el) => {
// this.isDragging = false;
// this.draggingItemId = null;
// this.overcontainer = null;
// });
}
ngOnInit(): void {}
ngOnInit(): void { }
ngAfterViewInit(): void {
@ -872,42 +487,6 @@ export class DescriptionTemplateTableOfContents extends BaseComponent implements
this.dragSubscriptions.forEach(subscription => subscription.unsubscribe())
}
private _scrollIntoDragginItem(id: string) {
// const table = document.getElementById('tocentrytable');
// if(table){
// // const element = document.getElementById('TABLE_ENTRY'+id);
// console.log('Table found!');
// const element = document.getElementById('TABLE_ENTRY' + id);
// const elementFromTable = table.closest('#TABLE_ENTRY'+ id);
// if(elementFromTable){
// console.log('found from table:', elementFromTable);
// }
// if(element){
// console.log('Element found!');
// // element.classList.add('text-danger');
// // console.log(element);
// const tableRect = table.getBoundingClientRect();
// const elementRect = element.getBoundingClientRect();
// console.log('tablerect :',tableRect);
// console.log('elementRect :',elementRect);
// const dY = elementRect.top - tableRect.top;
// console.log('Distance from table is ', dY);
// // table.scroll({top:dY,behavior:'smooth'});
// console.log('found from document ', element);
// // element.scrollIntoView();
// }
// // element.scrollIntoView();
// }
}
private _findTocEntryById(id: string, tocentries: ToCEntry[]): ToCEntry {
if (!tocentries) {
return null;

View File

@ -3,17 +3,22 @@ import { Component, OnInit } from '@angular/core';
import { FormArray, FormGroup, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { PrefillingSourceService } from '@app/core/services/prefilling-source/prefilling-source.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { PrefillingSourceService } from '@app/core/services/prefilling-source/prefilling-source.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { DatePipe } from '@angular/common';
import { MatCheckboxChange } from '@angular/material/checkbox';
import { Title } from '@angular/platform-browser';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { PrefillingSource, PrefillingSourcePersist } from '@app/core/model/prefilling-source/prefilling-source';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { ResultFieldsMappingConfigurationEditorModel } from '@app/ui/external-fetcher/external-fetcher-source-editor.model';
import { BaseEditor } from '@common/base/base-editor';
import { FormService } from '@common/forms/form-service';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
@ -22,15 +27,9 @@ 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 { PrefillingSourceDefinitionEditorModel, PrefillingSourceEditorModel } from './prefilling-source-editor.model';
import { PrefillingSourceEditorResolver } from './prefilling-source-editor.resolver';
import { PrefillingSourceEditorService } from './prefilling-source-editor.service';
import { PrefillingSourceDefinitionEditorModel, PrefillingSourceEditorModel } from './prefilling-source-editor.model';
import { ResultFieldsMappingConfigurationEditorModel } from '@app/ui/external-fetcher/external-fetcher-source-editor.model';
import { MatCheckboxChange } from '@angular/material/checkbox';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { Title } from '@angular/platform-browser';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
@Component({
selector: 'app-prefilling-source-editor-component',
@ -80,7 +79,7 @@ export class PrefillingSourceEditorComponent extends BaseEditor<PrefillingSource
private titleService: Title,
private analyticsService: AnalyticsService
) {
const descriptionLabel:string = route.snapshot.data['entity']?.label;
const descriptionLabel: string = route.snapshot.data['entity']?.label;
if (descriptionLabel) {
titleService.setTitle(descriptionLabel);
} else {
@ -190,13 +189,13 @@ export class PrefillingSourceEditorComponent extends BaseEditor<PrefillingSource
this.formService.validateAllFormFields(this.formGroup);
}
getEnabledChanged(event: MatCheckboxChange){
if(event.checked == true){
getEnabledChanged(event: MatCheckboxChange) {
if (event.checked == true) {
const definition = new PrefillingSourceDefinitionEditorModel(this.editorModel.validationErrorModel);
definition.buildGetConfiguration(this.formGroup.get('definition') as UntypedFormGroup, "definition.");
this.submitFields();
}else{
} else {
const definition = this.formGroup.get('definition') as UntypedFormGroup;
if (definition.get('getConfiguration')) definition.removeControl('getConfiguration');
this.submitFields();
@ -294,21 +293,21 @@ export class PrefillingSourceEditorComponent extends BaseEditor<PrefillingSource
}
const fieldsMapping = new ResultFieldsMappingConfigurationEditorModel(this.editorModel.validationErrorModel);
fieldsMapping.code = code;
formArray.push(fieldsMapping.buildForm({rootPath: "definition." + controlName + ".results.fieldsMapping[" + fieldMappingSize + "]."}));
formArray.push(fieldsMapping.buildForm({ rootPath: "definition." + controlName + ".results.fieldsMapping[" + fieldMappingSize + "]." }));
// formArray.at(fieldMappingSize).get('code').patchValue(code);
}
removeFieldMapping(baseFormGroup: any, fieldCode: string){
if(baseFormGroup){
removeFieldMapping(baseFormGroup: any, fieldCode: string) {
if (baseFormGroup) {
const fieldMappingFormArray = (baseFormGroup.get('results').get('fieldsMapping') as FormArray);
for (let j = 0; j < fieldMappingFormArray.length; j++) {
if (fieldCode == fieldMappingFormArray.at(j).get('code').getRawValue()) {
fieldMappingFormArray.removeAt(j);
PrefillingSourceEditorModel.reApplyDefinitionValidators({
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
formGroup: this.formGroup,
validationErrorModel: this.editorModel.validationErrorModel
}
);
}
}

View File

@ -1,21 +1,23 @@
import { DatePipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormArray, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { DatePipe } from '@angular/common';
import { ExternalFetcherApiHTTPMethodType } from '@app/core/common/enum/external-fetcher-api-http-method-type';
import { ExternalFetcherSourceType } from '@app/core/common/enum/external-fetcher-source-type';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type';
import { ExternalFetcherApiHTTPMethodType } from '@app/core/common/enum/external-fetcher-api-http-method-type';
import { ExternalFetcherSourceType } from '@app/core/common/enum/external-fetcher-source-type';
import { ReferenceType, ReferenceTypePersist } from '@app/core/model/reference-type/reference-type';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.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';
@ -25,12 +27,9 @@ 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 { ReferenceTypeEditorModel} from './reference-type-editor.model';
import { ReferenceTypeEditorModel } from './reference-type-editor.model';
import { ReferenceTypeEditorResolver } from './reference-type-editor.resolver';
import { ReferenceTypeEditorService } from './reference-type-editor.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { Title } from '@angular/platform-browser';
@Component({
selector: 'app-reference-type-editor-component',
@ -90,8 +89,8 @@ export class ReferenceTypeEditorComponent extends BaseEditor<ReferenceTypeEditor
private logger: LoggingService,
private referenceTypeEditorService: ReferenceTypeEditorService,
private titleService: Title
) {
const descriptionLabel:string = route.snapshot.data['entity']?.name;
) {
const descriptionLabel: string = route.snapshot.data['entity']?.name;
if (descriptionLabel) {
titleService.setTitle(descriptionLabel);
} else {
@ -227,7 +226,7 @@ export class ReferenceTypeEditorComponent extends BaseEditor<ReferenceTypeEditor
fieldForm.markAsDirty();
const sourceFormArray = (this.formGroup.get('definition').get('sources') as FormArray);
if(sourceFormArray){
if (sourceFormArray) {
for (let i = 0; i < sourceFormArray.length; i++) {
const fieldMappingFormArray = (sourceFormArray.at(i).get('results').get('fieldsMapping') as FormArray);
for (let j = 0; j < fieldMappingFormArray.length; j++) {
@ -334,10 +333,10 @@ export class ReferenceTypeEditorComponent extends BaseEditor<ReferenceTypeEditor
this.sourceKeysMap.set(referenceType.id, sourceKeys);
})
},
error => {
this.router.navigate(['/reference-type']);
this.httpErrorHandlingService.handleBackedRequestError(error);
});
error => {
this.router.navigate(['/reference-type']);
this.httpErrorHandlingService.handleBackedRequestError(error);
});
}
selectedReferenceTypeChanged(id: Guid): void {

View File

@ -1,21 +1,24 @@
import { DatePipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { FormArray, UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { ReferenceService } from '@app/core/services/reference/reference.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { DatePipe } from '@angular/common';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { ReferenceFieldDataType } from '@app/core/common/enum/reference-field-data-type';
import { ReferenceSourceType } from '@app/core/common/enum/reference-source-type';
import { Reference, ReferencePersist } from '@app/core/model/reference/reference';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
import { ReferenceService } from '@app/core/services/reference/reference.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { BaseEditor } from '@common/base/base-editor';
@ -29,10 +32,6 @@ import { map, takeUntil } from 'rxjs/operators';
import { ReferenceEditorModel } from './reference-editor.model';
import { ReferenceEditorResolver } from './reference-editor.resolver';
import { ReferenceEditorService } from './reference-editor.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { Title } from '@angular/platform-browser';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
@Component({
@ -92,8 +91,8 @@ export class ReferenceEditorComponent extends BaseEditor<ReferenceEditorModel, R
public titleService: Title,
private analyticsService: AnalyticsService
) {
const descriptionLabel:string = route.snapshot.data['entity']?.label;
const descriptionLabel: string = route.snapshot.data['entity']?.label;
if (descriptionLabel) {
titleService.setTitle(descriptionLabel);
} else {

View File

@ -1,34 +1,32 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
import { CultureInfo } from '@app/core/model/culture-info';
import { TenantConfiguration, TenantConfigurationPersist } from '@app/core/model/tenant-configuaration/tenant-configuration';
import { AuthService } from '@app/core/services/auth/auth.service';
import { CultureService } from '@app/core/services/culture/culture-service';
import { DefaultUserLocaleService } from '@app/core/services/default-user-locale/default-user-locale.service';
import { LanguageService } from '@app/core/services/language/language.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { BasePendingChangesComponent } from '@common/base/base-pending-changes.component';
import { FormService } from '@common/forms/form-service';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { TranslateService } from '@ngx-translate/core';
import moment from 'moment';
import { Observable } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { TenantConfigurationEditorModel } from './default-user-locale-editor.model';
import { TenantConfiguration, TenantConfigurationPersist } from '@app/core/model/tenant-configuaration/tenant-configuration';
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
import { DefaultUserLocaleEditorService } from './default-user-locale-editor.service';
import { DefaultUserLocaleEditorResolver } from './default-user-locale-editor.resolver';
import { BasePendingChangesComponent } from '@common/base/base-pending-changes.component';
import { Observable, of } from 'rxjs';
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
import { HttpErrorResponse } from '@angular/common/http';
import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { CultureInfo } from '@app/core/model/culture-info';
import moment from 'moment';
import { CultureService } from '@app/core/services/culture/culture-service';
import { LanguageService } from '@app/core/services/language/language.service';
import { SingleAutoCompleteConfiguration } from '@app/library/auto-complete/single/single-auto-complete-configuration';
import { DefaultUserLocaleService } from '@app/core/services/default-user-locale/default-user-locale.service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { DefaultUserLocaleEditorService } from './default-user-locale-editor.service';
@Component({
@ -93,7 +91,7 @@ export class DefaultUserLocaleEditorComponent extends BasePendingChangesComponen
ngOnInit(): void {
this.singleTimezoneAutocompleteConfiguration = this.defaultUserLocaleService.singleTimezoneAutocompleteConfiguration;
this.singleCultureAutocompleteConfiguration = this.defaultUserLocaleService.singleCultureAutocompleteConfiguration;
this.analyticsService.trackPageView(AnalyticsService.TenantConfigurationsUserLocaleEditor);
this.getItem((entity) => {
@ -115,16 +113,16 @@ export class DefaultUserLocaleEditorComponent extends BasePendingChangesComponen
onCallbackError(errorResponse: HttpErrorResponse) {
console.log("Error:", errorResponse);
this.httpErrorHandlingService.handleBackedRequestError(errorResponse)
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
if (error.statusCode === 400) {
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
this.formService.validateAllFormFields(this.formGroup);
}
}
onCallbackSuccess(data?: any): void {
console.log("Success:", data);
@ -225,7 +223,7 @@ export class DefaultUserLocaleEditorComponent extends BasePendingChangesComponen
if (culture == null
|| culture.displayName == null
|| culture.nativeName == null)
|| culture.nativeName == null)
return undefined;
return culture.displayName + '-' + culture.nativeName;

View File

@ -1,6 +1,5 @@
import { Component } from '@angular/core';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { BaseComponent } from '@common/base/base.component';

View File

@ -1,18 +1,20 @@
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 { TenantService } from '@app/core/services/tenant/tenant.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { DatePipe } from '@angular/common';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { Tenant, TenantPersist } from '@app/core/model/tenant/tenant';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { TenantService } from '@app/core/services/tenant/tenant.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { BaseEditor } from '@common/base/base-editor';
@ -23,12 +25,9 @@ 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 { TenantEditorModel } from './tenant-editor.model';
import { TenantEditorResolver } from './tenant-editor.resolver';
import { TenantEditorService } from './tenant-editor.service';
import { TenantEditorModel } from './tenant-editor.model';
import { LockService } from '@app/core/services/lock/lock.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
@Component({

View File

@ -159,11 +159,6 @@ export class AnnotationDialogComponent extends BaseComponent {
this.threads.add(element.threadId);
});
console.log(this.parentAnnotationsPerThread);
// console.log(this.comments);
// console.log(this.threads);
// console.log(this.parentAnnotationsPerThread);
// console.log(this.annotationsPerThread);
// this.annotationsChanged.emit(this.threads);
},
error => this.onCallbackError(error),
);

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DescriptionTemplate, DescriptionTemplateBaseFieldData, DescriptionTemplateDefaultValue, DescriptionTemplateDefinition, DescriptionTemplateExternalDatasetData, DescriptionTemplateField, DescriptionTemplateFieldSet, DescriptionTemplateMultiplicity, DescriptionTemplatePage, DescriptionTemplateReferenceTypeData, DescriptionTemplateRule, DescriptionTemplateSection, DescriptionTemplateSelectData, DescriptionTemplateSelectOption, DescriptionTemplateUploadData, DescriptionTemplateUploadOption } from '@app/core/model/description-template/description-template';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { Description, DescriptionExternalIdentifier, DescriptionField, DescriptionPropertyDefinition, DescriptionPropertyDefinitionFieldSet, DescriptionPropertyDefinitionFieldSetItem, DescriptionReference, DescriptionReferenceData, DescriptionTag } from '@app/core/model/description/description';
import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { Dmp, DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
@ -131,10 +131,7 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.label)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.ordinal)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.hasTemplates)].join('.'),
// (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.id)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.descriptionTemplateGroupId)].join('.'),
// (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.label)].join('.'),
// (prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.minMultiplicity)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.maxMultiplicity)].join('.'),
(prefix ? prefix + '.' : '') + [nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.prefillingSources), nameof<PrefillingSource>(x => x.id)].join('.'),
@ -169,19 +166,19 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
return this.descriptionService.getSingle(Guid.parse(id), fields).pipe(tap(d => this.breadcrumbService.addIdResolvedValue(d.id.toString(), d.label)));
} else if (dmpId != null && dmpSectionId != null && copyDmpId == null) {
return this.dmpService.getSingle(Guid.parse(dmpId), DescriptionEditorResolver.dmpLookupFields())
.pipe(tap(x => {
this.breadcrumbService.addIdResolvedValue(`${x.id}/${dmpSectionId}`, this.language.instant("DESCRIPTION-EDITOR.TITLE-NEW"));
.pipe(tap(x => {
this.breadcrumbService.addIdResolvedValue(`${x.id}/${dmpSectionId}`, this.language.instant("DESCRIPTION-EDITOR.TITLE-NEW"));
}), takeUntil(this._destroyed), map(dmp => {
}), takeUntil(this._destroyed), map(dmp => {
const description: Description = {};
description.dmp = dmp;
description.dmpDescriptionTemplate = {
sectionId: Guid.parse(dmpSectionId)
}
return description;
}));
} else if (copyDmpId != null && id != null && dmpSectionId != null) {
const description: Description = {};
description.dmp = dmp;
description.dmpDescriptionTemplate = {
sectionId: Guid.parse(dmpSectionId)
}
return description;
}));
} else if (copyDmpId != null && id != null && dmpSectionId != null) {
return this.dmpService.getSingle(Guid.parse(copyDmpId), DescriptionEditorResolver.dmpLookupFields()).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed), concatMap(dmp => {
return this.descriptionService.getSingle(Guid.parse(id), DescriptionEditorResolver.cloneLookupFields()).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed), map(description => {
@ -197,9 +194,5 @@ export class DescriptionEditorResolver extends BaseEditorResolver {
}));
}));
}
//TODO: check this
// else if (cloneid != null) {
// return this.descriptionService.clone(Guid.parse(cloneid), fields).pipe(tap(x => this.breadcrumbService.addIdResolvedValue(x.id?.toString(), x.label)), takeUntil(this._destroyed));
// }
}
}

View File

@ -1,13 +1,10 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
// import { DescriptionWizardComponent } from './description-wizard/description-wizard.component';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { PendingChangesGuard } from '@common/forms/pending-form-changes/pending-form-changes-guard.service';
// import { DescriptionOverviewComponent } from './overview/description-overview.component';
import { AuthGuard } from '@app/core/auth-guard.service';
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
import { PendingChangesGuard } from '@common/forms/pending-form-changes/pending-form-changes-guard.service';
import { DescriptionEditorComponent } from './description-editor.component';
import { DescriptionEditorResolver } from './description-editor.resolver';
import { AuthGuard } from '@app/core/auth-guard.service';
const routes: Routes = [
{
@ -22,11 +19,7 @@ const routes: Routes = [
breadcrumbs: true,
getFromTitleService: true,
usePrefix: false
//title: 'DESCRIPTION-EDITOR.TITLE-EDIT-DESCRIPTION'
// ,
// authContext: {
// permissions: [AppPermission.EditDescription]
// }
}
},
{
@ -43,10 +36,6 @@ const routes: Routes = [
hideNavigationItem: true
}),
title: 'DESCRIPTION-EDITOR.TITLE-EDIT-DESCRIPTION',
// ,
// authContext: {
// permissions: [AppPermission.EditDescription]
// }
}
},
{
@ -62,10 +51,6 @@ const routes: Routes = [
title: 'DESCRIPTION-EDITOR.TITLE-NEW',
getFromTitleService: true,
usePrefix: false
// ,
// authContext: {
// permissions: [AppPermission.EditDescription]
// }
}
},
{
@ -81,66 +66,8 @@ const routes: Routes = [
title: 'DESCRIPTION-EDITOR.TITLE-NEW',
getFromTitleService: true,
usePrefix: false
// ,
// authContext: {
// permissions: [AppPermission.EditDescription]
// }
}
},
// {
// path: 'edit/:id/finalize',
// component: DescriptionWizardComponent,
// canActivate: [AuthGuard],
// data: {
// breadcrumb: true,
// public: false,
// title: 'GENERAL.TITLES.DATASET-EDIT',
// finalize: true
// },
// canDeactivate:[CanDeactivateGuard]
// },
// {
// path: 'publicEdit/:publicId',
// component: DescriptionWizardComponent,
// //canActivate: [AuthGuard],
// data: {
// public: true,
// title: 'GENERAL.TITLES.DATASET-PUBLIC-EDIT'
// },
// canDeactivate:[CanDeactivateGuard]
// },
// {
// path: 'new',
// component: DescriptionWizardComponent,
// canActivate: [AuthGuard],
// data: {
// breadcrumb: true,
// title: 'GENERAL.TITLES.DATASET-NEW'
// },
// canDeactivate:[CanDeactivateGuard]
// },
// {
// path: 'copy/:id',
// component: DescriptionWizardComponent,
// canActivate: [AuthGuard],
// data: {
// breadcrumb: true,
// title: 'GENERAL.TITLES.DATASET-COPY'
// },
// canDeactivate:[CanDeactivateGuard]
// },
// {
// path: 'profileupdate/:updateId',
// component: DescriptionWizardComponent,
// canActivate: [AuthGuard],
// data: {
// breadcrumb: true,
// title: 'GENERAL.TITLES.DATASET-UPDATE'
// },
// canDeactivate:[CanDeactivateGuard]
// },
];
@NgModule({

View File

@ -35,18 +35,10 @@ export class DescriptionFormFieldSetComponent extends BaseComponent {
return this.fieldSet.hasMultiplicity && this.fieldSet.multiplicity != null;
}
isVisibleByVisibilityService: boolean = true;
@Input() visibilityRulesService: VisibilityRulesService;
@Input() path: String;
@Input() datasetProfileId: String;
// @Input() form: UntypedFormGroup;
@Input() isChild: Boolean = false;
@Input() showDelete: Boolean = false;
@Input() tocentry: ToCEntry;

View File

@ -190,11 +190,6 @@ export class DatasetCriteriaComponent extends BaseCriteriaComponent implements O
) {
setTimeout(() => this.refreshCallback(true));
}
// if (this.refreshCallback != null &&
// (this.criteria.like == null || this.criteria.like.length === 0 || this.criteria.like.length > 2)
// ) {
// this.refreshCallback();
// }
}
filterTags(value: string): Observable<ExternalSourceItemModel[]> {
@ -225,12 +220,6 @@ export class DatasetCriteriaComponent extends BaseCriteriaComponent implements O
const fields: Array<string> = new Array<string>();
fields.push('asc');
// if (this.isPublic) {
// const dmpDataTableRequest: DataTableRequest<ExploreDmpCriteriaModel> = new DataTableRequest(0, null, { fields: fields });
// dmpDataTableRequest.criteria = new ExploreDmpCriteriaModel();
// dmpDataTableRequest.criteria.like = value;
// return this.dmpService.getPublicPaged(dmpDataTableRequest, "autocomplete");
// } else {
const dmpDataTableRequest: DataTableRequest<DmpCriteria> = new DataTableRequest(0, null, { fields: fields });
dmpDataTableRequest.criteria = new DmpCriteria();
dmpDataTableRequest.criteria.like = value;

View File

@ -12,6 +12,7 @@ import { AppPermission } from '@app/core/common/enum/permission.enum';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { Description } from '@app/core/model/description/description';
import { DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { Dmp, DmpDescriptionTemplate, DmpUser } from '@app/core/model/dmp/dmp';
import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
@ -20,36 +21,29 @@ import { DescriptionLookup } from '@app/core/query/description.lookup';
import { DmpLookup } from '@app/core/query/dmp.lookup';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DescriptionService } from '@app/core/services/description/description.service';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { GuidedTour, Orientation } from '@app/library/guided-tour/guided-tour.constants';
import { GuidedTourService } from '@app/library/guided-tour/guided-tour.service';
import { StartNewDmpDialogComponent } from '@app/ui/dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component';
// import { IBreadCrumbComponent } from '@app/ui/misc/breadcrumb/definition/IBreadCrumbComponent';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { BaseComponent } from '@common/base/base.component';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { SortDirection } from '@common/modules/hybrid-listing/hybrid-listing.component';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { debounceTime, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { StartNewDescriptionDialogComponent } from '../start-new-description-dialog/start-new-description-dialog.component';
import { DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { SortDirection } from '@common/modules/hybrid-listing/hybrid-listing.component';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
@Component({
selector: 'app-description-listing-component',
templateUrl: 'description-listing.component.html',
styleUrls: ['./description-listing.component.scss']
})
export class DescriptionListingComponent extends BaseComponent implements OnInit {//IBreadCrumbComponent
export class DescriptionListingComponent extends BaseComponent implements OnInit {
@ViewChild(MatPaginator, { static: true }) _paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
// @ViewChild(DescriptionCriteriaComponent, { static: true }) criteria: DescriptionCriteriaComponent;
// breadCrumbs: Observable<BreadcrumbItem[]>;
titlePrefix: String;
dmpId: string;
@ -202,12 +196,12 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
this.refresh(this.lookup);
}
orderByChanged(){
if (this.formGroup.get('order').value == RecentActivityOrder.Status){
orderByChanged() {
if (this.formGroup.get('order').value == RecentActivityOrder.Status) {
this.lookup.order = { items: [this.sortingDirectionPrefix + nameof<Dmp>(x => x.status)] };
} else if(this.formGroup.get('order').value == RecentActivityOrder.Label){
} else if (this.formGroup.get('order').value == RecentActivityOrder.Label) {
this.lookup.order = { items: [this.sortingDirectionPrefix + nameof<Dmp>(x => x.label)] };
}else{
} else {
this.lookup.order = { items: [this.sortingDirectionPrefix + nameof<Dmp>(x => x.updatedAt)] };
}
this.refresh(this.lookup);
@ -256,96 +250,35 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
[nameof<Description>(x => x.dmpDescriptionTemplate), nameof<DmpDescriptionTemplate>(x => x.sectionId)].join('.'),
]
};
if(this.isPublic){
if (this.isPublic) {
this.descriptionService.publicQuery(lookup).pipe(takeUntil(this._destroyed))
.subscribe(result => {
if (!result) { return []; }
this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = [];
this.listingItems.push(...result.items);
this.hasListingItems = true;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error));
}else{
.subscribe(result => {
if (!result) { return []; }
this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = [];
this.listingItems.push(...result.items);
this.hasListingItems = true;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error));
} else {
this.descriptionService.query(lookup).pipe(takeUntil(this._destroyed))
.subscribe(result => {
if (!result) { return []; }
this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = [];
result.items.forEach(description => {
if (description.status != DescriptionStatus.Canceled) this.listingItems.push(description);
})
this.hasListingItems = true;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error));
.subscribe(result => {
if (!result) { return []; }
this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = [];
result.items.forEach(description => {
if (description.status != DescriptionStatus.Canceled) this.listingItems.push(description);
})
this.hasListingItems = true;
},
(error) => this.httpErrorHandlingService.handleBackedRequestError(error));
}
}
openFiltersDialog(): void {
//TODO: Add filters dialog
// const dialogRef = this.dialog.open(DescriptionCriteriaDialogComponent, {
// width: '456px',
// height: '100%',
// id: 'filters',
// restoreFocus: false,
// data: {
// isPublic: this.isPublic,
// status: this.status,
// criteria: this.criteria,
// formGroup: this.criteriaFormGroup,
// // criteria: this.grantId ? this.criteria : this.getDefaultCriteria(),
// updateDataFn: this.updateDataFn.bind(this)
// },
// position: { right: '0px;' },
// panelClass: 'dialog-side-panel'
// });
// dialogRef.afterClosed().subscribe(result => {
// });
}
// updateDataFn(criteria: DescriptionCriteriaComponent): void {
// this.criteriaFormGroup = criteria.formGroup;
// this.toDescriptionCriteria(criteria);
// this.refresh();
// }
// toDescriptionCriteria(criteria: DescriptionCriteriaComponent) {
// let formGroup = criteria.formGroup;
// this.criteria = {
// like: formGroup.get("like").value,
// status: formGroup.get("status").value,
// allVersions: formGroup.get("allVersions").value,
// role: formGroup.get("role").value
// }
// if (formGroup.get("tags") && formGroup.get("tags").value) {
// this.criteria.tags = formGroup.get("tags").value.map(x => (<ExternalTagEditorModel>x));
// }
// if (formGroup.get("collaborators") && formGroup.get("collaborators").value) {
// this.criteria.collaborators = formGroup.get("collaborators").value.map(x => x.id);
// }
// if (formGroup.get("dmpIds") && formGroup.get("dmpIds").value) {
// this.criteria.dmpIds = formGroup.get("dmpIds").value.map(x => x.id);
// }
// if (formGroup.get("groupIds") && formGroup.get("groupIds").value) {
// this.criteria.groupIds = formGroup.get("groupIds").value.map(x => x.groupId);
// }
// if (formGroup.get("grants") && formGroup.get("grants").value) {
// this.criteria.grants = formGroup.get("grants").value.map(x => x.id);
// }
// if (formGroup.get("organisations") && formGroup.get("organisations").value) {
// this.criteria.organisations = formGroup.get("organisations").value.map(x => x.id);
// }
// if (formGroup.get("descriptionTemplates") && formGroup.get("descriptionTemplates").value) {
// this.criteria.descriptionTemplates = formGroup.get("descriptionTemplates").value.map(x => x.id)
// }
// if (formGroup.get("grantStatus") && formGroup.get("grantStatus").value) {
// this.criteria.grantStatus = formGroup.get("grantStatus").value;
// }
// this.criteria.isPublic = this.isPublic;
// }
hasScrollbar(): boolean {
return document.getElementById("main-page").scrollHeight > document.documentElement.clientHeight
@ -360,16 +293,16 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
public setDashboardTourDmpText(): void {
this.dmpText = this.language.instant('DMP-LISTING.TEXT-INFO') + '\n\n' +
this.language.instant('DMP-LISTING.TEXT-INFO-QUESTION') + ' ' +
this.language.instant('DMP-LISTING.LINK-ZENODO') + ' ' +
this.language.instant('DMP-LISTING.GET-IDEA');
this.language.instant('DMP-LISTING.TEXT-INFO-QUESTION') + ' ' +
this.language.instant('DMP-LISTING.LINK-ZENODO') + ' ' +
this.language.instant('DMP-LISTING.GET-IDEA');
this.dashboardTour.steps[0].title = this.dmpText;
}
public setDashboardTourDescriptionText(): void {
this.descriptionText = this.language.instant('DESCRIPTION-LISTING.TEXT-INFO') + '\n\n' +
this.language.instant('DESCRIPTION-LISTING.TEXT-INFO-QUESTION') + ' ' +
this.language.instant('DESCRIPTION-LISTING.GET-IDEA');
this.language.instant('DESCRIPTION-LISTING.TEXT-INFO-QUESTION') + ' ' +
this.language.instant('DESCRIPTION-LISTING.GET-IDEA');
this.dashboardTour.steps[1].title = this.descriptionText;
}
@ -416,7 +349,7 @@ export class DescriptionListingComponent extends BaseComponent implements OnInit
}
toggleSortDirection(): void {
this.sortDirection = this.isAscending ? this.sortDirection = SortDirection.Descending : this.sortDirection = SortDirection.Ascending;
this.sortDirection = this.isAscending ? this.sortDirection = SortDirection.Descending : this.sortDirection = SortDirection.Ascending;
this.orderByChanged();
}
}

View File

@ -1,6 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '@common/base/base.component';
import { Location } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
@ -18,6 +17,7 @@ import { Dmp, DmpDescriptionTemplate, DmpUser, DmpUserRemovePersist } from '@app
import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { Reference } from '@app/core/model/reference/reference';
import { User } from '@app/core/model/user/user';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { DescriptionService } from '@app/core/services/description/description.service';
@ -28,22 +28,21 @@ import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
import { ReferenceService } from '@app/core/services/reference/reference.service';
import { UserService } from '@app/core/services/user/user.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component';
import { DescriptionValidationOutput } from '@app/ui/dmp/dmp-finalize-dialog/dmp-finalize-dialog.component';
import { DmpInvitationDialogComponent } from '@app/ui/dmp/invitation/dialog/dmp-invitation-dialog.component';
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
import { BaseComponent } from '@common/base/base.component';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { map, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { DescriptionCopyDialogComponent } from '../description-copy-dialog/description-copy-dialog.component';
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { Observable, of } from 'rxjs';
import { UserService } from '@app/core/services/user/user.service';
import { User } from '@app/core/model/user/user';
@Component({
@ -59,7 +58,6 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
isFinalized = false;
isPublicView = true;
hasPublishButton: boolean = true;
// breadCrumbs: Observable<BreadcrumbItem[]> = observableOf();
expand = false;
isLocked: Boolean;
descriptionStatusEnum = DescriptionStatus;
@ -189,14 +187,14 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
}
});
if (this.isAuthenticated()) {
this.userService.getSingle(this.authentication.userId(), [
nameof<User>(x => x.id),
nameof<User>(x => x.name)])
.pipe(map(u => u.name)).subscribe(name => this.userName = name);
} else {
this.userName = '';
}
if (this.isAuthenticated()) {
this.userService.getSingle(this.authentication.userId(), [
nameof<User>(x => x.id),
nameof<User>(x => x.name)])
.pipe(map(u => u.name)).subscribe(name => this.userName = name);
} else {
this.userName = '';
}
}
get unauthorizedTootipText(): string {
@ -216,10 +214,10 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
});
}
},
error => {
this.router.navigate(['/descriptions']);
this.httpErrorHandlingService.handleBackedRequestError(error);
});
error => {
this.router.navigate(['/descriptions']);
this.httpErrorHandlingService.handleBackedRequestError(error);
});
}
onFetchingDeletedCallbackError(redirectRoot: string) {
@ -417,39 +415,39 @@ export class DescriptionOverviewComponent extends BaseComponent implements OnIni
finalize(description: Description) {
this.descriptionService.validate([description.id]).pipe(takeUntil(this._destroyed))
.subscribe(result => {
if (result[0].result == DescriptionValidationOutput.Invalid) {
this.router.navigate(['descriptions/edit/' + description.id + '/finalize']);
} else {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
restoreFocus: false,
data: {
message: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.TITLE'),
confirmButton: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.CONFIRM'),
cancelButton: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.NEGATIVE'),
isDeleteConfirmation: false
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
const descriptionStatusPersist: DescriptionStatusPersist = {
id: description.id,
status: DescriptionStatus.Finalized,
hash: description.hash
};
this.descriptionService.persistStatus(descriptionStatusPersist).pipe(takeUntil(this._destroyed))
.subscribe(data => {
this.reloadPage();
this.onUpdateCallbackSuccess()
}, (error: any) => {
this.onUpdateCallbackError(error)
});
}
},
.subscribe(result => {
if (result[0].result == DescriptionValidationOutput.Invalid) {
this.router.navigate(['descriptions/edit/' + description.id + '/finalize']);
} else {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
restoreFocus: false,
data: {
message: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.TITLE'),
confirmButton: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.CONFIRM'),
cancelButton: this.language.instant('DESCRIPTION-OVERVIEW.FINALIZE-DIALOG.NEGATIVE'),
isDeleteConfirmation: false
}
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
const descriptionStatusPersist: DescriptionStatusPersist = {
id: description.id,
status: DescriptionStatus.Finalized,
hash: description.hash
};
this.descriptionService.persistStatus(descriptionStatusPersist).pipe(takeUntil(this._destroyed))
.subscribe(data => {
this.reloadPage();
this.onUpdateCallbackSuccess()
}, (error: any) => {
this.onUpdateCallbackError(error)
});
}
},
error => this.httpErrorHandlingService.handleBackedRequestError(error));
}
},
error => this.httpErrorHandlingService.handleBackedRequestError(error));
}
},
error => this.httpErrorHandlingService.handleBackedRequestError(error));
}
hasReversableStatus(description: Description): boolean {

View File

@ -317,11 +317,6 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
formSubmit(): void {
this.formService.removeAllBackEndErrors(this.formGroup);
// this.formService.touchAllFormFields(this.formGroup);
// if (this.formGroup.get('label').valid && this.formGroup.get('blueprint').valid && this.formGroup.get('status').valid
// && this.formGroup.get('descriptionTemplates').valid) {
// this.persistEntity();
// }
this.persistEntity();
}

View File

@ -53,9 +53,6 @@ export class DmpEditorResolver extends BaseEditorResolver {
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.email)].join('.'),
// [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.id)].join('.'),
// [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.repositoryId)].join('.'),
// [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.status)].join('.'),
@ -88,11 +85,6 @@ export class DmpEditorResolver extends BaseEditorResolver {
[nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.isActive)].join('.'),
// nameof<Dmp>(x => x.id),
// nameof<Dmp>(x => x.label),
// nameof<Dmp>(x => x.status),
// nameof<Dmp>(x => x.description),
// nameof<Dmp>(x => x.status),
...DmpEditorResolver.blueprintLookupFields(nameof<Dmp>(x => x.blueprint)),
]

View File

@ -1,33 +0,0 @@
<div class="main-info" [formGroup]="formGroup">
<div class="col-12 intro">
<p>{{'DMP-EDITOR.DATASET-INFO.SECOND-INTRO' | translate}}</p>
</div>
<div class="col-12 card">
<div class="row">
<div class="col-12">
<div class="heading">4.1 {{'DMP-EDITOR.STEPPER.DESCRIPTION-INFO' | translate}}*</div>
<div class="hint">
<div class="pb-1">{{'DMP-EDITOR.DATASET-INFO.HINT' | translate}}</div>
<div><span class="material-icons-outlined align-bottom">info</span> {{'DMP-EDITOR.MAIN-INFO.TYPING' | translate}}</div>
</div>
<div class="profile-form">
<mat-form-field>
<mat-label>{{'DMP-EDITOR.FIELDS.SELECT-TEMPLATE' | translate}}</mat-label>
<app-multiple-auto-complete placeholder="{{'DMP-EDITOR.FIELDS.SELECT-TEMPLATE' | translate}}" required='true' [formControl]="formGroup.get('profiles')" [configuration]="profilesAutoCompleteConfiguration" (optionRemoved)="onRemoveTemplate($event)" (optionActionClicked)="onPreviewTemplate($event)" (optionSelected)="onOptionSelected()">
</app-multiple-auto-complete>
<mat-error *ngIf="formGroup.get('profiles').hasError('backendError')">
{{formGroup.get('profiles').getError('backendError').message}}</mat-error>
<mat-error *ngIf="formGroup.get('profiles').hasError('required')">
{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
<button matSuffix class="input-btn" [disabled]="formGroup.get('profiles').disabled" (click)="allAvailableProfiles($event)">
<mat-icon class="icon-btn">view_list</mat-icon>
</button>
</mat-form-field>
</div>
<div class="col pl-0 pt-2 pb-3 d-flex">
<span class="not-found">{{'QUICKWIZARD.CREATE-ADD.CREATE.QUICKWIZARD_CREATE.SECOND-STEP.FIELDS.HELP' | translate}}</span>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,84 +0,0 @@
.main-info {
// position: relative;
// left: 362px;
// width: calc(100% - 366px);
.intro {
text-align: left;
font-weight: 400;
letter-spacing: 0px;
color: #212121;
opacity: 1;
margin: 3rem 0rem 3rem 0rem;
}
.heading {
text-align: left;
font-weight: 700;
font-size: 18px;
letter-spacing: 0px;
color: #212121;
opacity: 0.81;
margin-top: 1.625rem;
margin-bottom: 0.625rem;
}
.hint {
text-align: left;
font-weight: 400;
font-size: 16px;
letter-spacing: 0px;
color: #212121;
opacity: 0.81;
margin-bottom: 2.125rem;
}
.title-form,
.description-form {
text-align: left;
font-weight: 400;
font-size: 16px;
letter-spacing: 0.15px;
color: #7d7d7d;
opacity: 1;
margin-bottom: 1rem;
}
// textarea::placeholder {
// font-style: oblique;
// }
.input-btn {
border: none;
color: #aaaaaa;
background-color: #ffffff00;
cursor: pointer;
}
.input-btn :hover {
color: var(--primary-color-3) !important;
}
.not-found {
font-size: 0.875rem;
font-weight: 400;
padding: 0rem 0.5rem 0rem 0rem;
}
}
::ng-deep .profile-form .mat-form-field-appearance-outline .mat-form-field-outline {
background: #fafafa !important;
}
::ng-deep .profile-form .mat-form-field-appearance-outline .mat-form-field-infix {
font-size: 1rem;
padding: 0.6em 0 1em 0 !important;
}
::ng-deep .profile-form .mat-form-field-wrapper {
padding-bottom: 0rem;
}
:host ::ng-deep .multiple-auto-complete .align-arrow-right {
bottom: 1rem !important;
}

View File

@ -1,258 +0,0 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { DmpBlueprintDefinition } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { RequestItem } from '@app/core/query/request-item';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
import { BaseComponent } from '@common/base/base.component';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { map, takeUntil } from 'rxjs/operators';
import { AvailableProfilesComponent } from '../available-profiles/available-profiles.component';
import { DmpService } from '@app/core/services/dmp/dmp.service';
@Component({
selector: 'dataset-info',
templateUrl: './dataset-info.component.html',
styleUrls: ['./dataset-info.component.scss']
})
export class DatasetInfoComponent extends BaseComponent implements OnInit {
@Input() formGroup: UntypedFormGroup = null;
// @Input() datasetFormGroup: FormGroup = null;
@Input() isUserOwner: boolean;
@Input() dmp: DmpEditorModel;
@Input() hasDmpId: boolean;
@Input() isPublic: boolean;
@Input() isFinalized: boolean;
@Input() isNewVersion: boolean;
@Input() isClone: boolean;
@Output() onFormChanged: EventEmitter<any> = new EventEmitter();
availableProfiles: DatasetProfileModel[] = [];
selectedDmpBlueprintDefinition: DmpBlueprintDefinition;
profilesAutoCompleteConfiguration: MultipleAutoCompleteConfiguration;
datasetProfileDefinitionModel: DatasetDescriptionFormEditorModel;
datasetProfileDefinitionFormGroup: UntypedFormGroup;
constructor(
private language: TranslateService,
private configurationService: ConfigurationService,
private externalSourcesService: ExternalSourcesService,
private datasetWizardService: DatasetWizardService,
private dialog: MatDialog,
private _service: DmpService,
private dmpBlueprintService: DmpBlueprintService,
private router: Router,
private route: ActivatedRoute,
private uiNotificationService: UiNotificationService
) {
super();
}
ngOnInit() {
try {
const profiles = this.formGroup.get('profiles').value as { id: string, label: string }[];
profiles.sort((a, b) => a.label.localeCompare(b.label));
} catch {
console.info('Could not sort profiles');
}
this.profilesAutoCompleteConfiguration = {
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'],
titleFn: (item) => item['label'],
subtitleFn: (item) => item['description'],
popupItemActionIcon: 'visibility'
};
if (this.formGroup.get('definition')) { this.selectedDmpBlueprintDefinition = this.formGroup.get('definition').value; }
this.registerFormEventsForDmpBlueprint();
if (this.hasDmpId) {
this.loadDatasetProfiles();
this.profilesAutoCompleteConfiguration = {
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'],
titleFn: (item) => item['label'],
subtitleFn: (item) => item['description'],
popupItemActionIcon: 'visibility'
};
}
}
// Researchers
filterProfiles(value: string): Observable<DatasetProfileModel[]> {
const request = new DataTableRequest<DatasetProfileCriteria>(null, null, { fields: ['+label'] });
const criteria = new DatasetProfileCriteria();
criteria.like = value;
request.criteria = criteria;
return this._service.searchDmpBlueprints(request);
}
registerFormEventsForDmpBlueprint(definitionProperties?: DmpBlueprintDefinition): void {
this.formGroup.get('profile').valueChanges
.pipe(
takeUntil(this._destroyed))
.subscribe(Option => {
if (Option instanceof Object) {
this.selectedDmpBlueprintDefinition = null;
this.dmpBlueprintService.getSingle(Option.id)
.pipe(takeUntil(this._destroyed))
.subscribe(result => {
this.selectedDmpBlueprintDefinition = result.definition;
});
} else {
this.selectedDmpBlueprintDefinition = null;
}
this.selectedDmpBlueprintDefinition = definitionProperties;
})
}
loadDatasetProfiles() {
const datasetProfileRequestItem: RequestItem<DatasetProfileCriteria> = new RequestItem();
datasetProfileRequestItem.criteria = new DatasetProfileCriteria();
this.formGroup.get('id').value ? datasetProfileRequestItem.criteria.id = this.formGroup.get('id').value : datasetProfileRequestItem.criteria.id = this.formGroup.get('datasets')['controls'][0].get('dmp').value.id;
if (datasetProfileRequestItem.criteria.id) {
this.datasetWizardService.getAvailableProfiles(datasetProfileRequestItem)
.pipe(takeUntil(this._destroyed))
.subscribe(items => {
this.availableProfiles = items;
});
}
}
allAvailableProfiles(event: MouseEvent) {
event.stopPropagation();
const dialogRef = this.dialog.open(AvailableProfilesComponent, {
data: {
profiles: this.formGroup.get('profiles')
}
});
return false;
}
// dmpValueChanged(dmp: DmpListingModel) {
// if (dmp) {
// this.formGroup.get('profile').enable();
// this.loadDatasetProfiles();
// }
// else {
// this.availableProfiles = [];
// this.formGroup.get('profile').reset();
// this.formGroup.get('profile').disable();
// this.formGroup.removeControl('datasetProfileDefinition');
// }
// }
// registerFormListeners() {
// this.formGroup.get('datasets')['controls'][0].get('dmp').valueChanges
// .pipe(takeUntil(this._destroyed))
// .subscribe(x => {
// this.dmpValueChanged(x);
// });
// this.formGroup.get('profile').valueChanges
// .pipe(takeUntil(this._destroyed))
// .subscribe(x => {
// //this.datasetProfileValueChanged(x);
// });
// }
// datasetProfileValueChanged(profiledId: string) {
// if (profiledId && profiledId.length > 0) {
// this.formGroup.removeControl('datasetProfileDefinition');
// this.getDefinition();
// }
// }
// getDefinition() {
// this.datasetWizardService.getDefinition(this.formGroup.get('profile').value)
// .pipe(takeUntil(this._destroyed))
// .subscribe(item => {
// this.formGroup.get('datasets')['controls'][0].datasetProfileDefinition = new DatasetDescriptionFormEditorModel().fromModel(item);
// this.datasetProfileDefinitionModel = this.formGroup.get('datasets')['controls'][0].datasetProfileDefinition;
// this.formGroup.addControl('datasetProfileDefinition', this.datasetProfileDefinitionModel.buildForm());
// });
// }
onRemoveTemplate(event) {
let found = false;
const profiles = this.formGroup.get('profiles').value;
this.formGroup.get('datasets')['controls'].forEach(element => {
if (element.get('profile').value.id === event.id) {
found = true;
this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-REMOVE-TEMPLATE'), SnackBarNotificationLevel.Success);
}
});
if (found) {
this.formGroup.get('profiles').setValue(profiles);
this.profilesAutoCompleteConfiguration = {
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'],
titleFn: (item) => item['label'],
subtitleFn: (item) => item['description'],
popupItemActionIcon: 'visibility'
};
}
}
onPreviewTemplate(event) {
const dialogRef = this.dialog.open(DatasetPreviewDialogComponent, {
width: '590px',
minHeight: '200px',
restoreFocus: false,
data: {
template: event
},
panelClass: 'custom-modalbox'
});
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
if (result) {
let profiles = this.formGroup.get('profiles').value;
profiles.push(event);
this.formGroup.get('profiles').setValue(profiles);
this.profilesAutoCompleteConfiguration = {
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'],
titleFn: (item) => item['label'],
subtitleFn: (item) => item['description'],
popupItemActionIcon: 'visibility'
};
}
});
}
onOptionSelected() {
try {
const profiles = this.formGroup.get('profiles').value as { id: string, label: string }[];
const profileCounts: Map<String, number> = new Map<String, number>();
profiles.forEach((value) => profileCounts.set(value.id, (profileCounts.get(value.id) !== undefined ? profileCounts.get(value.id) : 0) + 1));
const duplicateProfiles = profiles.filter((value) => {
let isOk = profileCounts.get(value.id) > 1;
if (isOk) {
profileCounts.set(value.id, 0);
}
return isOk;
});
duplicateProfiles.forEach((value) => profiles.splice(profiles.lastIndexOf(value), 1));
profiles.sort((a, b) => a.label.localeCompare(b.label));
} catch {
console.info('Could not sort Dataset Templates')
}
}
}

View File

@ -6,43 +6,41 @@ import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { ActivatedRoute, Router } from '@angular/router';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { GuidedTour, Orientation } from '@app/library/guided-tour/guided-tour.constants';
import { GuidedTourService } from '@app/library/guided-tour/guided-tour.service';
// import { IBreadCrumbComponent } from '@app/ui/misc/breadcrumb/definition/IBreadCrumbComponent';
// import { BreadcrumbItem } from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DmpStatus } from '@app/core/common/enum/dmp-status';
import { DmpVersionStatus } from '@app/core/common/enum/dmp-version-status';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { RecentActivityOrder } from '@app/core/common/enum/recent-activity-order';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { Description } from '@app/core/model/description/description';
import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { Dmp, DmpDescriptionTemplate, DmpUser } from '@app/core/model/dmp/dmp';
import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { Reference } from '@app/core/model/reference/reference';
import { DmpLookup } from '@app/core/query/dmp.lookup';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { GuidedTour, Orientation } from '@app/library/guided-tour/guided-tour.constants';
import { GuidedTourService } from '@app/library/guided-tour/guided-tour.service';
import { BaseComponent } from '@common/base/base.component';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { SortDirection } from '@common/modules/hybrid-listing/hybrid-listing.component';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { NgDialogAnimationService } from "ng-dialog-animation";
import { debounceTime, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { DmpVersionStatus } from '@app/core/common/enum/dmp-version-status';
import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { Reference } from '@app/core/model/reference/reference';
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { DmpStatus } from '@app/core/common/enum/dmp-status';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { SortDirection } from '@common/modules/hybrid-listing/hybrid-listing.component';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
@Component({
selector: 'app-dmp-listing-component',
templateUrl: 'dmp-listing.component.html',
styleUrls: ['./dmp-listing.component.scss'],
})
export class DmpListingComponent extends BaseComponent implements OnInit { //IBreadCrumbComponent
export class DmpListingComponent extends BaseComponent implements OnInit {
@ViewChild(MatPaginator, { static: true }) _paginator: MatPaginator;
@ViewChild(MatSort) sort: MatSort;
@ -174,8 +172,8 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
this.language.instant('DMP-LISTING.GET-IDEA');
this.dashboardTour.steps[1].title = this.language.instant('DESCRIPTION-LISTING.TEXT-INFO') + '\n\n' +
this.language.instant('DESCRIPTION-LISTING.TEXT-INFO-QUESTION') + ' ' +
this.language.instant('DESCRIPTION-LISTING.GET-IDEA');
this.language.instant('DESCRIPTION-LISTING.TEXT-INFO-QUESTION') + ' ' +
this.language.instant('DESCRIPTION-LISTING.GET-IDEA');
this.guidedTourService.startTour(this.dashboardTour);
});
@ -186,12 +184,12 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
this.refresh(this.lookup);
}
orderByChanged(){
if (this.formGroup.get('order').value == RecentActivityOrder.Status){
orderByChanged() {
if (this.formGroup.get('order').value == RecentActivityOrder.Status) {
this.lookup.order = { items: [this.sortingDirectionPrefix + nameof<Dmp>(x => x.status)] };
} else if(this.formGroup.get('order').value == RecentActivityOrder.Label){
} else if (this.formGroup.get('order').value == RecentActivityOrder.Label) {
this.lookup.order = { items: [this.sortingDirectionPrefix + nameof<Dmp>(x => x.label)] };
}else{
} else {
this.lookup.order = { items: [this.sortingDirectionPrefix + nameof<Dmp>(x => x.updatedAt)] };
}
this.refresh(this.lookup);
@ -234,10 +232,6 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
[nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.hasTemplates)].join('.'),
[nameof<Dmp>(x => x.blueprint), nameof<DmpBlueprint>(x => x.definition), nameof<DmpBlueprintDefinition>(x => x.sections), nameof<DmpBlueprintDefinitionSection>(x => x.descriptionTemplates), nameof<DescriptionTemplatesInSection>(x => x.descriptionTemplateGroupId)].join('.'),
// [nameof<Description>(x => x.descriptionTemplate), nameof<DescriptionTemplate>(x => x.label)].join('.'),
// [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.id)].join('.'),
// [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.label)].join('.'),
// [nameof<Description>(x => x.dmp), nameof<Dmp>(x => x.accessType)].join('.'),
[nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.id)].join('.'),
[nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.user.id)].join('.'),
[nameof<Dmp>(x => x.dmpUsers), nameof<DmpUser>(x => x.role)].join('.'),
@ -253,43 +247,41 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
[nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.sectionId)].join('.'),
[nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.descriptionTemplateGroupId)].join('.'),
[nameof<Dmp>(x => x.dmpDescriptionTemplates), nameof<DmpDescriptionTemplate>(x => x.isActive)].join('.'),
// [nameof<Dmp>(x => x.dmpReferences), nameof<DmpReference>(x => x.reference), nameof<Reference>(x => x.reference)].join('.'),
]
};
if(this.isPublic){
if (this.isPublic) {
this.dmpService.publicQuery(lookup).pipe(takeUntil(this._destroyed))
.subscribe(result => {
if (!result) { return []; }
this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = [];
this.listingItems.push(...result.items);
this.hasListingItems = true;
},
error => this.httpErrorHandlingService.handleBackedRequestError(error));
.subscribe(result => {
if (!result) { return []; }
this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = [];
this.listingItems.push(...result.items);
this.hasListingItems = true;
},
error => this.httpErrorHandlingService.handleBackedRequestError(error));
}else{
} else {
this.dmpService.query(lookup).pipe(takeUntil(this._destroyed))
.subscribe(result => {
if (!result) { return []; }
this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = [];
result.items.forEach(x=> {
if (x.descriptions) {
if (x.status == DmpStatus.Finalized) {
x.descriptions = x.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else {
x.descriptions = x.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
.subscribe(result => {
if (!result) { return []; }
this.totalCount = result.count;
if (lookup?.page?.offset === 0) this.listingItems = [];
result.items.forEach(x => {
if (x.descriptions) {
if (x.status == DmpStatus.Finalized) {
x.descriptions = x.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
} else {
x.descriptions = x.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
}
}
}
x.dmpUsers = x.dmpUsers.filter(x=> x.isActive === IsActive.Active);
this.listingItems.push(x);
})
// this.listingItems.push(...result.items);
this.hasListingItems = true;
},
error => this.httpErrorHandlingService.handleBackedRequestError(error));
x.dmpUsers = x.dmpUsers.filter(x => x.isActive === IsActive.Active);
this.listingItems.push(x);
})
this.hasListingItems = true;
},
error => this.httpErrorHandlingService.handleBackedRequestError(error));
}
}
@ -303,92 +295,12 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
openShareDialog(rowId: any, rowName: any) {
//TODO: add this
// const dialogRef = this.dialog.open(DmpInvitationDialogComponent, {
// autoFocus: false,
// data: {
// dmpId: rowId,
// dmpName: rowName
// }
// });
}
openFiltersDialog(): void {
//TODO: Add filters dialog
// const dialogRef = this.dialog.open(DmpCriteriaDialogComponent, {
// width: '456px',
// height: '100%',
// id: 'filters',
// restoreFocus: false,
// data: {
// showGrant: this.showGrant,
// isPublic: this.isPublic,
// criteria: this.criteria,
// formGroup: this.criteriaFormGroup,
// // criteria: this.grantId ? this.criteria : this.getDefaultCriteria(),
// updateDataFn: this.updateDataFn.bind(this)
// },
// position: { right: '0px;' },
// panelClass: 'dialog-side-panel',
// // may remove NgDialogAnimationService package
// // animation: {
// // to: "left",
// // incomingOptions: {
// // keyframeAnimationOptions: { duration: 300, easing: "ease-in-out" }
// // }
// // }
// });
// dialogRef.afterClosed().subscribe(result => {
// });
}
// updateDataFn(criteria: DmpCriteriaComponent): void {
// this.criteriaFormGroup = criteria.formGroup;
// this.toDmpCriteria(criteria);
// this.refresh();
// }
// toDmpCriteria(criteria: DmpCriteriaComponent): void {
// let formGroup = criteria.formGroup;
// this.criteria = {
// like: formGroup.get('like').value,
// grants: formGroup.get('grants').value,
// role: formGroup.get('role').value
// }
// this.criteria.status = formGroup.get('status').value;
// this.setPublicCriteria(formGroup);
// if (formGroup.get('datasetTemplates').value)
// this.criteria.datasetTemplates = formGroup.get('datasetTemplates').value.map(x => x.id);
// if (formGroup.get('collaborators').value)
// this.criteria.collaborators = formGroup.get('collaborators').value.map(x => x.id);
// if (formGroup.get('organisations').value)
// this.criteria.organisations = formGroup.get('organisations').value.map(x => x.id);
// if (this.itemId) {
// this.criteria.groupIds = [this.itemId];
// this.criteria.allVersions = true;
// }
// this.criteria.grantStatus = formGroup.get('grantStatus').value;
// }
// setPublicCriteria(formGroup?: UntypedFormGroup): void {
// if (!isNullOrUndefined(formGroup)) {
// if (formGroup.get('status').value == 2) {
// this.criteria.status = 1;
// this.criteria.isPublic = true;
// } else {
// this.criteria.isPublic = false;
// }
// }
// this.criteria.onlyPublic = this.isPublic;
// if (this.isPublic) {
// this.criteria.isPublic = true;
// }
// // } else {
// // this.criteria.isPublic = false;
// // }
// }
hasScrollbar(): boolean {
return document.getElementById("main-page").scrollHeight > document.documentElement.clientHeight
}
@ -403,8 +315,8 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
public setDashboardTourDatasetText(): void {
const datasetText = this.language.instant('DESCRIPTION-LISTING.TEXT-INFO') + '\n\n' +
this.language.instant('DESCRIPTION-LISTING.TEXT-INFO-QUESTION') + ' ' +
this.language.instant('DESCRIPTION-LISTING.GET-IDEA');
this.language.instant('DESCRIPTION-LISTING.TEXT-INFO-QUESTION') + ' ' +
this.language.instant('DESCRIPTION-LISTING.GET-IDEA');
this.dashboardTour.steps[1].title = datasetText;
}
@ -419,7 +331,7 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
}
toggleSortDirection(): void {
this.sortDirection = this.isAscending ? this.sortDirection = SortDirection.Descending : this.sortDirection = SortDirection.Ascending;
this.sortDirection = this.isAscending ? this.sortDirection = SortDirection.Descending : this.sortDirection = SortDirection.Ascending;
this.orderByChanged();
}
}

View File

@ -1,18 +1,10 @@
import { Location } from '@angular/common';
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { DmpStatus } from '@app/core/common/enum/dmp-status';
import { AuthService } from '@app/core/services/auth/auth.service';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import {
SnackBarNotificationLevel,
UiNotificationService
} from '@app/core/services/notification/ui-notification-service';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
// import {BreadcrumbItem} from '@app/ui/misc/breadcrumb/definition/breadcrumb-item';
import { Location } from '@angular/common';
import { DescriptionStatus } from '@app/core/common/enum/description-status';
import { DmpAccessType } from '@app/core/common/enum/dmp-access-type';
import { DmpStatus } from '@app/core/common/enum/dmp-status';
import { DmpUserRole } from '@app/core/common/enum/dmp-user-role';
import { DmpVersionStatus } from '@app/core/common/enum/dmp-version-status';
import { FileTransformerEntityType } from '@app/core/common/enum/file-transformer-entity-type';
@ -27,18 +19,29 @@ import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { EntityDoi } from '@app/core/model/entity-doi/entity-doi';
import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { Reference } from '@app/core/model/reference/reference';
import { User } from '@app/core/model/user/user';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { DepositService } from '@app/core/services/deposit/deposit.service';
import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service';
import { DmpService } from '@app/core/services/dmp/dmp.service';
import { FileTransformerService } from '@app/core/services/file-transformer/file-transformer.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import {
SnackBarNotificationLevel,
UiNotificationService
} from '@app/core/services/notification/ui-notification-service';
import { ReferenceTypeService } from '@app/core/services/reference-type/reference-type.service';
import { ReferenceService } from '@app/core/services/reference/reference.service';
import { UserService } from '@app/core/services/user/user.service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { FileUtils } from '@app/core/services/utilities/file-utils.service';
import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component';
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
import { BaseComponent } from '@common/base/base.component';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { map, takeUntil } from 'rxjs/operators';
@ -49,11 +52,6 @@ import { DmpEditorResolver } from '../dmp-editor-blueprint/dmp-editor.resolver';
import { DmpFinalizeDialogComponent, DmpFinalizeDialogOutput } from '../dmp-finalize-dialog/dmp-finalize-dialog.component';
import { DmpInvitationDialogComponent } from '../invitation/dialog/dmp-invitation-dialog.component';
import { NewVersionDmpDialogComponent } from '../new-version-dialog/dmp-new-version-dialog.component';
import { BreadcrumbService } from '@app/ui/misc/breadcrumb/breadcrumb.service';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { User } from '@app/core/model/user/user';
import { UserService } from '@app/core/services/user/user.service';
import { Observable, of } from 'rxjs';
@Component({
selector: 'app-dmp-overview',
@ -70,8 +68,6 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
isFinalized = false;
isPublicView = true;
hasPublishButton: boolean = true;
// breadCrumbs: Observable<BreadcrumbItem[]> = observableOf();
// isUserOwner: boolean;
isLocked: Boolean;
textMessage: any;
selectedModel: EntityDoi;
@ -116,7 +112,7 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
) {
super();
}
ngOnInit() {
this.analyticsService.trackPageView(AnalyticsService.DmpOverview);
// Gets dmp data using parameter id
@ -210,10 +206,10 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
this.depositRepos = repos;
},
error => this.depositRepos = []);
this.userService.getSingle(this.authentication.userId(), [
nameof<User>(x => x.id),
nameof<User>(x => x.name)])
nameof<User>(x => x.id),
nameof<User>(x => x.name)])
.pipe(map(u => u.name)).subscribe(name => this.userName = name);
} else {
this.userName = '';
@ -324,90 +320,6 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
});
}
// private checkForGrant(blueprint: DmpBlueprintDefinition) {
// let hasGrant = false;
// blueprint.sections.forEach(section => section.fields.forEach(
// field => {
// if (field.category as unknown === DmpBlueprintFieldCategory.System && field.systemFieldType === DmpBlueprintSystemFieldType.GRANT) {
// hasGrant = true;
// }
// }
// ));
// if (!hasGrant) {
// this.formGroup.removeControl('grant');
// }
// }
// private checkForFunder(blueprint: DmpBlueprintDefinition) {
// let hasFunder = false;
// blueprint.sections.forEach(section => section.fields.forEach(
// field => {
// if (field.category as unknown === DmpBlueprintFieldCategory.System && field.systemFieldType === DmpBlueprintSystemFieldType.FUNDER) {
// hasFunder = true;
// }
// }
// ));
// if (!hasFunder) {
// this.formGroup.removeControl('funder');
// }
// }
// private checkForProject(blueprint: DmpBlueprintDefinition) {
// let hasProject = false;
// blueprint.sections.forEach(section => section.fields.forEach(
// field => {
// if (field.category as unknown === DmpBlueprintFieldCategory.System && field.systemFieldType === DmpBlueprintSystemFieldType.PROJECT) {
// hasProject = true;
// }
// }
// ));
// if (!hasProject) {
// this.formGroup.removeControl('project');
// }
// }
// openCloneDialog(isNewVersion: boolean) {
// const dialogRef = this.dialog.open(CloneDialogComponent, {
// maxWidth: '900px',
// maxHeight: '80vh',
// data: {
// formGroup: this.formGroup,
// descriptions: this.dmp.descriptions,
// isNewVersion: isNewVersion,
// confirmButton: this.language.instant('DMP-EDITOR.CLONE-DIALOG.SAVE'),
// cancelButton: this.language.instant('DMP-EDITOR.CLONE-DIALOG.CANCEL'),
// }
// });
// dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
// if (result) {
// if (!isNewVersion) {
// this.dmpService.clone(this.formGroup.getRawValue(), this.dmp.id)
// .pipe(takeUntil(this._destroyed))
// .subscribe(
// complete => this.onCallbackSuccess(complete),
// error => this.onCallbackError(error)
// );
// } else if (isNewVersion) {
// this.dmpService.newVersion(this.formGroup.getRawValue(), this.dmp.id)
// .pipe(takeUntil(this._destroyed))
// .subscribe(
// complete => this.onCallbackSuccess(complete),
// error => this.onCallbackError(error)
// );
// }
// }
// });
// }
// onCallbackSuccess(dmpId: String): void {
// this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Success);
// this.router.navigate(['/plans/edit/', dmpId]);
// }
// onCallbackError(error: any) {
// this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-UPDATE'), SnackBarNotificationLevel.Error);
// }
deleteClicked() {
let dialogRef: any;
if (this.dmp.descriptions && this.dmp.descriptions.length > 0) {
@ -520,38 +432,6 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
}
// private checkIfAnyProfileIsUsedLessThanMin(dmp: Dmp): Observable<boolean> {
// const blueprintId = dmp.profile.id;
// return this.dmpBlueprintService.getSingle(Guid.parse(blueprintId))
// .pipe(map(result => {
// return result.definition.sections.some(section => {
// if (!section.hasTemplates)
// return false;
// return section.descriptionTemplates.some(template => {
// if (!(template.minMultiplicity > 0))
// return false;
// let count = 0;
// dmp.descriptions.filter(description => description.dmpSectionIndex === (section.ordinal - 1)).forEach(description => {
// if (Guid.parse(description.profile.id) === template.descriptionTemplateId) {
// count++;
// }
// })
// if (count < template.minMultiplicity) {
// this.dialog.open(PopupNotificationDialogComponent, {
// data: {
// title: this.language.instant('DMP-OVERVIEW.MIN-DESCRIPTIONS-DIALOG.TITLE', { 'minMultiplicity': template.minMultiplicity }),
// message: this.language.instant('DMP-OVERVIEW.MIN-DESCRIPTIONS-DIALOG.MESSAGE',)
// }, maxWidth: '30em'
// });
// return true;
// }
// else
// return false;
// })
// })
// }), takeUntil(this._destroyed));
// }
public isAuthenticated(): boolean {
return this.authentication.currentAccountIsAuthenticated();
}
@ -630,16 +510,6 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
});
}
// updateUsers() {
// return this.dmpService.updateUsers(this.dmp.id, this.dmp.users).pipe(takeUntil(this._destroyed))
// .subscribe(
// complete => {
// this.onUpdateCallbackSuccess();
// },
// error => this.onUpdateCallbackError(error)
// );
// }
removeUserFromDmp(dmpUser: DmpUser) {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
data: {
@ -714,10 +584,10 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
});
}
},
error => {
this.router.navigate(['/plans']);
this.httpErrorHandlingService.handleBackedRequestError(error);
});
error => {
this.router.navigate(['/plans']);
this.httpErrorHandlingService.handleBackedRequestError(error);
});
}
private lookupFields(): string[] {

View File

@ -4,7 +4,6 @@ import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { MatMenuTrigger } from '@angular/material/menu';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { Router } from '@angular/router';
import { AppRole } from '@app/core/common/enum/app-role';
import { TenantConfigurationType } from '@app/core/common/enum/tenant-configuration-type';
import { StorageFile } from '@app/core/model/storage-file/storage-file';
import { LogoTenantConfiguration, TenantConfiguration } from '@app/core/model/tenant-configuaration/tenant-configuration';
@ -17,6 +16,7 @@ import { ProgressIndicationService } from '@app/core/services/progress-indicatio
import { SideNavService } from '@app/core/services/sidenav/side-nav.sevice';
import { StorageFileService } from '@app/core/services/storage-file/storage-file.service';
import { TenantConfigurationService } from '@app/core/services/tenant-configuration/tenant-configuration.service';
import { UserService } from '@app/core/services/user/user.service';
import { BaseComponent } from '@common/base/base.component';
import { InAppNotificationService } from '@notification-service/services/http/inapp-notification.service';
import { MineInAppNotificationListingDialogComponent } from '@notification-service/ui/inapp-notification/listing-dialog/mine-inapp-notification-listing-dialog.component';
@ -26,7 +26,6 @@ import { nameof } from 'ts-simple-nameof';
import { StartNewDmpDialogComponent } from '../dmp/new/start-new-dmp-dialogue/start-new-dmp-dialog.component';
import { FaqDialogComponent } from '../faq/dialog/faq-dialog.component';
import { UserDialogComponent } from './user-dialog/user-dialog.component';
import { UserService } from '@app/core/services/user/user.service';
@Component({
selector: 'app-navbar',
@ -230,21 +229,6 @@ export class NavbarComponent extends BaseComponent implements OnInit {
}
};
// getTitle() {
// var titlee = this.location.prepareExternalUrl(this.location.path());
// if (titlee.charAt(0) === '#') {
// titlee = titlee.slice(2);
// }
// titlee = titlee.split('/').pop();
// for (var item = 0; item < this.listTitles.length; item++) {
// if (this.listTitles[item].path === titlee) {
// return this.listTitles[item].title;
// }
// }
// return 'Dashboard';
// }
public getCurrentLanguage(): any {
const lang = this.languages.find(lang => lang.value === this.languageService.getCurrentLanguage());
return lang;
@ -270,10 +254,6 @@ export class NavbarComponent extends BaseComponent implements OnInit {
(ev.target as HTMLImageElement).src = this.getDefaultAvatar();
}
public isAdmin(): boolean {
return this.authentication.hasRole(AppRole.Admin);
}
openProfile() {
const dialogRef = this.dialog.open(UserDialogComponent, {
hasBackdrop: true,

View File

@ -113,13 +113,6 @@ export class ReferenceFieldComponent extends BaseComponent implements OnInit, On
} else {
this.referenceToUse = referenceToUse;
}
// if (this.referenceToUseInitialized && (!referenceToUse.map(x => x.reference).every(x => this.referenceToUse.map(y => y.reference).includes(x)) ||
// !this.referenceToUse.map(x => x.reference).every(x => referenceToUse.map(y => y.reference).includes(x)))) {
// this.form.setValue(null, {onlySelf: true, emitEvent: false});
// }
// this.referenceToUse = referenceToUse;
// this.referenceToUseInitialized = true;
if (this.multiple) {
this.multipleAutoCompleteSearchConfiguration = this.referenceService.getMultipleAutoCompleteSearchConfiguration(this.referenceType.id, this.referenceToUse);

View File

@ -3,14 +3,12 @@ import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { TranslateService } from '@ngx-translate/core';
import { AppRole } from '../../core/common/enum/app-role';
import { AuthService, LoginStatus } from '../../core/services/auth/auth.service';
import { AuthService } from '../../core/services/auth/auth.service';
import { LanguageDialogComponent } from '../language/dialog/language-dialog.component';
import { UserDialogComponent } from '../navbar/user-dialog/user-dialog.component';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { takeUntil } from 'rxjs/operators';
import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
declare interface RouteInfo {
path: string;
@ -151,14 +149,6 @@ export class SidebarComponent implements OnInit {
return this.authentication.currentAccountIsAuthenticated();
}
public isAdmin(): boolean {
return this.authentication.hasRole(AppRole.Admin);
}
public hasPermission(role: AppRole): boolean {
return this.authentication.hasRole(role);
}
isLoginRouteActivated(): boolean {
return this.location.path().indexOf('/login') > -1;
}

View File

@ -1,9 +1,8 @@
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { Component, OnInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-splash',
@ -23,22 +22,8 @@ export class SplashComponent extends BaseComponent implements OnInit {
}
ngOnInit() {
// this.httpClient.get(this.configurationService.splashPath, {responseType: "text"})
// .pipe(takeUntil(this._destroyed)).subscribe(response => {
// const blob = new Blob([response], { type: 'text/html' });
// this.readBlob(blob);
// });
// this.splashHTML = this.sanitizer.bypassSecurityTrustHtml(`${this.configurationService.app}/${this.configurationService.splashPath}`);
}
// readBlob(blob: Blob) {
// const fr = new FileReader();
// fr.onload = ev => {
// this.splashHTML = this.sanitizer.bypassSecurityTrustHtml(fr.result as string);
// //this.parse();
// };
// fr.readAsText(blob);
// }
}
resizeFrame() {
const frame: HTMLIFrameElement = (document.getElementById('splash') as HTMLIFrameElement);
@ -46,7 +31,6 @@ export class SplashComponent extends BaseComponent implements OnInit {
}
getSplashUrl() {
// return this.sanitizer.bypassSecurityTrustHtml(this.configurationService.splashPath);
}
}

View File

@ -9,7 +9,7 @@ import { AnalyticsService } from '@app/core/services/matomo/analytics-service';
import { SupportiveMaterialService } from '@app/core/services/supportive-material/supportive-material.service';
import { BaseComponent } from '@common/base/base.component';
import { LangChangeEvent, TranslateService } from '@ngx-translate/core';
import { interval, Subject } from 'rxjs';
import { Subject, interval } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Component({
@ -60,16 +60,7 @@ export class UserGuideContentComponent extends BaseComponent implements OnInit {
this.readBlob(blob);
} else {
this.guideHTMLUrl = this.sanitizer.bypassSecurityTrustResourceUrl((window.URL ? URL : webkitURL).createObjectURL(blob));
//GK: In case the app is in localhost (dev/debug build) apply the following transformation
// in order to show the user guide's images
// if (this.guideHTMLUrl && this.configurationService.app.includes('localhost')) {
// interval(1000).pipe(takeUntil(this._transformed)).subscribe(() => this.transform());
// }
}
// this.guideHTML = this.sanitizer.sanitize(SecurityContext.HTML, blob);
// this.sanitizedGuideUrl = this.sanitizer.sanitize(SecurityContext.URL, this.guideHTMLUrl);
// console.log(this.guideHTMLUrl);
});
}
@ -161,18 +152,6 @@ export class UserGuideContentComponent extends BaseComponent implements OnInit {
onIFrameLoad(iframe: HTMLIFrameElement) {
try {
// const contentDocument = iframe.contentDocument;
// const URI = contentDocument.URL;
// const refs = contentDocument.getElementsByTagName('a');
// const navLinks:HTMLAnchorElement[] = [];//only navigation links
// for(let i =0; i<refs.length;i++){
// const ref = refs[i];
// if(ref.classList.contains('nav-link')){
// navLinks.push(ref);
// }
// }
// navLinks.forEach(a=>a.href = URI+a.hash);
const images = iframe.contentWindow.document.getElementsByTagName('img');
for (let i = 0; i < images.length; i++) {
@ -181,11 +160,6 @@ export class UserGuideContentComponent extends BaseComponent implements OnInit {
tempDiv.innerHTML = currentImage.outerHTML.trim();
currentImage.src = (tempDiv.firstChild as HTMLImageElement).src;
}
// const elem = tempDiv.firstChild as HTMLImageElement;
// console.log('eleme', elem);
// firstimage.src = elem.src;
} catch {
console.warn('Could not find contentDocument');
}

View File

@ -4,27 +4,26 @@ import { Component, OnInit } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component';
import { BaseEntity } from '@common/base/base-entity.model';
import { BasePendingChangesComponent } from '@common/base/base-pending-changes.component';
import { FormService } from '@common/forms/form-service';
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { FilterService } from '@common/modules/text-filter/filter-service';
import { TranslateService } from '@ngx-translate/core';
import { interval, Observable } from 'rxjs';
import { isNullOrUndefined } from '@swimlane/ngx-datatable';
import { Observable, interval } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { Guid } from '../types/guid';
import { BaseEditorModel } from './base-form-editor-model';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { QueryParamsService } from '@app/core/services/utilities/query-params.service';
import { LockService } from '@app/core/services/lock/lock.service';
import { LockPersist } from '@app/core/model/lock/lock.model';
import { LockTargetType } from '@app/core/common/enum/lock-target-type';
import { isNullOrUndefined } from '@swimlane/ngx-datatable';
import { PopupNotificationDialogComponent } from '@app/library/notification/popup/popup-notification.component';
import { AuthService } from '@app/core/services/auth/auth.service';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
import { ResponseErrorCode } from '@app/core/common/enum/respone-error-code';
@Component({
selector: 'app-base-editor-component',
@ -73,13 +72,13 @@ export abstract class BaseEditor<EditorModelType extends BaseEditorModel, Entity
public ngOnInit(): void {
const entity = this.route.snapshot.data['entity'] as EntityType;
if(entity) {
if (entity) {
this.isNew = false;
this.prepareForm(entity);
if (this.formGroup && this.editorModel.belongsToCurrentTenant == false) {
this.formGroup.disable();
}
}else{
this.formGroup.disable();
}
} else {
this.prepareForm(null);
}
@ -131,11 +130,11 @@ export abstract class BaseEditor<EditorModelType extends BaseEditorModel, Entity
const error: HttpError = this.httpErrorHandlingService.getError(errorResponse);
if (error.statusCode === 400) {
this.editorModel.validationErrorModel.fromJSONObject(errorResponse.error);
if(errorResponse.error.code === ResponseErrorCode.DmpDescriptionTemplateCanNotRemove){
if (errorResponse.error.code === ResponseErrorCode.DmpDescriptionTemplateCanNotRemove) {
this.refreshOnNavigateToData(null);
}
this.formService.validateAllFormFields(this.formGroup);
}
}
@ -145,20 +144,7 @@ export abstract class BaseEditor<EditorModelType extends BaseEditorModel, Entity
this.formService.validateAllFormFields(this.formGroup);
}
// private refreshOnNavigateToData(id?: Guid): void {
// if (this.isNew) {
// this.formGroup.markAsPristine();
// this.router.navigate([this.formUtilsService.getFormRoute(this.editorModel.type) + '/' + (id ? id : this.editorModel.id)]);
// } else { this.internalRefreshData(); }
// }
internalRefreshData(): void {
// setTimeout(() => {
// this.formGroup = null;
// this.editorModel = null;
// });
this.refreshData();
}
@ -221,6 +207,6 @@ export abstract class BaseEditor<EditorModelType extends BaseEditorModel, Entity
ngOnDestroy(): void {
super.ngOnDestroy();
if(this.isLockedByUser) this.unlockTarget(this.editorModel.id);
if (this.isLockedByUser) this.unlockTarget(this.editorModel.id);
}
}

View File

@ -80,14 +80,5 @@ export class FormValidationErrorsDialogComponent {
} else if (formControl.nativeElement.localName === 'app-multiple-auto-complete') {
return (Array.from(formControl.nativeElement.firstChild.firstChild.firstChild.children).filter((x: any) => x.localName === 'input')[0] as any).getAttribute('placeholder');
}
// Needs to have <mat-label> in <mat-form-field> in html in order to fill results
// let result = '';
// try {
// result = (Array.from(formControl.nativeElement.parentElement.children).filter((x: any) => Array.from(x.classList).includes('mat-form-field-label-wrapper'))[0] as any).innerText;
// result = result.replace(' *', '');
// } catch (error) { }
// return result;
}
}

View File

@ -5,7 +5,6 @@ import { JsonInterceptor } from './interceptors/json.interceptor';
import { LocaleInterceptor } from './interceptors/locale.interceptor';
import { ProgressIndicationInterceptor } from './interceptors/progress-indication.interceptor';
import { RequestTimingInterceptor } from './interceptors/request-timing.interceptor';
import { ResponsePayloadInterceptor } from './interceptors/response-payload.interceptor';
import { UnauthorizedResponseInterceptor } from './interceptors/unauthorized-response.interceptor';
import { StatusCodeInterceptor } from './interceptors/status-code.interceptor';
import { TenantHeaderInterceptor } from './interceptors/tenant-header.interceptor';
@ -51,11 +50,6 @@ import { TenantHeaderInterceptor } from './interceptors/tenant-header.intercepto
useClass: ProgressIndicationInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: ResponsePayloadInterceptor,
multi: true,
},
{
provide: HTTP_INTERCEPTORS,
useClass: StatusCodeInterceptor,

View File

@ -1,43 +0,0 @@
import { HttpHandler, HttpHeaderResponse, HttpProgressEvent, HttpRequest, HttpResponse, HttpSentEvent, HttpUserEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { BaseInterceptor } from './base.interceptor';
import { InterceptorType } from './interceptor-type';
import { ConfigurationService } from '@app/core/services/configuration/configuration.service';
@Injectable()
export class ResponsePayloadInterceptor extends BaseInterceptor {
constructor(
private snackBar: MatSnackBar,
configurationService: ConfigurationService
) { super(configurationService); }
get type(): InterceptorType { return InterceptorType.ResponsePayload; }
interceptRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpSentEvent | HttpHeaderResponse | HttpProgressEvent | HttpResponse<any> | HttpUserEvent<any>> {
return next.handle(req).pipe(
map(response => {
// if (!(response instanceof HttpResponse) || (response instanceof Blob)) { return response; }
// if (response.status == 200) {
// if (response.body.statusCode === ApiMessageCode.SUCCESS_MESSAGE) {
// //throw new Error('Request failed');
// // this.snackBar.openFromComponent(SnackBarNotificationComponent, {
// // data: { message: response['message'], language: null },
// // duration: 3000,
// // });
// return response.body.payload;
// } else if (response.body.statusCode === ApiMessageCode.NO_MESSAGE) {
// return response.body.payload;
// } else {
// return response.body.payload;
// }
// }
return response;
}));
}
}

View File

@ -111,24 +111,7 @@ export class UserSettingsPickerComponent extends BaseComponent implements OnInit
}
renameCurrentUserSetting(): void {
// this.dialog.open<FilterNameDialogComponent, FilterNameDialogComponentParams, FilterNameDialogComponentReturn>(
// FilterNameDialogComponent,
// {
// maxWidth: '600px',
// maxHeight: '400px',
// restoreFocus: false,
// data: { name: this.currentUserSetting.name},
// disableClose: false
// }
// ).afterClosed()
// .pipe(
// filter(x => !!x),
// takeUntil(this._destroyed)
// )
// .subscribe(newName => {
// this.currentUserSetting.name = newName;
// this.persistLookupChangesManually(this.currentUserSetting, true);
// })
}
settingDeleted(value: Guid = this.currentUserSetting.id) {
@ -149,21 +132,10 @@ export class UserSettingsPickerComponent extends BaseComponent implements OnInit
}
});
}
/* this.userSettingsService.remove(this.currentUserSetting.id, this.key); */
}
saveFilter() {
// const saveDialogRef = this.dialog.open(FilterNameDialogComponent, {
// maxWidth: '600px',
// maxHeight: '400px',
// restoreFocus: false,
// data: { name: this.currentUserSetting ? this.currentUserSetting.name : '' },
// disableClose: false
// });
// saveDialogRef.afterClosed().subscribe(result => {
// if (result) { this.createNewFilter(result); }
// });
//TODO: implement
}

View File

@ -1,12 +1,12 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { UserSetting, UserSettingsService } from '@app/core/services/user-settings/user-settings.service';
import { BaseComponent } from '@common/base/base.component';
import { Lookup } from '@common/model/lookup';
import { takeUntil, filter } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { TranslateService } from '@ngx-translate/core';
import { isNullOrUndefined } from '@swimlane/ngx-datatable';
import { UserSetting, UserSettingsService } from '@app/core/services/user-settings/user-settings.service';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'app-user-settings-selector',
@ -87,7 +87,6 @@ export class UserSettingsSelectorComponent extends BaseComponent implements OnIn
if (setting === null) { return; }
//Persist the active user setting
//this.onSettingSelected.emit(setting.value);
this.userSettingsService.set(setting, true, this.key);
}
@ -109,21 +108,10 @@ export class UserSettingsSelectorComponent extends BaseComponent implements OnIn
}
});
}
/* this.userSettingsService.remove(this.currentUserSetting.id, this.key); */
}
saveFilter() {
// const saveDialogRef = this.dialog.open(FilterNameDialogComponent, {
// maxWidth: '600px',
// maxHeight: '400px',
// restoreFocus: false,
// data: { name: this.currentUserSetting ? this.currentUserSetting.name : '' },
// disableClose: false
// });
// saveDialogRef.afterClosed().subscribe(result => {
// if (result) { this.createNewFilter(result); }
// });
//TODO: implement
}
updateFilter() {
@ -147,7 +135,7 @@ export class UserSettingsSelectorComponent extends BaseComponent implements OnIn
setting.updatedAt = null;
setting.userId = null;
this.currentUserSetting = setting;
this.persistLookupChangesManually(this.currentUserSetting, true);
}

View File

@ -1,23 +1,22 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AuthService } from '@app/core/services/auth/auth.service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { BaseComponent } from '@common/base/base.component';
import { FormService } from '@common/forms/form-service';
import { LoggingService } from '@app/core/services/logging/logging-service';
import { ConfirmationDialogComponent } from '@common/modules/confirmation-dialog/confirmation-dialog.component';
import { HttpError, HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { HttpErrorHandlingService } from '@common/modules/errors/error-handling/http-error-handling.service';
import { Guid } from '@common/types/guid';
import { TranslateService } from '@ngx-translate/core';
import { IsActive } from '@app/core/common/enum/is-active.enum';
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
import { map, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
import { AuthService } from '@app/core/services/auth/auth.service';
import { InAppNotificationService } from '@notification-service/services/http/inapp-notification.service';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { NotificationInAppTracking } from '@notification-service/core/enum/notification-inapp-tracking.enum';
import { InAppNotification } from '@notification-service/core/model/inapp-notification.model';
import { InAppNotificationService } from '@notification-service/services/http/inapp-notification.service';
import { map, takeUntil } from 'rxjs/operators';
import { nameof } from 'ts-simple-nameof';
@Component({
selector: 'app-inapp-notification-editor',
@ -72,7 +71,7 @@ export class InAppNotificationEditorComponent extends BaseComponent implements O
this.isDeleted = this.inappNotification.isActive === IsActive.Inactive;
this.isRead = this.inappNotification.trackingState === NotificationInAppTracking.Delivered;
if(!this.isDeleted && !this.isRead) this.markAsRead()
if (!this.isDeleted && !this.isRead) this.markAsRead()
},
error => this.httpErrorHandlingService.handleBackedRequestError(error)
);
@ -89,7 +88,6 @@ export class InAppNotificationEditorComponent extends BaseComponent implements O
complete => this.onCallbackSuccess(),
error => this.httpErrorHandlingService.handleBackedRequestError(error)
);
// this.clearErrorModel();
}
public delete() {

View File

@ -2,8 +2,6 @@ import { DragDropModule } from '@angular/cdk/drag-drop';
import { NgModule } from '@angular/core';
import { CommonFormsModule } from '@common/forms/common-forms.module';
import { CommonUiModule } from '@common/ui/common-ui.module';
import { CoreNotificationServiceModule } from '@notification-service/services/core-service.module';
// import { TotpModule } from '@idp-service/ui/totp/totp.module';
import { UserProfileNotifierListEditorComponent } from '@notification-service/ui/user-profile/notifier-list/user-profile-notifier-list-editor.component';
@NgModule({

View File

@ -149,21 +149,6 @@ $mat-dark-theme: mat.m2-define-dark-theme((color: (primary: $mat-dark-theme-prim
filter: alpha(opacity=20);
}
// Custom Theme
// @import "./assets/scss/blue-theme.scss";
// Define a theme.
// $primary: mat-palette($app-blue-theme-primary-palette);
// $accent: mat-palette($mat-pink, A200, A100, A400);
// $theme: mat-light-theme($primary, $accent);
// Include all theme styles for the components.
// @include angular-material-theme($theme);
// @include covalent-theme($theme);
.cc-btn {
background-color: var(--primary-color-3) !important;
}
@ -186,7 +171,6 @@ $mat-dark-theme: mat.m2-define-dark-theme((color: (primary: $mat-dark-theme-prim
.lightblue-btn {
background-color: var(--primary-color) !important;
color: white !important;
// background-color: rgba(0, 112, 192, 1) !important;
}
.listing-item {
@ -211,7 +195,6 @@ $mat-dark-theme: mat.m2-define-dark-theme((color: (primary: $mat-dark-theme-prim
padding: 0.1em 1em;
border-radius: 10em;
background-color: #0d7489;
// background-color: rgba(0, 112, 192, 1);
color: #fff;
text-transform: uppercase;
font-weight: 500;
@ -227,8 +210,6 @@ $mat-dark-theme: mat.m2-define-dark-theme((color: (primary: $mat-dark-theme-prim
border-radius: 10em;
background-color: rgb(236, 241, 249);
color: var(--primary-color);
// color: rgba(0, 112, 192, 1);
// color: rgb(68, 114, 196);
text-transform: uppercase;
font-weight: 500;
max-width: 160px;
@ -274,21 +255,6 @@ $mat-dark-theme: mat.m2-define-dark-theme((color: (primary: $mat-dark-theme-prim
margin-bottom: 0px;
}
// .draft-bookmark {
// color: #e7e6e6;
// display: inline;
// position: absolute;
// padding-top: 3px;
// }
// .finalized-bookmark {
// color: #08bd63;
// // color: #92d050;
// display: inline;
// position: absolute;
// padding-top: 3px;
// }
h4 span {
color: #089dbb;
font-weight: 600;
@ -387,43 +353,6 @@ $mat-dark-theme: mat.m2-define-dark-theme((color: (primary: $mat-dark-theme-prim
margin-top: 8px;
}
//.angular-editor-textarea {
// min-height: 150px !important;
//}
//
//.editor-wrapper {
// border: 1px solid transparent !important;
// border-radius: 5px;
//}
//
//.angular-editor-toolbar {
// margin-left: 1px;
// margin-right: 1px;
//}
//
//.angular-editor-textarea, .angular-editor-toolbar {
// border: none !important;
//}
//
//.angular-editor {
// border: 1px solid #ddd !important;
// border-radius: 5px;
// background-color: #fff;
//}
//
//.editor-wrapper:hover, .angular-editor:hover {
// border: 1px solid #000 !important;
//}
//
//.editor-wrapper:focus-within, .angular-editor:focus-within {
// border: 1px solid #034459 !important;
//}
//
//.required.editor-wrapper, .required .editor .angular-editor {
// border: 1px solid #f44336 !important;
//}
// end of CSS for <angular-editor> (@kolkov/angular-editor)
/* Transition Group */
.list-move {
transition: transform 1s;

View File

@ -24,6 +24,9 @@ notification:
- #descriptionFinalised
type: 33790bad-94d4-488a-8ee2-7f6295ca18ea
contacts: [ inapp, email ]
- #descriptionAnnotationCreated
type: db1e99d2-a240-4e75-9bb2-ef25b234c1f0
contacts: [ inapp, email ]
- #mergeAcountConfirmation
type: BFE68845-CB05-4C5A-A03D-29161A7C9660
contacts: [ email ]
@ -196,6 +199,31 @@ notification:
bcc-mode: 0
allow-attachments: false
cipher-fields: [ ]
- #descriptionAnnotationCreated
key: db1e99d2-a240-4e75-9bb2-ef25b234c1f0
subject-path: classpath:notification_templates/descriptionannotationcreated/email/subject.{language}.txt
subject-field-options:
mandatory: [ ]
optional: [ ]
body-path: classpath:notification_templates/descriptionannotationcreated/email/body.{language}.html
body-field-options:
mandatory: [ "{reasonName}", "{name}", "{installation-url}", "{id}" ]
optional:
- key: "{recipient}"
value:
- key: "{tenant-url-path}"
value:
formatting:
'[{reasonName}]': null
'[{name}]': null
'[{recipient}]': null
'[{tenant-url-path}]': null
cc: [ ]
cc-mode: 0
bcc: [ ]
bcc-mode: 0
allow-attachments: false
cipher-fields: [ ]
- #mergeAccountConfirmation
key: BFE68845-CB05-4C5A-A03D-29161A7C9660
subject-path: classpath:notification_templates/mergeacountconfirmation/email/subject.{language}.txt
@ -449,6 +477,29 @@ notification:
'[{tenant-url-path}]': null
priority-key: null
cipher-fields: [ ]
- #descriptionAnnotationCreated
key: db1e99d2-a240-4e75-9bb2-ef25b234c1f0
subject-path: classpath:notification_templates/descriptionannotationcreated/inapp/subject.{language}.txt
subject-field-options:
mandatory: [ ]
optional: [ ]
body-path: classpath:notification_templates/descriptionannotationcreated/inapp/body.{language}.html
body-field-options:
mandatory: [ "{reasonName}", "{name}", "{installation-url}", "{id}" ]
optional:
- key: "{recipient}"
value:
- key: "{tenant-url-path}"
value:
formatting:
'[{reasonName}]': null
'[{name}]': null
'[{installation-url}]': null
'[{id}]': null
'[{recipient}]': null
'[{tenant-url-path}]': null
priority-key: null
cipher-fields: [ ]
- #mergeAccountConfirmation
key: BFE68845-CB05-4C5A-A03D-29161A7C9660
subject-path: classpath:notification_templates/mergeacountconfirmation/inapp/subject.{language}.txt

View File

@ -0,0 +1,304 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>OpenCDMP Notification</title>
<style>
/* -------------------------------------
GLOBAL RESETS
------------------------------------- */
img {
border: none;
-ms-interpolation-mode: bicubic;
max-width: 100%; }
body {
background-color: #f6f6f6;
font-family: sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
line-height: 1.4;
margin: 0;
padding: 0;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%; }
table {
border-collapse: separate;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
width: 100%; }
table td {
font-family: sans-serif;
font-size: 14px;
vertical-align: top; }
/* -------------------------------------
BODY & CONTAINER
------------------------------------- */
.body {
background-color: #f6f6f6;
width: 100%; }
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
.container {
display: block;
Margin: 0 auto !important;
/* makes it centered */
max-width: 580px;
padding: 10px;
width: 580px; }
/* This should also be a block element, so that it will fill 100% of the .container */
.content {
box-sizing: border-box;
display: block;
Margin: 0 auto;
max-width: 580px;
padding: 10px; }
/* -------------------------------------
HEADER, FOOTER, MAIN
------------------------------------- */
.main {
background: #ffffff;
border-radius: 3px;
width: 100%; }
.wrapper {
box-sizing: border-box;
padding: 20px; }
.content-block {
padding-bottom: 10px;
padding-top: 10px;
}
.footer {
clear: both;
Margin-top: 10px;
text-align: center;
width: 100%; }
.footer td,
.footer p,
.footer span,
.footer a {
color: #999999;
font-size: 12px;
text-align: center; }
/* -------------------------------------
TYPOGRAPHY
------------------------------------- */
h1,
h2,
h3,
h4 {
color: #000000;
font-family: sans-serif;
font-weight: 400;
line-height: 1.4;
margin: 0;
Margin-bottom: 30px; }
h1 {
font-size: 35px;
font-weight: 300;
text-align: center;
text-transform: capitalize; }
p,
ul,
ol {
font-family: sans-serif;
font-size: 14px;
font-weight: normal;
margin: 0;
Margin-bottom: 15px; }
p li,
ul li,
ol li {
list-style-position: inside;
margin-left: 5px; }
a {
color: #3498db;
text-decoration: underline; }
/* -------------------------------------
BUTTONS
------------------------------------- */
.btn {
box-sizing: border-box;
width: 100%; }
.btn > tbody > tr > td {
padding-bottom: 15px; }
.btn table {
width: auto; }
.btn table td {
background-color: #ffffff;
border-radius: 5px;
text-align: center; }
.btn a {
background-color: #ffffff;
border: solid 1px #3498db;
border-radius: 5px;
box-sizing: border-box;
color: #3498db;
cursor: pointer;
display: inline-block;
font-size: 14px;
font-weight: bold;
margin: 0;
padding: 12px 25px;
text-decoration: none;
text-transform: capitalize; }
.btn-primary table td {
background-color: #3498db; }
.btn-primary a {
background-color: #3498db;
border-color: #3498db;
color: #ffffff; }
/* -------------------------------------
OTHER STYLES THAT MIGHT BE USEFUL
------------------------------------- */
.last {
margin-bottom: 0; }
.first {
margin-top: 0; }
.align-center {
text-align: center; }
.align-right {
text-align: right; }
.align-left {
text-align: left; }
.clear {
clear: both; }
.mt0 {
margin-top: 0; }
.mb0 {
margin-bottom: 0; }
.preheader {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0; }
.powered-by a {
text-decoration: none; }
hr {
border: 0;
border-bottom: 1px solid #f6f6f6;
Margin: 20px 0; }
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important; }
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important; }
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important; }
table[class=body] .content {
padding: 0 !important; }
table[class=body] .container {
padding: 0 !important;
width: 100% !important; }
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important; }
table[class=body] .btn table {
width: 100% !important; }
table[class=body] .btn a {
width: 100% !important; }
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important; }}
/* -------------------------------------
PRESERVE THESE STYLES IN THE HEAD
------------------------------------- */
@media all {
.ExternalClass {
width: 100%; }
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%; }
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important; }
.btn-primary table td:hover {
background-color: #34495e !important; }
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important; } }
</style>
</head>
<body class="">
<table border="0" cellpadding="0" cellspacing="0" class="body">
<tr>
<td>&nbsp;</td>
<td class="container">
<div class="content">
<!-- START CENTERED WHITE CONTAINER -->
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
<table class="main">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper">
<table border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<p>Dear {recipient},</p>
<p>{reasonName} made a comment on the Description {name}.</p>
<table border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
<tbody>
<tr>
<td align="left">
<table border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td> <a href="{installation-url}{tenant-url-path}/descriptions/edit/{id}" target="_blank">Click here to view it.</a> </td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- START FOOTER -->
<div class="footer">
</div>
<!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER -->
</div>
</td>
<td>&nbsp;</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body class="">
<p>Dear {recipient},</p>
<p>{reasonName} made a comment on the Description {name}.</p>
<a href="{installation-url}{tenant-url-path}/descriptions/edit/{id}" target="_blank">Click here to view it.</a>
</body>
</html>