Merge commit 'e8e8bf8a683c9fee0d9b08fe797201aa2a8a3970' into HEAD

This commit is contained in:
Jenkins 2024-05-07 21:54:09 +02:00
commit 35ba824a6c
52 changed files with 36 additions and 2007 deletions
core/src
pom.xml
view/src

@ -1,81 +0,0 @@
package se.su.dsv.scipro.forummail;
import se.su.dsv.scipro.mail.MailEvent;
import se.su.dsv.scipro.system.DomainObject;
import se.su.dsv.scipro.system.User;
import jakarta.persistence.*;
import java.util.Objects;
@Entity
@Table(name = "forum_mail")
@Inheritance(strategy = InheritanceType.JOINED)
public class ForumMail extends DomainObject {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(optional = false)
private MailEvent mailEvent;
@ManyToOne(optional = false)
private User user;
protected ForumMail(){} // JPA
protected ForumMail(final MailEvent mailEvent, final User user) {
this.mailEvent = mailEvent;
this.user = user;
}
@Override
public Long getId() {
return this.id;
}
public MailEvent getMailEvent() {
return this.mailEvent;
}
public User getUser() {
return this.user;
}
public void setId(Long id) {
this.id = id;
}
public void setMailEvent(MailEvent mailEvent) {
this.mailEvent = mailEvent;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "ForumMail(id=" + this.getId() + ", mailEvent=" + this.getMailEvent() + ", user=" + this.getUser() + ")";
}
@Override
public boolean equals(final Object o) {
if (o == this) return true;
if (!(o instanceof ForumMail)) return false;
final ForumMail other = (ForumMail) o;
return other.canEqual(this)
&& super.equals(o)
&& Objects.equals(this.getId(), other.getId())
&& Objects.equals(this.getMailEvent(), other.getMailEvent())
&& Objects.equals(this.getUser(), other.getUser());
}
protected boolean canEqual(final Object other) {
return other instanceof ForumMail;
}
@Override
public int hashCode() {
return Objects.hash(this.getId(), this.getMailEvent(), this.getUser());
}
}

@ -1,7 +0,0 @@
package se.su.dsv.scipro.forummail;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
public interface ForumMailFormatter {
String formatBody(ForumThread forumThread);
}

@ -1,82 +0,0 @@
package se.su.dsv.scipro.forummail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import jakarta.mail.MessagingException;
import jakarta.mail.Multipart;
import jakarta.mail.Part;
import java.io.IOException;
import java.util.ListIterator;
public class ForumMailFormatterImpl implements MailContentParser, ForumMailFormatter {
private static final Logger LOGGER = LoggerFactory.getLogger(ForumMailFormatterImpl.class);
public static final String DELIMITER = "---------------- REPLY ABOVE THIS LINE ----------------";
@Override
public String format(Part message) {
try {
String messageBody = checkMimeType(message);
return messageBody != null ?
substringOnDelimiter(messageBody) : "";
} catch (MessagingException | IOException e) {
LOGGER.error(e.getMessage(), e);
return "";
}
}
private String substringOnDelimiter(String str) {
int delimiter = str.indexOf(DELIMITER);
if (delimiter >= 0) {
return str.substring(0, delimiter);
}
return str;
}
protected String checkMimeType(Part message) throws
MessagingException, IOException {
if (message.isMimeType("text/*")) {
return (String) message.getContent();
}
if (message.isMimeType("multipart/*")) {
Multipart mp = (Multipart) message.getContent();
for (int i = 0; i < mp.getCount(); i++) {
Part bp = mp.getBodyPart(i);
if (bp.isMimeType("text/plain")) {
return checkMimeType(bp);
}
}
}
return null;
}
@Override
public String formatBody(ForumThread forumThread) {
StringBuilder content = new StringBuilder(DELIMITER);
content.append("\n\n");
int nesting = 0;
ListIterator<ForumPost> posts = forumThread.getPosts().listIterator(forumThread.getPostCount());
while (posts.hasPrevious()) {
ForumPost forumPost = posts.previous();
String before = ">".repeat(nesting) + (nesting > 0 ? ' ' : "");
content.append(before).append(getPosterName(forumPost)).append(":\n");
content.append(before).append(forumPost.getContent()).append('\n');
content.append(before).append('\n');
nesting++;
}
content.append("\n\n");
return content.toString();
}
private String getPosterName(ForumPost forumPost) {
if (forumPost.getPostedBy() != null) {
return forumPost.getPostedBy().getFullName();
}
else {
return "SciPro";
}
}
}

@ -1,54 +0,0 @@
package se.su.dsv.scipro.forummail;
import se.su.dsv.scipro.forum.dataobjects.GroupThread;
import se.su.dsv.scipro.mail.MailEvent;
import se.su.dsv.scipro.system.User;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.util.Objects;
@Entity
@Table(name = "forum_mail_group")
public class ForumMailGroup extends ForumMail {
@ManyToOne(optional = false)
@JoinColumn(name = "group_thread_id")
private GroupThread groupThread;
protected ForumMailGroup() {}
public ForumMailGroup(final MailEvent mailEvent, final User user, final GroupThread groupThread) {
super(mailEvent, user);
this.groupThread = groupThread;
}
public void setGroupThread(GroupThread groupThread) {
this.groupThread = groupThread;
}
public GroupThread getGroupThread() {
return groupThread;
}
@Override
public boolean equals(final Object o) {
if (o == this) return true;
if (!(o instanceof ForumMailGroup)) return false;
final ForumMailGroup other = (ForumMailGroup) o;
return other.canEqual(this)
&& super.equals(o)
&& Objects.equals(this.getGroupThread(), other.getGroupThread());
}
@Override
protected boolean canEqual(final Object other) {
return other instanceof ForumMailGroup;
}
@Override
public int hashCode() {
return Objects.hashCode(this.getGroupThread());
}
}

@ -1,27 +0,0 @@
package se.su.dsv.scipro.forummail;
import java.util.Collection;
public class ForumMailMessage {
private final String inReplyTo;
private final String mailBody;
private final Collection<MailAttachment> attachments;
public ForumMailMessage(String inReplyTo, String mailBody, final Collection<MailAttachment> attachments) {
this.inReplyTo = inReplyTo;
this.mailBody = mailBody;
this.attachments = attachments;
}
public String getInReplyTo() {
return inReplyTo;
}
public String getMailBody() {
return mailBody;
}
public Collection<MailAttachment> getAttachments() {
return attachments;
}
}

@ -1,32 +0,0 @@
package se.su.dsv.scipro.forummail;
import com.google.inject.AbstractModule;
import com.google.inject.multibindings.Multibinder;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsServiceImpl;
import se.su.dsv.scipro.mail.MailEventService;
import se.su.dsv.scipro.springdata.serviceimpls.UserProfileServiceImpl;
import se.su.dsv.scipro.springdata.services.UserProfileService;
import se.su.dsv.scipro.system.Lifecycle;
public class ForumMailModule extends AbstractModule {
static final String FORUM_RELATIVE_URL = "forum/mail";
@Override
protected void configure() {
requireBinding(MailEventService.class);
bind(ForumMailRepository.class).to(ForumMailRepositoryImpl.class);
bind(ForumMailService.class).to(ForumMailServiceImpl.class);
bind(ForumMailSettingsService.class).to(ForumMailSettingsServiceImpl.class);
bind(ForumMailReader.class).to(IMAPReader.class);
bind(MailContentParser.class).to(ForumMailFormatterImpl.class);
bind(ForumMailFormatter.class).to(ForumMailFormatterImpl.class);
bind(SendForumMail.class).asEagerSingleton();
bind(UserProfileService.class).to(UserProfileServiceImpl.class);
bind(MailHandler.class).to(ReadForumMail.class);
bind(GeneralSystemSettingsService.class).to(GeneralSystemSettingsServiceImpl.class);
Multibinder.newSetBinder(binder(), Lifecycle.class).addBinding()
.to(ForumMailWorkerSchedule.class);
}
}

