Merge branch 'dmp-refactoring' of code-repo.d4science.org:MaDgiK-CITE/argos into dmp-refactoring
# Conflicts: # dmp-frontend/src/app/core/common/enum/app-role.ts # dmp-frontend/src/app/core/services/utilities/enum-utils.service.ts # dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.html # dmp-frontend/src/app/ui/admin/user/listing/role-editor/user-role-editor.component.scss # dmp-frontend/src/assets/i18n/en.json
This commit is contained in:
commit
48352f0461
|
@ -4,10 +4,7 @@ import eu.eudat.authorization.AuthorizationFlags;
|
|||
import eu.eudat.authorization.Permission;
|
||||
import eu.eudat.commons.enums.*;
|
||||
import eu.eudat.commons.scope.user.UserScope;
|
||||
import eu.eudat.data.DmpDescriptionTemplateEntity;
|
||||
import eu.eudat.data.DmpEntity;
|
||||
import eu.eudat.data.DmpReferenceEntity;
|
||||
import eu.eudat.data.DmpUserEntity;
|
||||
import eu.eudat.data.*;
|
||||
import eu.eudat.model.Dmp;
|
||||
import eu.eudat.model.PublicDmp;
|
||||
import eu.eudat.query.utils.QueryUtilsService;
|
||||
|
@ -364,6 +361,11 @@ public class DmpQuery extends QueryBase<DmpEntity> {
|
|||
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpEntity._id)).value(subQuery.Query));
|
||||
}
|
||||
|
||||
if (this.entityDoiQuery != null) {
|
||||
QueryContext<EntityDoiEntity, UUID> subQuery = this.applySubQuery(this.entityDoiQuery, queryContext, UUID.class, entityDoiEntityRoot -> entityDoiEntityRoot.get(EntityDoiEntity._entityId));
|
||||
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpEntity._id)).value(subQuery.Query));
|
||||
}
|
||||
|
||||
if (!predicates.isEmpty()) {
|
||||
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);
|
||||
return queryContext.CriteriaBuilder.and(predicatesArray);
|
||||
|
|
|
@ -5,13 +5,24 @@ import eu.eudat.authorization.Permission;
|
|||
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
|
||||
import eu.eudat.commonmodels.models.FileEnvelopeModel;
|
||||
import eu.eudat.commonmodels.models.dmp.DmpModel;
|
||||
import eu.eudat.commons.JsonHandlingService;
|
||||
import eu.eudat.commons.enums.ContactInfoType;
|
||||
import eu.eudat.commons.enums.IsActive;
|
||||
import eu.eudat.commons.enums.StorageType;
|
||||
import eu.eudat.commons.notification.NotificationProperties;
|
||||
import eu.eudat.commons.scope.user.UserScope;
|
||||
import eu.eudat.commons.types.notification.*;
|
||||
import eu.eudat.convention.ConventionService;
|
||||
import eu.eudat.data.DmpEntity;
|
||||
import eu.eudat.data.DmpUserEntity;
|
||||
import eu.eudat.data.UserEntity;
|
||||
import eu.eudat.depositinterface.repository.DepositClient;
|
||||
import eu.eudat.depositinterface.repository.DepositConfiguration;
|
||||
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEvent;
|
||||
import eu.eudat.integrationevent.outbox.notification.NotifyIntegrationEventHandler;
|
||||
import eu.eudat.model.EntityDoi;
|
||||
import eu.eudat.model.StorageFile;
|
||||
import eu.eudat.model.UserContactInfo;
|
||||
import eu.eudat.model.builder.commonmodels.DepositConfigurationBuilder;
|
||||
import eu.eudat.model.builder.commonmodels.dmp.DmpCommonModelBuilder;
|
||||
import eu.eudat.model.persist.StorageFilePersist;
|
||||
|
@ -19,6 +30,9 @@ import eu.eudat.model.persist.deposit.DepositAuthenticateRequest;
|
|||
import eu.eudat.model.persist.deposit.DepositRequest;
|
||||
import eu.eudat.model.persist.EntityDoiPersist;
|
||||
import eu.eudat.query.DmpQuery;
|
||||
import eu.eudat.query.DmpUserQuery;
|
||||
import eu.eudat.query.UserContactInfoQuery;
|
||||
import eu.eudat.query.UserQuery;
|
||||
import eu.eudat.service.entitydoi.EntityDoiService;
|
||||
import eu.eudat.service.storage.StorageFileProperties;
|
||||
import eu.eudat.service.storage.StorageFileService;
|
||||
|
@ -28,6 +42,7 @@ import gr.cite.commons.web.authz.service.AuthorizationService;
|
|||
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeFilterFunction;
|
||||
import gr.cite.commons.web.oidc.filter.webflux.TokenExchangeModel;
|
||||
import gr.cite.tools.data.builder.BuilderFactory;
|
||||
import gr.cite.tools.data.query.Ordering;
|
||||
import gr.cite.tools.data.query.QueryFactory;
|
||||
import gr.cite.tools.exception.MyNotFoundException;
|
||||
import gr.cite.tools.fieldset.BaseFieldSet;
|
||||
|
@ -42,11 +57,13 @@ import org.springframework.context.i18n.LocaleContextHolder;
|
|||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import javax.management.InvalidApplicationException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URLConnection;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class DepositServiceImpl implements DepositService {
|
||||
|
@ -67,6 +84,10 @@ public class DepositServiceImpl implements DepositService {
|
|||
private final ValidatorFactory validatorFactory;
|
||||
private final StorageFileProperties storageFileProperties;
|
||||
private final AuthorizationContentResolver authorizationContentResolver;
|
||||
private final ConventionService conventionService;
|
||||
private final JsonHandlingService jsonHandlingService;
|
||||
private final NotificationProperties notificationProperties;
|
||||
private final NotifyIntegrationEventHandler eventHandler;
|
||||
@Autowired
|
||||
public DepositServiceImpl(DepositProperties depositProperties,
|
||||
TokenExchangeCacheService tokenExchangeCacheService,
|
||||
|
@ -74,7 +95,7 @@ public class DepositServiceImpl implements DepositService {
|
|||
EntityDoiService doiService,
|
||||
QueryFactory queryFactory,
|
||||
MessageSource messageSource,
|
||||
BuilderFactory builderFactory, DepositConfigurationCacheService depositConfigurationCacheService, FileTransformerService fileTransformerService, StorageFileService storageFileService, UserScope userScope, ValidatorFactory validatorFactory, StorageFileProperties storageFileProperties, AuthorizationContentResolver authorizationContentResolver) {
|
||||
BuilderFactory builderFactory, DepositConfigurationCacheService depositConfigurationCacheService, FileTransformerService fileTransformerService, StorageFileService storageFileService, UserScope userScope, ValidatorFactory validatorFactory, StorageFileProperties storageFileProperties, AuthorizationContentResolver authorizationContentResolver, ConventionService conventionService, JsonHandlingService jsonHandlingService, NotificationProperties notificationProperties, NotifyIntegrationEventHandler eventHandler) {
|
||||
this.depositProperties = depositProperties;
|
||||
this.tokenExchangeCacheService = tokenExchangeCacheService;
|
||||
this.authorizationService = authorizationService;
|
||||
|
@ -89,6 +110,10 @@ public class DepositServiceImpl implements DepositService {
|
|||
this.validatorFactory = validatorFactory;
|
||||
this.storageFileProperties = storageFileProperties;
|
||||
this.authorizationContentResolver = authorizationContentResolver;
|
||||
this.conventionService = conventionService;
|
||||
this.jsonHandlingService = jsonHandlingService;
|
||||
this.notificationProperties = notificationProperties;
|
||||
this.eventHandler = eventHandler;
|
||||
this.clients = new HashMap<>();
|
||||
}
|
||||
|
||||
|
@ -181,9 +206,48 @@ public class DepositServiceImpl implements DepositService {
|
|||
doiPersist.setRepositoryId(dmpDepositModel.getRepositoryId());
|
||||
doiPersist.setDoi(doi);
|
||||
doiPersist.setEntityId(dmpEntity.getId());
|
||||
this.sendNotification(dmpEntity);
|
||||
return doiService.persist(doiPersist, dmpDepositModel.getProject());
|
||||
}
|
||||
|
||||
private void sendNotification(DmpEntity dmpEntity) throws InvalidApplicationException {
|
||||
List<DmpUserEntity> dmpUsers = this.queryFactory.query(DmpUserQuery.class).ids(dmpEntity.getId()).isActives(IsActive.Active).collect();
|
||||
if (this.conventionService.isListNullOrEmpty(dmpUsers)){
|
||||
throw new MyNotFoundException("Dmp does not have Users");
|
||||
}
|
||||
|
||||
List<UserEntity> users = this.queryFactory.query(UserQuery.class).ids(dmpUsers.stream().map(x -> x.getUserId()).collect(Collectors.toList())).isActive(IsActive.Active).collect();
|
||||
|
||||
for (UserEntity user: users) {
|
||||
if (!user.getId().equals(this.userScope.getUserIdSafe()) && !this.conventionService.isListNullOrEmpty(dmpUsers.stream().filter(x -> x.getUserId().equals(user.getId())).collect(Collectors.toList()))){
|
||||
this.createDmpDepositNotificationEvent(dmpEntity, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createDmpDepositNotificationEvent(DmpEntity dmp, UserEntity user) throws InvalidApplicationException {
|
||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||
event.setUserId(user.getId());
|
||||
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
||||
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
||||
|
||||
List<ContactPair> contactPairs = new ArrayList<>();
|
||||
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
||||
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
||||
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
||||
event.setNotificationType(notificationProperties.getDmpDepositType());
|
||||
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, this.queryFactory.query(UserQuery.class).ids(this.userScope.getUserId()).first().getName()));
|
||||
fieldInfoList.add(new FieldInfo("{name}", DataType.String, dmp.getLabel()));
|
||||
fieldInfoList.add(new FieldInfo("{id}", DataType.String, dmp.getId().toString()));
|
||||
data.setFields(fieldInfoList);
|
||||
event.setData(jsonHandlingService.toJsonSafe(data));
|
||||
|
||||
eventHandler.handle(event);
|
||||
}
|
||||
|
||||
private String addFileToSharedStorage(eu.eudat.model.file.FileEnvelope file) throws IOException {
|
||||
StorageFilePersist storageFilePersist = new StorageFilePersist();
|
||||
storageFilePersist.setName(FilenameUtils.removeExtension(file.getFilename()));
|
||||
|
|
|
@ -302,8 +302,7 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
if (!dmpUser.getUserId().equals(this.userScope.getUserIdSafe())){
|
||||
UserEntity user = this.queryFactory.query(UserQuery.class).ids(dmpUser.getUserId()).first();
|
||||
if (user != null){
|
||||
this.createDescriptionNotificationEvent(description, user, NotificationContactType.EMAIL);
|
||||
this.createDescriptionNotificationEvent(description, user, NotificationContactType.IN_APP);
|
||||
this.createDescriptionNotificationEvent(description, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -353,9 +352,9 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
return cleanData;
|
||||
}
|
||||
|
||||
private void createDescriptionNotificationEvent(DescriptionEntity description, UserEntity user, NotificationContactType type) throws InvalidApplicationException {
|
||||
private void createDescriptionNotificationEvent(DescriptionEntity description, UserEntity user) throws InvalidApplicationException {
|
||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||
event.setUserId(this.userScope.getUserId());
|
||||
event.setUserId(user.getId());
|
||||
|
||||
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
||||
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
||||
|
@ -364,7 +363,6 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
||||
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
||||
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
||||
event.setContactTypeHint(type);
|
||||
|
||||
event = this.applyNotificationType(description.getStatus(), event);
|
||||
NotificationFieldData data = new NotificationFieldData();
|
||||
|
@ -421,6 +419,7 @@ public class DescriptionServiceImpl implements DescriptionService {
|
|||
this.eventBroker.emit(new DescriptionTouchedEvent(data.getId()));
|
||||
|
||||
this.annotationEntityTouchedIntegrationEventHandler.handleDescription(data.getId());
|
||||
if (data.getStatus().equals(DescriptionStatus.Finalized)) this.sendNotification(data);
|
||||
}
|
||||
return this.builderFactory.builder(DescriptionBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Description._id), data);
|
||||
}
|
||||
|
|
|
@ -245,8 +245,7 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
|||
data.setRole(user.getRole());
|
||||
this.entityManager.persist(data);
|
||||
if (!this.userScope.getUserId().equals(user.getUserId())) {
|
||||
this.sendDescriptionTemplateInvitationEvent(data, NotificationContactType.EMAIL);
|
||||
this.sendDescriptionTemplateInvitationEvent(data, NotificationContactType.IN_APP);
|
||||
this.sendDescriptionTemplateInvitationEvent(data);
|
||||
}
|
||||
}
|
||||
updatedCreatedIds.add(data.getUserId());
|
||||
|
@ -256,13 +255,18 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
|||
this.deleterFactory.deleter(UserDescriptionTemplateDeleter.class).delete(toDelete);
|
||||
}
|
||||
|
||||
private void sendDescriptionTemplateInvitationEvent(UserDescriptionTemplateEntity userDescriptionTemplate, NotificationContactType type) throws InvalidApplicationException {
|
||||
private void sendDescriptionTemplateInvitationEvent(UserDescriptionTemplateEntity userDescriptionTemplate) throws InvalidApplicationException {
|
||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||
event.setUserId(userScope.getUserIdSafe());
|
||||
|
||||
UserEntity user = this.entityManager.find(UserEntity.class, userDescriptionTemplate.getUserId());
|
||||
if (user == null){
|
||||
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userDescriptionTemplate.getUserId(), User.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
}
|
||||
DescriptionTemplateEntity descriptionTemplate = this.queryFactory.query(DescriptionTemplateQuery.class).isActive(IsActive.Active).ids(userDescriptionTemplate.getDescriptionTemplateId()).first();
|
||||
|
||||
if (descriptionTemplate == null){
|
||||
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userDescriptionTemplate.getDescriptionTemplateId(), DescriptionTemplate.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
}
|
||||
event.setUserId(user.getId());
|
||||
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
||||
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
||||
|
||||
|
@ -270,7 +274,6 @@ public class DescriptionTemplateServiceImpl implements DescriptionTemplateServic
|
|||
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
||||
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
||||
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
||||
event.setContactTypeHint(type);
|
||||
event.setNotificationType(notificationProperties.getDescriptionTemplateInvitationType());
|
||||
NotificationFieldData data = new NotificationFieldData();
|
||||
List<FieldInfo> fieldInfoList = new ArrayList<>();
|
||||
|
|
|
@ -258,16 +258,15 @@ public class DmpServiceImpl implements DmpService {
|
|||
if (!dmpUser.getUserId().equals(this.userScope.getUserIdSafe())){
|
||||
UserEntity user = this.queryFactory.query(UserQuery.class).ids(dmpUser.getUserId()).first();
|
||||
if (user != null){
|
||||
this.createDmpNotificationEvent(dmp, user, NotificationContactType.EMAIL);
|
||||
this.createDmpNotificationEvent(dmp, user, NotificationContactType.IN_APP);
|
||||
this.createDmpNotificationEvent(dmp, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createDmpNotificationEvent(DmpEntity dmp, UserEntity user, NotificationContactType type) throws InvalidApplicationException {
|
||||
private void createDmpNotificationEvent(DmpEntity dmp, UserEntity user) throws InvalidApplicationException {
|
||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||
event.setUserId(this.userScope.getUserId());
|
||||
event.setUserId(user.getId());
|
||||
UserContactInfoQuery query = this.queryFactory.query(UserContactInfoQuery.class).userIds(user.getId());
|
||||
query.setOrder(new Ordering().addAscending(UserContactInfo._ordinal));
|
||||
|
||||
|
@ -275,7 +274,6 @@ public class DmpServiceImpl implements DmpService {
|
|||
contactPairs.add(new ContactPair(ContactInfoType.Email, query.first().getValue()));
|
||||
NotificationContactData contactData = new NotificationContactData(contactPairs, null, null);
|
||||
event.setContactHint(jsonHandlingService.toJsonSafe(contactData));
|
||||
event.setContactTypeHint(type);
|
||||
|
||||
this.applyNotificationType(dmp.getStatus(), event);
|
||||
NotificationFieldData data = new NotificationFieldData();
|
||||
|
@ -1186,6 +1184,15 @@ public class DmpServiceImpl implements DmpService {
|
|||
throw new InvalidApplicationException("Dmp does not exist!");
|
||||
}
|
||||
|
||||
List<DmpUserEntity> existingUsers = this.queryFactory.query(DmpUserQuery.class)
|
||||
.dmpIds(dmp.getId())
|
||||
.isActives(IsActive.Active)
|
||||
.collect();
|
||||
|
||||
if (this.conventionService.isListNullOrEmpty(existingUsers)){
|
||||
throw new InvalidApplicationException("Dmp does not have users!");
|
||||
}
|
||||
|
||||
List<DmpUserPersist> usersToAssign = new ArrayList<>();
|
||||
for (DmpUserPersist user :users) {
|
||||
UUID userId = null;
|
||||
|
@ -1200,7 +1207,7 @@ public class DmpServiceImpl implements DmpService {
|
|||
if (userId != null){
|
||||
user.setUser(userId);
|
||||
usersToAssign.add(user);
|
||||
if (this.userScope.getUserId() != userId){
|
||||
if (this.userScope.getUserId() != userId && !existingUsers.stream().map(x -> x.getUserId()).collect(Collectors.toList()).contains(userId)){
|
||||
this.sendDmpInvitationExistingUser(user.getUser(), dmp, user.getRole());
|
||||
}
|
||||
}else if (user.getEmail() != null) {
|
||||
|
@ -1215,20 +1222,18 @@ public class DmpServiceImpl implements DmpService {
|
|||
private void sendDmpInvitationExistingUser(UUID userId, DmpEntity dmp, DmpUserRole role) throws InvalidApplicationException {
|
||||
UserEntity recipient = this.queryFactory.query(UserQuery.class).ids(userId).isActive(IsActive.Active).first();
|
||||
String email = this.queryFactory.query(UserContactInfoQuery.class).userIds(recipient.getId()).first().getValue();
|
||||
this.createDmpInvitationExistingUserEvent(recipient, dmp, role, email, NotificationContactType.EMAIL);
|
||||
this.createDmpInvitationExistingUserEvent(recipient, dmp, role, email, NotificationContactType.IN_APP);
|
||||
this.createDmpInvitationExistingUserEvent(recipient, dmp, role, email);
|
||||
}
|
||||
|
||||
private void createDmpInvitationExistingUserEvent(UserEntity recipient, DmpEntity dmp, DmpUserRole role, String email, NotificationContactType type) throws InvalidApplicationException {
|
||||
private void createDmpInvitationExistingUserEvent(UserEntity recipient, DmpEntity dmp, DmpUserRole role, String email) throws InvalidApplicationException {
|
||||
|
||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||
event.setUserId(this.userScope.getUserIdSafe());
|
||||
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(jsonHandlingService.toJsonSafe(contactData));
|
||||
event.setContactTypeHint(type);
|
||||
event.setNotificationType(notificationProperties.getDmpInvitationExistingUserType());
|
||||
NotificationFieldData data = new NotificationFieldData();
|
||||
List<FieldInfo> fieldInfoList = new ArrayList<>();
|
||||
|
@ -1246,7 +1251,6 @@ public class DmpServiceImpl implements DmpService {
|
|||
String token = this.createActionConfirmation(email, dmp, role);
|
||||
|
||||
NotifyIntegrationEvent event = new NotifyIntegrationEvent();
|
||||
event.setUserId(this.userScope.getUserIdSafe());
|
||||
|
||||
List<ContactPair> contactPairs = new ArrayList<>();
|
||||
contactPairs.add(new ContactPair(ContactInfoType.Email, email));
|
||||
|
|
|
@ -117,11 +117,17 @@ public class ExternalFetcherServiceImpl implements ExternalFetcherService {
|
|||
Map<String, Object> rawData = new HashMap<>();
|
||||
for (Object object: item.getOptions()) {
|
||||
StaticOptionEntity staticOption = (StaticOptionEntity) object;
|
||||
if (!this.conventionService.isNullOrEmpty(externalReferenceCriteria.getLike()) && !externalReferenceCriteria.getLike().toUpperCase().contains(staticOption.getValue())) continue;
|
||||
if (this.conventionService.isNullOrEmpty(externalReferenceCriteria.getLike())){
|
||||
rawData.put(staticOption.getCode(), staticOption.getValue());
|
||||
result.put(staticOption.getCode(), staticOption.getValue());
|
||||
result.put(ReferenceEntity.KnownFields.SourceLabel, staticSource.getLabel());
|
||||
result.put(ReferenceEntity.KnownFields.Key, staticSource.getKey());
|
||||
}else if (!this.conventionService.isNullOrEmpty(externalReferenceCriteria.getLike()) && externalReferenceCriteria.getLike().toUpperCase().contains(staticOption.getValue().toUpperCase())){
|
||||
rawData.put(staticOption.getCode(), staticOption.getValue());
|
||||
result.put(staticOption.getCode(), staticOption.getValue());
|
||||
result.put(ReferenceEntity.KnownFields.SourceLabel, staticSource.getLabel());
|
||||
result.put(ReferenceEntity.KnownFields.Key, staticSource.getKey());
|
||||
}
|
||||
|
||||
}
|
||||
if (!rawData.isEmpty()) externalDataResult.getRawData().add(rawData);
|
||||
|
|
|
@ -66,15 +66,15 @@ public class MetricsServiceImpl implements MetricsService {
|
|||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculatePublishedDmps(true), MetricLabels.PUBLISHED);
|
||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculateDoiedDmps(true), MetricLabels.DOIED);
|
||||
|
||||
this.setGaugeValue(gauges, MetricNames.DMP, calculateDraftDmpsWithGrant(false), MetricLabels.DRAFT);
|
||||
this.setGaugeValue(gauges, MetricNames.DMP, calculateFinalizedDmpsWithGrant(false), MetricLabels.FINALIZED);
|
||||
this.setGaugeValue(gauges, MetricNames.DMP, calculatePublishedDmpsWithGrant(false), MetricLabels.PUBLISHED);
|
||||
this.setGaugeValue(gauges, MetricNames.DMP, calculateDoiedDmpsWithGrant(false), MetricLabels.DOIED);
|
||||
this.setGaugeValue(gauges, MetricNames.DMP_WITH_GRANT, calculateDraftDmpsWithGrant(false), MetricLabels.DRAFT);
|
||||
this.setGaugeValue(gauges, MetricNames.DMP_WITH_GRANT, calculateFinalizedDmpsWithGrant(false), MetricLabels.FINALIZED);
|
||||
this.setGaugeValue(gauges, MetricNames.DMP_WITH_GRANT, calculatePublishedDmpsWithGrant(false), MetricLabels.PUBLISHED);
|
||||
this.setGaugeValue(gauges, MetricNames.DMP_WITH_GRANT, calculateDoiedDmpsWithGrant(false), MetricLabels.DOIED);
|
||||
|
||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculateDraftDmpsWithGrant(true), MetricLabels.DRAFT);
|
||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculateFinalizedDmpsWithGrant(true), MetricLabels.FINALIZED);
|
||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculatePublishedDmpsWithGrant(true), MetricLabels.PUBLISHED);
|
||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP, calculateDoiedDmpsWithGrant(true), MetricLabels.DOIED);
|
||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP_WITH_GRANT, calculateDraftDmpsWithGrant(true), MetricLabels.DRAFT);
|
||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP_WITH_GRANT, calculateFinalizedDmpsWithGrant(true), MetricLabels.FINALIZED);
|
||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP_WITH_GRANT, calculatePublishedDmpsWithGrant(true), MetricLabels.PUBLISHED);
|
||||
this.setGaugeValue(gauges, MetricNames.NEXUS_PREFIX + MetricNames.DMP_WITH_GRANT, calculateDoiedDmpsWithGrant(true), MetricLabels.DOIED);
|
||||
|
||||
this.setGaugeValue(gauges, MetricNames.FUNDERS, calculateAllFunders(false), null);
|
||||
this.setGaugeValue(gauges, MetricNames.GRANTS, calculateAllGrants(false), null);
|
||||
|
@ -181,6 +181,7 @@ public class MetricsServiceImpl implements MetricsService {
|
|||
dmpQuery.after(_config.getNexusDate());
|
||||
EntityDoiQuery entityDoiQuery = this.queryFactory.query(EntityDoiQuery.class).types(EntityType.DMP).isActive(IsActive.Active);
|
||||
dmpQuery.entityDoiSubQuery(entityDoiQuery);
|
||||
dmpQuery.setDistinct(true);
|
||||
return dmpQuery.count();
|
||||
}
|
||||
|
||||
|
|
|
@ -423,7 +423,6 @@ public class UserServiceImpl implements UserService {
|
|||
|
||||
String token = this.createMergeAccountConfirmation(model.getEmail());
|
||||
createMergeNotificationEvent(token, user, model.getEmail(), NotificationContactType.EMAIL);
|
||||
createMergeNotificationEvent(token, user, model.getEmail(), NotificationContactType.IN_APP);
|
||||
}
|
||||
|
||||
private void createMergeNotificationEvent(String token, UserEntity user, String email, NotificationContactType type) throws InvalidApplicationException {
|
||||
|
@ -453,7 +452,6 @@ public class UserServiceImpl implements UserService {
|
|||
|
||||
String token = this.createRemoveConfirmation(data.getId());
|
||||
this.createRemoveCredentialNotificationEvent(token, data.getUserId(), NotificationContactType.EMAIL);
|
||||
this.createRemoveCredentialNotificationEvent(token, data.getUserId(), NotificationContactType.IN_APP);
|
||||
}
|
||||
|
||||
private void createRemoveCredentialNotificationEvent(String token, UUID userId, NotificationContactType type) throws InvalidApplicationException {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
metrics:
|
||||
task:
|
||||
enable: true
|
||||
intervalSeconds: 600
|
||||
nexusDate: "2021-01-01T00:00:00.00Z"
|
||||
usersLoginClient: ${IDP_APIKEY_CLIENT_ID_UUID:}
|
||||
referenceTypes:
|
||||
funderIds: ["538928bb-c7c6-452e-b66d-08e539f5f082"]
|
||||
grantIds: ["5b9c284f-f041-4995-96cc-fad7ad13289c"]
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
metrics:
|
||||
task:
|
||||
enable: true
|
||||
intervalSeconds: 600
|
||||
nexusDate: "2021-01-01T00:00:00.00Z"
|
||||
usersLoginClient: ${IDP_APIKEY_CLIENT_ID_UUID:}
|
||||
enable: false
|
||||
intervalSeconds: null
|
||||
nexusDate: null
|
||||
usersLoginClient: null
|
||||
referenceTypes:
|
||||
funderIds: []
|
||||
grantIds: []
|
||||
|
|
|
@ -4,6 +4,5 @@ export enum AppRole {
|
|||
TenantAdmin = "TenantAdmin",
|
||||
TenantUser = "TenantUser",
|
||||
TenantManager = "TenantManager",
|
||||
TenantDescriptionTemplateEditor = "TenantDescriptionTemplateEditor",
|
||||
|
||||
TenantDescriptionTemplateEditor = "TenantDescriptionTemplateEditor"
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ ngx-guided-tour {
|
|||
&.tour-top-right {
|
||||
.tour-arrow::before {
|
||||
transform: translateX(-100%);
|
||||
left: calc(100% - 5px);
|
||||
left: calc(100% - 15px);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,32 +7,47 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-label>{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.STATUS' | translate}}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.STATUS' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.statuses">
|
||||
<mat-option *ngFor="let descriptionTemplateStatus of descriptionTemplateStatusEnumValues" [value]="descriptionTemplateStatus">{{enumUtils.toDescriptionTemplateTypeStatusString(descriptionTemplateStatus)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-label>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'DESCRIPTION-TEMPLATE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -22,4 +22,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,32 +7,47 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-label>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.STATUS' | translate}}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.STATUS' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.statuses">
|
||||
<mat-option *ngFor="let descriptionTemplateTypeStatus of descriptionTemplateTypeStatusEnumValues" [value]="descriptionTemplateTypeStatus">{{enumUtils.toDescriptionTemplateTypeStatusString(descriptionTemplateTypeStatus)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-label>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'DESCRIPTION-TEMPLATE-TYPE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -22,4 +22,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'DMP-BLUEPRINT-LISTING.FILTER.CANCEL' | translate}}
|
||||
|
|
|
@ -7,39 +7,47 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'LOCK-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<div>
|
||||
<mat-form-field class="col-12">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'LOCK-LISTING.FILTER.USERS' | translate}}</mat-label>
|
||||
<app-multiple-auto-complete [(ngModel)]="internalFilters.userIds" [hidePlaceholder]="true" [separatorKeysCodes]="separatorKeysCodes" [configuration]="userService.multipleAutocompleteConfiguration">
|
||||
</app-multiple-auto-complete>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field class="col-12">
|
||||
<mat-label>{{'LOCK-LISTING.FILTER.TARGET-TYPE' | translate}}
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'LOCK-LISTING.FILTER.TARGET-TYPE' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.targetTypes">
|
||||
<mat-option *ngFor="let targetType of lockTargetTypeEnumValues" [value]="targetType">{{enumUtils.toLockTargetTypeString(targetType)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-label>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-between mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'LOCK-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'LOCK-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -17,5 +17,3 @@
|
|||
// height: 1.2em;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div class="row mt-3">
|
||||
<div class="col-4" *ngIf="isNew">
|
||||
<div class="col-6" *ngIf="isNew">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'LANGUAGE-EDITOR.FIELDS.CODE' | translate}}</mat-label>
|
||||
<input matInput type="text" name="code" [formControl]="formGroup.get('code')" required>
|
||||
|
@ -38,7 +38,7 @@
|
|||
<mat-error *ngIf="formGroup.get('code').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="col-4" *ngIf="!isNew">
|
||||
<div class="col-6" *ngIf="!isNew">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'LANGUAGE-EDITOR.FIELDS.CODE' | translate}}</mat-label>
|
||||
<mat-select (selectionChange)="selectedCodeChanged($event.value)" name= "code" [formControl]="formGroup.get('code')">
|
||||
|
|
|
@ -7,24 +7,36 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'LANGUAGE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'LANGUAGE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'LANGUAGE-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'LANGUAGE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -18,4 +18,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
|
||||
<form *ngIf="formGroup" (ngSubmit)="formSubmit()">
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card appearance="outlined" class="mb-1">
|
||||
<mat-card-header>
|
||||
<mat-card-title *ngIf="isNew">{{'PREFILLING-SOURCE-EDITOR.NEW' | translate}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
|
@ -42,7 +42,7 @@
|
|||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card appearance="outlined" class="mb-1">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.FIXED-VALUE-FIELDS' | translate}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
|
@ -82,7 +82,7 @@
|
|||
</div>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card appearance="outlined" class="mb-1">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.FIELDS' | translate}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
|
@ -122,7 +122,7 @@
|
|||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card appearance="outlined" class="mb-1">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.SOURCE-CONFIGURATION' | translate}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
|
@ -138,7 +138,7 @@
|
|||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
<mat-card appearance="outlined" *ngIf="formGroup.get('definition').get('getEnabled').value == true">
|
||||
<mat-card appearance="outlined" *ngIf="formGroup.get('definition').get('getEnabled').value == true" class="mb-1">
|
||||
<mat-card-header>
|
||||
<mat-card-title>{{'PREFILLING-SOURCE-EDITOR.FIELDS.GET-SOURCE-CONFIGURATION' | translate}}</mat-card-title>
|
||||
</mat-card-header>
|
||||
|
|
|
@ -7,24 +7,36 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'PREFILLING-SOURCE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'PREFILLING-SOURCE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'PREFILLING-SOURCE-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'PREFILLING-SOURCE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -22,4 +22,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="row reference-type-editor">
|
||||
<div class="col-md-10 offset-md-1 colums-gapped">
|
||||
<div class="row justify-content-between align-items-center">
|
||||
<div class="col">
|
||||
<div class="col-md col-12">
|
||||
<h3 *ngIf="isNew">{{'REFERENCE-TYPE-EDITOR.NEW' | translate}}</h3>
|
||||
<app-navigation-breadcrumb />
|
||||
</div>
|
||||
|
|
|
@ -7,24 +7,36 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'REFERENCE-TYPE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'REFERENCE-TYPE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'REFERENCE-TYPE-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'REFERENCE-TYPE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -22,4 +22,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="row reference-editor">
|
||||
<div class="col-md-10 offset-md-1 colums-gapped">
|
||||
<div class="row align-items-center mt-4 mb-4" *ngIf="formGroup">
|
||||
<div class="col-auto">
|
||||
<div class="col-md col-12">
|
||||
<h3 *ngIf="isNew && !isClone">{{'REFERENCE-EDITOR.NEW' | translate}}</h3>
|
||||
<app-navigation-breadcrumb />
|
||||
</div>
|
||||
|
|
|
@ -7,39 +7,58 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'REFERENCE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'REFERENCE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
|
||||
<div>
|
||||
<mat-label>{{'REFERENCE-LISTING.FILTER.TYPE' | translate}}
|
||||
<app-single-auto-complete [(ngModel)]="internalFilters.typeIds" placeholder="{{'REFERENCE-LISTING.FILTER.TYPE' | translate}}" [configuration]="referenceTypeService.singleAutocompleteConfiguration">
|
||||
</app-single-auto-complete>
|
||||
</mat-label>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-label>{{'REFERENCE-LISTING.FILTER.SOURCE-TYPE' | translate}}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'REFERENCE-LISTING.FILTER.TYPE' | translate}}</mat-label>
|
||||
<app-single-auto-complete [(ngModel)]="internalFilters.typeIds" placeholder="{{'REFERENCE-LISTING.FILTER.TYPE' | translate}}" [configuration]="referenceTypeService.singleAutocompleteConfiguration">
|
||||
</app-single-auto-complete>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'REFERENCE-LISTING.FILTER.SOURCE-TYPE' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.sourceTypes">
|
||||
<mat-option *ngFor="let referenceSourceType of referenceSourceTypeEnumValues" [value]="referenceSourceType">{{enumUtils.toReferenceSourceTypeString(referenceSourceType)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-label>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'REFERENCE-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'REFERENCE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -18,4 +18,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="row">
|
||||
<div class="col-md-10 offset-md-1 tenant-editor">
|
||||
<div class="row align-items-center mb-4 mt-4" *ngIf="formGroup">
|
||||
<div class="col-auto">
|
||||
<div class="col-md col-12">
|
||||
<h3 *ngIf="isNew && !isClone">{{'TENANT-EDITOR.NEW' | translate}}</h3>
|
||||
<app-navigation-breadcrumb />
|
||||
</div>
|
||||
|
|
|
@ -7,24 +7,36 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'TENANT-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'TENANT-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'TENANT-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'TENANT-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -22,4 +22,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,32 +7,47 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'USER-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'USER-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-label>{{'USER-LISTING.FILTER.ROLES' | translate}}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'USER-LISTING.FILTER.ROLES' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.userRoleSubQuery.roles">
|
||||
<mat-option *ngFor="let appRole of appRoleEnumValues" [value]="appRole">{{enumUtils.toAppRoleString(appRole)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-label>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'USER-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'USER-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -22,4 +22,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
<form class="row user-role-editor" *ngIf="formGroup" [formGroup]="formGroup" (ngSubmit)="formSubmit()">
|
||||
<div class="container-fluid">
|
||||
<div class="row align-items-center">
|
||||
<div *ngIf="!this.nowEditing"class="roles col">
|
||||
<ng-container *ngFor="let role of this.formGroup.get('roles').value">
|
||||
<div>
|
||||
<span class="user-role" [ngClass]="{'user': role == appRole.User, 'tenant-manager': role == appRole.TenantManager, 'admin': role == appRole.Admin, 'tenant-admin': role == appRole.TenantAdmin, 'tenant-user': role == appRole.TenantUser,'tenant-description-template-editor': role == appRole.TenantDescriptionTemplateEditor}">
|
||||
<div *ngIf="!this.nowEditing"class="roles col-8">
|
||||
<div *ngFor="let role of this.formGroup.get('roles').value" class="row">
|
||||
<div class="col-auto p-0">
|
||||
<span class="user-role" [ngClass]="{'user': role == appRole.User, 'tenant-manager': role == appRole.TenantManager, 'admin': role == appRole.Admin, 'tenant-description-template-editor': role == appRole.TenantDescriptionTemplateEditor, 'tenant-admin': role == appRole.TenantAdmin, 'tenant-user': role == appRole.TenantUser}">
|
||||
{{enumUtils.toAppRoleString(role)}}
|
||||
</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<mat-form-field *ngIf="this.nowEditing" class="w-100">
|
||||
<mat-select formControlName="roles" multiple required>
|
||||
</div>
|
||||
<div *ngIf="this.nowEditing" class="pl-0 col-8">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-select formControlName="roles" [panelWidth]="auto" multiple required>
|
||||
<ng-container *ngFor="let role of appRoleEnumValues">
|
||||
<mat-option [value]="role">{{enumUtils.toAppRoleString(role)}}</mat-option>
|
||||
</ng-container>
|
||||
|
@ -20,13 +21,19 @@
|
|||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<button *ngIf="!this.nowEditing" class="col" mat-icon-button color="primary" type="button" (click)="editItem()">
|
||||
<div *ngIf="!this.nowEditing" class="col-2 p-0">
|
||||
<button mat-icon-button color="primary" type="button" (click)="editItem()">
|
||||
<mat-icon class="mat-24" matTooltip="{{'USER-LISTING.ACTIONS.EDIT' | translate}}">edit</mat-icon>
|
||||
<span class="row-action"></span>
|
||||
</button>
|
||||
<button *ngIf="this.nowEditing" class="col-auto save-button" mat-icon-button color="primary" type="submit">
|
||||
</div>
|
||||
<div *ngIf="this.nowEditing" class="col-2 p-0">
|
||||
<button class="save-button" mat-icon-button color="primary" type="submit">
|
||||
<mat-icon class="mat-24" matTooltip="{{'USER-LISTING.ACTIONS.SAVE' | translate}}">save</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
|
|
@ -85,6 +85,24 @@
|
|||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.tenant-admin {
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
min-width: 77px;
|
||||
min-height: 28px;
|
||||
color: #ffa631;
|
||||
background: #ffe6c6 0% 0% no-repeat padding-box;
|
||||
border-radius: 44px;
|
||||
letter-spacing: 0.11px;
|
||||
font-weight: 400;
|
||||
opacity: 1;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.tenant-description-template-editor {
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<div class="main-content dashboard-main-container h-100" [class.non-auth-main-container]="!this.isAuthenticated()">
|
||||
<div class="main-content dashboard-main-container h-100">
|
||||
<div *ngIf="this.isAuthenticated()" class="container-fluid">
|
||||
<div *ngIf="this.dashboardStatistics" class="main-content">
|
||||
<div class="container-fluid">
|
||||
<div class="row flex-column-reverse flex-xl-row">
|
||||
|
||||
<div class="col-12 col-xl-10">
|
||||
<div class="row">
|
||||
<div *ngIf="newReleaseNotificationVisible" class="new-releases-card col-auto mt-0 mr-4">
|
||||
|
@ -79,7 +78,6 @@
|
|||
</mat-tab-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Sidebar -->
|
||||
<div *ngIf="!this.hasDmps()" class="col-12 col-xl-2 mb-4 stats">
|
||||
<div class="row">
|
||||
|
@ -108,7 +106,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="this.hasDmps()" class="col-12 col-xl-2 mb-4 stats">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
|
@ -147,13 +144,13 @@
|
|||
</div>
|
||||
|
||||
<!-- Home screen on log out -->
|
||||
<div class="col p-0" *ngIf="!this.isAuthenticated()">
|
||||
<div class="col-auto">
|
||||
<div class="container-fluid" *ngIf="!this.isAuthenticated()">
|
||||
<div class="main-content">
|
||||
<div class="col">
|
||||
<div class="container-fluid">
|
||||
<div class="row flex-column-reverse flex-xl-row">
|
||||
<div class="col-12 col-xl">
|
||||
<div class="row">
|
||||
<div class="col d-flex flex-column">
|
||||
<div class="card non-auth-card" [style.display]="isIntroCardVisible ? 'block' : 'none'">
|
||||
<div class="col-auto card" [style.display]="isIntroCardVisible ? 'block' : 'none'">
|
||||
<a class="col-auto d-flex" (click)="dismissIntroCard()"><span class="ml-auto mt-3 material-icons clear-icon">clear</span></a>
|
||||
<div class="d-flex flex-column align-items-center non-auth-title-container">
|
||||
<h4 class="pt-4">{{'DASHBOARD.TITLE' | translate}}</h4>
|
||||
|
@ -163,6 +160,7 @@
|
|||
<img class="col-auto ml-auto laptop-img" src="../../../assets/images/dashboard-popup.png">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div *ngIf="hasDmps()" class="col activity">
|
||||
<div class="latest-activity-title">{{'DASHBOARD.LATEST-ACTIVITY' | translate}}</div>
|
||||
<mat-tab-group mat-align-tabs="start" class="remove-border-bottom" ( [selectedIndex]="indexFromCurrentType" selectedTabChange)="currentType = $event.tab.ariaLabel">
|
||||
|
@ -181,31 +179,50 @@
|
|||
</mat-tab-group>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="col-12 col-xl-auto mb-4 stats">
|
||||
<div *ngIf="!hasDmps()" class="row flex-xl-column">
|
||||
<div class="col-12">
|
||||
<div class="personal-usage"><span>{{'DASHBOARD.PUBLIC-USAGE' | translate}}</span></div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div *ngIf="!hasDmps()" class="ml-auto pl-4 personal-usage-block">
|
||||
<div class="personal-usage">{{'DASHBOARD.PUBLIC-USAGE' | translate}}</div>
|
||||
<div class="counter-zero">0</div>
|
||||
<a>{{'DASHBOARD.PUBLIC-DMPS' | translate}}</a>
|
||||
<div class="counter-zero">0</div>
|
||||
<a>{{'DASHBOARD.PUBLIC-DATASETS' | translate}}</a>
|
||||
<div class="counter-zero">0</div>
|
||||
<div class="counter-zero"><span>0</span></div>
|
||||
<a class="link">{{'DASHBOARD.PUBLIC-DMPS' | translate}}</a>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="counter-zero"><span>0</span></div>
|
||||
<a class="link">{{'DASHBOARD.PUBLIC-DESCRIPTIONS' | translate}}</a>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="counter-zero"><span>0</span></div>
|
||||
<a class="link-disabled">{{'DASHBOARD.GRANTS' | translate}}</a>
|
||||
<div class="counter-zero">0</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="counter-zero"><span>0</span></div>
|
||||
<a class="link-disabled">{{'DASHBOARD.RELATED-ORGANISATIONS' | translate}}</a>
|
||||
</div>
|
||||
<div *ngIf="hasDmps()" class="ml-auto stats">
|
||||
<div class="personal-usage">{{'DASHBOARD.PUBLIC-USAGE' | translate}}</div>
|
||||
<div [ngClass]="{'counter': dashboardStatistics?.dmpCount != 0, 'counter-zero': dashboardStatistics?.dmpCount == 0}">
|
||||
{{dashboardStatistics?.dmpCount}}</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="hasDmps()" class="row flex-xl-column">
|
||||
<div class="col-12">
|
||||
<div class="personal-usage" ><span>{{'DASHBOARD.PUBLIC-USAGE' | translate}}</span></div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div [ngClass]="{'counter': dashboardStatistics?.dmpCount != 0, 'counter-zero': dashboardStatistics?.dmpCount == 0}" ><span>{{dashboardStatistics?.dmpCount}}</span></div>
|
||||
<a [routerLink]="['/explore-plans']" class="link">{{'DASHBOARD.PUBLIC-DMPS' | translate}}</a>
|
||||
<div [ngClass]="{'counter': dashboardStatistics?.descriptionCount != 0, 'counter-zero': dashboardStatistics?.descriptionCount == 0}">
|
||||
{{dashboardStatistics?.descriptionCount}}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<div [ngClass]="{'counter': dashboardStatistics?.descriptionCount != 0, 'counter-zero': dashboardStatistics?.descriptionCount == 0}"><span>{{dashboardStatistics?.descriptionCount}}</span></div>
|
||||
<a [routerLink]="['/explore-descriptions']" class="link">{{'DASHBOARD.PUBLIC-DESCRIPTIONS' | translate}}</a>
|
||||
<div [ngClass]="{'counter': grantCount != 0, 'counter-zero': grantCount == 0}">
|
||||
{{grantCount}}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<div [ngClass]="{'counter': grantCount != 0, 'counter-zero': grantCount == 0}" ><span>{{grantCount}}</span></div>
|
||||
<a href="#" class="link-disabled">{{'DASHBOARD.GRANTS' | translate}}</a>
|
||||
<div [ngClass]="{'counter': organizationCount != 0, 'counter-zero': organizationCount == 0}">
|
||||
{{organizationCount}}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto">
|
||||
<div [ngClass]="{'counter': organizationCount != 0, 'counter-zero': organizationCount == 0}"><span>{{organizationCount}}</span></div>
|
||||
<a href="#" class="link-disabled">{{'DASHBOARD.RELATED-ORGANISATIONS' | translate}}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -374,14 +374,6 @@ input[type="text"] {
|
|||
padding: 0rem 7em 0rem 3rem;
|
||||
}
|
||||
|
||||
.non-auth-main-container {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.non-auth-card {
|
||||
margin-left: 3rem;
|
||||
}
|
||||
|
||||
.app-info {
|
||||
font-size: 1rem;
|
||||
padding: 1rem 2rem;
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { UntypedFormGroup } from '@angular/forms';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { DescriptionStatus } from '@app/core/common/enum/description-status';
|
||||
import { DescriptionTemplateVersionStatus } from '@app/core/common/enum/description-template-version-status';
|
||||
import { IsActive } from '@app/core/common/enum/is-active.enum';
|
||||
import { DescriptionTemplate } from '@app/core/model/description-template/description-template';
|
||||
import { Description } from '@app/core/model/description/description';
|
||||
import { DmpBlueprintDefinitionSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
|
||||
import { Dmp, DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
|
||||
import { DescriptionTemplateService } from '@app/core/services/description-template/description-template.service';
|
||||
import { DmpDescriptionTemplate } from '@app/core/model/dmp/dmp';
|
||||
import { DescriptionService } from '@app/core/services/description/description.service';
|
||||
import { DmpBlueprintService } from '@app/core/services/dmp/dmp-blueprint.service';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { DeprecatedDescriptionTemplateDialog } from './dialog-description-template/deprecated-description-template-dialog.component';
|
||||
import { DescriptionTemplateVersionStatus } from '@app/core/common/enum/description-template-version-status';
|
||||
import { DescriptionStatus } from '@app/core/common/enum/description-status';
|
||||
|
||||
@Component({
|
||||
selector: 'app-description-base-fields-editor-component',
|
||||
|
@ -47,6 +44,7 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
|
|||
const currentVersionsOfDescriptionTemplates = dmpDescriptionTemplates.map(x => x.currentDescriptionTemplate);
|
||||
this.availableDescriptionTemplates.push(...currentVersionsOfDescriptionTemplates);
|
||||
|
||||
if (this.description?.descriptionTemplate != null) {
|
||||
const isPreviousVersion: boolean = this.description.descriptionTemplate.versionStatus === DescriptionTemplateVersionStatus.Previous;
|
||||
if (isPreviousVersion === true) {
|
||||
if (this.description.status === DescriptionStatus.Draft) {
|
||||
|
@ -58,6 +56,7 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
|
|||
this.availableDescriptionTemplates.push(this.description.descriptionTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private openDeprecatedDescriptionTemplateDialog(): void {
|
||||
const dialogRef = this.dialog.open(DeprecatedDescriptionTemplateDialog, {
|
||||
|
@ -67,7 +66,7 @@ export class DescriptionBaseFieldsEditorComponent extends BaseComponent {
|
|||
});
|
||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(
|
||||
result => {
|
||||
if(result) {
|
||||
if (result) {
|
||||
this.descriptionService.updateDescriptionTemplate({
|
||||
id: this.description.id,
|
||||
hash: this.description.hash
|
||||
|
|
|
@ -111,13 +111,13 @@
|
|||
<span class="material-icons">chevron_left</span>
|
||||
<div>{{'DMP-EDITOR.ACTIONS.PREVIOUS-STEP' | translate}}</div>
|
||||
</div>
|
||||
<div *ngIf="this.step < this.maxStep" mat-raised-button type="button" class="col-auto stepper-btn ml-auto" [ngClass]="{ 'next-disabled': this.step === this.maxStep, 'next': this.step < selectedBlueprint?.definition?.sections?.length, 'description-next': this.step >= selectedBlueprint?.definition?.sections?.length }" (click)="nextStep()">
|
||||
<div *ngIf="this.step < this.maxSteps" mat-raised-button type="button" class="col-auto stepper-btn ml-auto" [ngClass]="{ 'next-disabled': this.step === this.maxSteps, 'next': this.step < selectedBlueprint?.definition?.sections?.length, 'description-next': this.step >= selectedBlueprint?.definition?.sections?.length }" (click)="nextStep()">
|
||||
<div>{{'DMP-EDITOR.ACTIONS.NEXT-STEP' | translate}}</div>
|
||||
<span class="material-icons">chevron_right</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto pr-0" *ngIf="this.step !== 0">
|
||||
<!-- <app-form-progress-indication class="col-12" *ngIf="formGroup && !formGroup.disabled && !lockStatus" [formGroup]="formGroup" [isDmpEditor]="true"></app-form-progress-indication> -->
|
||||
<app-dmp-form-progress-indication class="col-12" *ngIf="formGroup && !formGroup.disabled && !lockStatus" [formGroup]="formGroup"></app-dmp-form-progress-indication>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
|||
filterFn: (searchQuery: string, data?: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(searchQuery, null, null, [DmpBlueprintStatus.Finalized])).pipe(map(x => x.items)),
|
||||
getSelectedItem: (selectedItem: any) => this.dmpBlueprintService.query(this.dmpBlueprintService.buildAutocompleteLookup(null, null, [selectedItem])).pipe(map(x => x.items[0])),
|
||||
displayFn: (item: DmpBlueprint) => item.label,
|
||||
subtitleFn: (item: DmpBlueprint) => this.language.instant('DMP-EDITOR.FIELDS.DMP-BLUEPRINT-VERSION') + ' '+ item.version,
|
||||
subtitleFn: (item: DmpBlueprint) => this.language.instant('DMP-EDITOR.FIELDS.DMP-BLUEPRINT-VERSION') + ' ' + item.version,
|
||||
titleFn: (item: DmpBlueprint) => item.label,
|
||||
valueAssign: (item: DmpBlueprint) => item.id,
|
||||
};
|
||||
|
@ -174,14 +174,14 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
|||
try {
|
||||
this.editorModel = data ? new DmpEditorModel().fromModel(data) : new DmpEditorModel();
|
||||
if (data) {
|
||||
if(data.descriptions){
|
||||
if (data.descriptions) {
|
||||
if (data.status == DmpStatus.Finalized) {
|
||||
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status === DescriptionStatus.Finalized);
|
||||
} else {
|
||||
data.descriptions = data.descriptions.filter(x => x.isActive === IsActive.Active && x.status !== DescriptionStatus.Canceled);
|
||||
}
|
||||
}
|
||||
if(data.dmpDescriptionTemplates){
|
||||
if (data.dmpDescriptionTemplates) {
|
||||
data.dmpDescriptionTemplates = data.dmpDescriptionTemplates.filter(x => x.isActive === IsActive.Active);
|
||||
}
|
||||
|
||||
|
@ -304,6 +304,11 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
|||
// Steps
|
||||
//
|
||||
//
|
||||
|
||||
get maxSteps(): number {
|
||||
return this.item?.blueprint?.definition?.sections?.length ?? 0;
|
||||
}
|
||||
|
||||
changeStep(index: number) {
|
||||
this.step = index;
|
||||
this.resetScroll();
|
||||
|
@ -389,7 +394,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
|||
DmpEditorModel.reApplyPropertiesValidators(
|
||||
{
|
||||
formGroup: this.formGroup,
|
||||
validationErrorModel: this.editorModel.validationErrorModel
|
||||
validationErrorModel: this.editorModel.validationErrorModel,
|
||||
blueprint: this.item.blueprint
|
||||
}
|
||||
);
|
||||
this.formGroup.get('properties').get('contacts').markAsDirty();
|
||||
|
@ -404,7 +410,8 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
|||
DmpEditorModel.reApplyPropertiesValidators(
|
||||
{
|
||||
formGroup: this.formGroup,
|
||||
validationErrorModel: this.editorModel.validationErrorModel
|
||||
validationErrorModel: this.editorModel.validationErrorModel,
|
||||
blueprint: this.item.blueprint
|
||||
}
|
||||
);
|
||||
this.formGroup.get('properties').get('contacts').markAsDirty();
|
||||
|
@ -455,32 +462,32 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
|||
});
|
||||
}
|
||||
|
||||
canAddDescription(section: DmpBlueprintDefinitionSection ): boolean{
|
||||
if(section.hasTemplates){
|
||||
if (section.descriptionTemplates?.length > 0){
|
||||
canAddDescription(section: DmpBlueprintDefinitionSection): boolean {
|
||||
if (section.hasTemplates) {
|
||||
if (section.descriptionTemplates?.length > 0) {
|
||||
const descriptions = this.descriptionsInSection(section.id)
|
||||
|
||||
if (this.item.dmpDescriptionTemplates.filter(x => x.sectionId == section.id).length > descriptions.map(x => x.dmpDescriptionTemplate).length){
|
||||
if (this.item.dmpDescriptionTemplates.filter(x => x.sectionId == section.id).length > descriptions.map(x => x.dmpDescriptionTemplate).length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let multiplicityValidResults :boolean[] = [];
|
||||
let multiplicityValidResults: boolean[] = [];
|
||||
section.descriptionTemplates.forEach(sectionDescriptionTemplate => {
|
||||
if (sectionDescriptionTemplate.maxMultiplicity != null){
|
||||
if (sectionDescriptionTemplate.maxMultiplicity != null) {
|
||||
const count = descriptions.filter(x => x.dmpDescriptionTemplate.descriptionTemplateGroupId == sectionDescriptionTemplate.descriptionTemplateGroupId).length || 0;
|
||||
if (count >= sectionDescriptionTemplate.maxMultiplicity) multiplicityValidResults.push(false);
|
||||
else multiplicityValidResults.push(true);
|
||||
}else{
|
||||
} else {
|
||||
multiplicityValidResults.push(true);
|
||||
}
|
||||
})
|
||||
|
||||
if(multiplicityValidResults.includes(true)) return true
|
||||
if (multiplicityValidResults.includes(true)) return true
|
||||
else return false;
|
||||
}else{
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -504,10 +511,10 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
|
|||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(groupId => {
|
||||
if (groupId) {
|
||||
let data = this.formGroup.get('descriptionTemplates').get(sectionId.toString()).value as Guid[];
|
||||
if (data){
|
||||
if (data) {
|
||||
data.push(groupId);
|
||||
this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue(data);
|
||||
} else{
|
||||
} else {
|
||||
this.formGroup.get('descriptionTemplates').get(sectionId.toString()).patchValue([groupId]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { FormArray, FormControl, UntypedFormArray, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||
import { FormArray, FormControl, UntypedFormBuilder, UntypedFormGroup, Validators } from "@angular/forms";
|
||||
import { DmpAccessType } from "@app/core/common/enum/dmp-access-type";
|
||||
import { DmpBlueprintFieldCategory } from "@app/core/common/enum/dmp-blueprint-field-category";
|
||||
import { DmpContactType } from "@app/core/common/enum/dmp-contact-type";
|
||||
import { DmpStatus } from "@app/core/common/enum/dmp-status";
|
||||
import { DmpUserRole } from "@app/core/common/enum/dmp-user-role";
|
||||
import { DmpUserType } from "@app/core/common/enum/dmp-user-type";
|
||||
import { IsActive } from "@app/core/common/enum/is-active.enum";
|
||||
import { DmpBlueprint } from "@app/core/model/dmp-blueprint/dmp-blueprint";
|
||||
import { DmpBlueprint, FieldInSection } from "@app/core/model/dmp-blueprint/dmp-blueprint";
|
||||
import { Dmp, DmpBlueprintValue, DmpBlueprintValuePersist, DmpContact, DmpContactPersist, DmpDescriptionTemplate, DmpDescriptionTemplatePersist, DmpPersist, DmpProperties, DmpPropertiesPersist, DmpReferenceDataPersist, DmpReferencePersist, DmpUser, DmpUserPersist } from "@app/core/model/dmp/dmp";
|
||||
import { DmpReference } from "@app/core/model/dmp/dmp-reference";
|
||||
import { ReferencePersist } from "@app/core/model/reference/reference";
|
||||
|
@ -120,13 +121,16 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
|
|||
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||
baseValidationArray.push({ key: 'id', validators: [BackendErrorValidator(this.validationErrorModel, 'id')] });
|
||||
baseValidationArray.push({ key: 'label', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'label')] });
|
||||
baseValidationArray.push({ key: 'status', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'status')] });
|
||||
baseValidationArray.push({ key: 'properties', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'properties')] });
|
||||
baseValidationArray.push({ key: 'status', validators: [BackendErrorValidator(this.validationErrorModel, 'status')] });
|
||||
baseValidationArray.push({ key: 'properties', validators: [BackendErrorValidator(this.validationErrorModel, 'properties')] });
|
||||
baseValidationArray.push({ key: 'description', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'description')] });
|
||||
baseValidationArray.push({ key: 'language', validators: [BackendErrorValidator(this.validationErrorModel, 'language')] });
|
||||
baseValidationArray.push({ key: 'blueprint', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'blueprint')] });
|
||||
baseValidationArray.push({ key: 'accessType', validators: [BackendErrorValidator(this.validationErrorModel, 'accessType')] });
|
||||
baseValidationArray.push({ key: 'language', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'language')] });
|
||||
baseValidationArray.push({ key: 'blueprint', validators: [BackendErrorValidator(this.validationErrorModel, 'blueprint')] });
|
||||
baseValidationArray.push({ key: 'accessType', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'accessType')] });
|
||||
baseValidationArray.push({ key: 'descriptionTemplates', validators: [Validators.required, BackendErrorValidator(this.validationErrorModel, 'descriptionTemplates')] });
|
||||
|
||||
baseValidationArray.push({ key: 'dmpDescriptionValidator', validators: [] });
|
||||
|
||||
baseValidationArray.push({ key: 'users', validators: [BackendErrorValidator(this.validationErrorModel, `users`)] });
|
||||
baseValidationArray.push({ key: 'hash', validators: [] });
|
||||
|
||||
|
@ -143,6 +147,7 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
|
|||
static reApplyPropertiesValidators(params: {
|
||||
formGroup: UntypedFormGroup,
|
||||
validationErrorModel: ValidationErrorModel,
|
||||
blueprint: DmpBlueprint
|
||||
}): void {
|
||||
|
||||
const { formGroup, validationErrorModel } = params;
|
||||
|
@ -150,20 +155,36 @@ export class DmpEditorModel extends BaseEditorModel implements DmpPersist {
|
|||
DmpPropertiesEditorModel.reapplyValidators({
|
||||
formGroup: control as UntypedFormGroup,
|
||||
rootPath: `properties.`,
|
||||
validationErrorModel: validationErrorModel
|
||||
validationErrorModel: validationErrorModel,
|
||||
blueprint: params.blueprint
|
||||
});
|
||||
}
|
||||
|
||||
static reApplyDescriptionTemplateValidators(params: {
|
||||
formGroup: UntypedFormGroup,
|
||||
validationErrorModel: ValidationErrorModel,
|
||||
}): void {
|
||||
|
||||
const { formGroup, validationErrorModel } = params;
|
||||
|
||||
const descriptionTemplates = formGroup?.get('descriptionTemplates') as UntypedFormGroup;
|
||||
const keys = Object.keys(descriptionTemplates.value as Object);
|
||||
keys.forEach((key) => {
|
||||
const control = descriptionTemplates?.get(key);
|
||||
DmpBlueprintValueEditorModel.reapplyValidators({
|
||||
DmpDescriptionTemplateEditorModel.reapplyValidators({
|
||||
formGroup: control as UntypedFormGroup,
|
||||
rootPath: `descriptionTemplates[${key}].`,
|
||||
validationErrorModel: validationErrorModel
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
static reApplyUsersValidators(params: {
|
||||
formGroup: UntypedFormGroup,
|
||||
validationErrorModel: ValidationErrorModel,
|
||||
}): void {
|
||||
|
||||
const { formGroup, validationErrorModel } = params;
|
||||
(formGroup.get('users') as FormArray).controls?.forEach(
|
||||
(control, index) => DmpUserEditorModel.reapplyValidators({
|
||||
formGroup: control as UntypedFormGroup,
|
||||
|
@ -189,11 +210,13 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
|
|||
|
||||
dmpBlueprint.definition.sections.forEach(section => {
|
||||
section.fields?.forEach(field => {
|
||||
if (field.category !== DmpBlueprintFieldCategory.System) {
|
||||
this.dmpBlueprintValues.set(field.id, new DmpBlueprintValueEditorModel(this.validationErrorModel).fromModel(
|
||||
{
|
||||
fieldId: field.id,
|
||||
fieldValue: item?.dmpBlueprintValues?.find(x => x.fieldId == field.id)?.fieldValue,
|
||||
}, dmpReferences));
|
||||
}, dmpReferences, field));
|
||||
}
|
||||
});
|
||||
});
|
||||
if (item?.contacts) { item.contacts.map(x => this.contacts.push(new DmpContactEditorModel(this.validationErrorModel).fromModel(x))); }
|
||||
|
@ -254,7 +277,8 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
|
|||
static reapplyValidators(params: {
|
||||
formGroup: UntypedFormGroup,
|
||||
validationErrorModel: ValidationErrorModel,
|
||||
rootPath: string
|
||||
rootPath: string,
|
||||
blueprint: DmpBlueprint
|
||||
}): void {
|
||||
|
||||
const { formGroup, rootPath, validationErrorModel } = params;
|
||||
|
@ -266,7 +290,8 @@ export class DmpPropertiesEditorModel implements DmpPropertiesPersist {
|
|||
DmpBlueprintValueEditorModel.reapplyValidators({
|
||||
formGroup: control as UntypedFormGroup,
|
||||
rootPath: `${rootPath}dmpBlueprintValues[${key}].`,
|
||||
validationErrorModel: validationErrorModel
|
||||
validationErrorModel: validationErrorModel,
|
||||
isRequired: params.blueprint.definition.sections.flatMap(x => x.fields).find(x => x.id.toString() == key).required
|
||||
})
|
||||
});
|
||||
|
||||
|
@ -284,6 +309,8 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
|||
fieldId: Guid;
|
||||
fieldValue: string;
|
||||
references: DmpReferencePersist[] = [];
|
||||
isRequired: boolean = false;
|
||||
category: DmpBlueprintFieldCategory;
|
||||
|
||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||
|
||||
|
@ -291,7 +318,7 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
|||
public validationErrorModel: ValidationErrorModel = new ValidationErrorModel()
|
||||
) { }
|
||||
|
||||
fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[]): DmpBlueprintValueEditorModel {
|
||||
fromModel(item: DmpBlueprintValue, dmpReferences: DmpReference[], field: FieldInSection): DmpBlueprintValueEditorModel {
|
||||
this.fieldId = item.fieldId;
|
||||
this.fieldValue = item.fieldValue;
|
||||
this.references = dmpReferences?.filter(x => x.data.blueprintFieldId == this.fieldId && x.isActive == IsActive.Active).map(x => {
|
||||
|
@ -311,6 +338,10 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
|||
}
|
||||
});
|
||||
|
||||
this.isRequired = field.required;
|
||||
if (this.isRequired) console.log(field);
|
||||
this.category = field.category;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -323,28 +354,39 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
|||
if (context == null) {
|
||||
context = DmpBlueprintValueEditorModel.createValidationContext({
|
||||
validationErrorModel: this.validationErrorModel,
|
||||
rootPath
|
||||
rootPath,
|
||||
isRequired: this.isRequired
|
||||
});
|
||||
}
|
||||
|
||||
return this.formBuilder.group({
|
||||
const formGroup = this.formBuilder.group({
|
||||
fieldId: [{ value: this.fieldId, disabled: disabled }, context.getValidation('fieldId').validators],
|
||||
fieldValue: [{ value: this.fieldValue, disabled: disabled }, context.getValidation('fieldValue').validators],
|
||||
references: [{ value: this.references?.map(x => x.reference), disabled: disabled }, context.getValidation('references').validators],
|
||||
});
|
||||
switch (this.category) {
|
||||
case DmpBlueprintFieldCategory.ReferenceType:
|
||||
formGroup.addControl('references', new FormControl({ value: this.references?.map(x => x.reference), disabled: disabled }, context.getValidation('references').validators));
|
||||
break;
|
||||
case DmpBlueprintFieldCategory.System:
|
||||
case DmpBlueprintFieldCategory.Extra:
|
||||
formGroup.addControl('fieldValue', new FormControl({ value: this.fieldValue, disabled: disabled }, context.getValidation('fieldValue').validators));
|
||||
break;
|
||||
}
|
||||
|
||||
return formGroup;
|
||||
}
|
||||
|
||||
static createValidationContext(params: {
|
||||
rootPath?: string,
|
||||
validationErrorModel: ValidationErrorModel
|
||||
validationErrorModel: ValidationErrorModel,
|
||||
isRequired: boolean,
|
||||
}): ValidationContext {
|
||||
const { rootPath = '', validationErrorModel } = params;
|
||||
|
||||
const baseContext: ValidationContext = new ValidationContext();
|
||||
const baseValidationArray: Validation[] = new Array<Validation>();
|
||||
baseValidationArray.push({ key: 'fieldId', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldId`)] });
|
||||
baseValidationArray.push({ key: 'fieldValue', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] });
|
||||
baseValidationArray.push({ key: 'references', validators: [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] });
|
||||
baseValidationArray.push({ key: 'fieldValue', validators: params.isRequired ? [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] : [BackendErrorValidator(validationErrorModel, `${rootPath}fieldValue`)] });
|
||||
baseValidationArray.push({ key: 'references', validators: params.isRequired ? [Validators.required, BackendErrorValidator(validationErrorModel, `${rootPath}references`)] : [BackendErrorValidator(validationErrorModel, `${rootPath}references`)] });
|
||||
|
||||
baseContext.validation = baseValidationArray;
|
||||
return baseContext;
|
||||
|
@ -353,13 +395,15 @@ export class DmpBlueprintValueEditorModel implements DmpBlueprintValuePersist {
|
|||
static reapplyValidators(params: {
|
||||
formGroup: UntypedFormGroup,
|
||||
validationErrorModel: ValidationErrorModel,
|
||||
rootPath: string
|
||||
rootPath: string,
|
||||
isRequired: boolean
|
||||
}): void {
|
||||
|
||||
const { formGroup, rootPath, validationErrorModel } = params;
|
||||
const context = DmpBlueprintValueEditorModel.createValidationContext({
|
||||
rootPath,
|
||||
validationErrorModel
|
||||
validationErrorModel,
|
||||
isRequired: params.isRequired
|
||||
});
|
||||
|
||||
['fieldId', 'fieldValue', 'references'].forEach(keyField => {
|
||||
|
@ -375,7 +419,7 @@ export class DmpContactEditorModel implements DmpContactPersist {
|
|||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
contactType: DmpContactType= DmpContactType.Internal;
|
||||
contactType: DmpContactType = DmpContactType.Internal;
|
||||
|
||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||
|
||||
|
@ -384,7 +428,7 @@ export class DmpContactEditorModel implements DmpContactPersist {
|
|||
) { }
|
||||
|
||||
fromModel(item: DmpContact): DmpContactEditorModel {
|
||||
if(item?.user?.id) this.userId = item.user.id;
|
||||
if (item?.user?.id) this.userId = item.user.id;
|
||||
this.firstName = item.firstName;
|
||||
this.lastName = item.lastName;
|
||||
this.email = item.email;
|
||||
|
@ -457,7 +501,7 @@ export class DmpUserEditorModel implements DmpUserPersist {
|
|||
user: Guid;
|
||||
role: DmpUserRole;
|
||||
email: string;
|
||||
userType: DmpUserType= DmpUserType.Internal;
|
||||
userType: DmpUserType = DmpUserType.Internal;
|
||||
sectionId: Guid;
|
||||
|
||||
protected formBuilder: UntypedFormBuilder = new UntypedFormBuilder();
|
||||
|
@ -467,7 +511,7 @@ export class DmpUserEditorModel implements DmpUserPersist {
|
|||
) { }
|
||||
|
||||
fromModel(item: DmpUser): DmpUserEditorModel {
|
||||
if(item?.user?.id) this.user = item.user.id;
|
||||
if (item?.user?.id) this.user = item.user.id;
|
||||
this.role = item.role;
|
||||
// TODO this.email = item.email;
|
||||
this.userType = (item == null || this.user != null) ? DmpUserType.Internal : DmpUserType.External;
|
||||
|
@ -623,7 +667,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate
|
|||
if (context == null) {
|
||||
context = DmpDescriptionTemplateEditorModel.createValidationContext({
|
||||
validationErrorModel: this.validationErrorModel,
|
||||
rootPath
|
||||
rootPath: rootPath,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -635,7 +679,7 @@ export class DmpDescriptionTemplateEditorModel implements DmpDescriptionTemplate
|
|||
|
||||
static createValidationContext(params: {
|
||||
rootPath?: string,
|
||||
validationErrorModel: ValidationErrorModel
|
||||
validationErrorModel: ValidationErrorModel,
|
||||
}): ValidationContext {
|
||||
const { rootPath = '', validationErrorModel } = params;
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormattingModule } from '@app/core/formatting.module';
|
||||
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
|
||||
import { RichTextEditorModule } from '@app/library/rich-text-editor/rich-text-editor.module';
|
||||
import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module';
|
||||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||
import { ConfirmationDialogModule } from '@common/modules/confirmation-dialog/confirmation-dialog.module';
|
||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||
import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
|
||||
import { DmpEditorComponent } from './dmp-editor.component';
|
||||
import { DmpEditorRoutingModule } from './dmp-editor.routing';
|
||||
import { AutoCompleteModule } from '@app/library/auto-complete/auto-complete.module';
|
||||
import { ReferenceFieldModule } from '@app/ui/reference/reference-field/reference-field.module';
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
|
||||
import { DmpFormProgressIndicationModule } from './form-progress-indication/dmp-form-progress-indication.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -22,7 +23,8 @@ import { DmpUserFieldModule } from '../dmp-user-field/dmp-user-field.module';
|
|||
AutoCompleteModule,
|
||||
ReferenceFieldModule,
|
||||
DragDropModule,
|
||||
DmpUserFieldModule
|
||||
DmpUserFieldModule,
|
||||
DmpFormProgressIndicationModule
|
||||
],
|
||||
declarations: [
|
||||
DmpEditorComponent,
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<div class="demo-progress-bar-container">
|
||||
<div class="percentage d-flex justify-content-center">{{progressSoFar}} {{'GENERAL.PREPOSITIONS.OF' | translate}} {{total}}</div>
|
||||
<mat-progress-bar class="form-progress-bar" [ngClass]="{'progress-bar': true}" mode="determinate" [value]="value"></mat-progress-bar>
|
||||
<div class="percentage" [ngStyle]="{'padding-left': value ? value - 10 + '%' : 0 + '%' }">{{value}}%</div>
|
||||
</div>
|
|
@ -0,0 +1,16 @@
|
|||
.percentage {
|
||||
color: #212121;
|
||||
opacity: 0.7;
|
||||
font-weight: 400;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
border-radius: 20px;
|
||||
height: 11px;
|
||||
|
||||
}
|
||||
|
||||
::ng-deep .mat-progress-bar .mat-progress-bar-fill::after {
|
||||
border-radius: 20px !important;
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
|
||||
import { AbstractControl, UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms';
|
||||
import { VisibilityRulesService } from '@app/ui/description/editor/description-form/visibility-rules/visibility-rules.service';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dmp-form-progress-indication',
|
||||
templateUrl: './dmp-form-progress-indication.component.html',
|
||||
styleUrls: ['./dmp-form-progress-indication.component.scss']
|
||||
})
|
||||
export class DmpFormProgressIndicationComponent extends BaseComponent implements OnInit, OnChanges {
|
||||
@Input() formGroup: UntypedFormGroup;
|
||||
|
||||
@Input() public progressValueAccuracy = 2;
|
||||
progressSoFar: number;
|
||||
total: number;
|
||||
percent: number;
|
||||
|
||||
constructor(private visibilityRulesService: VisibilityRulesService) { super(); }
|
||||
|
||||
public value = 0;
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes.formGroup) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
setTimeout(() => { this.calculateValueForProgressbar(); });
|
||||
this.formGroup
|
||||
.valueChanges
|
||||
.pipe(takeUntil(this._destroyed))
|
||||
.subscribe(control => {
|
||||
setTimeout(() => { this.calculateValueForProgressbar(); });
|
||||
});
|
||||
}
|
||||
|
||||
calculateValueForProgressbar() {
|
||||
this.progressSoFar = this.countFormControlsValidForProgress(this.formGroup);
|
||||
this.total = this.countFormControlsRequiredFieldsForTotal(this.formGroup);
|
||||
this.percent = (this.progressSoFar / this.total) * 100;
|
||||
this.value = Number.parseFloat(this.percent.toPrecision(this.progressValueAccuracy));
|
||||
}
|
||||
|
||||
countFormControlsValidForProgress(formControl: AbstractControl): number {
|
||||
let valueCurrent = 0;
|
||||
if (formControl instanceof UntypedFormControl) {
|
||||
if (this.controlRequired(formControl) && this.controlEnabled(formControl) && formControl.valid) {
|
||||
valueCurrent++;
|
||||
}
|
||||
} else if (formControl instanceof UntypedFormGroup) {
|
||||
Object.keys(formControl.controls).forEach(item => {
|
||||
const control = formControl.get(item);
|
||||
valueCurrent = valueCurrent + this.countFormControlsValidForProgress(control);
|
||||
});
|
||||
} else if (formControl instanceof UntypedFormArray) {
|
||||
formControl.controls.forEach(item => {
|
||||
valueCurrent = valueCurrent + this.countFormControlsValidForProgress(item);
|
||||
});
|
||||
}
|
||||
return valueCurrent;
|
||||
}
|
||||
|
||||
countFormControlsRequiredFieldsForTotal(formControl: AbstractControl, checkVisibility = false): number {
|
||||
let valueCurrent = 0;
|
||||
if (formControl instanceof UntypedFormControl) {
|
||||
if (this.controlRequired(formControl) && this.controlEnabled(formControl)) {
|
||||
valueCurrent++;
|
||||
}
|
||||
} else if (formControl instanceof UntypedFormGroup) {
|
||||
if (!checkVisibility || (!formControl.get('id')?.value || (this.visibilityRulesService.isVisibleMap[formControl.get('id').value] ?? true))) {
|
||||
Object.keys(formControl.controls).forEach(item => {
|
||||
const control = formControl.get(item);
|
||||
valueCurrent = valueCurrent + this.countFormControlsRequiredFieldsForTotal(control, checkVisibility);
|
||||
});
|
||||
}
|
||||
} else if (formControl instanceof UntypedFormArray) {
|
||||
formControl.controls.forEach(item => {
|
||||
valueCurrent = valueCurrent + this.countFormControlsRequiredFieldsForTotal(item, checkVisibility);
|
||||
});
|
||||
}
|
||||
return valueCurrent;
|
||||
}
|
||||
|
||||
controlRequired(formControl: AbstractControl) {
|
||||
if (formControl.validator) {
|
||||
const validator = formControl.validator({} as AbstractControl);
|
||||
if (validator && validator.required) {
|
||||
return true;
|
||||
}
|
||||
} else { return false }
|
||||
}
|
||||
|
||||
controlEnabled(formControl: AbstractControl) {
|
||||
if (formControl.enabled) {
|
||||
return true;
|
||||
} else { return false }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { CommonFormsModule } from '@common/forms/common-forms.module';
|
||||
import { CommonUiModule } from '@common/ui/common-ui.module';
|
||||
import { DmpFormProgressIndicationComponent } from './dmp-form-progress-indication.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonUiModule,
|
||||
CommonFormsModule
|
||||
],
|
||||
declarations: [
|
||||
DmpFormProgressIndicationComponent
|
||||
],
|
||||
exports: [
|
||||
DmpFormProgressIndicationComponent
|
||||
]
|
||||
})
|
||||
export class DmpFormProgressIndicationModule { }
|
|
@ -43,7 +43,7 @@
|
|||
</div>
|
||||
<div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.Internal">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'DMP-EDITOR.FIELDS.USER' | translate}}*</mat-label>
|
||||
<mat-label>{{'DMP-EDITOR.FIELDS.USER' | translate}}</mat-label>
|
||||
<app-single-auto-complete [formControl]="user.get('user')" [hidePlaceholder]="true" [configuration]="userService.singleAutoCompleteDmpAssociatedUserConfiguration"></app-single-auto-complete>
|
||||
<mat-error *ngIf="user.get('user').hasError('backendError')">{{user.get('user').getError('backendError').message}}</mat-error>
|
||||
<mat-error *ngIf="user.get('user').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||
|
@ -51,7 +51,7 @@
|
|||
</div>
|
||||
<div class="col-12 col-xl mt-3" *ngIf="user.get('userType').value == dmpUserTypeEnum.External">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'DMP-EDITOR.FIELDS.EMAIL' | translate}}*</mat-label>
|
||||
<mat-label>{{'DMP-EDITOR.FIELDS.EMAIL' | translate}}</mat-label>
|
||||
<input matInput type="text" name="email" [formControl]="user.get('email')">
|
||||
<mat-error *ngIf="user.get('email').hasError('backendError')">{{user.get('email').getError('backendError').message}}</mat-error>
|
||||
<mat-error *ngIf="user.get('email').hasError('required')">{{'GENERAL.VALIDATION.REQUIRED' | translate}}</mat-error>
|
||||
|
|
|
@ -57,10 +57,10 @@ export class DmpUserFieldComponent extends BaseComponent implements OnInit {
|
|||
removeUser(userIndex: number): void {
|
||||
(this.form.get('users') as FormArray).removeAt(userIndex);
|
||||
|
||||
DmpEditorModel.reApplyPropertiesValidators(
|
||||
DmpEditorModel.reApplyUsersValidators(
|
||||
{
|
||||
formGroup: this.form,
|
||||
validationErrorModel: this.validationErrorModel
|
||||
validationErrorModel: this.validationErrorModel,
|
||||
}
|
||||
);
|
||||
this.form.get('users').markAsDirty();
|
||||
|
@ -78,7 +78,7 @@ export class DmpUserFieldComponent extends BaseComponent implements OnInit {
|
|||
moveItemInArray(usersFormArray.controls, event.previousIndex, event.currentIndex);
|
||||
usersFormArray.updateValueAndValidity();
|
||||
|
||||
DmpEditorModel.reApplyPropertiesValidators(
|
||||
DmpEditorModel.reApplyUsersValidators(
|
||||
{
|
||||
formGroup: this.form,
|
||||
validationErrorModel: this.validationErrorModel
|
||||
|
|
|
@ -131,7 +131,8 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
|
|||
selector: '.dataset-tour',
|
||||
content: 'Step 2',
|
||||
orientation: Orientation.Right,
|
||||
isStepUnique: false
|
||||
isStepUnique: false,
|
||||
useHighlightPadding: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
@ -195,8 +195,8 @@
|
|||
<p class="mb-0 mr-0 pl-2 frame-txt" [matMenuTriggerFor]="exportMenu">{{ 'DMP-OVERVIEW.ACTIONS.EXPORT' | translate }}</p>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="canCreateNewVersion()" (click)="newVersionClicked()">
|
||||
<div class="col-12 d-flex align-items-center">
|
||||
<ng-container *ngIf="canCreateNewVersion()">
|
||||
<div class="col-12 d-flex align-items-center" (click)="newVersionClicked()">
|
||||
<button mat-mini-fab class="frame-btn">
|
||||
<mat-icon class="mat-mini-fab-icon">add_to_photos</mat-icon>
|
||||
</button>
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
.nav-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 10px 20px;
|
||||
text-align: left;
|
||||
|
@ -43,6 +44,19 @@
|
|||
font-family: 'Roboto',sans-serif;
|
||||
}
|
||||
|
||||
.nav-subrow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
letter-spacing: 0px;
|
||||
color: #000000;
|
||||
opacity: 1;
|
||||
font-size: 0.93rem;
|
||||
font-family: 'Roboto',sans-serif;
|
||||
}
|
||||
|
||||
.nav-row:hover {
|
||||
background-color: #ececec;
|
||||
border-radius: 6px;
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
<i *ngIf="groupMenuRoute.path == '/plans'" class="material-symbols-outlined icon-mask">person</i>
|
||||
<span [ngClass]="{'pl-0': groupMenuRoute.path == '/plans'}">{{groupMenuRoute.title | translate}}</span>
|
||||
</a>
|
||||
<a class="nav-link nav-row dataset-tour" *ngIf="groupMenuRoute.path === '/descriptions'" [routerLink]="[groupMenuRoute.path]">
|
||||
<span class="inner-line"></span>
|
||||
<a class="nav-link nav-row" *ngIf="groupMenuRoute.path === '/descriptions'" [routerLink]="[groupMenuRoute.path]">
|
||||
<span class="mb-2 inner-line"></span>
|
||||
<div class="pl-0 pt-1 pb-1 container-fluid nav-subrow dataset-tour">
|
||||
<i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i>
|
||||
<i class="material-symbols-outlined icon-mask">person</i>
|
||||
<span class="pl-0">{{groupMenuRoute.title | translate}}</span>
|
||||
</div>
|
||||
</a>
|
||||
<a class="nav-link nav-row" *ngIf="groupMenuRoute.path === '/co-branding'" href="/splash/resources/co-branding.html">
|
||||
<i class="material-symbols-outlined icon">{{ groupMenuRoute.icon }}</i>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { UntypedFormArray } from '@angular/forms';
|
||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||
import { NotificationServiceEnumUtils } from '@notification-service/core/formatting/enum-utils.service';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { ValidationErrorModel } from '@common/forms/validation/error-model/validation-error-model';
|
||||
import { NotificationFieldInfoEditorModel, NotificationFieldOptionsEditorModel } from '../notification-template-editor.model';
|
||||
|
@ -25,7 +25,7 @@ export class NotificationTemplateFieldOptionsComponent extends BaseComponent imp
|
|||
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
|
||||
|
||||
constructor(
|
||||
public enumUtils: EnumUtils,
|
||||
public enumUtils: NotificationServiceEnumUtils,
|
||||
) { super(); }
|
||||
|
||||
ngOnInit() {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class="row notification-template-editor">
|
||||
<div class="col-md-10 offset-md-1 colums-gapped">
|
||||
<div class="row align-items-center mt-4 mb-4" *ngIf="formGroup">
|
||||
<div class="col-auto">
|
||||
<div class="col-md col-12">
|
||||
<h3 *ngIf="isNew && !isClone">{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-EDITOR.NEW' | translate}}</h3>
|
||||
<app-navigation-breadcrumb />
|
||||
</div>
|
||||
|
|
|
@ -7,48 +7,69 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.NOTIFICATION-TYPE' | translate}}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.NOTIFICATION-TYPE' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.notificationTypes">
|
||||
<mat-option *ngFor="let type of notificationTypeEnumValues" [value]="type">{{enumUtils.toNotificationTypeString(type)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-label>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.KIND' | translate}}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.KIND' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.kinds">
|
||||
<mat-option *ngFor="let kind of notificationTemplateKindEnumValues" [value]="kind">{{enumUtils.toNotificationTemplateKindString(kind)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-label>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.CHANNEL' | translate}}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.CHANNEL' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.channels">
|
||||
<mat-option *ngFor="let channel of notificationTemplateChannelEnumValues" [value]="channel">{{enumUtils.toNotificationTemplateChannelString(channel)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-label>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-TEMPLATE-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -22,4 +22,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,66 +7,85 @@
|
|||
|
||||
|
||||
<mat-menu #filterMenu>
|
||||
<div class="p-3" (click)="$event?.stopPropagation?.()">
|
||||
<div class="search-listing-filters-container">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="container-fluid" (click)="$event?.stopPropagation?.()">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto mt-2">
|
||||
<h4>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.TITLE' | translate}}</h4>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button color="accent" mat-button (click)="clearFilters()">
|
||||
{{'COMMONS.LISTING-COMPONENT.CLEAR-ALL-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<section class="w-100">
|
||||
<mat-slide-toggle labelPosition="before" [(ngModel)]="internalFilters.isActive">
|
||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.IS-ACTIVE' | translate}}
|
||||
</mat-slide-toggle>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<div>
|
||||
<mat-form-field class="col-12">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.USERS' | translate}}</mat-label>
|
||||
<app-multiple-auto-complete [(ngModel)]="internalFilters.userIds" [hidePlaceholder]="true" [separatorKeysCodes]="separatorKeysCodes" [configuration]="userAutoCompleteConfiguration">
|
||||
</app-multiple-auto-complete>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-form-field class="col-12">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.NOTIFICATION-TYPE' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.type">
|
||||
<mat-option *ngFor="let type of notificationTypeEnumValues" [value]="type">{{enumUtils.toNotificationTypeString(type)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-form-field class="col-12">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.CONTACT-TYPE' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.contactType">
|
||||
<mat-option *ngFor="let contactType of notificationContactTypeEnumValues" [value]="contactType">{{enumUtils.toNotificationContactTypeString(contactType)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-form-field class="col-12">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.NOTIFY-STATE' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.notifyState">
|
||||
<mat-option *ngFor="let notifyState of notificationNotifyStateEnumValues" [value]="notifyState">{{enumUtils.toNotificationNotifyStateString(notifyState)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-form-field class="col-12">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.TRACKING-STATE' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.trackingState">
|
||||
<mat-option *ngFor="let trackingState of notificationTrackingStateEnumValues" [value]="trackingState">{{enumUtils.toNotificationTrackingStateString(trackingState)}}</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-form-field class="col-12">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<mat-form-field class="w-100">
|
||||
<mat-label>{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.TRACKING-PROCESS' | translate}}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="internalFilters.trackingProcess">
|
||||
<mat-option *ngFor="let trackingProcess of notificationTrackingProcessEnumValues" [value]="trackingProcess">{{enumUtils.toNotificationTrackingProcessString(trackingProcess)}}</mat-option>
|
||||
|
@ -75,11 +94,14 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4 gap-1-rem">
|
||||
<button mat-stroked-button color="primary" (click)="filterMenuTrigger?.closeMenu()">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-sm" (click)="filterMenuTrigger.closeMenu(); applyFilters();">
|
||||
{{'NOTIFICATION-SERVICE.NOTIFICATION-LISTING.FILTER.APPLY-FILTERS' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
// ::ng-deep.mat-mdc-menu-panel {
|
||||
// max-width: 100% !important;
|
||||
// height: 100% !important;
|
||||
// }
|
||||
::ng-deep.mat-mdc-menu-panel {
|
||||
max-width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
// :host::ng-deep.mat-mdc-menu-content:not(:empty) {
|
||||
// padding-top: 0 !important;
|
||||
// }
|
||||
:host::ng-deep.mat-mdc-menu-content:not(:empty) {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
.filter-button{
|
||||
|
@ -18,4 +18,8 @@
|
|||
// }
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .mdc-form-field {
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes';
|
|||
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
||||
import { NotificationFilter } from '@notification-service/core/query/notification.lookup';
|
||||
import { UserService } from '@app/core/services/user/user.service';
|
||||
import { EnumUtils } from '@app/core/services/utilities/enum-utils.service';
|
||||
import { NotificationServiceEnumUtils } from '@notification-service/core/formatting/enum-utils.service';
|
||||
import { MultipleAutoCompleteConfiguration } from '@app/library/auto-complete/multiple/multiple-auto-complete-configuration';
|
||||
import { BaseComponent } from '@common/base/base.component';
|
||||
import { Guid } from '@common/types/guid';
|
||||
|
@ -37,7 +37,7 @@ export class NotificationListingFiltersComponent extends BaseComponent implement
|
|||
|
||||
protected appliedFilterCount: number = 0;
|
||||
constructor(
|
||||
public enumUtils: EnumUtils,
|
||||
public enumUtils: NotificationServiceEnumUtils,
|
||||
private userService: UserService,
|
||||
) { super(); }
|
||||
|
||||
|
|
|
@ -99,9 +99,9 @@ export class InAppNotificationEditorComponent extends BaseComponent implements O
|
|||
maxWidth: '300px',
|
||||
restoreFocus: false,
|
||||
data: {
|
||||
message: this.language.instant('COMMONS.CONFIRMATION-DIALOG.DELETE-ITEM'),
|
||||
confirmButton: this.language.instant('COMMONS.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
||||
cancelButton: this.language.instant('COMMONS.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
||||
message: this.language.instant('GENERAL.CONFIRMATION-DIALOG.DELETE-ITEM'),
|
||||
confirmButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CONFIRM'),
|
||||
cancelButton: this.language.instant('GENERAL.CONFIRMATION-DIALOG.ACTIONS.CANCEL')
|
||||
}
|
||||
});
|
||||
dialogRef.afterClosed().pipe(takeUntil(this._destroyed)).subscribe(result => {
|
||||
|
|
|
@ -15,14 +15,12 @@
|
|||
<mat-divider inset *ngIf="!last"></mat-divider>
|
||||
</div>
|
||||
</a>
|
||||
<mat-list-item *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)">
|
||||
<a (click)="goToNotifications()">{{'NAV-BAR.INAPP-NOTIFICATIONS'
|
||||
| translate}}</a>
|
||||
</mat-list-item>
|
||||
<mat-list-item *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)">
|
||||
<a (click)="readAllNotifications()">{{'NAV-BAR.READ-ALL-INAPP-NOTIFICATIONS'
|
||||
| translate}}</a>
|
||||
</mat-list-item>
|
||||
<a *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)" (click)="goToNotifications()">
|
||||
<mat-list-item>{{'NAV-BAR.INAPP-NOTIFICATIONS' | translate}}</mat-list-item>
|
||||
</a>
|
||||
<a *ngIf="authService.hasPermission(authService.permissionEnum.ViewMineInAppNotificationPage)" (click)="readAllNotifications()">
|
||||
<mat-list-item>{{'NAV-BAR.READ-ALL-INAPP-NOTIFICATIONS' | translate}}</mat-list-item>
|
||||
</a>
|
||||
</mat-nav-list>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-end align-items-center mt-4 gap-1-rem">
|
||||
<div class="row justify-content-end align-items-center mt-4 mb-1 gap-1-rem">
|
||||
<div class="col-auto">
|
||||
<button class="normal-btn-light-sm" (click)="filterMenuTrigger?.closeMenu()">
|
||||
{{'NOTIFICATION-SERVICE.INAPP-NOTIFICATION-LISTING.FILTER.CANCEL' | translate}}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<div class="row">
|
||||
<div class="col"></div>
|
||||
<div class="col-auto">
|
||||
<button mat-raised-button color="primary" class="rounded-button" (click)="formSubmit()">
|
||||
<button class="normal-btn-sm" (click)="formSubmit()">
|
||||
{{'NOTIFICATION-SERVICE.USER-PROFILE.NOTIFIER-LIST-EDITOR.ACTIONS.SAVE' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -2,6 +2,7 @@ package gr.cite.notification.web.controllers;
|
|||
|
||||
import gr.cite.notification.audit.AuditableAction;
|
||||
import gr.cite.notification.authorization.AuthorizationFlags;
|
||||
import gr.cite.notification.common.enums.IsActive;
|
||||
import gr.cite.notification.common.enums.TenantConfigurationType;
|
||||
import gr.cite.notification.common.types.tenantconfiguration.NotifierListConfigurationDataContainer;
|
||||
import gr.cite.notification.data.UserNotificationPreferenceEntity;
|
||||
|
@ -18,6 +19,7 @@ import gr.cite.notification.web.model.QueryResult;
|
|||
import gr.cite.tools.auditing.AuditService;
|
||||
import gr.cite.tools.data.builder.BuilderFactory;
|
||||
import gr.cite.tools.data.censor.CensorFactory;
|
||||
import gr.cite.tools.data.query.Ordering;
|
||||
import gr.cite.tools.data.query.QueryFactory;
|
||||
import gr.cite.tools.exception.MyApplicationException;
|
||||
import gr.cite.tools.exception.MyForbiddenException;
|
||||
|
@ -80,15 +82,16 @@ public class UserNotificationPreferenceController {
|
|||
|
||||
@GetMapping("user/{userId}/current")
|
||||
@Transactional
|
||||
public UserNotificationPreference current(@PathVariable UUID userId, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
||||
public List<UserNotificationPreference> current(@PathVariable UUID userId, FieldSet fieldSet, Locale locale) throws MyApplicationException, MyForbiddenException, MyNotFoundException {
|
||||
logger.debug(new MapLogEntry("retrieving" + UserNotificationPreference.class.getSimpleName()).And("userId", userId).And("fields", fieldSet));
|
||||
|
||||
this.censorFactory.censor(UserNotificationPreferenceCensor.class).censor(fieldSet, userId);
|
||||
|
||||
UserNotificationPreferenceQuery query = this.queryFactory.query(UserNotificationPreferenceQuery.class).userId(userId);
|
||||
UserNotificationPreference model = this.builderFactory.builder(UserNotificationPreferenceBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(fieldSet, query.firstAs(fieldSet));
|
||||
if (model == null)
|
||||
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{userId, TenantConfiguration.class.getSimpleName()}, LocaleContextHolder.getLocale()));
|
||||
Ordering ordering = new Ordering();
|
||||
ordering.addAscending(UserNotificationPreference._ordinal);
|
||||
UserNotificationPreferenceQuery query = this.queryFactory.query(UserNotificationPreferenceQuery.class).userId(userId).isActives(IsActive.Active);
|
||||
query.setOrder(ordering);
|
||||
List<UserNotificationPreference> model = this.builderFactory.builder(UserNotificationPreferenceBuilder.class).authorize(AuthorizationFlags.OwnerOrPermission).build(fieldSet, query.collectAs(fieldSet));
|
||||
|
||||
this.auditService.track(AuditableAction.User_Notification_Preference_Lookup, Map.ofEntries(
|
||||
new AbstractMap.SimpleEntry<String, Object>("userId", userId),
|
||||
|
|
|
@ -3,7 +3,7 @@ notification:
|
|||
fields:
|
||||
- key: "{installation-url}"
|
||||
type: "String"
|
||||
value: "http://localhost:42000"
|
||||
value: "http://localhost:4200"
|
||||
resolver:
|
||||
global-policies:
|
||||
- #dmpInvitationExternalUser
|
||||
|
@ -14,16 +14,16 @@ notification:
|
|||
contacts: [ inapp, email ]
|
||||
- #dpmModified
|
||||
type: 4542262A-22F8-4BAA-9DB6-1C8E70AC1DBB
|
||||
contacts: [ email ]
|
||||
contacts: [ inapp, email ]
|
||||
- #dmpFinalised
|
||||
type: 90DB0B46-42DE-BD89-AEBF-6F27EFEB256E
|
||||
contacts: [ email ]
|
||||
contacts: [ inapp, email ]
|
||||
- #descriptionModified
|
||||
type: 4FDBFA80-7A71-4A69-B854-67CBB70648F1
|
||||
contacts: [ email ]
|
||||
contacts: [ inapp, email ]
|
||||
- #descriptionFinalised
|
||||
type: 33790bad-94d4-488a-8ee2-7f6295ca18ea
|
||||
contacts: [ email ]
|
||||
contacts: [ inapp, email ]
|
||||
- #mergeAcountConfirmation
|
||||
type: BFE68845-CB05-4C5A-A03D-29161A7C9660
|
||||
contacts: [ email ]
|
||||
|
@ -32,10 +32,10 @@ notification:
|
|||
contacts: [ email ]
|
||||
- #dmpDeposit
|
||||
type: 55736F7A-83AB-4190-AF43-9D031A6F9612
|
||||
contacts: [ email ]
|
||||
contacts: [ inapp, email ]
|
||||
- #descriptionTemplateInvitation
|
||||
type: 223BB607-EFA1-4CE7-99EC-4BEABFEF9A8B
|
||||
contacts: [ email ]
|
||||
contacts: [ inapp, email ]
|
||||
- #contactSupportType
|
||||
type: 5B1D6C52-88F9-418B-9B8A-6F1F963D9EAD
|
||||
contacts: [ email ]
|
||||
|
|
|
@ -2,7 +2,7 @@ notification:
|
|||
task:
|
||||
processor:
|
||||
enable: true
|
||||
interval-seconds: 3
|
||||
interval-seconds: 5
|
||||
options:
|
||||
retry-threshold: 300
|
||||
max-retry-delay-seconds: 10800
|
||||
|
|
|
@ -27,7 +27,7 @@ queue:
|
|||
exchange: null
|
||||
rabbitmq:
|
||||
enable: false
|
||||
interval-seconds: 30
|
||||
interval-seconds: 5
|
||||
options:
|
||||
retry-threashold: 100
|
||||
retry-delay-step-seconds: 300
|
||||
|
@ -45,7 +45,7 @@ queue:
|
|||
user-touched-topic: user.touch
|
||||
rabbitmq:
|
||||
enable: false
|
||||
interval-seconds: 30
|
||||
interval-seconds: 5
|
||||
options:
|
||||
retry-threashold: 100
|
||||
retry-delay-step-seconds: 300
|
||||
|
|
|
@ -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} just finalised the Description {name}.</p>
|
||||
<a href="{installation-url}/descriptions/edit/{id}" target="_blank">Click here to view it.</a>
|
||||
</body>
|
||||
</html>
|
|
@ -3,302 +3,10 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Simple Transactional Email</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%; }
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%; }
|
||||
table {
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
width: 100%; }
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top; }
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
.body {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%; }
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
Margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px; }
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
Margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px; }
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%; }
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px; }
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.footer {
|
||||
clear: both;
|
||||
Margin-top: 10px;
|
||||
text-align: center;
|
||||
width: 100%; }
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
text-align: center; }
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #000000;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
Margin-bottom: 30px; }
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize; }
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
Margin-bottom: 15px; }
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px; }
|
||||
a {
|
||||
color: #3498db;
|
||||
text-decoration: underline; }
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%; }
|
||||
.btn > tbody > tr > td {
|
||||
padding-bottom: 15px; }
|
||||
.btn table {
|
||||
width: auto; }
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center; }
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #3498db;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #3498db;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize; }
|
||||
.btn-primary table td {
|
||||
background-color: #3498db; }
|
||||
.btn-primary a {
|
||||
background-color: #3498db;
|
||||
border-color: #3498db;
|
||||
color: #ffffff; }
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0; }
|
||||
.first {
|
||||
margin-top: 0; }
|
||||
.align-center {
|
||||
text-align: center; }
|
||||
.align-right {
|
||||
text-align: right; }
|
||||
.align-left {
|
||||
text-align: left; }
|
||||
.clear {
|
||||
clear: both; }
|
||||
.mt0 {
|
||||
margin-top: 0; }
|
||||
.mb0 {
|
||||
margin-bottom: 0; }
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
mso-hide: all;
|
||||
visibility: hidden;
|
||||
width: 0; }
|
||||
.powered-by a {
|
||||
text-decoration: none; }
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0; }
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important; }
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important; }
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important; }
|
||||
table[class=body] .content {
|
||||
padding: 0 !important; }
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important; }
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important; }
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important; }
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important; }
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important; }}
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%; }
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%; }
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important; }
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important; }
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
||||
<table class="main">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p>Dear {recipient},</p>
|
||||
<p>{reasonName} just made changes to 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}/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> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<a href="{installation-url}/descriptions/edit/{id}" target="_blank">Click here to view it.</a>
|
||||
</body>
|
||||
</html>
|
|
@ -3,303 +3,11 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Simple Transactional Email</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%; }
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%; }
|
||||
table {
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
width: 100%; }
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top; }
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
.body {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%; }
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
Margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px; }
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
Margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px; }
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%; }
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px; }
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.footer {
|
||||
clear: both;
|
||||
Margin-top: 10px;
|
||||
text-align: center;
|
||||
width: 100%; }
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
text-align: center; }
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #000000;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
Margin-bottom: 30px; }
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize; }
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
Margin-bottom: 15px; }
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px; }
|
||||
a {
|
||||
color: #3498db;
|
||||
text-decoration: underline; }
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%; }
|
||||
.btn > tbody > tr > td {
|
||||
padding-bottom: 15px; }
|
||||
.btn table {
|
||||
width: auto; }
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center; }
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #3498db;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #3498db;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize; }
|
||||
.btn-primary table td {
|
||||
background-color: #3498db; }
|
||||
.btn-primary a {
|
||||
background-color: #3498db;
|
||||
border-color: #3498db;
|
||||
color: #ffffff; }
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0; }
|
||||
.first {
|
||||
margin-top: 0; }
|
||||
.align-center {
|
||||
text-align: center; }
|
||||
.align-right {
|
||||
text-align: right; }
|
||||
.align-left {
|
||||
text-align: left; }
|
||||
.clear {
|
||||
clear: both; }
|
||||
.mt0 {
|
||||
margin-top: 0; }
|
||||
.mb0 {
|
||||
margin-bottom: 0; }
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
mso-hide: all;
|
||||
visibility: hidden;
|
||||
width: 0; }
|
||||
.powered-by a {
|
||||
text-decoration: none; }
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0; }
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important; }
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important; }
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important; }
|
||||
table[class=body] .content {
|
||||
padding: 0 !important; }
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important; }
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important; }
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important; }
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important; }
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important; }}
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%; }
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%; }
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important; }
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important; }
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
||||
<table class="main">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p>Dear {recipient},</p>
|
||||
<p>You have been invited to co-develop the Template {templateName}.</p>
|
||||
<p>Click the button to redirect to {templateName}.</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}/description-templates/{templateID}" target="_blank">{templateName}</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- END MAIN CONTENT AREA -->
|
||||
</table>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
<div class="footer">
|
||||
|
||||
</div>
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<a href="{installation-url}/description-templates/{templateID}" target="_blank">{templateName}</a>
|
||||
</body>
|
||||
</html>
|
|
@ -1,304 +0,0 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Simple Transactional Email</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%; }
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%; }
|
||||
table {
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
width: 100%; }
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top; }
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
.body {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%; }
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
Margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px; }
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
Margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px; }
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%; }
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px; }
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.footer {
|
||||
clear: both;
|
||||
Margin-top: 10px;
|
||||
text-align: center;
|
||||
width: 100%; }
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
text-align: center; }
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #000000;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
Margin-bottom: 30px; }
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize; }
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
Margin-bottom: 15px; }
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px; }
|
||||
a {
|
||||
color: #3498db;
|
||||
text-decoration: underline; }
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%; }
|
||||
.btn > tbody > tr > td {
|
||||
padding-bottom: 15px; }
|
||||
.btn table {
|
||||
width: auto; }
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center; }
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #3498db;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #3498db;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize; }
|
||||
.btn-primary table td {
|
||||
background-color: #3498db; }
|
||||
.btn-primary a {
|
||||
background-color: #3498db;
|
||||
border-color: #3498db;
|
||||
color: #ffffff; }
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0; }
|
||||
.first {
|
||||
margin-top: 0; }
|
||||
.align-center {
|
||||
text-align: center; }
|
||||
.align-right {
|
||||
text-align: right; }
|
||||
.align-left {
|
||||
text-align: left; }
|
||||
.clear {
|
||||
clear: both; }
|
||||
.mt0 {
|
||||
margin-top: 0; }
|
||||
.mb0 {
|
||||
margin-bottom: 0; }
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
mso-hide: all;
|
||||
visibility: hidden;
|
||||
width: 0; }
|
||||
.powered-by a {
|
||||
text-decoration: none; }
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0; }
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important; }
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important; }
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important; }
|
||||
table[class=body] .content {
|
||||
padding: 0 !important; }
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important; }
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important; }
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important; }
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important; }
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important; }}
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%; }
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%; }
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important; }
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important; }
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
||||
<table class="main">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p>Dear {recipient},</p>
|
||||
<p>{reasonName} just finalised 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}/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> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
|
@ -3,302 +3,10 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Simple Transactional Email</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%; }
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%; }
|
||||
table {
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
width: 100%; }
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top; }
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
.body {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%; }
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
Margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px; }
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
Margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px; }
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%; }
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px; }
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.footer {
|
||||
clear: both;
|
||||
Margin-top: 10px;
|
||||
text-align: center;
|
||||
width: 100%; }
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
text-align: center; }
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #000000;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
Margin-bottom: 30px; }
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize; }
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
Margin-bottom: 15px; }
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px; }
|
||||
a {
|
||||
color: #3498db;
|
||||
text-decoration: underline; }
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%; }
|
||||
.btn > tbody > tr > td {
|
||||
padding-bottom: 15px; }
|
||||
.btn table {
|
||||
width: auto; }
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center; }
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #3498db;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #3498db;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize; }
|
||||
.btn-primary table td {
|
||||
background-color: #3498db; }
|
||||
.btn-primary a {
|
||||
background-color: #3498db;
|
||||
border-color: #3498db;
|
||||
color: #ffffff; }
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0; }
|
||||
.first {
|
||||
margin-top: 0; }
|
||||
.align-center {
|
||||
text-align: center; }
|
||||
.align-right {
|
||||
text-align: right; }
|
||||
.align-left {
|
||||
text-align: left; }
|
||||
.clear {
|
||||
clear: both; }
|
||||
.mt0 {
|
||||
margin-top: 0; }
|
||||
.mb0 {
|
||||
margin-bottom: 0; }
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
mso-hide: all;
|
||||
visibility: hidden;
|
||||
width: 0; }
|
||||
.powered-by a {
|
||||
text-decoration: none; }
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0; }
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important; }
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important; }
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important; }
|
||||
table[class=body] .content {
|
||||
padding: 0 !important; }
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important; }
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important; }
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important; }
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important; }
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important; }}
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%; }
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%; }
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important; }
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important; }
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
||||
<table class="main">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p>Dear {recipient},</p>
|
||||
<p>{reasonName} just publish the {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}/{path}/{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> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<a href="{installation-url}/{path}/{id}" target="_blank">Click here to view it.</a>
|
||||
</body>
|
||||
</html>
|
|
@ -3,302 +3,10 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Simple Transactional Email</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%; }
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%; }
|
||||
table {
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
width: 100%; }
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top; }
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
.body {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%; }
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
Margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px; }
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
Margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px; }
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%; }
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px; }
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.footer {
|
||||
clear: both;
|
||||
Margin-top: 10px;
|
||||
text-align: center;
|
||||
width: 100%; }
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
text-align: center; }
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #000000;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
Margin-bottom: 30px; }
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize; }
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
Margin-bottom: 15px; }
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px; }
|
||||
a {
|
||||
color: #3498db;
|
||||
text-decoration: underline; }
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%; }
|
||||
.btn > tbody > tr > td {
|
||||
padding-bottom: 15px; }
|
||||
.btn table {
|
||||
width: auto; }
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center; }
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #3498db;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #3498db;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize; }
|
||||
.btn-primary table td {
|
||||
background-color: #3498db; }
|
||||
.btn-primary a {
|
||||
background-color: #3498db;
|
||||
border-color: #3498db;
|
||||
color: #ffffff; }
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0; }
|
||||
.first {
|
||||
margin-top: 0; }
|
||||
.align-center {
|
||||
text-align: center; }
|
||||
.align-right {
|
||||
text-align: right; }
|
||||
.align-left {
|
||||
text-align: left; }
|
||||
.clear {
|
||||
clear: both; }
|
||||
.mt0 {
|
||||
margin-top: 0; }
|
||||
.mb0 {
|
||||
margin-bottom: 0; }
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
mso-hide: all;
|
||||
visibility: hidden;
|
||||
width: 0; }
|
||||
.powered-by a {
|
||||
text-decoration: none; }
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0; }
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important; }
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important; }
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important; }
|
||||
table[class=body] .content {
|
||||
padding: 0 !important; }
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important; }
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important; }
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important; }
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important; }
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important; }}
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%; }
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%; }
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important; }
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important; }
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
||||
<table class="main">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p>Dear {recipient},</p>
|
||||
<p>{reasonName} just finalised the Dmp {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}/plans/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> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<a href="{installation-url}/plans/edit/{id}" target="_blank">Click here to view it.</a>
|
||||
</body>
|
||||
</html>
|
|
@ -4,301 +4,11 @@
|
|||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Simple Transactional Email</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%; }
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%; }
|
||||
table {
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
width: 100%; }
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top; }
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
.body {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%; }
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
Margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px; }
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
Margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px; }
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%; }
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px; }
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.footer {
|
||||
clear: both;
|
||||
Margin-top: 10px;
|
||||
text-align: center;
|
||||
width: 100%; }
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
text-align: center; }
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #000000;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
Margin-bottom: 30px; }
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize; }
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
Margin-bottom: 15px; }
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px; }
|
||||
a {
|
||||
color: #3498db;
|
||||
text-decoration: underline; }
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%; }
|
||||
.btn > tbody > tr > td {
|
||||
padding-bottom: 15px; }
|
||||
.btn table {
|
||||
width: auto; }
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center; }
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #3498db;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #3498db;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize; }
|
||||
.btn-primary table td {
|
||||
background-color: #3498db; }
|
||||
.btn-primary a {
|
||||
background-color: #3498db;
|
||||
border-color: #3498db;
|
||||
color: #ffffff; }
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0; }
|
||||
.first {
|
||||
margin-top: 0; }
|
||||
.align-center {
|
||||
text-align: center; }
|
||||
.align-right {
|
||||
text-align: right; }
|
||||
.align-left {
|
||||
text-align: left; }
|
||||
.clear {
|
||||
clear: both; }
|
||||
.mt0 {
|
||||
margin-top: 0; }
|
||||
.mb0 {
|
||||
margin-bottom: 0; }
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
mso-hide: all;
|
||||
visibility: hidden;
|
||||
width: 0; }
|
||||
.powered-by a {
|
||||
text-decoration: none; }
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0; }
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important; }
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important; }
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important; }
|
||||
table[class=body] .content {
|
||||
padding: 0 !important; }
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important; }
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important; }
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important; }
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important; }
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important; }}
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%; }
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%; }
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important; }
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important; }
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
||||
<table class="main">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p>Dear {recipient},</p>
|
||||
<p>{reasonName} just add you to collaborate to Data Management plan {dmpname} with role {dmprole}.</p>
|
||||
<p>Click the button to redirect to {dmpname}.</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}/plans/edit/{id}" target="_blank">Join</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- END MAIN CONTENT AREA -->
|
||||
</table>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
<div class="footer">
|
||||
|
||||
</div>
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<a href="{installation-url}/plans/edit/{id}" target="_blank">Join</a>
|
||||
</body>
|
||||
</html>
|
|
@ -3,302 +3,11 @@
|
|||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Simple Transactional Email</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%; }
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%; }
|
||||
table {
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
width: 100%; }
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top; }
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
.body {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%; }
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
Margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px; }
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
Margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px; }
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%; }
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px; }
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
.footer {
|
||||
clear: both;
|
||||
Margin-top: 10px;
|
||||
text-align: center;
|
||||
width: 100%; }
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
text-align: center; }
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #000000;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
Margin-bottom: 30px; }
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize; }
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
Margin-bottom: 15px; }
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px; }
|
||||
a {
|
||||
color: #3498db;
|
||||
text-decoration: underline; }
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%; }
|
||||
.btn > tbody > tr > td {
|
||||
padding-bottom: 15px; }
|
||||
.btn table {
|
||||
width: auto; }
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center; }
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #3498db;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #3498db;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize; }
|
||||
.btn-primary table td {
|
||||
background-color: #3498db; }
|
||||
.btn-primary a {
|
||||
background-color: #3498db;
|
||||
border-color: #3498db;
|
||||
color: #ffffff; }
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0; }
|
||||
.first {
|
||||
margin-top: 0; }
|
||||
.align-center {
|
||||
text-align: center; }
|
||||
.align-right {
|
||||
text-align: right; }
|
||||
.align-left {
|
||||
text-align: left; }
|
||||
.clear {
|
||||
clear: both; }
|
||||
.mt0 {
|
||||
margin-top: 0; }
|
||||
.mb0 {
|
||||
margin-bottom: 0; }
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
mso-hide: all;
|
||||
visibility: hidden;
|
||||
width: 0; }
|
||||
.powered-by a {
|
||||
text-decoration: none; }
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0; }
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important; }
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important; }
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important; }
|
||||
table[class=body] .content {
|
||||
padding: 0 !important; }
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important; }
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important; }
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important; }
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important; }
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important; }}
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%; }
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%; }
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important; }
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important; }
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important; } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
|
||||
<!-- START CENTERED WHITE CONTAINER -->
|
||||
<span class="preheader">This is preheader text. Some clients will show this text as a preview.</span>
|
||||
<table class="main">
|
||||
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p>Dear {recipient},</p>
|
||||
<p>{reasonName} just made changes to the Dmp {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}/plans/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> </td>
|
||||
</tr>
|
||||
<a href="{installation-url}/plans/edit/{id}" target="_blank">Click here to view it.</a>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -8,6 +8,7 @@ import gr.cite.tools.data.query.FieldResolver;
|
|||
import gr.cite.tools.data.query.QueryBase;
|
||||
import gr.cite.tools.data.query.QueryContext;
|
||||
import jakarta.persistence.Tuple;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
@ -89,15 +90,20 @@ public class UserNotificationPreferenceQuery extends QueryBase<UserNotificationP
|
|||
}
|
||||
|
||||
if (this.isActives != null) {
|
||||
predicates.add(queryContext.Root.get(UserNotificationPreferenceEntity._isActive).in(isActives));
|
||||
}
|
||||
CriteriaBuilder.In<IsActive> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserNotificationPreferenceEntity._isActive));
|
||||
for (IsActive item : this.isActives)
|
||||
inClause.value(item);
|
||||
predicates.add(inClause); }
|
||||
|
||||
if (this.type != null) {
|
||||
predicates.add(queryContext.Root.get(UserNotificationPreferenceEntity._type).in(this.type));
|
||||
}
|
||||
|
||||
if (this.channel != null) {
|
||||
predicates.add(queryContext.Root.get(UserNotificationPreferenceEntity._channel).in(this.channel));
|
||||
CriteriaBuilder.In<NotificationContactType> inClause = queryContext.CriteriaBuilder.in(queryContext.Root.get(UserNotificationPreferenceEntity._channel));
|
||||
for (NotificationContactType item : this.channel)
|
||||
inClause.value(item);
|
||||
predicates.add(inClause);
|
||||
}
|
||||
|
||||
if (!predicates.isEmpty()) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package gr.cite.notification.service.inappnotification;
|
||||
|
||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||
import gr.cite.notification.authorization.OwnedResource;
|
||||
import gr.cite.notification.authorization.Permission;
|
||||
import gr.cite.notification.common.enums.NotificationInAppTracking;
|
||||
import gr.cite.notification.common.scope.user.UserScope;
|
||||
|
@ -92,7 +93,7 @@ public class InAppNotificationServiceImpl implements InAppNotificationService {
|
|||
|
||||
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
|
||||
logger.debug("deleting in-app notification: {}", id);
|
||||
this.authService.authorizeForce(Permission.DeleteInAppNotification);
|
||||
this.authService.authorizeAtLeastOneForce(this.userScope.getUserId() != null ? List.of(new OwnedResource(this.userScope.getUserId())) : null, Permission.DeleteInAppNotification);
|
||||
this.deleterFactory.deleter(InAppNotificationDeleter.class).deleteAndSaveByIds(List.of(id));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ public class InAppMessageBuilder extends MessageBuilderBase implements MessageBu
|
|||
}
|
||||
|
||||
NotificationProperties.Flow options = this.flowMap.get("in-app").getOrDefault(notification.getType(), null);
|
||||
NotificationTemplate template = notificationTemplateService.lookupOverriddenTemplates(notification.getType(), NotificationTemplateChannel.Email, messageInfo.getLanguage());
|
||||
NotificationTemplate template = notificationTemplateService.lookupOverriddenTemplates(notification.getType(), NotificationTemplateChannel.InApp, messageInfo.getLanguage());
|
||||
|
||||
if (options == null && template == null) {
|
||||
logger.error("Could not retrieve flow options for notification " + notification.getId() + " of type " + notification.getType());
|
||||
|
|
|
@ -165,7 +165,6 @@ public abstract class MessageBuilderBase {
|
|||
return formatting;
|
||||
}
|
||||
|
||||
//TODO: Here check with a language accepted list and fallback to default
|
||||
protected String lookupOrReadLocalizedFile(NotificationProperties.Template.TemplateCache templateCache, String path, String language) {
|
||||
String filename = path.replace("{language}", language);
|
||||
File file = null;
|
||||
|
|
|
@ -10,13 +10,16 @@ import gr.cite.notification.convention.ConventionService;
|
|||
import gr.cite.notification.data.TenantEntity;
|
||||
import gr.cite.notification.data.TenantEntityManager;
|
||||
import gr.cite.notification.data.NotificationEntity;
|
||||
import gr.cite.notification.data.UserNotificationPreferenceEntity;
|
||||
import gr.cite.notification.errorcode.ErrorThesaurusProperties;
|
||||
import gr.cite.notification.model.SendNotificationResult;
|
||||
import gr.cite.notification.model.UserNotificationPreference;
|
||||
import gr.cite.notification.model.builder.NotificationBuilder;
|
||||
import gr.cite.notification.model.Notification;
|
||||
import gr.cite.notification.model.deleter.NotificationDeleter;
|
||||
import gr.cite.notification.model.persist.NotificationPersist;
|
||||
import gr.cite.notification.query.NotificationQuery;
|
||||
import gr.cite.notification.query.UserNotificationPreferenceQuery;
|
||||
import gr.cite.notification.service.channelResolution.ChannelResolutionService;
|
||||
import gr.cite.notification.service.contact.extractor.ContactExtractorFactory;
|
||||
import gr.cite.notification.service.contact.model.Contact;
|
||||
|
@ -25,6 +28,7 @@ import gr.cite.notification.service.message.model.Message;
|
|||
import gr.cite.notification.service.notify.NotifierFactory;
|
||||
import gr.cite.tools.data.builder.BuilderFactory;
|
||||
import gr.cite.tools.data.deleter.DeleterFactory;
|
||||
import gr.cite.tools.data.query.Ordering;
|
||||
import gr.cite.tools.data.query.QueryFactory;
|
||||
import gr.cite.tools.exception.MyApplicationException;
|
||||
import gr.cite.tools.exception.MyForbiddenException;
|
||||
|
@ -49,6 +53,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@RequestScope
|
||||
|
@ -141,7 +146,9 @@ public class NotificationServiceImpl implements NotificationService {
|
|||
}
|
||||
|
||||
public SendNotificationResult doNotify(NotificationEntity notification) {
|
||||
List<NotificationContactType> contactTypes = this.orderContactTypes(notification);
|
||||
List<NotificationContactType> contactTypes = this.orderContactTypesFromPreferences(notification);
|
||||
if (this.conventionService.isListNullOrEmpty(contactTypes)) contactTypes = this.orderContactTypes(notification);
|
||||
|
||||
for (NotificationContactType contactType: contactTypes) {
|
||||
SendNotificationResult result = this.sendNotification(notification, contactType);
|
||||
if (result.getSuccess()) return result;
|
||||
|
@ -149,6 +156,17 @@ public class NotificationServiceImpl implements NotificationService {
|
|||
return null;
|
||||
}
|
||||
|
||||
private List<NotificationContactType> orderContactTypesFromPreferences(NotificationEntity notification) {
|
||||
Ordering ordering = new Ordering();
|
||||
ordering.addAscending(UserNotificationPreference._ordinal);
|
||||
UserNotificationPreferenceQuery query = this.queryFactory.query(UserNotificationPreferenceQuery.class).userId(notification.getUserId()).type(notification.getType()).isActives(IsActive.Active);
|
||||
query.setOrder(ordering);
|
||||
|
||||
List<UserNotificationPreferenceEntity> preferences = query.collectAs(new BaseFieldSet().ensure(UserNotificationPreference._channel));
|
||||
if (!this.conventionService.isListNullOrEmpty(preferences)) return preferences.stream().map(x -> x.getChannel()).collect(Collectors.toList());
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<NotificationContactType> orderContactTypes(NotificationEntity notification) {
|
||||
List<NotificationContactType> contactTypes = this.channelResolutionService.resolve(notification.getType(), notification.getUserId());
|
||||
if (notification.getContactTypeHint() == null) return contactTypes;
|
||||
|
|
|
@ -141,8 +141,8 @@ public class NotificationSchedulingServiceImpl implements NotificationScheduling
|
|||
} else {
|
||||
tenantScope.setTempTenant(entityManager, null, tenantScope.getDefaultTenantCode());
|
||||
}
|
||||
notification = entityManager.merge(notification);
|
||||
entityManager.persist(notification);
|
||||
// notification = entityManager.merge(notification);
|
||||
entityManager.merge(notification);
|
||||
entityManager.flush();
|
||||
} finally {
|
||||
tenantScope.removeTempTenant(entityManager);
|
||||
|
|
|
@ -70,6 +70,7 @@ public class InAppNotifier implements Notify{
|
|||
inApp.setTenantId(tenantScope.getTenant());
|
||||
|
||||
entityManager.persist(inApp);
|
||||
entityManager.flush();
|
||||
|
||||
InAppTrackingData trackingData = new InAppTrackingData(inApp.getId());
|
||||
data = this.jsonHandlingService.toJsonSafe(trackingData);
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package gr.cite.notification.service.userNotificationPreference;
|
||||
|
||||
import gr.cite.commons.web.authz.service.AuthorizationService;
|
||||
import gr.cite.notification.authorization.OwnedResource;
|
||||
import gr.cite.notification.authorization.Permission;
|
||||
import gr.cite.notification.common.enums.IsActive;
|
||||
import gr.cite.notification.common.enums.NotificationContactType;
|
||||
import gr.cite.notification.common.types.tenantconfiguration.NotifierListConfigurationDataContainer;
|
||||
import gr.cite.notification.config.notification.NotificationConfig;
|
||||
|
@ -66,7 +68,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
|||
public List<UserNotificationPreference> persist(UserNotificationPreferencePersist model, FieldSet fieldSet) {
|
||||
logger.debug(new MapLogEntry("persisting").And("model", model).And("fields", fieldSet));
|
||||
|
||||
this.authService.authorizeForce(Permission.EditUserNotificationPreference);
|
||||
this.authService.authorizeAtLeastOneForce(model.getUserId() != null ? List.of(new OwnedResource(model.getUserId())) : null, Permission.EditUserNotificationPreference);
|
||||
|
||||
Map<UUID, List<NotificationContactType>> currentNotificationListPolicies;
|
||||
NotifierListConfigurationDataContainer tenantNotifierListPolicies = this.tenantConfigurationService.collectTenantNotifierList();
|
||||
|
@ -169,6 +171,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
|||
preferences = this.queryFactory
|
||||
.query(UserNotificationPreferenceQuery.class)
|
||||
.type(type)
|
||||
.isActives(IsActive.Active)
|
||||
.userId(userId).collect();
|
||||
int ordinal = 0;
|
||||
|
||||
|
@ -176,6 +179,7 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
|||
for (NotificationContactType contactType : contactTypes) {
|
||||
UserNotificationPreferenceEntity preference = preferences.stream().filter(x -> x.getChannel() == contactType).findFirst().orElse(null);
|
||||
|
||||
boolean isUpdate = preference != null;
|
||||
if (preference != null) {
|
||||
preference.setOrdinal(ordinal);
|
||||
|
||||
|
@ -186,9 +190,12 @@ public class UserNotificationPreferenceServiceImpl implements UserNotificationPr
|
|||
preference.setOrdinal(ordinal);
|
||||
preference.setChannel(contactType);
|
||||
preference.setCreatedAt(Instant.now());
|
||||
preference.setIsActive(IsActive.Active);
|
||||
}
|
||||
this.entityManager.merge(preference);
|
||||
this.entityManager.persist(preference);
|
||||
preference.setUpdatedAt(Instant.now());
|
||||
|
||||
if(isUpdate) this.entityManager.merge(preference);
|
||||
else this.entityManager.persist(preference);
|
||||
updatedPreferences.add(preference);
|
||||
ordinal++;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue