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

This commit is contained in:
Sofia Papacharalampous 2024-03-14 14:03:16 +02:00
commit 025933ee61
28 changed files with 212 additions and 113 deletions

View File

@ -33,7 +33,7 @@ public class DmpBlueprintValueCensor extends BaseCensor {
if (fields == null || fields.isEmpty()) if (fields == null || fields.isEmpty())
return; return;
this.authService.authorizeForce(Permission.BrowseDmp); this.authService.authorizeForce(Permission.BrowseDmp, Permission.DeferredAffiliation);
} }
} }

View File

@ -3,10 +3,8 @@ package eu.eudat.model.censorship.dmpproperties;
import eu.eudat.authorization.Permission; import eu.eudat.authorization.Permission;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.model.censorship.BaseCensor; import eu.eudat.model.censorship.BaseCensor;
import eu.eudat.model.censorship.DmpCensor;
import eu.eudat.model.censorship.UserCensor; import eu.eudat.model.censorship.UserCensor;
import eu.eudat.model.dmpproperties.DmpContact; import eu.eudat.model.dmpproperties.DmpContact;
import eu.eudat.model.dmpproperties.DmpProperties;
import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.censor.CensorFactory; import gr.cite.tools.data.censor.CensorFactory;
import gr.cite.tools.fieldset.FieldSet; import gr.cite.tools.fieldset.FieldSet;
@ -40,7 +38,7 @@ public class DmpContactCensor extends BaseCensor {
if (fields == null || fields.isEmpty()) if (fields == null || fields.isEmpty())
return; return;
this.authService.authorizeForce(Permission.BrowseDmp); this.authService.authorizeForce(Permission.BrowseDmp, Permission.DeferredAffiliation);
FieldSet userFields = fields.extractPrefixed(this.asIndexerPrefix(DmpContact._user)); FieldSet userFields = fields.extractPrefixed(this.asIndexerPrefix(DmpContact._user));
this.censorFactory.censor(UserCensor.class).censor(userFields, userId); this.censorFactory.censor(UserCensor.class).censor(userFields, userId);
} }

View File

@ -38,7 +38,7 @@ public class DmpPropertiesCensor extends BaseCensor {
if (fields == null || fields.isEmpty()) if (fields == null || fields.isEmpty())
return; return;
this.authService.authorizeForce(Permission.BrowseDmp); this.authService.authorizeForce(Permission.BrowseDmp, Permission.DeferredAffiliation);
FieldSet dmpBlueprintValuesFields = fields.extractPrefixed(this.asIndexerPrefix(DmpProperties._dmpBlueprintValues)); FieldSet dmpBlueprintValuesFields = fields.extractPrefixed(this.asIndexerPrefix(DmpProperties._dmpBlueprintValues));
this.censorFactory.censor(DmpBlueprintValueCensor.class).censor(dmpBlueprintValuesFields, userId); this.censorFactory.censor(DmpBlueprintValueCensor.class).censor(dmpBlueprintValuesFields, userId);
FieldSet contactsFields = fields.extractPrefixed(this.asIndexerPrefix(DmpProperties._contacts)); FieldSet contactsFields = fields.extractPrefixed(this.asIndexerPrefix(DmpProperties._contacts));

View File

@ -36,7 +36,7 @@ public class DmpReferenceDataCensor extends BaseCensor {
if (fields == null || fields.isEmpty()) if (fields == null || fields.isEmpty())
return; return;
this.authService.authorizeForce(Permission.BrowseDmpDescriptionTemplate); this.authService.authorizeForce(Permission.BrowseDmpDescriptionTemplate, Permission.DeferredAffiliation);
} }
} }

View File