@ -1,50 +0,0 @@
package se.su.dsv.scipro.forummail;
import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
import se.su.dsv.scipro.mail.MailEvent;
import se.su.dsv.scipro.system.User;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.util.Objects;
@Entity
@Table(name = "forum_mail_project")
public class ForumMailProject extends ForumMail {
@ManyToOne(optional = false)
@JoinColumn(name = "project_thread_id")
private ProjectThread projectThread;
protected ForumMailProject() {}
public ForumMailProject(final MailEvent mailEvent, final User user, final ProjectThread projectThread) {
super(mailEvent, user);
this.projectThread = projectThread;
}
public ProjectThread getProjectThread() {
return projectThread;
}
@Override
public boolean equals(final Object o) {
if (o == this) return true;
if (!(o instanceof ForumMailProject)) return false;
final ForumMailProject other = (ForumMailProject) o;
return other.canEqual(this)
&& super.equals(o)
&& Objects.equals(this.getProjectThread(), other.getProjectThread());
}
@Override
protected boolean canEqual(final Object other) {
return other instanceof ForumMailProject;
}
@Override
public int hashCode() {
return Objects.hashCode(this.getProjectThread());
}
}

@ -1,5 +0,0 @@
package se.su.dsv.scipro.forummail;
public interface ForumMailReader {
void accept(MailHandler mailHandler);
}

@ -1,8 +0,0 @@
package se.su.dsv.scipro.forummail;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ForumMailRepository extends JpaRepository<ForumMail, Long> {
ForumMail findByMessageID(String messageID);
}

@ -1,19 +0,0 @@
package se.su.dsv.scipro.forummail;
import se.su.dsv.scipro.system.GenericRepo;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.persistence.EntityManager;
public class ForumMailRepositoryImpl extends GenericRepo<ForumMail, Long> implements ForumMailRepository {
@Inject
public ForumMailRepositoryImpl(final Provider<EntityManager> em) {
super(em, ForumMail.class, QForumMail.forumMail);
}
@Override
public ForumMail findByMessageID(String messageID) {
return findOne(QForumMail.forumMail.mailEvent.messageID.eq(messageID));
}
}

@ -1,6 +0,0 @@
package se.su.dsv.scipro.forummail;
public interface ForumMailService {
ForumMail findByMessageID(String messageID);
}

@ -1,18 +0,0 @@
package se.su.dsv.scipro.forummail;
import jakarta.inject.Inject;
public class ForumMailServiceImpl implements ForumMailService {
private ForumMailRepository forumMailRepository;
@Inject
public ForumMailServiceImpl(ForumMailRepository forumMailRepository) {
this.forumMailRepository = forumMailRepository;
}
@Override
public ForumMail findByMessageID(String messageID) {
return forumMailRepository.findByMessageID(messageID);
}
}

@ -1,129 +0,0 @@
package se.su.dsv.scipro.forummail;
import se.su.dsv.scipro.system.DomainObject;
import jakarta.persistence.Basic;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.Objects;
@Table(name="forum_mail_settings")
@Entity
public class ForumMailSettings extends DomainObject {
@Id
private Long id;
@Basic
private String host;
@Basic
private int port;
@Basic
private String replyAddress;
@Basic
private String password;
@Basic
private String account;
@Basic(optional = false)
private boolean enabled = false;
public ForumMailSettings(long id) {
this.id = id;
}
public ForumMailSettings() {
}
public boolean isEnabled() {
return enabled;
}
public String getReplyAddress() {
return replyAddress;
}
public String getAccount() {
return account;
}
@Override
public Long getId() {
return this.id;
}
public String getHost() {
return this.host;
}
public int getPort() {
return this.port;
}
public String getPassword() {
return this.password;
}
public void setId(Long id) {
this.id = id;
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
public void setReplyAddress(String replyAddress) {
this.replyAddress = replyAddress;
}
public void setPassword(String password) {
this.password = password;
}
public void setAccount(String account) {
this.account = account;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
public String toString() {
return "ForumMailSettings(id=" + this.getId() + ", host=" + this.getHost() + ", port=" + this.getPort() + ", replyAddress=" + this.getReplyAddress() + ", password=" + this.getPassword() + ", account=" + this.getAccount() + ", enabled=" + this.isEnabled() + ")";
}
@Override
public boolean equals(final Object o) {
if (o == this) return true;
if (!(o instanceof ForumMailSettings)) return false;
final ForumMailSettings other = (ForumMailSettings) o;
return other.canEqual(this)
&& super.equals(o)
&& Objects.equals(this.getId(), other.getId())
&& Objects.equals(this.getHost(), other.getHost())
&& this.getPort() == other.getPort()
&& Objects.equals(this.getReplyAddress(), other.getReplyAddress())
&& Objects.equals(this.getPassword(), other.getPassword())
&& Objects.equals(this.getAccount(), other.getAccount())
&& this.isEnabled() == other.isEnabled();
}
protected boolean canEqual(final Object other) {
return other instanceof ForumMailSettings;
}
@Override
public int hashCode() {
return Objects.hash(this.getId(), this.getHost(), this.getPort(), this.getReplyAddress(), this.getPassword(), this.getAccount(), this.isEnabled());
}
}

@ -1,8 +0,0 @@
package se.su.dsv.scipro.forummail;
import se.su.dsv.scipro.system.GenericService;
public interface ForumMailSettingsService extends GenericService<ForumMailSettings, Long> {
ForumMailSettings getSettings();
}

@ -1,29 +0,0 @@
package se.su.dsv.scipro.forummail;
import com.google.inject.persist.Transactional;
import se.su.dsv.scipro.system.AbstractServiceImpl;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.persistence.EntityManager;
public class ForumMailSettingsServiceImpl extends AbstractServiceImpl<ForumMailSettings,Long> implements ForumMailSettingsService {
public static final long INSTANCE_ID = 1L;
@Inject
public ForumMailSettingsServiceImpl(Provider<EntityManager> em) {
super(em, ForumMailSettings.class, QForumMailSettings.forumMailSettings);
}
@Override
@Transactional
public ForumMailSettings getSettings() {
ForumMailSettings settings = findOne(INSTANCE_ID);
if (settings == null) {
settings = new ForumMailSettings(INSTANCE_ID);
save(settings);
}
return settings;
}
}

@ -1,26 +0,0 @@
package se.su.dsv.scipro.forummail;
import se.su.dsv.scipro.workerthreads.AbstractWorker;
import jakarta.inject.Inject;
public class ForumMailWorker extends AbstractWorker {
private final ForumMailReader mailReader;
private final MailHandler mailHandler;
private final ForumMailSettingsService forumMailSettingsService;
@Inject
public ForumMailWorker(ForumMailReader mailReader, MailHandler mailHandler, ForumMailSettingsService forumMailSettingsService) {
this.mailReader = mailReader;
this.mailHandler = mailHandler;
this.forumMailSettingsService = forumMailSettingsService;
}
@Override
protected void doWork() {
if (forumMailSettingsService.getSettings().isEnabled()){
mailReader.accept(mailHandler);
}
}
}

@ -1,31 +0,0 @@
package se.su.dsv.scipro.forummail;
import se.su.dsv.scipro.system.Lifecycle;
import se.su.dsv.scipro.workerthreads.Scheduler;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import jakarta.inject.Singleton;
import java.util.concurrent.TimeUnit;
@Singleton
public class ForumMailWorkerSchedule implements Lifecycle {
private final Scheduler scheduler;
private final Provider<ForumMailWorker> forumMailWorker;
@Inject
public ForumMailWorkerSchedule(final Scheduler scheduler, final Provider<ForumMailWorker> forumMailWorker) {
this.scheduler = scheduler;
this.forumMailWorker = forumMailWorker;
}
@Override
public void start() {
scheduler.schedule("Forum mail worker").runBy(forumMailWorker).every(10, TimeUnit.MINUTES);
}
@Override
public void stop() {
}
}

@ -1,97 +0,0 @@
package se.su.dsv.scipro.forummail;
import jakarta.mail.Flags;
import jakarta.mail.Folder;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.Multipart;
import jakarta.mail.Part;
import jakarta.mail.Session;
import jakarta.mail.Store;
import jakarta.mail.internet.MimeMessage;
import jakarta.mail.search.FlagTerm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.inject.Inject;
import java.io.IOException;
import java.util.*;
public class IMAPReader implements ForumMailReader {
private static final Logger LOGGER = LoggerFactory.getLogger(IMAPReader.class);
private final ForumMailSettingsService forumMailSettingsService;
private final MailContentParser contentParser;
@Inject
public IMAPReader(ForumMailSettingsService forumMailSettingsService,
MailContentParser contentParser) {
this.forumMailSettingsService = forumMailSettingsService;
this.contentParser = contentParser;
}
@Override
public void accept(MailHandler mailHandler) {
try {
try (Store store = connectToIMAP(); Folder folder = openFolder(store)) {
List<ForumMailMessage> forumMailMessages = messagesAsList(folder.search(unreadOnly()));
for (ForumMailMessage forumMailMessage : forumMailMessages) {
mailHandler.accept(forumMailMessage);
}
}
} catch (MessagingException | IOException e) {
LOGGER.info("Something went wrong while reading e-mail: ", e);
}
}
private FlagTerm unreadOnly() {
return new FlagTerm(new Flags(Flags.Flag.SEEN), false);
}
private Store connectToIMAP() throws MessagingException {
Properties props = System.getProperties();
props.setProperty("mail.store.protocol", "imaps");
Session session = Session.getInstance(props);
Store store = session.getStore("imaps");
ForumMailSettings settings = forumMailSettingsService.getSettings();
store.connect(settings.getHost(), settings.getPort(),
settings.getAccount(), settings.getPassword());
return store;
}
private Folder openFolder(Store store) throws MessagingException {
Folder folder = store.getFolder("inbox");
if (!folder.isOpen()) {
folder.open(Folder.READ_WRITE);
}
return folder;
}
private List<ForumMailMessage> messagesAsList(Message[] messages) throws MessagingException, IOException {
List<ForumMailMessage> messageList = new ArrayList<>();
for (Message message : messages) {
MimeMessage mime = (MimeMessage) message;
String inReplyTo = mime.getHeader("In-Reply-To", null);
if (inReplyTo != null) {
Collection<MailAttachment> attachments = getAttachments(message);
messageList.add(new ForumMailMessage(inReplyTo, contentParser.format(message), attachments));
}
}
return messageList;
}
private Collection<MailAttachment> getAttachments(Part part) throws MessagingException, IOException {
Collection<MailAttachment> attachments = new ArrayList<>();
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition())) {
attachments.add(new MailAttachment(part));
}
if (part.isMimeType("multipart/*")) {
Multipart multipart = (Multipart) part.getContent();
for (int i = 0; i < multipart.getCount(); i++) {
attachments.addAll(getAttachments(multipart.getBodyPart(i)));
}
}
return attachments;
}
}

