From de3803d9d12ba9160fdcee9e3f736943b2ea1d07 Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 3 Jul 2024 13:21:07 +0200 Subject: [PATCH 1/9] View/edit project notes as supervisor --- core/src/main/java/modules/CoreModule.java | 2 + .../se/su/dsv/scipro/project/Project.java | 15 +++ .../scipro/project/ProjectNoteService.java | 9 ++ .../se/su/dsv/scipro/project/ProjectRepo.java | 4 + .../dsv/scipro/project/ProjectRepoImpl.java | 13 +++ .../scipro/project/ProjectServiceImpl.java | 12 +- .../V388__user_notes_for_projects.sql | 8 ++ .../dsv/scipro/components/MaxLengthLabel.java | 43 +++++++ ...ctNoteColumn$ViewAndEditNoteCellPanel.html | 10 ++ ...ProjectNoteColumn$ViewAndEditNoteForm.html | 15 +++ .../supervisor/panels/ProjectNoteColumn.java | 108 ++++++++++++++++++ .../panels/SupervisorMyProjectsPanel.java | 2 + 12 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/se/su/dsv/scipro/project/ProjectNoteService.java create mode 100644 core/src/main/resources/db/migration/V388__user_notes_for_projects.sql create mode 100644 view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java create mode 100644 view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html create mode 100644 view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html create mode 100644 view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java diff --git a/core/src/main/java/modules/CoreModule.java b/core/src/main/java/modules/CoreModule.java index cb2c500685..075c6e9ed8 100644 --- a/core/src/main/java/modules/CoreModule.java +++ b/core/src/main/java/modules/CoreModule.java @@ -48,6 +48,7 @@ import se.su.dsv.scipro.notifications.settings.service.ReceiverConfigurationServ import se.su.dsv.scipro.notifications.settings.service.ReceiverConfigurationServiceImpl; import se.su.dsv.scipro.peer.*; import se.su.dsv.scipro.plagiarism.*; +import se.su.dsv.scipro.project.ProjectNoteService; import se.su.dsv.scipro.project.ProjectPeopleStatisticsService; import se.su.dsv.scipro.project.ProjectPeopleStatisticsServiceImpl; import se.su.dsv.scipro.project.ProjectService; @@ -139,6 +140,7 @@ public class CoreModule extends AbstractModule { bind(FirstMeetingService.class).to(FirstMeetingServiceImpl.class); bind(FinalSeminarCreationSubscribers.class).asEagerSingleton(); bind(ProjectPartnerRepository.class).to(ProjectPartnerRepositoryImpl.class); + bind(ProjectNoteService.class).to(ProjectServiceImpl.class); install(new PlagiarismModule()); install(new NotificationModule()); diff --git a/core/src/main/java/se/su/dsv/scipro/project/Project.java b/core/src/main/java/se/su/dsv/scipro/project/Project.java index 92c82a9014..9429d2cbfc 100755 --- a/core/src/main/java/se/su/dsv/scipro/project/Project.java +++ b/core/src/main/java/se/su/dsv/scipro/project/Project.java @@ -85,6 +85,13 @@ public class Project extends DomainObject { @Enumerated(EnumType.STRING) private Language language; + @ElementCollection(fetch = FetchType.LAZY) + @CollectionTable(name = "project_user_note", joinColumns = @JoinColumn(name = "project_id")) + @Column(name = "note") + @SuppressWarnings("JpaDataSourceORMInspection") // false warning from IntelliJ for the @MapKeyJoinColumn + @MapKeyJoinColumn(name = "user_id") + private Map userNotes = new HashMap<>(); + @PrePersist @PreUpdate void cleanTitle() { @@ -94,6 +101,14 @@ public class Project extends DomainObject { title = title.trim(); } + public Map getUserNotes() { + return userNotes; + } + + public void setUserNotes(Map userNotes) { + this.userNotes = userNotes; + } + public boolean isFinalSeminarRuleExempted() { return finalSeminarRuleExempted; } diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectNoteService.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectNoteService.java new file mode 100644 index 0000000000..7ff1657d63 --- /dev/null +++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectNoteService.java @@ -0,0 +1,9 @@ +package se.su.dsv.scipro.project; + +import se.su.dsv.scipro.system.User; + +public interface ProjectNoteService { + String getUserNote(Project project, User user); + + void setUserNote(Project project, User user, String note); +} diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java index 69dcda672d..f27da7b94a 100755 --- a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java +++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepo.java @@ -11,4 +11,8 @@ import java.util.List; @Transactional public interface ProjectRepo extends JpaRepository, QueryDslPredicateExecutor { List findMultipleAuthors(Collection projects); + + String getUserNoteForProject(Project project, User user); + + void setUserNoteForProject(Project project, User user, String note); } \ No newline at end of file diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java index 627af554ec..f8dfb462bd 100644 --- a/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectRepoImpl.java @@ -1,5 +1,6 @@ package se.su.dsv.scipro.project; +import com.google.inject.persist.Transactional; import se.su.dsv.scipro.system.GenericRepo; import se.su.dsv.scipro.system.User; @@ -30,4 +31,16 @@ public class ProjectRepoImpl extends GenericRepo implements Proje query.setParameter("projects", projects); return query.getResultList(); } + + @Override + public String getUserNoteForProject(Project project, User user) { + return project.getUserNotes().get(user); + } + + @Override + @Transactional + public void setUserNoteForProject(Project project, User user, String note) { + project.getUserNotes().put(user, note); + save(project); + } } diff --git a/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java b/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java index c2f8e83098..2ba2a6a436 100755 --- a/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java +++ b/core/src/main/java/se/su/dsv/scipro/project/ProjectServiceImpl.java @@ -22,7 +22,7 @@ import java.time.Duration; import java.time.Instant; import java.util.*; -public class ProjectServiceImpl extends AbstractServiceImpl implements ProjectService { +public class ProjectServiceImpl extends AbstractServiceImpl implements ProjectService, ProjectNoteService { public static final int MIN_TITLE_LENGTH = 3; private final ProjectRepo projectRepo; @@ -175,6 +175,16 @@ public class ProjectServiceImpl extends AbstractServiceImpl imple return completed; } + @Override + public String getUserNote(Project project, User user) { + return projectRepo.getUserNoteForProject(project, user); + } + + @Override + public void setUserNote(Project project, User user, String note) { + projectRepo.setUserNoteForProject(project, user, note); + } + @Override public List findAll(Filter filter, Pageable pageable) { return findAll(toPredicate(filter), pageable); diff --git a/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql b/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql new file mode 100644 index 0000000000..fb994da0c0 --- /dev/null +++ b/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS `project_user_note` ( + `project_id` bigint NOT NULL, + `user_id` bigint NOT NULL, + `note` text NULL, + PRIMARY KEY (`project_id`, `user_id`), + CONSTRAINT `FK_project_user_note_project` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `FK_project_user_note_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE +); diff --git a/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java b/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java new file mode 100644 index 0000000000..00232bc377 --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java @@ -0,0 +1,43 @@ +package se.su.dsv.scipro.components; + +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.model.IModel; +import org.apache.wicket.util.convert.ConversionException; +import org.apache.wicket.util.convert.IConverter; + +import java.util.Locale; + +public class MaxLengthLabel extends Label { + private static final int DEFAULT_MAX_LENGTH = 50; + + private final int maxLength; + + public MaxLengthLabel(String id, IModel dataModel) { + this(id, dataModel, DEFAULT_MAX_LENGTH); + } + + public MaxLengthLabel(String id, IModel dataModel, int maxLength) { + super(id, dataModel); + this.maxLength = maxLength; + } + + @Override + protected IConverter createConverter(Class type) { + return new MaxLengthConverter(); + } + + private class MaxLengthConverter implements IConverter { + @Override + public String convertToObject(String s, Locale locale) { + return s; + } + + @Override + public String convertToString(String o, Locale locale) { + if (o.length() > maxLength) { + return o.substring(0, maxLength) + "..."; + } + return o; + } + } +} diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html new file mode 100644 index 0000000000..101a8a2f5e --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html @@ -0,0 +1,10 @@ + + + + +
+ + [view/edit note] +
+ + \ No newline at end of file diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html new file mode 100644 index 0000000000..b461a26944 --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html @@ -0,0 +1,15 @@ + + + + +
+
+
+ + +
+ +
+
+ + \ No newline at end of file diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java new file mode 100644 index 0000000000..30bf1f7543 --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java @@ -0,0 +1,108 @@ +package se.su.dsv.scipro.supervisor.panels; + +import jakarta.inject.Inject; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.AjaxLink; +import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; +import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; +import org.apache.wicket.extensions.markup.html.repeater.data.table.export.AbstractExportableColumn; +import org.apache.wicket.feedback.FencedFeedbackPanel; +import org.apache.wicket.injection.Injector; +import org.apache.wicket.markup.html.form.Form; +import org.apache.wicket.markup.html.form.TextArea; +import org.apache.wicket.markup.html.panel.FeedbackPanel; +import org.apache.wicket.markup.html.panel.GenericPanel; +import org.apache.wicket.markup.repeater.Item; +import org.apache.wicket.model.IModel; +import org.apache.wicket.model.LoadableDetachableModel; +import org.apache.wicket.model.Model; +import se.su.dsv.scipro.components.LargeModalWindow; +import se.su.dsv.scipro.components.MaxLengthLabel; +import se.su.dsv.scipro.components.ModalWindowPlus; +import se.su.dsv.scipro.project.Project; +import se.su.dsv.scipro.project.ProjectNoteService; +import se.su.dsv.scipro.system.User; + +import java.time.LocalTime; + +public class ProjectNoteColumn extends AbstractExportableColumn { + @Inject + private ProjectNoteService projectNoteService; + + private final IModel user; + + public ProjectNoteColumn(IModel displayModel, IModel user) { + super(displayModel); + Injector.get().inject(this); + this.user = user; + } + + @Override + public IModel getDataModel(IModel rowModel) { + return LoadableDetachableModel.of(() -> projectNoteService.getUserNote( + rowModel.getObject(), + user.getObject())); + } + + @Override + public void populateItem( + Item> cellItem, + String componentId, + IModel rowModel) + { + cellItem.add(new ViewAndEditNoteCellPanel(componentId, rowModel)); + } + + private class ViewAndEditNoteCellPanel extends GenericPanel { + public ViewAndEditNoteCellPanel(String id, IModel model) { + super(id, model); + + ModalWindowPlus modal = new LargeModalWindow("edit_note_modal"); + modal.setTitle(model.map(Project::getTitle).map(projectTitle -> "View/Edit note for " + projectTitle)); + modal.setContent(componentId -> new ViewAndEditNoteForm(componentId, model)); + add(modal); + + add(new MaxLengthLabel("shortened_note", getDataModel(model))); + + AjaxLink noteLink = new AjaxLink<>("view_note") { + @Override + public void onClick(AjaxRequestTarget target) { + modal.show(target); + } + }; + noteLink.setBody(Model.of("View/Edit note")); + add(noteLink); + } + } + + private class ViewAndEditNoteForm extends GenericPanel { + public ViewAndEditNoteForm(String id, IModel model) { + super(id, model); + + IModel note = getDataModel(model); + + Form form = new Form<>("form", model) { + @Override + protected void onSubmit() { + projectNoteService.setUserNote( + model.getObject(), + user.getObject(), + note.getObject() + ); + success("Note saved at " + LocalTime.now()); + } + }; + add(form); + + form.add(new FencedFeedbackPanel("feedback", form)); + + form.add(new TextArea<>("note", note)); + form.add(new AjaxSubmitLink("save") { + @Override + protected void onAfterSubmit(AjaxRequestTarget target) { + target.add(form); + } + }); + } + } +} 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..84b1d5936f 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 @@ -12,6 +12,7 @@ import org.apache.wicket.markup.html.form.LambdaChoiceRenderer; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LambdaModel; +import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import se.su.dsv.scipro.components.*; import se.su.dsv.scipro.components.datatables.MultipleUsersColumn; @@ -81,6 +82,7 @@ public class SupervisorMyProjectsPanel extends Panel { return new ListAdapterModel<>(rowModel.map(Project::getProjectParticipants)); } }); + columns.add(new ProjectNoteColumn(Model.of("Note"), LoadableDetachableModel.of(this::currentUser))); columns.add(new UserColumn<>(Model.of("Head supervisor"), "headSupervisor.fullName", Project::getHeadSupervisor)); return columns; } -- 2.39.5 From f5a7b592c0d652e809a546d0bb6714167b850120 Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 3 Jul 2024 13:32:38 +0200 Subject: [PATCH 2/9] Do not include milliseconds in feedback when saving the note --- .../se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java index 30bf1f7543..66678fcc3a 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java @@ -24,6 +24,7 @@ import se.su.dsv.scipro.project.ProjectNoteService; import se.su.dsv.scipro.system.User; import java.time.LocalTime; +import java.time.temporal.ChronoUnit; public class ProjectNoteColumn extends AbstractExportableColumn { @Inject @@ -89,7 +90,7 @@ public class ProjectNoteColumn extends AbstractExportableColumn user.getObject(), note.getObject() ); - success("Note saved at " + LocalTime.now()); + success("Note saved at " + LocalTime.now().truncatedTo(ChronoUnit.SECONDS)); } }; add(form); -- 2.39.5 From 481d8cd76e3a601d2bd57cfd4102f55754725179 Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 3 Jul 2024 13:33:21 +0200 Subject: [PATCH 3/9] Update the note in the table when the user is done editing. --- .../se/su/dsv/scipro/components/ModalWindowPlus.java | 11 +++++++++++ .../scipro/supervisor/panels/ProjectNoteColumn.java | 3 +++ 2 files changed, 14 insertions(+) diff --git a/view/src/main/java/se/su/dsv/scipro/components/ModalWindowPlus.java b/view/src/main/java/se/su/dsv/scipro/components/ModalWindowPlus.java index 9faa92158d..0eca546462 100644 --- a/view/src/main/java/se/su/dsv/scipro/components/ModalWindowPlus.java +++ b/view/src/main/java/se/su/dsv/scipro/components/ModalWindowPlus.java @@ -1,6 +1,7 @@ package se.su.dsv.scipro.components; import org.apache.wicket.Component; +import org.apache.wicket.ajax.AjaxEventBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.markup.ComponentTag; @@ -9,6 +10,7 @@ import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; +import org.danekja.java.util.function.serializable.SerializableConsumer; import java.util.function.Function; @@ -83,4 +85,13 @@ public class ModalWindowPlus extends Panel { component.setOutputMarkupPlaceholderTag(true); replace(component); } + + public void onClose(SerializableConsumer onClose) { + add(new AjaxEventBehavior("hidden.bs.modal") { + @Override + protected void onEvent(AjaxRequestTarget target) { + onClose.accept(target); + } + }); + } } diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java index 66678fcc3a..ad06ef13a3 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java @@ -63,6 +63,9 @@ public class ProjectNoteColumn extends AbstractExportableColumn modal.setContent(componentId -> new ViewAndEditNoteForm(componentId, model)); add(modal); + setOutputMarkupId(true); + modal.onClose(target -> target.add(this)); + add(new MaxLengthLabel("shortened_note", getDataModel(model))); AjaxLink noteLink = new AjaxLink<>("view_note") { -- 2.39.5 From 4facacb398a9a4cfc64d6bfa53d8e39fd418de6d Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 3 Jul 2024 13:34:25 +0200 Subject: [PATCH 4/9] Hide the text area label since the modal title is enough --- .../panels/ProjectNoteColumn$ViewAndEditNoteForm.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html index b461a26944..bf546f6a76 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteForm.html @@ -5,7 +5,7 @@
- +
-- 2.39.5 From ec6eb86f91ab881b4bfc2474c79005a378c97dda Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 3 Jul 2024 15:00:55 +0200 Subject: [PATCH 5/9] Allow supervisors to toggle showing the entire note --- .../SupervisorProjectNoteDisplay.java | 5 +++++ .../settings/dataobjects/UserProfile.java | 13 ++++++++++++ .../V388__user_notes_for_projects.sql | 3 +++ .../dsv/scipro/components/MaxLengthLabel.java | 14 ++++--------- .../supervisor/panels/ProjectNoteColumn.java | 13 +++++++++--- .../panels/SupervisorMyProjectsPanel.html | 4 ++++ .../panels/SupervisorMyProjectsPanel.java | 20 ++++++++++++++++++- .../SupervisorMyProjectsPanel.utf8.properties | 3 +++ 8 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/se/su/dsv/scipro/settings/dataobjects/SupervisorProjectNoteDisplay.java diff --git a/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/SupervisorProjectNoteDisplay.java b/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/SupervisorProjectNoteDisplay.java new file mode 100644 index 0000000000..1a6c45d452 --- /dev/null +++ b/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/SupervisorProjectNoteDisplay.java @@ -0,0 +1,5 @@ +package se.su.dsv.scipro.settings.dataobjects; + +public enum SupervisorProjectNoteDisplay { + COMPACT, FULL +} diff --git a/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/UserProfile.java b/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/UserProfile.java index 8cf7d44e31..ac863a2afa 100644 --- a/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/UserProfile.java +++ b/core/src/main/java/se/su/dsv/scipro/settings/dataobjects/UserProfile.java @@ -58,6 +58,11 @@ public class UserProfile extends DomainObject { @Enumerated(EnumType.STRING) private Roles selectedRole; + @Basic + @Enumerated(EnumType.STRING) + @Column(name = "supervisor_project_note_display") + private SupervisorProjectNoteDisplay supervisorProjectNoteDisplay = SupervisorProjectNoteDisplay.COMPACT; + @Override public Long getId() { return this.id; @@ -147,6 +152,14 @@ public class UserProfile extends DomainObject { this.selectedRole = selectedRole; } + public SupervisorProjectNoteDisplay getSupervisorProjectNoteDisplay() { + return supervisorProjectNoteDisplay; + } + + public void setSupervisorProjectNoteDisplay(SupervisorProjectNoteDisplay supervisorProjectNoteDisplay) { + this.supervisorProjectNoteDisplay = supervisorProjectNoteDisplay; + } + @Override public String toString() { return "UserProfile(id=" + this.getId() + ", user=" + this.getUser() + ", skypeId=" + this.getSkypeId() + ", phoneNumber=" + this.getPhoneNumber() + ", otherInfo=" + this.getOtherInfo() + ", mailCompilation=" + this.isMailCompilation() + ", defaultProjectStatusFilter=" + this.getDefaultProjectStatusFilter() + ", defaultProjectTeamMemberRolesFilter=" + this.getDefaultProjectTeamMemberRolesFilter() + ", defaultSupervisorFilter=" + this.isDefaultSupervisorFilter() + ", defaultProjectTypeFilter=" + this.getDefaultProjectTypeFilter() + ", selectedRole=" + this.getSelectedRole() + ")"; diff --git a/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql b/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql index fb994da0c0..334caad59b 100644 --- a/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql +++ b/core/src/main/resources/db/migration/V388__user_notes_for_projects.sql @@ -6,3 +6,6 @@ CREATE TABLE IF NOT EXISTS `project_user_note` ( CONSTRAINT `FK_project_user_note_project` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `FK_project_user_note_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ); + +ALTER TABLE `user_profile` + ADD COLUMN `supervisor_project_note_display` VARCHAR(15) NOT NULL DEFAULT 'COMPACT'; diff --git a/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java b/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java index 00232bc377..edfdd48ab9 100644 --- a/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java +++ b/view/src/main/java/se/su/dsv/scipro/components/MaxLengthLabel.java @@ -2,23 +2,16 @@ package se.su.dsv.scipro.components; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.model.IModel; -import org.apache.wicket.util.convert.ConversionException; import org.apache.wicket.util.convert.IConverter; import java.util.Locale; public class MaxLengthLabel extends Label { - private static final int DEFAULT_MAX_LENGTH = 50; + private final IModel maxLengthModel; - private final int maxLength; - - public MaxLengthLabel(String id, IModel dataModel) { - this(id, dataModel, DEFAULT_MAX_LENGTH); - } - - public MaxLengthLabel(String id, IModel dataModel, int maxLength) { + public MaxLengthLabel(String id, IModel dataModel, IModel maxLength) { super(id, dataModel); - this.maxLength = maxLength; + this.maxLengthModel = maxLength; } @Override @@ -34,6 +27,7 @@ public class MaxLengthLabel extends Label { @Override public String convertToString(String o, Locale locale) { + Integer maxLength = maxLengthModel.getObject(); if (o.length() > maxLength) { return o.substring(0, maxLength) + "..."; } diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java index ad06ef13a3..a0e6a2f25a 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java @@ -10,7 +10,6 @@ import org.apache.wicket.feedback.FencedFeedbackPanel; import org.apache.wicket.injection.Injector; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextArea; -import org.apache.wicket.markup.html.panel.FeedbackPanel; import org.apache.wicket.markup.html.panel.GenericPanel; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.IModel; @@ -21,6 +20,7 @@ import se.su.dsv.scipro.components.MaxLengthLabel; import se.su.dsv.scipro.components.ModalWindowPlus; import se.su.dsv.scipro.project.Project; import se.su.dsv.scipro.project.ProjectNoteService; +import se.su.dsv.scipro.settings.dataobjects.SupervisorProjectNoteDisplay; import se.su.dsv.scipro.system.User; import java.time.LocalTime; @@ -31,10 +31,13 @@ public class ProjectNoteColumn extends AbstractExportableColumn private ProjectNoteService projectNoteService; private final IModel user; + private final IModel supervisorProjectNoteDisplayModel; - public ProjectNoteColumn(IModel displayModel, IModel user) { + public ProjectNoteColumn(IModel displayModel, IModel user, + IModel supervisorProjectNoteDisplayModel) { super(displayModel); Injector.get().inject(this); + this.supervisorProjectNoteDisplayModel = supervisorProjectNoteDisplayModel; this.user = user; } @@ -66,7 +69,11 @@ public class ProjectNoteColumn extends AbstractExportableColumn setOutputMarkupId(true); modal.onClose(target -> target.add(this)); - add(new MaxLengthLabel("shortened_note", getDataModel(model))); + IModel maxLength = supervisorProjectNoteDisplayModel.map(display -> switch (display) { + case FULL -> Integer.MAX_VALUE; + case COMPACT -> 100; + }); + add(new MaxLengthLabel("shortened_note", getDataModel(model), maxLength)); AjaxLink noteLink = new AjaxLink<>("view_note") { @Override diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html index 9b68bf8d79..73ab571f7f 100755 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.html @@ -31,6 +31,10 @@
+
+ Note +
+
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 84b1d5936f..2f22478645 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 @@ -1,6 +1,7 @@ package se.su.dsv.scipro.supervisor.panels; import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; @@ -25,6 +26,7 @@ import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.ProjectStatus; import se.su.dsv.scipro.project.ProjectTeamMemberRoles; import se.su.dsv.scipro.session.SciProSession; +import se.su.dsv.scipro.settings.dataobjects.SupervisorProjectNoteDisplay; import se.su.dsv.scipro.settings.dataobjects.UserProfile; import se.su.dsv.scipro.springdata.services.UserProfileService; import se.su.dsv.scipro.system.ProjectType; @@ -55,6 +57,7 @@ public class SupervisorMyProjectsPanel extends Panel { private ExportableDataPanel dataPanel; private ProjectService.Filter filter = new ProjectService.Filter(); + private IModel supervisorProjectNoteDisplayModel = new Model<>(); public SupervisorMyProjectsPanel(String id) { super(id); @@ -82,7 +85,7 @@ public class SupervisorMyProjectsPanel extends Panel { return new ListAdapterModel<>(rowModel.map(Project::getProjectParticipants)); } }); - columns.add(new ProjectNoteColumn(Model.of("Note"), LoadableDetachableModel.of(this::currentUser))); + columns.add(new ProjectNoteColumn(Model.of("Note"), LoadableDetachableModel.of(this::currentUser), supervisorProjectNoteDisplayModel)); columns.add(new UserColumn<>(Model.of("Head supervisor"), "headSupervisor.fullName", Project::getHeadSupervisor)); return columns; } @@ -105,6 +108,7 @@ public class SupervisorMyProjectsPanel extends Panel { filter.setRoles(userProfile.getDefaultProjectTeamMemberRolesFilter()); filter.setFilterSupervisor(userProfile.isDefaultSupervisorFilter()); filter.setProjectTypes(userProfile.getDefaultProjectTypeFilter()); + supervisorProjectNoteDisplayModel.setObject(userProfile.getSupervisorProjectNoteDisplay()); } private User currentUser() { @@ -148,6 +152,19 @@ public class SupervisorMyProjectsPanel extends Panel { updateProfileWithCurrentFilter(); } }); + BootstrapRadioChoice noteDisplay = new BootstrapRadioChoice<>( + "note_display", + supervisorProjectNoteDisplayModel, + List.of(SupervisorProjectNoteDisplay.values()), + new EnumChoiceRenderer<>(this)); + noteDisplay.add(new AjaxFormChoiceComponentUpdatingBehavior() { + @Override + public void onUpdate(AjaxRequestTarget target) { + target.add(dataPanel); + updateProfileWithCurrentFilter(); + } + }); + add(noteDisplay); } private void updateProfileWithCurrentFilter() { @@ -156,6 +173,7 @@ public class SupervisorMyProjectsPanel extends Panel { userProfile.setDefaultProjectTeamMemberRolesFilter(filter.getRoles()); userProfile.setDefaultSupervisorFilter(filter.isFilterSupervisor()); userProfile.setDefaultProjectTypeFilter(filter.getProjectTypes()); + userProfile.setSupervisorProjectNoteDisplay(supervisorProjectNoteDisplayModel.getObject()); profileService.save(userProfile); } } diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties index 8c2d03baf2..17f454ad8c 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/SupervisorMyProjectsPanel.utf8.properties @@ -9,3 +9,6 @@ ProjectTeamMemberRoles.CO_SUPERVISOR= Co-Supervisor ProjectStatus.ACTIVE= Active ProjectStatus.INACTIVE= Inactive ProjectStatus.COMPLETED= Completed + +SupervisorProjectNoteDisplay.COMPACT=Compact +SupervisorProjectNoteDisplay.FULL=Full -- 2.39.5 From eeedd0cd560597b37d2987b08a25be63e9b2be97 Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 3 Jul 2024 15:11:32 +0200 Subject: [PATCH 6/9] Fix test --- view/src/test/java/se/su/dsv/scipro/SciProTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/view/src/test/java/se/su/dsv/scipro/SciProTest.java b/view/src/test/java/se/su/dsv/scipro/SciProTest.java index 9a400e0086..ee39fc8b41 100755 --- a/view/src/test/java/se/su/dsv/scipro/SciProTest.java +++ b/view/src/test/java/se/su/dsv/scipro/SciProTest.java @@ -87,6 +87,7 @@ import se.su.dsv.scipro.peer.PerformReviewService; import se.su.dsv.scipro.plagiarism.PlagiarismControl; import se.su.dsv.scipro.plagiarism.urkund.UrkundService; import se.su.dsv.scipro.profiles.CurrentProfile; +import se.su.dsv.scipro.project.ProjectNoteService; import se.su.dsv.scipro.project.ProjectPeopleStatisticsService; import se.su.dsv.scipro.project.ProjectService; import se.su.dsv.scipro.project.pages.ProjectStartPage; @@ -355,6 +356,8 @@ public abstract class SciProTest { protected ExaminerTimelineService examinerTimelineService; @Mock protected NationalSubjectCategoryService nationalSubjectCategoryService; + @Mock + protected ProjectNoteService projectNoteService; protected WicketTester tester; -- 2.39.5 From 0684919bb258f95b6a75898757e1dd48778b60af Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 10 Jul 2024 13:09:44 +0200 Subject: [PATCH 7/9] Make the full view of project notes respect formatting (line breaks) --- ...ectNoteColumn$ViewAndEditNoteCellPanel.html | 1 + .../supervisor/panels/ProjectNoteColumn.java | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html index 101a8a2f5e..ef4a536c45 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html @@ -4,6 +4,7 @@
+ [view/edit note]
diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java index a0e6a2f25a..720df1bbd2 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java @@ -8,6 +8,7 @@ import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulato import org.apache.wicket.extensions.markup.html.repeater.data.table.export.AbstractExportableColumn; import org.apache.wicket.feedback.FencedFeedbackPanel; import org.apache.wicket.injection.Injector; +import org.apache.wicket.markup.html.basic.MultiLineLabel; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.TextArea; import org.apache.wicket.markup.html.panel.GenericPanel; @@ -69,11 +70,20 @@ public class ProjectNoteColumn extends AbstractExportableColumn setOutputMarkupId(true); modal.onClose(target -> target.add(this)); - IModel maxLength = supervisorProjectNoteDisplayModel.map(display -> switch (display) { - case FULL -> Integer.MAX_VALUE; - case COMPACT -> 100; + add(new MaxLengthLabel("shortened_note", getDataModel(model), Model.of(100)) { + @Override + protected void onConfigure() { + super.onConfigure(); + setVisibilityAllowed(supervisorProjectNoteDisplayModel.getObject() == SupervisorProjectNoteDisplay.COMPACT); + } + }); + add(new MultiLineLabel("full_note", getDataModel(model)) { + @Override + protected void onConfigure() { + super.onConfigure(); + setVisibilityAllowed(supervisorProjectNoteDisplayModel.getObject() == SupervisorProjectNoteDisplay.FULL); + } }); - add(new MaxLengthLabel("shortened_note", getDataModel(model), maxLength)); AjaxLink noteLink = new AjaxLink<>("view_note") { @Override -- 2.39.5 From 9f583f9e7ab71df7ffe83f32685759487e526432 Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 10 Jul 2024 13:25:23 +0200 Subject: [PATCH 8/9] Replace string concatenation with proper i18n --- .../panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html | 2 +- ...ProjectNoteColumn$ViewAndEditNoteCellPanel.utf8.properties | 2 ++ .../se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.utf8.properties diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html index ef4a536c45..7a5e924096 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.html @@ -5,7 +5,7 @@
- [view/edit note] + \ No newline at end of file diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.utf8.properties b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.utf8.properties new file mode 100644 index 0000000000..5f31ea7d1c --- /dev/null +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn$ViewAndEditNoteCellPanel.utf8.properties @@ -0,0 +1,2 @@ +note.modal.title=View/edit note for ${title} +note.view.edit=View/edit note diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java index 720df1bbd2..998da7d2d0 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java @@ -16,6 +16,7 @@ import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; +import org.apache.wicket.model.StringResourceModel; import se.su.dsv.scipro.components.LargeModalWindow; import se.su.dsv.scipro.components.MaxLengthLabel; import se.su.dsv.scipro.components.ModalWindowPlus; @@ -63,7 +64,7 @@ public class ProjectNoteColumn extends AbstractExportableColumn super(id, model); ModalWindowPlus modal = new LargeModalWindow("edit_note_modal"); - modal.setTitle(model.map(Project::getTitle).map(projectTitle -> "View/Edit note for " + projectTitle)); + modal.setTitle(new StringResourceModel("note.modal.title", this, model)); modal.setContent(componentId -> new ViewAndEditNoteForm(componentId, model)); add(modal); @@ -91,7 +92,6 @@ public class ProjectNoteColumn extends AbstractExportableColumn modal.show(target); } }; - noteLink.setBody(Model.of("View/Edit note")); add(noteLink); } } -- 2.39.5 From 0ffe1bd79a9c08783727a06b6c3b414f2ec881ef Mon Sep 17 00:00:00 2001 From: Andreas Svanberg Date: Wed, 10 Jul 2024 13:38:29 +0200 Subject: [PATCH 9/9] Rename variable to match other model variable names --- .../scipro/supervisor/panels/ProjectNoteColumn.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java index 998da7d2d0..b35962f891 100644 --- a/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java +++ b/view/src/main/java/se/su/dsv/scipro/supervisor/panels/ProjectNoteColumn.java @@ -97,16 +97,16 @@ public class ProjectNoteColumn extends AbstractExportableColumn } private class ViewAndEditNoteForm extends GenericPanel { - public ViewAndEditNoteForm(String id, IModel model) { - super(id, model); + public ViewAndEditNoteForm(String id, IModel project) { + super(id, project); - IModel note = getDataModel(model); + IModel note = getDataModel(project); - Form form = new Form<>("form", model) { + Form form = new Form<>("form", project) { @Override protected void onSubmit() { projectNoteService.setUserNote( - model.getObject(), + project.getObject(), user.getObject(), note.getObject() ); -- 2.39.5