Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • aou/aou-backend
1 result
Select Git revision
Show changes
Commits on Source (2)
Showing
with 662 additions and 23 deletions
package ch.unige.aou.controller.admin;
import ch.unige.aou.business.NotificationService;
import ch.unige.aou.business.UserService;
import ch.unige.aou.controller.AdminController;
import ch.unige.aou.model.notification.Notification;
import ch.unige.aou.model.security.User;
import ch.unige.aou.repository.UserRepository;
import ch.unige.aou.rest.AouActionName;
import ch.unige.aou.rest.UrlPath;
import ch.unige.solidify.SolidifyConstants;
......@@ -32,7 +32,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
import java.time.OffsetDateTime;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
......@@ -42,18 +41,16 @@ import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
@RequestMapping(UrlPath.ADMIN_NOTIFICATIONS)
public class NotificationController extends ResourceController<Notification> {
private final HttpRequestInfoProvider httpRequestInfoProvider;
private final UserRepository userRepository;
private final UserService userService;
public NotificationController(HttpRequestInfoProvider httpRequestInfoProvider, UserRepository userRepository) {
this.httpRequestInfoProvider = httpRequestInfoProvider;
this.userRepository = userRepository;
public NotificationController(UserService userService) {
this.userService = userService;
}
@Override
@PreAuthorize("@notificationPermissionService.isAllowedToCreate(#notification)")
public HttpEntity<Notification> create(@RequestBody Notification notification) {
notification.setRecipient(this.getCurrentUser().getPerson());
notification.setRecipient(this.userService.getCurrentUser().getPerson());
notification.setSentTime(OffsetDateTime.now());
return super.create(notification);
}
......@@ -79,7 +76,7 @@ public class NotificationController extends ResourceController<Notification> {
@UserPermissions
@GetMapping(SolidifyConstants.URL_SEP + AouActionName.INBOX + SolidifyConstants.URL_ID)
public HttpEntity<Notification> getInboxNotification(@PathVariable String id) {
final Notification inboxNotification = ((NotificationService) this.itemService).getInboxNotification(id, this.getCurrentUser());
final Notification inboxNotification = ((NotificationService) this.itemService).getInboxNotification(id, this.userService.getCurrentUser());
return new ResponseEntity<>(inboxNotification, HttpStatus.OK);
}
......@@ -93,7 +90,7 @@ public class NotificationController extends ResourceController<Notification> {
@GetMapping(SolidifyConstants.URL_SEP + AouActionName.INBOX)
@SuppressWarnings("squid:S4684")
public HttpEntity<Collection<Notification>> listInboxNotification(@ModelAttribute Notification search, Pageable pageable) {
final Page<Notification> listItem = ((NotificationService) (this.itemService)).listInboxNotification(this.getCurrentUser(), search, null, null, pageable);
final Page<Notification> listItem = ((NotificationService) (this.itemService)).listInboxNotification(this.userService.getCurrentUser(), search, null, null, pageable);
final Collection<Notification> collection = this.addRemainingLinks(listItem, pageable);
return new ResponseEntity<>(collection, HttpStatus.OK);
}
......@@ -103,7 +100,7 @@ public class NotificationController extends ResourceController<Notification> {
public HttpEntity<Collection<Notification>> advancedSearch(@ModelAttribute Notification notification,
@RequestParam(value="structureId", required = false) String structureId,
@RequestParam(value = "researchGroupId", required = false) String researchGroupId, Pageable pageable) {
final Page<Notification> listItem = ((NotificationService) (this.itemService)).listInboxNotification(this.getCurrentUser(), notification, structureId, researchGroupId, pageable);
final Page<Notification> listItem = ((NotificationService) (this.itemService)).listInboxNotification(this.userService.getCurrentUser(), notification, structureId, researchGroupId, pageable);
final Collection<Notification> collection = this.addRemainingLinks(listItem, pageable);
return new ResponseEntity<>(collection, HttpStatus.OK);
}
......@@ -111,7 +108,7 @@ public class NotificationController extends ResourceController<Notification> {
@PreAuthorize("@notificationPermissionService.isMyNotification(#id)")
@PostMapping(SolidifyConstants.URL_ID + SolidifyConstants.URL_SEP + AouActionName.SET_READ)
public HttpEntity<Notification> markNotificationAsRead(@PathVariable String id) {
final Notification notification = ((NotificationService) (this.itemService)).getInboxNotification(id, this.getCurrentUser());
final Notification notification = ((NotificationService) (this.itemService)).getInboxNotification(id, this.userService.getCurrentUser());
if (notification != null) {
notification.setReadTime(OffsetDateTime.now());
this.itemService.save(notification);
......@@ -141,9 +138,5 @@ public class NotificationController extends ResourceController<Notification> {
return collection;
}
private User getCurrentUser() {
final Principal principal = this.httpRequestInfoProvider.getPrincipal();
return this.userRepository.findByExternalUid(principal.getName());
}
}
package ch.unige.aou.controller.admin;
import ch.unige.aou.controller.AdminController;
import ch.unige.aou.model.notification.NotificationType;
import ch.unige.aou.model.settings.Person;
import ch.unige.aou.model.settings.PersonNotificationType;
import ch.unige.aou.rest.ResourceName;
import ch.unige.aou.rest.UrlPath;
import ch.unige.solidify.SolidifyConstants;
import ch.unige.solidify.controller.Relation2TiersController;
import ch.unige.solidify.model.RoleApplication;
import ch.unige.solidify.rest.Collection;
import ch.unige.solidify.security.AdminPermissions;
import ch.unige.solidify.security.UserPermissions;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@UserPermissions
@RestController
@ConditionalOnBean(AdminController.class)
@RequestMapping(UrlPath.ADMIN_PEOPLE + SolidifyConstants.URL_PARENT_ID + ResourceName.NOTIFICATION_TYPES)
public class PersonNotificationTypeController extends Relation2TiersController<Person, NotificationType, PersonNotificationType> {
@Override
@PreAuthorize("@personPermissionService.isUserConnectedLinkedToPerson(#parentid)")
public HttpEntity<?> create(@PathVariable String parentid, @RequestBody String[] ids) {
return super.create(parentid, ids);
}
@Override
@PreAuthorize("@personPermissionService.isUserConnectedLinkedToPerson(#parentid)")
public HttpEntity<PersonNotificationType> update(@PathVariable String parentid, @PathVariable String id, @RequestBody PersonNotificationType newItem) {
return super.update(parentid, id, newItem);
}
@Override
@PreAuthorize("@personPermissionService.isUserConnectedLinkedToPerson(#parentid)")
public HttpStatus delete(@PathVariable String parentid, @PathVariable String id) {
return super.delete(parentid, id);
}
@Override
@PreAuthorize("@personPermissionService.isUserConnectedLinkedToPerson(#parentid)")
public HttpStatus deleteList(@PathVariable String parentid, @RequestBody(required = false) String[] ids) {
return super.deleteList(parentid, ids);
}
@Override
@PreAuthorize("hasAnyAuthority('" + RoleApplication.ADMIN_ID + "', '" + RoleApplication.TRUSTED_CLIENT_ID + "', '" + RoleApplication.ROOT_ID
+ "') or @personPermissionService.isUserConnectedLinkedToPerson(#parentid)")
public HttpEntity<?> get(@PathVariable String parentid, @PathVariable String id) {
return super.get(parentid, id);
}
@Override
@PreAuthorize("hasAnyAuthority('" + RoleApplication.ADMIN_ID + "', '" + RoleApplication.TRUSTED_CLIENT_ID + "', '" + RoleApplication.ROOT_ID
+ "') or @personPermissionService.isUserConnectedLinkedToPerson(#parentid)")
public HttpEntity<Collection<?>> list(@PathVariable String parentid, Pageable pageable) {
return super.list(parentid, pageable);
}
@Override
protected Class<PersonNotificationType> getCompositeResourceClass() {
return PersonNotificationType.class;
}
@Override
protected Class<Person> getMainResourceClass() {
return Person.class;
}
@Override
protected Class<NotificationType> getSubResourceClass() {
return NotificationType.class;
}
}
......@@ -3,6 +3,9 @@ package ch.unige.aou.controller.admin;
import java.util.List;
import java.util.stream.Collectors;
import ch.unige.aou.AouConstants;
import ch.unige.aou.business.PublicationService;
import ch.unige.solidify.rest.Result;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
......@@ -13,8 +16,10 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ch.unige.aou.business.AouResourceService;
......@@ -22,6 +27,7 @@ import ch.unige.aou.business.PersonService;
import ch.unige.aou.controller.AdminController;
import ch.unige.aou.model.publication.Publication;
import ch.unige.aou.model.settings.Person;
import ch.unige.aou.model.publication.Publication.PublicationStatus;
import ch.unige.aou.rest.AouActionName;
import ch.unige.aou.rest.UrlPath;
......@@ -32,6 +38,12 @@ import ch.unige.solidify.security.UserPermissions;
import ch.unige.solidify.specification.JoinedTableInValues;
import ch.unige.solidify.specification.SolidifySpecification;
import static ch.unige.aou.model.publication.Publication.PublicationStatus.FEEDBACK_REQUIRED;
import static ch.unige.aou.model.publication.Publication.PublicationStatus.IN_PROGRESS;
import static ch.unige.aou.model.publication.Publication.PublicationStatus.IN_VALIDATION;
import static ch.unige.aou.model.publication.Publication.PublicationStatus.SUBMITTED;
import static ch.unige.aou.model.publication.Publication.PublicationStatus.REJECTED;
@UserPermissions
@RestController
@ConditionalOnBean(AdminController.class)
......@@ -94,4 +106,68 @@ public class PublicationController extends ResourceController<Publication> {
final Collection<Publication> collection = this.setCollectionLinks(listItem, pageable);
return new ResponseEntity<>(collection, HttpStatus.OK);
}
@PostMapping(SolidifyConstants.URL_ID_PLUS_SEP + AouActionName.SUBMIT_FOR_VALIDATION)
public HttpEntity<Result> submitForApproval(@PathVariable String id){
return this.changePublicationStatus(IN_VALIDATION, id);
}
@PostMapping(SolidifyConstants.URL_ID_PLUS_SEP + AouActionName.SUBMIT)
public HttpEntity<Result> approve(@PathVariable String id) {
return this.changePublicationStatus(SUBMITTED, id);
}
@PostMapping(SolidifyConstants.URL_ID_PLUS_SEP + AouActionName.REJECT)
public HttpEntity<Result> reject(@PathVariable String id, @RequestParam(value = AouConstants.REASON, required = true) String reason) {
final Publication deposit = this.itemService.findOne(id);
if (deposit == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return this.changePublicationStatus(REJECTED, id, reason);
}
@PostMapping(SolidifyConstants.URL_ID_PLUS_SEP + AouActionName.ASK_FEEDBACK)
public HttpEntity<Result> askFeedback(@PathVariable String id) {
final Publication deposit = this.itemService.findOne(id);
if (deposit == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
if (((PublicationService)this.itemService).existCommentsByOtherPersonInPublication(id)) {
return this.changePublicationStatus(FEEDBACK_REQUIRED, id);
} else {
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}
}
@PostMapping(SolidifyConstants.URL_ID_PLUS_SEP + AouActionName.ENABLE_REVISION)
public HttpEntity<Result> enableRevision(@PathVariable String id) {
return this.changePublicationStatus(IN_PROGRESS, id);
}
private HttpEntity<Result> changePublicationStatus(PublicationStatus newStatus, String id) {
return this.changePublicationStatus(newStatus, id, null);
}
private HttpEntity<Result> changePublicationStatus(PublicationStatus newStatus, String id, String statusMessage) {
final Publication item = this.itemService.findOne(id);
final Result res = new Result(id);
if (item == null) {
res.setStatus(Result.ActionStatus.NOT_EXECUTED);
return new ResponseEntity<>(res, HttpStatus.NOT_FOUND);
} else if (((PublicationService) this.itemService).statusUpdateIsValid(item, newStatus)) {
item.setStatus(newStatus);
item.setStatusMessage(statusMessage);
this.itemService.save(item);
res.setStatus(Result.ActionStatus.EXECUTED);
res.setMesssage("Publication status changed successfully");
return new ResponseEntity<>(res, HttpStatus.OK);
} else {
res.setStatus(Result.ActionStatus.NON_APPLICABLE);
res.setMesssage("Publications's status cannot be altered in this way");
return new ResponseEntity<>(res, HttpStatus.BAD_REQUEST);
}
}
}
......@@ -4,6 +4,8 @@ import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import ch.unige.aou.model.display.NotificationTypePerson;
import ch.unige.aou.model.notification.NotificationType;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
......@@ -130,4 +132,22 @@ public class PersonClientService extends ResourceClientService<Person> implement
public void removeStructure(String personId, String structureId, String roleName) {
this.removeItem(personId, ResourceName.STRUCTURES, structureId, roleName);
}
@Override
public List<NotificationTypePerson> getNotificationTypes(String personId) {
return this.findAllLinkedResources(personId, ResourceName.NOTIFICATION_TYPES, NotificationTypePerson.class);
}
/*
* Add notification type with default frequency: DAILY
*/
@Override
public void addNotificationType(String personId, String notificationTypeId) {
this.addItem(personId, ResourceName.NOTIFICATION_TYPES, notificationTypeId, NotificationType.class);
}
@Override
public void removeNotificationType(String personId, String notificationTypeId) {
this.removeItem(personId, ResourceName.NOTIFICATION_TYPES, notificationTypeId);
}
}
......@@ -3,6 +3,10 @@ package ch.unige.aou.service.admin;
import java.nio.file.Path;
import java.util.List;
import ch.unige.aou.model.display.NotificationTypePerson;
import ch.unige.aou.model.settings.ResearchGroup;
import ch.unige.aou.model.settings.Structure;
import org.springframework.core.io.Resource;
import ch.unige.aou.model.settings.Institution;
......@@ -48,4 +52,13 @@ public interface PersonClientServiceInterface extends ResourceClientServiceInter
void removeStructure(String personId, String structureId);
void removeStructure(String personId, String structureId, String roleName);
List<NotificationTypePerson> getNotificationTypes(String personId);
/*
* Add notification type with default frequency: DAILY
*/
void addNotificationType(String personId, String notificationTypeId);
void removeNotificationType(String personId, String notificationTypeId);
}
package ch.unige.aou.repository;
import ch.unige.aou.AouConstants;
import ch.unige.aou.model.notification.NotificationType;
import ch.unige.aou.model.settings.Person;
import ch.unige.aou.model.settings.PersonNotificationType;
import ch.unige.solidify.repository.SolidifyCompositeRepository;
import org.checkerframework.checker.units.qual.A;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface PersonNotificationTypeRepository extends SolidifyCompositeRepository<PersonNotificationType> {
// *************
// ** Queries **
// *************
@Query("SELECT DISTINCT nt FROM NotificationType nt JOIN nt.personsToNotify ntp WHERE ntp.compositeResId.person.resId = :"
+ AouConstants.DB_PERSON_ID)
Page<NotificationType> findByPerson(@Param(AouConstants.DB_PERSON_ID) String personId, Pageable pageable);
@Query("SELECT DISTINCT p FROM Person p JOIN p.subscribedNotifications psn WHERE psn.compositeResId.notificationType.resId=:"
+ AouConstants.DB_NOTIFICATION_ID)
Page<Person> findByNotificationType(@Param(AouConstants.DB_NOTIFICATION_ID) String notificationId, Pageable pageable);
@Query("SELECT DISTINCT nt FROM NotificationType nt JOIN nt.personsToNotify ntp WHERE ntp.compositeResId.person.resId = :"
+ AouConstants.DB_PERSON_ID)
List<NotificationType> findByPerson(@Param(AouConstants.DB_PERSON_ID) String personId);
@Query("SELECT DISTINCT p FROM Person p JOIN p.subscribedNotifications psn WHERE psn.compositeResId.notificationType.resId=:"
+ AouConstants.DB_NOTIFICATION_ID)
List<Person> findByNotificationType(@Param(AouConstants.DB_NOTIFICATION_ID) String notificationId);
@Query("SELECT pnt FROM PersonNotificationType pnt WHERE pnt.compositeResId.person.resId=:"
+ AouConstants.DB_PERSON_ID + " AND pnt.compositeResId.notificationType.resId=:" + AouConstants.DB_NOTIFICATION_ID)
PersonNotificationType findByPersonAndNotificationType(@Param(AouConstants.DB_PERSON_ID) String personId,
@Param(AouConstants.DB_NOTIFICATION_ID) String notificationId);
// ***************
// ** Relations **
// ***************
@Override
public default <V> List<V> findByCompositeId(Class<?> compositeClass, Class<?> mainClass, String id) {
if (compositeClass.equals(PersonNotificationType.class) && mainClass.equals(Person.class)) {
return (List<V>) this.findByPerson(id);
}
if (compositeClass.equals(PersonNotificationType.class) && mainClass.equals(NotificationType.class)) {
return (List<V>) this.findByNotificationType(id);
}
throw new UnsupportedOperationException(
"Missing implementation 'findByCompositeId' method (" + compositeClass.getSimpleName() + " -> " + mainClass.getSimpleName() + ")");
}
@Override
public default <V> Page<V> findByCompositeId(Class<?> compositeClass, Class<?> mainClass, String id1, Pageable pageable) {
if (compositeClass.equals(PersonNotificationType.class) && mainClass.equals(Person.class)) {
return (Page<V>) this.findByPerson(id1, pageable);
}
if (compositeClass.equals(PersonNotificationType.class) && mainClass.equals(NotificationType.class)) {
return (Page<V>) this.findByNotificationType(id1, pageable);
}
throw new UnsupportedOperationException(
"Missing implementation 'findByCompositeId' pageable method (" + compositeClass.getSimpleName() + " -> " + mainClass.getSimpleName()
+ ")");
}
@Override
public default <V> V findByCompositeId(Class<?> compositeClass, Class<?> mainClass1, String id1, Class<?> mainClass2, String id2) {
if (compositeClass.equals(PersonNotificationType.class) && mainClass1.equals(Person.class) && mainClass2
.equals(NotificationType.class)) {
return (V) this.findByPersonAndNotificationType(id1, id2);
}
throw new UnsupportedOperationException(
"Missing implementation 'findByCompositeId' pageable method (" + compositeClass.getSimpleName() + " -> " + mainClass1.getSimpleName()
+ " & " + mainClass2.getSimpleName() + ")");
}
}
package ch.unige.aou.repository;
import ch.unige.solidify.service.HttpRequestInfoProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
......@@ -8,6 +9,8 @@ import ch.unige.aou.model.security.User;
import ch.unige.solidify.repository.SolidifyRepository;
import java.security.Principal;
@Repository
public interface UserRepository extends SolidifyRepository<User> {
......@@ -24,4 +27,8 @@ public interface UserRepository extends SolidifyRepository<User> {
User findByAccessTokenHash(String accessTokenHash);
User findByRefreshTokenHash(String refreshTokenHash);
default User getCurrentUser(Principal principal) {
return this.findByExternalUid(principal.getName());
}
}
......@@ -11,6 +11,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import ch.unige.aou.model.display.NotificationTypePerson;
import org.springframework.beans.factory.annotation.Autowired;
import ch.unige.aou.AouConstants;
......@@ -409,4 +410,14 @@ public abstract class AbstractAdminIT extends AbstractIT {
eventList.stream().forEach(e -> this.eventService.delete(e.getResId()));
}
}
protected void clearPersonNotificationTypes() {
User myUser = this.userService.getAuthenticatedUser();
Person myPerson = this.getLinkedPersonToUser(myUser);
List<NotificationTypePerson> notifications = this.personService.getNotificationTypes(myPerson.getResId());
for(NotificationTypePerson ntp: notifications) {
this.personService.removeNotificationType(myPerson.getResId(), ntp.getItem().getNotificationType().getResId());
}
}
}
......@@ -13,6 +13,8 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import ch.unige.aou.model.display.NotificationTypePerson;
import ch.unige.aou.model.notification.NotificationType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
......@@ -236,9 +238,47 @@ public class PersonAsUserIT extends AbstractAdminIT {
this.personService.deleteAvatar(thirdPartyPerson.getResId());
}
@Test
public void addAndRemoveNotificationTypeTest() {
/*
* Find the person linked to the user connected
*/
User myUser = this.userService.getAuthenticatedUser();
Person myPerson = this.getLinkedPersonToUser(myUser);
// check the notification types that has been already described
List<NotificationTypePerson> notifications = this.personService.getNotificationTypes(myPerson.getResId());
final int initialState = notifications.size();
// Add new notification type to person
this.personService.addNotificationType(myPerson.getResId(), NotificationType.COMMENT_IN_YOUR_PUBLICATION.getResId());
notifications = this.personService.getNotificationTypes(myPerson.getResId());
assertTrue(notifications.size() == initialState + 1);
// Test added person on the OrganizationalUnit
for (final NotificationTypePerson n : notifications) {
if (n.getResId().equals(NotificationType.COMMENT_IN_YOUR_PUBLICATION.getResId())) {
assertNotNull(n);
assertEquals(NotificationType.COMMENT_IN_YOUR_PUBLICATION.getEventType(), n.getEventType());
assertEquals(NotificationType.COMMENT_IN_YOUR_PUBLICATION.getNotifiedApplicationRole(), n.getNotifiedApplicationRole());
}
}
// Remove notification
this.personService.removeNotificationType(myPerson.getResId(), NotificationType.COMMENT_IN_YOUR_PUBLICATION.getResId());
// Test remove
notifications = this.personService.getNotificationTypes(myPerson.getResId());
assertTrue(notifications.size() == initialState);
}
@Override
protected void deleteFixtures() {
// Do nothing, clean is done on ZPersonUserCleanIt because we are not admin here
this.restClientTool.sudoAdmin();
this.clearPersonNotificationTypes();
this.restClientTool.exitSudo();
}
private Person createPerson(String firstName, String lastName, boolean orcid) {
......
......@@ -81,6 +81,7 @@ public class AouConstants {
public static final String DB_DOCUMENT_FILE_TYPE_ID = "document_file_type_id";
public static final String DB_PUBLICATION_ID = "publicationId";
public static final String DB_EVENT_TYPE_ID = "event_type_id";
public static final String DB_NOTIFICATION_ID = "notification_id";
// Index
public static final String REGISTRY = "registry";
......
package ch.unige.aou.model.display;
import ch.unige.aou.model.notification.NotificationType;
import ch.unige.aou.model.settings.PersonNotificationType;
import javax.persistence.Transient;
import java.util.List;
public class NotificationTypePerson extends NotificationType implements ExtendedResourceInterface<PersonNotificationType> {
@Transient
PersonNotificationType personNotificationType;
public NotificationTypePerson() {
}
public NotificationTypePerson(NotificationType notificationType) {
this.mapResourceToExtendedResource(this, notificationType);
}
@Override
public PersonNotificationType getItem() {
return this.personNotificationType;
}
@Override
public List<PersonNotificationType> getItems() {
return null;
}
@Override
public void setItem(PersonNotificationType item) {
this.personNotificationType = item;
}
@Override
public void setItems(List<PersonNotificationType> items) {
}
}
......@@ -30,7 +30,6 @@ public class Notification extends AouResourceNormalized<Notification> {
@ManyToOne
private Event event;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = StringTool.DATE_TIME_FORMAT)
@Column(length = 3)
private OffsetDateTime readTime;
......
......@@ -2,11 +2,16 @@ package ch.unige.aou.model.notification;
import ch.unige.aou.model.AouResourceNormalized;
import ch.unige.aou.model.security.Role;
import ch.unige.aou.model.settings.PersonNotificationType;
import ch.unige.solidify.model.RoleApplication;
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
......@@ -33,6 +38,10 @@ public class NotificationType extends AouResourceNormalized<NotificationType> {
@ManyToOne
private RoleApplication notifiedApplicationRole;
@JsonIgnore
@OneToMany(mappedBy = "compositeResId.notificationType", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PersonNotificationType> personsToNotify = new ArrayList<>();
public NotificationType(String resId, Role notifiedRoleStructureOrResearchGroup, RoleApplication notifiedApplicationRole, EventType eventType) {
this.setResId(resId);
this.notifiedRoleStructure = notifiedRoleStructureOrResearchGroup;
......
......@@ -27,7 +27,7 @@ public class Comment extends AouResourceNormalized<Comment> {
}
public void setText(String text) {
this.text = text;
this.setCurrentProperty(text);
}
public Publication getPublication() {
......@@ -35,7 +35,7 @@ public class Comment extends AouResourceNormalized<Comment> {
}
public void setPublication(Publication publication) {
this.publication = publication;
this.setCurrentProperty(publication);
}
public Person getPerson() {
......@@ -43,6 +43,6 @@ public class Comment extends AouResourceNormalized<Comment> {
}
public void setPerson(Person person) {
this.person = person;
this.setCurrentProperty(person);
}
}
......@@ -16,6 +16,8 @@ import javax.persistence.OneToMany;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import ch.unige.aou.rest.AouActionName;
import ch.unige.aou.rest.ResourceName;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
import ch.unige.aou.AouConstants;
......@@ -182,6 +184,28 @@ public class Publication extends AouResourceNormalized<Publication> {
@Override
public void addLinks(WebMvcLinkBuilder linkBuilder, boolean mainRes, boolean subResOnly) {
super.addLinks(linkBuilder, mainRes, subResOnly);
if (mainRes) {
this.add(linkBuilder.slash(this.getResId()).slash(ResourceName.COMMENTS).withRel(ResourceName.COMMENTS));
}
switch (this.getStatus()) {
case IN_PROGRESS:
this.add(linkBuilder.slash(this.getResId()).slash(AouActionName.SUBMIT_FOR_VALIDATION).withRel(AouActionName.SUBMIT_FOR_VALIDATION));
break;
case IN_VALIDATION:
this.add(linkBuilder.slash(this.getResId()).slash(AouActionName.SUBMIT).withRel(AouActionName.SUBMIT));
this.add(linkBuilder.slash(this.getResId()).slash(AouActionName.ASK_FEEDBACK).withRel(AouActionName.ASK_FEEDBACK));
this.add(linkBuilder.slash(this.getResId()).slash(AouActionName.REJECT).withRel(AouActionName.REJECT));
this.add(linkBuilder.slash(this.getResId()).slash(AouActionName.ENABLE_REVISION).withRel(AouActionName.ENABLE_REVISION));
break;
case IN_ERROR:
case REJECTED:
case COMPLETED:
case FEEDBACK_REQUIRED:
this.add(linkBuilder.slash(this.getResId()).slash(AouActionName.ENABLE_REVISION).withRel(AouActionName.ENABLE_REVISION));
break;
default:
break;
}
}
@Override
......
......@@ -17,6 +17,8 @@ import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import ch.unige.aou.model.display.NotificationTypePerson;
import ch.unige.aou.model.notification.NotificationType;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
......@@ -77,6 +79,10 @@ public class Person extends ResourceNormalized<Person> implements PersonInterfac
@UniqueConstraint(columnNames = { AouConstants.DB_PERSON_ID, AouConstants.DB_RESEARCH_GROUP_ID }) })
private Set<ResearchGroup> researchGroups = new HashSet<>();
@JsonIgnore
@OneToMany(mappedBy = "compositeResId.person", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PersonNotificationType> subscribedNotifications = new ArrayList<>();
public boolean addInstitution(Institution institution) {
final boolean result = this.institutions.add(institution);
if (!institution.getPeople().contains(this)) {
......@@ -93,6 +99,9 @@ public class Person extends ResourceNormalized<Person> implements PersonInterfac
if (t instanceof ResearchGroup) {
return this.addResearchGroup((ResearchGroup) t);
}
if (t instanceof NotificationType) {
return this.addNotificationType((NotificationType) t);
}
return false;
}
......@@ -101,6 +110,9 @@ public class Person extends ResourceNormalized<Person> implements PersonInterfac
if (t instanceof Structure) {
return this.setStructureRole((Structure) t, (Role) v);
}
if (t instanceof NotificationType) {
return this.addNotificationTypeFrequency((NotificationType) t, (PersonNotificationType.Frequency) v);
}
return false;
}
......@@ -112,6 +124,18 @@ public class Person extends ResourceNormalized<Person> implements PersonInterfac
return result;
}
protected boolean addNotificationType(NotificationType notificationType) {
return this.addNotificationTypeFrequency(notificationType, PersonNotificationType.Frequency.DAILY);
}
protected boolean addNotificationTypeFrequency(NotificationType notificationType, PersonNotificationType.Frequency frequency) {
final PersonNotificationType personNotificationType = new PersonNotificationType();
personNotificationType.setPerson(this);
personNotificationType.setNotificationType(notificationType);
personNotificationType.setNotificationFrequency(frequency);
return this.subscribedNotifications.add(personNotificationType);
}
private boolean setStructureRole(Structure structure, Role role) {
// Remove existing a membership
this.structuresRoles.removeIf(our -> structure.getResId().equals(our.getStructure().getResId()));
......@@ -133,6 +157,7 @@ public class Person extends ResourceNormalized<Person> implements PersonInterfac
this.add(linkBuilder.slash(this.getResId()).slash(AouActionName.UPLOAD_AVATAR).withRel(AouActionName.UPLOAD_AVATAR));
this.add(linkBuilder.slash(this.getResId()).slash(ResourceName.RESEARCH_GROUPS).withRel(ResourceName.RESEARCH_GROUPS));
this.add(linkBuilder.slash(this.getResId()).slash(ResourceName.STRUCTURES).withRel(ResourceName.STRUCTURES));
this.add(linkBuilder.slash(this.getResId()).slash(ResourceName.NOTIFICATION_TYPES).withRel(ResourceName.NOTIFICATION_TYPES));
}
}
......@@ -149,6 +174,11 @@ public class Person extends ResourceNormalized<Person> implements PersonInterfac
st.setItems((List<Role>) v);
return st;
}
if (t instanceof NotificationType) {
final NotificationTypePerson pnt = new NotificationTypePerson((NotificationType) t);
pnt.setItem((PersonNotificationType)v);
return pnt;
}
throw new UnsupportedOperationException(
"Missing implementation 'getDetails' method (" + t.getClass().getSimpleName() + " -> " + v.getClass().getSimpleName() + ")");
}
......@@ -207,6 +237,11 @@ public class Person extends ResourceNormalized<Person> implements PersonInterfac
return this.researchGroups;
}
@JsonIgnore
public List<PersonNotificationType> getSubscribedNotifications() {
return this.subscribedNotifications;
}
@Override
public void init() {
// Do nothing
......@@ -244,6 +279,12 @@ public class Person extends ResourceNormalized<Person> implements PersonInterfac
if (t instanceof Structure) {
return this.removeStructure((Structure) t);
}
if (t instanceof NotificationType) {
return this.removeNotificationType((NotificationType) t);
}
if (t instanceof PersonNotificationType) {
return this.removeNotificationType(((PersonNotificationType) t).getNotificationType());
}
return false;
}
......@@ -252,20 +293,35 @@ public class Person extends ResourceNormalized<Person> implements PersonInterfac
if (t instanceof Structure) {
return this.removeStructureRole((Structure) t, (Role) v);
}
if (t instanceof NotificationType) {
return this.removeNotificationTypeFrequency((NotificationType) t, (PersonNotificationType.Frequency) v);
}
return false;
}
protected boolean removeStructure(Structure structure) {
final Structure structure1 = structure;
return this.structuresRoles.removeIf(ouprPredicate -> ouprPredicate.getCompositeResId()
.getStructure() == structure);
.getStructure().equals(structure));
}
private boolean removeStructureRole(Structure structure, Role role) {
return this.structuresRoles.removeIf(our -> our.getStructure().equals(this) &&
return this.structuresRoles.removeIf(our -> our.getStructure().equals(structure) &&
our.getPerson().equals(this) && our.getRole().equals(role));
}
protected boolean removeNotificationTypeFrequency(NotificationType notificationType, PersonNotificationType.Frequency frequency) {
return this.subscribedNotifications.removeIf(oupPredicate ->
oupPredicate.getCompositeResId().getNotificationType().equals(notificationType)
&& oupPredicate.getCompositeResId().getPerson().equals(this)
&& oupPredicate.getNotificationFrequency().equals(frequency));
}
protected boolean removeNotificationType(NotificationType notificationType) {
return this.subscribedNotifications.removeIf(oupPredicate ->
oupPredicate.getCompositeResId().getNotificationType().equals(notificationType)
&& oupPredicate.getCompositeResId().getPerson().equals(this));
}
@Override
public void setFirstName(String firstName) {
this.setProperty(ReflectionTool.getCurrentSetterVariableName(), firstName);
......
package ch.unige.aou.model.settings;
import ch.unige.aou.model.notification.NotificationType;
import ch.unige.solidify.rest.CompositeResource;
import ch.unige.solidify.util.ReflectionTool;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import java.util.Objects;
@Entity
@Table(name = "personNotificationTypes")
public class PersonNotificationType extends CompositeResource<PersonNotificationType, PersonNotificationTypeId> {
public enum Frequency {
IMMEDIATELY,
DAILY,
WEEKLY,
}
@EmbeddedId
@JsonUnwrapped
private PersonNotificationTypeId compositeResId;
@Enumerated(EnumType.STRING)
@NotNull
private Frequency notificationFrequency;
public PersonNotificationType() {
this.compositeResId = new PersonNotificationTypeId();
}
@Override
public boolean equals(final Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PersonNotificationType)) {
return false;
}
final PersonNotificationType castOther = (PersonNotificationType) other;
return Objects.equals(this.getPerson(), castOther.getPerson()) && Objects
.equals(this.getNotificationType(), castOther.getNotificationType())
&& Objects.equals(this.getNotificationFrequency(), castOther.getNotificationFrequency());
}
@Override
public int hashCode() {
return Objects.hash(this.getPerson(), this.getNotificationType(), this.getNotificationFrequency());
}
@Override
public void setCompositeResId(PersonNotificationTypeId compositeResId) {
this.compositeResId = compositeResId;
}
public void setNotificationFrequency(Frequency notificationFrequency) {
this.setProperty(ReflectionTool.getCurrentSetterVariableName(), notificationFrequency);
}
@Transient
@JsonIgnore
public void setPerson(Person person) {
this.compositeResId.setPerson(person);
}
@Transient
@JsonIgnore
public void setNotificationType(NotificationType notificationType) {
this.compositeResId.setNotificationType(notificationType);
}
@Override
public PersonNotificationTypeId getCompositeResId() {
return compositeResId;
}
@Override
public Specification<PersonNotificationType> getSpecification() {
return null;
}
@Transient
@JsonIgnore
public Person getPerson() {
return this.compositeResId.getPerson();
}
@Transient
@JsonIgnore
public NotificationType getNotificationType() {
return this.compositeResId.getNotificationType();
}
public Frequency getNotificationFrequency() {
return notificationFrequency;
}
@Override
public String toString() {
return super.toString() + " (frequency=" + this.getNotificationFrequency() + ")";
}
}
package ch.unige.aou.model.settings;
import ch.unige.aou.AouConstants;
import ch.unige.aou.model.notification.NotificationType;
import javax.persistence.Embeddable;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import java.io.Serializable;
import java.util.Objects;
@Embeddable
public class PersonNotificationTypeId implements Serializable {
private static final long serialVersionUID = 2642815310967850318L;
@ManyToOne
@JoinColumn(name = AouConstants.DB_PERSON_ID, referencedColumnName = AouConstants.DB_RES_ID)
private Person person;
@ManyToOne
@JoinColumn(name = AouConstants.DB_NOTIFICATION_ID, referencedColumnName = AouConstants.DB_RES_ID)
private NotificationType notificationType;
@Override
public boolean equals(final Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PersonNotificationTypeId)) {
return false;
}
final PersonNotificationTypeId castOther = (PersonNotificationTypeId) other;
return Objects.equals(this.getPerson(), castOther.getPerson()) && Objects
.equals(this.getNotificationType(), castOther.getNotificationType());
}
@Override
public int hashCode() {
return Objects.hash(this.getPerson(), this.getNotificationType());
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public NotificationType getNotificationType() {
return notificationType;
}
public void setNotificationType(NotificationType notificationType) {
this.notificationType = notificationType;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + ": " + AouConstants.DB_PERSON_ID + "=" + this.getPerson().getResId() + " "
+ AouConstants.DB_NOTIFICATION_ID + "=" + this.getNotificationType().getResId();
}
}
......@@ -39,6 +39,11 @@ public class AouActionName {
public static final String SEARCH_INBOX = "search-inbox";
public static final String SENT = "sent";
public static final String SET_READ = "set-read";
public static final String SUBMIT_FOR_VALIDATION = "submit-for-validation";
public static final String SUBMIT = "submit";
public static final String REJECT = "reject";
public static final String ASK_FEEDBACK = "ask-feedback";
public static final String ENABLE_REVISION = "enable-revision";
public static final String ADD_VIEW = "add-view";
public static final String ADD_DOWNLOAD = "add-download";
......
......@@ -30,6 +30,7 @@ public class ResourceName {
public static final String ROLES = "roles";
public static final String RESEARCH_GROUPS = "research-groups";
public static final String NOTIFICATIONS = "notifications";
public static final String NOTIFICATION_TYPES = "notification-types";
public static final String STRUCTURES = "structures";
public static final String SYSTEM_PROPERTIES = "system-properties";
public static final String USERS = "users";
......