/**
* Yobi, Project Hosting SW
*
* Copyright 2013 NAVER Corp.
* http://yobi.io
*
* @Author Yi EungJun
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package models;
import controllers.UserApp;
import controllers.routes;
import models.enumeration.*;
import models.resource.GlobalResource;
import models.resource.Resource;
import models.resource.ResourceConvertible;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.joda.time.DateTime;
import org.tmatesoft.svn.core.SVNException;
import play.api.i18n.Lang;
import play.db.ebean.Model;
import play.i18n.Messages;
import play.libs.Akka;
import playRepository.*;
import scala.concurrent.duration.Duration;
import utils.EventConstants;
import utils.RouteUtil;
import javax.naming.LimitExceededException;
import javax.persistence.*;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static models.enumeration.EventType.*;
@Entity
public class NotificationEvent extends Model {
private static final long serialVersionUID = 1L;
@Id
public Long id;
public static Finder<Long, NotificationEvent> find = new Finder<>(Long.class, NotificationEvent.class);
public String title;
@Lob
public String message;
public Long senderId;
@ManyToMany(cascade = CascadeType.ALL)
public Set<User> receivers;
@Temporal(TemporalType.TIMESTAMP)
public Date created;
@Enumerated(EnumType.STRING)
public ResourceType resourceType;
public String resourceId;
@Enumerated(EnumType.STRING)
public EventType eventType;
@Lob
public String oldValue;
@Lob
public String newValue;
@OneToOne(mappedBy="notificationEvent", cascade = CascadeType.ALL)
public NotificationMail notificationMail;
public String getOldValue() {
return oldValue;
}
@Transient
public String getMessage() {
return getMessage(Lang.defaultLang());
}
@Transient
public String getMessage(Lang lang) {
if (message != null) {
return message;
}
switch (eventType) {
case ISSUE_STATE_CHANGED:
if (newValue.equals(State.CLOSED.state())) {
return Messages.get(lang, "notification.issue.closed");
} else {
return Messages.get(lang, "notification.issue.reopened");
}
case ISSUE_ASSIGNEE_CHANGED:
if (newValue == null) {
return Messages.get(lang, "notification.issue.unassigned");
} else {
return Messages.get(lang, "notification.issue.assigned", newValue);
}
case NEW_ISSUE:
case NEW_POSTING:
case NEW_COMMENT:
case NEW_PULL_REQUEST:
case NEW_REVIEW_COMMENT:
case NEW_COMMIT:
case ISSUE_BODY_CHANGED:
return newValue;
case PULL_REQUEST_STATE_CHANGED:
if (State.OPEN.state().equals(newValue)) {
return Messages.get(lang, "notification.pullrequest.reopened");
} else {
return Messages.get(lang, "notification.pullrequest." + newValue);
}
case PULL_REQUEST_COMMIT_CHANGED:
return newValue;
case PULL_REQUEST_MERGED:
return Messages.get(lang, "notification.type.pullrequest.merged." + newValue) + "\n" + StringUtils.defaultString(oldValue, StringUtils.EMPTY);
case MEMBER_ENROLL_REQUEST:
if (RequestState.REQUEST.name().equals(newValue)) {
return Messages.get(lang, "notification.member.enroll.request");
} else if (RequestState.ACCEPT.name().equals(newValue)) {
return Messages.get(lang, "notification.member.enroll.accept");
} else {
return Messages.get(lang, "notification.member.enroll.cancel");
}
case ORGANIZATION_MEMBER_ENROLL_REQUEST:
if (RequestState.REQUEST.name().equals(newValue)) {
return Messages.get(lang, "notification.organization.member.enroll.request");
} else if (RequestState.ACCEPT.name().equals(newValue)) {
return Messages.get(lang, "notification.organization.member.enroll.accept");
} else {
return Messages.get(lang, "notification.organization.member.enroll.cancel");
}
case PULL_REQUEST_REVIEW_STATE_CHANGED:
if (PullRequestReviewAction.DONE.name().equals(newValue)) {
return Messages.get(lang, "notification.pullrequest.reviewed", User.find.byId(senderId).loginId);
} else {
return Messages.get(lang, "notification.pullrequest.unreviewed", User.find.byId(senderId).loginId);
}
case REVIEW_THREAD_STATE_CHANGED:
if (newValue.equals(CommentThread.ThreadState.CLOSED.name())) {
return Messages.get(lang, "notification.reviewthread.closed");
} else {
return Messages.get(lang, "notification.reviewthread.reopened");
}
default:
return null;
}
}
public User getSender() {
return User.find.byId(this.senderId);
}
public Resource getResource() {
return Resource.get(resourceType, resourceId);
}
public Project getProject() {
switch(resourceType) {
case ISSUE_ASSIGNEE:
return Assignee.finder.byId(Long.valueOf(resourceId)).project;
case PROJECT:
return Project.find.byId(Long.valueOf(resourceId));
default:
Resource resource = getResource();
if (resource != null) {
if (resource instanceof GlobalResource) {
return null;
} else {
return resource.getProject();
}
} else {
return null;
}
}
}
public Organization getOrganization() {
switch (resourceType) {
case ORGANIZATION:
return Organization.find.byId(Long.valueOf(resourceId));
default:
return null;
}
}
public boolean resourceExists() {
return Resource.exists(resourceType, resourceId);
}
public static void add(NotificationEvent event) {
if (event.notificationMail == null) {
event.notificationMail = new NotificationMail();
event.notificationMail.notificationEvent = event;
}
Date draftDate = DateTime.now().minusMillis(EventConstants.DRAFT_TIME_IN_MILLIS).toDate();
NotificationEvent lastEvent = NotificationEvent.find.where()
.eq("resourceId", event.resourceId)
.eq("resourceType", event.resourceType)
.gt("created", draftDate)
.orderBy("id desc").setMaxRows(1).findUnique();
if (lastEvent != null) {
if (lastEvent.eventType == event.eventType &&
event.senderId.equals(lastEvent.senderId)) {
// If the last event is A -> B and the current event is B -> C,
// they are merged into the new event A -> C.
event.oldValue = lastEvent.getOldValue();
lastEvent.delete();
// If the last event is A -> B and the current event is B -> A,
// they are removed.
if (StringUtils.equals(event.oldValue, event.newValue)) {
return;
}
}
}
filterReceivers(event);
if (event.receivers.isEmpty()) {
return;
}
event.save();
event.saveManyToManyAssociations("receivers");
}
private static void filterReceivers(final NotificationEvent event) {
final Project project = event.getProject();
if (project == null) {
return;
}
final Resource resource = project.asResource();
CollectionUtils.filter(event.receivers, new Predicate() {
@Override
public boolean evaluate(Object obj) {
User receiver = (User) obj;
if(receiver.loginId == null) {
return false;
}
if (!Watch.isWatching(receiver, resource)) {
return true;
}
return UserProjectNotification.isEnabledNotiType(receiver, project, event.eventType);
}
});
}
public static void deleteBy(Resource resource) {
for (NotificationEvent event : NotificationEvent.find.where().where().eq("resourceType",
resource.getType()).eq("resourceId", resource.getId()).findList()) {
event.delete();
}
}
/**
* @see {@link controllers.PullRequestApp#newPullRequest(String, String)}
*/
public static NotificationEvent afterNewPullRequest(User sender, PullRequest pullRequest) {
NotificationEvent notiEvent = createFrom(sender, pullRequest);
notiEvent.title = formatNewTitle(pullRequest);
notiEvent.receivers = getReceiversWithRelatedAuthors(sender, pullRequest);
notiEvent.eventType = NEW_PULL_REQUEST;
notiEvent.oldValue = null;
notiEvent.newValue = pullRequest.body;
NotificationEvent.add(notiEvent);
return notiEvent;
}
public String getUrlToView() {
switch(eventType) {
case MEMBER_ENROLL_REQUEST:
if (getProject() == null) {
return null;
} else {
return routes.ProjectApp.members(
getProject().owner, getProject().name).url();
}
case ORGANIZATION_MEMBER_ENROLL_REQUEST:
Organization organization = getOrganization();
if (organization == null) {
return null;
}
return routes.OrganizationApp.members(organization.name).url();
case NEW_COMMIT:
if (getProject() == null) {
return null;
} else {
return routes.CodeHistoryApp.historyUntilHead(
getProject().owner, getProject().name).url();
}
default:
return RouteUtil.getUrl(resourceType, resourceId);
}
}
/**
* @see {@link models.PullRequest#merge(models.PullRequestEventMessage)}
* @see {@link controllers.PullRequestApp#addNotification(models.PullRequest, models.enumeration.State, models.enumeration.State)}
*/
public static NotificationEvent afterPullRequestUpdated(User sender, PullRequest pullRequest, State oldState, State newState) {
NotificationEvent notiEvent = createFrom(sender, pullRequest);
notiEvent.title = formatReplyTitle(pullRequest);
notiEvent.receivers = getReceivers(sender, pullRequest);
notiEvent.eventType = PULL_REQUEST_STATE_CHANGED;
notiEvent.oldValue = oldState.state();
notiEvent.newValue = newState.state();
NotificationEvent.add(notiEvent);
return notiEvent;
}
/**
* @see {@link actors.PullRequestActor#processPullRequestMerging(models.PullRequestEventMessage, models.PullRequest)}
*/
public static NotificationEvent afterMerge(User sender, PullRequest pullRequest, GitConflicts conflicts, State state) {
NotificationEvent notiEvent = createFrom(sender, pullRequest);
notiEvent.title = formatReplyTitle(pullRequest);
notiEvent.receivers = state == State.MERGED ? getReceiversWithRelatedAuthors(sender, pullRequest) : getReceivers(sender, pullRequest);
notiEvent.eventType = PULL_REQUEST_MERGED;
notiEvent.newValue = state.state();
if (conflicts != null) {
notiEvent.oldValue = StringUtils.join(conflicts.conflictFiles, "\n");
}
NotificationEvent.add(notiEvent);
return notiEvent;
}
/**
* @see {@link controllers.PullRequestApp#newComment(String, String, Long, String)}
*/
public static void afterNewComment(User sender, PullRequest pullRequest,
ReviewComment newComment, String urlToView) {
NotificationEvent notiEvent = createFrom(sender, newComment);
notiEvent.title = formatReplyTitle(pullRequest);
Set<User> receivers = getMentionedUsers(newComment.getContents());
receivers.addAll(getReceivers(sender, pullRequest));
receivers.remove(User.findByLoginId(newComment.author.loginId));
notiEvent.receivers = receivers;
notiEvent.eventType = NEW_REVIEW_COMMENT;
notiEvent.oldValue = null;
notiEvent.newValue = newComment.getContents();
NotificationEvent.add(notiEvent);
}
public static NotificationEvent afterNewPullRequest(PullRequest pullRequest) {
return afterNewPullRequest(UserApp.currentUser(), pullRequest);
}
public static NotificationEvent afterPullRequestUpdated(PullRequest pullRequest, State oldState, State newState) {
return afterPullRequestUpdated(UserApp.currentUser(), pullRequest, oldState, newState);
}
public static void afterNewComment(Comment comment) {
AbstractPosting post = comment.getParent();
NotificationEvent notiEvent = createFromCurrentUser(comment);
notiEvent.title = formatReplyTitle(post);
Set<User> receivers = getReceivers(post);
receivers.addAll(getMentionedUsers(comment.contents));
receivers.remove(UserApp.currentUser());
notiEvent.receivers = receivers;
notiEvent.eventType = NEW_COMMENT;
notiEvent.oldValue = null;
notiEvent.newValue = comment.contents;
notiEvent.resourceType = comment.asResource().getType();
notiEvent.resourceId = comment.asResource().getId();
NotificationEvent.add(notiEvent);
}
public static void afterNewCommentWithState(Comment comment, State state) {
AbstractPosting post = comment.getParent();
NotificationEvent notiEvent = createFromCurrentUser(comment);
notiEvent.title = formatReplyTitle(post);
Set<User> receivers = getReceivers(post);
receivers.addAll(getMentionedUsers(comment.contents));
receivers.remove(UserApp.currentUser());
notiEvent.receivers = receivers;
notiEvent.eventType = NEW_COMMENT;
notiEvent.oldValue = null;
notiEvent.newValue = comment.contents + "\n" + state.state();
notiEvent.resourceType = comment.asResource().getType();
notiEvent.resourceId = comment.asResource().getId();
NotificationEvent.add(notiEvent);
}
public static NotificationEvent afterStateChanged(State oldState, Issue issue) {
NotificationEvent notiEvent = createFromCurrentUser(issue);
notiEvent.title = formatReplyTitle(issue);
notiEvent.receivers = getReceivers(issue);
notiEvent.eventType = ISSUE_STATE_CHANGED;
notiEvent.oldValue = oldState != null ? oldState.state() : null;
notiEvent.newValue = issue.state.state();
NotificationEvent.add(notiEvent);
return notiEvent;
}
public static NotificationEvent afterStateChanged(
CommentThread.ThreadState oldState, CommentThread thread)
throws IOException, SVNException, ServletException {
NotificationEvent notiEvent = createFromCurrentUser(thread);
notiEvent.eventType = REVIEW_THREAD_STATE_CHANGED;
notiEvent.oldValue = oldState.name() != null ? oldState.name() : null;
notiEvent.newValue = thread.state.name();
// Set receivers
Set<User> receivers;
if (thread.isOnPullRequest()) {
PullRequest pullRequest = thread.pullRequest;
notiEvent.title = formatReplyTitle(pullRequest);
receivers = pullRequest.getWatchers();
} else {
String commitId;
if (thread instanceof CodeCommentThread) {
commitId = ((CodeCommentThread)thread).commitId;
} else {
commitId = ((NonRangedCodeCommentThread)thread).commitId;
}
Project project = thread.project;
Commit commit = RepositoryService.getRepository(project).getCommit(commitId);
notiEvent.title = formatReplyTitle(project, commit);
receivers = commit.getWatchers(project);
}
receivers.remove(UserApp.currentUser());
notiEvent.receivers = receivers;
NotificationEvent.add(notiEvent);
return notiEvent;
}
public static NotificationEvent afterAssigneeChanged(User oldAssignee, Issue issue) {
NotificationEvent notiEvent = createFromCurrentUser(issue);
Set<User> receivers = getReceivers(issue);
if(oldAssignee != null) {
notiEvent.oldValue = oldAssignee.loginId;
if(!oldAssignee.loginId.equals(UserApp.currentUser().loginId)) {
receivers.add(oldAssignee);
}
}
if (issue.assignee != null) {
notiEvent.newValue = User.find.byId(issue.assignee.user.id).loginId;
}
notiEvent.title = formatReplyTitle(issue);
notiEvent.receivers = receivers;
notiEvent.eventType = ISSUE_ASSIGNEE_CHANGED;
NotificationEvent.add(notiEvent);
return notiEvent;
}
public static void afterNewIssue(Issue issue) {
NotificationEvent notiEvent = createFromCurrentUser(issue);
notiEvent.title = formatNewTitle(issue);
notiEvent.receivers = getReceivers(issue);
notiEvent.eventType = NEW_ISSUE;
notiEvent.oldValue = null;
notiEvent.newValue = issue.body;
NotificationEvent.add(notiEvent);
}
public static NotificationEvent afterIssueBodyChanged(String oldBody, Issue issue) {
NotificationEvent notiEvent = createFromCurrentUser(issue);
notiEvent.title = formatReplyTitle(issue);
notiEvent.receivers = getReceivers(issue);
notiEvent.eventType = EventType.ISSUE_BODY_CHANGED;
notiEvent.oldValue = oldBody;
notiEvent.newValue = issue.body;
NotificationEvent.add(notiEvent);
return notiEvent;
}
public static void afterNewPost(Posting post) {
NotificationEvent notiEvent = createFromCurrentUser(post);
notiEvent.title = formatNewTitle(post);
notiEvent.receivers = getReceivers(post);
notiEvent.eventType = NEW_POSTING;
notiEvent.oldValue = null;
notiEvent.newValue = post.body;
NotificationEvent.add(notiEvent);
}
public static void afterNewCommitComment(Project project, ReviewComment comment,
String commitId) throws
IOException, SVNException, ServletException {
Commit commit = RepositoryService.getRepository(project).getCommit(commitId);
Set<User> watchers = commit.getWatchers(project);
watchers.addAll(getMentionedUsers(comment.getContents()));
watchers.remove(UserApp.currentUser());
NotificationEvent notiEvent = createFromCurrentUser(comment);
notiEvent.title = formatReplyTitle(project, commit);
notiEvent.receivers = watchers;
notiEvent.eventType = NEW_COMMENT;
notiEvent.oldValue = null;
notiEvent.newValue = comment.getContents();
NotificationEvent.add(notiEvent);
}
public static void afterNewSVNCommitComment(Project project, CommitComment codeComment) throws IOException, SVNException, ServletException {
Commit commit = RepositoryService.getRepository(project).getCommit(codeComment.commitId);
Set<User> watchers = commit.getWatchers(project);
watchers.addAll(getMentionedUsers(codeComment.contents));
watchers.remove(UserApp.currentUser());
NotificationEvent notiEvent = createFromCurrentUser(codeComment);
notiEvent.title = formatReplyTitle(project, commit);
notiEvent.receivers = watchers;
notiEvent.eventType = NEW_COMMENT;
notiEvent.oldValue = null;
notiEvent.newValue = codeComment.contents;
NotificationEvent.add(notiEvent);
}
public static void afterMemberRequest(Project project, User user, RequestState state) {
NotificationEvent notiEvent = createFromCurrentUser(project);
notiEvent.eventType = MEMBER_ENROLL_REQUEST;
notiEvent.receivers = getReceivers(project);
notiEvent.newValue = state.name();
if (state == RequestState.ACCEPT || state == RequestState.REJECT) {
notiEvent.receivers.remove(UserApp.currentUser());
notiEvent.receivers.add(user);
}
switch (state) {
case REQUEST:
notiEvent.title = formatMemberRequestTitle(project, user);
notiEvent.oldValue = RequestState.CANCEL.name();
break;
case CANCEL:
notiEvent.title = formatMemberRequestCancelTitle(project, user);
notiEvent.oldValue = RequestState.REQUEST.name();
break;
case ACCEPT:
notiEvent.title = formatMemberAcceptTitle(project, user);
notiEvent.oldValue = RequestState.REQUEST.name();
break;
}
notiEvent.resourceType = project.asResource().getType();
notiEvent.resourceId = project.asResource().getId();
NotificationEvent.add(notiEvent);
}
public static void afterOrganizationMemberRequest(Organization organization, User user, RequestState state) {
NotificationEvent notiEvent = createFromCurrentUser(organization);
notiEvent.eventType = ORGANIZATION_MEMBER_ENROLL_REQUEST;
notiEvent.receivers = getReceivers(organization);
notiEvent.newValue = state.name();
if (state == RequestState.ACCEPT || state == RequestState.REJECT) {
notiEvent.receivers.remove(UserApp.currentUser());
notiEvent.receivers.add(user);
}
switch (state) {
case REQUEST:
notiEvent.title = formatMemberRequestTitle(organization, user);
notiEvent.oldValue = RequestState.CANCEL.name();
break;
case CANCEL:
notiEvent.title = formatMemberRequestCancelTitle(organization, user);
notiEvent.oldValue = RequestState.REQUEST.name();
break;
case ACCEPT:
notiEvent.title = formatMemberAcceptTitle(organization, user);
notiEvent.oldValue = RequestState.REQUEST.name();
break;
}
notiEvent.resourceType = organization.asResource().getType();
notiEvent.resourceId = organization.asResource().getId();
NotificationEvent.add(notiEvent);
}
public static void afterNewCommits(List<RevCommit> commits, List<String> refNames, Project project, User sender, String title, Set<User> watchers) {
NotificationEvent notiEvent = createFrom(sender, project);
notiEvent.title = title;
notiEvent.receivers = watchers;
notiEvent.eventType = NEW_COMMIT;
notiEvent.oldValue = null;
notiEvent.newValue = newCommitsMessage(commits, refNames, project);
notiEvent.resourceType = project.asResource().getType();
notiEvent.resourceId = project.asResource().getId();
NotificationEvent.add(notiEvent);
}
public static NotificationEvent afterReviewed(PullRequest pullRequest, PullRequestReviewAction reviewAction) {
String title = formatReplyTitle(pullRequest);
Resource resource = pullRequest.asResource();
Set<User> receivers = pullRequest.getWatchers();
receivers.add(pullRequest.contributor);
User reviewer = UserApp.currentUser();
receivers.remove(reviewer);
NotificationEvent notiEvent = new NotificationEvent();
notiEvent.created = new Date();
notiEvent.title = title;
notiEvent.senderId = reviewer.id;
notiEvent.receivers = receivers;
notiEvent.resourceId = resource.getId();
notiEvent.resourceType = resource.getType();
notiEvent.eventType = EventType.PULL_REQUEST_REVIEW_STATE_CHANGED;
notiEvent.oldValue = reviewAction.getOppositAction().name();
notiEvent.newValue = reviewAction.name();
add(notiEvent);
return notiEvent;
}
private static String newCommitsMessage(List<RevCommit> commits, List<String> refNames, Project project) {
StringBuilder result = new StringBuilder();
if(commits.size() > 0) {
result.append("New Commits: \n");
for(RevCommit commit : commits) {
GitCommit gitCommit = new GitCommit(commit);
result.append(gitCommit.getShortId());
result.append(" ");
result.append(gitCommit.getShortMessage());
result.append("\n");
}
}
if(refNames.size() > 0) {
result.append("Branches: \n");
for(String refName: refNames) {
result.append(refName);
result.append("\n");
}
}
return result.toString();
}
private static NotificationEvent createFrom(User sender, ResourceConvertible rc) {
NotificationEvent notiEvent = new NotificationEvent();
notiEvent.senderId = sender.id;
notiEvent.created = new Date();
Resource resource = rc.asResource();
notiEvent.resourceId = resource.getId();
notiEvent.resourceType = resource.getType();
return notiEvent;
}
/**
* @see {@link #createFrom(models.User, models.resource.ResourceConvertible)}
*/
private static NotificationEvent createFromCurrentUser(ResourceConvertible rc) {
return createFrom(UserApp.currentUser(), rc);
}
private static Set<User> getReceivers(AbstractPosting abstractPosting) {
Set<User> receivers = abstractPosting.getWatchers();
receivers.addAll(getMentionedUsers(abstractPosting.body));
receivers.remove(UserApp.currentUser());
return receivers;
}
private static String getPrefixedNumber(AbstractPosting posting) {
if (posting instanceof Issue) {
return "#" + posting.getNumber();
} else {
return posting.getNumber().toString();
}
}
private static String formatReplyTitle(AbstractPosting posting) {
return String.format("Re: [%s] %s (%s)",
posting.project.name, posting.title, getPrefixedNumber(posting));
}
private static String formatNewTitle(AbstractPosting posting) {
return String.format("[%s] %s (%s)",
posting.project.name, posting.title, getPrefixedNumber(posting));
}
private static String formatReplyTitle(Project project, Commit commit) {
return String.format("Re: [%s] %s (%s)",
project.name, commit.getShortMessage(), commit.getShortId());
}
private static Set<User> getReceivers(User sender, PullRequest pullRequest) {
Set<User> watchers = getDefaultReceivers(pullRequest);
watchers.remove(sender);
return watchers;
}
private static Set<User> getDefaultReceivers(PullRequest pullRequest) {
Set<User> watchers = pullRequest.getWatchers();
watchers.addAll(getMentionedUsers(pullRequest.body));
return watchers;
}
private static Set<User> getReceiversWithRelatedAuthors(User sender, PullRequest pullRequest) {
Set<User> receivers = getDefaultReceivers(pullRequest);
String failureMessage =
"Failed to get authors related to the pullrequest " + pullRequest;
try {
Repository clonedRepository = GitRepository.buildMergingRepository(pullRequest);
if (pullRequest.mergedCommitIdFrom != null
&& pullRequest.mergedCommitIdTo != null) {
receivers.addAll(GitRepository.getRelatedAuthors(
clonedRepository,
pullRequest.mergedCommitIdFrom,
pullRequest.mergedCommitIdTo));
}
} catch (LimitExceededException e) {
for (ProjectUser member : pullRequest.toProject.members()) {
receivers.add(member.user);
}
play.Logger.info(failureMessage
+ ": Get all project members instead", e);
} catch (GitAPIException e) {
play.Logger.warn(failureMessage, e);
} catch (IOException e) {
play.Logger.warn(failureMessage, e);
}
receivers.remove(sender);
return receivers;
}
private static String formatNewTitle(PullRequest pullRequest) {
return String.format("[%s] %s (#%d)",
pullRequest.toProject.name, pullRequest.title, pullRequest.number);
}
private static String formatReplyTitle(PullRequest pullRequest) {
return String.format("Re: [%s] %s (#%s)",
pullRequest.toProject.name, pullRequest.title, pullRequest.number);
}
private static Set<User> getReceivers(Project project) {
Set<User> receivers = new HashSet<>();
List<User> managers = User.findUsersByProject(project.id, RoleType.MANAGER);
for (User manager : managers) {
if (Watch.isWatching(manager, project.asResource())) {
receivers.add(manager);
}
}
return receivers;
}
private static Set<User> getReceivers(Organization organization) {
Set<User> receivers = new HashSet<>();
List<User> managers = User.findUsersByOrganization(organization.id, RoleType.ORG_ADMIN);
receivers.addAll(managers);
return receivers;
}
private static String formatMemberRequestTitle(Project project, User user) {
return Messages.get("notification.member.request.title", project.name, user.loginId);
}
private static String formatMemberRequestCancelTitle(Project project, User user) {
return Messages.get("notification.member.request.cancel.title", project.name, user.loginId);
}
private static String formatMemberRequestCancelTitle(Organization organization, User user) {
return Messages.get("notification.member.request.cancel.title", organization.name, user.loginId);
}
private static String formatMemberRequestTitle(Organization organization, User user) {
return Messages.get("notification.organization.member.request.title", organization.name, user.loginId);
}
private static String formatMemberAcceptTitle(Project project, User user) {
return Messages.get("notification.member.request.accept.title", project.name, user.loginId);
}
private static String formatMemberAcceptTitle(Organization organization, User user) {
return Messages.get("notification.member.request.accept.title", organization.name, user.loginId);
}
public static Set<User> getMentionedUsers(String body) {
Matcher matcher = Pattern.compile("@" + User.LOGIN_ID_PATTERN_ALLOW_FORWARD_SLASH).matcher(body);
Set<User> users = new HashSet<>();
while(matcher.find()) {
String mentionWord = matcher.group().substring(1);
users.addAll(findOrganizationMembers(mentionWord));
users.addAll(findProjectMembers(mentionWord));
users.add(User.findByLoginId(mentionWord));
}
users.remove(User.anonymous);
return users;
}
private static Set<User> findOrganizationMembers(String mentionWord) {
Set<User> users = new HashSet<>();
Organization org = Organization.findByName(mentionWord);
if (org != null) {
for (OrganizationUser orgUser : org.users) {
users.add(orgUser.user);
}
}
return users;
}
private static Set<User> findProjectMembers(String mentionWord) {
Set<User> users = new HashSet<>();
if(mentionWord.contains("/")){
String projectName = mentionWord.substring(mentionWord.lastIndexOf("/")+1);
String loginId = mentionWord.substring(0, mentionWord.lastIndexOf("/"));
Project mentionedProject = Project.findByOwnerAndProjectName(loginId, projectName);
if(mentionedProject == null) {
return users;
}
for(ProjectUser projectUser: mentionedProject.members() ){
users.add(projectUser.user);
}
}
return users;
}
public static void scheduleDeleteOldNotifications() {
if (EventConstants.KEEP_TIME_IN_DAYS > 0) {
Akka.system().scheduler().schedule(
Duration.create(1, TimeUnit.MINUTES),
Duration.create(1, TimeUnit.DAYS),
new Runnable() {
@Override
public void run() {
Date threshold = DateTime.now()
.minusDays(EventConstants.KEEP_TIME_IN_DAYS).toDate();
List<NotificationEvent> olds = find.where().lt("created", threshold).findList();
for (NotificationEvent old : olds) {
old.delete();
}
}
},
Akka.system().dispatcher()
);
}
}
public static void onStart() {
scheduleDeleteOldNotifications();
}
public static List<NotificationEvent> findByReceiver(User user, int from, int size) {
return find.where().eq("receivers.id", user.id)
.order().desc("created")
.setFirstRow(from).setMaxRows(size).findList();
}
}