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,18 +52,19 @@ 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(
EntityManager entityManager, EntityManager entityManager,
UserScope userScope, UserScope userScope,
AuthorizationService authorizationService, AuthorizationService authorizationService,
DeleterFactory deleterFactory, DeleterFactory deleterFactory,
BuilderFactory builderFactory, BuilderFactory builderFactory,
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('.'),