diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java index 522fbcdb06..d3b1c35773 100644 --- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java +++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumService.java @@ -25,4 +25,5 @@ public interface ProjectForumService { // TODO: Get these away from here List> latestPost(Project a, int amount); + boolean hasUnreadThreads(Project project, User user); } diff --git a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java index 3785b66d6f..7ed3e1715c 100644 --- a/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/forum/ProjectForumServiceImpl.java @@ -95,6 +95,17 @@ public class ProjectForumServiceImpl implements ProjectForumService { return postRepository.latestPost(project, amount); } + @Override + public boolean hasUnreadThreads(Project project, User user) { + List threads = getThreads(project); + for (ProjectThread thread : threads) { + if (!basicForumService.isThreadRead(user, thread.getForumThread())) { + return true; + } + } + return false; + } + @Override public ProjectThread findOne(long threadId) { return projectThreadRepository.findOne(threadId); diff --git a/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java b/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java index 199d5e8520..c25abbd10f 100644 --- a/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java +++ b/core/src/test/java/se/su/dsv/scipro/forum/ProjectForumServiceImplTest.java @@ -20,6 +20,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ProjectForumServiceImplTest extends ForumModuleTest { @@ -29,23 +30,24 @@ public class ProjectForumServiceImplTest extends ForumModuleTest { ProjectForumService service; private Project project; - private User user; + private User author; + private User supervisor; @BeforeEach public void setUp() throws Exception { ProjectType projectType = new ProjectType(DegreeType.BACHELOR, "Some project type", "Some description"); projectType.addModule(ProjectModule.FORUM); save(projectType); - final User supervisor = save(User.builder().firstName("Bob").lastName("The Builder").emailAddress("bob@example.com").build()); + supervisor = save(User.builder().firstName("Bob").lastName("The Builder").emailAddress("bob@example.com").build()); project = Project.builder().title("Some title").projectType(projectType).startDate(LocalDate.now()).headSupervisor(supervisor).build(); save(project); - user = User.builder().firstName("Stina").lastName("Student").emailAddress("stina@example.com").build(); - save(user); + author = User.builder().firstName("Stina").lastName("Student").emailAddress("stina@example.com").build(); + save(author); } @Test public void testLoadThread() { - final ProjectThread thread = service.createThread(project, user, "subject", "content", Set.of()); + final ProjectThread thread = service.createThread(project, author, "subject", "content", Set.of()); ProjectThread serviceThread = service.findOne(thread.getId()); assertEquals(thread, serviceThread); @@ -55,17 +57,17 @@ public class ProjectForumServiceImplTest extends ForumModuleTest { public void testCreateThreadWithPostAttachment() throws Exception { final String file = "attachment.txt"; try (var is = ProjectForumServiceImplTest.class.getResourceAsStream(file)) { - final StreamingUpload upload = new StreamingUpload(file, "text/plain", user, 2, is); - final ProjectThread thread = service.createThread(project, user, SUBJECT, CONTENT, Set.of(Attachment.newUpload(upload))); + final StreamingUpload upload = new StreamingUpload(file, "text/plain", author, 2, is); + final ProjectThread thread = service.createThread(project, author, SUBJECT, CONTENT, Set.of(Attachment.newUpload(upload))); - assertNewForumThread(thread, project, user, SUBJECT, CONTENT, file); + assertNewForumThread(thread, project, author, SUBJECT, CONTENT, file); } } @Test public void testGetPostPageByForumThread() { - final ProjectThread thread = service.createThread(project, user, "subject", "content", Set.of()); - final ForumPost reply = service.createReply(thread, user, "reply", Set.of()); + final ProjectThread thread = service.createThread(project, author, "subject", "content", Set.of()); + final ForumPost reply = service.createReply(thread, author, "reply", Set.of()); List servicePage = service.getPosts(thread); @@ -76,8 +78,8 @@ public class ProjectForumServiceImplTest extends ForumModuleTest { @Test public void testGetThreadsByProject() { - final ProjectThread thread1 = service.createThread(project, user, "subject 1", "content", Set.of()); - final ProjectThread thread2 = service.createThread(project, user, "subject 2", "content", Set.of()); + final ProjectThread thread1 = service.createThread(project, author, "subject 1", "content", Set.of()); + final ProjectThread thread2 = service.createThread(project, author, "subject 2", "content", Set.of()); List serviceThreads = service.getThreads(project); @@ -86,6 +88,16 @@ public class ProjectForumServiceImplTest extends ForumModuleTest { assertThat(serviceThreads, hasSize(2)); } + @Test + public void supervisor_has_unread_forum_threads_after_student_replies() { + final ProjectThread thread = service.createThread(project, supervisor, "subject", "content", Set.of()); + service.createReply(thread, author, "reply", Set.of()); + + boolean hasUnreadThreads = service.hasUnreadThreads(project, supervisor); + + assertTrue(hasUnreadThreads); + } + private void assertNewForumThread( ProjectThread thread, Project project, User user, String subject, String content, String attachmentFileName) { assertEquals(project, thread.getProject(), "Thread created for the wrong project"); diff --git a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java index 4f57d632d0..25eea67afa 100644 --- a/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java +++ b/view/src/main/java/se/su/dsv/scipro/forum/panels/AbstractReadStatePanel.java @@ -20,7 +20,7 @@ public abstract class AbstractReadStatePanel extends Panel { @Override public void onClick(final Optional target) { target.ifPresent(t -> { - toggleReadState(t); + onFlagClick(t); t.add(icon); }); } @@ -33,7 +33,7 @@ public abstract class AbstractReadStatePanel extends Panel { } protected abstract boolean isRead(); - protected abstract void toggleReadState(final AjaxRequestTarget target); + protected abstract void onFlagClick(final AjaxRequestTarget target); public static final String TOGGLE = "toggle"; static final String ICON = "icon"; diff --git a/view/src/main/java/se/su/dsv/scipro/forum/panels/threaded/ThreadReadStatePanel.java b/view/src/main/java/se/su/dsv/scipro/forum/panels/threaded/ThreadReadStatePanel.java index 2e16cb4953..908c096695 100644 --- a/view/src/main/java/se/su/dsv/scipro/forum/panels/threaded/ThreadReadStatePanel.java +++ b/view/src/main/java/se/su/dsv/scipro/forum/panels/threaded/ThreadReadStatePanel.java @@ -31,7 +31,7 @@ public class ThreadReadStatePanel extends AbstractReadStatePanel { } @Override - protected void toggleReadState(final AjaxRequestTarget target) { + protected void onFlagClick(final AjaxRequestTarget target) { boolean read = isRead(); basicForumService.setThreadRead(SciProSession.get().getUser(), model.getObject(), !read); diff --git a/view/src/main/java/se/su/dsv/scipro/notifications/panels/NotificationDataPanel.java b/view/src/main/java/se/su/dsv/scipro/notifications/panels/NotificationDataPanel.java index 8718550924..d5e132eb9f 100755 --- a/view/src/main/java/se/su/dsv/scipro/notifications/panels/NotificationDataPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/notifications/panels/NotificationDataPanel.java @@ -117,7 +117,7 @@ public class NotificationDataPanel extends Panel { } @Override - protected void toggleReadState(final AjaxRequestTarget target) { + protected void onFlagClick(final AjaxRequestTarget target) { Notification notification = rowModel.getObject(); notification.setUnread(!notification.isUnread()); notificationService.save(notification); diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java index 00498cbd6b..e13137a359 100755 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.java @@ -2,7 +2,9 @@ package se.su.dsv.scipro.supervisor.panels; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; +import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder; +import org.apache.wicket.extensions.markup.html.repeater.data.table.AbstractColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.LambdaColumn; import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider; @@ -10,6 +12,7 @@ import org.apache.wicket.markup.html.form.EnumChoiceRenderer; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.LambdaChoiceRenderer; import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LambdaModel; import org.apache.wicket.model.Model; @@ -19,6 +22,9 @@ import se.su.dsv.scipro.components.datatables.UserColumn; import se.su.dsv.scipro.dataproviders.FilteredDataProvider; import se.su.dsv.scipro.datatables.project.ProjectStateColumn; import se.su.dsv.scipro.datatables.project.ProjectTitleColumn; +import se.su.dsv.scipro.forum.ProjectForumService; +import se.su.dsv.scipro.forum.pages.threaded.SupervisorThreadedForumPage; +import se.su.dsv.scipro.forum.panels.AbstractReadStatePanel; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.ProjectStatus; @@ -51,6 +57,8 @@ public class SupervisorMyProjectsPanel extends Panel { private ProjectService projectService; @Inject private UserProfileService profileService; + @Inject + private ProjectForumService projectForumService; private ExportableDataPanel dataPanel; private ProjectService.Filter filter = new ProjectService.Filter(); @@ -72,6 +80,7 @@ public class SupervisorMyProjectsPanel extends Panel { private List> createColumns() { List> columns = new ArrayList<>(); columns.add(new ProjectStateColumn(Model.of("State"), "stateOfMind")); + columns.add(new ProjectForumStateColumn(Model.of("Forum"))); columns.add(new TemporalColumn<>(Model.of("Started"), "startDate", Project::getStartDate)); columns.add(new LambdaColumn<>(Model.of("Type"), "projectType.name", Project::getProjectTypeName)); columns.add(new ProjectTitleColumn(Model.of("Title"), "title")); @@ -157,4 +166,29 @@ public class SupervisorMyProjectsPanel extends Panel { profileService.save(userProfile); } } + + private class ProjectForumStateColumn extends AbstractColumn { + public ProjectForumStateColumn(IModel label) { + super(label); + } + + @Override + public void populateItem(Item> item, String id, IModel projectModel) { + item.add(new AbstractReadStatePanel(id) { + @Override + protected boolean isRead() { + return !projectForumService.hasUnreadThreads( + projectModel.getObject(), + SciProSession.get().getUser()); + } + + @Override + protected void onFlagClick(AjaxRequestTarget target) { + setResponsePage( + SupervisorThreadedForumPage.class, + SupervisorThreadedForumPage.getPageParameters(projectModel.getObject())); + } + }); + } + } }