@ -1,50 +0,0 @@
package se.su.dsv.scipro.forummail;
import jakarta.mail.MessagingException;
import jakarta.mail.Part;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.function.Function;
public class MailAttachment {
private final Part part;
public MailAttachment(final Part part) {
this.part = part;
}
public String getFileName() {
try {
return part.getFileName();
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
public String getContentType() {
try {
return part.getContentType();
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
public long getSize() {
try {
return part.getSize();
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
public <T> T handleData(Function<InputStream, T> handler) {
try (InputStream is = part.getInputStream()) {
return handler.apply(is);
} catch (MessagingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}

@ -1,8 +0,0 @@
package se.su.dsv.scipro.forummail;
import jakarta.mail.Part;
public interface MailContentParser {
String format(Part message);
}

@ -1,5 +0,0 @@
package se.su.dsv.scipro.forummail;
public interface MailHandler {
void accept(ForumMailMessage mail);
}

@ -1,95 +0,0 @@
package se.su.dsv.scipro.forummail;
import com.google.inject.persist.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.su.dsv.scipro.file.FileUpload;
import se.su.dsv.scipro.forum.Attachment;
import se.su.dsv.scipro.forum.GroupForumService;
import se.su.dsv.scipro.forum.ProjectForumService;
import se.su.dsv.scipro.system.User;
import jakarta.inject.Inject;
import java.io.InputStream;
import java.util.*;
import java.util.function.Function;
public class ReadForumMail implements MailHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ReadForumMail.class);
private final GroupForumService groupForumService;
private final ProjectForumService projectForumService;
private final ForumMailService forumMailService;
@Inject
public ReadForumMail(final GroupForumService groupForumService, final ProjectForumService projectForumService, final ForumMailService forumMailService) {
this.groupForumService = groupForumService;
this.projectForumService = projectForumService;
this.forumMailService = forumMailService;
}
@Override
@Transactional
public void accept(final ForumMailMessage mail) {
final ForumMail allowed = forumMailService.findByMessageID(mail.getInReplyTo());
if (allowed != null) {
Set<Attachment> attachments = new HashSet<>();
for (MailAttachment mailAttachment : mail.getAttachments()){
attachments.add(Attachment.newUpload(new MailUpload(mailAttachment, allowed.getUser())));
}
if (allowed instanceof ForumMailGroup) {
groupForumService.createReply(
((ForumMailGroup) allowed).getGroupThread(),
allowed.getUser(),
mail.getMailBody(),
attachments);
}
else if (allowed instanceof ForumMailProject) {
projectForumService.createReply(
((ForumMailProject) allowed).getProjectThread(),
allowed.getUser(),
mail.getMailBody(),
attachments);
}
LOGGER.info("New reply through mail: {}", allowed);
}
}
private static class MailUpload implements FileUpload {
private final MailAttachment attachment;
private final User uploader;
public MailUpload(final MailAttachment attachment, final User uploader) {
this.attachment = attachment;
this.uploader = uploader;
}
@Override
public String getFileName() {
return attachment.getFileName();
}
@Override
public String getContentType() {
return attachment.getContentType();
}
@Override
public User getUploader() {
return uploader;
}
@Override
public long getSize() {
return attachment.getSize();
}
@Override
public <T> T handleData(final Function<InputStream, T> handler) {
return attachment.handleData(handler);
}
}
}

@ -1,193 +0,0 @@
package se.su.dsv.scipro.forummail;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import se.su.dsv.scipro.file.FileReference;
import se.su.dsv.scipro.forum.NewGroupForumReplyEvent;
import se.su.dsv.scipro.forum.NewProjectForumReplyEvent;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.forum.dataobjects.GroupThread;
import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
import se.su.dsv.scipro.group.Group;
import se.su.dsv.scipro.mail.MailEvent;
import se.su.dsv.scipro.mail.MailEventService;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.springdata.services.UserProfileService;
import se.su.dsv.scipro.system.User;
import jakarta.inject.Inject;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class SendForumMail {
private final ForumMailRepository forumMailRepository;
private final ForumMailSettingsService mailSettingsService;
private final MailEventService mailEventService;
private final ForumMailFormatter formatter;
private final GeneralSystemSettingsService generalSystemSettings;
private final UserProfileService userProfileService;
SendForumMail(
ForumMailRepository forumMailRepository,
ForumMailSettingsService mailSettingsService,
MailEventService mailEventService,
ForumMailFormatter formatter,
GeneralSystemSettingsService generalSystemSettings,
UserProfileService userProfileService)
{
this.forumMailRepository = forumMailRepository;
this.mailSettingsService = mailSettingsService;
this.mailEventService = mailEventService;
this.formatter = formatter;
this.generalSystemSettings = generalSystemSettings;
this.userProfileService = userProfileService;
}
@Inject
public SendForumMail(
ForumMailRepository forumMailRepository,
ForumMailSettingsService mailSettingsService,
MailEventService mailEventService,
ForumMailFormatter formatter,
GeneralSystemSettingsService generalSystemSettings,
EventBus eventBus,
UserProfileService userProfileService)
{
this(forumMailRepository, mailSettingsService, mailEventService, formatter, generalSystemSettings, userProfileService);
eventBus.register(this);
}
@Subscribe
public void newProjectForumReply(NewProjectForumReplyEvent event) {
if (mailSettingsService.getSettings().isEnabled()) {
Project project = event.getProjectThread().getProject();
List<User> recipients = Stream.concat(Stream.of(project.getHeadSupervisor()), project.getCoSupervisors().stream())
.filter(user -> !Objects.equals(event.getReply().getPostedBy(), user))
.filter(userProfileService::isReceiveForumMail)
.toList();
Set<FileReference> attachments = event.getReply().getAttachments();
MailData data = new MailData(event.getProjectThread().getForumThread(), attachments);
final String subject = subject(event.getProjectThread());
final String footer = footer(event.getProjectThread());
final String url = url(event.getProjectThread());
String mailContent = generateMailContent(data, footer, url);
sendMails(subject, mailContent, recipients, (mailEvent, recipient) ->
forumMailRepository.save(new ForumMailProject(mailEvent, recipient, event.getProjectThread())));
}
}
@Subscribe
public void newGroupForumReply(NewGroupForumReplyEvent event) {
if (mailSettingsService.getSettings().isEnabled()) {
Group group = event.getGroupThread().getGroup();
List<User> recipients = group.getProjects().stream()
.flatMap(project -> Stream.concat(Stream.of(project.getHeadSupervisor()), project.getCoSupervisors().stream()))
.filter(user -> !Objects.equals(event.getReply().getPostedBy(), user))
.filter(userProfileService::isReceiveForumMail)
.toList();
Set<FileReference> attachments = event.getReply().getAttachments();
MailData data = new MailData(event.getGroupThread().getForumThread(), attachments);
final String subject = subject(event.getGroupThread());
final String footer = footer(event.getGroupThread());
final String url = url(event.getGroupThread());
String mailContent = generateMailContent(data, footer, url);
sendMails(subject, mailContent, recipients, (mailEvent, recipient) ->
forumMailRepository.save(new ForumMailGroup(mailEvent, recipient, event.getGroupThread())));
}
}
private void sendMails(String subject, String body, Iterable<User> recipients, BiConsumer<MailEvent, User> f) {
for (User recipient : recipients) {
MailEvent mailEvent = generateMail(recipient, subject, body);
f.accept(mailEvent, recipient);
}
}
private static class MailData {
private final ForumThread thread;
private final Set<FileReference> attachments;
MailData(ForumThread thread, Set<FileReference> attachments) {
this.thread = thread;
this.attachments = attachments;
}
}
private String generateMailContent(MailData data, CharSequence footer, CharSequence url) {
StringBuilder content = new StringBuilder();
content.append(formatter.formatBody(data.thread));
if (!data.attachments.isEmpty()) {
content.append("\n\n");
content.append("Attachments: (go to project forum to download)\n");
data.attachments.forEach(attachment -> content.append(attachment.getName()).append("\n"));
}
content.append(footer).append("\n");
content.append("* View forum in SciPro here: ").append(url);
return content.toString();
}
private MailEvent generateMail(User recipient, String subject, String body) {
MailEvent mail = new MailEvent();
mail.getRecipients().add(recipient);
mail.setMessageBody(body);
mail.setSubject(subject);
ForumMailSettings settings = mailSettingsService.getSettings();
mail.setFromEmail(settings.getReplyAddress());
mail.setFromName(settings.getAccount());
return mailEventService.save(mail);
}
private String subject(ProjectThread a) {
if (a.getForumThread().getPostCount() == 1)
return String.format("Forum post: %s", a.getForumThread().getSubject());
else return String.format("Forum reply: %s", a.getForumThread().getSubject());
}
private String footer(ProjectThread a) {
return String.format("* Project: %s", a.getProject().getTitle());
}
private String url(ProjectThread a) {
return String.format("%s/project/?id=%d", baseUrl(), a.getId());
}
private String baseUrl() {
return generalSystemSettings.getGeneralSystemSettingsInstance().getSciproURL() + ForumMailModule.FORUM_RELATIVE_URL;
}
private String subject(GroupThread a) {
if (a.getForumThread().getPostCount() == 1)
return String.format("Group forum: %s", a.getForumThread().getSubject());
else return String.format("Group forum reply: %s", a.getForumThread().getSubject());
}
private String footer(GroupThread a) {
String projects = a.getGroup()
.getProjects()
.stream()
.map(Project::getTitle)
.collect(Collectors.joining(", "));
return String.format("* Group: %s\n* Projects: %s", a.getGroup().getTitle(), projects);
}
private String url(GroupThread a) {
return String.format("%s/group/?id=%d", baseUrl(), a.getId());
}
}

@ -19,13 +19,13 @@ public class ProjectRepoImpl extends GenericRepo<Project, Long> implements Proje
@Override
public List<User> findMultipleAuthors(Collection<Project> projects) {
TypedQuery<User> query = em().createQuery("""
from User u where u in (
select participant
from User u where u.id in (
select participant.id
from Project project
join project.projectParticipants as participant
where project in :projects
group by participant
having count(participant) > 1)
group by participant.id
having count(participant.id) > 1)
""", User.class);
query.setParameter("projects", projects);
return query.getResultList();

@ -49,6 +49,7 @@ public class UserProfile extends DomainObject {
@ManyToMany
@JoinTable(
name = "user_profile_ProjectType",
joinColumns = {@JoinColumn(name = "user_profile_id")}
)
private Collection<ProjectType> defaultProjectTypeFilter = new ArrayList<>();
@ -57,9 +58,6 @@ public class UserProfile extends DomainObject {
@Enumerated(EnumType.STRING)
private Roles selectedRole;
@Basic(optional = false)
private boolean receiveForumMail = false;
@Override
public Long getId() {
return this.id;
@ -105,10 +103,6 @@ public class UserProfile extends DomainObject {
return this.selectedRole;
}
public boolean isReceiveForumMail() {
return this.receiveForumMail;
}
public void setId(Long id) {
this.id = id;
}
@ -153,13 +147,9 @@ public class UserProfile extends DomainObject {
this.selectedRole = selectedRole;
}
public void setReceiveForumMail(boolean receiveForumMail) {
this.receiveForumMail = receiveForumMail;
}
@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() + ", receiveForumMail=" + this.isReceiveForumMail() + ")";
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() + ")";
}
@Override

@ -32,18 +32,6 @@ public class UserProfileServiceImpl extends AbstractServiceImpl<UserProfile, Lon
return profile;
}
@Override
public boolean isReceiveForumMail(User user) {
return findByUser(user).isReceiveForumMail();
}
@Override
public void setReceiveForumMail(User user, boolean receive) {
UserProfile profile = findByUser(user);
profile.setReceiveForumMail(receive);
save(profile);
}
@Override
public List<User> findUsersWithMailCompilation() {
return createQuery().where(QUserProfile.userProfile.mailCompilation.isTrue()).select(QUserProfile.userProfile.user).fetch();

@ -10,8 +10,6 @@ import java.util.List;
public interface UserProfileService extends GenericService<UserProfile, Long> {
UserProfile findByUser(User user);
boolean isReceiveForumMail(User user);
void setReceiveForumMail(User user, boolean receive);
List<User> findUsersWithMailCompilation();
Roles findSelectedRole(User user);
void setSelectedRole(User user, Roles role);

@ -0,0 +1,6 @@
ALTER TABLE `user_profile` DROP COLUMN `receiveForumMail`;
DROP TABLE `forum_mail_settings`;
DROP TABLE `forum_mail_project`;
DROP TABLE `forum_mail_group`;
DROP TABLE `forum_mail_reviewer`;
DROP TABLE `forum_mail`;

@ -1,156 +0,0 @@
package se.su.dsv.scipro.forummail;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.system.User;
import jakarta.mail.MessagingException;
import jakarta.mail.Multipart;
import jakarta.mail.Part;
import jakarta.mail.internet.MimeBodyPart;
import jakarta.mail.internet.MimeMultipart;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class ForumMailFormatterImplTest {
ForumMailFormatterImpl parser;
@BeforeEach
public void setUp() throws Exception {
parser = new ForumMailFormatterImpl();
}
@Test
public void message_without_delimiter_dont_substring() throws Exception {
String noDelimiter = "string without delimiter";
Part message = createMessage(noDelimiter, "text/plain");
assertEquals(noDelimiter, parser.format(message));
}
@Test
public void substring_on_delimiter() throws Exception {
String body = stringWithDelimiter("This is the reply", "This is the old message");
Part message = createMessage(body, "text/plain");
assertEquals("This is the reply", parser.format(message));
}
@Test
public void unknown_mime_type_returns_null() throws Exception {
Part message = createMessage("message body", "foo/bar");
assertEquals("", parser.format(message));
}
@Test
public void multipart_return_text_plain_if_available() throws Exception {
String plainText = "plain text";
Part message = createMultipartMessage(plainText, 2);
assertEquals(plainText, parser.format(message));
}
@Test
public void empty_string_when_no_text_plain_is_available() throws Exception {
assertEquals("", parser.format(createMultipartMessage("text", 1)));
}
@Test
public void empty_string_on_messaging_exception() throws Exception {
assertEquals("", parser.format(createInvalidMessage()));
}
@Test
public void composed_content_starts_with_delimiter() {
assertThat(parser.formatBody(newThread()), startsWith(ForumMailFormatterImpl.DELIMITER));
}
@Test
public void includes_the_content_of_every_post_in_the_thread() {
ForumThread forumThread = newThread();
String content = parser.formatBody(forumThread);
for (ForumPost post : forumThread.getPosts()) {
assertThat(content, containsString(post.getContent()));
}
}
@Test
public void includes_the_poster_of_every_post_in_the_thread() {
ForumThread forumThread = newThread();
String content = parser.formatBody(forumThread);
for (ForumPost forumPost : forumThread.getPosts()) {
assertThat(content, containsString(forumPost.getPostedBy().getFullName()));
}
}
private ForumThread newThread() {
ForumThread forumThread = mock(ForumThread.class);
List<ForumPost> forumPosts = Arrays.asList(createPost("My first post"), createPost("My second post"));
when(forumThread.getPosts()).thenReturn(forumPosts);
when(forumThread.getPostCount()).thenReturn(forumPosts.size());
return forumThread;
}
private ForumPost createPost(String content) {
ForumPost forumPost = new ForumPost();
forumPost.setContent(content);
forumPost.setPostedBy(User.builder().firstName("Bob").lastName("Tester").emailAddress("bob@example.com").build());
return forumPost;
}
private String stringWithDelimiter(String first, String second) {
return first + ForumMailFormatterImpl.DELIMITER + second;
}
private Part createMessage(String body, String type) throws MessagingException {
Part part = new MimeBodyPart();
part.setContent(body, type);
part.setHeader("Content-Type", type);
return part;
}
private Part createMultipartMessage(String plain, int parts) throws MessagingException {
Part part = new MimeBodyPart();
Multipart mp = mock(Multipart.class);
when(mp.getCount()).thenReturn(parts);
MimeBodyPart textPlainPart = createBodyPart(plain, "text/plain");
MimeBodyPart multi = createBodyPart("multi", "multipart/mixed");
when(mp.getBodyPart(0)).thenReturn(multi);
lenient().when(mp.getBodyPart(1)).thenReturn(textPlainPart);
part.setContent(mp, "multipart/alternative");
part.setHeader("Content-Type", "multipart/alternative");
return part;
}
private Part createInvalidMessage() throws MessagingException {
MimeBodyPart part = new MimeBodyPart();
MimeMultipart mp = new MimeMultipart();
mp.addBodyPart(new MimeBodyPart());
part.setContent(mp);
part.setHeader("Content-Type", "multipart/alternative");
return part;
}
private MimeBodyPart createBodyPart(String plain, String contentType) throws MessagingException {
MimeBodyPart textPlainPart = new MimeBodyPart();
textPlainPart.setText(plain);
textPlainPart.setHeader("Content-Type", contentType);
return textPlainPart;
}
}

@ -1,47 +0,0 @@
package se.su.dsv.scipro.forummail;
import com.google.inject.AbstractModule;
import com.google.inject.Module;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.file.FileModule;
import se.su.dsv.scipro.file.FileStore;
import se.su.dsv.scipro.forum.GroupForumService;
import se.su.dsv.scipro.forum.ProjectForumService;
import se.su.dsv.scipro.mail.MailEventService;
import se.su.dsv.scipro.test.GuiceTest;
import se.su.dsv.scipro.test.InMemoryFileStore;
import se.su.dsv.scipro.workerthreads.Scheduler;
import se.su.dsv.scipro.workerthreads.WorkerDataService;
@ExtendWith(MockitoExtension.class)
public abstract class ForumMailModuleTest extends GuiceTest {
@Mock
protected MailEventService mailEventService;
@Mock
protected GroupForumService groupForumService;
@Mock
protected ProjectForumService projectForumService;
@Mock
protected Scheduler scheduler;
@Mock
protected WorkerDataService workerDataService;
@Override
protected Module moduleUnderTest() {
return new AbstractModule() {
@Override
protected void configure() {
install(new ForumMailModule());
install(new FileModule());
bind(FileStore.class).to(InMemoryFileStore.class);
bind(MailEventService.class).toInstance(mailEventService);
bind(GroupForumService.class).toInstance(groupForumService);
bind(ProjectForumService.class).toInstance(projectForumService);
bind(Scheduler.class).toInstance(scheduler);
bind(WorkerDataService.class).toInstance(workerDataService);
}
};
}
}

@ -1,63 +0,0 @@
package se.su.dsv.scipro.forummail;
import org.junit.jupiter.api.Test;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
import se.su.dsv.scipro.mail.MailEvent;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.DegreeType;
import se.su.dsv.scipro.system.ProjectType;
import se.su.dsv.scipro.system.User;
import jakarta.inject.Inject;
import java.time.LocalDate;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ForumMailServiceImplTest extends ForumMailModuleTest {
@Inject
ForumMailService mailService;
@Test
public void find_by_message_id() {
String messageID = "abc123";
ForumMail forumMail = createForumMail(messageID);
assertEquals(forumMail, mailService.findByMessageID(messageID));
}
private ForumMail createForumMail(String messageID) {
return save(new ForumMailProject(
createMailEvent(messageID),
createUser(),
createForumThread())
);
}
private User createUser() {
return save(User.builder().firstName("first").lastName("last")
.emailAddress("first@last.com").build());
}
private ProjectThread createForumThread() {
ForumThread forumThread = new ForumThread();
forumThread.setSubject("thread subject");
save(forumThread);
ProjectType bachelor = save(new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor"));
User user = save(User.builder().firstName("Bob").lastName("Example").emailAddress("bob@example.com").build());
Project project = save(Project.builder().title("Project").projectType(bachelor).startDate(LocalDate.now()).headSupervisor(user).build());
ProjectThread projectThread = new ProjectThread();
projectThread.setForumThread(forumThread);
projectThread.setProject(project);
return save(projectThread);
}
private MailEvent createMailEvent(String messageID) {
MailEvent mailEvent = new MailEvent();
mailEvent.setMessageID(messageID);
mailEvent.setMessageBody("body");
mailEvent.setSubject("subject");
return save(mailEvent);
}
}

@ -1,25 +0,0 @@
package se.su.dsv.scipro.forummail;
import org.junit.jupiter.api.Test;
import jakarta.inject.Inject;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class ForumMailSettingsServiceImplTest extends ForumMailModuleTest {
@Inject
ForumMailSettingsServiceImpl forumMailSettingsService;
@Test
public void finds_existing_settings() {
ForumMailSettings settings = save(new ForumMailSettings(1L));
assertEquals(settings, forumMailSettingsService.getSettings());
}
@Test
public void saves_new_one_if_no_one_exists() {
assertNotNull(forumMailSettingsService.getSettings());
}
}

@ -1,32 +0,0 @@
package se.su.dsv.scipro.forummail;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class ForumMailWorkerTest {
@Mock
private ForumMailReader forumMailReader;
@Mock
private MailHandler mailHandler;
@Mock
private ForumMailSettingsService forumMailSettingsService;
@InjectMocks
private ForumMailWorker worker;
@Test
public void process_mails() {
ForumMailSettings settings = new ForumMailSettings();
settings.setEnabled(true);
when(forumMailSettingsService.getSettings()).thenReturn(settings);
worker.doWork();
verify(forumMailReader).accept(mailHandler);
}
}

@ -1,114 +0,0 @@
package se.su.dsv.scipro.forummail;
import jakarta.mail.Part;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.forum.dataobjects.GroupThread;
import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
import se.su.dsv.scipro.group.Group;
import se.su.dsv.scipro.mail.MailEvent;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.system.DegreeType;
import se.su.dsv.scipro.system.ProjectType;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.test.ObjectMother;
import jakarta.inject.Inject;
import java.time.LocalDate;
import java.util.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class ReadForumMailTest extends ForumMailModuleTest {
@Inject
private ReadForumMail readForumMail;
private ForumMailMessage message;
private GroupThread thread;
@BeforeEach
public void setUp() throws Exception {
message = new ForumMailMessage("replyToID", "This is the body", Collections.singleton(new MailAttachment(mock(Part.class))));
thread = createGroupThread();
}
@Test
public void creates_group_thread_reply_if_found_message_matches_mail_event_message_id() {
ForumMailGroup forumMailGroup = createForumMailGroup();
readForumMail.accept(message);
verify(groupForumService).createReply(eq(forumMailGroup.getGroupThread()), eq(forumMailGroup.getUser()), eq(message.getMailBody()), anySet());
}
@Test
public void creates_projct_thread_reply_if_found_message_matches_mail_event_message_id() {
ForumMailProject forumMailGroup = createForumMailProject();
readForumMail.accept(message);
verify(projectForumService).createReply(eq(forumMailGroup.getProjectThread()), eq(forumMailGroup.getUser()), eq(message.getMailBody()), anySet());
}
@Test
public void no_mails_exists_should_not_create_thread_reply() {
readForumMail.accept(message);
verify(groupForumService, never()).createReply(any(GroupThread.class), any(User.class), anyString(), anySet());
}
private ForumThread createThread() {
ForumThread forumThread = new ForumThread();
forumThread.setSubject("Subject");
return save(forumThread);
}
private GroupThread createGroupThread() {
Group group = createGroup();
ForumThread forumThread1 = createThread();
GroupThread groupThread = new GroupThread();
groupThread.setForumThread(forumThread1);
groupThread.setGroup(group);
return save(groupThread);
}
private Group createGroup() {
User user = save(User.builder().firstName("Bill").lastName("Tester").emailAddress("bill@example.com").build());
Group group = new Group();
group.setTitle("Group title");
group.setUser(user);
return save(group);
}
private MailEvent createMailEvent() {
MailEvent mail = new MailEvent();
mail.setSubject("subject");
mail.setMessageBody("body");
mail.setMessageID(message.getInReplyTo());
return save(mail);
}
private ForumMailGroup createForumMailGroup() {
MailEvent mailEvent = createMailEvent();
return save(new ForumMailGroup(mailEvent, ObjectMother.SOME_USER, thread));
}
private ForumMailProject createForumMailProject() {
MailEvent mailEvent = createMailEvent();
return save(new ForumMailProject(mailEvent, createUser(), createProjectThread()));
}
private ProjectThread createProjectThread() {
ProjectType bachelor = save(new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor"));
User supervisor = createUser();
Project project = save(Project.builder().title("Project").projectType(bachelor).startDate(LocalDate.now()).headSupervisor(supervisor).build());
ProjectThread projectThread = new ProjectThread();
projectThread.setProject(project);
projectThread.setForumThread(createThread());
return save(projectThread);
}
private User createUser() {
return save(User.builder().firstName("Bob").lastName("Example").emailAddress("bob@example.com").build());
}
}

@ -1,113 +0,0 @@
package se.su.dsv.scipro.forummail;
import com.google.common.eventbus.EventBus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import se.su.dsv.scipro.forum.NewProjectForumReplyEvent;
import se.su.dsv.scipro.forum.dataobjects.ForumPost;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
import se.su.dsv.scipro.mail.MailEvent;
import se.su.dsv.scipro.mail.MailEventService;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.springdata.services.UserProfileService;
import se.su.dsv.scipro.system.DegreeType;
import se.su.dsv.scipro.system.ProjectType;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.test.DomainObjects;
import java.time.LocalDate;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class SendForumMailTest {
@Mock
private ForumMailRepository forumMailRepository;
@Mock
private MailEventService mailEventService;
@Mock
private ForumMailSettingsService mailSettingsService;
@Mock
private ForumMailFormatter composer;
@Mock
private GeneralSystemSettingsService generalSystemSettings;
@Mock
private EventBus eventBus;
@Mock
private UserProfileService userProfileService;
private ProjectThread projectThread;
private ForumPost forumPost;
private SendForumMail sendForumMail;
private User coSupervisor;
@BeforeEach
public void setUp() throws Exception {
when(generalSystemSettings.getGeneralSystemSettingsInstance()).thenReturn(new GeneralSystemSettings());
when(mailEventService.save(isA(MailEvent.class))).thenReturn(new MailEvent());
ForumMailSettings settings = new ForumMailSettings();
settings.setEnabled(true);
when(mailSettingsService.getSettings()).thenReturn(settings);
User poster = newUser(1L);
final User threadMember = newUser(2L);
coSupervisor = newUser(3L);
ProjectType bachelor = new ProjectType(DegreeType.BACHELOR, "Bachelor", "Bachelor");
Project project = Project.builder().title("Project").projectType(bachelor).startDate(LocalDate.now()).headSupervisor(poster).build();
project.addProjectParticipant(threadMember);
project.addCoSupervisor(coSupervisor);
ForumThread forumThread = new ForumThread();
projectThread = new ProjectThread();
projectThread.setProject(project);
projectThread.setForumThread(forumThread);
forumPost = new ForumPost();
forumPost.setForumThread(forumThread);
forumPost.setPostedBy(poster);
when(userProfileService.isReceiveForumMail(any(User.class))).thenReturn(true);
sendForumMail = new SendForumMail(forumMailRepository, mailSettingsService, mailEventService, composer, generalSystemSettings, eventBus, userProfileService);
}
@Test
public void generates_forum_mail_for_every_member() {
sendForumMail.newProjectForumReply(new NewProjectForumReplyEvent(projectThread, forumPost));
ArgumentCaptor<ForumMailProject> captor = ArgumentCaptor.forClass(ForumMailProject.class);
verify(forumMailRepository).save(captor.capture());
ForumMailProject forumMail = captor.getValue();
assertThat(forumMail.getUser(), is(coSupervisor));
assertThat(forumMail.getProjectThread(), is(projectThread));
assertNotNull(forumMail.getMailEvent());
}
@Test
public void uses_correct_body() {
String content = "Reply here!";
when(composer.formatBody(projectThread.getForumThread())).thenReturn(content);
sendForumMail.newProjectForumReply(new NewProjectForumReplyEvent(projectThread, forumPost));
ArgumentCaptor<MailEvent> captor = ArgumentCaptor.forClass(MailEvent.class);
verify(mailEventService).save(captor.capture());
assertThat(captor.getValue().getMessageBody(), is(content + "* Project: Project\n* View forum in SciPro here: http://localhost:8080/forum/mail/project/?id=null"));
}
private User newUser(final long id) {
User user = User.builder().firstName("Kalle").lastName("Tester").emailAddress("kalle@example.com").build();
DomainObjects.injectId(user, id);
return user;
}
}

@ -26,7 +26,7 @@
<!-- See https://hibernate.org/orm/releases/ for which version Hibernate implements -->
<jakarta.persistence-api.version>3.1.0</jakarta.persistence-api.version>
<hibernate.version>6.1.7.Final</hibernate.version>
<hibernate.version>6.5.0.Final</hibernate.version>
<mariadb-java-client.version>3.2.0</mariadb-java-client.version>
<querydsl.version>5.0.0</querydsl.version>

@ -29,8 +29,6 @@ import se.su.dsv.scipro.components.DisableSubmitButtonsOnSubmit;
import se.su.dsv.scipro.examiner.pages.ExaminerStartPage;
import se.su.dsv.scipro.finalseminar.*;
import se.su.dsv.scipro.finalthesis.SupervisorFinalThesisListingPage;
import se.su.dsv.scipro.forum.mail.ForumMailGroupPage;
import se.su.dsv.scipro.forum.mail.ForumMailProjectPage;
import se.su.dsv.scipro.forum.pages.ProjectForumBasePage;
import se.su.dsv.scipro.forum.pages.SupervisorForumBasePage;
import se.su.dsv.scipro.forum.pages.threaded.*;
@ -137,7 +135,6 @@ public class SciProApplication extends LifecycleManagedWebApplication {
mountSupervisorPages();
mountPeerPages();
mountErrorPages();
mountForumPage();
//Set up authorization strategies for local authentication.
CompoundAuthorizationStrategy cas = new CompoundAuthorizationStrategy();
@ -167,11 +164,6 @@ public class SciProApplication extends LifecycleManagedWebApplication {
});
}
private void mountForumPage() {
mountPage("forum/mail/project", ForumMailProjectPage.class);
mountPage("forum/mail/group", ForumMailGroupPage.class);
}
private void mountErrorPages() {
mountPage("404", NotFoundPage.class);
mountPage("403", AccessDeniedPage.class);
@ -303,7 +295,6 @@ public class SciProApplication extends LifecycleManagedWebApplication {
mountPage("admin/settings/nonworkdays", NonWorkDaysPage.class);
mountPage("admin/settings/modules", AdminProjectModuleSettingsPage.class);
mountPage("admin/settings/footer", AdminFooterSettingsPage.class);
mountPage("admin/settings/forum", AdminForumSettingsPage.class);
mountPage("admin/settings/urkund", AdminUrkundSettingsPage.class);
mountPage("admin/gdpr", AdminGDPRPage.class);
mountPage("admin/allpeers", AdminPeersPage.class);

@ -35,7 +35,6 @@ public abstract class AbstractAdminSystemPage extends AbstractAdminPage {
items.add(new MenuItem("Project module settings", AdminProjectModuleSettingsPage.class));
items.add(new MenuItem("Footer settings", AdminFooterSettingsPage.class));
items.add(new MenuItem("Reviewer deadline settings", AdminReviewerDeadlineSettingsPage.class));
items.add(new MenuItem("Forum mail settings", AdminForumSettingsPage.class));
items.add(new MenuItem("GDPR", AdminGDPRPage.class));
return items;
}

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<body>
<wicket:extend>
<h4>Mail reply settings</h4>
<form class="form-horizontal" wicket:id="form">
<div class="mb-3">
<label wicket:for="enabled" class="col-lg-3">Enabled</label>
<div class="col-lg-3">
<input type="checkbox" wicket:id="enabled" />
</div>
</div>
<div class="mb-3">
<label for="host" wicket:for="host" class="col-lg-2">Incoming mail host (IMAP):</label>
<div class="col-lg-5">
<input id="host" class="form-control" type="text" wicket:id="host"/>
</div>
</div>
<div class="mb-3">
<label for="port" wicket:for="port" class="col-lg-2">Incoming mail port:</label>
<div class="col-lg-1">
<input id="port" class="form-control" type="text" wicket:id="port"/>
</div>
</div>
<div class="mb-3">
<label for="account" wicket:for="account" class="col-lg-2">Incoming mail account:</label>
<div class="col-lg-5">
<input id="account" class="form-control" type="text" wicket:id="account">
</div>
</div>
<div class="mb-3">
<label for="password" wicket:for="password" class="col-lg-2">Incoming mail password:</label>
<div class="col-lg-5">
<input id="password" class="form-control" type="password" wicket:id="password"/>
</div>
</div>
<div class="mb-3">
<label for="replyAddress" wicket:for="replyAddress" class="col-lg-2">Reply e-mail address:</label>
<div class="col-lg-5">
<input id="replyAddress" class="form-control" type="text" wicket:id="replyAddress"/>
</div>
</div>
<button class="btn btn-success" type="submit">Save changes</button>
</form>
</wicket:extend>
</body>
</html>

@ -1,54 +0,0 @@
package se.su.dsv.scipro.admin.pages;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.PasswordTextField;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LambdaModel;
import org.apache.wicket.model.LoadableDetachableModel;
import se.su.dsv.scipro.forummail.ForumMailSettings;
import se.su.dsv.scipro.forummail.ForumMailSettingsService;
import jakarta.inject.Inject;
public class AdminForumSettingsPage extends AbstractAdminSystemPage {
@Inject
private ForumMailSettingsService forumMailSettingsService;
public AdminForumSettingsPage() {
add(new SettingsForm("form", getSettings()));
}
private class SettingsForm extends Form<ForumMailSettings> {
public SettingsForm(String id, IModel<ForumMailSettings> model) {
super(id, model);
add(new CheckBox("enabled", LambdaModel.of(model, ForumMailSettings::isEnabled, ForumMailSettings::setEnabled)));
add(new TextField<>("host", LambdaModel.of(model, ForumMailSettings::getHost, ForumMailSettings::setHost)));
add(new TextField<>("port", LambdaModel.of(model, ForumMailSettings::getPort, ForumMailSettings::setPort), Integer.class));
add(new TextField<>("replyAddress", LambdaModel.of(model, ForumMailSettings::getReplyAddress, ForumMailSettings::setReplyAddress)));
add(new TextField<>("account", LambdaModel.of(model, ForumMailSettings::getAccount, ForumMailSettings::setAccount)));
PasswordTextField password = new PasswordTextField("password", LambdaModel.of(model, ForumMailSettings::getPassword, ForumMailSettings::setPassword));
password.setResetPassword(false);
password.setRequired(false);
add(password);
}
@Override
protected void onSubmit() {
success(getString("saved"));
forumMailSettingsService.save(getModelObject());
}
}
private IModel<ForumMailSettings> getSettings() {
return new LoadableDetachableModel<>() {
@Override
protected ForumMailSettings load() {
return forumMailSettingsService.getSettings();
}
};
}
}

@ -1,66 +0,0 @@
package se.su.dsv.scipro.forum.mail;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import se.su.dsv.scipro.forum.GroupThreadRepository;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.forum.dataobjects.GroupThread;
import se.su.dsv.scipro.group.Group;
import se.su.dsv.scipro.group.ViewThreadPage;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.security.auth.Authorization;
import se.su.dsv.scipro.session.SciProSession;
import se.su.dsv.scipro.supervisor.pages.SupervisorViewGroupThreadPage;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.util.PageParameterKeys;
import jakarta.inject.Inject;
import static se.su.dsv.scipro.security.auth.roles.Roles.AUTHOR;
import static se.su.dsv.scipro.security.auth.roles.Roles.REVIEWER;
import static se.su.dsv.scipro.security.auth.roles.Roles.SUPERVISOR;
@Authorization(requiresLoggedInUser = true, authorizedRoles = {AUTHOR, SUPERVISOR, REVIEWER})
public class ForumMailGroupPage extends WebPage {
@Inject
GroupThreadRepository groupThreadRepository;
public ForumMailGroupPage(PageParameters pp) {
super(pp);
final User currentUser = SciProSession.get().getUser();
final long id = pp.get("id").toLong(0L);
final GroupThread groupThread = groupThreadRepository.findOne(id);
if (groupThread == null) {
throw new RestartResponseException(getApplication().getHomePage());
}
final Group group = groupThread.getGroup();
final PageParameters pageParameters = new PageParameters();
pageParameters.set(PageParameterKeys.MAP.get(ForumThread.class), groupThread.getId());
pageParameters.set(PageParameterKeys.MAP.get(Group.class), group.getId());
final boolean hasSupervisingRole = group.getProjects()
.stream()
.anyMatch(project -> hasSupervisingRole(currentUser, project));
if (hasSupervisingRole) {
throw new RestartResponseException(SupervisorViewGroupThreadPage.class, pageParameters);
}
final boolean isAuthor = group.getProjects()
.stream()
.anyMatch(project -> project.isParticipant(currentUser));
if (isAuthor) {
throw new RestartResponseException(ViewThreadPage.class, pageParameters);
}
setResponsePage(getApplication().getHomePage());
}
private boolean hasSupervisingRole(User currentUser, Project project) {
return project.isReviewer(currentUser) || project.isCoSupervisor(currentUser) || project.isSupervisor(currentUser);
}
}

@ -1,53 +0,0 @@
package se.su.dsv.scipro.forum.mail;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import se.su.dsv.scipro.forum.ProjectThreadRepository;
import se.su.dsv.scipro.forum.dataobjects.ForumThread;
import se.su.dsv.scipro.forum.dataobjects.ProjectThread;
import se.su.dsv.scipro.forum.pages.threaded.ProjectViewForumThreadPage;
import se.su.dsv.scipro.forum.pages.threaded.SupervisorViewForumThreadPage;
import se.su.dsv.scipro.project.Project;
import se.su.dsv.scipro.security.auth.Authorization;
import se.su.dsv.scipro.session.SciProSession;
import se.su.dsv.scipro.system.User;
import se.su.dsv.scipro.util.PageParameterKeys;
import jakarta.inject.Inject;
import static se.su.dsv.scipro.security.auth.roles.Roles.AUTHOR;
import static se.su.dsv.scipro.security.auth.roles.Roles.REVIEWER;
import static se.su.dsv.scipro.security.auth.roles.Roles.SUPERVISOR;
@Authorization(requiresLoggedInUser = true, authorizedRoles = {AUTHOR, SUPERVISOR, REVIEWER})
public class ForumMailProjectPage extends WebPage {
@Inject
ProjectThreadRepository projectThreadRepository;
public ForumMailProjectPage(PageParameters pp) {
super(pp);
final User currentUser = SciProSession.get().getUser();
final long id = pp.get("id").toLong(0L);
final ProjectThread projectThread = projectThreadRepository.findOne(id);
if (projectThread == null) {
throw new RestartResponseException(getApplication().getHomePage());
}
final Project project = projectThread.getProject();
final PageParameters pageParameters = new PageParameters();
pageParameters.set(PageParameterKeys.MAP.get(ForumThread.class), projectThread.getId());
pageParameters.set(PageParameterKeys.MAP.get(Project.class), project.getId());
if (project.isReviewer(currentUser) || project.isCoSupervisor(currentUser) || project.isSupervisor(currentUser)) {
throw new RestartResponseException(SupervisorViewForumThreadPage.class, pageParameters);
}
if (project.isParticipant(currentUser)) {
throw new RestartResponseException(ProjectViewForumThreadPage.class, pageParameters);
}
setResponsePage(getApplication().getHomePage());
}
}

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
<body>
<wicket:panel>
<div class="row">
<div class="col-lg-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" wicket:id="receiveForumMails">
<label class="form-check-label" wicket:for="receiveForumMails">
Receive forum mails from projects, groups
</label>
</div>
</div>
</div>
</wicket:panel>
</body>
</html>

@ -1,26 +0,0 @@
package se.su.dsv.scipro.forum.mail;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import se.su.dsv.scipro.session.SciProSession;
import se.su.dsv.scipro.springdata.services.UserProfileService;
import jakarta.inject.Inject;
public class ForumMailReceiverSettingsPanel extends Panel {
@Inject
private UserProfileService userProfileService;
public ForumMailReceiverSettingsPanel(String id) {
super(id);
add(new AjaxCheckBox("receiveForumMails", Model.of(userProfileService.isReceiveForumMail(SciProSession.get().getUser()))) {
@Override
protected void onUpdate(AjaxRequestTarget target) {
userProfileService.setReceiveForumMail(SciProSession.get().getUser(), getModelObject());
}
});
}
}

@ -264,11 +264,20 @@ public class SendToExaminer extends GenericPanel<Project> {
.map(instant -> instant.atZone(ZoneId.systemDefault()))
.map(ZonedDateTime::toLocalDate);
if (seminarDate.isPresent() && seminarDate.get().isAfter(finalThesis.getUploadDate())) {
return seminarDate.get();
}
else {
boolean hasFinalThesis = finalThesis != null;
boolean hasSeminarDate = seminarDate.isPresent();
if (hasFinalThesis && hasSeminarDate) {
if (finalThesis.getUploadDate().isAfter(seminarDate.get())) {
return finalThesis.getUploadDate();
} else {
return seminarDate.get();
}
} else if (hasFinalThesis) {
return finalThesis.getUploadDate();
} else if (hasSeminarDate) {
return seminarDate.get();
} else {
return null;
}
}

@ -35,18 +35,7 @@
<div class="card" wicket:id="tabs"></div>
</div>
<div class="col-lg-6">
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item active"><a class="nav-link active" href="#discussion" role="tab" data-bs-toggle="tab">Interact with reviewer</a></li>
<li class="nav-item">
<a class="nav-link" href="#examiner-timeline" role="tab" data-bs-toggle="tab">
Interactions with examiner
</a>
</li>
</ul>
<div class="tab-content">
<div id="discussion" class="tab-pane active bordered" wicket:id="discussion"></div>
<div id="examiner-timeline" class="tab-pane bordered" wicket:id="examiner_timeline"></div>
</div>
<div class="card" wicket:id="interact"></div>
</div>
</div>
</wicket:extend>

@ -136,6 +136,15 @@ public class SupervisorGradingReportPage extends AbstractSupervisorProjectDetail
TabbedPanel<ITab> tabbedPanel = new BootstrapCardTabbedPanel<>("tabs", tabs);
add(tabbedPanel);
List<ITab> interactTabs = new ArrayList<>();
interactTabs.add(createTab(Model.of("Interact with reviewer"),
panelId -> new TimelinePanel(panelId, projectModel)));
interactTabs.add(createTab(Model.of("Interactions with examiner"),
panelId -> new ExaminerTimelinePanel(panelId, projectModel)));
TabbedPanel<ITab> tabbedPanelInteract = new BootstrapCardTabbedPanel<>("interact", interactTabs);
add(tabbedPanelInteract);
final String externalGradingURL = generalSystemSettingsService.getGeneralSystemSettingsInstance().getExternalGradingURL();
add(new ExternalLink("moreInformation", externalGradingURL) {
@Override
@ -145,8 +154,6 @@ public class SupervisorGradingReportPage extends AbstractSupervisorProjectDetail
}
});
add(new TimelinePanel("discussion", projectModel));
add(new ExaminerTimelinePanel("examiner_timeline", projectModel));
}
private Component newGreenHighlight(String id, IModel<Boolean> completed, IModel<String> text) {

@ -6,7 +6,6 @@
<div class="col-lg-12">
<div wicket:id="feedbackPanel"></div>
<div wicket:id="settingsPanel"></div>
<div wicket:id="forumMail"></div>
</div>
</div>
<div wicket:id="deliveryConfiguration">

@ -1,7 +1,6 @@
package se.su.dsv.scipro.notifications.pages;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import se.su.dsv.scipro.forum.mail.ForumMailReceiverSettingsPanel;
import se.su.dsv.scipro.notifications.panels.NotificationSettingsPanel;
import se.su.dsv.scipro.notifications.settings.panels.MemberDeliveryConfigurationPanel;
import se.su.dsv.scipro.security.auth.Authorization;
@ -17,7 +16,6 @@ public class SupervisorNotificationSettingsPage extends AbstractSettingsPage {
public SupervisorNotificationSettingsPage() {
add(new FeedbackPanel(FEEDBACK).setOutputMarkupId(true));
add(new ForumMailReceiverSettingsPanel("forumMail"));
add(new NotificationSettingsPanel(SETTINGS_PANEL));
add(new MemberDeliveryConfigurationPanel("deliveryConfiguration", SUPERVISOR));
}

@ -1,7 +1,6 @@
se.su.dsv.scipro.SciProModule
se.su.dsv.scipro.reviewing.ReviewingModule
se.su.dsv.scipro.crosscutting.CrosscuttingModule
se.su.dsv.scipro.forummail.ForumMailModule
se.su.dsv.scipro.integration.activityfinalseminar.ActivityFinalSeminarModule
se.su.dsv.scipro.integration.activityforum.ActivityForumModule
se.su.dsv.scipro.peer.PeerModule

@ -46,7 +46,6 @@ import se.su.dsv.scipro.finalthesis.PublishingConsentService;
import se.su.dsv.scipro.forum.BasicForumService;
import se.su.dsv.scipro.forum.GroupForumService;
import se.su.dsv.scipro.forum.ProjectForumService;
import se.su.dsv.scipro.forummail.ForumMailSettingsService;
import se.su.dsv.scipro.gdpr.Reporter;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettings;
import se.su.dsv.scipro.generalsystemsettings.GeneralSystemSettingsService;
@ -88,7 +87,6 @@ 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.Project;
import se.su.dsv.scipro.project.ProjectPeopleStatisticsService;
import se.su.dsv.scipro.project.ProjectService;
import se.su.dsv.scipro.project.pages.ProjectStartPage;
@ -322,8 +320,6 @@ public abstract class SciProTest {
@Mock
protected FooterAddressRepo footerAddressRepo;
@Mock
protected ForumMailSettingsService forumMailSettingsService;
@Mock
protected ApplicationPeriodFacade applicationPeriodFacade;
@Mock
protected FirstMeetingRepository firstMeetingRepository;

@ -1,67 +0,0 @@
package se.su.dsv.scipro.admin.pages;
import org.apache.wicket.feedback.FeedbackMessage;
import org.apache.wicket.util.tester.FormTester;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import se.su.dsv.scipro.SciProTest;
import se.su.dsv.scipro.forummail.ForumMailSettings;
import java.io.Serializable;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class AdminForumSettingsPageTest extends SciProTest {
private AdminForumSettingsPage page;
@BeforeEach
public void setUp() throws Exception {
when(forumMailSettingsService.getSettings()).thenReturn(createSettings());
page = tester.startPage(AdminForumSettingsPage.class);
}
@Test
public void renders() throws Exception {
tester.assertRenderedPage(AdminForumSettingsPage.class);
}
@Test
public void form_submit_save_changes() {
String mailServer = submitForm();
ArgumentCaptor<ForumMailSettings> captor = ArgumentCaptor.forClass(ForumMailSettings.class);
verify(forumMailSettingsService).save(captor.capture());
assertEquals(mailServer, captor.getValue().getHost());
}
@Test
public void feedback_when_submitting_form() {
submitForm();
List<Serializable> messages = tester.getMessages(FeedbackMessage.SUCCESS);
assertThat(messages, hasItem(containsString(page.getString("saved"))));
}
private String submitForm() {
FormTester form = tester.newFormTester("form");
String mailServer = "test";
form.setValue("host", mailServer);
form.submit();
return mailServer;
}
private ForumMailSettings createSettings() {
return new ForumMailSettings();
}
}