@ -17,6 +17,7 @@ import gr.cite.tools.data.query.QueryContext;
import jakarta.persistence.Tuple; import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Subquery;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -189,7 +190,7 @@ public class EntityDoiQuery extends QueryBase<EntityDoiEntity> {
List<Predicate> predicates = new ArrayList<>(); List<Predicate> predicates = new ArrayList<>();
boolean usePublic = this.authorize.contains(AuthorizationFlags.Public); boolean usePublic = this.authorize.contains(AuthorizationFlags.Public);
if (userId != null || usePublic) { if (userId != null || usePublic) {
predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(EntityDoiEntity._entityId)).value( queryContext.CriteriaBuilder.in(queryContext.Root.get(DmpUserEntity._dmpId)).value(queryUtilsService.buildDmpAuthZSubQuery(queryContext.Query, queryContext.CriteriaBuilder, userId, usePublic)))); predicates.add(queryContext.CriteriaBuilder.in(queryContext.Root.get(EntityDoiEntity._entityId)).value(queryUtilsService.buildDmpAuthZSubQuery(queryContext.Query, queryContext.CriteriaBuilder, userId, usePublic)));
} }
if (!predicates.isEmpty()) { if (!predicates.isEmpty()) {
Predicate[] predicatesArray = predicates.toArray(new Predicate[0]); Predicate[] predicatesArray = predicates.toArray(new Predicate[0]);

View File

@ -150,7 +150,7 @@ public class UserQuery extends QueryBase<UserEntity> {
if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseUser)) return null; if (this.authorize.contains(AuthorizationFlags.Permission) && this.authService.authorize(Permission.BrowseUser)) return null;
UUID userId; UUID userId;
if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe(); if (this.authorize.contains(AuthorizationFlags.Owner)) userId = this.userScope.getUserIdSafe();
if (this.authorize.contains(AuthorizationFlags.Public)) userId = this.userScope.getUserIdSafe(); if (this.authorize.contains(AuthorizationFlags.DmpAssociated)) userId = this.userScope.getUserIdSafe();
else userId = null; else userId = null;
List<Predicate> predicates = new ArrayList<>(); List<Predicate> predicates = new ArrayList<>();

View File

@ -1,8 +1,5 @@
package eu.eudat.service.dmp; package eu.eudat.service.dmp;
import com.fasterxml.jackson.core.JsonProcessingException;
import eu.eudat.data.DmpEntity;
import eu.eudat.data.DmpUserEntity;
import eu.eudat.model.Dmp; import eu.eudat.model.Dmp;
import eu.eudat.model.DmpUser; import eu.eudat.model.DmpUser;
import eu.eudat.model.persist.*; import eu.eudat.model.persist.*;
@ -36,7 +33,7 @@ public interface DmpService {
ResponseEntity<byte[]> export(UUID id, String transformerId, String exportType) throws InvalidApplicationException, IOException; ResponseEntity<byte[]> export(UUID id, String transformerId, String exportType) throws InvalidApplicationException, IOException;
void inviteUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException; void inviteUserOrAssignUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException;
void dmpInvitationAccept(String token) throws InvalidApplicationException, IOException; void dmpInvitationAccept(String token) throws InvalidApplicationException, IOException;

View File

@ -191,6 +191,12 @@ public class DmpServiceImpl implements DmpService {
this.patchAndSaveTemplates(data.getId(), model.getDescriptionTemplates()); this.patchAndSaveTemplates(data.getId(), model.getDescriptionTemplates());
if (!isUpdate || userScope.isSet()) {
this.addOwner(data);
if (model.getUsers() == null) model.setUsers(new ArrayList<>());
if (model.getUsers().stream().noneMatch(x-> x.getUser() != null && x.getUser().equals(this.userScope.getUserIdSafe()) && DmpUserRole.Owner.equals(x.getRole()))) model.getUsers().add(this.createOwnerPersist());
}
this.eventBroker.emit(new DmpTouchedEvent(data.getId())); this.eventBroker.emit(new DmpTouchedEvent(data.getId()));
this.dmpTouchedIntegrationEventHandler.handle(DmpTouchedIntegrationEventHandler.buildEventFromPersistModel(model)); this.dmpTouchedIntegrationEventHandler.handle(DmpTouchedIntegrationEventHandler.buildEventFromPersistModel(model));
@ -200,10 +206,7 @@ public class DmpServiceImpl implements DmpService {
this.sendNotification(data); this.sendNotification(data);
if (!this.conventionService.isListNullOrEmpty(model.getUsers())){ if (!this.conventionService.isListNullOrEmpty(model.getUsers())){
this.inviteUsers(data.getId(), model.getUsers()); this.inviteUserOrAssignUsers(data.getId(), model.getUsers());
}else{
this.addOwner(data);
this.assignUsers(data.getId(), new ArrayList<>(), null);
} }
this.elasticService.persistDmp(data); this.elasticService.persistDmp(data);
@ -211,6 +214,13 @@ public class DmpServiceImpl implements DmpService {
return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data); return this.builderFactory.builder(DmpBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Dmp._id, Dmp._hash), data);
} }
private DmpUserPersist createOwnerPersist() {
DmpUserPersist persist = new DmpUserPersist();
persist.setRole(DmpUserRole.Owner);
persist.setUser(userScope.getUserIdSafe());
return persist;
}
private void addOwner(DmpEntity dmpEntity) throws InvalidApplicationException { private void addOwner(DmpEntity dmpEntity) throws InvalidApplicationException {
DmpUserEntity data = new DmpUserEntity(); DmpUserEntity data = new DmpUserEntity();
data.setId(UUID.randomUUID()); data.setId(UUID.randomUUID());
@ -286,14 +296,14 @@ public class DmpServiceImpl implements DmpService {
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException { public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException, IOException {
logger.debug("deleting dmp: {}", id); logger.debug("deleting dmp: {}", id);
this.authorizationService.authorizeForce(Permission.DeleteDmp); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.DeleteDmp);
this.deleterFactory.deleter(DmpDeleter.class).deleteAndSaveByIds(List.of(id), false); this.deleterFactory.deleter(DmpDeleter.class).deleteAndSaveByIds(List.of(id), false);
} }
@Override @Override
public Dmp createNewVersion(NewVersionDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, IOException, TransformerException { public Dmp createNewVersion(NewVersionDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, JAXBException, ParserConfigurationException, IOException, TransformerException {
this.authorizationService.authorizeForce(Permission.CreateNewVersionDmp); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation( model.getId())), Permission.CreateNewVersionDmp);
DmpEntity oldDmpEntity = this.entityManager.find(DmpEntity.class, model.getId()); DmpEntity oldDmpEntity = this.entityManager.find(DmpEntity.class, model.getId());
if (oldDmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (oldDmpEntity == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
@ -395,7 +405,7 @@ public class DmpServiceImpl implements DmpService {
@Override @Override
public Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException { public Dmp buildClone(CloneDmpPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, IOException, InvalidApplicationException {
this.authorizationService.authorizeForce(Permission.CloneDmp); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation( model.getId())), Permission.CloneDmp);
DmpEntity existingDmpEntity = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(model.getId()).firstAs(fields); DmpEntity existingDmpEntity = this.queryFactory.query(DmpQuery.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).ids(model.getId()).firstAs(fields);
if (!this.conventionService.isValidGuid(model.getId()) || existingDmpEntity == null) if (!this.conventionService.isValidGuid(model.getId()) || existingDmpEntity == null)
@ -527,7 +537,7 @@ public class DmpServiceImpl implements DmpService {
@Override @Override
public Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException { public Dmp removeUser(DmpUserRemovePersist model, FieldSet fields) throws InvalidApplicationException, IOException {
this.authorizationService.authorizeForce(Permission.AssignDmpUsers); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getDmpId())), Permission.AssignDmpUsers);
DmpEntity data = this.entityManager.find(DmpEntity.class, model.getDmpId()); DmpEntity data = this.entityManager.find(DmpEntity.class, model.getDmpId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
@ -566,7 +576,7 @@ public class DmpServiceImpl implements DmpService {
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale())); if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Dmp.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage()); if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash())) throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
if (model.getStatus() != null && model.getStatus() == DmpStatus.Finalized && data.getStatus() != DmpStatus.Finalized) { if (model.getStatus() != null && model.getStatus() == DmpStatus.Finalized && data.getStatus() != DmpStatus.Finalized) {
this.authorizationService.authorizeForce(Permission.FinalizeDmp); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getId())), Permission.FinalizeDmp);
data.setStatus(model.getStatus()); data.setStatus(model.getStatus());
data.setFinalizedAt(Instant.now()); data.setFinalizedAt(Instant.now());
} }
@ -807,9 +817,8 @@ public class DmpServiceImpl implements DmpService {
} }
// invites // invites
public void inviteUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException { public void inviteUserOrAssignUsers(UUID id, List<DmpUserPersist> users) throws InvalidApplicationException, JAXBException, IOException {
this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.InviteDmpUsers);
this.authorizationService.authorizeForce(Permission.InviteDmpUsers);
DmpEntity dmp = this.queryFactory.query(DmpQuery.class).ids(id).first(); DmpEntity dmp = this.queryFactory.query(DmpQuery.class).ids(id).first();
if (dmp == null){ if (dmp == null){

View File

@ -2,6 +2,7 @@ package eu.eudat.service.lock;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission; import eu.eudat.authorization.Permission;
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import eu.eudat.commons.scope.user.UserScope; import eu.eudat.commons.scope.user.UserScope;
import eu.eudat.convention.ConventionService; import eu.eudat.convention.ConventionService;
import eu.eudat.data.LockEntity; import eu.eudat.data.LockEntity;
@ -51,6 +52,7 @@ public class LockServiceImpl implements LockService {
private final ConventionService conventionService; private final ConventionService conventionService;
private final MessageSource messageSource; private final MessageSource messageSource;
private final ErrorThesaurusProperties errors; private final ErrorThesaurusProperties errors;
private final AuthorizationContentResolver authorizationContentResolver;
@Autowired @Autowired
public LockServiceImpl( public LockServiceImpl(
@ -62,7 +64,7 @@ public class LockServiceImpl implements LockService {
QueryFactory queryFactory, QueryFactory queryFactory,
ConventionService conventionService, ConventionService conventionService,
MessageSource messageSource, MessageSource messageSource,
ErrorThesaurusProperties errors) { ErrorThesaurusProperties errors, AuthorizationContentResolver authorizationContentResolver) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.userScope = userScope; this.userScope = userScope;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
@ -72,12 +74,13 @@ public class LockServiceImpl implements LockService {
this.conventionService = conventionService; this.conventionService = conventionService;
this.messageSource = messageSource; this.messageSource = messageSource;
this.errors = errors; this.errors = errors;
this.authorizationContentResolver = authorizationContentResolver;
} }
public Lock persist(LockPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException { public Lock persist(LockPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException {
logger.debug(new MapLogEntry("persisting data").And("model", model).And("fields", fields)); logger.debug(new MapLogEntry("persisting data").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditLock); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(model.getTarget())), Permission.EditLock);
Boolean isUpdate = this.conventionService.isValidGuid(model.getId()); Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
@ -176,7 +179,7 @@ public class LockServiceImpl implements LockService {
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException { public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting : {}", id); logger.debug("deleting : {}", id);
this.authorizationService.authorizeForce(Permission.DeleteLock); this.authorizationService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(id)), Permission.DeleteLock);
this.deleterFactory.deleter(LockDeleter.class).deleteAndSaveByIds(List.of(id)); this.deleterFactory.deleter(LockDeleter.class).deleteAndSaveByIds(List.of(id));
} }

View File

@ -256,7 +256,7 @@ public class DmpController {
public boolean inviteUsers(@PathVariable("id") UUID id, @RequestBody DmpUserInvitePersist model) throws InvalidApplicationException, JAXBException, IOException { public boolean inviteUsers(@PathVariable("id") UUID id, @RequestBody DmpUserInvitePersist model) throws InvalidApplicationException, JAXBException, IOException {
logger.debug(new MapLogEntry("inviting users to dmp").And("model", model)); logger.debug(new MapLogEntry("inviting users to dmp").And("model", model));
this.dmpService.inviteUsers(id, model.getUsers()); this.dmpService.inviteUserOrAssignUsers(id, model.getUsers());
this.auditService.track(AuditableAction.Dmp_Invite_Users, Map.ofEntries( this.auditService.track(AuditableAction.Dmp_Invite_Users, Map.ofEntries(
new AbstractMap.SimpleEntry<String, Object>("model", model) new AbstractMap.SimpleEntry<String, Object>("model", model)

View File

@ -3,6 +3,7 @@ package eu.eudat.controllers;
import eu.eudat.audit.AuditableAction; import eu.eudat.audit.AuditableAction;
import eu.eudat.authorization.AuthorizationFlags; import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.Permission; import eu.eudat.authorization.Permission;
import eu.eudat.authorization.authorizationcontentresolver.AuthorizationContentResolver;
import gr.cite.tools.validation.ValidationFilterAnnotation; import gr.cite.tools.validation.ValidationFilterAnnotation;
import eu.eudat.data.LockEntity; import eu.eudat.data.LockEntity;
import eu.eudat.model.Lock; import eu.eudat.model.Lock;
@ -62,14 +63,14 @@ public class LockController {
private final MessageSource messageSource; private final MessageSource messageSource;
private final AuthorizationService authService; private final AuthorizationService authService;
private final AuthorizationContentResolver authorizationContentResolver;
@Autowired @Autowired
public LockController(BuilderFactory builderFactory, public LockController(BuilderFactory builderFactory,
AuditService auditService, AuditService auditService,
LockService lockService, LockService lockService,
CensorFactory censorFactory, CensorFactory censorFactory,
QueryFactory queryFactory, QueryFactory queryFactory,
MessageSource messageSource, AuthorizationService authService) { MessageSource messageSource, AuthorizationService authService, AuthorizationContentResolver authorizationContentResolver) {
this.builderFactory = builderFactory; this.builderFactory = builderFactory;
this.auditService = auditService; this.auditService = auditService;
this.lockService = lockService; this.lockService = lockService;
@ -77,6 +78,7 @@ public class LockController {
this.queryFactory = queryFactory; this.queryFactory = queryFactory;
this.messageSource = messageSource; this.messageSource = messageSource;
this.authService = authService; this.authService = authService;
this.authorizationContentResolver = authorizationContentResolver;
} }
@PostMapping("query") @PostMapping("query")
@ -154,7 +156,7 @@ public class LockController {
@GetMapping("target/status/{id}") @GetMapping("target/status/{id}")
public Boolean getLocked(@PathVariable("id") UUID targetId) throws Exception { public Boolean getLocked(@PathVariable("id") UUID targetId) throws Exception {
logger.debug(new MapLogEntry("is locked" + Lock.class.getSimpleName()).And("targetId", targetId)); logger.debug(new MapLogEntry("is locked" + Lock.class.getSimpleName()).And("targetId", targetId));
this.authService.authorizeForce(Permission.BrowseLock); this.authService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(targetId)), Permission.BrowseLock);
Boolean isLocked = this.lockService.isLocked(targetId); Boolean isLocked = this.lockService.isLocked(targetId);
this.auditService.track(AuditableAction.Lock_IsLocked, Map.ofEntries( this.auditService.track(AuditableAction.Lock_IsLocked, Map.ofEntries(
@ -167,7 +169,7 @@ public class LockController {
@DeleteMapping("target/unlock/{id}") @DeleteMapping("target/unlock/{id}")
public boolean unlock(@PathVariable("id") UUID targetId) throws Exception { public boolean unlock(@PathVariable("id") UUID targetId) throws Exception {
logger.debug(new MapLogEntry("unlock" + Lock.class.getSimpleName()).And("targetId", targetId)); logger.debug(new MapLogEntry("unlock" + Lock.class.getSimpleName()).And("targetId", targetId));
this.authService.authorizeForce(Permission.BrowseLock); this.authService.authorizeAtLeastOneForce(List.of(this.authorizationContentResolver.dmpAffiliation(targetId)), Permission.BrowseLock);
this.lockService.unlock(targetId); this.lockService.unlock(targetId);
this.auditService.track(AuditableAction.Lock_UnLocked, Map.ofEntries( this.auditService.track(AuditableAction.Lock_UnLocked, Map.ofEntries(

View File

@ -342,6 +342,12 @@ permissions:
DeleteDmp: DeleteDmp:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -349,6 +355,12 @@ permissions:
CloneDmp: CloneDmp:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -356,6 +368,12 @@ permissions:
CreateNewVersionDmp: CreateNewVersionDmp:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -363,6 +381,12 @@ permissions:
ExportDmp: ExportDmp:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -370,6 +394,12 @@ permissions:
FinalizeDmp: FinalizeDmp:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -390,6 +420,12 @@ permissions:
InviteDmpUsers: InviteDmpUsers:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
@ -709,18 +745,36 @@ permissions:
BrowseLock: BrowseLock:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
EditLock: EditLock:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false
allowAuthenticated: false allowAuthenticated: false
DeleteLock: DeleteLock:
roles: roles:
- Admin - Admin
dmp:
roles:
- Owner
- User
- DescriptionContributor
- Reviewer
claims: [ ] claims: [ ]
clients: [ ] clients: [ ]
allowAnonymous: false allowAnonymous: false

View File

@ -10,7 +10,7 @@ import { catchError } from "rxjs/operators";
@Injectable() @Injectable()
export class AnnotationService { export class AnnotationService {
private get apiBase(): string { return `${this.installationConfiguration.annotationServiceAddress}annotation`; } private get apiBase(): string { return `${this.installationConfiguration.annotationServiceAddress}api/annotation`; }
constructor( constructor(
private installationConfiguration: ConfigurationService, private installationConfiguration: ConfigurationService,

View File

@ -12,6 +12,7 @@ import { ReferencePersist } from '../reference/reference';
import { DmpAssociatedUser, User } from "../user/user"; import { DmpAssociatedUser, User } from "../user/user";
import { DmpReference } from './dmp-reference'; import { DmpReference } from './dmp-reference';
import { IsActive } from '@app/core/common/enum/is-active.enum'; import { IsActive } from '@app/core/common/enum/is-active.enum';
import { AppPermission } from '@app/core/common/enum/permission.enum';
export interface Dmp extends BaseEntity { export interface Dmp extends BaseEntity {
label?: string; label?: string;
@ -33,6 +34,7 @@ export interface Dmp extends BaseEntity {
descriptions?: Description[]; descriptions?: Description[];
dmpDescriptionTemplates?: DmpDescriptionTemplate[]; dmpDescriptionTemplates?: DmpDescriptionTemplate[];
entityDois?: EntityDoi[]; entityDois?: EntityDoi[];
authorizationFlags?: AppPermission[];
} }
export interface DmpProperties { export interface DmpProperties {

View File

@ -16,7 +16,7 @@ export class InAppNotificationService {
constructor(private http: BaseHttpV2Service, private configurationService: ConfigurationService) { constructor(private http: BaseHttpV2Service, private configurationService: ConfigurationService) {
} }
private get apiBase(): string { return `${this.configurationService.notificationServiceAddress}inapp-notification`; } private get apiBase(): string { return `${this.configurationService.notificationServiceAddress}api/inapp-notification`; }
query(q: InAppNotificationLookup): Observable<QueryResult<InAppNotification>> { query(q: InAppNotificationLookup): Observable<QueryResult<InAppNotification>> {

View File

@ -15,7 +15,7 @@ export class NotificationTemplateService {
constructor(private http: BaseHttpV2Service, private configurationService: ConfigurationService) { constructor(private http: BaseHttpV2Service, private configurationService: ConfigurationService) {
} }
private get apiBase(): string { return `${this.configurationService.notificationServiceAddress}notification-template`; } private get apiBase(): string { return `${this.configurationService.notificationServiceAddress}api/notification-template`; }
query(q: NotificationTemplateLookup): Observable<QueryResult<NotificationTemplate>> { query(q: NotificationTemplateLookup): Observable<QueryResult<NotificationTemplate>> {
const url = `${this.apiBase}/query`; const url = `${this.apiBase}/query`;

View File

@ -14,7 +14,7 @@ export class NotificationService {
constructor(private http: BaseHttpV2Service, private configurationService: ConfigurationService) { constructor(private http: BaseHttpV2Service, private configurationService: ConfigurationService) {
} }
private get apiBase(): string { return `${this.configurationService.notificationServiceAddress}notification`; } private get apiBase(): string { return `${this.configurationService.notificationServiceAddress}api/notification`; }
query(q: NotificationLookup): Observable<QueryResult<Notification>> { query(q: NotificationLookup): Observable<QueryResult<Notification>> {
const url = `${this.apiBase}/query`; const url = `${this.apiBase}/query`;

View File

@ -30,7 +30,7 @@
</div> </div>
</form> </form>
</div> </div>
<div class="col-12 mb-3 pt-2 gr-color" *ngIf="threads?.length > 0">{{'ANNOTATION-DIALOG.TITLE' | translate}}</div> <div class="col-12 mb-3 pt-2 gr-color" *ngIf="threads?.size > 0">{{'ANNOTATION-DIALOG.TITLE' | translate}}</div>
<div class="col-12"> <div class="col-12">
<div *ngFor="let thread of threads" class="row"> <div *ngFor="let thread of threads" class="row">
<!-- Parent Thread --> <!-- Parent Thread -->
@ -43,12 +43,12 @@
<div class="row reply-content"> <div class="row reply-content">
<div class="col-12"> <div class="col-12">
<div class="row h-100"> <div class="row h-100">
<div class="col annotation-time">{{thread.timeStamp | date:'EEEE, MMMM d, y, h:mm a'}}</div> <div class="col-10 annotation-time">{{getParentAnnotation(thread).timeStamp | date:'EEEE, MMMM d, y, h:mm a'}}</div>
<div class="col-auto" *ngIf="thread.protectionType === annotationProtectionTypeEnum.Private" matTooltip="{{'ANNOTATION-DIALOG.PROTECTION.PRIVATE' | translate}}"><i class="material-icons protection-icon icon-{{getAnnotationProtectionType(thread)}}"></i>{{getAnnotationProtectionType(thread)}}</div> <div class="col-auto" *ngIf="getParentAnnotation(thread).protectionType === annotationProtectionTypeEnum.Private" matTooltip="{{'ANNOTATION-DIALOG.PROTECTION.PRIVATE' | translate}}"><i class="material-icons protection-icon icon-{{getAnnotationProtectionType(thread)}}"></i>{{getAnnotationProtectionType(thread)}}</div>
<div class="col-auto" *ngIf="thread.protectionType === annotationProtectionTypeEnum.EntityAccessors" matTooltip="{{'ANNOTATION-DIALOG.PROTECTION.ENTITY-ACCESSORS' | translate}}"><i class="material-icons protection-icon icon-{{getAnnotationProtectionType(thread)}}"></i>{{getAnnotationProtectionType(thread)}}</div> <div class="col-auto" *ngIf="getParentAnnotation(thread).protectionType === annotationProtectionTypeEnum.EntityAccessors" matTooltip="{{'ANNOTATION-DIALOG.PROTECTION.ENTITY-ACCESSORS' | translate}}"><i class="material-icons protection-icon icon-{{getAnnotationProtectionType(thread)}}"></i>{{getAnnotationProtectionType(thread)}}</div>
<div class="col-md-12 annotation-full-text">{{thread.payload}}</div> <div class="col-md-12 annotation-full-text">{{getParentAnnotation(thread).payload}}</div>
<div class="col-md-12"> <div class="col-md-12">
<em class="user">{{'ANNOTATION-DIALOG.THREADS.FROM-USER' | translate}} {{thread.author.name}}</em> <em class="user">{{'ANNOTATION-DIALOG.THREADS.FROM-USER' | translate}} {{getParentAnnotation(thread).author.name}}</em>
</div> </div>
</div> </div>
</div> </div>
@ -59,10 +59,10 @@
<!-- Previous Replies --> <!-- Previous Replies -->
<div class="col-12"> <div class="col-12">
<div *ngFor="let annotation of annotationsPerThread[thread.threadId]; let i = index;" class="row replies"> <div *ngFor="let annotation of annotationsPerThread[thread]; let i = index;" class="row replies">
<div class="col-auto pr-0"> <div class="col-auto pr-0">
<div class="col reply child"></div> <div class="col reply child"></div>
<div class="col reply child dummy-for-next-child" *ngIf="i != annotationsPerThread[thread.threadId].length - 1"></div> <div class="col reply child dummy-for-next-child" *ngIf="i != annotationsPerThread[thread].length - 1"></div>
</div> </div>
<div class="col pl-0 pt-1"> <div class="col pl-0 pt-1">
<div class="parent row m-0"> <div class="parent row m-0">
@ -72,7 +72,7 @@
<div class="col reply-content"> <div class="col reply-content">
<div class="row h-100"> <div class="row h-100">
<div class="col-md-12 annotation-time">{{annotation.timeStamp | date:'EEEE, MMMM d, y, h:mm a'}}</div> <div class="col-md-12 annotation-time">{{annotation.timeStamp | date:'EEEE, MMMM d, y, h:mm a'}}</div>
<div class="col-md-12 annotation-full-text">{{annotation.text}}</div> <div class="col-md-12 annotation-full-text">{{annotation.payload}}</div>
<div class="col-md-12"> <div class="col-md-12">
<em class="gr-color">{{'ANNOTATION-DIALOG.THREADS.FROM-USER' | translate}}</em> <em class="gr-color">{{'ANNOTATION-DIALOG.THREADS.FROM-USER' | translate}}</em>
{{annotation.author.name}} {{annotation.author.name}}
@ -89,11 +89,11 @@
<div class="col-12"> <div class="col-12">
<div class="row new-reply mr-0"> <div class="row new-reply mr-0">
<mat-form-field class="col pt-2 pb-2 pr-0"> <mat-form-field class="col pt-2 pb-2 pr-0">
<textarea matInput matTextareaAutosize matAutosizeMinRows="1" [formControl]="this.threadReplyTextsFG[thread.id.toString()].get('replyText')" placeholder="{{'ANNOTATION-DIALOG.THREADS.REPLY' | translate}}"></textarea> <textarea matInput matTextareaAutosize matAutosizeMinRows="1" [formControl]="this.threadReplyTextsFG[thread.toString()].get('replyText')" placeholder="{{'ANNOTATION-DIALOG.THREADS.REPLY' | translate}}"></textarea>
<mat-error *ngIf="this.threadReplyTextsFG[thread.id.toString()]?.get('replyText')?.hasError('required')">{{'COMMONS.VALIDATION.REQUIRED' | translate}}</mat-error> <mat-error *ngIf="this.threadReplyTextsFG[thread.toString()]?.get('replyText')?.hasError('required')">{{'COMMONS.VALIDATION.REQUIRED' | translate}}</mat-error>
</mat-form-field> </mat-form-field>
<div class="col-auto send-msg"> <div class="col-auto send-msg">
<button class="form-field-margin" mat-icon-button type="button" color="accent" (click)="replyThread(thread.threadId, thread.protectionType)" matTooltip="{{'ANNOTATION-DIALOG.THREADS.REPLY' | translate}}"> <button class="form-field-margin" mat-icon-button type="button" color="accent" (click)="replyThread(thread)" matTooltip="{{'ANNOTATION-DIALOG.THREADS.REPLY' | translate}}">
<i class="fa fa-paper-plane"></i> <i class="fa fa-paper-plane"></i>
</button> </button>
</div> </div>

View File

@ -30,10 +30,11 @@ export class AnnotationDialogComponent extends BaseComponent {
private entityId: Guid; private entityId: Guid;
private anchor: string; private anchor: string;
private entityType: string; private entityType: string;
// public annotations: Array<Annotation> = [];
public threads = new Array<Annotation>(); public comments = new Array<Annotation>();
public threads = new Set<Guid>();
public annotationsPerThread = {}; public annotationsPerThread = {};
public parentAnnotationsPerThread = {};
threadReplyTextsFG: Array<UntypedFormGroup>; threadReplyTextsFG: Array<UntypedFormGroup>;
threadFormGroup: UntypedFormGroup; threadFormGroup: UntypedFormGroup;
private formBuilder: FormBuilder = new FormBuilder(); private formBuilder: FormBuilder = new FormBuilder();
@ -73,6 +74,7 @@ export class AnnotationDialogComponent extends BaseComponent {
return; return;
} }
const threadToCreate: AnnotationPersist = { const threadToCreate: AnnotationPersist = {
threadId: Guid.create(),
payload: this.threadFormGroup.get('text').value, payload: this.threadFormGroup.get('text').value,
protectionType: this.threadFormGroup.get('protectionType').value, protectionType: this.threadFormGroup.get('protectionType').value,
entityId: this.entityId, entityId: this.entityId,
@ -86,7 +88,7 @@ export class AnnotationDialogComponent extends BaseComponent {
); );
} }
replyThread(threadId: Guid, threadProtection: AnnotationProtectionType) { replyThread(threadId: Guid) {
// if (!this.threadReplyTexts[threadId.toString()] || this.threadReplyTexts[threadId.toString()].length === 0) { return; } // if (!this.threadReplyTexts[threadId.toString()] || this.threadReplyTexts[threadId.toString()].length === 0) { return; }
this.formService.removeAllBackEndErrors(this.threadReplyTextsFG[threadId.toString()]); this.formService.removeAllBackEndErrors(this.threadReplyTextsFG[threadId.toString()]);
this.formService.touchAllFormFields(this.threadReplyTextsFG[threadId.toString()]); this.formService.touchAllFormFields(this.threadReplyTextsFG[threadId.toString()]);
@ -96,10 +98,11 @@ export class AnnotationDialogComponent extends BaseComponent {
const replyToCreate: AnnotationPersist = { const replyToCreate: AnnotationPersist = {
threadId: threadId, threadId: threadId,
payload: this.threadReplyTextsFG[threadId.toString()].get('replyText').value, payload: this.threadReplyTextsFG[threadId.toString()].get('replyText').value,
protectionType: threadProtection, protectionType: this.parentAnnotationsPerThread[threadId.toString()].protectionType,
entityId: this.entityId, entityId: this.entityId,
entityType: this.entityType, entityType: this.entityType,
anchor: this.anchor anchor: this.anchor,
parentId: this.parentAnnotationsPerThread[threadId.toString()].id
}; };
this.annotationService.persist(replyToCreate).pipe(takeUntil(this._destroyed)) this.annotationService.persist(replyToCreate).pipe(takeUntil(this._destroyed))
.subscribe( .subscribe(
@ -118,12 +121,13 @@ export class AnnotationDialogComponent extends BaseComponent {
private loadThreads() { private loadThreads() {
const lookup: AnnotationLookup = new AnnotationLookup(); const lookup: AnnotationLookup = new AnnotationLookup();
lookup.entityIds = [this.entityId]; lookup.entityIds = [this.entityId];
// lookup.anchors = [this.anchor]; lookup.anchors = [this.anchor];
lookup.entityTypes = [this.entityType]; lookup.entityTypes = [this.entityType];
lookup.project = { lookup.project = {
fields: [ fields: [
nameof<Annotation>(x => x.id), nameof<Annotation>(x => x.id),
nameof<Annotation>(x => x.threadId), nameof<Annotation>(x => x.threadId),
nameof<Annotation>(x => x.parent.id),
nameof<Annotation>(x => x.timeStamp), nameof<Annotation>(x => x.timeStamp),
nameof<Annotation>(x => x.author.name), nameof<Annotation>(x => x.author.name),
nameof<Annotation>(x => x.payload), nameof<Annotation>(x => x.payload),
@ -135,23 +139,33 @@ export class AnnotationDialogComponent extends BaseComponent {
.pipe(takeUntil(this._destroyed)) .pipe(takeUntil(this._destroyed))
.subscribe( .subscribe(
data => { data => {
// this.annotationsPerThread = {}; this.annotationsPerThread = {};
// this.threadReplyTextsFG = new Array<UntypedFormGroup>(); this.parentAnnotationsPerThread = {};
// this.resetFormGroup(); this.threads = new Set();
// this.threads = data.items.filter(item => item.id === item.threadId).sort((a1, a2) => new Date(a2.timeStamp).getTime() - new Date(a1.timeStamp).getTime()); this.threadReplyTextsFG = new Array<UntypedFormGroup>();
// this.threads.forEach(element => { this.resetFormGroup();
// // this.threadReplyTextsFG.addControl(element.id.toString(), new FormControl(null)); this.comments = data.items.sort((a1, a2) => new Date(a2.timeStamp).getTime() - new Date(a1.timeStamp).getTime());
// this.threadReplyTextsFG[element.id.toString()] = this.fb.group({ replyText: new FormControl(null, [Validators.required]) }); this.comments.forEach(element => {
// this.annotationsPerThread[element.id.toString()] = data.items.filter(x => x.threadId === element.id && x.id !== element.id).sort((a1, a2) => new Date(a1.timeStamp).getTime() - new Date(a2.timeStamp).getTime()); // this.threadReplyTextsFG.addControl(element.id.toString(), new FormControl(null));
// }); this.threadReplyTextsFG[element.threadId.toString()] = this.formBuilder.group({ replyText: new FormControl(null, [Validators.required]) });
// // this.annotationsChanged.emit(this.threads); this.annotationsPerThread[element.threadId.toString()] = data.items.filter(x => x.threadId === element.threadId && x.id !== element.id).sort((a1, a2) => new Date(a1.timeStamp).getTime() - new Date(a2.timeStamp).getTime());
this.parentAnnotationsPerThread[element.threadId.toString()] = data.items.filter(x => x.threadId === element.threadId && x.id === element.id)[0];
this.threads = data.items; this.threads.add(element.threadId);
});
// console.log(this.comments);
// console.log(this.threads);
// console.log(this.parentAnnotationsPerThread);
// console.log(this.annotationsPerThread);
// this.annotationsChanged.emit(this.threads);
}, },
error => this.onCallbackError(error), error => this.onCallbackError(error),
); );
} }
getParentAnnotation(thread: Guid): Annotation {
return this.parentAnnotationsPerThread[thread.toString()];
}
resetFormGroup() { resetFormGroup() {
this.threadFormGroup.reset(); this.threadFormGroup.reset();
this.threadFormGroup.get('protectionType').setValue(AnnotationProtectionType.EntityAccessors); this.threadFormGroup.get('protectionType').setValue(AnnotationProtectionType.EntityAccessors);
@ -181,8 +195,9 @@ export class AnnotationDialogComponent extends BaseComponent {
} }
private onCallbackSuccess() { private onCallbackSuccess() {
this.uiNotificationService.snackBarNotification(this.language.instant('DMP-UPLOAD.UPLOAD-SUCCESS'), SnackBarNotificationLevel.Success); this.uiNotificationService.snackBarNotification(this.language.instant('ANNOTATION-DIALOG.SUCCESS'), SnackBarNotificationLevel.Success);
this.router.navigate(['/reload']).then(() => this.router.navigate(['/plans'])); this.refreshAnnotations();
// this.router.navigate(['/reload']).then(() => this.router.navigate(['/plans']));
// this.router.navigate(['/reload']).then(() => this.isPublic ? this.router.navigate(['/explore-plans']) : this.router.navigate(['/plans'])); // this.router.navigate(['/reload']).then(() => this.isPublic ? this.router.navigate(['/explore-plans']) : this.router.navigate(['/plans']));
} }
@ -191,8 +206,8 @@ export class AnnotationDialogComponent extends BaseComponent {
} }
getAnnotationProtectionType(thread: Annotation): string { getAnnotationProtectionType(thread: Guid): string {
return this.enumUtils.toAnnotationProtectionTypeString(thread.protectionType); return this.enumUtils.toAnnotationProtectionTypeString(this.parentAnnotationsPerThread[thread.toString()].protectionType);
} }

View File

@ -127,6 +127,7 @@ export class DescriptionFormFieldSetComponent extends BaseComponent {
const dialogRef = this.dialog.open(AnnotationDialogComponent, { const dialogRef = this.dialog.open(AnnotationDialogComponent, {
width: '40rem', width: '40rem',
maxWidth: '90vw', maxWidth: '90vw',
maxHeight: '90vh',
data: { data: {
entityId: this.descriptionId, entityId: this.descriptionId,
anchor: fieldSetId, anchor: fieldSetId,

View File

@ -196,7 +196,7 @@ export class DmpEditorComponent extends BaseEditor<DmpEditorModel, Dmp> implemen
} }
buildForm() { buildForm() {
const canedit = this.isNew ? this.authService.hasPermission(AppPermission.NewDmp) : this.authService.hasPermission(AppPermission.EditDmp); const canedit = this.isNew ? this.authService.hasPermission(AppPermission.NewDmp) : this.item.authorizationFlags?.some(x => x === AppPermission.EditDmp) || this.authService.hasPermission(AppPermission.EditDmp);
this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !canedit); this.formGroup = this.editorModel.buildForm(null, this.isDeleted || !canedit);
if (this.editorModel.status == DmpStatus.Finalized || this.isDeleted) { if (this.editorModel.status == DmpStatus.Finalized || this.isDeleted) {

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { AppPermission } from '@app/core/common/enum/permission.enum';
import { Description } from '@app/core/model/description/description'; import { Description } from '@app/core/model/description/description';
import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, ExtraFieldInSection, FieldInSection, ReferenceTypeFieldInSection, SystemFieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint'; import { DescriptionTemplatesInSection, DmpBlueprint, DmpBlueprintDefinition, DmpBlueprintDefinitionSection, ExtraFieldInSection, FieldInSection, ReferenceTypeFieldInSection, SystemFieldInSection } from '@app/core/model/dmp-blueprint/dmp-blueprint';
import { Dmp, DmpBlueprintValue, DmpContact, DmpDescriptionTemplate, DmpProperties, DmpUser } from '@app/core/model/dmp/dmp'; import { Dmp, DmpBlueprintValue, DmpContact, DmpDescriptionTemplate, DmpProperties, DmpUser } from '@app/core/model/dmp/dmp';
@ -38,6 +39,8 @@ export class DmpEditorResolver extends BaseEditorResolver {
nameof<Dmp>(x => x.publicAfter), nameof<Dmp>(x => x.publicAfter),
nameof<Dmp>(x => x.hash), nameof<Dmp>(x => x.hash),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldId)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldId)].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldValue)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.dmpBlueprintValues), nameof<DmpBlueprintValue>(x => x.fieldValue)].join('.'),
[nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.user), nameof<DmpAssociatedUser>(x => x.id)].join('.'), [nameof<Dmp>(x => x.properties), nameof<DmpProperties>(x => x.contacts), nameof<DmpContact>(x => x.user), nameof<DmpAssociatedUser>(x => x.id)].join('.'),

View File

@ -35,10 +35,11 @@ const routes: Routes = [
data: { data: {
...BreadcrumbService.generateRouteDataConfiguration({ ...BreadcrumbService.generateRouteDataConfiguration({
title: 'BREADCRUMBS.EDIT-DMP' title: 'BREADCRUMBS.EDIT-DMP'
}), })
authContext: { // ,
permissions: [AppPermission.EditDmp] // authContext: {
} // permissions: [AppPermission.EditDmp]
// }
} }
}, },

View File

@ -31,6 +31,7 @@ import { DmpVersionStatus } from '@app/core/common/enum/dmp-version-status';
import { DmpReference } from '@app/core/model/dmp/dmp-reference'; import { DmpReference } from '@app/core/model/dmp/dmp-reference';
import { Reference } from '@app/core/model/reference/reference'; import { Reference } from '@app/core/model/reference/reference';
import { ReferenceType } from '@app/core/model/reference-type/reference-type'; import { ReferenceType } from '@app/core/model/reference-type/reference-type';
import { AppPermission } from '@app/core/common/enum/permission.enum';
@Component({ @Component({
selector: 'app-dmp-listing-component', selector: 'app-dmp-listing-component',
@ -185,6 +186,14 @@ export class DmpListingComponent extends BaseComponent implements OnInit { //IBr
nameof<Dmp>(x => x.groupId), nameof<Dmp>(x => x.groupId),
nameof<Dmp>(x => x.updatedAt), nameof<Dmp>(x => x.updatedAt),
nameof<Dmp>(x => x.hash), nameof<Dmp>(x => x.hash),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.CreateNewVersionDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.DeleteDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.CloneDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.FinalizeDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.ExportDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.InviteDmpUsers].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.AssignDmpUsers].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'), [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.id)].join('.'),
[nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'), [nameof<Dmp>(x => x.descriptions), nameof<Description>(x => x.label)].join('.'),

View File

@ -50,7 +50,7 @@
<button mat-menu-item (click)="viewVersions(dmp)"> <button mat-menu-item (click)="viewVersions(dmp)">
<mat-icon>library_books</mat-icon>{{'DMP-LISTING.ACTIONS.VIEW-VERSION' | translate}} <mat-icon>library_books</mat-icon>{{'DMP-LISTING.ACTIONS.VIEW-VERSION' | translate}}
</button> </button>
<button mat-menu-item *ngIf="isDraftDmp(dmp) && isDmpOwner(dmp)" (click)="deleteClicked(dmp.id)" class="menu-item"> <button mat-menu-item *ngIf="isDraftDmp(dmp) && canDeleteDmp(dmp)" (click)="deleteClicked(dmp.id)" class="menu-item">
<mat-icon>delete</mat-icon>{{ 'DMP-LISTING.ACTIONS.DELETE' | translate }} <mat-icon>delete</mat-icon>{{ 'DMP-LISTING.ACTIONS.DELETE' | translate }}
</button> </button>
</mat-menu> </mat-menu>

View File

@ -107,7 +107,7 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit {
} }
viewVersions(dmp: Dmp) { viewVersions(dmp: Dmp) {
if (dmp.accessType == DmpAccessType.Public && dmp.status == DmpStatus.Finalized && !this.isDmpOwner(dmp)) { if (dmp.accessType == DmpAccessType.Public && dmp.status == DmpStatus.Finalized && !this.dmp.authorizationFlags?.some(x => x === AppPermission.EditDmp)) {
let url = this.router.createUrlTree(['/explore-plans/versions/', dmp.groupId]); let url = this.router.createUrlTree(['/explore-plans/versions/', dmp.groupId]);
window.open(url.toString(), '_blank'); window.open(url.toString(), '_blank');
} else { } else {
@ -217,36 +217,31 @@ export class DmpListingItemComponent extends BaseComponent implements OnInit {
this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error); this.uiNotificationService.snackBarNotification(error.error.message ? error.error.message : this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-DELETE'), SnackBarNotificationLevel.Error);
} }
isDmpOwner(dmp: Dmp): boolean {
const principalId: Guid = this.authentication.userId();
if (principalId) return !!dmp.dmpUsers?.find(x => (x.role === DmpUserRole.Owner) && (principalId === x.id));
}
canCreateNewVersion(dmp: Dmp): boolean { canCreateNewVersion(dmp: Dmp): boolean {
return (this.isDmpOwner(dmp) || this.authentication.hasPermission(AppPermission.CreateNewVersionDmp)) && this.isPublic == false; return (this.dmp.authorizationFlags?.some(x => x === AppPermission.CreateNewVersionDmp) || this.authentication.hasPermission(AppPermission.CreateNewVersionDmp)) && this.isPublic == false;
} }
canDeleteDmp(dmp: Dmp): boolean { canDeleteDmp(dmp: Dmp): boolean {
return (this.isDmpOwner(dmp) || this.authentication.hasPermission(AppPermission.DeleteDmp)) && this.isPublic == false; return (this.dmp.authorizationFlags?.some(x => x === AppPermission.DeleteDmp) || this.authentication.hasPermission(AppPermission.DeleteDmp)) && this.isPublic == false;
} }
canCloneDmp(dmp: Dmp): boolean { canCloneDmp(dmp: Dmp): boolean {
return this.isDmpOwner(dmp) || this.authentication.hasPermission(AppPermission.CloneDmp); return this.dmp.authorizationFlags?.some(x => x === AppPermission.CloneDmp) || this.authentication.hasPermission(AppPermission.CloneDmp);
} }
canFinalizeDmp(dmp: Dmp): boolean { canFinalizeDmp(dmp: Dmp): boolean {
return (this.isDmpOwner(dmp) || this.authentication.hasPermission(AppPermission.FinalizeDmp)) && this.isPublic == false; return (this.dmp.authorizationFlags?.some(x => x === AppPermission.FinalizeDmp) || this.authentication.hasPermission(AppPermission.FinalizeDmp)) && this.isPublic == false;
} }
canExportDmp(dmp: Dmp): boolean { canExportDmp(dmp: Dmp): boolean {
return this.isDmpOwner(dmp) || this.authentication.hasPermission(AppPermission.ExportDmp); return this.dmp.authorizationFlags?.some(x => x === AppPermission.ExportDmp) || this.authentication.hasPermission(AppPermission.ExportDmp);
} }
canInviteDmpUsers(dmp: Dmp): boolean { canInviteDmpUsers(dmp: Dmp): boolean {
return (this.isDmpOwner(dmp) || this.authentication.hasPermission(AppPermission.InviteDmpUsers)) && this.isPublic == false; return (this.dmp.authorizationFlags?.some(x => x === AppPermission.InviteDmpUsers) || this.authentication.hasPermission(AppPermission.InviteDmpUsers)) && this.isPublic == false;
} }
canAssignDmpUsers(dmp: Dmp): boolean { canAssignDmpUsers(dmp: Dmp): boolean {
return (this.isDmpOwner(dmp) || this.authentication.hasPermission(AppPermission.AssignDmpUsers)) && this.isPublic == false; return (this.dmp.authorizationFlags?.some(x => x === AppPermission.AssignDmpUsers) || this.authentication.hasPermission(AppPermission.AssignDmpUsers)) && this.isPublic == false;
} }
} }

View File

@ -190,7 +190,7 @@
<p class="authors-role">{{ enumUtils.toDmpUserRoleString(dmpUser.role) }}</p> <p class="authors-role">{{ enumUtils.toDmpUserRoleString(dmpUser.role) }}</p>
</div> </div>
</div> </div>
<button *ngIf="isDmpOwner(dmp) && dmp.status === dmpStatusEnum.Draft && dmpUser.role != dmpUserRoleEnum.Owner" (click)="removeUserFromDmp(dmpUser)" class="remove-btn">{{ 'DMP-OVERVIEW.ACTIONS.REMOVE-AUTHOR' | translate}}</button> <button *ngIf="canAssignDmpUsers(dmp) && dmp.status === dmpStatusEnum.Draft && dmpUser.role != dmpUserRoleEnum.Owner" (click)="removeUserFromDmp(dmpUser)" class="remove-btn">{{ 'DMP-OVERVIEW.ACTIONS.REMOVE-AUTHOR' | translate}}</button>
</div> </div>
</div> </div>
<div *ngIf="canInviteDmpUsers()" class="row mt-3 mb-3 d-flex align-items-center justify-content-center"> <div *ngIf="canInviteDmpUsers()" class="row mt-3 mb-3 d-flex align-items-center justify-content-center">

View File

@ -197,39 +197,39 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
isDmpOwner(): boolean { isDmpOwner(): boolean {
const principalId: Guid = this.authentication.userId(); const principalId: Guid = this.authentication.userId();
if (principalId) return !!this.dmp.dmpUsers?.find(x => (x.role === DmpUserRole.Owner) && (principalId === x.id)); if (principalId) return !!this.dmp.dmpUsers?.find(x => (x.role === DmpUserRole.Owner) && (principalId === x.user?.id));
} }
canEditDmp(): boolean { canEditDmp(): boolean{
return (this.isDraftDmp()) && (this.isDmpOwner() || this.authentication.hasPermission(AppPermission.EditDmp)) && this.isPublicView == false; return (this.isDraftDmp()) && (this.dmp.authorizationFlags?.some(x => x === AppPermission.EditDmp) || this.authentication.hasPermission(AppPermission.EditDmp)) && this.isPublicView == false;
} }
canCreateNewVersion(): boolean { canCreateNewVersion(): boolean {
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.CreateNewVersionDmp) && this.isPublicView == false; return this.dmp.authorizationFlags?.some(x => x === AppPermission.CreateNewVersionDmp) || this.authentication.hasPermission(AppPermission.CreateNewVersionDmp) && this.isPublicView == false;
} }
canDeleteDmp(): boolean { canDeleteDmp(): boolean {
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.DeleteDmp) && this.isPublicView == false; return this.dmp.authorizationFlags?.some(x => x === AppPermission.DeleteDmp) || this.authentication.hasPermission(AppPermission.DeleteDmp) && this.isPublicView == false;
} }
canCloneDmp(): boolean { canCloneDmp(): boolean {
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.CloneDmp); return this.dmp.authorizationFlags?.some(x => x === AppPermission.CloneDmp) || this.authentication.hasPermission(AppPermission.CloneDmp);
} }
canFinalizeDmp(): boolean { canFinalizeDmp(): boolean {
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.FinalizeDmp) && this.isPublicView == false; return this.dmp.authorizationFlags?.some(x => x === AppPermission.FinalizeDmp) || this.authentication.hasPermission(AppPermission.FinalizeDmp) && this.isPublicView == false;
} }
canExportDmp(): boolean { canExportDmp(): boolean {
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.ExportDmp); return this.dmp.authorizationFlags?.some(x => x === AppPermission.ExportDmp) || this.authentication.hasPermission(AppPermission.ExportDmp);
} }
canInviteDmpUsers(): boolean { canInviteDmpUsers(): boolean {
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.InviteDmpUsers) && this.isPublicView == false; return this.dmp.authorizationFlags?.some(x => x === AppPermission.InviteDmpUsers) || this.authentication.hasPermission(AppPermission.InviteDmpUsers) && this.isPublicView == false;
} }
canAssignDmpUsers(): boolean { canAssignDmpUsers(): boolean {
return this.isDmpOwner() || this.authentication.hasPermission(AppPermission.AssignDmpUsers) && this.isPublicView == false; return this.dmp.authorizationFlags?.some(x => x === AppPermission.AssignDmpUsers) || this.authentication.hasPermission(AppPermission.AssignDmpUsers) && this.isPublicView == false;
} }
editClicked() { editClicked() {
@ -727,6 +727,15 @@ export class DmpOverviewComponent extends BaseComponent implements OnInit {
nameof<Dmp>(x => x.groupId), nameof<Dmp>(x => x.groupId),
nameof<Dmp>(x => x.version), nameof<Dmp>(x => x.version),
nameof<Dmp>(x => x.updatedAt), nameof<Dmp>(x => x.updatedAt),
nameof<Dmp>(x => x.updatedAt),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.CreateNewVersionDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.DeleteDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.CloneDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.FinalizeDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.ExportDmp].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.InviteDmpUsers].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.AssignDmpUsers].join('.'),
[nameof<Dmp>(x => x.authorizationFlags), AppPermission.EditDmp].join('.'),
[nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.id)].join('.'), [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.id)].join('.'),
[nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.repositoryId)].join('.'), [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.repositoryId)].join('.'),
[nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'), [nameof<Dmp>(x => x.entityDois), nameof<EntityDoi>(x => x.doi)].join('.'),