mirror of
https://github.com/theonedev/onedev.git
synced 2026-02-01 17:37:19 +00:00
Fix issue #1169 - Improve issue / pull request fuzzy search to include comments
This commit is contained in:
parent
d319013e0c
commit
ca2c02831a
@ -10,11 +10,6 @@ public interface AttachmentManager {
|
||||
|
||||
File getAttachmentGroupDirLocal(Long projectId, String attachmentGroup);
|
||||
|
||||
void moveAttachmentGroupTargetLocal(Long targetProjectId, Long sourceProjectId, String attachmentGroup);
|
||||
|
||||
void copyAttachmentGroupTargetLocal(Long targetProjectId, String targetAttachmentGroup,
|
||||
Long sourceProjectId, String sourceAttachmentGroup);
|
||||
|
||||
String saveAttachment(Long projectId, String attachmentGroup, String suggestedAttachmentName,
|
||||
InputStream attachmentStream);
|
||||
|
||||
|
||||
@ -14,11 +14,15 @@ import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.event.entity.EntityPersisted;
|
||||
import io.onedev.server.event.entity.EntityRemoved;
|
||||
import io.onedev.server.event.project.issue.IssuesCopied;
|
||||
import io.onedev.server.event.project.issue.IssuesMoved;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.event.system.SystemStopping;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.storage.StorageManager;
|
||||
import io.onedev.server.util.AttachmentTooLargeException;
|
||||
import io.onedev.server.util.artifact.FileInfo;
|
||||
@ -39,10 +43,7 @@ import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
import static io.onedev.commons.bootstrap.Bootstrap.BUFFER_SIZE;
|
||||
|
||||
@ -57,6 +58,8 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
|
||||
|
||||
private static final String TEMP = "temp";
|
||||
|
||||
private final Dao dao;
|
||||
|
||||
private final StorageManager storageManager;
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
@ -72,9 +75,10 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
|
||||
private String taskId;
|
||||
|
||||
@Inject
|
||||
public DefaultAttachmentManager(StorageManager storageManager, TransactionManager transactionManager,
|
||||
TaskScheduler taskScheduler, SettingManager settingManager, ProjectManager projectManager,
|
||||
ClusterManager clusterManager) {
|
||||
public DefaultAttachmentManager(Dao dao, StorageManager storageManager, TransactionManager transactionManager,
|
||||
TaskScheduler taskScheduler, SettingManager settingManager,
|
||||
ProjectManager projectManager, ClusterManager clusterManager) {
|
||||
this.dao = dao;
|
||||
this.storageManager = storageManager;
|
||||
this.transactionManager = transactionManager;
|
||||
this.taskScheduler = taskScheduler;
|
||||
@ -97,26 +101,40 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
|
||||
return new File(baseDir, TEMP + "/" + attachmentGroup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAttachmentGroupTargetLocal(Long targetProjectId, Long sourceProjectId, String attachmentGroup) {
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesMoved event) {
|
||||
Long sourceProjectId = event.getSourceProject().getId();
|
||||
Long targetProjectId = event.getProject().getId();
|
||||
File targetBaseDir = storageManager.getProjectAttachmentDir(targetProjectId);
|
||||
File targetGroupDir = getPermanentAttachmentGroupDir(targetBaseDir, attachmentGroup);
|
||||
|
||||
UUID sourceStorageServerUUID = projectManager.getStorageServerUUID(sourceProjectId, true);
|
||||
if (sourceStorageServerUUID.equals(clusterManager.getLocalServerUUID())) {
|
||||
File sourceBaseDir = storageManager.getProjectAttachmentDir(sourceProjectId);
|
||||
File sourceGroupDir = getPermanentAttachmentGroupDir(sourceBaseDir, attachmentGroup);
|
||||
FileUtils.createDir(targetGroupDir.getParentFile());
|
||||
try {
|
||||
FileUtils.moveDirectory(sourceGroupDir, targetGroupDir);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
for (Long issueId: event.getIssueIds()) {
|
||||
Issue issue = dao.load(Issue.class, issueId);
|
||||
String attachmentGroup = issue.getAttachmentGroup();
|
||||
File targetGroupDir = getPermanentAttachmentGroupDir(targetBaseDir, attachmentGroup);
|
||||
File sourceGroupDir = getPermanentAttachmentGroupDir(sourceBaseDir, attachmentGroup);
|
||||
FileUtils.createDir(targetGroupDir.getParentFile());
|
||||
try {
|
||||
FileUtils.moveDirectory(sourceGroupDir, targetGroupDir);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FileUtils.createDir(targetGroupDir);
|
||||
downloadAttachments(targetGroupDir, sourceStorageServerUUID,
|
||||
sourceProjectId, attachmentGroup);
|
||||
|
||||
Collection<String> attachmentGroups = new HashSet<>();
|
||||
for (Long issueId: event.getIssueIds()) {
|
||||
Issue issue = dao.load(Issue.class, issueId);
|
||||
String attachmentGroup = issue.getAttachmentGroup();
|
||||
attachmentGroups.add(attachmentGroup);
|
||||
File targetGroupDir = getPermanentAttachmentGroupDir(targetBaseDir, attachmentGroup);
|
||||
|
||||
FileUtils.createDir(targetGroupDir);
|
||||
downloadAttachments(targetGroupDir, sourceStorageServerUUID,
|
||||
sourceProjectId, attachmentGroup);
|
||||
}
|
||||
projectManager.runOnProjectServer(sourceProjectId, new ClusterTask<Void>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -124,35 +142,51 @@ public class DefaultAttachmentManager implements AttachmentManager, SchedulableT
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
File sourceBaseDir = storageManager.getProjectAttachmentDir(sourceProjectId);
|
||||
File sourceGroupDir = getPermanentAttachmentGroupDir(sourceBaseDir, attachmentGroup);
|
||||
FileUtils.deleteDir(sourceGroupDir);
|
||||
for (var attachmentGroup: attachmentGroups)
|
||||
FileUtils.deleteDir(getPermanentAttachmentGroupDir(sourceBaseDir, attachmentGroup));
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void copyAttachmentGroupTargetLocal(Long targetProjectId, String targetAttachmentGroup,
|
||||
Long sourceProjectId, String sourceAttachmentGroup) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesCopied event) {
|
||||
Long sourceProjectId = event.getSourceProject().getId();
|
||||
Long targetProjectId = event.getProject().getId();
|
||||
|
||||
File targetBaseDir = storageManager.getProjectAttachmentDir(targetProjectId);
|
||||
File targetGroupDir = getPermanentAttachmentGroupDir(targetBaseDir, targetAttachmentGroup);
|
||||
|
||||
UUID sourceStorageServerUUID = projectManager.getStorageServerUUID(sourceProjectId, true);
|
||||
if (sourceStorageServerUUID.equals(clusterManager.getLocalServerUUID())) {
|
||||
File sourceBaseDir = storageManager.getProjectAttachmentDir(sourceProjectId);
|
||||
File sourceGroupDir = getPermanentAttachmentGroupDir(sourceBaseDir, sourceAttachmentGroup);
|
||||
FileUtils.createDir(targetGroupDir.getParentFile());
|
||||
try {
|
||||
FileUtils.copyDirectory(sourceGroupDir, targetGroupDir);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
for (var entry: event.getIssueIdMapping().entrySet()) {
|
||||
Issue sourceIssue = dao.load(Issue.class, entry.getKey());
|
||||
Issue targetIssue = dao.load(Issue.class, entry.getValue());
|
||||
File sourceGroupDir = getPermanentAttachmentGroupDir(sourceBaseDir, sourceIssue.getAttachmentGroup());
|
||||
File targetGroupDir = getPermanentAttachmentGroupDir(targetBaseDir, targetIssue.getAttachmentGroup());
|
||||
|
||||
FileUtils.createDir(targetGroupDir.getParentFile());
|
||||
try {
|
||||
FileUtils.copyDirectory(sourceGroupDir, targetGroupDir);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
FileUtils.createDir(targetGroupDir);
|
||||
downloadAttachments(targetGroupDir, sourceStorageServerUUID,
|
||||
sourceProjectId, sourceAttachmentGroup);
|
||||
}
|
||||
for (var entry: event.getIssueIdMapping().entrySet()) {
|
||||
Issue sourceIssue = dao.load(Issue.class, entry.getKey());
|
||||
Issue targetIssue = dao.load(Issue.class, entry.getValue());
|
||||
File targetGroupDir = getPermanentAttachmentGroupDir(targetBaseDir, targetIssue.getAttachmentGroup());
|
||||
|
||||
FileUtils.createDir(targetGroupDir);
|
||||
downloadAttachments(targetGroupDir, sourceStorageServerUUID,
|
||||
sourceProjectId, sourceIssue.getAttachmentGroup());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void downloadAttachments(File targetDir, UUID sourceStorageServerUUID,
|
||||
|
||||
@ -22,6 +22,8 @@ public interface IssueChangeManager extends EntityManager<IssueChange> {
|
||||
void removeLink(LinkSpec spec, Issue issue, Issue linkedIssue, boolean opposite);
|
||||
|
||||
void changeTitle(Issue issue, String title);
|
||||
|
||||
void changeDescription(Issue issue, String description);
|
||||
|
||||
void changeConfidential(Issue issue, boolean confidential);
|
||||
|
||||
@ -29,7 +31,7 @@ public interface IssueChangeManager extends EntityManager<IssueChange> {
|
||||
|
||||
void changeMilestones(Issue issue, Collection<Milestone> milestones);
|
||||
|
||||
void save(IssueChange change, @Nullable String note);
|
||||
void create(IssueChange change, @Nullable String note);
|
||||
|
||||
void addSchedule(Issue issue, Milestone milestone);
|
||||
|
||||
|
||||
@ -7,6 +7,10 @@ import io.onedev.server.persistence.dao.EntityManager;
|
||||
|
||||
public interface IssueCommentManager extends EntityManager<IssueComment> {
|
||||
|
||||
void save(IssueComment comment, Collection<String> notifiedEmailAddresses);
|
||||
void create(IssueComment comment, Collection<String> notifiedEmailAddresses);
|
||||
|
||||
void delete(IssueComment comment);
|
||||
|
||||
void update(IssueComment comment);
|
||||
|
||||
}
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
package io.onedev.server.entitymanager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Project;
|
||||
@ -21,6 +15,11 @@ import io.onedev.server.web.component.issue.workflowreconcile.UndefinedFieldValu
|
||||
import io.onedev.server.web.component.issue.workflowreconcile.UndefinedFieldValuesResolution;
|
||||
import io.onedev.server.web.component.issue.workflowreconcile.UndefinedStateResolution;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface IssueManager extends EntityManager<Issue> {
|
||||
|
||||
@Nullable
|
||||
@ -62,16 +61,14 @@ public interface IssueManager extends EntityManager<Issue> {
|
||||
|
||||
void fixStateAndFieldOrdinals();
|
||||
|
||||
void saveDescription(Issue issue, @Nullable String description);
|
||||
|
||||
@Override
|
||||
void delete(Issue issue);
|
||||
|
||||
void move(Collection<Issue> issues, Project targetProject);
|
||||
void move(Collection<Issue> issues, Project sourceProject, Project targetProject);
|
||||
|
||||
void copy(Collection<Issue> issues, Project targetProject);
|
||||
void copy(Collection<Issue> issues, Project sourceProject, Project targetProject);
|
||||
|
||||
void delete(Collection<Issue> issues);
|
||||
void delete(Collection<Issue> issues, Project project);
|
||||
|
||||
Collection<MilestoneAndIssueState> queryMilestoneAndIssueStates(Project project, Collection<Milestone> milestones);
|
||||
|
||||
|
||||
@ -1,18 +1,14 @@
|
||||
package io.onedev.server.entitymanager.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.entityreference.EntityReferenceManager;
|
||||
import io.onedev.server.model.support.issue.changedata.*;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
@ -56,17 +52,6 @@ import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.PullRequest;
|
||||
import io.onedev.server.model.support.issue.TransitionSpec;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueBatchUpdateData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueConfidentialChangeData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueFieldChangeData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueLinkAddData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueLinkChangeData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueLinkRemoveData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueMilestoneAddData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueMilestoneChangeData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueMilestoneRemoveData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueStateChangeData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueTitleChangeData;
|
||||
import io.onedev.server.model.support.issue.transitiontrigger.BranchUpdateTrigger;
|
||||
import io.onedev.server.model.support.issue.transitiontrigger.BuildSuccessfulTrigger;
|
||||
import io.onedev.server.model.support.issue.transitiontrigger.DiscardPullRequestTrigger;
|
||||
@ -124,13 +109,16 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
private final EntityReferenceManager entityReferenceManager;
|
||||
|
||||
private String taskId;
|
||||
|
||||
@Inject
|
||||
public DefaultIssueChangeManager(Dao dao, IssueManager issueManager, IssueFieldManager issueFieldManager,
|
||||
ProjectManager projectManager, ListenerRegistry listenerRegistry, TaskScheduler taskScheduler,
|
||||
IssueScheduleManager issueScheduleManager, IssueLinkManager issueLinkManager,
|
||||
ClusterManager clusterManager) {
|
||||
ProjectManager projectManager, ListenerRegistry listenerRegistry,
|
||||
TaskScheduler taskScheduler, IssueScheduleManager issueScheduleManager,
|
||||
IssueLinkManager issueLinkManager, ClusterManager clusterManager,
|
||||
EntityReferenceManager entityReferenceManager) {
|
||||
super(dao);
|
||||
this.issueManager = issueManager;
|
||||
this.issueFieldManager = issueFieldManager;
|
||||
@ -140,11 +128,12 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
this.issueScheduleManager = issueScheduleManager;
|
||||
this.issueLinkManager = issueLinkManager;
|
||||
this.clusterManager = clusterManager;
|
||||
this.entityReferenceManager = entityReferenceManager;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void save(IssueChange change, String note) {
|
||||
public void create(IssueChange change, @Nullable String note) {
|
||||
dao.persist(change);
|
||||
if (note != null) {
|
||||
IssueComment comment = new IssueComment();
|
||||
@ -159,11 +148,6 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
listenerRegistry.post(new IssueChanged(change, note));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(IssueChange change) {
|
||||
save(change, null);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void changeTitle(Issue issue, String title) {
|
||||
@ -175,7 +159,25 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
change.setIssue(issue);
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
change.setData(new IssueTitleChangeData(prevTitle, issue.getTitle()));
|
||||
save(change);
|
||||
create(change, null);
|
||||
dao.persist(issue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeDescription(Issue issue, @Nullable String description) {
|
||||
String prevDescription = issue.getDescription();
|
||||
if (!Objects.equals(description, prevDescription)) {
|
||||
if (description != null && description.length() > Issue.MAX_DESCRIPTION_LEN)
|
||||
throw new ExplicitException("Description too long");
|
||||
issue.setDescription(description);
|
||||
entityReferenceManager.addReferenceChange(issue, description);
|
||||
|
||||
IssueChange change = new IssueChange();
|
||||
change.setIssue(issue);
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
change.setData(new IssueDescriptionChangeData(prevDescription, issue.getDescription()));
|
||||
create(change, null);
|
||||
dao.persist(issue);
|
||||
}
|
||||
}
|
||||
@ -191,7 +193,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
change.setIssue(issue);
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
change.setData(new IssueConfidentialChangeData(prevConfidential, issue.isConfidential()));
|
||||
save(change);
|
||||
create(change, null);
|
||||
dao.persist(issue);
|
||||
}
|
||||
}
|
||||
@ -205,7 +207,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
change.setIssue(issue);
|
||||
change.setData(new IssueMilestoneAddData(milestone.getName()));
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
save(change);
|
||||
create(change, null);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@ -218,7 +220,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
change.setIssue(issue);
|
||||
change.setData(new IssueMilestoneRemoveData(milestone.getName()));
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
save(change);
|
||||
create(change, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,7 +236,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
change.setIssue(issue);
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
change.setData(new IssueFieldChangeData(prevFields, issue.getFieldInputs()));
|
||||
save(change);
|
||||
create(change, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,7 +258,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
change.setData(new IssueStateChangeData(prevState, issue.getState(),
|
||||
prevFields, issue.getFieldInputs()));
|
||||
save(change, comment);
|
||||
create(change, comment);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@ -296,7 +298,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
change.setData(new IssueBatchUpdateData(prevState, issue.getState(),
|
||||
prevConfidential, issue.isConfidential(), prevMilestoneList,
|
||||
currentMilestoneList, prevFields, issue.getFieldInputs()));
|
||||
save(change, comment);
|
||||
create(change, comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -622,7 +624,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
List<Milestone> currentMilestoneList = new ArrayList<>(milestones);
|
||||
currentMilestoneList.sort(new Milestone.DatesAndStatusComparator());
|
||||
change.setData(new IssueMilestoneChangeData(prevMilestoneList, currentMilestoneList));
|
||||
save(change);
|
||||
create(change, null);
|
||||
}
|
||||
|
||||
}
|
||||
@ -645,7 +647,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
getLinkedIssueInfo(issue, prevLinkedIssue),
|
||||
getLinkedIssueInfo(issue, linkedIssue));
|
||||
change.setData(data);
|
||||
save(change);
|
||||
create(change, null);
|
||||
|
||||
logLinkedSideChange(spec, issue, prevLinkedIssue, linkedIssue, opposite);
|
||||
}
|
||||
@ -670,7 +672,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
change.setData(new IssueLinkRemoveData(linkName, prevIssueSummary));
|
||||
else
|
||||
change.setData(new IssueLinkChangeData(linkName, prevIssueSummary, null));
|
||||
save(change);
|
||||
create(change, null);
|
||||
}
|
||||
if (linkedIssue != null) {
|
||||
IssueChange change = new IssueChange();
|
||||
@ -681,7 +683,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
change.setData(new IssueLinkAddData(linkName, issueSummary));
|
||||
else
|
||||
change.setData(new IssueLinkChangeData(linkName, null, issueSummary));
|
||||
save(change);
|
||||
create(change, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -711,7 +713,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
String linkName = spec.getName(opposite);
|
||||
IssueLinkAddData data = new IssueLinkAddData(linkName, getLinkedIssueInfo(issue, linkedIssue));
|
||||
change.setData(data);
|
||||
save(change);
|
||||
create(change, null);
|
||||
|
||||
logLinkedSideChange(spec, issue, null, linkedIssue, opposite);
|
||||
}
|
||||
@ -730,7 +732,7 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
String linkName = spec.getName(opposite);
|
||||
IssueLinkRemoveData data = new IssueLinkRemoveData(linkName, getLinkedIssueInfo(issue, linkedIssue));
|
||||
change.setData(data);
|
||||
save(change);
|
||||
create(change, null);
|
||||
|
||||
logLinkedSideChange(spec, issue, linkedIssue, null, opposite);
|
||||
}
|
||||
|
||||
@ -1,25 +1,23 @@
|
||||
package io.onedev.server.entitymanager.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.onedev.server.entitymanager.IssueCommentManager;
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.project.issue.IssueCommented;
|
||||
import io.onedev.server.event.project.issue.IssueCommentCreated;
|
||||
import io.onedev.server.event.project.issue.IssueCommentDeleted;
|
||||
import io.onedev.server.event.project.issue.IssueCommentUpdated;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.persistence.SessionManager;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.persistence.dao.BaseEntityManager;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
|
||||
@Singleton
|
||||
public class DefaultIssueCommentManager extends BaseEntityManager<IssueComment>
|
||||
implements IssueCommentManager {
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Collection;
|
||||
|
||||
@Singleton
|
||||
public class DefaultIssueCommentManager extends BaseEntityManager<IssueComment> implements IssueCommentManager {
|
||||
|
||||
private final ListenerRegistry listenerRegistry;
|
||||
|
||||
private final SessionManager sessionManager;
|
||||
@ -33,35 +31,26 @@ public class DefaultIssueCommentManager extends BaseEntityManager<IssueComment>
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void save(IssueComment comment) {
|
||||
save(comment, Lists.newArrayList());
|
||||
public void update(IssueComment comment) {
|
||||
dao.persist(comment);
|
||||
listenerRegistry.post(new IssueCommentUpdated(comment));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void delete(IssueComment comment) {
|
||||
super.delete(comment);
|
||||
dao.remove(comment);
|
||||
|
||||
comment.getIssue().setCommentCount(comment.getIssue().getCommentCount()-1);
|
||||
listenerRegistry.post(new IssueCommentDeleted(comment));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void save(IssueComment comment, Collection<String> notifiedEmailAddresses) {
|
||||
boolean isNew = comment.isNew();
|
||||
public void create(IssueComment comment, Collection<String> notifiedEmailAddresses) {
|
||||
dao.persist(comment);
|
||||
if (isNew) {
|
||||
comment.getIssue().setCommentCount(comment.getIssue().getCommentCount()+1);
|
||||
|
||||
Long commentId = comment.getId();
|
||||
sessionManager.runAsyncAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
listenerRegistry.post(new IssueCommented(load(commentId), notifiedEmailAddresses));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
comment.getIssue().setCommentCount(comment.getIssue().getCommentCount()+1);
|
||||
listenerRegistry.post(new IssueCommentCreated(comment, notifiedEmailAddresses));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,22 +5,13 @@ import com.google.common.collect.Lists;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.attachment.AttachmentManager;
|
||||
import io.onedev.server.cluster.ClusterManager;
|
||||
import io.onedev.server.cluster.ClusterRunnable;
|
||||
import io.onedev.server.cluster.ClusterTask;
|
||||
import io.onedev.server.entitymanager.*;
|
||||
import io.onedev.server.entityreference.EntityReferenceManager;
|
||||
import io.onedev.server.entityreference.ReferenceMigrator;
|
||||
import io.onedev.server.entityreference.ReferencedFromAware;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.entity.EntityRemoved;
|
||||
import io.onedev.server.event.project.issue.IssueCommitsAttached;
|
||||
import io.onedev.server.event.project.issue.IssueChanged;
|
||||
import io.onedev.server.event.project.issue.IssueEvent;
|
||||
import io.onedev.server.event.project.issue.IssueOpened;
|
||||
import io.onedev.server.event.project.issue.*;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.migration.VersionedXmlDoc;
|
||||
import io.onedev.server.model.*;
|
||||
@ -29,7 +20,6 @@ import io.onedev.server.model.support.administration.GlobalIssueSetting;
|
||||
import io.onedev.server.model.support.inputspec.choiceinput.choiceprovider.SpecifiedChoices;
|
||||
import io.onedev.server.model.support.issue.NamedIssueQuery;
|
||||
import io.onedev.server.model.support.issue.StateSpec;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueChangeData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueProjectChangeData;
|
||||
import io.onedev.server.model.support.issue.field.spec.FieldSpec;
|
||||
import io.onedev.server.persistence.SequenceGenerator;
|
||||
@ -47,7 +37,10 @@ import io.onedev.server.search.entity.issue.IssueQueryParseOption;
|
||||
import io.onedev.server.search.entity.issue.IssueQueryUpdater;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.security.permission.AccessProject;
|
||||
import io.onedev.server.util.*;
|
||||
import io.onedev.server.util.MilestoneAndIssueState;
|
||||
import io.onedev.server.util.ProjectIssueStats;
|
||||
import io.onedev.server.util.ProjectScope;
|
||||
import io.onedev.server.util.ProjectScopedNumber;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
import io.onedev.server.util.validation.ProjectPathValidator;
|
||||
import io.onedev.server.web.component.issue.workflowreconcile.UndefinedFieldResolution;
|
||||
@ -55,9 +48,6 @@ import io.onedev.server.web.component.issue.workflowreconcile.UndefinedFieldValu
|
||||
import io.onedev.server.web.component.issue.workflowreconcile.UndefinedFieldValuesResolution;
|
||||
import io.onedev.server.web.component.issue.workflowreconcile.UndefinedStateResolution;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.ImmutableTriple;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
import org.apache.wicket.util.lang.Objects;
|
||||
import org.hibernate.criterion.Order;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
import org.hibernate.query.Query;
|
||||
@ -68,7 +58,6 @@ import org.unbescape.java.JavaEscape;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.*;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
@ -104,10 +93,6 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
|
||||
private final IssueQueryPersonalizationManager queryPersonalizationManager;
|
||||
|
||||
private final AttachmentManager attachmentManager;
|
||||
|
||||
private final IssueCommentManager commentManager;
|
||||
|
||||
private final SettingManager settingManager;
|
||||
|
||||
private final ProjectManager projectManager;
|
||||
@ -118,8 +103,6 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
|
||||
private final TransactionManager transactionManager;
|
||||
|
||||
private final EntityReferenceManager entityReferenceManager;
|
||||
|
||||
private final RoleManager roleManager;
|
||||
|
||||
private final LinkSpecManager linkSpecManager;
|
||||
@ -128,10 +111,6 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
private final IssueChangeManager changeManager;
|
||||
|
||||
private final IssueScheduleManager scheduleManager;
|
||||
|
||||
private final SequenceGenerator numberGenerator;
|
||||
|
||||
private volatile Map<String, Long> issueIds;
|
||||
@ -141,11 +120,8 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
IssueQueryPersonalizationManager queryPersonalizationManager,
|
||||
SettingManager settingManager, ListenerRegistry listenerRegistry,
|
||||
ProjectManager projectManager, UserManager userManager, ClusterManager clusterManager,
|
||||
RoleManager roleManager, AttachmentManager attachmentStorageManager,
|
||||
IssueCommentManager commentManager, EntityReferenceManager entityReferenceManager,
|
||||
LinkSpecManager linkSpecManager, IssueLinkManager linkManager,
|
||||
IssueAuthorizationManager authorizationManager, IssueChangeManager changeManager,
|
||||
IssueScheduleManager scheduleManager) {
|
||||
RoleManager roleManager, LinkSpecManager linkSpecManager, IssueLinkManager linkManager,
|
||||
IssueAuthorizationManager authorizationManager) {
|
||||
super(dao);
|
||||
this.fieldManager = fieldManager;
|
||||
this.queryPersonalizationManager = queryPersonalizationManager;
|
||||
@ -157,13 +133,8 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
this.roleManager = roleManager;
|
||||
this.linkSpecManager = linkSpecManager;
|
||||
this.linkManager = linkManager;
|
||||
this.attachmentManager = attachmentStorageManager;
|
||||
this.commentManager = commentManager;
|
||||
this.entityReferenceManager = entityReferenceManager;
|
||||
this.authorizationManager = authorizationManager;
|
||||
this.clusterManager = clusterManager;
|
||||
this.changeManager = changeManager;
|
||||
this.scheduleManager = scheduleManager;
|
||||
|
||||
numberGenerator = new SequenceGenerator(Issue.class, clusterManager, dao);
|
||||
}
|
||||
@ -186,7 +157,7 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
Long issueId = (Long) fields[0];
|
||||
Long projectId = (Long)fields[1];
|
||||
Long issueNumber = (Long) fields[2];
|
||||
issueIds.put(projectId + ":" + issueNumber, issueId);
|
||||
issueIds.put(getCacheKey(projectId, issueNumber), issueId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,7 +204,7 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
lastActivity.setDate(issue.getSubmitDate());
|
||||
issue.setLastActivity(lastActivity);
|
||||
|
||||
save(issue);
|
||||
dao.persist(issue);
|
||||
|
||||
fieldManager.saveFields(issue);
|
||||
for (IssueSchedule schedule: issue.getSchedules())
|
||||
@ -245,28 +216,10 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
issue.getAuthorizations().add(authorization);
|
||||
authorizationManager.save(authorization);
|
||||
|
||||
updateCacheAfterCommit(Lists.newArrayList(issue));
|
||||
listenerRegistry.post(new IssueOpened(issue));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void save(Issue issue) {
|
||||
super.save(issue);
|
||||
|
||||
Long projectId = issue.getProject().getId();
|
||||
Long issueId = issue.getId();
|
||||
Long issueNumber = issue.getNumber();
|
||||
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
issueIds.put(projectId + ":" + issueNumber, issueId);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private List<javax.persistence.criteria.Order> getOrders(List<EntitySort> sorts, CriteriaBuilder builder, Root<Issue> root) {
|
||||
List<javax.persistence.criteria.Order> orders = new ArrayList<>();
|
||||
for (EntitySort sort: sorts) {
|
||||
@ -321,16 +274,7 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
@Transactional
|
||||
@Listen
|
||||
public void on(IssueEvent event) {
|
||||
boolean minorChange = false;
|
||||
if (event instanceof IssueCommitsAttached) {
|
||||
minorChange = true;
|
||||
} else if (event instanceof IssueChanged) {
|
||||
IssueChangeData changeData = ((IssueChanged)event).getChange().getData();
|
||||
if (changeData instanceof ReferencedFromAware)
|
||||
minorChange = true;
|
||||
}
|
||||
|
||||
if (!(event instanceof IssueOpened || minorChange))
|
||||
if (!(event instanceof IssueOpened || event.isMinor()))
|
||||
event.getIssue().setLastActivity(event.getLastUpdate());
|
||||
}
|
||||
|
||||
@ -904,19 +848,51 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
@Transactional
|
||||
@Override
|
||||
public void delete(Issue issue) {
|
||||
super.delete(issue);
|
||||
dao.remove(issue);
|
||||
|
||||
Long projectId = issue.getProject().getId();
|
||||
Long issueNumber = issue.getNumber();
|
||||
removeFromCacheAfterCommit(Lists.newArrayList(issue));
|
||||
listenerRegistry.post(new IssuesDeleted(issue.getProject(), Lists.newArrayList(issue)));
|
||||
}
|
||||
|
||||
private String getCacheKey(Issue issue) {
|
||||
return getCacheKey(issue.getProject().getId(), issue.getNumber());
|
||||
}
|
||||
|
||||
private String getCacheKey(Long projectId, Long issueNumber) {
|
||||
return projectId + ":" + issueNumber;
|
||||
}
|
||||
|
||||
private void removeFromCacheAfterCommit(Collection<Issue> issues) {
|
||||
Collection<String> cacheKeysToDelete = new ArrayList<>();
|
||||
for (Issue issue: issues)
|
||||
cacheKeysToDelete.add(getCacheKey(issue));
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
issueIds.remove(new ProjectScopedNumber(projectId, issueNumber));
|
||||
for (var issueKey: cacheKeysToDelete)
|
||||
issueIds.remove(issueKey);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void updateCacheAfterCommit(Collection<Issue> issues) {
|
||||
Map<String, Long> cacheEntriesToUpdate = new HashMap<>();
|
||||
for (Issue issue: issues)
|
||||
cacheEntriesToUpdate.put(getCacheKey(issue), issue.getId());
|
||||
transactionManager.runAfterCommit(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (var entry: cacheEntriesToUpdate.entrySet())
|
||||
issueIds.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Listen
|
||||
public void on(EntityRemoved event) {
|
||||
@ -940,9 +916,16 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Listen
|
||||
@Sessional
|
||||
public void on(IssuesImported event) {
|
||||
for (var issueId: event.getIssueIds())
|
||||
issueIds.put(getCacheKey(dao.load(Issue.class, issueId)), issueId);
|
||||
}
|
||||
|
||||
private Long getIssueId(Long projectId, Long issueNumber) {
|
||||
return issueIds.get(projectId + ":" + issueNumber);
|
||||
return issueIds.get(getCacheKey(projectId, issueNumber));
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@ -992,12 +975,11 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void copy(Collection<Issue> issues, Project targetProject) {
|
||||
public void copy(Collection<Issue> issues, Project sourceProject, Project targetProject) {
|
||||
List<Issue> sortedIssues = new ArrayList<>(issues);
|
||||
Collections.sort(sortedIssues);
|
||||
Map<Issue, Issue> cloneMapping = new HashMap<>();
|
||||
Map<Long, Long> numberMapping = new HashMap<>();
|
||||
List<Triple<Long, String, String>> attachmentGroupInfos = new ArrayList<>();
|
||||
|
||||
sortedIssues.forEach(issue -> {
|
||||
Issue clonedIssue = VersionedXmlDoc.cloneBean(issue);
|
||||
@ -1009,10 +991,6 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
clonedIssue.setNumber(getNextNumber(numberScope));
|
||||
cloneMapping.put(issue, clonedIssue);
|
||||
numberMapping.put(issue.getNumber(), clonedIssue.getNumber());
|
||||
attachmentGroupInfos.add(new ImmutableTriple<>(
|
||||
issue.getAttachmentProject().getId(),
|
||||
issue.getAttachmentGroup(),
|
||||
clonedIssue.getAttachmentGroup()));
|
||||
});
|
||||
|
||||
cloneMapping.forEach((key, value) -> {
|
||||
@ -1025,17 +1003,8 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
.migratePrefixed(description, "#");
|
||||
value.setDescription(description);
|
||||
}
|
||||
save(value);
|
||||
dao.persist(value);
|
||||
|
||||
for (IssueSchedule schedule: key.getSchedules()) {
|
||||
if (schedule.getMilestone().getProject().isSelfOrAncestorOf(targetProject)) {
|
||||
IssueSchedule clonedSchedule = VersionedXmlDoc.cloneBean(schedule);
|
||||
clonedSchedule.setId(null);
|
||||
clonedSchedule.setIssue(value);
|
||||
dao.persist(clonedSchedule);
|
||||
}
|
||||
}
|
||||
|
||||
key.getComments().forEach(comment -> {
|
||||
var clonedComment = VersionedXmlDoc.cloneBean(comment);
|
||||
clonedComment.setId(null);
|
||||
@ -1050,13 +1019,6 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
dao.persist(clonedComment);
|
||||
});
|
||||
|
||||
key.getChanges().forEach(change -> {
|
||||
var clonedChange = VersionedXmlDoc.cloneBean(change);
|
||||
clonedChange.setId(null);
|
||||
clonedChange.setIssue(value);
|
||||
dao.persist(clonedChange);
|
||||
});
|
||||
|
||||
key.getFields().forEach(field -> {
|
||||
var clonedField = VersionedXmlDoc.cloneBean(field);
|
||||
clonedField.setId(null);
|
||||
@ -1093,52 +1055,26 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Long targetProjectId = targetProject.getId();
|
||||
transactionManager.runAfterCommit(new ClusterRunnable() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
projectManager.runOnProjectServer(targetProjectId, new ClusterTask<Void>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
for (var attachmentGroupInfo: attachmentGroupInfos) {
|
||||
attachmentManager.copyAttachmentGroupTargetLocal(targetProjectId,
|
||||
attachmentGroupInfo.getRight(), attachmentGroupInfo.getLeft(),
|
||||
attachmentGroupInfo.getMiddle());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
updateCacheAfterCommit(cloneMapping.values());
|
||||
listenerRegistry.post(new IssuesCopied(sourceProject, targetProject, cloneMapping));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void move(Collection<Issue> issues, Project targetProject) {
|
||||
List<Pair<Long, String>> attachmentGroupInfos = new ArrayList<>();
|
||||
public void move(Collection<Issue> issues, Project sourceProject, Project targetProject) {
|
||||
Map<Long, Long> numberMapping = new HashMap<>();
|
||||
List<Issue> sortedIssues = new ArrayList<>(issues);
|
||||
Collections.sort(sortedIssues);
|
||||
for (Issue issue: sortedIssues) {
|
||||
attachmentGroupInfos.add(new Pair<>(issue.getAttachmentProject().getId(), issue.getAttachmentGroup()));
|
||||
|
||||
if (issue.getDescription() != null) {
|
||||
issue.setDescription(issue.getDescription().replace(
|
||||
issue.getAttachmentProject().getId() + "/attachments/" + issue.getAttachmentGroup(),
|
||||
sourceProject.getId() + "/attachments/" + issue.getAttachmentGroup(),
|
||||
targetProject.getId() + "/attachments/" + issue.getAttachmentGroup()));
|
||||
}
|
||||
for (IssueComment comment: issue.getComments()) {
|
||||
comment.setContent(comment.getContent().replace(
|
||||
issue.getAttachmentProject().getId() + "/attachments/" + issue.getAttachmentGroup(),
|
||||
sourceProject.getId() + "/attachments/" + issue.getAttachmentGroup(),
|
||||
targetProject.getId() + "/attachments/" + issue.getAttachmentGroup()));
|
||||
}
|
||||
|
||||
@ -1163,7 +1099,7 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
change.setIssue(issue);
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
change.setData(new IssueProjectChangeData(oldProject.getPath(), targetProject.getPath()));
|
||||
changeManager.save(change);
|
||||
dao.persist(change);
|
||||
}
|
||||
|
||||
for (Issue issue: sortedIssues) {
|
||||
@ -1175,53 +1111,22 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
for (IssueComment comment: issue.getComments()) {
|
||||
comment.setContent(new ReferenceMigrator(Issue.class, numberMapping)
|
||||
.migratePrefixed(comment.getContent(), "#"));
|
||||
commentManager.save(comment);
|
||||
dao.persist(comment);
|
||||
}
|
||||
save(issue);
|
||||
dao.persist(issue);
|
||||
}
|
||||
|
||||
Long targetProjectId = targetProject.getId();
|
||||
transactionManager.runAfterCommit(new ClusterRunnable() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
projectManager.runOnProjectServer(targetProjectId, new ClusterTask<Void>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
for (var attachmentGroupInfo: attachmentGroupInfos)
|
||||
attachmentManager.moveAttachmentGroupTargetLocal(targetProjectId, attachmentGroupInfo.getFirst(), attachmentGroupInfo.getSecond());
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
updateCacheAfterCommit(issues);
|
||||
listenerRegistry.post(new IssuesMoved(sourceProject, targetProject, issues));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void saveDescription(Issue issue, @Nullable String description) {
|
||||
String prevDescription = issue.getDescription();
|
||||
if (!Objects.equal(description, prevDescription)) {
|
||||
if (description != null && description.length() > Issue.MAX_DESCRIPTION_LEN)
|
||||
throw new ExplicitException("Description too long");
|
||||
issue.setDescription(description);
|
||||
entityReferenceManager.addReferenceChange(issue, description);
|
||||
save(issue);
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void delete(Collection<Issue> issues) {
|
||||
public void delete(Collection<Issue> issues, Project project) {
|
||||
for (Issue issue: issues)
|
||||
delete(issue);
|
||||
dao.remove(issue);
|
||||
removeFromCacheAfterCommit(issues);
|
||||
listenerRegistry.post(new IssuesDeleted(project, issues));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
|
||||
@ -20,6 +20,7 @@ import io.onedev.server.cluster.ProjectServer;
|
||||
import io.onedev.server.entitymanager.*;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.project.ProjectDeleted;
|
||||
import io.onedev.server.event.entity.EntityPersisted;
|
||||
import io.onedev.server.event.entity.EntityRemoved;
|
||||
import io.onedev.server.event.project.ProjectCreated;
|
||||
@ -416,6 +417,8 @@ public class DefaultProjectManager extends BaseEntityManager<Project>
|
||||
if (repository != null)
|
||||
repository.close();
|
||||
}
|
||||
|
||||
listenerRegistry.post(new ProjectDeleted(SecurityUtils.getUser(), new Date(), project));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -19,7 +19,7 @@ import io.onedev.server.event.project.codecomment.CodeCommentReplied;
|
||||
import io.onedev.server.event.project.codecomment.CodeCommentStatusChanged;
|
||||
import io.onedev.server.event.project.codecomment.CodeCommentUpdated;
|
||||
import io.onedev.server.event.project.issue.IssueChanged;
|
||||
import io.onedev.server.event.project.issue.IssueCommented;
|
||||
import io.onedev.server.event.project.issue.IssueCommentCreated;
|
||||
import io.onedev.server.event.project.issue.IssueOpened;
|
||||
import io.onedev.server.event.project.pullrequest.PullRequestChanged;
|
||||
import io.onedev.server.event.project.pullrequest.PullRequestCommented;
|
||||
@ -78,7 +78,7 @@ public class DefaultEntityReferenceManager implements EntityReferenceManager {
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
change.setIssue(referencedIssue);
|
||||
referencedIssue.getChanges().add(change);
|
||||
issueChangeManager.save(change);
|
||||
issueChangeManager.create(change, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -139,7 +139,7 @@ public class DefaultEntityReferenceManager implements EntityReferenceManager {
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
change.setIssue(referencedIssue);
|
||||
referencedIssue.getChanges().add(change);
|
||||
issueChangeManager.save(change);
|
||||
issueChangeManager.create(change, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,7 +200,7 @@ public class DefaultEntityReferenceManager implements EntityReferenceManager {
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
change.setIssue(referencedIssue);
|
||||
referencedIssue.getChanges().add(change);
|
||||
issueChangeManager.save(change);
|
||||
issueChangeManager.create(change, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,7 +236,7 @@ public class DefaultEntityReferenceManager implements EntityReferenceManager {
|
||||
|
||||
@Transactional
|
||||
@Listen
|
||||
public void on(IssueCommented event) {
|
||||
public void on(IssueCommentCreated event) {
|
||||
addReferenceChange(event.getIssue(), event.getComment().getContent());
|
||||
}
|
||||
|
||||
|
||||
@ -3,9 +3,11 @@ package io.onedev.server.event;
|
||||
import io.onedev.commons.loader.AppLoader;
|
||||
import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.commons.utils.LockUtils;
|
||||
import io.onedev.server.cluster.ClusterManager;
|
||||
import io.onedev.server.cluster.ClusterRunnable;
|
||||
import io.onedev.server.cluster.ClusterTask;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.event.project.ProjectDeleted;
|
||||
import io.onedev.server.event.project.ProjectEvent;
|
||||
import io.onedev.server.persistence.SessionManager;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
@ -29,15 +31,18 @@ public class DefaultListenerRegistry implements ListenerRegistry, Serializable {
|
||||
|
||||
private final SessionManager sessionManager;
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
private volatile Map<Object, Collection<Method>> listenerMethods;
|
||||
|
||||
private final Map<Class<?>, List<Listener>> listeners = new ConcurrentHashMap<>();
|
||||
|
||||
@Inject
|
||||
public DefaultListenerRegistry(ProjectManager projectManager,
|
||||
TransactionManager transactionManager, SessionManager sessionManager) {
|
||||
public DefaultListenerRegistry(ProjectManager projectManager, ClusterManager clusterManager,
|
||||
TransactionManager transactionManager, SessionManager sessionManager) {
|
||||
this.projectManager = projectManager;
|
||||
this.transactionManager = transactionManager;
|
||||
this.clusterManager = clusterManager;
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
@ -103,7 +108,7 @@ public class DefaultListenerRegistry implements ListenerRegistry, Serializable {
|
||||
if (event instanceof ProjectEvent) {
|
||||
ProjectEvent projectEvent = (ProjectEvent) event;
|
||||
Long projectId = projectEvent.getProject().getId();
|
||||
|
||||
|
||||
transactionManager.runAfterCommit(new ClusterRunnable() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -128,11 +133,11 @@ public class DefaultListenerRegistry implements ListenerRegistry, Serializable {
|
||||
public void run() {
|
||||
invokeListeners(event);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
} else {
|
||||
sessionManager.run(new Runnable() {
|
||||
@ -141,16 +146,49 @@ public class DefaultListenerRegistry implements ListenerRegistry, Serializable {
|
||||
public void run() {
|
||||
invokeListeners(event);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
} else if (event instanceof ProjectDeleted) {
|
||||
ProjectDeleted projectDeleted = (ProjectDeleted) event;
|
||||
Long projectId = projectDeleted.getProjectId();
|
||||
UUID projectStorageServerUUID = projectManager.getStorageServerUUID(projectId, false);
|
||||
if (projectStorageServerUUID != null) {
|
||||
transactionManager.runAfterCommit(new ClusterRunnable() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
clusterManager.submitToServer(projectStorageServerUUID, new ClusterTask<Void>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
sessionManager.run(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
invokeListeners(event);
|
||||
}
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
} else {
|
||||
invokeListeners(event);
|
||||
}
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
package io.onedev.server.event.project;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
public class ProjectDeleted implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Long projectId;
|
||||
|
||||
private final Long userId;
|
||||
|
||||
private final Date date;
|
||||
|
||||
public ProjectDeleted(User user, Date date, Project project) {
|
||||
userId = user.getId();
|
||||
this.date = date;
|
||||
projectId = project.getId();
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return OneDev.getInstance(UserManager.class).load(userId);
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
}
|
||||
@ -113,4 +113,8 @@ public abstract class ProjectEvent implements Serializable {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isMinor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -72,5 +72,9 @@ public class IssueChanged extends IssueEvent {
|
||||
public String getUrl() {
|
||||
return OneDev.getInstance(UrlManager.class).urlFor(getChange());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isMinor() {
|
||||
return getChange().isMinor();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
package io.onedev.server.event.project.issue;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.IssueCommentManager;
|
||||
import io.onedev.server.entitymanager.UrlManager;
|
||||
@ -9,7 +7,9 @@ import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.util.commenttext.CommentText;
|
||||
import io.onedev.server.util.commenttext.MarkdownText;
|
||||
|
||||
public class IssueCommented extends IssueEvent {
|
||||
import java.util.Collection;
|
||||
|
||||
public class IssueCommentCreated extends IssueEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -17,7 +17,7 @@ public class IssueCommented extends IssueEvent {
|
||||
|
||||
private final Collection<String> notifiedEmailAddresses;
|
||||
|
||||
public IssueCommented(IssueComment comment, Collection<String> notifiedEmailAddresses) {
|
||||
public IssueCommentCreated(IssueComment comment, Collection<String> notifiedEmailAddresses) {
|
||||
super(comment.getUser(), comment.getDate(), comment.getIssue());
|
||||
commentId = comment.getId();
|
||||
this.notifiedEmailAddresses = notifiedEmailAddresses;
|
||||
@ -50,5 +50,5 @@ public class IssueCommented extends IssueEvent {
|
||||
public String getUrl() {
|
||||
return OneDev.getInstance(UrlManager.class).urlFor(getComment());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package io.onedev.server.event.project.issue;
|
||||
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class IssueCommentDeleted extends IssueEvent {
|
||||
|
||||
private final Long commentId;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public IssueCommentDeleted(IssueComment comment) {
|
||||
super(SecurityUtils.getUser(), new Date(), comment.getIssue());
|
||||
this.commentId = comment.getId();
|
||||
}
|
||||
|
||||
public Long getCommentId() {
|
||||
return commentId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean affectsListing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMinor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "comment deleted";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package io.onedev.server.event.project.issue;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.IssueCommentManager;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class IssueCommentUpdated extends IssueEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Long commentId;
|
||||
|
||||
public IssueCommentUpdated(IssueComment comment) {
|
||||
super(SecurityUtils.getUser(), new Date(), comment.getIssue());
|
||||
this.commentId = comment.getId();
|
||||
}
|
||||
|
||||
public IssueComment getComment() {
|
||||
return OneDev.getInstance(IssueCommentManager.class).load(commentId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean affectsListing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMinor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "comment updated";
|
||||
}
|
||||
|
||||
}
|
||||
@ -21,5 +21,10 @@ public class IssueCommitsAttached extends IssueEvent {
|
||||
public String getActivity() {
|
||||
return "commits attached";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMinor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
package io.onedev.server.event.project.issue;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.event.project.ProjectEvent;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class IssuesCopied extends ProjectEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Long sourceProjectId;
|
||||
|
||||
private final Map<Long, Long> issueIdMapping;
|
||||
|
||||
public IssuesCopied(Project sourceProject, Project targetProject, Map<Issue, Issue> issueMapping) {
|
||||
super(SecurityUtils.getUser(), new Date(), targetProject);
|
||||
sourceProjectId = sourceProject.getId();
|
||||
issueIdMapping = new HashMap<>();
|
||||
for (var entry: issueMapping.entrySet())
|
||||
issueIdMapping.put(entry.getKey().getId(), entry.getValue().getId());
|
||||
}
|
||||
|
||||
public Project getSourceProject() {
|
||||
return OneDev.getInstance(Dao.class).load(Project.class, sourceProjectId);
|
||||
}
|
||||
|
||||
public Project getTargetProject() {
|
||||
return getProject();
|
||||
}
|
||||
|
||||
public Map<Long, Long> getIssueIdMapping() {
|
||||
return issueIdMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "issues copied";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package io.onedev.server.event.project.issue;
|
||||
|
||||
import io.onedev.server.event.project.ProjectEvent;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class IssuesDeleted extends ProjectEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Collection<Long> issueIds;
|
||||
|
||||
public IssuesDeleted(Project project, Collection<Issue> issues) {
|
||||
super(SecurityUtils.getUser(), new Date(), project);
|
||||
issueIds = issues.stream().map(Issue::getId).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Collection<Long> getIssueIds() {
|
||||
return issueIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "issues deleted";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package io.onedev.server.event.project.issue;
|
||||
|
||||
import io.onedev.server.event.project.ProjectEvent;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class IssuesImported extends ProjectEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Collection<Long> issueIds;
|
||||
|
||||
public IssuesImported(Project project, Collection<Issue> issues) {
|
||||
super(SecurityUtils.getUser(), new Date(), project);
|
||||
issueIds = issues.stream().map(Issue::getId).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Collection<Long> getIssueIds() {
|
||||
return issueIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "issues imported";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package io.onedev.server.event.project.issue;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.event.project.ProjectEvent;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class IssuesMoved extends ProjectEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Long sourceProjectId;
|
||||
|
||||
private final Collection<Long> issueIds;
|
||||
|
||||
public IssuesMoved(Project sourceProject, Project targetProject, Collection<Issue> issues) {
|
||||
super(SecurityUtils.getUser(), new Date(), targetProject);
|
||||
sourceProjectId = sourceProject.getId();
|
||||
issueIds = issues.stream().map(Issue::getId).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Project getSourceProject() {
|
||||
return OneDev.getInstance(Dao.class).load(Project.class, sourceProjectId);
|
||||
}
|
||||
|
||||
public Project getTargetProject() {
|
||||
return getProject();
|
||||
}
|
||||
|
||||
public Collection<Long> getIssueIds() {
|
||||
return issueIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "issues moved";
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,23 +1,6 @@
|
||||
package io.onedev.server.infomanager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.commons.utils.FileUtils;
|
||||
import io.onedev.server.cluster.ClusterManager;
|
||||
@ -28,16 +11,17 @@ import io.onedev.server.entitymanager.IssueManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.event.entity.EntityPersisted;
|
||||
import io.onedev.server.event.entity.EntityRemoved;
|
||||
import io.onedev.server.event.project.ProjectDeleted;
|
||||
import io.onedev.server.event.project.issue.*;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueChange;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueBatchUpdateData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueStateChangeData;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.storage.StorageManager;
|
||||
import io.onedev.server.util.Day;
|
||||
import io.onedev.server.util.concurrent.BatchWorkManager;
|
||||
@ -45,11 +29,20 @@ import io.onedev.server.util.concurrent.BatchWorker;
|
||||
import io.onedev.server.util.concurrent.Prioritized;
|
||||
import jetbrains.exodus.ArrayByteIterable;
|
||||
import jetbrains.exodus.ByteIterable;
|
||||
import jetbrains.exodus.env.Environment;
|
||||
import jetbrains.exodus.env.Store;
|
||||
import jetbrains.exodus.env.Transaction;
|
||||
import jetbrains.exodus.env.TransactionalComputable;
|
||||
import jetbrains.exodus.env.TransactionalExecutable;
|
||||
import jetbrains.exodus.env.*;
|
||||
import org.apache.commons.lang.SerializationUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.File;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Singleton
|
||||
public class DefaultIssueInfoManager extends AbstractMultiEnvironmentManager
|
||||
@ -87,11 +80,14 @@ public class DefaultIssueInfoManager extends AbstractMultiEnvironmentManager
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
private final Dao dao;
|
||||
|
||||
@Inject
|
||||
public DefaultIssueInfoManager(TransactionManager transactionManager,
|
||||
StorageManager storageManager, IssueManager issueManager,
|
||||
IssueChangeManager issueChangeManager, BatchWorkManager batchWorkManager,
|
||||
ProjectManager projectManager, ClusterManager clusterManager) {
|
||||
public DefaultIssueInfoManager(Dao dao, TransactionManager transactionManager,
|
||||
StorageManager storageManager, IssueManager issueManager,
|
||||
IssueChangeManager issueChangeManager, BatchWorkManager batchWorkManager,
|
||||
ProjectManager projectManager, ClusterManager clusterManager) {
|
||||
this.dao = dao;
|
||||
this.storageManager = storageManager;
|
||||
this.issueManager = issueManager;
|
||||
this.issueChangeManager = issueChangeManager;
|
||||
@ -294,51 +290,61 @@ public class DefaultIssueInfoManager extends AbstractMultiEnvironmentManager
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(EntityRemoved event) {
|
||||
if (event.getEntity() instanceof Issue) {
|
||||
Issue issue = (Issue) event.getEntity();
|
||||
Long projectId = issue.getProject().getId();
|
||||
Long issueId = issue.getId();
|
||||
transactionManager.runAfterCommit(new ClusterRunnable() {
|
||||
public void on(ProjectDeleted event) {
|
||||
removeEnv(event.getProjectId().toString());
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssueOpened event) {
|
||||
batchWorkManager.submit(getBatchWorker(event.getProject().getId()), new Prioritized(PRIORITY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
projectManager.submitToProjectServer(projectId, new ClusterTask<Void>() {
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesCopied event) {
|
||||
batchWorkManager.submit(getBatchWorker(event.getProject().getId()), new Prioritized(PRIORITY));
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesImported event) {
|
||||
batchWorkManager.submit(getBatchWorker(event.getProject().getId()), new Prioritized(PRIORITY));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
remove(projectId, issueId);
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
} else if (event.getEntity() instanceof Project) {
|
||||
Long projectId = event.getEntity().getId();
|
||||
UUID storageServerUUID = projectManager.getStorageServerUUID(projectId, false);
|
||||
if (storageServerUUID != null) {
|
||||
clusterManager.runOnServer(storageServerUUID, new ClusterTask<Void>() {
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssueChanged event) {
|
||||
batchWorkManager.submit(getBatchWorker(event.getProject().getId()), new Prioritized(PRIORITY));
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesDeleted event) {
|
||||
for (var issueId: event.getIssueIds())
|
||||
remove(event.getProject().getId(), issueId);
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesMoved event) {
|
||||
Long sourceProjectId = event.getSourceProject().getId();
|
||||
projectManager.submitToProjectServer(sourceProjectId, new ClusterTask<Void>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
removeEnv(projectId.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
event.getIssueIds().forEach(it->remove(sourceProjectId, it));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
batchWorkManager.submit(getBatchWorker(event.getProject().getId()), new Prioritized(PRIORITY));
|
||||
}
|
||||
|
||||
@Sessional
|
||||
|
||||
@ -680,7 +680,7 @@ public class DefaultMailManager implements MailManager, Serializable {
|
||||
// Add double line breaks in the beginning and ending as otherwise plain text content
|
||||
// received from email may not be formatted correctly with our markdown renderer.
|
||||
comment.setContent(decorateContent(content));
|
||||
issueCommentManager.save(comment, receiverEmailAddresses);
|
||||
issueCommentManager.create(comment, receiverEmailAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,50 +1,25 @@
|
||||
package io.onedev.server.model;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import io.onedev.server.model.support.CompareContext;
|
||||
import io.onedev.server.model.support.EntityComment;
|
||||
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(indexes={
|
||||
@Index(columnList="o_comment_id"), @Index(columnList="o_user_id"),
|
||||
@Index(columnList="o_pullRequest_id"),
|
||||
})
|
||||
public class CodeCommentReply extends AbstractEntity {
|
||||
public class CodeCommentReply extends EntityComment {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int MAX_CONTENT_LEN = 14000;
|
||||
|
||||
public static final String PROP_COMPARE_CONTEXT = "compareContext";
|
||||
|
||||
public static final String PROP_CONTENT = "content";
|
||||
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@JoinColumn(nullable=false)
|
||||
private CodeComment comment;
|
||||
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@JoinColumn(nullable=false)
|
||||
private User user;
|
||||
|
||||
@Column(nullable=false)
|
||||
private Date date;
|
||||
|
||||
@Column(nullable=false, length=MAX_CONTENT_LEN)
|
||||
private String content;
|
||||
|
||||
@Embedded
|
||||
private CompareContext compareContext;
|
||||
|
||||
@ -55,36 +30,6 @@ public class CodeCommentReply extends AbstractEntity {
|
||||
public void setComment(CodeComment comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getAnchor() {
|
||||
return getClass().getSimpleName() + "-" + getId();
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = StringUtils.abbreviate(content, MAX_CONTENT_LEN);
|
||||
}
|
||||
|
||||
public CompareContext getCompareContext() {
|
||||
return compareContext;
|
||||
}
|
||||
@ -93,4 +38,9 @@ public class CodeCommentReply extends AbstractEntity {
|
||||
this.compareContext = compareContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractEntity getEntity() {
|
||||
return getComment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -75,4 +75,8 @@ public class IssueChange extends AbstractEntity {
|
||||
return data.affectsListing();
|
||||
}
|
||||
|
||||
public boolean isMinor() {
|
||||
return getData().isMinor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,41 +1,19 @@
|
||||
package io.onedev.server.model;
|
||||
|
||||
import java.util.Date;
|
||||
import io.onedev.server.model.support.EntityComment;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(indexes={
|
||||
@Index(columnList="o_issue_id"), @Index(columnList="o_user_id")})
|
||||
public class IssueComment extends AbstractEntity {
|
||||
public class IssueComment extends EntityComment {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int MAX_CONTENT_LEN = 15000;
|
||||
|
||||
public static final String PROP_CONTENT = "content";
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(nullable=false)
|
||||
private Issue issue;
|
||||
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@JoinColumn(nullable=false)
|
||||
private User user;
|
||||
|
||||
@Column(nullable=false)
|
||||
private Date date = new Date();
|
||||
|
||||
@Column(nullable=false, length=MAX_CONTENT_LEN)
|
||||
private String content;
|
||||
|
||||
public Issue getIssue() {
|
||||
return issue;
|
||||
@ -45,32 +23,9 @@ public class IssueComment extends AbstractEntity {
|
||||
this.issue = issue;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
@Override
|
||||
public AbstractEntity getEntity() {
|
||||
return getIssue();
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = StringUtils.abbreviate(content, MAX_CONTENT_LEN);
|
||||
}
|
||||
|
||||
public String getAnchor() {
|
||||
return getClass().getSimpleName() + "-" + getId();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,44 +1,19 @@
|
||||
package io.onedev.server.model;
|
||||
|
||||
import java.util.Date;
|
||||
import io.onedev.server.model.support.EntityComment;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import javax.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(indexes={@Index(columnList="o_request_id"), @Index(columnList="o_user_id")})
|
||||
public class PullRequestComment extends AbstractEntity {
|
||||
public class PullRequestComment extends EntityComment {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int MAX_CONTENT_LEN = 14000;
|
||||
|
||||
public static final int DIFF_CONTEXT_SIZE = 3;
|
||||
|
||||
public static final String PROP_CONTENT = "content";
|
||||
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@JoinColumn(nullable=false)
|
||||
private PullRequest request;
|
||||
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@JoinColumn(nullable=false)
|
||||
private User user;
|
||||
|
||||
@Column(nullable=false, length=MAX_CONTENT_LEN)
|
||||
private String content;
|
||||
|
||||
@Column(nullable=false)
|
||||
private Date date = new Date();
|
||||
|
||||
public PullRequest getRequest() {
|
||||
return request;
|
||||
}
|
||||
@ -47,36 +22,13 @@ public class PullRequestComment extends AbstractEntity {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(@Nullable User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = StringUtils.abbreviate(content, MAX_CONTENT_LEN);
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return request.getTargetProject();
|
||||
}
|
||||
|
||||
public String getAnchor() {
|
||||
return getClass().getSimpleName() + "-" + getId();
|
||||
@Override
|
||||
public AbstractEntity getEntity() {
|
||||
return getRequest();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,61 @@
|
||||
package io.onedev.server.model.support;
|
||||
|
||||
import io.onedev.server.model.AbstractEntity;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.util.facade.ProjectBelongingFacade;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.persistence.*;
|
||||
import java.util.Date;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class EntityComment extends AbstractEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int MAX_CONTENT_LEN = 15000;
|
||||
|
||||
public static final String PROP_CONTENT = "content";
|
||||
|
||||
@Column(nullable=false)
|
||||
private Date date = new Date();
|
||||
|
||||
@ManyToOne(fetch= FetchType.LAZY)
|
||||
@JoinColumn(nullable=false)
|
||||
private User user;
|
||||
|
||||
@Column(nullable=false, length=MAX_CONTENT_LEN)
|
||||
private String content;
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = StringUtils.abbreviate(content, MAX_CONTENT_LEN);
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(User user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public String getAnchor() {
|
||||
return getClass().getSimpleName() + "-" + getId();
|
||||
}
|
||||
|
||||
public abstract AbstractEntity getEntity();
|
||||
|
||||
}
|
||||
@ -22,6 +22,10 @@ public abstract class IssueChangeData implements Serializable {
|
||||
|
||||
public abstract boolean affectsListing();
|
||||
|
||||
public boolean isMinor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ActivityDetail getActivityDetail() {
|
||||
return null;
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
package io.onedev.server.model.support.issue.changedata;
|
||||
|
||||
import io.onedev.server.model.Group;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.notification.ActivityDetail;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class IssueDescriptionChangeData extends IssueChangeData {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String oldDescription;
|
||||
|
||||
private final String newDescription;
|
||||
|
||||
public IssueDescriptionChangeData(@Nullable String oldDescription, @Nullable String newDescription) {
|
||||
if (oldDescription == null)
|
||||
oldDescription = "";
|
||||
this.oldDescription = oldDescription;
|
||||
if (newDescription == null)
|
||||
newDescription = "";
|
||||
this.newDescription = newDescription;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "changed description";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Collection<User>> getNewUsers() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Group> getNewGroups() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean affectsListing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMinor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityDetail getActivityDetail() {
|
||||
Map<String, String> oldFieldValues = new HashMap<>();
|
||||
oldFieldValues.put("Description", oldDescription);
|
||||
Map<String, String> newFieldValues = new HashMap<>();
|
||||
newFieldValues.put("Description", newDescription);
|
||||
return ActivityDetail.compare(oldFieldValues, newFieldValues, true);
|
||||
}
|
||||
|
||||
}
|
||||
@ -41,6 +41,11 @@ public class IssueReferencedFromCodeCommentData extends IssueChangeData implemen
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMinor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean affectsListing() {
|
||||
return false;
|
||||
|
||||
@ -46,6 +46,11 @@ public class IssueReferencedFromIssueData extends IssueChangeData implements Ref
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMinor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Issue getReferencedFrom() {
|
||||
return OneDev.getInstance(IssueManager.class).get(issueId);
|
||||
|
||||
@ -51,6 +51,11 @@ public class IssueReferencedFromPullRequestData extends IssueChangeData implemen
|
||||
return OneDev.getInstance(PullRequestManager.class).get(requestId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMinor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityDetail getActivityDetail() {
|
||||
return ActivityDetail.referencedFrom(getReferencedFrom());
|
||||
|
||||
@ -41,6 +41,11 @@ public class IssueTitleChangeData extends IssueChangeData {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMinor() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActivityDetail getActivityDetail() {
|
||||
Map<String, String> oldFieldValues = new HashMap<>();
|
||||
|
||||
@ -17,7 +17,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.onedev.server.entityreference.ReferencedFromAware;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.infomanager.VisitInfoManager;
|
||||
import io.onedev.server.mail.MailManager;
|
||||
@ -72,7 +71,7 @@ public class IssueNotificationManager extends AbstractNotificationManager {
|
||||
@Transactional
|
||||
@Listen
|
||||
public void on(IssueEvent event) {
|
||||
if (event instanceof IssueCommitsAttached)
|
||||
if (event.isMinor())
|
||||
return;
|
||||
|
||||
Issue issue = event.getIssue();
|
||||
@ -202,8 +201,8 @@ public class IssueNotificationManager extends AbstractNotificationManager {
|
||||
}
|
||||
|
||||
Collection<String> notifiedEmailAddresses;
|
||||
if (event instanceof IssueCommented)
|
||||
notifiedEmailAddresses = ((IssueCommented) event).getNotifiedEmailAddresses();
|
||||
if (event instanceof IssueCommentCreated)
|
||||
notifiedEmailAddresses = ((IssueCommentCreated) event).getNotifiedEmailAddresses();
|
||||
else
|
||||
notifiedEmailAddresses = new ArrayList<>();
|
||||
|
||||
@ -233,37 +232,34 @@ public class IssueNotificationManager extends AbstractNotificationManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (!(event instanceof IssueChanged)
|
||||
|| !(((IssueChanged) event).getChange().getData() instanceof ReferencedFromAware)) {
|
||||
Collection<String> bccEmailAddresses = new HashSet<>();
|
||||
|
||||
for (IssueWatch watch: issue.getWatches()) {
|
||||
Date visitDate = userInfoManager.getIssueVisitDate(watch.getUser(), issue);
|
||||
if (watch.isWatching()
|
||||
&& (visitDate == null || visitDate.before(event.getDate()))
|
||||
&& !notifiedUsers.contains(watch.getUser())
|
||||
&& !isNotified(notifiedEmailAddresses, watch.getUser())
|
||||
&& SecurityUtils.canAccess(watch.getUser().asSubject(), issue)) {
|
||||
EmailAddress emailAddress = watch.getUser().getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
bccEmailAddresses.add(emailAddress.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!bccEmailAddresses.isEmpty()) {
|
||||
String subject = String.format("[Issue %s] (%s) %s",
|
||||
issue.getFQN(), (event instanceof IssueOpened)?"Opened":"Updated", issue.getTitle());
|
||||
|
||||
Unsubscribable unsubscribable = new Unsubscribable(mailManager.getUnsubscribeAddress(issue));
|
||||
String htmlBody = getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, unsubscribable);
|
||||
String textBody = getTextBody(event, summary, event.getTextBody(), url, replyable, unsubscribable);
|
||||
|
||||
String threadingReferences = issue.getEffectiveThreadingReference();
|
||||
mailManager.sendMailAsync(Sets.newHashSet(), Sets.newHashSet(),
|
||||
bccEmailAddresses, subject, htmlBody, textBody,
|
||||
replyAddress, threadingReferences);
|
||||
Collection<String> bccEmailAddresses = new HashSet<>();
|
||||
|
||||
for (IssueWatch watch: issue.getWatches()) {
|
||||
Date visitDate = userInfoManager.getIssueVisitDate(watch.getUser(), issue);
|
||||
if (watch.isWatching()
|
||||
&& (visitDate == null || visitDate.before(event.getDate()))
|
||||
&& !notifiedUsers.contains(watch.getUser())
|
||||
&& !isNotified(notifiedEmailAddresses, watch.getUser())
|
||||
&& SecurityUtils.canAccess(watch.getUser().asSubject(), issue)) {
|
||||
EmailAddress emailAddress = watch.getUser().getPrimaryEmailAddress();
|
||||
if (emailAddress != null && emailAddress.isVerified())
|
||||
bccEmailAddresses.add(emailAddress.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (!bccEmailAddresses.isEmpty()) {
|
||||
String subject = String.format("[Issue %s] (%s) %s",
|
||||
issue.getFQN(), (event instanceof IssueOpened)?"Opened":"Updated", issue.getTitle());
|
||||
|
||||
Unsubscribable unsubscribable = new Unsubscribable(mailManager.getUnsubscribeAddress(issue));
|
||||
String htmlBody = getHtmlBody(event, summary, event.getHtmlBody(), url, replyable, unsubscribable);
|
||||
String textBody = getTextBody(event, summary, event.getTextBody(), url, replyable, unsubscribable);
|
||||
|
||||
String threadingReferences = issue.getEffectiveThreadingReference();
|
||||
mailManager.sendMailAsync(Sets.newHashSet(), Sets.newHashSet(),
|
||||
bccEmailAddresses, subject, htmlBody, textBody,
|
||||
replyAddress, threadingReferences);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,24 +1,18 @@
|
||||
package io.onedev.server.rest;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.shiro.authz.UnauthorizedException;
|
||||
|
||||
import io.onedev.server.entitymanager.IssueCommentManager;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.rest.annotation.Api;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import org.apache.shiro.authz.UnauthorizedException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.ws.rs.*;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@Api(order=2300)
|
||||
@Path("/issue-comments")
|
||||
@ -52,7 +46,10 @@ public class IssueCommentResource {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
commentManager.save(comment);
|
||||
if (comment.isNew())
|
||||
commentManager.create(comment, new ArrayList<>());
|
||||
else
|
||||
commentManager.update(comment);
|
||||
return comment.getId();
|
||||
}
|
||||
|
||||
|
||||
@ -266,7 +266,7 @@ public class IssueResource {
|
||||
Issue issue = issueManager.load(issueId);
|
||||
if (!SecurityUtils.canModify(issue))
|
||||
throw new UnauthorizedException();
|
||||
issueManager.saveDescription(issue, description);
|
||||
issueChangeManager.changeDescription(issue, description);
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
|
||||
@ -1,30 +1,10 @@
|
||||
package io.onedev.server.search.entitytext;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
import org.apache.lucene.document.LongPoint;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queryparser.flexible.core.QueryNodeException;
|
||||
import org.apache.lucene.queryparser.flexible.standard.StandardQueryParser;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.search.BoostQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.WildcardQuery;
|
||||
|
||||
import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.server.cluster.ClusterManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.model.CodeComment;
|
||||
import io.onedev.server.model.CodeCommentReply;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
@ -32,6 +12,27 @@ import io.onedev.server.storage.StorageManager;
|
||||
import io.onedev.server.util.concurrent.BatchWorkManager;
|
||||
import io.onedev.server.util.lucene.BooleanQueryBuilder;
|
||||
import io.onedev.server.util.lucene.LuceneUtils;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
import org.apache.lucene.document.LongPoint;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.document.TextField;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
|
||||
import org.apache.lucene.queryparser.classic.ParseException;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.search.BoostQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.WildcardQuery;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Singleton
|
||||
public class DefaultCodeCommentTextManager extends ProjectTextManager<CodeComment>
|
||||
@ -41,6 +42,8 @@ public class DefaultCodeCommentTextManager extends ProjectTextManager<CodeCommen
|
||||
|
||||
private static final String FIELD_COMMENT = "comment";
|
||||
|
||||
private static final String FIELD_REPLIES = "replies";
|
||||
|
||||
@Inject
|
||||
public DefaultCodeCommentTextManager(Dao dao, StorageManager storageManager,
|
||||
BatchWorkManager batchWorkManager, TransactionManager transactionManager,
|
||||
@ -55,13 +58,18 @@ public class DefaultCodeCommentTextManager extends ProjectTextManager<CodeCommen
|
||||
|
||||
@Override
|
||||
protected int getIndexVersion() {
|
||||
return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addFields(Document document, CodeComment entity) {
|
||||
document.add(new StringField(FIELD_PATH, entity.getMark().getPath(), Store.NO));
|
||||
document.add(new TextField(FIELD_COMMENT, entity.getContent(), Store.NO));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (CodeCommentReply reply: entity.getReplies())
|
||||
builder.append(reply.getContent()).append("\n");
|
||||
if (builder.length() != 0)
|
||||
document.add(new TextField(FIELD_REPLIES, builder.toString(), Store.NO));
|
||||
}
|
||||
|
||||
private Query buildQuery(Project project, String queryString) {
|
||||
@ -72,12 +80,13 @@ public class DefaultCodeCommentTextManager extends ProjectTextManager<CodeCommen
|
||||
queryString = LuceneUtils.escape(queryString);
|
||||
Query pathQuery = new BoostQuery(new WildcardQuery(new Term(FIELD_PATH, "*" + queryString + "*")), 0.75f);
|
||||
try (Analyzer analyzer = newAnalyzer()) {
|
||||
contentQueryBuilder.add(pathQuery, Occur.SHOULD);
|
||||
StandardQueryParser parser = new StandardQueryParser(analyzer);
|
||||
contentQueryBuilder.add(
|
||||
new BoostQuery(parser.parse(queryString, FIELD_COMMENT), 0.5f),
|
||||
Occur.SHOULD);
|
||||
} catch (QueryNodeException e) {
|
||||
Map<String, Float> boosts = new HashMap<>();
|
||||
boosts.put(FIELD_COMMENT, 0.75f);
|
||||
boosts.put(FIELD_REPLIES, 0.5f);
|
||||
MultiFieldQueryParser parser = new MultiFieldQueryParser(
|
||||
new String[] {FIELD_COMMENT, FIELD_REPLIES}, analyzer, boosts);
|
||||
contentQueryBuilder.add(parser.parse(LuceneUtils.escape(queryString)), Occur.SHOULD);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
queryBuilder.add(contentQueryBuilder.build(), Occur.MUST);
|
||||
|
||||
@ -1,17 +1,32 @@
|
||||
package io.onedev.server.search.entitytext;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.server.cluster.ClusterManager;
|
||||
import io.onedev.server.cluster.ClusterTask;
|
||||
import io.onedev.server.entitymanager.IssueFieldManager;
|
||||
import io.onedev.server.entitymanager.IssueLinkManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.event.project.issue.*;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueChangeData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueDescriptionChangeData;
|
||||
import io.onedev.server.model.support.issue.changedata.IssueTitleChangeData;
|
||||
import io.onedev.server.persistence.SessionManager;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.security.permission.AccessProject;
|
||||
import io.onedev.server.storage.StorageManager;
|
||||
import io.onedev.server.util.ProjectScope;
|
||||
import io.onedev.server.util.concurrent.BatchWorkManager;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
import io.onedev.server.util.lucene.BooleanQueryBuilder;
|
||||
import io.onedev.server.util.lucene.LuceneUtils;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
@ -24,23 +39,12 @@ import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.BoostQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
|
||||
import io.onedev.commons.loader.ManagedSerializedForm;
|
||||
import io.onedev.server.cluster.ClusterManager;
|
||||
import io.onedev.server.entitymanager.IssueFieldManager;
|
||||
import io.onedev.server.entitymanager.IssueLinkManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.security.permission.AccessProject;
|
||||
import io.onedev.server.storage.StorageManager;
|
||||
import io.onedev.server.util.ProjectScope;
|
||||
import io.onedev.server.util.concurrent.BatchWorkManager;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
import io.onedev.server.util.lucene.BooleanQueryBuilder;
|
||||
import io.onedev.server.util.lucene.LuceneUtils;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Singleton
|
||||
public class DefaultIssueTextManager extends ProjectTextManager<Issue> implements IssueTextManager {
|
||||
@ -53,19 +57,27 @@ public class DefaultIssueTextManager extends ProjectTextManager<Issue> implement
|
||||
|
||||
private static final String FIELD_DESCRIPTION = "description";
|
||||
|
||||
private static final String FIELD_COMMENTS = "comments";
|
||||
|
||||
private final IssueFieldManager fieldManager;
|
||||
|
||||
private final UserManager userManager;
|
||||
|
||||
private final IssueLinkManager linkManager;
|
||||
|
||||
private final SessionManager sessionManager;
|
||||
|
||||
@Inject
|
||||
public DefaultIssueTextManager(Dao dao, StorageManager storageManager,
|
||||
BatchWorkManager batchWorkManager, TransactionManager transactionManager,
|
||||
ProjectManager projectManager, IssueFieldManager fieldManager,
|
||||
IssueLinkManager linkManager, ClusterManager clusterManager) {
|
||||
super(dao, storageManager, batchWorkManager, transactionManager,
|
||||
projectManager, clusterManager);
|
||||
public DefaultIssueTextManager(Dao dao, StorageManager storageManager, BatchWorkManager batchWorkManager,
|
||||
TransactionManager transactionManager, ProjectManager projectManager,
|
||||
IssueFieldManager fieldManager, IssueLinkManager linkManager,
|
||||
ClusterManager clusterManager, UserManager userManager,
|
||||
SessionManager sessionManager) {
|
||||
super(dao, storageManager, batchWorkManager, transactionManager, projectManager, clusterManager);
|
||||
this.fieldManager = fieldManager;
|
||||
this.linkManager = linkManager;
|
||||
this.userManager = userManager;
|
||||
this.sessionManager = sessionManager;
|
||||
}
|
||||
|
||||
public Object writeReplace() throws ObjectStreamException {
|
||||
@ -74,7 +86,7 @@ public class DefaultIssueTextManager extends ProjectTextManager<Issue> implement
|
||||
|
||||
@Override
|
||||
protected int getIndexVersion() {
|
||||
return 3;
|
||||
return 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,8 +96,92 @@ public class DefaultIssueTextManager extends ProjectTextManager<Issue> implement
|
||||
document.add(new TextField(FIELD_TITLE, entity.getTitle(), Store.NO));
|
||||
if (entity.getDescription() != null)
|
||||
document.add(new TextField(FIELD_DESCRIPTION, entity.getDescription(), Store.NO));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (IssueComment comment: entity.getComments()) {
|
||||
if (!comment.getUser().equals(userManager.getSystem()))
|
||||
builder.append(comment.getContent()).append("\n");
|
||||
}
|
||||
if (builder.length() != 0)
|
||||
document.add(new TextField(FIELD_COMMENTS, builder.toString(), Store.NO));
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssueOpened event) {
|
||||
requestIndexLocal(event.getIssue());
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssueChanged event) {
|
||||
IssueChangeData data = event.getChange().getData();
|
||||
if (data instanceof IssueTitleChangeData || data instanceof IssueDescriptionChangeData)
|
||||
requestIndexLocal(event.getIssue());
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssueCommentCreated event) {
|
||||
requestIndexLocal(event.getIssue());
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssueCommentUpdated event) {
|
||||
requestIndexLocal(event.getIssue());
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssueCommentDeleted event) {
|
||||
requestIndexLocal(event.getIssue());
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesDeleted event) {
|
||||
deleteEntitiesLocal(event.getIssueIds());
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesMoved event) {
|
||||
UUID sourceProjectServerUUID = projectManager.getStorageServerUUID(
|
||||
event.getSourceProject().getId(), true);
|
||||
UUID targetProjectServerUUID = projectManager.getStorageServerUUID(
|
||||
event.getTargetProject().getId(), true);
|
||||
if (!sourceProjectServerUUID.equals(targetProjectServerUUID)) {
|
||||
clusterManager.submitToServer(sourceProjectServerUUID, new ClusterTask<Void>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
deleteEntitiesLocal(event.getIssueIds());
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
for (Long issueId: event.getIssueIds())
|
||||
requestIndexLocal(dao.load(Issue.class, issueId));
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesCopied event) {
|
||||
for (Long issueId: event.getIssueIdMapping().values())
|
||||
requestIndexLocal(dao.load(Issue.class, issueId));
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssuesImported event) {
|
||||
for (Long issueId: event.getIssueIds())
|
||||
requestIndexLocal(dao.load(Issue.class, issueId));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Query buildQuery(@Nullable ProjectScope projectScope, String queryString) {
|
||||
BooleanQueryBuilder queryBuilder = new BooleanQueryBuilder();
|
||||
@ -134,8 +230,9 @@ public class DefaultIssueTextManager extends ProjectTextManager<Issue> implement
|
||||
Map<String, Float> boosts = new HashMap<>();
|
||||
boosts.put(FIELD_TITLE, 0.75f);
|
||||
boosts.put(FIELD_DESCRIPTION, 0.5f);
|
||||
boosts.put(FIELD_COMMENTS, 0.25f);
|
||||
MultiFieldQueryParser parser = new MultiFieldQueryParser(
|
||||
new String[] {FIELD_TITLE, FIELD_DESCRIPTION}, analyzer, boosts);
|
||||
new String[] {FIELD_TITLE, FIELD_DESCRIPTION, FIELD_COMMENTS}, analyzer, boosts);
|
||||
contentQueryBuilder.add(parser.parse(LuceneUtils.escape(queryString)), Occur.SHOULD);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@ -12,6 +12,8 @@ import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.model.PullRequestComment;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field.Store;
|
||||
@ -49,20 +51,25 @@ public class DefaultPullRequestTextManager extends ProjectTextManager<PullReques
|
||||
private static final String FIELD_TITLE = "title";
|
||||
|
||||
private static final String FIELD_DESCRIPTION = "description";
|
||||
|
||||
private static final String FIELD_COMMENTS = "comments";
|
||||
|
||||
private final PullRequestReviewManager reviewManager;
|
||||
|
||||
private final BuildManager buildManager;
|
||||
|
||||
private final UserManager userManager;
|
||||
|
||||
@Inject
|
||||
public DefaultPullRequestTextManager(Dao dao, StorageManager storageManager,
|
||||
BatchWorkManager batchWorkManager, TransactionManager transactionManager,
|
||||
ProjectManager projectManager, PullRequestReviewManager reviewManager,
|
||||
BuildManager buildManager, ClusterManager clusterManager) {
|
||||
public DefaultPullRequestTextManager(Dao dao, StorageManager storageManager, UserManager userManager,
|
||||
BatchWorkManager batchWorkManager, TransactionManager transactionManager,
|
||||
ProjectManager projectManager, PullRequestReviewManager reviewManager,
|
||||
BuildManager buildManager, ClusterManager clusterManager) {
|
||||
super(dao, storageManager, batchWorkManager, transactionManager, projectManager,
|
||||
clusterManager);
|
||||
this.reviewManager = reviewManager;
|
||||
this.buildManager = buildManager;
|
||||
this.userManager = userManager;
|
||||
}
|
||||
|
||||
public Object writeReplace() throws ObjectStreamException {
|
||||
@ -71,7 +78,7 @@ public class DefaultPullRequestTextManager extends ProjectTextManager<PullReques
|
||||
|
||||
@Override
|
||||
protected int getIndexVersion() {
|
||||
return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -80,6 +87,13 @@ public class DefaultPullRequestTextManager extends ProjectTextManager<PullReques
|
||||
document.add(new TextField(FIELD_TITLE, entity.getTitle(), Store.NO));
|
||||
if (entity.getDescription() != null)
|
||||
document.add(new TextField(FIELD_DESCRIPTION, entity.getDescription(), Store.NO));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (PullRequestComment comment: entity.getComments()) {
|
||||
if (!comment.getUser().equals(userManager.getSystem()))
|
||||
builder.append(comment.getContent()).append("\n");
|
||||
}
|
||||
if (builder.length() != 0)
|
||||
document.add(new TextField(FIELD_COMMENTS, builder.toString(), Store.NO));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -114,8 +128,9 @@ public class DefaultPullRequestTextManager extends ProjectTextManager<PullReques
|
||||
Map<String, Float> boosts = new HashMap<>();
|
||||
boosts.put(FIELD_TITLE, 0.75f);
|
||||
boosts.put(FIELD_DESCRIPTION, 0.5f);
|
||||
boosts.put(FIELD_COMMENTS, 0.25f);
|
||||
MultiFieldQueryParser parser = new MultiFieldQueryParser(
|
||||
new String[] {FIELD_TITLE, FIELD_DESCRIPTION}, analyzer, boosts);
|
||||
new String[] {FIELD_TITLE, FIELD_DESCRIPTION, FIELD_COMMENTS}, analyzer, boosts);
|
||||
contentQueryBuilder.add(parser.parse(LuceneUtils.escape(queryString)), Occur.SHOULD);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@ -1,20 +1,27 @@
|
||||
package io.onedev.server.search.entitytext;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
import io.onedev.commons.utils.ExceptionUtils;
|
||||
import io.onedev.commons.utils.FileUtils;
|
||||
import io.onedev.commons.utils.WordUtils;
|
||||
import io.onedev.server.cluster.ClusterManager;
|
||||
import io.onedev.server.cluster.ClusterTask;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.event.project.ProjectDeleted;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.event.system.SystemStopping;
|
||||
import io.onedev.server.model.AbstractEntity;
|
||||
import io.onedev.server.model.support.ProjectBelonging;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.persistence.dao.EntityCriteria;
|
||||
import io.onedev.server.storage.StorageManager;
|
||||
import io.onedev.server.util.ReflectionUtils;
|
||||
import io.onedev.server.util.concurrent.BatchWorkManager;
|
||||
import io.onedev.server.util.concurrent.BatchWorker;
|
||||
import io.onedev.server.util.concurrent.Prioritized;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.CharArraySet;
|
||||
import org.apache.lucene.analysis.WordlistLoader;
|
||||
@ -38,21 +45,11 @@ import org.apache.lucene.document.Field.Store;
|
||||
import org.apache.lucene.document.LongPoint;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexFormatTooOldException;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.index.*;
|
||||
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queryparser.classic.ParseException;
|
||||
import org.apache.lucene.queryparser.classic.QueryParser;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.SearcherManager;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.search.TotalHitCountCollector;
|
||||
import org.apache.lucene.search.*;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.store.FSDirectory;
|
||||
import org.apache.lucene.util.IOUtils;
|
||||
@ -60,32 +57,12 @@ import org.hibernate.criterion.Restrictions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
import io.onedev.commons.utils.ExceptionUtils;
|
||||
import io.onedev.commons.utils.FileUtils;
|
||||
import io.onedev.commons.utils.WordUtils;
|
||||
import io.onedev.server.cluster.ClusterManager;
|
||||
import io.onedev.server.cluster.ClusterRunnable;
|
||||
import io.onedev.server.cluster.ClusterTask;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.event.entity.EntityPersisted;
|
||||
import io.onedev.server.event.entity.EntityRemoved;
|
||||
import io.onedev.server.event.system.SystemStarted;
|
||||
import io.onedev.server.event.system.SystemStopping;
|
||||
import io.onedev.server.model.AbstractEntity;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.support.ProjectBelonging;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.persistence.dao.EntityCriteria;
|
||||
import io.onedev.server.storage.StorageManager;
|
||||
import io.onedev.server.util.ReflectionUtils;
|
||||
import io.onedev.server.util.concurrent.BatchWorkManager;
|
||||
import io.onedev.server.util.concurrent.BatchWorker;
|
||||
import io.onedev.server.util.concurrent.Prioritized;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
public abstract class ProjectTextManager<T extends ProjectBelonging> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -136,7 +113,7 @@ public abstract class ProjectTextManager<T extends ProjectBelonging> implements
|
||||
|
||||
private final Class<T> entityClass;
|
||||
|
||||
private final Dao dao;
|
||||
protected final Dao dao;
|
||||
|
||||
private final StorageManager storageManager;
|
||||
|
||||
@ -146,7 +123,7 @@ public abstract class ProjectTextManager<T extends ProjectBelonging> implements
|
||||
|
||||
protected final ProjectManager projectManager;
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
protected final ClusterManager clusterManager;
|
||||
|
||||
private volatile SearcherManager searcherManager;
|
||||
|
||||
@ -232,137 +209,35 @@ public abstract class ProjectTextManager<T extends ProjectBelonging> implements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void deleteEntitiesLocal(Collection<Long> entityIds) {
|
||||
doWithWriter(new WriterRunnable() {
|
||||
|
||||
@Transactional
|
||||
@Listen
|
||||
public void on(EntityPersisted event) {
|
||||
if (entityClass.isAssignableFrom(event.getEntity().getClass())) {
|
||||
ProjectBelonging projectBelonging = ((ProjectBelonging)event.getEntity());
|
||||
Long entityId;
|
||||
if (!event.isNew())
|
||||
entityId = event.getEntity().getId();
|
||||
else
|
||||
entityId = null;
|
||||
Long projectId = projectBelonging.getProject().getId();
|
||||
UUID projectServerUUID = projectManager.getStorageServerUUID(projectId, true);
|
||||
|
||||
UUID oldProjectServerUUID;
|
||||
if (projectBelonging.getOldVersion() != null) {
|
||||
oldProjectServerUUID = projectManager.getStorageServerUUID(
|
||||
projectBelonging.getOldVersion().getProjectId(), true);
|
||||
} else {
|
||||
oldProjectServerUUID = null;
|
||||
@Override
|
||||
public void run(IndexWriter writer) throws IOException {
|
||||
for (Long entityId: entityIds)
|
||||
writer.deleteDocuments(getTerm(FIELD_ENTITY_ID, String.valueOf(entityId)));
|
||||
}
|
||||
transactionManager.runAfterCommit(new ClusterRunnable() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (oldProjectServerUUID != null && !oldProjectServerUUID.equals(projectServerUUID)) {
|
||||
clusterManager.submitToServer(oldProjectServerUUID, new ClusterTask<Void>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
doWithWriter(new WriterRunnable() {
|
||||
|
||||
@Override
|
||||
public void run(IndexWriter writer) throws IOException {
|
||||
writer.deleteDocuments(getTerm(FIELD_ENTITY_ID, String.valueOf(entityId)));
|
||||
}
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
clusterManager.submitToServer(projectServerUUID, new ClusterTask<Void>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
batchWorkManager.submit(getBatchWorker(), new IndexWork(INDEXING_PRIORITY, entityId));
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(EntityRemoved event) {
|
||||
if (entityClass.isAssignableFrom(event.getEntity().getClass())) {
|
||||
Long entityId = event.getEntity().getId();
|
||||
Long projectId = ((ProjectBelonging)event.getEntity()).getProject().getId();
|
||||
transactionManager.runAfterCommit(new ClusterRunnable() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
projectManager.submitToProjectServer(projectId, new ClusterTask<Void>() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
doWithWriter(new WriterRunnable() {
|
||||
|
||||
@Override
|
||||
public void run(IndexWriter writer) throws IOException {
|
||||
writer.deleteDocuments(getTerm(FIELD_ENTITY_ID, String.valueOf(entityId)));
|
||||
}
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
} else if (event.getEntity() instanceof Project) {
|
||||
Long projectId = event.getEntity().getId();
|
||||
UUID storageServerUUID = projectManager.getStorageServerUUID(projectId, false);
|
||||
transactionManager.runAfterCommit(new ClusterRunnable() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (storageServerUUID != null) {
|
||||
clusterManager.submitToServer(storageServerUUID, new ClusterTask<Void>() {
|
||||
public void on(ProjectDeleted event) {
|
||||
Long projectId = event.getProjectId();
|
||||
doWithWriter(new WriterRunnable() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@Override
|
||||
public void run(IndexWriter writer) throws IOException {
|
||||
writer.deleteDocuments(LongPoint.newExactQuery(FIELD_PROJECT_ID, projectId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
doWithWriter(new WriterRunnable() {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(IndexWriter writer) throws IOException {
|
||||
writer.deleteDocuments(LongPoint.newExactQuery(FIELD_PROJECT_ID, projectId));
|
||||
}
|
||||
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
protected void requestIndexLocal(T entity) {
|
||||
batchWorkManager.submit(getBatchWorker(), new IndexWork(INDEXING_PRIORITY, entity.getId()));
|
||||
}
|
||||
|
||||
protected void doWithWriter(WriterRunnable runnable) {
|
||||
@ -417,7 +292,6 @@ public abstract class ProjectTextManager<T extends ProjectBelonging> implements
|
||||
@Override
|
||||
public void doWorks(Collection<Prioritized> works) {
|
||||
String entityName = WordUtils.uncamel(entityClass.getSimpleName()).toLowerCase();
|
||||
logger.debug("Indexing {}s...", entityName);
|
||||
|
||||
boolean checkNewEntities = false;
|
||||
Collection<Long> entityIds = new HashSet<>();
|
||||
|
||||
@ -82,7 +82,7 @@ public abstract class ChannelNotificationManager<T extends ChannelNotificationSe
|
||||
@Sessional
|
||||
@Listen
|
||||
public void on(IssueEvent event) {
|
||||
if (!(event instanceof IssueCommitsAttached) && (!(event instanceof IssueChanged) || !(((IssueChanged) event).getChange().getData() instanceof ReferencedFromAware))) {
|
||||
if (!event.isMinor()) {
|
||||
Issue issue = event.getIssue();
|
||||
User user = event.getUser();
|
||||
|
||||
@ -94,7 +94,6 @@ public abstract class ChannelNotificationManager<T extends ChannelNotificationSe
|
||||
|
||||
postIfApplicable(issueInfo + " " + eventDescription, event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Sessional
|
||||
|
||||
@ -1,373 +1,374 @@
|
||||
package io.onedev.server.web.component.issue.activities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import org.apache.wicket.AttributeModifier;
|
||||
import org.apache.wicket.Component;
|
||||
import org.apache.wicket.RestartResponseAtInterceptPageException;
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
|
||||
import org.apache.wicket.ajax.markup.html.AjaxLink;
|
||||
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
|
||||
import org.apache.wicket.behavior.AttributeAppender;
|
||||
import org.apache.wicket.behavior.Behavior;
|
||||
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
|
||||
import org.apache.wicket.feedback.FencedFeedbackPanel;
|
||||
import org.apache.wicket.markup.head.CssHeaderItem;
|
||||
import org.apache.wicket.markup.head.IHeaderResponse;
|
||||
import org.apache.wicket.markup.html.WebMarkupContainer;
|
||||
import org.apache.wicket.markup.html.form.Form;
|
||||
import org.apache.wicket.markup.html.link.Link;
|
||||
import org.apache.wicket.markup.html.panel.Fragment;
|
||||
import org.apache.wicket.markup.html.panel.Panel;
|
||||
import org.apache.wicket.markup.repeater.RepeatingView;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.apache.wicket.request.cycle.RequestCycle;
|
||||
import org.apache.wicket.request.http.WebRequest;
|
||||
import org.apache.wicket.request.http.WebResponse;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.attachment.AttachmentSupport;
|
||||
import io.onedev.server.attachment.ProjectAttachmentSupport;
|
||||
import io.onedev.server.entitymanager.IssueCommentManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.entityreference.ReferencedFromAware;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueChange;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.util.facade.UserCache;
|
||||
import io.onedev.server.web.ajaxlistener.ConfirmLeaveListener;
|
||||
import io.onedev.server.web.behavior.WebSocketObserver;
|
||||
import io.onedev.server.web.component.issue.activities.activity.IssueActivity;
|
||||
import io.onedev.server.web.component.issue.activities.activity.IssueChangeActivity;
|
||||
import io.onedev.server.web.component.issue.activities.activity.IssueCommentedActivity;
|
||||
import io.onedev.server.web.component.issue.activities.activity.IssueOpenedActivity;
|
||||
import io.onedev.server.web.component.project.comment.CommentInput;
|
||||
import io.onedev.server.web.component.user.ident.Mode;
|
||||
import io.onedev.server.web.component.user.ident.UserIdentPanel;
|
||||
import io.onedev.server.web.page.simple.security.LoginPage;
|
||||
import io.onedev.server.web.util.DeleteCallback;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class IssueActivitiesPanel extends Panel {
|
||||
|
||||
private static final String COOKIE_SHOW_COMMENTS = "onedev.server.issue.showComments";
|
||||
|
||||
private static final String COOKIE_SHOW_CHANGE_HISTORY = "onedev.server.issue.showChangeHistory";
|
||||
|
||||
private RepeatingView activitiesView;
|
||||
|
||||
private boolean showComments = true;
|
||||
|
||||
private boolean showChangeHistory = true;
|
||||
|
||||
public IssueActivitiesPanel(String panelId) {
|
||||
super(panelId);
|
||||
|
||||
WebRequest request = (WebRequest) RequestCycle.get().getRequest();
|
||||
Cookie cookie = request.getCookie(COOKIE_SHOW_COMMENTS);
|
||||
if (cookie != null)
|
||||
showComments = Boolean.valueOf(cookie.getValue());
|
||||
|
||||
cookie = request.getCookie(COOKIE_SHOW_CHANGE_HISTORY);
|
||||
if (cookie != null)
|
||||
showChangeHistory = Boolean.valueOf(cookie.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBeforeRender() {
|
||||
activitiesView = new RepeatingView("activities");
|
||||
addOrReplace(activitiesView);
|
||||
Issue issue = getIssue();
|
||||
|
||||
for (IssueActivity activity: getActivities()) {
|
||||
if (issue.isVisitedAfter(activity.getDate())) {
|
||||
activitiesView.add(newActivityRow(activitiesView.newChildId(), activity));
|
||||
} else {
|
||||
Component row = newActivityRow(activitiesView.newChildId(), activity);
|
||||
row.add(AttributeAppender.append("class", "new"));
|
||||
activitiesView.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
super.onBeforeRender();
|
||||
}
|
||||
|
||||
private List<IssueActivity> getActivities() {
|
||||
List<IssueActivity> activities = new ArrayList<>();
|
||||
|
||||
activities.add(new IssueOpenedActivity(getIssue()));
|
||||
|
||||
List<IssueActivity> otherActivities = new ArrayList<>();
|
||||
|
||||
if (showChangeHistory) {
|
||||
for (IssueChange change: getIssue().getChanges()) {
|
||||
if (change.getData() instanceof ReferencedFromAware) {
|
||||
ReferencedFromAware<?> referencedFromAware = (ReferencedFromAware<?>) change.getData();
|
||||
if (referencedFromAware.getReferencedFrom() instanceof Issue) {
|
||||
Issue issue = (Issue) referencedFromAware.getReferencedFrom();
|
||||
if (SecurityUtils.canAccess(issue))
|
||||
otherActivities.add(new IssueChangeActivity(change));
|
||||
} else if (referencedFromAware.getReferencedFrom() != null) {
|
||||
otherActivities.add(new IssueChangeActivity(change));
|
||||
}
|
||||
} else {
|
||||
otherActivities.add(new IssueChangeActivity(change));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showComments) {
|
||||
for (IssueComment comment: getIssue().getComments())
|
||||
otherActivities.add(new IssueCommentedActivity(comment));
|
||||
}
|
||||
|
||||
otherActivities.sort((o1, o2) -> {
|
||||
if (o1.getDate().getTime()<o2.getDate().getTime())
|
||||
return -1;
|
||||
else if (o1.getDate().getTime()>o2.getDate().getTime())
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
});
|
||||
|
||||
activities.addAll(otherActivities);
|
||||
|
||||
return activities;
|
||||
}
|
||||
|
||||
private Component newActivityRow(String id, IssueActivity activity) {
|
||||
WebMarkupContainer row = new WebMarkupContainer(id, Model.of(activity));
|
||||
row.setOutputMarkupId(true);
|
||||
String anchor = activity.getAnchor();
|
||||
if (anchor != null)
|
||||
row.setMarkupId(anchor);
|
||||
|
||||
if (activity.getUser() != null) {
|
||||
row.add(new UserIdentPanel("avatar", activity.getUser(), Mode.AVATAR));
|
||||
row.add(AttributeAppender.append("class", "with-avatar"));
|
||||
} else {
|
||||
row.add(new WebMarkupContainer("avatar").setVisible(false));
|
||||
}
|
||||
|
||||
row.add(activity.render("content", new DeleteCallback() {
|
||||
|
||||
@Override
|
||||
public void onDelete(AjaxRequestTarget target) {
|
||||
row.remove();
|
||||
target.appendJavaScript(String.format("$('#%s').remove();", row.getMarkupId()));
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
row.add(AttributeAppender.append("class", activity.getClass().getSimpleName()));
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
add(new WebSocketObserver() {
|
||||
|
||||
@Override
|
||||
public void onObservableChanged(IPartialPageRequestHandler handler) {
|
||||
updateActivities(handler);
|
||||
}
|
||||
|
||||
private void updateActivities(IPartialPageRequestHandler handler) {
|
||||
@SuppressWarnings("deprecation")
|
||||
Component prevActivityRow = activitiesView.get(activitiesView.size()-1);
|
||||
IssueActivity lastActivity = (IssueActivity) prevActivityRow.getDefaultModelObject();
|
||||
List<IssueActivity> newActivities = new ArrayList<>();
|
||||
for (IssueActivity activity: getActivities()) {
|
||||
if (activity.getDate().getTime() > lastActivity.getDate().getTime())
|
||||
newActivities.add(activity);
|
||||
}
|
||||
|
||||
for (IssueActivity activity: newActivities) {
|
||||
Component newActivityRow = newActivityRow(activitiesView.newChildId(), activity);
|
||||
newActivityRow.add(AttributeAppender.append("class", "new"));
|
||||
activitiesView.add(newActivityRow);
|
||||
|
||||
String script = String.format("$(\"<tr id='%s'></tr>\").insertAfter('#%s');",
|
||||
newActivityRow.getMarkupId(), prevActivityRow.getMarkupId());
|
||||
handler.prependJavaScript(script);
|
||||
handler.add(newActivityRow);
|
||||
prevActivityRow = newActivityRow;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getObservables() {
|
||||
return Lists.newArrayList(Issue.getWebSocketObservable(getIssue().getId()));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (SecurityUtils.getUser() != null) {
|
||||
Fragment fragment = new Fragment("addComment", "addCommentFrag", this);
|
||||
fragment.setOutputMarkupId(true);
|
||||
CommentInput input = new CommentInput("comment", Model.of(""), false) {
|
||||
|
||||
@Override
|
||||
protected AttachmentSupport getAttachmentSupport() {
|
||||
return new ProjectAttachmentSupport(getProject(), getIssue().getUUID(),
|
||||
SecurityUtils.canManageIssues(getProject()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Project getProject() {
|
||||
return getIssue().getProject();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<User> getMentionables() {
|
||||
UserCache cache = OneDev.getInstance(UserManager.class).cloneCache();
|
||||
List<User> users = new ArrayList<>(cache.getUsers());
|
||||
users.sort(cache.comparingDisplayName(getIssue().getParticipants()));
|
||||
return users;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Behavior> getInputBehaviors() {
|
||||
return Lists.newArrayList(AttributeModifier.replace("placeholder", "Leave a comment"));
|
||||
}
|
||||
|
||||
};
|
||||
input.setRequired(true).setLabel(Model.of("Comment"));
|
||||
|
||||
Form<?> form = new Form<Void>("form");
|
||||
form.add(new FencedFeedbackPanel("feedback", form));
|
||||
form.add(input);
|
||||
form.add(new AjaxSubmitLink("save") {
|
||||
|
||||
@Override
|
||||
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
|
||||
super.onSubmit(target, form);
|
||||
String content = input.getModelObject();
|
||||
if (content.length() > IssueComment.MAX_CONTENT_LEN) {
|
||||
error("Comment too long");
|
||||
target.add(form);
|
||||
} else {
|
||||
IssueComment comment = new IssueComment();
|
||||
comment.setContent(content);
|
||||
comment.setUser(SecurityUtils.getUser());
|
||||
comment.setDate(new Date());
|
||||
comment.setIssue(getIssue());
|
||||
OneDev.getInstance(IssueCommentManager.class).save(comment);
|
||||
|
||||
input.clearMarkdown();
|
||||
|
||||
target.add(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onError(AjaxRequestTarget target, Form<?> form) {
|
||||
super.onError(target, form);
|
||||
target.add(form);
|
||||
}
|
||||
|
||||
});
|
||||
form.setOutputMarkupId(true);
|
||||
fragment.add(form);
|
||||
add(fragment);
|
||||
} else {
|
||||
Fragment fragment = new Fragment("addComment", "loginToCommentFrag", this);
|
||||
fragment.add(new Link<Void>("login") {
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
throw new RestartResponseAtInterceptPageException(LoginPage.class);
|
||||
}
|
||||
|
||||
});
|
||||
add(fragment);
|
||||
}
|
||||
|
||||
setOutputMarkupId(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
super.renderHead(response);
|
||||
response.render(CssHeaderItem.forReference(new IssueActivitiesCssResourceReference()));
|
||||
}
|
||||
|
||||
protected abstract Issue getIssue();
|
||||
|
||||
public Component renderOptions(String componentId) {
|
||||
Fragment fragment = new Fragment(componentId, "optionsFrag", this);
|
||||
fragment.add(new AjaxLink<Void>("showComments") {
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
if (showComments)
|
||||
add(AttributeAppender.append("class", "active"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
|
||||
super.updateAjaxAttributes(attributes);
|
||||
attributes.getAjaxCallListeners().add(new ConfirmLeaveListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
showComments = !showComments;
|
||||
WebResponse response = (WebResponse) RequestCycle.get().getResponse();
|
||||
Cookie cookie = new Cookie(COOKIE_SHOW_COMMENTS, String.valueOf(showComments));
|
||||
cookie.setPath("/");
|
||||
cookie.setMaxAge(Integer.MAX_VALUE);
|
||||
response.addCookie(cookie);
|
||||
target.add(IssueActivitiesPanel.this);
|
||||
target.appendJavaScript(String.format("$('#%s').toggleClass('active');", getMarkupId()));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
fragment.add(new AjaxLink<Void>("showChangeHistory") {
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
if (showChangeHistory)
|
||||
add(AttributeAppender.append("class", "active"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
|
||||
super.updateAjaxAttributes(attributes);
|
||||
attributes.getAjaxCallListeners().add(new ConfirmLeaveListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
showChangeHistory = !showChangeHistory;
|
||||
WebResponse response = (WebResponse) RequestCycle.get().getResponse();
|
||||
Cookie cookie = new Cookie(COOKIE_SHOW_CHANGE_HISTORY, String.valueOf(showChangeHistory));
|
||||
cookie.setPath("/");
|
||||
cookie.setMaxAge(Integer.MAX_VALUE);
|
||||
response.addCookie(cookie);
|
||||
target.add(IssueActivitiesPanel.this);
|
||||
target.appendJavaScript(String.format("$('#%s').toggleClass('active');", getMarkupId()));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
}
|
||||
package io.onedev.server.web.component.issue.activities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
|
||||
import io.onedev.server.model.support.issue.changedata.IssueDescriptionChangeData;
|
||||
import org.apache.wicket.AttributeModifier;
|
||||
import org.apache.wicket.Component;
|
||||
import org.apache.wicket.RestartResponseAtInterceptPageException;
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
|
||||
import org.apache.wicket.ajax.markup.html.AjaxLink;
|
||||
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
|
||||
import org.apache.wicket.behavior.AttributeAppender;
|
||||
import org.apache.wicket.behavior.Behavior;
|
||||
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
|
||||
import org.apache.wicket.feedback.FencedFeedbackPanel;
|
||||
import org.apache.wicket.markup.head.CssHeaderItem;
|
||||
import org.apache.wicket.markup.head.IHeaderResponse;
|
||||
import org.apache.wicket.markup.html.WebMarkupContainer;
|
||||
import org.apache.wicket.markup.html.form.Form;
|
||||
import org.apache.wicket.markup.html.link.Link;
|
||||
import org.apache.wicket.markup.html.panel.Fragment;
|
||||
import org.apache.wicket.markup.html.panel.Panel;
|
||||
import org.apache.wicket.markup.repeater.RepeatingView;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.apache.wicket.request.cycle.RequestCycle;
|
||||
import org.apache.wicket.request.http.WebRequest;
|
||||
import org.apache.wicket.request.http.WebResponse;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.attachment.AttachmentSupport;
|
||||
import io.onedev.server.attachment.ProjectAttachmentSupport;
|
||||
import io.onedev.server.entitymanager.IssueCommentManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.entityreference.ReferencedFromAware;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueChange;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.util.facade.UserCache;
|
||||
import io.onedev.server.web.ajaxlistener.ConfirmLeaveListener;
|
||||
import io.onedev.server.web.behavior.WebSocketObserver;
|
||||
import io.onedev.server.web.component.issue.activities.activity.IssueActivity;
|
||||
import io.onedev.server.web.component.issue.activities.activity.IssueChangeActivity;
|
||||
import io.onedev.server.web.component.issue.activities.activity.IssueCommentedActivity;
|
||||
import io.onedev.server.web.component.issue.activities.activity.IssueOpenedActivity;
|
||||
import io.onedev.server.web.component.project.comment.CommentInput;
|
||||
import io.onedev.server.web.component.user.ident.Mode;
|
||||
import io.onedev.server.web.component.user.ident.UserIdentPanel;
|
||||
import io.onedev.server.web.page.simple.security.LoginPage;
|
||||
import io.onedev.server.web.util.DeleteCallback;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class IssueActivitiesPanel extends Panel {
|
||||
|
||||
private static final String COOKIE_SHOW_COMMENTS = "onedev.server.issue.showComments";
|
||||
|
||||
private static final String COOKIE_SHOW_CHANGE_HISTORY = "onedev.server.issue.showChangeHistory";
|
||||
|
||||
private RepeatingView activitiesView;
|
||||
|
||||
private boolean showComments = true;
|
||||
|
||||
private boolean showChangeHistory = true;
|
||||
|
||||
public IssueActivitiesPanel(String panelId) {
|
||||
super(panelId);
|
||||
|
||||
WebRequest request = (WebRequest) RequestCycle.get().getRequest();
|
||||
Cookie cookie = request.getCookie(COOKIE_SHOW_COMMENTS);
|
||||
if (cookie != null)
|
||||
showComments = Boolean.valueOf(cookie.getValue());
|
||||
|
||||
cookie = request.getCookie(COOKIE_SHOW_CHANGE_HISTORY);
|
||||
if (cookie != null)
|
||||
showChangeHistory = Boolean.valueOf(cookie.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBeforeRender() {
|
||||
activitiesView = new RepeatingView("activities");
|
||||
addOrReplace(activitiesView);
|
||||
Issue issue = getIssue();
|
||||
|
||||
for (IssueActivity activity: getActivities()) {
|
||||
if (issue.isVisitedAfter(activity.getDate())) {
|
||||
activitiesView.add(newActivityRow(activitiesView.newChildId(), activity));
|
||||
} else {
|
||||
Component row = newActivityRow(activitiesView.newChildId(), activity);
|
||||
row.add(AttributeAppender.append("class", "new"));
|
||||
activitiesView.add(row);
|
||||
}
|
||||
}
|
||||
|
||||
super.onBeforeRender();
|
||||
}
|
||||
|
||||
private List<IssueActivity> getActivities() {
|
||||
List<IssueActivity> activities = new ArrayList<>();
|
||||
|
||||
activities.add(new IssueOpenedActivity(getIssue()));
|
||||
|
||||
List<IssueActivity> otherActivities = new ArrayList<>();
|
||||
|
||||
if (showChangeHistory) {
|
||||
for (IssueChange change: getIssue().getChanges()) {
|
||||
if (change.getData() instanceof ReferencedFromAware) {
|
||||
ReferencedFromAware<?> referencedFromAware = (ReferencedFromAware<?>) change.getData();
|
||||
if (referencedFromAware.getReferencedFrom() instanceof Issue) {
|
||||
Issue issue = (Issue) referencedFromAware.getReferencedFrom();
|
||||
if (SecurityUtils.canAccess(issue))
|
||||
otherActivities.add(new IssueChangeActivity(change));
|
||||
} else if (referencedFromAware.getReferencedFrom() != null) {
|
||||
otherActivities.add(new IssueChangeActivity(change));
|
||||
}
|
||||
} else if (!(change.getData() instanceof IssueDescriptionChangeData)) {
|
||||
otherActivities.add(new IssueChangeActivity(change));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showComments) {
|
||||
for (IssueComment comment: getIssue().getComments())
|
||||
otherActivities.add(new IssueCommentedActivity(comment));
|
||||
}
|
||||
|
||||
otherActivities.sort((o1, o2) -> {
|
||||
if (o1.getDate().getTime()<o2.getDate().getTime())
|
||||
return -1;
|
||||
else if (o1.getDate().getTime()>o2.getDate().getTime())
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
});
|
||||
|
||||
activities.addAll(otherActivities);
|
||||
|
||||
return activities;
|
||||
}
|
||||
|
||||
private Component newActivityRow(String id, IssueActivity activity) {
|
||||
WebMarkupContainer row = new WebMarkupContainer(id, Model.of(activity));
|
||||
row.setOutputMarkupId(true);
|
||||
String anchor = activity.getAnchor();
|
||||
if (anchor != null)
|
||||
row.setMarkupId(anchor);
|
||||
|
||||
if (activity.getUser() != null) {
|
||||
row.add(new UserIdentPanel("avatar", activity.getUser(), Mode.AVATAR));
|
||||
row.add(AttributeAppender.append("class", "with-avatar"));
|
||||
} else {
|
||||
row.add(new WebMarkupContainer("avatar").setVisible(false));
|
||||
}
|
||||
|
||||
row.add(activity.render("content", new DeleteCallback() {
|
||||
|
||||
@Override
|
||||
public void onDelete(AjaxRequestTarget target) {
|
||||
row.remove();
|
||||
target.appendJavaScript(String.format("$('#%s').remove();", row.getMarkupId()));
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
row.add(AttributeAppender.append("class", activity.getClass().getSimpleName()));
|
||||
return row;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
add(new WebSocketObserver() {
|
||||
|
||||
@Override
|
||||
public void onObservableChanged(IPartialPageRequestHandler handler) {
|
||||
updateActivities(handler);
|
||||
}
|
||||
|
||||
private void updateActivities(IPartialPageRequestHandler handler) {
|
||||
@SuppressWarnings("deprecation")
|
||||
Component prevActivityRow = activitiesView.get(activitiesView.size()-1);
|
||||
IssueActivity lastActivity = (IssueActivity) prevActivityRow.getDefaultModelObject();
|
||||
List<IssueActivity> newActivities = new ArrayList<>();
|
||||
for (IssueActivity activity: getActivities()) {
|
||||
if (activity.getDate().getTime() > lastActivity.getDate().getTime())
|
||||
newActivities.add(activity);
|
||||
}
|
||||
|
||||
for (IssueActivity activity: newActivities) {
|
||||
Component newActivityRow = newActivityRow(activitiesView.newChildId(), activity);
|
||||
newActivityRow.add(AttributeAppender.append("class", "new"));
|
||||
activitiesView.add(newActivityRow);
|
||||
|
||||
String script = String.format("$(\"<tr id='%s'></tr>\").insertAfter('#%s');",
|
||||
newActivityRow.getMarkupId(), prevActivityRow.getMarkupId());
|
||||
handler.prependJavaScript(script);
|
||||
handler.add(newActivityRow);
|
||||
prevActivityRow = newActivityRow;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getObservables() {
|
||||
return Lists.newArrayList(Issue.getWebSocketObservable(getIssue().getId()));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (SecurityUtils.getUser() != null) {
|
||||
Fragment fragment = new Fragment("addComment", "addCommentFrag", this);
|
||||
fragment.setOutputMarkupId(true);
|
||||
CommentInput input = new CommentInput("comment", Model.of(""), false) {
|
||||
|
||||
@Override
|
||||
protected AttachmentSupport getAttachmentSupport() {
|
||||
return new ProjectAttachmentSupport(getProject(), getIssue().getUUID(),
|
||||
SecurityUtils.canManageIssues(getProject()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Project getProject() {
|
||||
return getIssue().getProject();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<User> getMentionables() {
|
||||
UserCache cache = OneDev.getInstance(UserManager.class).cloneCache();
|
||||
List<User> users = new ArrayList<>(cache.getUsers());
|
||||
users.sort(cache.comparingDisplayName(getIssue().getParticipants()));
|
||||
return users;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Behavior> getInputBehaviors() {
|
||||
return Lists.newArrayList(AttributeModifier.replace("placeholder", "Leave a comment"));
|
||||
}
|
||||
|
||||
};
|
||||
input.setRequired(true).setLabel(Model.of("Comment"));
|
||||
|
||||
Form<?> form = new Form<Void>("form");
|
||||
form.add(new FencedFeedbackPanel("feedback", form));
|
||||
form.add(input);
|
||||
form.add(new AjaxSubmitLink("save") {
|
||||
|
||||
@Override
|
||||
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
|
||||
super.onSubmit(target, form);
|
||||
String content = input.getModelObject();
|
||||
if (content.length() > IssueComment.MAX_CONTENT_LEN) {
|
||||
error("Comment too long");
|
||||
target.add(form);
|
||||
} else {
|
||||
IssueComment comment = new IssueComment();
|
||||
comment.setContent(content);
|
||||
comment.setUser(SecurityUtils.getUser());
|
||||
comment.setDate(new Date());
|
||||
comment.setIssue(getIssue());
|
||||
OneDev.getInstance(IssueCommentManager.class).create(comment, new ArrayList<>());
|
||||
|
||||
input.clearMarkdown();
|
||||
|
||||
target.add(fragment);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onError(AjaxRequestTarget target, Form<?> form) {
|
||||
super.onError(target, form);
|
||||
target.add(form);
|
||||
}
|
||||
|
||||
});
|
||||
form.setOutputMarkupId(true);
|
||||
fragment.add(form);
|
||||
add(fragment);
|
||||
} else {
|
||||
Fragment fragment = new Fragment("addComment", "loginToCommentFrag", this);
|
||||
fragment.add(new Link<Void>("login") {
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
throw new RestartResponseAtInterceptPageException(LoginPage.class);
|
||||
}
|
||||
|
||||
});
|
||||
add(fragment);
|
||||
}
|
||||
|
||||
setOutputMarkupId(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
super.renderHead(response);
|
||||
response.render(CssHeaderItem.forReference(new IssueActivitiesCssResourceReference()));
|
||||
}
|
||||
|
||||
protected abstract Issue getIssue();
|
||||
|
||||
public Component renderOptions(String componentId) {
|
||||
Fragment fragment = new Fragment(componentId, "optionsFrag", this);
|
||||
fragment.add(new AjaxLink<Void>("showComments") {
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
if (showComments)
|
||||
add(AttributeAppender.append("class", "active"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
|
||||
super.updateAjaxAttributes(attributes);
|
||||
attributes.getAjaxCallListeners().add(new ConfirmLeaveListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
showComments = !showComments;
|
||||
WebResponse response = (WebResponse) RequestCycle.get().getResponse();
|
||||
Cookie cookie = new Cookie(COOKIE_SHOW_COMMENTS, String.valueOf(showComments));
|
||||
cookie.setPath("/");
|
||||
cookie.setMaxAge(Integer.MAX_VALUE);
|
||||
response.addCookie(cookie);
|
||||
target.add(IssueActivitiesPanel.this);
|
||||
target.appendJavaScript(String.format("$('#%s').toggleClass('active');", getMarkupId()));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
fragment.add(new AjaxLink<Void>("showChangeHistory") {
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
if (showChangeHistory)
|
||||
add(AttributeAppender.append("class", "active"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
|
||||
super.updateAjaxAttributes(attributes);
|
||||
attributes.getAjaxCallListeners().add(new ConfirmLeaveListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
showChangeHistory = !showChangeHistory;
|
||||
WebResponse response = (WebResponse) RequestCycle.get().getResponse();
|
||||
Cookie cookie = new Cookie(COOKIE_SHOW_CHANGE_HISTORY, String.valueOf(showChangeHistory));
|
||||
cookie.setPath("/");
|
||||
cookie.setMaxAge(Integer.MAX_VALUE);
|
||||
response.addCookie(cookie);
|
||||
target.add(IssueActivitiesPanel.this);
|
||||
target.appendJavaScript(String.format("$('#%s').toggleClass('active');", getMarkupId()));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
package io.onedev.server.web.component.issue.activities.activity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.markup.html.panel.Panel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.IssueCommentManager;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.util.DeleteCallback;
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.markup.html.panel.Panel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class IssueCommentedActivity implements IssueActivity {
|
||||
|
||||
@ -67,7 +67,7 @@ class IssueCommentedPanel extends GenericPanel<IssueComment> {
|
||||
if (comment.length() > IssueComment.MAX_CONTENT_LEN)
|
||||
throw new ExplicitException("Comment too long");
|
||||
IssueCommentedPanel.this.getComment().setContent(comment);
|
||||
OneDev.getInstance(IssueCommentManager.class).save(IssueCommentedPanel.this.getComment());
|
||||
OneDev.getInstance(IssueCommentManager.class).update(IssueCommentedPanel.this.getComment());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -3,6 +3,7 @@ package io.onedev.server.web.component.issue.activities.activity;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.onedev.server.entitymanager.IssueChangeManager;
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.behavior.AttributeAppender;
|
||||
import org.apache.wicket.markup.html.basic.Label;
|
||||
@ -49,7 +50,7 @@ class IssueOpenedPanel extends GenericPanel<Issue> {
|
||||
|
||||
@Override
|
||||
protected void onSaveComment(AjaxRequestTarget target, String comment) {
|
||||
OneDev.getInstance(IssueManager.class).saveDescription(getIssue(), comment);
|
||||
OneDev.getInstance(IssueChangeManager.class).changeDescription(getIssue(), comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -675,7 +675,7 @@ public abstract class IssueListPanel extends Panel {
|
||||
Collection<Issue> issues = new ArrayList<>();
|
||||
for (IModel<Issue> each: selectionColumn.getSelections())
|
||||
issues.add(each.getObject());
|
||||
OneDev.getInstance(IssueManager.class).move(issues, getTargetProject());
|
||||
OneDev.getInstance(IssueManager.class).move(issues, getProject(), getTargetProject());
|
||||
setResponsePage(ProjectIssueListPage.class,
|
||||
ProjectIssueListPage.paramsOf(getTargetProject(), null, 0));
|
||||
Session.get().success("Issues moved");
|
||||
@ -757,7 +757,7 @@ public abstract class IssueListPanel extends Panel {
|
||||
Collection<Issue> issues = new ArrayList<>();
|
||||
for (IModel<Issue> each: selectionColumn.getSelections())
|
||||
issues.add(each.getObject());
|
||||
OneDev.getInstance(IssueManager.class).copy(issues, getTargetProject());
|
||||
OneDev.getInstance(IssueManager.class).copy(issues, getProject(), getTargetProject());
|
||||
setResponsePage(ProjectIssueListPage.class,
|
||||
ProjectIssueListPage.paramsOf(getTargetProject(), null, 0));
|
||||
Session.get().success("Issues copied");
|
||||
@ -821,7 +821,7 @@ public abstract class IssueListPanel extends Panel {
|
||||
Collection<Issue> issues = new ArrayList<>();
|
||||
for (IModel<Issue> each: selectionColumn.getSelections())
|
||||
issues.add(each.getObject());
|
||||
OneDev.getInstance(IssueManager.class).delete(issues);
|
||||
OneDev.getInstance(IssueManager.class).delete(issues, getProject());
|
||||
selectionColumn.getSelections().clear();
|
||||
target.add(body);
|
||||
}
|
||||
@ -978,7 +978,7 @@ public abstract class IssueListPanel extends Panel {
|
||||
for (Iterator<Issue> it = (Iterator<Issue>) dataProvider.iterator(0, issuesTable.getItemCount()); it.hasNext();) {
|
||||
issues.add(it.next());
|
||||
}
|
||||
OneDev.getInstance(IssueManager.class).move(issues, getTargetProject());
|
||||
OneDev.getInstance(IssueManager.class).move(issues, getProject(), getTargetProject());
|
||||
setResponsePage(ProjectIssueListPage.class,
|
||||
ProjectIssueListPage.paramsOf(getTargetProject(), null, 0));
|
||||
Session.get().success("Issues moved");
|
||||
@ -1062,7 +1062,7 @@ public abstract class IssueListPanel extends Panel {
|
||||
for (Iterator<Issue> it = (Iterator<Issue>) dataProvider.iterator(0, issuesTable.getItemCount()); it.hasNext();) {
|
||||
issues.add(it.next());
|
||||
}
|
||||
OneDev.getInstance(IssueManager.class).copy(issues, getTargetProject());
|
||||
OneDev.getInstance(IssueManager.class).copy(issues, getProject(), getTargetProject());
|
||||
setResponsePage(ProjectIssueListPage.class,
|
||||
ProjectIssueListPage.paramsOf(getTargetProject(), null, 0));
|
||||
Session.get().success("Issues copied");
|
||||
@ -1128,7 +1128,7 @@ public abstract class IssueListPanel extends Panel {
|
||||
Collection<Issue> issues = new ArrayList<>();
|
||||
for (Iterator<Issue> it = (Iterator<Issue>) dataProvider.iterator(0, issuesTable.getItemCount()); it.hasNext();)
|
||||
issues.add(it.next());
|
||||
OneDev.getInstance(IssueManager.class).delete(issues);
|
||||
OneDev.getInstance(IssueManager.class).delete(issues, getProject());
|
||||
dataProvider.detach();
|
||||
selectionColumn.getSelections().clear();
|
||||
target.add(body);
|
||||
|
||||
@ -1,29 +1,6 @@
|
||||
package io.onedev.server.plugin.imports.bitbucketcloud;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Invocation;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.glassfish.jersey.client.ClientProperties;
|
||||
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import io.onedev.commons.bootstrap.SensitiveMasker;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
@ -39,6 +16,26 @@ import io.onedev.server.util.validation.Validatable;
|
||||
import io.onedev.server.util.validation.annotation.ClassValidating;
|
||||
import io.onedev.server.web.editable.annotation.Editable;
|
||||
import io.onedev.server.web.editable.annotation.Password;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.glassfish.jersey.client.ClientProperties;
|
||||
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Invocation;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Editable
|
||||
@ClassValidating
|
||||
@ -188,7 +185,8 @@ public class ImportServer implements Serializable, Validatable {
|
||||
if (!isPrivate && option.getPublicRole() != null)
|
||||
project.setDefaultRole(option.getPublicRole());
|
||||
|
||||
if (project.isNew() || project.getDefaultBranch() == null) {
|
||||
boolean newlyCreated = project.isNew();
|
||||
if (newlyCreated || project.getDefaultBranch() == null) {
|
||||
logger.log("Cloning code from repository " + projectMapping.getBitbucketRepo() + "...");
|
||||
|
||||
String cloneUrl = null;
|
||||
@ -216,7 +214,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
if (dryRun) {
|
||||
new LsRemoteCommand(builder.build().toString()).refs("HEAD").quiet(true).run();
|
||||
} else {
|
||||
if (project.isNew())
|
||||
if (newlyCreated)
|
||||
projectManager.create(project);
|
||||
projectManager.clone(project, builder.build().toString());
|
||||
}
|
||||
@ -227,7 +225,6 @@ public class ImportServer implements Serializable, Validatable {
|
||||
logger.warning("Skipping code clone as the project already has code");
|
||||
}
|
||||
}
|
||||
|
||||
return "Repositories imported successfully";
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
package io.onedev.server.plugin.imports.gitea;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.imports.IssueImporter;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.util.ImportStep;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.imports.IssueImporter;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.util.ImportStep;
|
||||
|
||||
public class GiteaIssueImporter implements IssueImporter {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -83,8 +82,8 @@ public class GiteaIssueImporter implements IssueImporter {
|
||||
logger.log("Importing issues from repository " + giteaRepo + "...");
|
||||
Map<String, Optional<User>> users = new HashMap<>();
|
||||
|
||||
return server.importIssues(giteaRepo, project, option, users, dryRun, logger)
|
||||
.toHtml("Issues imported successfully");
|
||||
ImportResult result = server.importIssues(giteaRepo, project, option, users, dryRun, logger);
|
||||
return result.toHtml("Issues imported successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -6,6 +6,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.onedev.server.model.Issue;
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
public class ImportResult {
|
||||
@ -18,7 +19,7 @@ public class ImportResult {
|
||||
|
||||
Set<String> nonExistentMilestones = new HashSet<>();
|
||||
|
||||
boolean issuesImported = false;
|
||||
Set<Issue> importedIssues = new HashSet<>();
|
||||
|
||||
private String getEntryFeedback(String entryDescription, Collection<String> entries) {
|
||||
if (entries.size() > MAX_DISPLAY_ENTRIES) {
|
||||
@ -35,7 +36,7 @@ public class ImportResult {
|
||||
boolean hasNotice = false;
|
||||
|
||||
if (!nonExistentMilestones.isEmpty() || !unmappedIssueLabels.isEmpty()
|
||||
|| !nonExistentLogins.isEmpty() || issuesImported) {
|
||||
|| !nonExistentLogins.isEmpty() || !importedIssues.isEmpty()) {
|
||||
hasNotice = true;
|
||||
}
|
||||
|
||||
@ -51,7 +52,7 @@ public class ImportResult {
|
||||
nonExistentLogins));
|
||||
}
|
||||
|
||||
if (issuesImported) {
|
||||
if (!importedIssues.isEmpty()) {
|
||||
feedback.append("<li> Attachments in issue description and comments are not imported as Gitea does not "
|
||||
+ "provide attachment api currently");
|
||||
feedback.append("<li> Issue dependencies are not imported as Gitea does not "
|
||||
|
||||
@ -1,62 +1,18 @@
|
||||
package io.onedev.server.plugin.imports.gitea;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Invocation;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.glassfish.jersey.client.ClientProperties;
|
||||
import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
import io.onedev.commons.bootstrap.SensitiveMasker;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.IssueManager;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.entitymanager.*;
|
||||
import io.onedev.server.entityreference.ReferenceMigrator;
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.project.issue.IssuesImported;
|
||||
import io.onedev.server.git.command.LsRemoteCommand;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.model.IssueField;
|
||||
import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.*;
|
||||
import io.onedev.server.model.support.LastActivity;
|
||||
import io.onedev.server.model.support.administration.GlobalIssueSetting;
|
||||
import io.onedev.server.model.support.inputspec.InputSpec;
|
||||
@ -70,6 +26,29 @@ import io.onedev.server.util.validation.Validatable;
|
||||
import io.onedev.server.util.validation.annotation.ClassValidating;
|
||||
import io.onedev.server.web.editable.annotation.Editable;
|
||||
import io.onedev.server.web.editable.annotation.Password;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.glassfish.jersey.client.ClientProperties;
|
||||
import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Invocation;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Editable
|
||||
@ClassValidating
|
||||
@ -460,7 +439,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
if (issue.getDescription() != null)
|
||||
issue.setDescription(migrator.migratePrefixed(issue.getDescription(), "#"));
|
||||
|
||||
issueManager.save(issue);
|
||||
dao.persist(issue);
|
||||
for (IssueSchedule schedule: issue.getSchedules())
|
||||
dao.persist(schedule);
|
||||
for (IssueField field: issue.getFields())
|
||||
@ -476,9 +455,10 @@ public class ImportServer implements Serializable, Validatable {
|
||||
result.nonExistentLogins.addAll(nonExistentLogins);
|
||||
result.nonExistentMilestones.addAll(nonExistentMilestones);
|
||||
result.unmappedIssueLabels.addAll(unmappedIssueLabels);
|
||||
result.importedIssues.addAll(issues);
|
||||
|
||||
if (numOfImportedIssues.get() != 0)
|
||||
result.issuesImported = true;
|
||||
if (!dryRun && !issues.isEmpty())
|
||||
OneDev.getInstance(ListenerRegistry.class).post(new IssuesImported(oneDevProject, issues));
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
@ -565,8 +545,8 @@ public class ImportServer implements Serializable, Validatable {
|
||||
result.nonExistentLogins.addAll(currentResult.nonExistentLogins);
|
||||
result.nonExistentMilestones.addAll(currentResult.nonExistentMilestones);
|
||||
result.unmappedIssueLabels.addAll(currentResult.unmappedIssueLabels);
|
||||
result.issuesImported |= currentResult.issuesImported;
|
||||
}
|
||||
result.importedIssues.addAll(currentResult.importedIssues);
|
||||
}
|
||||
}
|
||||
|
||||
return result.toHtml("Repositories imported successfully");
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
package io.onedev.server.plugin.imports.github;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.event.project.issue.IssuesImported;
|
||||
import io.onedev.server.imports.IssueImporter;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.util.ImportStep;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.imports.IssueImporter;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.util.ImportStep;
|
||||
|
||||
public class GitHubIssueImporter implements IssueImporter {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -79,7 +79,7 @@ public class GitHubIssueImporter implements IssueImporter {
|
||||
logger.log("Importing issues from repository " + repositoryStep.getSetting().getRepository() + "...");
|
||||
Map<String, Optional<User>> users = new HashMap<>();
|
||||
|
||||
ImportResult result = serverStep.getSetting().importIssues(repositoryStep.getSetting().getRepository(),
|
||||
ImportResult result = serverStep.getSetting().importIssues(repositoryStep.getSetting().getRepository(),
|
||||
project, optionStep.getSetting(), users, dryRun, logger);
|
||||
return result.toHtml("Issues imported successfully");
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import io.onedev.server.model.Issue;
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
public class ImportResult {
|
||||
@ -18,7 +19,7 @@ public class ImportResult {
|
||||
|
||||
Set<String> nonExistentMilestones = new HashSet<>();
|
||||
|
||||
boolean issuesImported = false;
|
||||
Set<Issue> importedIssues = new HashSet<>();
|
||||
|
||||
private String getEntryFeedback(String entryDescription, Collection<String> entries) {
|
||||
if (entries.size() > MAX_DISPLAY_ENTRIES) {
|
||||
@ -35,7 +36,7 @@ public class ImportResult {
|
||||
boolean hasNotice = false;
|
||||
|
||||
if (!nonExistentMilestones.isEmpty() || !unmappedIssueLabels.isEmpty()
|
||||
|| !nonExistentLogins.isEmpty() || issuesImported) {
|
||||
|| !nonExistentLogins.isEmpty() || !importedIssues.isEmpty()) {
|
||||
hasNotice = true;
|
||||
}
|
||||
|
||||
@ -53,7 +54,7 @@ public class ImportResult {
|
||||
nonExistentLogins));
|
||||
}
|
||||
|
||||
if (issuesImported)
|
||||
if (!importedIssues.isEmpty())
|
||||
feedback.append("<li> Attachments in issue description and comments are not imported due to GitHub limitation");
|
||||
|
||||
if (hasNotice)
|
||||
|
||||
@ -9,6 +9,8 @@ import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.*;
|
||||
import io.onedev.server.entityreference.ReferenceMigrator;
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.project.issue.IssuesImported;
|
||||
import io.onedev.server.git.command.LsRemoteCommand;
|
||||
import io.onedev.server.model.*;
|
||||
import io.onedev.server.model.support.LastActivity;
|
||||
@ -412,7 +414,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
if (issue.getDescription() != null)
|
||||
issue.setDescription(migrator.migratePrefixed(issue.getDescription(), "#"));
|
||||
|
||||
issueManager.save(issue);
|
||||
dao.persist(issue);
|
||||
for (IssueSchedule schedule: issue.getSchedules())
|
||||
dao.persist(schedule);
|
||||
for (IssueField field: issue.getFields())
|
||||
@ -428,9 +430,10 @@ public class ImportServer implements Serializable, Validatable {
|
||||
result.nonExistentLogins.addAll(nonExistentLogins);
|
||||
result.nonExistentMilestones.addAll(nonExistentMilestones);
|
||||
result.unmappedIssueLabels.addAll(unmappedIssueLabels);
|
||||
result.importedIssues.addAll(issues);
|
||||
|
||||
if (numOfImportedIssues.get() != 0)
|
||||
result.issuesImported = true;
|
||||
if (!dryRun && !issues.isEmpty())
|
||||
OneDev.getInstance(ListenerRegistry.class).post(new IssuesImported(oneDevProject, issues));
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
@ -592,8 +595,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
result.nonExistentLogins.addAll(currentResult.nonExistentLogins);
|
||||
result.nonExistentMilestones.addAll(currentResult.nonExistentMilestones);
|
||||
result.unmappedIssueLabels.addAll(currentResult.unmappedIssueLabels);
|
||||
|
||||
result.issuesImported |= currentResult.issuesImported;
|
||||
result.importedIssues.addAll(currentResult.importedIssues);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
package io.onedev.server.plugin.imports.gitlab;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.imports.IssueImporter;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.util.ImportStep;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.imports.IssueImporter;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.web.util.ImportStep;
|
||||
|
||||
public class GitLabIssueImporter implements IssueImporter {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
@ -81,8 +80,9 @@ public class GitLabIssueImporter implements IssueImporter {
|
||||
IssueImportOption option = optionStep.getSetting();
|
||||
logger.log("Importing issues from project " + gitLabProject + "...");
|
||||
Map<String, Optional<User>> users = new HashMap<>();
|
||||
return server.importIssues(gitLabProject, project, option, users, dryRun, logger)
|
||||
.toHtml("Issues imported successfully");
|
||||
|
||||
ImportResult result = server.importIssues(gitLabProject, project, option, users, dryRun, logger);
|
||||
return result.toHtml("Issues imported successfully");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
package io.onedev.server.plugin.imports.gitlab;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ImportResult {
|
||||
|
||||
private static final int MAX_DISPLAY_ENTRIES = 100;
|
||||
@ -22,7 +17,7 @@ public class ImportResult {
|
||||
Set<String> tooLargeAttachments = new LinkedHashSet<>();
|
||||
|
||||
Set<String> errorAttachments = new LinkedHashSet<>();
|
||||
|
||||
|
||||
private String getEntryFeedback(String entryDescription, Collection<String> entries) {
|
||||
if (entries.size() > MAX_DISPLAY_ENTRIES) {
|
||||
List<String> entriesToDisplay = new ArrayList<>(entries).subList(0, MAX_DISPLAY_ENTRIES);
|
||||
|
||||
@ -1,23 +1,37 @@
|
||||
package io.onedev.server.plugin.imports.gitlab;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
import io.onedev.commons.bootstrap.SensitiveMasker;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.attachment.AttachmentManager;
|
||||
import io.onedev.server.entitymanager.*;
|
||||
import io.onedev.server.entityreference.ReferenceMigrator;
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.project.issue.IssuesImported;
|
||||
import io.onedev.server.git.command.LsRemoteCommand;
|
||||
import io.onedev.server.model.*;
|
||||
import io.onedev.server.model.support.LastActivity;
|
||||
import io.onedev.server.model.support.administration.GlobalIssueSetting;
|
||||
import io.onedev.server.model.support.inputspec.InputSpec;
|
||||
import io.onedev.server.model.support.issue.field.spec.FieldSpec;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.util.*;
|
||||
import io.onedev.server.util.JerseyUtils.PageDataConsumer;
|
||||
import io.onedev.server.util.validation.Validatable;
|
||||
import io.onedev.server.util.validation.annotation.ClassValidating;
|
||||
import io.onedev.server.web.editable.annotation.Editable;
|
||||
import io.onedev.server.web.editable.annotation.Password;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.glassfish.jersey.client.ClientProperties;
|
||||
import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
@ -27,53 +41,15 @@ import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Invocation;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.glassfish.jersey.client.ClientProperties;
|
||||
import org.glassfish.jersey.client.oauth2.OAuth2ClientSupport;
|
||||
import org.joda.time.format.ISODateTimeFormat;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
import io.onedev.commons.bootstrap.SensitiveMasker;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.commons.utils.TaskLogger;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.attachment.AttachmentManager;
|
||||
import io.onedev.server.entitymanager.IssueManager;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.entityreference.ReferenceMigrator;
|
||||
import io.onedev.server.git.command.LsRemoteCommand;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.model.IssueField;
|
||||
import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.model.support.LastActivity;
|
||||
import io.onedev.server.model.support.administration.GlobalIssueSetting;
|
||||
import io.onedev.server.model.support.inputspec.InputSpec;
|
||||
import io.onedev.server.model.support.issue.field.spec.FieldSpec;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.util.AttachmentTooLargeException;
|
||||
import io.onedev.server.util.CollectionUtils;
|
||||
import io.onedev.server.util.DateUtils;
|
||||
import io.onedev.server.util.JerseyUtils;
|
||||
import io.onedev.server.util.JerseyUtils.PageDataConsumer;
|
||||
import io.onedev.server.util.Pair;
|
||||
import io.onedev.server.util.validation.Validatable;
|
||||
import io.onedev.server.util.validation.annotation.ClassValidating;
|
||||
import io.onedev.server.web.editable.annotation.Editable;
|
||||
import io.onedev.server.web.editable.annotation.Password;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Editable
|
||||
@ClassValidating
|
||||
@ -342,7 +318,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
result.errorAttachments.addAll(currentResult.errorAttachments);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result.toHtml("Projects imported successfully");
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -679,7 +655,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
if (issue.getDescription() != null)
|
||||
issue.setDescription(migrator.migratePrefixed(issue.getDescription(), "#"));
|
||||
|
||||
issueManager.save(issue);
|
||||
dao.persist(issue);
|
||||
for (IssueSchedule schedule: issue.getSchedules())
|
||||
dao.persist(schedule);
|
||||
for (IssueField field: issue.getFields())
|
||||
@ -698,6 +674,9 @@ public class ImportServer implements Serializable, Validatable {
|
||||
result.tooLargeAttachments.addAll(tooLargeAttachments);
|
||||
result.errorAttachments.addAll(errorAttachments);
|
||||
|
||||
if (!dryRun && !issues.isEmpty())
|
||||
OneDev.getInstance(ListenerRegistry.class).post(new IssuesImported(oneDevProject, issues));
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
if (!dryRun)
|
||||
|
||||
@ -1,14 +1,9 @@
|
||||
package io.onedev.server.plugin.imports.jiracloud;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ImportResult {
|
||||
|
||||
private static final int MAX_DISPLAY_ENTRIES = 100;
|
||||
|
||||
@ -33,6 +33,9 @@ import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.project.issue.IssuesImported;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.glassfish.jersey.client.ClientProperties;
|
||||
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
|
||||
@ -337,7 +340,6 @@ public class ImportServer implements Serializable, Validatable {
|
||||
try {
|
||||
Map<String, Optional<User>> users = new HashMap<>();
|
||||
ImportResult result = new ImportResult();
|
||||
|
||||
for (ProjectMapping projectMapping: projects.getProjectMappings()) {
|
||||
String jiraProject = projectMapping.getJiraProject();
|
||||
JsonNode projectNode = projectNodes.get(jiraProject);
|
||||
@ -821,7 +823,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
if (issue.getDescription() != null)
|
||||
issue.setDescription(migrator.migratePrefixed(issue.getDescription(), jiraProjectKey + "-"));
|
||||
|
||||
issueManager.save(issue);
|
||||
dao.persist(issue);
|
||||
for (IssueSchedule schedule: issue.getSchedules())
|
||||
dao.persist(schedule);
|
||||
for (IssueField field: issue.getFields())
|
||||
@ -841,6 +843,9 @@ public class ImportServer implements Serializable, Validatable {
|
||||
result.tooLargeAttachments.addAll(tooLargeAttachments);
|
||||
result.errorAttachments.addAll(errorAttachments);
|
||||
|
||||
if (!dryRun && !issues.isEmpty())
|
||||
OneDev.getInstance(ListenerRegistry.class).post(new IssuesImported(oneDevProject, issues));
|
||||
|
||||
return result;
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@ -1,15 +1,5 @@
|
||||
package io.onedev.server.plugin.imports.url;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.shiro.authz.UnauthorizedException;
|
||||
|
||||
import io.onedev.commons.bootstrap.SensitiveMasker;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
@ -24,6 +14,14 @@ import io.onedev.server.util.validation.Validatable;
|
||||
import io.onedev.server.util.validation.annotation.ClassValidating;
|
||||
import io.onedev.server.web.editable.annotation.Editable;
|
||||
import io.onedev.server.web.editable.annotation.Password;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.shiro.authz.UnauthorizedException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.io.Serializable;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
@Editable
|
||||
@ClassValidating
|
||||
@ -116,7 +114,8 @@ public class ImportServer implements Serializable, Validatable {
|
||||
if (dryRun) {
|
||||
new LsRemoteCommand(builder.build().toString()).refs("HEAD").quiet(true).run();
|
||||
} else {
|
||||
if (project.isNew())
|
||||
boolean newlyCreated = project.isNew();
|
||||
if (newlyCreated)
|
||||
getProjectManager().create(project);
|
||||
getProjectManager().clone(project, builder.build().toString());
|
||||
}
|
||||
|
||||
@ -1,15 +1,9 @@
|
||||
package io.onedev.server.plugin.imports.youtrack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ImportResult {
|
||||
|
||||
private static final int MAX_DISPLAY_ENTRIES = 100;
|
||||
|
||||
@ -9,6 +9,8 @@ import io.onedev.server.OneDev;
|
||||
import io.onedev.server.attachment.AttachmentManager;
|
||||
import io.onedev.server.entitymanager.*;
|
||||
import io.onedev.server.entityreference.ReferenceMigrator;
|
||||
import io.onedev.server.event.ListenerRegistry;
|
||||
import io.onedev.server.event.project.issue.IssuesImported;
|
||||
import io.onedev.server.model.*;
|
||||
import io.onedev.server.model.support.LastActivity;
|
||||
import io.onedev.server.model.support.administration.GlobalIssueSetting;
|
||||
@ -255,7 +257,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
return option;
|
||||
}
|
||||
|
||||
private ImportResult importIssues(String youTrackProjectId, Project oneDevProject,
|
||||
private ImportResult doImportIssues(String youTrackProjectId, Project oneDevProject,
|
||||
ImportOption option, boolean dryRun, TaskLogger logger) {
|
||||
IssueManager issueManager = OneDev.getInstance(IssueManager.class);
|
||||
Client client = newClient();
|
||||
@ -977,7 +979,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
if (issue.getDescription() != null)
|
||||
issue.setDescription(migrator.migratePrefixed(issue.getDescription(), youTrackProjectShortName + "-"));
|
||||
|
||||
issueManager.save(issue);
|
||||
dao.persist(issue);
|
||||
for (IssueSchedule schedule: issue.getSchedules())
|
||||
dao.persist(schedule);
|
||||
for (IssueField field: issue.getFields())
|
||||
@ -1019,6 +1021,9 @@ public class ImportServer implements Serializable, Validatable {
|
||||
result.unmappedIssueLinks.addAll(unmappedIssueLinks);
|
||||
result.unmappedIssueTags.addAll(unmappedIssueTags);
|
||||
|
||||
if (!dryRun && !issues.isEmpty())
|
||||
OneDev.getInstance(ListenerRegistry.class).post(new IssuesImported(oneDevProject, issues));
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
if (!dryRun)
|
||||
@ -1070,20 +1075,21 @@ public class ImportServer implements Serializable, Validatable {
|
||||
project.setDescription(youTrackProjectDescriptions.get(projectMapping.getYouTrackProject()));
|
||||
project.setIssueManagement(true);
|
||||
|
||||
if (!dryRun && project.isNew())
|
||||
boolean newlyCreated = project.isNew();
|
||||
if (!dryRun && newlyCreated)
|
||||
projectManager.create(project);
|
||||
|
||||
ImportResult currentResult = importIssues(youTrackProjectId, project,
|
||||
option, dryRun, logger);
|
||||
result.mismatchedIssueFields.putAll(currentResult.mismatchedIssueFields);
|
||||
result.nonExistentLogins.addAll(currentResult.nonExistentLogins);
|
||||
result.tooLargeAttachments.addAll(currentResult.tooLargeAttachments);
|
||||
result.unmappedIssueFields.addAll(currentResult.unmappedIssueFields);
|
||||
result.unmappedIssueStates.addAll(currentResult.unmappedIssueStates);
|
||||
result.unmappedIssueLinks.addAll(currentResult.unmappedIssueLinks);
|
||||
result.unmappedIssueTags.addAll(currentResult.unmappedIssueTags);
|
||||
ImportResult currentResult = doImportIssues(youTrackProjectId, project,
|
||||
option, dryRun, logger);
|
||||
result.mismatchedIssueFields.putAll(currentResult.mismatchedIssueFields);
|
||||
result.nonExistentLogins.addAll(currentResult.nonExistentLogins);
|
||||
result.tooLargeAttachments.addAll(currentResult.tooLargeAttachments);
|
||||
result.unmappedIssueFields.addAll(currentResult.unmappedIssueFields);
|
||||
result.unmappedIssueStates.addAll(currentResult.unmappedIssueStates);
|
||||
result.unmappedIssueLinks.addAll(currentResult.unmappedIssueLinks);
|
||||
result.unmappedIssueTags.addAll(currentResult.unmappedIssueTags);
|
||||
}
|
||||
|
||||
|
||||
return result.toHtml("Projects imported successfully");
|
||||
} finally {
|
||||
client.close();
|
||||
@ -1125,7 +1131,7 @@ public class ImportServer implements Serializable, Validatable {
|
||||
String apiEndpoint = getApiEndpoint("/admin/projects?fields=id,name");
|
||||
for (JsonNode projectNode: list(client, apiEndpoint, logger)) {
|
||||
if (youTrackProject.equals(projectNode.get("name").asText())) {
|
||||
ImportResult result = importIssues(projectNode.get("id").asText(),
|
||||
ImportResult result = doImportIssues(projectNode.get("id").asText(),
|
||||
project, option, dryRun, logger);
|
||||
return result.toHtml("Issues imported successfully");
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user