mirror of
https://github.com/theonedev/onedev.git
synced 2025-12-08 18:26:30 +00:00
feat: Replace milestone with iteration (OD-1961)
This commit is contained in:
parent
088ca2829b
commit
cb03254f97
@ -60,7 +60,7 @@ files in certain branches.
|
||||
|
||||
Move tasks manually in Kanban, or define rules to move them automatically
|
||||
when related work is committed/tested/released/deployed.
|
||||
[**See It In Action**](https://code.onedev.io/onedev/server/~boards/State?milestone=4.2.0&backlog=true)
|
||||
[**See It In Action**](https://code.onedev.io/onedev/server/~boards/State?iteration=4.2.0&backlog=true)
|
||||
|
||||

|
||||
|
||||
|
||||
@ -253,7 +253,7 @@ public class CoreModule extends AbstractPluginModule {
|
||||
bind(IssueChangeManager.class).to(DefaultIssueChangeManager.class);
|
||||
bind(IssueVoteManager.class).to(DefaultIssueVoteManager.class);
|
||||
bind(IssueWorkManager.class).to(DefaultIssueWorkManager.class);
|
||||
bind(MilestoneManager.class).to(DefaultMilestoneManager.class);
|
||||
bind(IterationManager.class).to(DefaultIterationManager.class);
|
||||
bind(IssueCommentManager.class).to(DefaultIssueCommentManager.class);
|
||||
bind(IssueQueryPersonalizationManager.class).to(DefaultIssueQueryPersonalizationManager.class);
|
||||
bind(PullRequestQueryPersonalizationManager.class).to(DefaultPullRequestQueryPersonalizationManager.class);
|
||||
|
||||
@ -7,6 +7,6 @@ import java.lang.annotation.Target;
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MilestoneChoice {
|
||||
public @interface IterationChoice {
|
||||
String value() default "";
|
||||
}
|
||||
@ -2075,5 +2075,53 @@ public class BuildSpec implements Serializable, Validatable {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateParamSpecs(VersionedYamlDoc doc, Stack<Integer> versions,
|
||||
Consumer<SequenceNode> paramSpecMigrator) {
|
||||
for (NodeTuple specTuple: doc.getValue()) {
|
||||
String specObjectKey = ((ScalarNode)specTuple.getKeyNode()).getValue();
|
||||
if (specObjectKey.equals("jobs")) {
|
||||
SequenceNode jobsNode = (SequenceNode) specTuple.getValueNode();
|
||||
for (Node jobsNodeItem: jobsNode.getValue()) {
|
||||
MappingNode jobNode = (MappingNode) jobsNodeItem;
|
||||
for (NodeTuple jobTuple: jobNode.getValue()) {
|
||||
String jobTupleKey = ((ScalarNode)jobTuple.getKeyNode()).getValue();
|
||||
if (jobTupleKey.equals("paramSpecs"))
|
||||
paramSpecMigrator.accept((SequenceNode) jobTuple.getValueNode());
|
||||
}
|
||||
}
|
||||
} else if (specObjectKey.equals("stepTemplates")) {
|
||||
SequenceNode stepTemplatesNode = (SequenceNode) specTuple.getValueNode();
|
||||
for (Node stepTemplatesNodeItem: stepTemplatesNode.getValue()) {
|
||||
MappingNode stepTemplateNode = (MappingNode) stepTemplatesNodeItem;
|
||||
for (NodeTuple stepTemplateTuple: stepTemplateNode.getValue()) {
|
||||
String stepTemplateTupleKey = ((ScalarNode)stepTemplateTuple.getKeyNode()).getValue();
|
||||
if (stepTemplateTupleKey.equals("paramSpecs"))
|
||||
paramSpecMigrator.accept((SequenceNode)stepTemplateTuple.getValueNode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void migrate34(VersionedYamlDoc doc, Stack<Integer> versions) {
|
||||
migrateSteps(doc, versions, stepsNode -> {
|
||||
for (var itStepNode = stepsNode.getValue().iterator(); itStepNode.hasNext();) {
|
||||
MappingNode stepNode = (MappingNode) itStepNode.next();
|
||||
var stepType = stepNode.getTag().getValue();
|
||||
if (stepType.equals("!CloseMilestoneStep"))
|
||||
stepNode.setTag(new Tag("!CloseIterationStep"));
|
||||
}
|
||||
});
|
||||
migrateParamSpecs(doc, versions, paramSpecsNode -> {
|
||||
for (var itParamSpecNode = paramSpecsNode.getValue().iterator(); itParamSpecNode.hasNext();) {
|
||||
MappingNode paramSpecNode = (MappingNode) itParamSpecNode.next();
|
||||
var paramSpecType = paramSpecNode.getTag().getValue();
|
||||
if (paramSpecType.equals("!MilestoneChoiceParam"))
|
||||
paramSpecNode.setTag(new Tag("!IterationChoiceParam"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,33 +3,33 @@ package io.onedev.server.buildspec.param.spec;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.onedev.server.buildspecmodel.inputspec.MilestoneChoiceInput;
|
||||
import io.onedev.server.buildspecmodel.inputspec.IterationChoiceInput;
|
||||
import io.onedev.server.annotation.Editable;
|
||||
|
||||
@Editable(order=1110, name=ParamSpec.MILESTONE)
|
||||
public class MilestoneChoiceParam extends ParamSpec {
|
||||
@Editable(order=1110, name=ParamSpec.ITERATION)
|
||||
public class IterationChoiceParam extends ParamSpec {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public String getPropertyDef(Map<String, Integer> indexes) {
|
||||
return MilestoneChoiceInput.getPropertyDef(this, indexes);
|
||||
return IterationChoiceInput.getPropertyDef(this, indexes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertToObject(List<String> strings) {
|
||||
return MilestoneChoiceInput.convertToObject(this, strings);
|
||||
return IterationChoiceInput.convertToObject(this, strings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> convertToStrings(Object value) {
|
||||
return MilestoneChoiceInput.convertToStrings(this, value);
|
||||
return IterationChoiceInput.convertToStrings(this, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getOrdinal(String fieldValue) {
|
||||
if (fieldValue != null)
|
||||
return MilestoneChoiceInput.getOrdinal(fieldValue);
|
||||
return IterationChoiceInput.getOrdinal(fieldValue);
|
||||
else
|
||||
return super.getOrdinal(fieldValue);
|
||||
}
|
||||
@ -9,8 +9,8 @@ import io.onedev.server.annotation.Editable;
|
||||
import io.onedev.server.annotation.Interpolative;
|
||||
import io.onedev.server.buildspec.BuildSpec;
|
||||
import io.onedev.server.entitymanager.BuildManager;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.entitymanager.IterationManager;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
|
||||
@ -19,24 +19,24 @@ import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Editable(name="Close Milestone", order=400)
|
||||
public class CloseMilestoneStep extends ServerSideStep {
|
||||
@Editable(name="Close Iteration", order=400)
|
||||
public class CloseIterationStep extends ServerSideStep {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String milestoneName;
|
||||
private String iterationName;
|
||||
|
||||
private String accessTokenSecret;
|
||||
|
||||
@Editable(order=1000, description="Specify name of the milestone")
|
||||
@Editable(order=1000, description="Specify name of the iteration")
|
||||
@Interpolative(variableSuggester="suggestVariables")
|
||||
@NotEmpty
|
||||
public String getMilestoneName() {
|
||||
return milestoneName;
|
||||
public String getIterationName() {
|
||||
return iterationName;
|
||||
}
|
||||
|
||||
public void setMilestoneName(String milestoneName) {
|
||||
this.milestoneName = milestoneName;
|
||||
public void setIterationName(String iterationName) {
|
||||
this.iterationName = iterationName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ -66,19 +66,19 @@ public class CloseMilestoneStep extends ServerSideStep {
|
||||
return OneDev.getInstance(TransactionManager.class).call(() -> {
|
||||
var build = OneDev.getInstance(BuildManager.class).load(buildId);
|
||||
Project project = build.getProject();
|
||||
String milestoneName = getMilestoneName();
|
||||
MilestoneManager milestoneManager = OneDev.getInstance(MilestoneManager.class);
|
||||
Milestone milestone = milestoneManager.findInHierarchy(project, milestoneName);
|
||||
if (milestone != null) {
|
||||
if (build.canCloseMilestone(getAccessTokenSecret())) {
|
||||
milestone.setClosed(true);
|
||||
milestoneManager.createOrUpdate(milestone);
|
||||
String iterationName = getIterationName();
|
||||
IterationManager iterationManager = OneDev.getInstance(IterationManager.class);
|
||||
Iteration iteration = iterationManager.findInHierarchy(project, iterationName);
|
||||
if (iteration != null) {
|
||||
if (build.canCloseIteration(getAccessTokenSecret())) {
|
||||
iteration.setClosed(true);
|
||||
iterationManager.createOrUpdate(iteration);
|
||||
} else {
|
||||
logger.error("This build is not authorized to close milestone '" + milestoneName + "'");
|
||||
logger.error("This build is not authorized to close iteration '" + iterationName + "'");
|
||||
return new ServerStepResult(false);
|
||||
}
|
||||
} else {
|
||||
logger.warning("Unable to find milestone '" + milestoneName + "' to close. Ignored.");
|
||||
logger.warning("Unable to find iteration '" + iterationName + "' to close. Ignored.");
|
||||
}
|
||||
return new ServerStepResult(true);
|
||||
});
|
||||
@ -54,7 +54,7 @@ public abstract class InputSpec implements Serializable {
|
||||
|
||||
public static final String ISSUE = "Issue";
|
||||
|
||||
public static final String MILESTONE = "Milestone";
|
||||
public static final String ITERATION = "Iteration";
|
||||
|
||||
public static final String BUILD = "Build";
|
||||
|
||||
|
||||
@ -7,10 +7,10 @@ import java.util.Map;
|
||||
import javax.validation.ValidationException;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
|
||||
public class MilestoneChoiceInput {
|
||||
public class IterationChoiceInput {
|
||||
|
||||
public static String getPropertyDef(InputSpec inputSpec, Map<String, Integer> indexes) {
|
||||
int index = indexes.get(inputSpec.getName());
|
||||
@ -19,12 +19,12 @@ public class MilestoneChoiceInput {
|
||||
inputSpec.appendCommonAnnotations(buffer, index);
|
||||
if (!inputSpec.isAllowEmpty()) {
|
||||
if (inputSpec.isAllowMultiple())
|
||||
buffer.append(" @Size(min=1, message=\"At least one milestone needs to be selected\")\n");
|
||||
buffer.append(" @Size(min=1, message=\"At least one iteration needs to be selected\")\n");
|
||||
else
|
||||
buffer.append(" @NotEmpty\n");
|
||||
}
|
||||
|
||||
buffer.append(" @MilestoneChoice\n");
|
||||
buffer.append(" @IterationChoice\n");
|
||||
|
||||
if (inputSpec.isAllowMultiple())
|
||||
inputSpec.appendMethods(buffer, index, "List<String>", null, null);
|
||||
@ -62,11 +62,11 @@ public class MilestoneChoiceInput {
|
||||
int ordinal = -1;
|
||||
Project project = Project.get();
|
||||
if (project != null) {
|
||||
List<Milestone> milestones = new ArrayList<>(project.getHierarchyMilestones());
|
||||
Collections.sort(milestones);
|
||||
for (Milestone milestone: milestones) {
|
||||
List<Iteration> iterations = new ArrayList<>(project.getHierarchyIterations());
|
||||
Collections.sort(iterations);
|
||||
for (Iteration iteration: iterations) {
|
||||
ordinal++;
|
||||
if (milestone.getName().equals(fieldValue))
|
||||
if (iteration.getName().equals(fieldValue))
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -6443,6 +6443,107 @@ public class DataMigrator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void migrate167(File dataDir, Stack<Integer> versions) {
|
||||
for (File file : dataDir.listFiles()) {
|
||||
if (file.getName().startsWith("Milestones.xml")) {
|
||||
File renamedFile = new File(dataDir, file.getName().replace("Milestones.xml", "Iterations.xml"));
|
||||
try {
|
||||
FileUtils.moveFile(file, renamedFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (File file : dataDir.listFiles()) {
|
||||
if (file.getName().startsWith("Iterations.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Element element : dom.getRootElement().elements())
|
||||
element.setName("io.onedev.server.model.Iteration");
|
||||
dom.writeToFile(file, false);
|
||||
} else if (file.getName().startsWith("Settings.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Element element : dom.getRootElement().elements()) {
|
||||
String key = element.elementTextTrim("key");
|
||||
if (key.equals("ISSUE")) {
|
||||
for (var fieldSpecElement: element.element("value").element("fieldSpecs").elements()) {
|
||||
if (fieldSpecElement.getName().contains("MilestoneChoiceField"))
|
||||
fieldSpecElement.setName("io.onedev.server.model.support.issue.field.spec.IterationChoiceField");
|
||||
}
|
||||
}
|
||||
}
|
||||
dom.writeToFile(file, false);
|
||||
} else if (file.getName().startsWith("IssueSchedules.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Element element : dom.getRootElement().elements())
|
||||
element.element("milestone").setName("iteration");
|
||||
dom.writeToFile(file, false);
|
||||
} else if (file.getName().startsWith("Dashboards.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Node node : dom.selectNodes("//io.onedev.server.ee.dashboard.widgets.MilestoneListWidget")) {
|
||||
if (node instanceof Element) {
|
||||
Element element = (Element) node;
|
||||
element.setName("io.onedev.server.ee.dashboard.widgets.IterationListWidget");
|
||||
}
|
||||
}
|
||||
for (Node node : dom.selectNodes("//io.onedev.server.ee.dashboard.widgets.BurnDownChartWidget")) {
|
||||
if (node instanceof Element) {
|
||||
Element element = (Element) node;
|
||||
var milestoneNameElement = element.element("milestoneName");
|
||||
if (milestoneNameElement != null)
|
||||
milestoneNameElement.setName("iterationName");
|
||||
}
|
||||
}
|
||||
dom.writeToFile(file, false);
|
||||
} else if (file.getName().startsWith("IssueFields.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Element element : dom.getRootElement().elements()) {
|
||||
var typeElement = element.element("type");
|
||||
if (typeElement.getTextTrim().equals("Milestone"))
|
||||
typeElement.setText("Iteration");
|
||||
}
|
||||
dom.writeToFile(file, false);
|
||||
} else if (file.getName().startsWith("IssueChanges.xml")) {
|
||||
VersionedXmlDoc dom = VersionedXmlDoc.fromFile(file);
|
||||
for (Element element : dom.getRootElement().elements()) {
|
||||
var dataElement = element.element("data");
|
||||
if (dataElement.attributeValue("class").contains("IssueMilestoneAddData"))
|
||||
dataElement.addAttribute("class", "io.onedev.server.model.support.issue.changedata.IssueIterationAddData");
|
||||
else if (dataElement.attributeValue("class").contains("IssueMilestoneRemoveData"))
|
||||
dataElement.addAttribute("class", "io.onedev.server.model.support.issue.changedata.IssueIterationRemoveData");
|
||||
else if (dataElement.attributeValue("class").contains("IssueMilestoneChangeData"))
|
||||
dataElement.addAttribute("class", "io.onedev.server.model.support.issue.changedata.IssueIterationChangeData");
|
||||
var milestoneElement = dataElement.element("milestone");
|
||||
if (milestoneElement != null)
|
||||
milestoneElement.setName("iteration");
|
||||
var oldMilestonesElement = dataElement.element("oldMilestones");
|
||||
if (oldMilestonesElement != null)
|
||||
oldMilestonesElement.setName("oldIterations");
|
||||
var newMilestonesElement = dataElement.element("newMilestones");
|
||||
if (newMilestonesElement != null)
|
||||
newMilestonesElement.setName("newIterations");
|
||||
}
|
||||
dom.writeToFile(file, false);
|
||||
}
|
||||
}
|
||||
|
||||
for (File file : dataDir.listFiles()) {
|
||||
try {
|
||||
String content = FileUtils.readFileToString(file, UTF_8);
|
||||
content = StringUtils.replace(content,
|
||||
"\"Milestone\" is", "\"Iteration\" is");
|
||||
content = StringUtils.replace(content,
|
||||
"\"Milestone\" is", "\"Iteration\" is");
|
||||
content = StringUtils.replace(content,
|
||||
"\"Milestone\" is", "\"Iteration\" is");
|
||||
content = StringUtils.replace(content,
|
||||
"\"Milestone\" is", "\"Iteration\" is");
|
||||
FileUtils.writeStringToFile(file, content, UTF_8);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ package io.onedev.server.entitymanager;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueChange;
|
||||
import io.onedev.server.model.LinkSpec;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.persistence.dao.EntityManager;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -36,19 +36,19 @@ public interface IssueChangeManager extends EntityManager<IssueChange> {
|
||||
|
||||
void changeFields(Issue issue, Map<String, Object> fieldValues);
|
||||
|
||||
void changeMilestones(Issue issue, Collection<Milestone> milestones);
|
||||
void changeIterations(Issue issue, Collection<Iteration> iterations);
|
||||
|
||||
void create(IssueChange change, @Nullable String note);
|
||||
|
||||
void addSchedule(Issue issue, Milestone milestone);
|
||||
void addSchedule(Issue issue, Iteration iteration);
|
||||
|
||||
void removeSchedule(Issue issue, Milestone milestone);
|
||||
void removeSchedule(Issue issue, Iteration iteration);
|
||||
|
||||
void changeState(Issue issue, String state, Map<String, Object> fieldValues,
|
||||
Collection<String> removeFields, @Nullable String comment);
|
||||
|
||||
void batchUpdate(Iterator<? extends Issue> issues, @Nullable String state, @Nullable Boolean confidential,
|
||||
@Nullable Collection<Milestone> milestone, Map<String, Object> fieldValues, @Nullable String comment);
|
||||
@Nullable Collection<Iteration> iterations, Map<String, Object> fieldValues, @Nullable String comment);
|
||||
|
||||
List<IssueChange> queryAfter(Long projectId, Long afterChangeId, int count);
|
||||
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
package io.onedev.server.entitymanager;
|
||||
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.dao.EntityManager;
|
||||
import io.onedev.server.search.entity.EntityQuery;
|
||||
import io.onedev.server.search.entity.EntitySort;
|
||||
import io.onedev.server.util.IssueTimes;
|
||||
import io.onedev.server.util.MilestoneAndIssueState;
|
||||
import io.onedev.server.util.IterationAndIssueState;
|
||||
import io.onedev.server.util.ProjectIssueStats;
|
||||
import io.onedev.server.util.ProjectScope;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
@ -81,13 +81,13 @@ public interface IssueManager extends EntityManager<Issue> {
|
||||
|
||||
void delete(Collection<Issue> issues, Project project);
|
||||
|
||||
Collection<MilestoneAndIssueState> queryMilestoneAndIssueStates(Project project, Collection<Milestone> milestones);
|
||||
Collection<IterationAndIssueState> queryIterationAndIssueStates(Project project, Collection<Iteration> iterations);
|
||||
|
||||
List<ProjectIssueStats> queryStats(Collection<Project> projects);
|
||||
|
||||
Collection<Milestone> queryUsedMilestones(Project project);
|
||||
Collection<Iteration> queryUsedIterations(Project project);
|
||||
|
||||
void clearSchedules(Project project, Collection<Milestone> milestones);
|
||||
void clearSchedules(Project project, Collection<Iteration> iterations);
|
||||
|
||||
List<Issue> queryAfter(Long projectId, Long afterIssueId, int count);
|
||||
|
||||
|
||||
@ -4,12 +4,12 @@ import java.util.Collection;
|
||||
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.persistence.dao.EntityManager;
|
||||
|
||||
public interface IssueScheduleManager extends EntityManager<IssueSchedule> {
|
||||
|
||||
void syncMilestones(Issue issue, Collection<Milestone> milestones);
|
||||
void syncIterations(Issue issue, Collection<Iteration> iterations);
|
||||
|
||||
void create(IssueSchedule schedule);
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package io.onedev.server.entitymanager;
|
||||
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.dao.EntityManager;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IterationManager extends EntityManager<Iteration> {
|
||||
|
||||
@Nullable
|
||||
Iteration findInHierarchy(Project project, String name);
|
||||
|
||||
void delete(Iteration iteration);
|
||||
|
||||
Iteration findInHierarchy(String iterationFQN);
|
||||
|
||||
void createOrUpdate(Iteration iteration);
|
||||
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
package io.onedev.server.entitymanager;
|
||||
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.dao.EntityManager;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface MilestoneManager extends EntityManager<Milestone> {
|
||||
|
||||
@Nullable
|
||||
Milestone findInHierarchy(Project project, String name);
|
||||
|
||||
void delete(Milestone milestone);
|
||||
|
||||
Milestone findInHierarchy(String milestoneFQN);
|
||||
|
||||
void createOrUpdate(Milestone milestone);
|
||||
|
||||
}
|
||||
@ -243,25 +243,25 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void addSchedule(Issue issue, Milestone milestone) {
|
||||
issueScheduleManager.create(issue.addSchedule(milestone));
|
||||
public void addSchedule(Issue issue, Iteration iteration) {
|
||||
issueScheduleManager.create(issue.addSchedule(iteration));
|
||||
|
||||
IssueChange change = new IssueChange();
|
||||
change.setIssue(issue);
|
||||
change.setData(new IssueMilestoneAddData(milestone.getName()));
|
||||
change.setData(new IssueIterationAddData(iteration.getName()));
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
create(change, null);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void removeSchedule(Issue issue, Milestone milestone) {
|
||||
IssueSchedule schedule = issue.removeSchedule(milestone);
|
||||
public void removeSchedule(Issue issue, Iteration iteration) {
|
||||
IssueSchedule schedule = issue.removeSchedule(iteration);
|
||||
if (schedule != null) {
|
||||
issueScheduleManager.delete(schedule);
|
||||
IssueChange change = new IssueChange();
|
||||
change.setIssue(issue);
|
||||
change.setData(new IssueMilestoneRemoveData(milestone.getName()));
|
||||
change.setData(new IssueIterationRemoveData(iteration.getName()));
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
create(change, null);
|
||||
}
|
||||
@ -307,12 +307,12 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
@Transactional
|
||||
@Override
|
||||
public void batchUpdate(Iterator<? extends Issue> issues, @Nullable String state, @Nullable Boolean confidential,
|
||||
@Nullable Collection<Milestone> milestones, Map<String, Object> fieldValues, @Nullable String comment) {
|
||||
@Nullable Collection<Iteration> iterations, Map<String, Object> fieldValues, @Nullable String comment) {
|
||||
while (issues.hasNext()) {
|
||||
Issue issue = issues.next();
|
||||
String prevState = issue.getState();
|
||||
boolean prevConfidential = issue.isConfidential();
|
||||
Collection<Milestone> prevMilestones = issue.getMilestones();
|
||||
Collection<Iteration> prevIterations = issue.getIterations();
|
||||
Map<String, Input> prevFields = issue.getFieldInputs();
|
||||
if (state != null)
|
||||
issue.setState(state);
|
||||
@ -320,8 +320,8 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
if (confidential != null)
|
||||
issue.setConfidential(confidential);
|
||||
|
||||
if (milestones != null)
|
||||
issueScheduleManager.syncMilestones(issue, milestones);
|
||||
if (iterations != null)
|
||||
issueScheduleManager.syncIterations(issue, iterations);
|
||||
|
||||
issue.setFieldValues(fieldValues);
|
||||
issueFieldManager.saveFields(issue);
|
||||
@ -329,18 +329,18 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
if (!prevState.equals(issue.getState())
|
||||
|| prevConfidential != issue.isConfidential()
|
||||
|| !prevFields.equals(issue.getFieldInputs())
|
||||
|| !new HashSet<>(prevMilestones).equals(new HashSet<>(issue.getMilestones()))) {
|
||||
|| !new HashSet<>(prevIterations).equals(new HashSet<>(issue.getIterations()))) {
|
||||
IssueChange change = new IssueChange();
|
||||
change.setIssue(issue);
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
|
||||
List<Milestone> prevMilestoneList = new ArrayList<>(prevMilestones);
|
||||
prevMilestoneList.sort(new Milestone.DatesAndStatusComparator());
|
||||
List<Milestone> currentMilestoneList = new ArrayList<>(issue.getMilestones());
|
||||
currentMilestoneList.sort(new Milestone.DatesAndStatusComparator());
|
||||
List<Iteration> prevIterationList = new ArrayList<>(prevIterations);
|
||||
prevIterationList.sort(new Iteration.DatesAndStatusComparator());
|
||||
List<Iteration> currentIterationList = new ArrayList<>(issue.getIterations());
|
||||
currentIterationList.sort(new Iteration.DatesAndStatusComparator());
|
||||
change.setData(new IssueBatchUpdateData(prevState, issue.getState(),
|
||||
prevConfidential, issue.isConfidential(), prevMilestoneList,
|
||||
currentMilestoneList, prevFields, issue.getFieldInputs()));
|
||||
prevConfidential, issue.isConfidential(), prevIterationList,
|
||||
currentIterationList, prevFields, issue.getFieldInputs()));
|
||||
create(change, comment);
|
||||
}
|
||||
}
|
||||
@ -660,19 +660,19 @@ public class DefaultIssueChangeManager extends BaseEntityManager<IssueChange>
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void changeMilestones(Issue issue, Collection<Milestone> milestones) {
|
||||
Collection<Milestone> prevMilestones = new HashSet<>(issue.getMilestones());
|
||||
if (!prevMilestones.equals(new HashSet<>(milestones))) {
|
||||
issueScheduleManager.syncMilestones(issue, milestones);
|
||||
public void changeIterations(Issue issue, Collection<Iteration> iterations) {
|
||||
Collection<Iteration> prevIterations = new HashSet<>(issue.getIterations());
|
||||
if (!prevIterations.equals(new HashSet<>(iterations))) {
|
||||
issueScheduleManager.syncIterations(issue, iterations);
|
||||
IssueChange change = new IssueChange();
|
||||
change.setIssue(issue);
|
||||
change.setUser(SecurityUtils.getUser());
|
||||
|
||||
List<Milestone> prevMilestoneList = new ArrayList<>(prevMilestones);
|
||||
prevMilestoneList.sort(new Milestone.DatesAndStatusComparator());
|
||||
List<Milestone> currentMilestoneList = new ArrayList<>(milestones);
|
||||
currentMilestoneList.sort(new Milestone.DatesAndStatusComparator());
|
||||
change.setData(new IssueMilestoneChangeData(prevMilestoneList, currentMilestoneList));
|
||||
List<Iteration> prevIterationList = new ArrayList<>(prevIterations);
|
||||
prevIterationList.sort(new Iteration.DatesAndStatusComparator());
|
||||
List<Iteration> currentIterationList = new ArrayList<>(iterations);
|
||||
currentIterationList.sort(new Iteration.DatesAndStatusComparator());
|
||||
change.setData(new IssueIterationChangeData(prevIterationList, currentIterationList));
|
||||
create(change, null);
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ 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.IssueTimes;
|
||||
import io.onedev.server.util.MilestoneAndIssueState;
|
||||
import io.onedev.server.util.IterationAndIssueState;
|
||||
import io.onedev.server.util.ProjectIssueStats;
|
||||
import io.onedev.server.util.ProjectScope;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
@ -885,33 +885,33 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public Collection<MilestoneAndIssueState> queryMilestoneAndIssueStates(Project project, Collection<Milestone> milestones) {
|
||||
public Collection<IterationAndIssueState> queryIterationAndIssueStates(Project project, Collection<Iteration> iterations) {
|
||||
CriteriaBuilder builder = getSession().getCriteriaBuilder();
|
||||
CriteriaQuery<MilestoneAndIssueState> criteriaQuery = builder.createQuery(MilestoneAndIssueState.class);
|
||||
CriteriaQuery<IterationAndIssueState> criteriaQuery = builder.createQuery(IterationAndIssueState.class);
|
||||
Root<IssueSchedule> root = criteriaQuery.from(IssueSchedule.class);
|
||||
Join<Issue, Issue> issueJoin = root.join(IssueSchedule.PROP_ISSUE, JoinType.INNER);
|
||||
criteriaQuery.multiselect(
|
||||
root.get(IssueSchedule.PROP_MILESTONE).get(Milestone.PROP_ID),
|
||||
root.get(IssueSchedule.PROP_ITERATION).get(Iteration.PROP_ID),
|
||||
issueJoin.get(Issue.PROP_STATE));
|
||||
|
||||
List<Predicate> milestonePredicates = new ArrayList<>();
|
||||
for (Milestone milestone: milestones)
|
||||
milestonePredicates.add(builder.equal(root.get(IssueSchedule.PROP_MILESTONE), milestone));
|
||||
List<Predicate> iterationPredicates = new ArrayList<>();
|
||||
for (Iteration iteration: iterations)
|
||||
iterationPredicates.add(builder.equal(root.get(IssueSchedule.PROP_ITERATION), iteration));
|
||||
|
||||
criteriaQuery.where(builder.and(
|
||||
buildSubtreePredicate(builder, issueJoin.get(Issue.PROP_PROJECT), project),
|
||||
builder.or(milestonePredicates.toArray(new Predicate[0]))));
|
||||
builder.or(iterationPredicates.toArray(new Predicate[0]))));
|
||||
|
||||
return getSession().createQuery(criteriaQuery).getResultList();
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public Collection<Milestone> queryUsedMilestones(Project project) {
|
||||
public Collection<Iteration> queryUsedIterations(Project project) {
|
||||
CriteriaBuilder builder = getSession().getCriteriaBuilder();
|
||||
CriteriaQuery<Milestone> criteriaQuery = builder.createQuery(Milestone.class);
|
||||
CriteriaQuery<Iteration> criteriaQuery = builder.createQuery(Iteration.class);
|
||||
Root<IssueSchedule> root = criteriaQuery.from(IssueSchedule.class);
|
||||
criteriaQuery.select(root.get(IssueSchedule.PROP_MILESTONE));
|
||||
criteriaQuery.select(root.get(IssueSchedule.PROP_ITERATION));
|
||||
|
||||
Path<Project> projectPath = root
|
||||
.join(IssueSchedule.PROP_ISSUE, JoinType.INNER)
|
||||
@ -1044,8 +1044,8 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
numberMapping.put(oldNumber, nextNumber);
|
||||
|
||||
for (IssueSchedule schedule: issue.getSchedules()) {
|
||||
if (schedule.getMilestone() != null
|
||||
&& !schedule.getMilestone().getProject().isSelfOrAncestorOf(targetProject)) {
|
||||
if (schedule.getIteration() != null
|
||||
&& !schedule.getIteration().getProject().isSelfOrAncestorOf(targetProject)) {
|
||||
dao.remove(schedule);
|
||||
}
|
||||
}
|
||||
@ -1086,21 +1086,21 @@ public class DefaultIssueManager extends BaseEntityManager<Issue> implements Iss
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void clearSchedules(Project project, Collection<Milestone> milestones) {
|
||||
public void clearSchedules(Project project, Collection<Iteration> iterations) {
|
||||
CriteriaBuilder builder = getSession().getCriteriaBuilder();
|
||||
CriteriaQuery<IssueSchedule> criteriaQuery = builder.createQuery(IssueSchedule.class);
|
||||
Root<IssueSchedule> root = criteriaQuery.from(IssueSchedule.class);
|
||||
|
||||
List<Predicate> milestonePredicates = new ArrayList<>();
|
||||
for (Milestone milestone: milestones)
|
||||
milestonePredicates.add(builder.equal(root.get(IssueSchedule.PROP_MILESTONE), milestone));
|
||||
List<Predicate> iterationPredicates = new ArrayList<>();
|
||||
for (Iteration iteration: iterations)
|
||||
iterationPredicates.add(builder.equal(root.get(IssueSchedule.PROP_ITERATION), iteration));
|
||||
|
||||
Path<Project> projectPath = root
|
||||
.join(IssueSchedule.PROP_ISSUE, JoinType.INNER)
|
||||
.get(Issue.PROP_PROJECT);
|
||||
criteriaQuery.where(builder.and(
|
||||
buildSubtreePredicate(builder, projectPath, project),
|
||||
builder.or(milestonePredicates.toArray(new Predicate[0]))));
|
||||
builder.or(iterationPredicates.toArray(new Predicate[0]))));
|
||||
|
||||
for (IssueSchedule schedule: getSession().createQuery(criteriaQuery).getResultList())
|
||||
dao.remove(schedule);
|
||||
|
||||
@ -10,7 +10,7 @@ import com.google.common.base.Preconditions;
|
||||
import io.onedev.server.entitymanager.IssueScheduleManager;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.persistence.dao.BaseEntityManager;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
@ -26,19 +26,19 @@ public class DefaultIssueScheduleManager extends BaseEntityManager<IssueSchedule
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void syncMilestones(Issue issue, Collection<Milestone> milestones) {
|
||||
public void syncIterations(Issue issue, Collection<Iteration> iterations) {
|
||||
for (Iterator<IssueSchedule> it = issue.getSchedules().iterator(); it.hasNext();) {
|
||||
IssueSchedule schedule = it.next();
|
||||
if (!milestones.contains(schedule.getMilestone())) {
|
||||
if (!iterations.contains(schedule.getIteration())) {
|
||||
dao.remove(schedule);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
for (Milestone milestone: milestones) {
|
||||
if (!issue.getMilestones().contains(milestone)) {
|
||||
for (Iteration iteration: iterations) {
|
||||
if (!issue.getIterations().contains(iteration)) {
|
||||
IssueSchedule schedule = new IssueSchedule();
|
||||
schedule.setIssue(issue);
|
||||
schedule.setMilestone(milestone);
|
||||
schedule.setIteration(iteration);
|
||||
issue.getSchedules().add(schedule);
|
||||
dao.persist(schedule);
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
package io.onedev.server.entitymanager.impl;
|
||||
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.entitymanager.IterationManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
@ -17,26 +17,26 @@ import javax.inject.Singleton;
|
||||
import java.util.List;
|
||||
|
||||
@Singleton
|
||||
public class DefaultMilestoneManager extends BaseEntityManager<Milestone> implements MilestoneManager {
|
||||
public class DefaultIterationManager extends BaseEntityManager<Iteration> implements IterationManager {
|
||||
|
||||
private final ProjectManager projectManager;
|
||||
|
||||
@Inject
|
||||
public DefaultMilestoneManager(Dao dao, ProjectManager projectManager) {
|
||||
public DefaultIterationManager(Dao dao, ProjectManager projectManager) {
|
||||
super(dao);
|
||||
this.projectManager = projectManager;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public Milestone findInHierarchy(String milestoneFQN) {
|
||||
String projectName = StringUtils.substringBefore(milestoneFQN, ":");
|
||||
public Iteration findInHierarchy(String iterationFQN) {
|
||||
String projectName = StringUtils.substringBefore(iterationFQN, ":");
|
||||
Project project = projectManager.findByPath(projectName);
|
||||
if (project != null) {
|
||||
String milestoneName = StringUtils.substringAfter(milestoneFQN, ":");
|
||||
EntityCriteria<Milestone> criteria = EntityCriteria.of(Milestone.class);
|
||||
String iterationName = StringUtils.substringAfter(iterationFQN, ":");
|
||||
EntityCriteria<Iteration> criteria = EntityCriteria.of(Iteration.class);
|
||||
criteria.add(Restrictions.in("project", project.getSelfAndAncestors()));
|
||||
criteria.add(Restrictions.eq("name", milestoneName));
|
||||
criteria.add(Restrictions.eq("name", iterationName));
|
||||
criteria.setCacheable(true);
|
||||
return find(criteria);
|
||||
} else {
|
||||
@ -46,8 +46,8 @@ public class DefaultMilestoneManager extends BaseEntityManager<Milestone> implem
|
||||
|
||||
@Sessional
|
||||
@Override
|
||||
public Milestone findInHierarchy(Project project, String name) {
|
||||
EntityCriteria<Milestone> criteria = EntityCriteria.of(Milestone.class);
|
||||
public Iteration findInHierarchy(Project project, String name) {
|
||||
EntityCriteria<Iteration> criteria = EntityCriteria.of(Iteration.class);
|
||||
criteria.add(Restrictions.in("project", project.getSelfAndAncestors()));
|
||||
criteria.add(Restrictions.eq("name", name));
|
||||
criteria.setCacheable(true);
|
||||
@ -55,7 +55,7 @@ public class DefaultMilestoneManager extends BaseEntityManager<Milestone> implem
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Milestone> query() {
|
||||
public List<Iteration> query() {
|
||||
return query(true);
|
||||
}
|
||||
|
||||
@ -66,8 +66,8 @@ public class DefaultMilestoneManager extends BaseEntityManager<Milestone> implem
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void createOrUpdate(Milestone milestone) {
|
||||
dao.persist(milestone);
|
||||
public void createOrUpdate(Iteration iteration) {
|
||||
dao.persist(iteration);
|
||||
}
|
||||
|
||||
}
|
||||
@ -268,14 +268,14 @@ public class DefaultProjectManager extends BaseEntityManager<Project>
|
||||
}
|
||||
dao.persist(project);
|
||||
if (oldPath != null && !oldPath.equals(project.getPath())) {
|
||||
Collection<Milestone> milestones = new ArrayList<>();
|
||||
for (Milestone milestone : issueManager.queryUsedMilestones(project)) {
|
||||
if (!project.isSelfOrAncestorOf(milestone.getProject())
|
||||
&& !milestone.getProject().isSelfOrAncestorOf(project)) {
|
||||
milestones.add(milestone);
|
||||
Collection<Iteration> iterations = new ArrayList<>();
|
||||
for (Iteration iteration : issueManager.queryUsedIterations(project)) {
|
||||
if (!project.isSelfOrAncestorOf(iteration.getProject())
|
||||
&& !iteration.getProject().isSelfOrAncestorOf(project)) {
|
||||
iterations.add(iteration);
|
||||
}
|
||||
}
|
||||
issueManager.clearSchedules(project, milestones);
|
||||
issueManager.clearSchedules(project, iterations);
|
||||
settingManager.onMoveProject(oldPath, project.getPath());
|
||||
|
||||
for (LinkSpec link : linkSpecManager.query()) {
|
||||
|
||||
@ -945,7 +945,7 @@ public class Build extends ProjectBelonging
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public boolean canCloseMilestone(@Nullable String accessTokenSecret) {
|
||||
public boolean canCloseIteration(@Nullable String accessTokenSecret) {
|
||||
var project = getProject();
|
||||
return project.isCommitOnBranch(getCommitId(), project.getDefaultBranch())
|
||||
|| accessTokenSecret != null && SecurityUtils.canManageIssues(getAccessToken(accessTokenSecret).asSubject(), project);
|
||||
|
||||
@ -29,7 +29,7 @@ import io.onedev.server.util.ProjectScopedCommit;
|
||||
import io.onedev.server.util.facade.IssueFacade;
|
||||
import io.onedev.server.web.UrlManager;
|
||||
import io.onedev.server.web.asset.emoji.Emojis;
|
||||
import io.onedev.server.web.component.milestone.burndown.BurndownIndicators;
|
||||
import io.onedev.server.web.component.iteration.burndown.BurndownIndicators;
|
||||
import io.onedev.server.web.editable.BeanDescriptor;
|
||||
import io.onedev.server.web.editable.PropertyDescriptor;
|
||||
import io.onedev.server.web.util.IssueAware;
|
||||
@ -52,7 +52,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import static io.onedev.server.model.AbstractEntity.PROP_NUMBER;
|
||||
import static io.onedev.server.model.Issue.*;
|
||||
import static io.onedev.server.model.IssueSchedule.NAME_MILESTONE;
|
||||
import static io.onedev.server.model.IssueSchedule.NAME_ITERATION;
|
||||
import static java.util.Comparator.comparing;
|
||||
import static java.util.Comparator.comparingInt;
|
||||
|
||||
@ -153,7 +153,7 @@ public class Issue extends ProjectBelonging implements AttachmentStorageSupport
|
||||
public static final Set<String> ALL_FIELDS = Sets.newHashSet(
|
||||
NAME_PROJECT, NAME_NUMBER, NAME_STATE, NAME_TITLE, NAME_SUBMITTER,
|
||||
NAME_DESCRIPTION, NAME_COMMENT, NAME_SUBMIT_DATE, NAME_LAST_ACTIVITY_DATE,
|
||||
NAME_VOTE_COUNT, NAME_COMMENT_COUNT, NAME_MILESTONE,
|
||||
NAME_VOTE_COUNT, NAME_COMMENT_COUNT, NAME_ITERATION,
|
||||
NAME_ESTIMATED_TIME, NAME_SPENT_TIME, NAME_PROGRESS,
|
||||
BurndownIndicators.ISSUE_COUNT, BurndownIndicators.REMAINING_TIME);
|
||||
|
||||
@ -161,7 +161,7 @@ public class Issue extends ProjectBelonging implements AttachmentStorageSupport
|
||||
NAME_PROJECT, NAME_NUMBER, NAME_STATE, NAME_TITLE, NAME_DESCRIPTION,
|
||||
NAME_ESTIMATED_TIME, NAME_SPENT_TIME, NAME_PROGRESS,
|
||||
NAME_COMMENT, NAME_SUBMIT_DATE, NAME_LAST_ACTIVITY_DATE, NAME_VOTE_COUNT,
|
||||
NAME_COMMENT_COUNT, NAME_MILESTONE);
|
||||
NAME_COMMENT_COUNT, NAME_ITERATION);
|
||||
|
||||
public static final Map<String, SortField<Issue>> ORDER_FIELDS = new LinkedHashMap<>();
|
||||
|
||||
@ -924,23 +924,23 @@ public class Issue extends ProjectBelonging implements AttachmentStorageSupport
|
||||
return OneDev.getInstance(UrlManager.class).urlFor(this);
|
||||
}
|
||||
|
||||
public Collection<Milestone> getMilestones() {
|
||||
return getSchedules().stream().map(it->it.getMilestone()).collect(Collectors.toList());
|
||||
public Collection<Iteration> getIterations() {
|
||||
return getSchedules().stream().map(it->it.getIteration()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public IssueSchedule addSchedule(Milestone milestone) {
|
||||
public IssueSchedule addSchedule(Iteration iteration) {
|
||||
IssueSchedule schedule = new IssueSchedule();
|
||||
schedule.setIssue(this);
|
||||
schedule.setMilestone(milestone);
|
||||
schedule.setIteration(iteration);
|
||||
getSchedules().add(schedule);
|
||||
return schedule;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IssueSchedule removeSchedule(Milestone milestone) {
|
||||
public IssueSchedule removeSchedule(Iteration iteration) {
|
||||
for (Iterator<IssueSchedule> it = getSchedules().iterator(); it.hasNext();) {
|
||||
IssueSchedule schedule = it.next();
|
||||
if (schedule.getMilestone().equals(milestone)) {
|
||||
if (schedule.getIteration().equals(iteration)) {
|
||||
it.remove();
|
||||
return schedule;
|
||||
}
|
||||
|
||||
@ -15,8 +15,8 @@ import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
|
||||
@Entity
|
||||
@Table(
|
||||
indexes={@Index(columnList="o_issue_id"), @Index(columnList="o_milestone_id")},
|
||||
uniqueConstraints={@UniqueConstraint(columnNames={"o_issue_id", "o_milestone_id"})
|
||||
indexes={@Index(columnList="o_issue_id"), @Index(columnList="o_iteration_id")},
|
||||
uniqueConstraints={@UniqueConstraint(columnNames={"o_issue_id", "o_iteration_id"})
|
||||
})
|
||||
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
|
||||
public class IssueSchedule extends AbstractEntity {
|
||||
@ -25,9 +25,9 @@ public class IssueSchedule extends AbstractEntity {
|
||||
|
||||
public static String PROP_ISSUE = "issue";
|
||||
|
||||
public static String NAME_MILESTONE = "Milestone";
|
||||
public static String NAME_ITERATION = "Iteration";
|
||||
|
||||
public static String PROP_MILESTONE = "milestone";
|
||||
public static String PROP_ITERATION = "iteration";
|
||||
|
||||
public static String PROP_DATE = "date";
|
||||
|
||||
@ -37,7 +37,7 @@ public class IssueSchedule extends AbstractEntity {
|
||||
|
||||
@ManyToOne(fetch=FetchType.LAZY)
|
||||
@JoinColumn(nullable=false)
|
||||
private Milestone milestone;
|
||||
private Iteration iteration;
|
||||
|
||||
private Date date = new Date();
|
||||
|
||||
@ -49,12 +49,12 @@ public class IssueSchedule extends AbstractEntity {
|
||||
this.issue = issue;
|
||||
}
|
||||
|
||||
public Milestone getMilestone() {
|
||||
return milestone;
|
||||
public Iteration getIteration() {
|
||||
return iteration;
|
||||
}
|
||||
|
||||
public void setMilestone(Milestone milestone) {
|
||||
this.milestone = milestone;
|
||||
public void setIteration(Iteration iteration) {
|
||||
this.iteration = iteration;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
|
||||
@ -12,10 +12,10 @@ import java.util.*;
|
||||
@Entity
|
||||
@Table(
|
||||
indexes={@Index(columnList="o_project_id")},
|
||||
uniqueConstraints={@UniqueConstraint(columnNames={"o_project_id", Milestone.PROP_NAME})}
|
||||
uniqueConstraints={@UniqueConstraint(columnNames={"o_project_id", Iteration.PROP_NAME})}
|
||||
)
|
||||
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
|
||||
public class Milestone extends AbstractEntity {
|
||||
public class Iteration extends AbstractEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -48,7 +48,7 @@ public class Milestone extends AbstractEntity {
|
||||
|
||||
private boolean closed;
|
||||
|
||||
@OneToMany(mappedBy="milestone", cascade=CascadeType.REMOVE)
|
||||
@OneToMany(mappedBy= "iteration", cascade=CascadeType.REMOVE)
|
||||
private Collection<IssueSchedule> schedules = new ArrayList<>();
|
||||
|
||||
public Project getProject() {
|
||||
@ -131,7 +131,7 @@ public class Milestone extends AbstractEntity {
|
||||
public static class DatesAndStatusComparator extends DatesComparator {
|
||||
|
||||
@Override
|
||||
public int compare(Milestone o1, Milestone o2) {
|
||||
public int compare(Iteration o1, Iteration o2) {
|
||||
if (o1.isClosed()) {
|
||||
if (o2.isClosed())
|
||||
return super.compare(o1, o2) * -1;
|
||||
@ -146,10 +146,10 @@ public class Milestone extends AbstractEntity {
|
||||
|
||||
}
|
||||
|
||||
public static class DatesComparator implements Comparator<Milestone> {
|
||||
public static class DatesComparator implements Comparator<Iteration> {
|
||||
|
||||
@Override
|
||||
public int compare(Milestone o1, Milestone o2) {
|
||||
public int compare(Iteration o1, Iteration o2) {
|
||||
if (o1.getStartDate() != null) {
|
||||
if (o2.getStartDate() != null)
|
||||
return o1.getStartDate().compareTo(o2.getStartDate());
|
||||
@ -326,7 +326,7 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
|
||||
@OneToMany(mappedBy="project", cascade=CascadeType.REMOVE)
|
||||
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
|
||||
private Collection<Milestone> milestones = new ArrayList<>();
|
||||
private Collection<Iteration> iterations = new ArrayList<>();
|
||||
|
||||
private boolean codeManagement = true;
|
||||
|
||||
@ -411,7 +411,7 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
|
||||
private transient Optional<CommitQueryPersonalization> commitQueryPersonalizationOfCurrentUserHolder;
|
||||
|
||||
private transient List<Milestone> sortedHierarchyMilestones;
|
||||
private transient List<Iteration> sortedHierarchyIterations;
|
||||
|
||||
private transient Optional<String> activeServer;
|
||||
|
||||
@ -1283,27 +1283,27 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
return getGitService().getMode(this, getObjectId(revision, true), path);
|
||||
}
|
||||
|
||||
public Collection<Milestone> getMilestones() {
|
||||
return milestones;
|
||||
public Collection<Iteration> getIterations() {
|
||||
return iterations;
|
||||
}
|
||||
|
||||
public Collection<Milestone> getHierarchyMilestones() {
|
||||
Collection<Milestone> milestones = new ArrayList<>(getMilestones());
|
||||
public Collection<Iteration> getHierarchyIterations() {
|
||||
Collection<Iteration> iterations = new ArrayList<>(getIterations());
|
||||
if (getParent() != null)
|
||||
milestones.addAll(getParent().getHierarchyMilestones());
|
||||
return milestones;
|
||||
iterations.addAll(getParent().getHierarchyIterations());
|
||||
return iterations;
|
||||
}
|
||||
|
||||
public void setMilestones(Collection<Milestone> milestones) {
|
||||
this.milestones = milestones;
|
||||
public void setIterations(Collection<Iteration> iterations) {
|
||||
this.iterations = iterations;
|
||||
}
|
||||
|
||||
public List<Milestone> getSortedHierarchyMilestones() {
|
||||
if (sortedHierarchyMilestones == null) {
|
||||
sortedHierarchyMilestones = new ArrayList<>(getHierarchyMilestones());
|
||||
sortedHierarchyMilestones.sort(new Milestone.DatesAndStatusComparator());
|
||||
public List<Iteration> getSortedHierarchyIterations() {
|
||||
if (sortedHierarchyIterations == null) {
|
||||
sortedHierarchyIterations = new ArrayList<>(getHierarchyIterations());
|
||||
sortedHierarchyIterations.sort(new Iteration.DatesAndStatusComparator());
|
||||
}
|
||||
return sortedHierarchyMilestones;
|
||||
return sortedHierarchyIterations;
|
||||
}
|
||||
|
||||
@Editable
|
||||
@ -1494,19 +1494,19 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Milestone getHierarchyMilestone(@Nullable String milestoneName) {
|
||||
for (Milestone milestone: getHierarchyMilestones()) {
|
||||
if (milestone.getName().equals(milestoneName))
|
||||
return milestone;
|
||||
public Iteration getHierarchyIteration(@Nullable String iterationName) {
|
||||
for (Iteration iteration: getHierarchyIterations()) {
|
||||
if (iteration.getName().equals(iterationName))
|
||||
return iteration;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Milestone getMilestone(String milestoneName) {
|
||||
for (Milestone milestone: getMilestones()) {
|
||||
if (milestone.getName().equals(milestoneName))
|
||||
return milestone;
|
||||
public Iteration getIteration(String iterationName) {
|
||||
for (Iteration iteration: getIterations()) {
|
||||
if (iteration.getName().equals(iterationName))
|
||||
return iteration;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -203,7 +203,7 @@ public class Role extends AbstractEntity implements BasePermission {
|
||||
this.accessConfidentialIssues = accessConfidentialIssues;
|
||||
}
|
||||
|
||||
@Editable(order=500, description = "This permission enables one to schedule issues into milestones")
|
||||
@Editable(order=500, description = "This permission enables one to schedule issues into iterations")
|
||||
@ShowCondition("isManageIssuesDisabled")
|
||||
public boolean isScheduleIssues() {
|
||||
return scheduleIssues;
|
||||
|
||||
@ -212,7 +212,7 @@ public class GlobalIssueSetting implements Serializable {
|
||||
board.setName(Issue.NAME_STATE);
|
||||
board.setIdentifyField(Issue.NAME_STATE);
|
||||
board.setColumns(Lists.newArrayList("Open", "Closed"));
|
||||
board.setDisplayFields(Lists.newArrayList(Issue.NAME_STATE, "Type", "Priority", "Assignees", IssueSchedule.NAME_MILESTONE));
|
||||
board.setDisplayFields(Lists.newArrayList(Issue.NAME_STATE, "Type", "Priority", "Assignees", IssueSchedule.NAME_ITERATION));
|
||||
board.setDisplayLinks(Lists.newArrayList("Child Issue", "Blocked By"));
|
||||
boardSpecs.add(board);
|
||||
|
||||
@ -220,7 +220,7 @@ public class GlobalIssueSetting implements Serializable {
|
||||
listFields.add("Type");
|
||||
listFields.add("Priority");
|
||||
listFields.add("Assignees");
|
||||
listFields.add(IssueSchedule.NAME_MILESTONE);
|
||||
listFields.add(IssueSchedule.NAME_ITERATION);
|
||||
|
||||
listLinks.add("Child Issue");
|
||||
listLinks.add("Blocked By");
|
||||
@ -236,7 +236,7 @@ public class GlobalIssueSetting implements Serializable {
|
||||
namedQueries.add(new NamedIssueQuery("Has activity recently", "\"Last Activity Date\" is since \"last week\""));
|
||||
namedQueries.add(new NamedIssueQuery("Open & Critical", "\"State\" is \"Open\" and \"Priority\" is \"Critical\""));
|
||||
namedQueries.add(new NamedIssueQuery("Open & Unassigned", "\"State\" is \"Open\" and \"Assignees\" is empty"));
|
||||
namedQueries.add(new NamedIssueQuery("Open & Unscheduled", "\"State\" is \"Open\" and \"Milestone\" is empty"));
|
||||
namedQueries.add(new NamedIssueQuery("Open & Unscheduled", "\"State\" is \"Open\" and \"Iteration\" is empty"));
|
||||
namedQueries.add(new NamedIssueQuery("Closed", "\"State\" is \"Closed\""));
|
||||
namedQueries.add(new NamedIssueQuery("All", null));
|
||||
|
||||
@ -354,7 +354,7 @@ public class GlobalIssueSetting implements Serializable {
|
||||
Collection<String> undefinedFields = new HashSet<>();
|
||||
for (String fieldName: getListFields()) {
|
||||
if (!fieldName.equals(Issue.NAME_STATE)
|
||||
&& !fieldName.equals(IssueSchedule.NAME_MILESTONE)
|
||||
&& !fieldName.equals(IssueSchedule.NAME_ITERATION)
|
||||
&& getFieldSpec(fieldName) == null) {
|
||||
undefinedFields.add(fieldName);
|
||||
}
|
||||
|
||||
@ -61,9 +61,9 @@ public class BoardSpec implements Serializable {
|
||||
|
||||
private List<String> columns = new ArrayList<>();
|
||||
|
||||
private String milestonePrefix;
|
||||
private String iterationPrefix;
|
||||
|
||||
private List<String> displayFields = Lists.newArrayList(Issue.NAME_STATE, IssueSchedule.NAME_MILESTONE);
|
||||
private List<String> displayFields = Lists.newArrayList(Issue.NAME_STATE, IssueSchedule.NAME_ITERATION);
|
||||
|
||||
private List<String> displayLinks = new ArrayList<>();
|
||||
|
||||
@ -92,7 +92,7 @@ public class BoardSpec implements Serializable {
|
||||
}
|
||||
|
||||
@Editable(order=250, placeholder="Not specified", description="Optionally specify a base query to filter/order issues in backlog. "
|
||||
+ "Backlog issues are those not associating with current milestone")
|
||||
+ "Backlog issues are those not associating with current iteration")
|
||||
@IssueQuery(withCurrentUserCriteria = true, withCurrentProjectCriteria = true, withOrder = false)
|
||||
@Nullable
|
||||
public String getBacklogBaseQuery() {
|
||||
@ -152,15 +152,13 @@ public class BoardSpec implements Serializable {
|
||||
return displayColumns;
|
||||
}
|
||||
|
||||
@Editable(order=450, description = "" +
|
||||
"If specified, OneDev will only display milestones with this prefix, and the prefix will be stripped " +
|
||||
"for brevity. Also milestones created from this board will get this prefix automatically")
|
||||
public String getMilestonePrefix() {
|
||||
return milestonePrefix;
|
||||
@Editable(order=450, description = "If specified, OneDev will only display iterations with this prefix")
|
||||
public String getIterationPrefix() {
|
||||
return iterationPrefix;
|
||||
}
|
||||
|
||||
public void setMilestonePrefix(String milestonePrefix) {
|
||||
this.milestonePrefix = milestonePrefix;
|
||||
public void setIterationPrefix(String iterationPrefix) {
|
||||
this.iterationPrefix = iterationPrefix;
|
||||
}
|
||||
|
||||
@Editable(order=500, placeholder="Not displaying any fields", description="Specify fields to display in board card")
|
||||
@ -213,7 +211,7 @@ public class BoardSpec implements Serializable {
|
||||
for (FieldSpec fieldSpec: getIssueSetting().getFieldSpecs()) {
|
||||
choices.add(fieldSpec.getName());
|
||||
}
|
||||
choices.add(IssueSchedule.NAME_MILESTONE);
|
||||
choices.add(IssueSchedule.NAME_ITERATION);
|
||||
return choices;
|
||||
}
|
||||
|
||||
@ -282,7 +280,7 @@ public class BoardSpec implements Serializable {
|
||||
undefinedFields.add(getIdentifyField());
|
||||
}
|
||||
for (String displayField: getDisplayFields()) {
|
||||
if (!Issue.NAME_STATE.equals(displayField) && !IssueSchedule.NAME_MILESTONE.equals(displayField)) {
|
||||
if (!Issue.NAME_STATE.equals(displayField) && !IssueSchedule.NAME_ITERATION.equals(displayField)) {
|
||||
FieldSpec fieldSpec = getIssueSetting().getFieldSpec(displayField);
|
||||
if (fieldSpec == null)
|
||||
undefinedFields.add(displayField);
|
||||
|
||||
@ -150,7 +150,7 @@ public class ProjectIssueSetting implements Serializable {
|
||||
if (listFields != null) {
|
||||
for (String fieldName: listFields) {
|
||||
if (!fieldName.equals(Issue.NAME_STATE)
|
||||
&& !fieldName.equals(IssueSchedule.NAME_MILESTONE)
|
||||
&& !fieldName.equals(IssueSchedule.NAME_ITERATION)
|
||||
&& getGlobalSetting().getFieldSpec(fieldName) == null) {
|
||||
undefinedFields.add(fieldName);
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.util.Input;
|
||||
|
||||
public class IssueBatchUpdateData extends IssueFieldChangeData {
|
||||
@ -21,21 +21,21 @@ public class IssueBatchUpdateData extends IssueFieldChangeData {
|
||||
|
||||
private final boolean newConfidential;
|
||||
|
||||
private final List<String> oldMilestones;
|
||||
private final List<String> oldIterations;
|
||||
|
||||
private final List<String> newMilestones;
|
||||
private final List<String> newIterations;
|
||||
|
||||
public IssueBatchUpdateData(String oldState, String newState,
|
||||
boolean oldConfidential, boolean newConfidential,
|
||||
List<Milestone> oldMilestones, List<Milestone> newMilestones,
|
||||
Map<String, Input> oldFields, Map<String, Input> newFields) {
|
||||
public IssueBatchUpdateData(String oldState, String newState,
|
||||
boolean oldConfidential, boolean newConfidential,
|
||||
List<Iteration> oldIterations, List<Iteration> newIterations,
|
||||
Map<String, Input> oldFields, Map<String, Input> newFields) {
|
||||
super(oldFields, newFields);
|
||||
this.oldState = oldState;
|
||||
this.newState = newState;
|
||||
this.oldConfidential = oldConfidential;
|
||||
this.newConfidential = newConfidential;
|
||||
this.oldMilestones = oldMilestones.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
this.newMilestones = newMilestones.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
this.oldIterations = oldIterations.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
this.newIterations = newIterations.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -43,8 +43,8 @@ public class IssueBatchUpdateData extends IssueFieldChangeData {
|
||||
Map<String, String> oldFieldValues = new LinkedHashMap<>();
|
||||
oldFieldValues.put("State", oldState);
|
||||
oldFieldValues.put("Confidential", String.valueOf(oldConfidential));
|
||||
if (!oldMilestones.isEmpty())
|
||||
oldFieldValues.put("Milestones", StringUtils.join(oldMilestones));
|
||||
if (!oldIterations.isEmpty())
|
||||
oldFieldValues.put("Iterations", StringUtils.join(oldIterations));
|
||||
oldFieldValues.putAll(super.getOldFieldValues());
|
||||
return oldFieldValues;
|
||||
}
|
||||
@ -54,8 +54,8 @@ public class IssueBatchUpdateData extends IssueFieldChangeData {
|
||||
Map<String, String> newFieldValues = new LinkedHashMap<>();
|
||||
newFieldValues.put("State", newState);
|
||||
newFieldValues.put("Confidential", String.valueOf(newConfidential));
|
||||
if (!newMilestones.isEmpty())
|
||||
newFieldValues.put("Milestones", StringUtils.join(newMilestones));
|
||||
if (!newIterations.isEmpty())
|
||||
newFieldValues.put("Iterations", StringUtils.join(newIterations));
|
||||
newFieldValues.putAll(super.getNewFieldValues());
|
||||
return newFieldValues;
|
||||
}
|
||||
@ -76,12 +76,12 @@ public class IssueBatchUpdateData extends IssueFieldChangeData {
|
||||
return newConfidential;
|
||||
}
|
||||
|
||||
public List<String> getOldMilestones() {
|
||||
return oldMilestones;
|
||||
public List<String> getOldIterations() {
|
||||
return oldIterations;
|
||||
}
|
||||
|
||||
public List<String> getNewMilestones() {
|
||||
return newMilestones;
|
||||
public List<String> getNewIterations() {
|
||||
return newIterations;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -7,19 +7,19 @@ import java.util.Map;
|
||||
import io.onedev.server.model.Group;
|
||||
import io.onedev.server.model.User;
|
||||
|
||||
public class IssueMilestoneAddData extends IssueChangeData {
|
||||
public class IssueIterationAddData extends IssueChangeData {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String milestone;
|
||||
private final String iteration;
|
||||
|
||||
public IssueMilestoneAddData(String milestone) {
|
||||
this.milestone = milestone;
|
||||
public IssueIterationAddData(String iteration) {
|
||||
this.iteration = iteration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "added to milestone \"" + milestone + "\"";
|
||||
return "added to iteration \"" + iteration + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -8,34 +8,34 @@ import java.util.stream.Collectors;
|
||||
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.server.model.Group;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.notification.ActivityDetail;
|
||||
|
||||
public class IssueMilestoneChangeData extends IssueChangeData {
|
||||
public class IssueIterationChangeData extends IssueChangeData {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final List<String> oldMilestones;
|
||||
private final List<String> oldIterations;
|
||||
|
||||
private final List<String> newMilestones;
|
||||
private final List<String> newIterations;
|
||||
|
||||
public IssueMilestoneChangeData(List<Milestone> oldMilestones, List<Milestone> newMilestones) {
|
||||
this.oldMilestones = oldMilestones.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
this.newMilestones = newMilestones.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
public IssueIterationChangeData(List<Iteration> oldIterations, List<Iteration> newIterations) {
|
||||
this.oldIterations = oldIterations.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
this.newIterations = newIterations.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<String> getOldMilestones() {
|
||||
return oldMilestones;
|
||||
public List<String> getOldIterations() {
|
||||
return oldIterations;
|
||||
}
|
||||
|
||||
public List<String> getNewMilestones() {
|
||||
return newMilestones;
|
||||
public List<String> getNewIterations() {
|
||||
return newIterations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "changed milestones";
|
||||
return "changed iterations";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -56,9 +56,9 @@ public class IssueMilestoneChangeData extends IssueChangeData {
|
||||
@Override
|
||||
public ActivityDetail getActivityDetail() {
|
||||
Map<String, String> oldFieldValues = new HashMap<>();
|
||||
oldFieldValues.put("Milestones", StringUtils.join(oldMilestones));
|
||||
oldFieldValues.put("Iterations", StringUtils.join(oldIterations));
|
||||
Map<String, String> newFieldValues = new HashMap<>();
|
||||
newFieldValues.put("Milestones", StringUtils.join(newMilestones));
|
||||
newFieldValues.put("Iterations", StringUtils.join(newIterations));
|
||||
return ActivityDetail.compare(oldFieldValues, newFieldValues, true);
|
||||
}
|
||||
|
||||
@ -7,19 +7,19 @@ import java.util.Map;
|
||||
import io.onedev.server.model.Group;
|
||||
import io.onedev.server.model.User;
|
||||
|
||||
public class IssueMilestoneRemoveData extends IssueChangeData {
|
||||
public class IssueIterationRemoveData extends IssueChangeData {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String milestone;
|
||||
private final String iteration;
|
||||
|
||||
public IssueMilestoneRemoveData(String milestone) {
|
||||
this.milestone = milestone;
|
||||
public IssueIterationRemoveData(String iteration) {
|
||||
this.iteration = iteration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivity() {
|
||||
return "removed from milestone \"" + milestone + "\"";
|
||||
return "removed from iteration \"" + iteration + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -3,33 +3,33 @@ package io.onedev.server.model.support.issue.field.spec;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.onedev.server.buildspecmodel.inputspec.MilestoneChoiceInput;
|
||||
import io.onedev.server.buildspecmodel.inputspec.IterationChoiceInput;
|
||||
import io.onedev.server.annotation.Editable;
|
||||
|
||||
@Editable(order=1110, name=FieldSpec.MILESTONE)
|
||||
public class MilestoneChoiceField extends FieldSpec {
|
||||
@Editable(order=1110, name=FieldSpec.ITERATION)
|
||||
public class IterationChoiceField extends FieldSpec {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public String getPropertyDef(Map<String, Integer> indexes) {
|
||||
return MilestoneChoiceInput.getPropertyDef(this, indexes);
|
||||
return IterationChoiceInput.getPropertyDef(this, indexes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertToObject(List<String> strings) {
|
||||
return MilestoneChoiceInput.convertToObject(this, strings);
|
||||
return IterationChoiceInput.convertToObject(this, strings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> convertToStrings(Object value) {
|
||||
return MilestoneChoiceInput.convertToStrings(this, value);
|
||||
return IterationChoiceInput.convertToStrings(this, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getOrdinal(String fieldValue) {
|
||||
if (fieldValue != null)
|
||||
return MilestoneChoiceInput.getOrdinal(fieldValue);
|
||||
return IterationChoiceInput.getOrdinal(fieldValue);
|
||||
else
|
||||
return super.getOrdinal(fieldValue);
|
||||
}
|
||||
@ -36,8 +36,8 @@ public abstract class BaseEntityManager<T extends AbstractEntity> implements Ent
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(T entity) {
|
||||
dao.remove(entity);
|
||||
public void delete(T iteration) {
|
||||
dao.remove(iteration);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -44,7 +44,7 @@ public class IssueResource {
|
||||
|
||||
private final IssueChangeManager issueChangeManager;
|
||||
|
||||
private final MilestoneManager milestoneManager;
|
||||
private final IterationManager iterationManager;
|
||||
|
||||
private final ProjectManager projectManager;
|
||||
|
||||
@ -52,12 +52,12 @@ public class IssueResource {
|
||||
|
||||
@Inject
|
||||
public IssueResource(SettingManager settingManager, IssueManager issueManager,
|
||||
IssueChangeManager issueChangeManager, MilestoneManager milestoneManager,
|
||||
IssueChangeManager issueChangeManager, IterationManager iterationManager,
|
||||
ProjectManager projectManager, ObjectMapper objectMapper) {
|
||||
this.settingManager = settingManager;
|
||||
this.issueManager = issueManager;
|
||||
this.issueChangeManager = issueChangeManager;
|
||||
this.milestoneManager = milestoneManager;
|
||||
this.iterationManager = iterationManager;
|
||||
this.projectManager = projectManager;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
@ -123,13 +123,13 @@ public class IssueResource {
|
||||
}
|
||||
|
||||
@Api(order=450)
|
||||
@Path("/{issueId}/milestones")
|
||||
@Path("/{issueId}/iterations")
|
||||
@GET
|
||||
public Collection<Milestone> getMilestones(@PathParam("issueId") Long issueId) {
|
||||
public Collection<Iteration> getIterations(@PathParam("issueId") Long issueId) {
|
||||
Issue issue = issueManager.load(issueId);
|
||||
if (!SecurityUtils.canAccessIssue(issue))
|
||||
throw new UnauthorizedException();
|
||||
return issue.getMilestones();
|
||||
return issue.getIterations();
|
||||
}
|
||||
|
||||
@Api(order=500)
|
||||
@ -237,7 +237,7 @@ public class IssueResource {
|
||||
if (!SecurityUtils.canAccessProject(project))
|
||||
throw new UnauthorizedException();
|
||||
|
||||
if (!data.getMilestoneIds().isEmpty() && !SecurityUtils.canScheduleIssues(project))
|
||||
if (!data.getIterationIds().isEmpty() && !SecurityUtils.canScheduleIssues(project))
|
||||
throw new UnauthorizedException("No permission to schedule issue");
|
||||
|
||||
var issueSetting = settingManager.getIssueSetting();
|
||||
@ -252,13 +252,13 @@ public class IssueResource {
|
||||
issue.setState(issueSetting.getInitialStateSpec().getName());
|
||||
issue.setOwnEstimatedTime(data.getOwnEstimatedTime());
|
||||
|
||||
for (Long milestoneId : data.getMilestoneIds()) {
|
||||
Milestone milestone = milestoneManager.load(milestoneId);
|
||||
if (!milestone.getProject().isSelfOrAncestorOf(project))
|
||||
throw new BadRequestException("Milestone is not defined in project hierarchy of the issue");
|
||||
for (Long iterationId : data.getIterationIds()) {
|
||||
Iteration iteration = iterationManager.load(iterationId);
|
||||
if (!iteration.getProject().isSelfOrAncestorOf(project))
|
||||
throw new BadRequestException("Iteration is not defined in project hierarchy of the issue");
|
||||
IssueSchedule schedule = new IssueSchedule();
|
||||
schedule.setIssue(issue);
|
||||
schedule.setMilestone(milestone);
|
||||
schedule.setIteration(iteration);
|
||||
issue.getSchedules().add(schedule);
|
||||
}
|
||||
|
||||
@ -311,23 +311,23 @@ public class IssueResource {
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
@Api(order=1300, description="Schedule issue into specified milestones with list of milestone id")
|
||||
@Path("/{issueId}/milestones")
|
||||
@Api(order=1300, description="Schedule issue into specified iterations with list of iteration id")
|
||||
@Path("/{issueId}/iterations")
|
||||
@POST
|
||||
public Response setMilestones(@PathParam("issueId") Long issueId, List<Long> milestoneIds) {
|
||||
public Response setIterations(@PathParam("issueId") Long issueId, List<Long> iterationIds) {
|
||||
Issue issue = issueManager.load(issueId);
|
||||
if (!SecurityUtils.canScheduleIssues(issue.getProject()))
|
||||
throw new UnauthorizedException("No permission to schedule issue");
|
||||
|
||||
Collection<Milestone> milestones = new HashSet<>();
|
||||
for (Long milestoneId: milestoneIds) {
|
||||
Milestone milestone = milestoneManager.load(milestoneId);
|
||||
if (!milestone.getProject().isSelfOrAncestorOf(issue.getProject()))
|
||||
throw new InvalidParamException("Milestone is not defined in project hierarchy of the issue");
|
||||
milestones.add(milestone);
|
||||
Collection<Iteration> iterations = new HashSet<>();
|
||||
for (Long iterationId: iterationIds) {
|
||||
Iteration iteration = iterationManager.load(iterationId);
|
||||
if (!iteration.getProject().isSelfOrAncestorOf(issue.getProject()))
|
||||
throw new InvalidParamException("Iteration is not defined in project hierarchy of the issue");
|
||||
iterations.add(iteration);
|
||||
}
|
||||
|
||||
issueChangeManager.changeMilestones(issue, milestones);
|
||||
issueChangeManager.changeIterations(issue, iterations);
|
||||
|
||||
return Response.ok().build();
|
||||
}
|
||||
@ -434,7 +434,7 @@ public class IssueResource {
|
||||
private int ownEstimatedTime;
|
||||
|
||||
@Api(order=500)
|
||||
private List<Long> milestoneIds = new ArrayList<>();
|
||||
private List<Long> iterationIds = new ArrayList<>();
|
||||
|
||||
@Api(order=600, exampleProvider = "getFieldsExample")
|
||||
private Map<String, Serializable> fields = new HashMap<>();
|
||||
@ -481,12 +481,12 @@ public class IssueResource {
|
||||
this.ownEstimatedTime = ownEstimatedTime;
|
||||
}
|
||||
|
||||
public List<Long> getMilestoneIds() {
|
||||
return milestoneIds;
|
||||
public List<Long> getIterationIds() {
|
||||
return iterationIds;
|
||||
}
|
||||
|
||||
public void setMilestoneIds(List<Long> milestoneIds) {
|
||||
this.milestoneIds = milestoneIds;
|
||||
public void setIterationIds(List<Long> iterationIds) {
|
||||
this.iterationIds = iterationIds;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
package io.onedev.server.rest.resource;
|
||||
|
||||
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.IterationManager;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.rest.annotation.Api;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
@Api(order=2500)
|
||||
@Path("/iterations")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Singleton
|
||||
public class IterationResource {
|
||||
|
||||
private final IterationManager iterationManager;
|
||||
|
||||
@Inject
|
||||
public IterationResource(IterationManager iterationManager) {
|
||||
this.iterationManager = iterationManager;
|
||||
}
|
||||
|
||||
@Api(order=100)
|
||||
@Path("/{iterationId}")
|
||||
@GET
|
||||
public Iteration get(@PathParam("iterationId") Long iterationId) {
|
||||
Iteration iteration = iterationManager.load(iterationId);
|
||||
if (!SecurityUtils.canAccessProject(iteration.getProject()))
|
||||
throw new UnauthorizedException();
|
||||
return iteration;
|
||||
}
|
||||
|
||||
@Api(order=200, description="Create new iteration")
|
||||
@POST
|
||||
public Long create(@NotNull Iteration iteration) {
|
||||
if (!SecurityUtils.canManageIssues(iteration.getProject()))
|
||||
throw new UnauthorizedException();
|
||||
iterationManager.createOrUpdate(iteration);
|
||||
return iteration.getId();
|
||||
}
|
||||
|
||||
@Api(order=250, description="Update iteration of specified id")
|
||||
@Path("/{iterationId}")
|
||||
@POST
|
||||
public Long update(@PathParam("iterationId") Long iterationId, @NotNull Iteration iteration) {
|
||||
if (!SecurityUtils.canManageIssues(iteration.getProject()))
|
||||
throw new UnauthorizedException();
|
||||
iterationManager.createOrUpdate(iteration);
|
||||
return iteration.getId();
|
||||
}
|
||||
|
||||
@Api(order=300)
|
||||
@Path("/{iterationId}")
|
||||
@DELETE
|
||||
public Response delete(@PathParam("iterationId") Long iterationId) {
|
||||
Iteration iteration = iterationManager.load(iterationId);
|
||||
if (!SecurityUtils.canManageIssues(iteration.getProject()))
|
||||
throw new UnauthorizedException();
|
||||
iterationManager.delete(iteration);
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
package io.onedev.server.rest.resource;
|
||||
|
||||
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.MilestoneManager;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.rest.annotation.Api;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
|
||||
@Api(order=2500)
|
||||
@Path("/milestones")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Singleton
|
||||
public class MilestoneResource {
|
||||
|
||||
private final MilestoneManager milestoneManager;
|
||||
|
||||
@Inject
|
||||
public MilestoneResource(MilestoneManager milestoneManager) {
|
||||
this.milestoneManager = milestoneManager;
|
||||
}
|
||||
|
||||
@Api(order=100)
|
||||
@Path("/{milestoneId}")
|
||||
@GET
|
||||
public Milestone get(@PathParam("milestoneId") Long milestoneId) {
|
||||
Milestone milestone = milestoneManager.load(milestoneId);
|
||||
if (!SecurityUtils.canAccessProject(milestone.getProject()))
|
||||
throw new UnauthorizedException();
|
||||
return milestone;
|
||||
}
|
||||
|
||||
@Api(order=200, description="Create new milestone")
|
||||
@POST
|
||||
public Long create(@NotNull Milestone milestone) {
|
||||
if (!SecurityUtils.canManageIssues(milestone.getProject()))
|
||||
throw new UnauthorizedException();
|
||||
milestoneManager.createOrUpdate(milestone);
|
||||
return milestone.getId();
|
||||
}
|
||||
|
||||
@Api(order=250, description="Update milestone of specified id")
|
||||
@Path("/{milestoneId}")
|
||||
@POST
|
||||
public Long update(@PathParam("milestoneId") Long milestoneId, @NotNull Milestone milestone) {
|
||||
if (!SecurityUtils.canManageIssues(milestone.getProject()))
|
||||
throw new UnauthorizedException();
|
||||
milestoneManager.createOrUpdate(milestone);
|
||||
return milestone.getId();
|
||||
}
|
||||
|
||||
@Api(order=300)
|
||||
@Path("/{milestoneId}")
|
||||
@DELETE
|
||||
public Response delete(@PathParam("milestoneId") Long milestoneId) {
|
||||
Milestone milestone = milestoneManager.load(milestoneId);
|
||||
if (!SecurityUtils.canManageIssues(milestone.getProject()))
|
||||
throw new UnauthorizedException();
|
||||
milestoneManager.delete(milestone);
|
||||
return Response.ok().build();
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,7 +2,7 @@ package io.onedev.server.rest.resource;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.entitymanager.IterationManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.model.support.pack.ProjectPackSetting;
|
||||
import io.onedev.server.web.UrlManager;
|
||||
@ -53,17 +53,17 @@ public class ProjectResource {
|
||||
|
||||
private final ProjectManager projectManager;
|
||||
|
||||
private final MilestoneManager milestoneManager;
|
||||
private final IterationManager iterationManager;
|
||||
|
||||
private final CommitInfoManager commitInfoManager;
|
||||
|
||||
private final UrlManager urlManager;
|
||||
|
||||
@Inject
|
||||
public ProjectResource(ProjectManager projectManager, MilestoneManager milestoneManager,
|
||||
public ProjectResource(ProjectManager projectManager, IterationManager iterationManager,
|
||||
CommitInfoManager commitInfoManager, UrlManager urlManager) {
|
||||
this.projectManager = projectManager;
|
||||
this.milestoneManager = milestoneManager;
|
||||
this.iterationManager = iterationManager;
|
||||
this.commitInfoManager = commitInfoManager;
|
||||
this.urlManager = urlManager;
|
||||
}
|
||||
@ -186,15 +186,15 @@ public class ProjectResource {
|
||||
}
|
||||
|
||||
@Api(order=750)
|
||||
@Path("/{projectId}/milestones")
|
||||
@Path("/{projectId}/iterations")
|
||||
@GET
|
||||
public List<Milestone> queryMilestones(@PathParam("projectId") Long projectId, @QueryParam("name") String name,
|
||||
@QueryParam("startBefore") @Api(exampleProvider="getDateExample", description="ISO 8601 date") String startBefore,
|
||||
@QueryParam("startAfter") @Api(exampleProvider="getDateExample", description="ISO 8601 date") String startAfter,
|
||||
@QueryParam("dueBefore") @Api(exampleProvider="getDateExample", description="ISO 8601 date") String dueBefore,
|
||||
@QueryParam("dueAfter") @Api(exampleProvider="getDateExample", description="ISO 8601 date") String dueAfter,
|
||||
@QueryParam("closed") Boolean closed, @QueryParam("offset") @Api(example="0") int offset,
|
||||
@QueryParam("count") @Api(example="100") int count) {
|
||||
public List<Iteration> queryIterations(@PathParam("projectId") Long projectId, @QueryParam("name") String name,
|
||||
@QueryParam("startBefore") @Api(exampleProvider="getDateExample", description="ISO 8601 date") String startBefore,
|
||||
@QueryParam("startAfter") @Api(exampleProvider="getDateExample", description="ISO 8601 date") String startAfter,
|
||||
@QueryParam("dueBefore") @Api(exampleProvider="getDateExample", description="ISO 8601 date") String dueBefore,
|
||||
@QueryParam("dueAfter") @Api(exampleProvider="getDateExample", description="ISO 8601 date") String dueAfter,
|
||||
@QueryParam("closed") Boolean closed, @QueryParam("offset") @Api(example="0") int offset,
|
||||
@QueryParam("count") @Api(example="100") int count) {
|
||||
Project project = projectManager.load(projectId);
|
||||
if (!SecurityUtils.canAccessProject(project))
|
||||
throw new UnauthorizedException();
|
||||
@ -202,22 +202,22 @@ public class ProjectResource {
|
||||
if (count > RestConstants.MAX_PAGE_SIZE)
|
||||
throw new InvalidParamException("Count should not be greater than " + RestConstants.MAX_PAGE_SIZE);
|
||||
|
||||
EntityCriteria<Milestone> criteria = EntityCriteria.of(Milestone.class);
|
||||
criteria.add(Restrictions.in(Milestone.PROP_PROJECT, project.getSelfAndAncestors()));
|
||||
EntityCriteria<Iteration> criteria = EntityCriteria.of(Iteration.class);
|
||||
criteria.add(Restrictions.in(Iteration.PROP_PROJECT, project.getSelfAndAncestors()));
|
||||
if (name != null)
|
||||
criteria.add(Restrictions.ilike(Milestone.PROP_NAME, name.replace('%', '*')));
|
||||
criteria.add(Restrictions.ilike(Iteration.PROP_NAME, name.replace('%', '*')));
|
||||
if (startBefore != null)
|
||||
criteria.add(Restrictions.le(Milestone.PROP_START_DATE, DateUtils.parseISO8601Date(startBefore)));
|
||||
criteria.add(Restrictions.le(Iteration.PROP_START_DATE, DateUtils.parseISO8601Date(startBefore)));
|
||||
if (startAfter != null)
|
||||
criteria.add(Restrictions.ge(Milestone.PROP_START_DATE, DateUtils.parseISO8601Date(startAfter)));
|
||||
criteria.add(Restrictions.ge(Iteration.PROP_START_DATE, DateUtils.parseISO8601Date(startAfter)));
|
||||
if (dueBefore != null)
|
||||
criteria.add(Restrictions.le(Milestone.PROP_DUE_DATE, DateUtils.parseISO8601Date(dueBefore)));
|
||||
criteria.add(Restrictions.le(Iteration.PROP_DUE_DATE, DateUtils.parseISO8601Date(dueBefore)));
|
||||
if (dueAfter != null)
|
||||
criteria.add(Restrictions.ge(Milestone.PROP_DUE_DATE, DateUtils.parseISO8601Date(dueAfter)));
|
||||
criteria.add(Restrictions.ge(Iteration.PROP_DUE_DATE, DateUtils.parseISO8601Date(dueAfter)));
|
||||
if (closed != null)
|
||||
criteria.add(Restrictions.eq(Milestone.PROP_CLOSED, closed));
|
||||
criteria.add(Restrictions.eq(Iteration.PROP_CLOSED, closed));
|
||||
|
||||
return milestoneManager.query(criteria, offset, count);
|
||||
return iterationManager.query(criteria, offset, count);
|
||||
}
|
||||
|
||||
@Api(order=760, description="Get top contributors on default branch")
|
||||
|
||||
@ -156,14 +156,14 @@ public abstract class EntityQuery<T extends AbstractEntity> implements Serializa
|
||||
throw new ExplicitException("Unable to find build: " + value);
|
||||
}
|
||||
|
||||
public static Milestone getMilestone(@Nullable Project project, String value) {
|
||||
public static Iteration getIteration(@Nullable Project project, String value) {
|
||||
if (project != null && !value.contains(":"))
|
||||
value = project.getPath() + ":" + value;
|
||||
Milestone milestone = OneDev.getInstance(MilestoneManager.class).findInHierarchy(value);
|
||||
if (milestone != null)
|
||||
return milestone;
|
||||
Iteration iteration = OneDev.getInstance(IterationManager.class).findInHierarchy(value);
|
||||
if (iteration != null)
|
||||
return iteration;
|
||||
else
|
||||
throw new ExplicitException("Unable to find milestone: " + value);
|
||||
throw new ExplicitException("Unable to find iteration: " + value);
|
||||
}
|
||||
|
||||
public boolean matches(T entity) {
|
||||
|
||||
@ -154,8 +154,8 @@ public class IssueQuery extends EntityQuery<Issue> implements Comparator<Issue>
|
||||
checkField(fieldName, operator, option);
|
||||
if (fieldName.equals(NAME_PROJECT)) {
|
||||
return new ProjectIsCurrentCriteria();
|
||||
} else if (fieldName.equals(IssueSchedule.NAME_MILESTONE)) {
|
||||
return new MilestoneEmptyCriteria(operator);
|
||||
} else if (fieldName.equals(IssueSchedule.NAME_ITERATION)) {
|
||||
return new IterationEmptyCriteria(operator);
|
||||
} else {
|
||||
FieldSpec fieldSpec = getGlobalIssueSetting().getFieldSpec(fieldName);
|
||||
if (fieldSpec != null)
|
||||
@ -246,8 +246,8 @@ public class IssueQuery extends EntityQuery<Issue> implements Comparator<Issue>
|
||||
case IssueQueryLexer.IsNot:
|
||||
if (fieldName.equals(NAME_PROJECT)) {
|
||||
return new ProjectCriteria(value, operator);
|
||||
} else if (fieldName.equals(IssueSchedule.NAME_MILESTONE)) {
|
||||
return new MilestoneCriteria(value, operator);
|
||||
} else if (fieldName.equals(IssueSchedule.NAME_ITERATION)) {
|
||||
return new IterationCriteria(value, operator);
|
||||
} else if (fieldName.equals(NAME_STATE)) {
|
||||
return new StateCriteria(value, operator);
|
||||
} else if (fieldName.equals(NAME_VOTE_COUNT)) {
|
||||
@ -357,7 +357,7 @@ public class IssueQuery extends EntityQuery<Issue> implements Comparator<Issue>
|
||||
FieldSpec fieldSpec = getGlobalIssueSetting().getFieldSpec(fieldName);
|
||||
if (validate && !(fieldSpec instanceof ChoiceField) && !(fieldSpec instanceof DateField)
|
||||
&& !(fieldSpec instanceof DateTimeField) && !(fieldSpec instanceof IntegerField)
|
||||
&& !(fieldSpec instanceof MilestoneChoiceField)) {
|
||||
&& !(fieldSpec instanceof IterationChoiceField)) {
|
||||
throw new ExplicitException("Can not order by field: " + fieldName);
|
||||
}
|
||||
}
|
||||
@ -396,7 +396,7 @@ public class IssueQuery extends EntityQuery<Issue> implements Comparator<Issue>
|
||||
case IssueQueryLexer.IsEmpty:
|
||||
case IssueQueryLexer.IsNotEmpty:
|
||||
if (Issue.QUERY_FIELDS.contains(fieldName)
|
||||
&& !fieldName.equals(IssueSchedule.NAME_MILESTONE)) {
|
||||
&& !fieldName.equals(IssueSchedule.NAME_ITERATION)) {
|
||||
throw newOperatorException(fieldName, operator);
|
||||
}
|
||||
break;
|
||||
@ -442,7 +442,7 @@ public class IssueQuery extends EntityQuery<Issue> implements Comparator<Issue>
|
||||
&& !fieldName.equals(Issue.NAME_VOTE_COUNT)
|
||||
&& !fieldName.equals(Issue.NAME_COMMENT_COUNT)
|
||||
&& !fieldName.equals(Issue.NAME_NUMBER)
|
||||
&& !fieldName.equals(IssueSchedule.NAME_MILESTONE)
|
||||
&& !fieldName.equals(IssueSchedule.NAME_ITERATION)
|
||||
&& !(fieldSpec instanceof IssueChoiceField)
|
||||
&& !(fieldSpec instanceof PullRequestChoiceField)
|
||||
&& !(fieldSpec instanceof BuildChoiceField)
|
||||
|
||||
@ -11,20 +11,20 @@ import javax.persistence.criteria.Subquery;
|
||||
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
import io.onedev.commons.utils.match.WildcardUtils;
|
||||
|
||||
public class MilestoneCriteria extends Criteria<Issue> {
|
||||
public class IterationCriteria extends Criteria<Issue> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String milestoneName;
|
||||
private final String iterationName;
|
||||
|
||||
private final int operator;
|
||||
|
||||
public MilestoneCriteria(String milestoneName, int operator) {
|
||||
this.milestoneName = milestoneName;
|
||||
public IterationCriteria(String iterationName, int operator) {
|
||||
this.iterationName = iterationName;
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
@ -33,10 +33,10 @@ public class MilestoneCriteria extends Criteria<Issue> {
|
||||
Subquery<IssueSchedule> scheduleQuery = query.subquery(IssueSchedule.class);
|
||||
Root<IssueSchedule> schedule = scheduleQuery.from(IssueSchedule.class);
|
||||
scheduleQuery.select(schedule);
|
||||
Join<?, ?> milestoneJoin = schedule.join(IssueSchedule.PROP_MILESTONE, JoinType.INNER);
|
||||
Join<?, ?> iterationJoin = schedule.join(IssueSchedule.PROP_ITERATION, JoinType.INNER);
|
||||
scheduleQuery.where(builder.and(
|
||||
builder.equal(schedule.get(IssueSchedule.PROP_ISSUE), from),
|
||||
builder.like(milestoneJoin.get(Milestone.PROP_NAME), milestoneName.replace("*", "%"))));
|
||||
builder.like(iterationJoin.get(Iteration.PROP_NAME), iterationName.replace("*", "%"))));
|
||||
var predicate = builder.exists(scheduleQuery);
|
||||
if (operator == IssueQueryLexer.IsNot)
|
||||
predicate = builder.not(predicate);
|
||||
@ -46,7 +46,7 @@ public class MilestoneCriteria extends Criteria<Issue> {
|
||||
@Override
|
||||
public boolean matches(Issue issue) {
|
||||
var matches = issue.getSchedules().stream()
|
||||
.anyMatch(it->WildcardUtils.matchString(milestoneName, it.getMilestone().getName()));
|
||||
.anyMatch(it->WildcardUtils.matchString(iterationName, it.getIteration().getName()));
|
||||
if (operator == IssueQueryLexer.IsNot)
|
||||
matches = !matches;
|
||||
return matches;
|
||||
@ -54,19 +54,19 @@ public class MilestoneCriteria extends Criteria<Issue> {
|
||||
|
||||
@Override
|
||||
public String toStringWithoutParens() {
|
||||
return quote(IssueSchedule.NAME_MILESTONE) + " "
|
||||
return quote(IssueSchedule.NAME_ITERATION) + " "
|
||||
+ IssueQuery.getRuleName(operator) + " "
|
||||
+ quote(milestoneName);
|
||||
+ quote(iterationName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(Issue issue) {
|
||||
if (operator == IssueQueryLexer.Is) {
|
||||
Milestone milestone = issue.getProject().getHierarchyMilestone(milestoneName);
|
||||
if (milestone != null) {
|
||||
Iteration iteration = issue.getProject().getHierarchyIteration(iterationName);
|
||||
if (iteration != null) {
|
||||
IssueSchedule schedule = new IssueSchedule();
|
||||
schedule.setIssue(issue);
|
||||
schedule.setMilestone(milestone);
|
||||
schedule.setIteration(iteration);
|
||||
issue.getSchedules().add(schedule);
|
||||
}
|
||||
}
|
||||
@ -9,13 +9,13 @@ import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
|
||||
public class MilestoneEmptyCriteria extends Criteria<Issue> {
|
||||
public class IterationEmptyCriteria extends Criteria<Issue> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final int operator;
|
||||
|
||||
public MilestoneEmptyCriteria(int operator) {
|
||||
public IterationEmptyCriteria(int operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@ public class MilestoneEmptyCriteria extends Criteria<Issue> {
|
||||
|
||||
@Override
|
||||
public String toStringWithoutParens() {
|
||||
return quote(IssueSchedule.NAME_MILESTONE) + " " + IssueQuery.getRuleName(operator);
|
||||
return quote(IssueSchedule.NAME_ITERATION) + " " + IssueQuery.getRuleName(operator);
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,21 +2,21 @@ package io.onedev.server.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class MilestoneAndIssueState implements Serializable {
|
||||
public class IterationAndIssueState implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Long milestoneId;
|
||||
private final Long iterationId;
|
||||
|
||||
private final String issueState;
|
||||
|
||||
public MilestoneAndIssueState(Long milestoneId, String issueState) {
|
||||
this.milestoneId = milestoneId;
|
||||
public IterationAndIssueState(Long iterationId, String issueState) {
|
||||
this.iterationId = iterationId;
|
||||
this.issueState = issueState;
|
||||
}
|
||||
|
||||
public Long getMilestoneId() {
|
||||
return milestoneId;
|
||||
public Long getIterationId() {
|
||||
return iterationId;
|
||||
}
|
||||
|
||||
public String getIssueState() {
|
||||
@ -3,14 +3,14 @@ package io.onedev.server.util;
|
||||
import org.eclipse.jgit.util.StringUtils;
|
||||
import org.hibernate.criterion.Order;
|
||||
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
|
||||
public enum MilestoneSort {
|
||||
public enum IterationSort {
|
||||
CLOSEST_DUE_DATE {
|
||||
|
||||
@Override
|
||||
public Order getOrder(boolean closed) {
|
||||
return closed?Order.desc(Milestone.PROP_DUE_DATE):Order.asc(Milestone.PROP_DUE_DATE);
|
||||
return closed?Order.desc(Iteration.PROP_DUE_DATE):Order.asc(Iteration.PROP_DUE_DATE);
|
||||
}
|
||||
|
||||
},
|
||||
@ -18,7 +18,7 @@ public enum MilestoneSort {
|
||||
|
||||
@Override
|
||||
public Order getOrder(boolean closed) {
|
||||
return closed?Order.asc(Milestone.PROP_DUE_DATE):Order.desc(Milestone.PROP_DUE_DATE);
|
||||
return closed?Order.asc(Iteration.PROP_DUE_DATE):Order.desc(Iteration.PROP_DUE_DATE);
|
||||
}
|
||||
|
||||
},
|
||||
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1718635970649" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7534" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M460.356 215.04c0 62.976-51.052 114.03-114.038 114.03-62.964 0-114.018-51.052-114.018-114.03 0-62.982 51.052-114.036 114.018-114.036C409.302 101.004 460.356 152.056 460.356 215.04zM346.328 694.924c-62.986 0-114.038 51.068-114.038 114.038 0 62.984 51.05 114.036 114.038 114.036 62.984 0 114.038-51.05 114.038-114.036C460.366 745.992 409.312 694.924 346.328 694.924zM520.258 220.554c91.046 8.744 170.018 59.456 217.28 132.514 34.118-15.68 71.982-19.584 107.642-12.224-63.526-131.058-197.91-221.646-353.062-221.646-0.202 0-0.402 0.008-0.604 0.008C510.898 148.472 521.406 183.404 520.258 220.554zM245.304 667.328c-60.176-94.848-60.428-216.006-0.608-311.108-28.804-20.792-51.018-50.17-62.858-84.354-109.222 141.222-109.128 338.154 0.338 479.282C194.188 717.142 216.476 687.95 245.304 667.328zM737.542 669.504c-47.272 73.076-126.26 123.792-217.328 132.526 1.436 36.774-8.56 71.744-27.758 101.35 155.012-0.132 289.25-90.684 352.728-221.652C809.616 689.064 771.76 685.23 737.542 669.504zM809.964 397.26c-62.966 0-114.02 51.05-114.02 114.028 0 62.974 51.052 114.026 114.02 114.026 62.982 0 114.036-51.05 114.036-114.026C924 448.31 872.948 397.26 809.964 397.26z" p-id="7535"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M540 175.8H428v-112h112v112z m224 280H204c-30.8 0-56-25.2-56-56v-112c0-30.8 25.2-56 56-56h560l112 112-112 112z m-224-168H428v112h112v-112zM428 959.7h112V511.8H428v447.9z" /></svg>
|
||||
|
Before Width: | Height: | Size: 446 B |
@ -141,7 +141,7 @@ public class IssueQueryBehavior extends ANTLRAssistBehavior {
|
||||
for (FieldSpec field: issueSetting.getFieldSpecs()) {
|
||||
if (field instanceof IntegerField || field instanceof ChoiceField
|
||||
|| field instanceof DateField || field instanceof DateTimeField
|
||||
|| field instanceof MilestoneChoiceField) {
|
||||
|| field instanceof IterationChoiceField) {
|
||||
candidates.put(field.getName(), null);
|
||||
}
|
||||
}
|
||||
@ -227,9 +227,9 @@ public class IssueQueryBehavior extends ANTLRAssistBehavior {
|
||||
}
|
||||
} else if (fieldName.equals(NAME_NUMBER)) {
|
||||
return SuggestionUtils.suggestIssues(project, matchWith, InputAssistBehavior.MAX_SUGGESTIONS);
|
||||
} else if (fieldName.equals(IssueSchedule.NAME_MILESTONE)) {
|
||||
} else if (fieldName.equals(IssueSchedule.NAME_ITERATION)) {
|
||||
if (project != null && !matchWith.contains("*"))
|
||||
return SuggestionUtils.suggestMilestones(project, matchWith);
|
||||
return SuggestionUtils.suggestIterations(project, matchWith);
|
||||
else
|
||||
return null;
|
||||
} else if (fieldName.equals(NAME_ESTIMATED_TIME) || fieldName.equals(NAME_SPENT_TIME)) {
|
||||
@ -328,7 +328,7 @@ public class IssueQueryBehavior extends ANTLRAssistBehavior {
|
||||
} else if (fieldName.equals(Issue.NAME_TITLE)
|
||||
|| fieldName.equals(Issue.NAME_DESCRIPTION)
|
||||
|| fieldName.equals(Issue.NAME_COMMENT)
|
||||
|| fieldName.equals(IssueSchedule.NAME_MILESTONE)) {
|
||||
|| fieldName.equals(IssueSchedule.NAME_ITERATION)) {
|
||||
hints.add("Use '*' for wildcard match");
|
||||
hints.add("Use '\\' to escape quotes");
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ public abstract class CommandPalettePanel extends Panel {
|
||||
"~test/** ~errors/** ~sso/** ~oauth/** ~verify-email-address/** ~create-user-from-invitation/** " +
|
||||
"~reset-password/** ~signup/** ~logout/** ~login/** ~loading/** ~init/** ~help/** **/invalid " +
|
||||
"**/${issue}/** -**/${issue} **/${request}/** -**/${request} **/${build}/** -**/${build} " +
|
||||
"**/${milestone}/** -**/${milestone} **/${agent}/** -**/${agent} **/${group}/** -**/${group} " +
|
||||
"**/${iteration}/** -**/${iteration} **/${agent}/** -**/${agent} **/${group}/** -**/${group} " +
|
||||
"projects/**");
|
||||
|
||||
private static final PatternSet eeUrlPatterns = PatternSet.parse("" +
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
package io.onedev.server.web.component.commandpalette;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.IterationManager;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.web.page.project.issues.iteration.IterationDetailPage;
|
||||
|
||||
public class IterationParam extends ParamSegment {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public IterationParam(boolean optional) {
|
||||
super(IterationDetailPage.PARAM_ITERATION, optional);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> suggest(String matchWith, Map<String, String> paramValues, int count) {
|
||||
Map<String, String> suggestions = new LinkedHashMap<>();
|
||||
for (Iteration iteration: ParsedUrl.getProject(paramValues).getSortedHierarchyIterations()) {
|
||||
if (iteration.getName().toLowerCase().contains(matchWith)) {
|
||||
suggestions.put(iteration.getName(), String.valueOf(iteration.getId()));
|
||||
if (--count == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExactMatch(String matchWith, Map<String, String> paramValues) {
|
||||
try {
|
||||
Long iterationId = Long.valueOf(matchWith);
|
||||
if (OneDev.getInstance(IterationManager.class).get(iterationId) != null)
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
package io.onedev.server.web.component.commandpalette;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.web.page.project.issues.milestones.MilestoneDetailPage;
|
||||
|
||||
public class MilestoneParam extends ParamSegment {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MilestoneParam(boolean optional) {
|
||||
super(MilestoneDetailPage.PARAM_MILESTONE, optional);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> suggest(String matchWith, Map<String, String> paramValues, int count) {
|
||||
Map<String, String> suggestions = new LinkedHashMap<>();
|
||||
for (Milestone milestone: ParsedUrl.getProject(paramValues).getSortedHierarchyMilestones()) {
|
||||
if (milestone.getName().toLowerCase().contains(matchWith)) {
|
||||
suggestions.put(milestone.getName(), String.valueOf(milestone.getId()));
|
||||
if (--count == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExactMatch(String matchWith, Map<String, String> paramValues) {
|
||||
try {
|
||||
Long milestoneId = Long.valueOf(matchWith);
|
||||
if (OneDev.getInstance(MilestoneManager.class).get(milestoneId) != null)
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -33,7 +33,7 @@ import io.onedev.server.web.page.project.builds.detail.BuildDetailPage;
|
||||
import io.onedev.server.web.page.project.commits.CommitDetailPage;
|
||||
import io.onedev.server.web.page.project.issues.boards.IssueBoardsPage;
|
||||
import io.onedev.server.web.page.project.issues.detail.IssueDetailPage;
|
||||
import io.onedev.server.web.page.project.issues.milestones.MilestoneDetailPage;
|
||||
import io.onedev.server.web.page.project.issues.iteration.IterationDetailPage;
|
||||
import io.onedev.server.web.page.project.pullrequests.detail.PullRequestDetailPage;
|
||||
|
||||
public abstract class ParsedUrl implements Serializable {
|
||||
@ -88,8 +88,8 @@ public abstract class ParsedUrl implements Serializable {
|
||||
case AgentDetailPage.PARAM_AGENT:
|
||||
parsedSegments.add(new AgentParam(optional));
|
||||
break;
|
||||
case MilestoneDetailPage.PARAM_MILESTONE:
|
||||
parsedSegments.add(new MilestoneParam(optional));
|
||||
case IterationDetailPage.PARAM_ITERATION:
|
||||
parsedSegments.add(new IterationParam(optional));
|
||||
break;
|
||||
case ServerDetailPage.PARAM_SERVER:
|
||||
if (getServers().size() > 1)
|
||||
@ -147,7 +147,7 @@ public abstract class ParsedUrl implements Serializable {
|
||||
case "~boards":
|
||||
case "~issues":
|
||||
return project.isIssueManagement();
|
||||
case "~milestones":
|
||||
case "~iterations":
|
||||
if (project.isIssueManagement()) {
|
||||
if (segment2.equals("new"))
|
||||
return SecurityUtils.canManageIssues(project);
|
||||
|
||||
@ -27,10 +27,10 @@
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
<wicket:enclosure child="milestones">
|
||||
<wicket:enclosure child="iterations">
|
||||
<div class="form-group">
|
||||
<label class="control-label">Milestones</label>
|
||||
<input wicket:id="milestones" type="hidden" class="form-control">
|
||||
<label class="control-label">Iterations</label>
|
||||
<input wicket:id="iterations" type="hidden" class="form-control">
|
||||
</div>
|
||||
</wicket:enclosure>
|
||||
<div wicket:id="fields"></div>
|
||||
|
||||
@ -9,7 +9,7 @@ import io.onedev.server.buildspecmodel.inputspec.InputSpec;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.support.administration.GlobalIssueSetting;
|
||||
import io.onedev.server.model.support.issue.IssueTemplate;
|
||||
@ -27,7 +27,7 @@ import io.onedev.server.web.behavior.ReferenceInputBehavior;
|
||||
import io.onedev.server.web.component.comment.CommentInput;
|
||||
import io.onedev.server.web.component.issue.IssueStateBadge;
|
||||
import io.onedev.server.web.component.issue.title.IssueTitlePanel;
|
||||
import io.onedev.server.web.component.milestone.choice.MilestoneMultiChoice;
|
||||
import io.onedev.server.web.component.iteration.choice.IterationMultiChoice;
|
||||
import io.onedev.server.web.component.modal.confirm.ConfirmModalPanel;
|
||||
import io.onedev.server.web.editable.BeanContext;
|
||||
import io.onedev.server.web.editable.BeanEditor;
|
||||
@ -76,7 +76,7 @@ public abstract class NewIssueEditor extends FormComponentPanel<Issue> implement
|
||||
|
||||
private CheckBox confidentialInput;
|
||||
|
||||
private MilestoneMultiChoice milestoneChoice;
|
||||
private IterationMultiChoice iterationChoice;
|
||||
|
||||
private BeanEditor fieldEditor;
|
||||
|
||||
@ -211,20 +211,20 @@ public abstract class NewIssueEditor extends FormComponentPanel<Issue> implement
|
||||
|
||||
add(confidentialInput = new CheckBox("confidential", Model.of(false)));
|
||||
|
||||
Collection<Milestone> milestones = issue.getMilestones();
|
||||
milestoneChoice = new MilestoneMultiChoice("milestones", Model.of(milestones),
|
||||
new LoadableDetachableModel<Collection<Milestone>>() {
|
||||
Collection<Iteration> iterations = issue.getIterations();
|
||||
iterationChoice = new IterationMultiChoice("iterations", Model.of(iterations),
|
||||
new LoadableDetachableModel<Collection<Iteration>>() {
|
||||
|
||||
@Override
|
||||
protected Collection<Milestone> load() {
|
||||
return getProject().getSortedHierarchyMilestones();
|
||||
protected Collection<Iteration> load() {
|
||||
return getProject().getSortedHierarchyIterations();
|
||||
}
|
||||
|
||||
});
|
||||
milestoneChoice.setVisible(SecurityUtils.canScheduleIssues(getProject()));
|
||||
milestoneChoice.setRequired(false);
|
||||
iterationChoice.setVisible(SecurityUtils.canScheduleIssues(getProject()));
|
||||
iterationChoice.setRequired(false);
|
||||
|
||||
add(milestoneChoice);
|
||||
add(iterationChoice);
|
||||
|
||||
Collection<String> properties = FieldUtils.getEditablePropertyNames(getProject(),
|
||||
fieldBeanClass, fieldNames);
|
||||
@ -411,12 +411,12 @@ public abstract class NewIssueEditor extends FormComponentPanel<Issue> implement
|
||||
issue.setFieldValues(FieldUtils.getFieldValues(fieldEditor.newComponentContext(),
|
||||
fieldEditor.getConvertedInput(), fieldNames));
|
||||
|
||||
milestoneChoice.convertInput();
|
||||
iterationChoice.convertInput();
|
||||
issue.getSchedules().clear();
|
||||
for (Milestone milestone: milestoneChoice.getConvertedInput()) {
|
||||
for (Iteration iteration: iterationChoice.getConvertedInput()) {
|
||||
IssueSchedule schedule = new IssueSchedule();
|
||||
schedule.setIssue(issue);
|
||||
schedule.setMilestone(milestone);
|
||||
schedule.setIteration(iteration);
|
||||
issue.getSchedules().add(schedule);
|
||||
}
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ import io.onedev.server.web.page.base.BasePage;
|
||||
import io.onedev.server.web.page.project.builds.detail.dashboard.BuildDashboardPage;
|
||||
import io.onedev.server.web.page.project.commits.CommitDetailPage;
|
||||
import io.onedev.server.web.page.project.issues.detail.IssueActivitiesPage;
|
||||
import io.onedev.server.web.page.project.issues.milestones.MilestoneIssuesPage;
|
||||
import io.onedev.server.web.page.project.issues.iteration.IterationIssuesPage;
|
||||
import io.onedev.server.web.page.project.pullrequests.detail.activities.PullRequestActivitiesPage;
|
||||
import io.onedev.server.web.util.ProjectAware;
|
||||
import org.apache.wicket.Component;
|
||||
@ -280,14 +280,14 @@ public abstract class FieldValuesPanel extends Panel implements EditContext, Pro
|
||||
} else {
|
||||
valueContainer.add(new Label("value", "<i>Not Found</i>").setEscapeModelStrings(false));
|
||||
}
|
||||
} else if (getField().getType().equals(FieldSpec.MILESTONE)) {
|
||||
Milestone milestone = OneDev.getInstance(MilestoneManager.class).findInHierarchy(getIssue().getProject(), value);
|
||||
if (milestone != null) {
|
||||
} else if (getField().getType().equals(FieldSpec.ITERATION)) {
|
||||
Iteration iteration = OneDev.getInstance(IterationManager.class).findInHierarchy(getIssue().getProject(), value);
|
||||
if (iteration != null) {
|
||||
Fragment linkFrag = new Fragment("value", "linkFrag", FieldValuesPanel.this);
|
||||
Link<Void> milestoneLink = new BookmarkablePageLink<Void>("link", MilestoneIssuesPage.class,
|
||||
MilestoneIssuesPage.paramsOf(getIssue().getProject(), milestone));
|
||||
milestoneLink.add(new Label("label", milestone.getName()));
|
||||
linkFrag.add(milestoneLink);
|
||||
Link<Void> iterationLink = new BookmarkablePageLink<Void>("link", IterationIssuesPage.class,
|
||||
IterationIssuesPage.paramsOf(getIssue().getProject(), iteration));
|
||||
iterationLink.add(new Label("label", iteration.getName()));
|
||||
linkFrag.add(iterationLink);
|
||||
valueContainer.add(linkFrag);
|
||||
} else {
|
||||
valueContainer.add(new Label("value", "<i>Not Found</i>").setEscapeModelStrings(false));
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
package io.onedev.server.web.component.issue.iteration;
|
||||
|
||||
import io.onedev.server.web.page.base.BaseDependentCssResourceReference;
|
||||
|
||||
public class IterationCrumbCssResourceReference extends BaseDependentCssResourceReference {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public IterationCrumbCssResourceReference() {
|
||||
super(IterationCrumbCssResourceReference.class, "iteration-crumb.css");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
<wicket:panel>
|
||||
<div wicket:id="edit" class="iteration-crumb">
|
||||
<a wicket:id="iterations" onclick="event.stopPropagation();">
|
||||
<wicket:svg href="iteration" class="icon"/>
|
||||
<span wicket:id="name"></span>
|
||||
</a>
|
||||
</div>
|
||||
</wicket:panel>
|
||||
@ -1,4 +1,4 @@
|
||||
package io.onedev.server.web.component.issue.milestone;
|
||||
package io.onedev.server.web.component.issue.iteration;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.IssueChangeManager;
|
||||
@ -8,7 +8,7 @@ import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.web.component.floating.FloatingPanel;
|
||||
import io.onedev.server.web.editable.InplacePropertyEditLink;
|
||||
import io.onedev.server.web.page.base.BasePage;
|
||||
import io.onedev.server.web.page.project.issues.milestones.MilestoneIssuesPage;
|
||||
import io.onedev.server.web.page.project.issues.iteration.IterationIssuesPage;
|
||||
import org.apache.wicket.Component;
|
||||
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
|
||||
import org.apache.wicket.markup.head.CssHeaderItem;
|
||||
@ -22,9 +22,9 @@ import java.io.Serializable;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
public abstract class MilestoneCrumbPanel extends Panel {
|
||||
public abstract class IterationCrumbPanel extends Panel {
|
||||
|
||||
public MilestoneCrumbPanel(String id) {
|
||||
public IterationCrumbPanel(String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@ -45,25 +45,25 @@ public abstract class MilestoneCrumbPanel extends Panel {
|
||||
|
||||
@Override
|
||||
protected Serializable getBean() {
|
||||
var bean = new MilestonesBean();
|
||||
bean.setMilestoneNames(getIssue().getSchedules().stream()
|
||||
.map(it -> it.getMilestone().getName()).collect(toList()));
|
||||
var bean = new IterationsBean();
|
||||
bean.setIterationNames(getIssue().getSchedules().stream()
|
||||
.map(it -> it.getIteration().getName()).collect(toList()));
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPropertyName() {
|
||||
return "milestoneNames";
|
||||
return "iterationNames";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUpdated(IPartialPageRequestHandler handler, Serializable bean, String propertyName) {
|
||||
var milestonesBean = (MilestonesBean) bean;
|
||||
var iterationsBean = (IterationsBean) bean;
|
||||
var issue = getIssue();
|
||||
var milestones = milestonesBean.getMilestoneNames().stream()
|
||||
.map(it -> issue.getProject().getHierarchyMilestone(it))
|
||||
var iterations = iterationsBean.getIterationNames().stream()
|
||||
.map(it -> issue.getProject().getHierarchyIteration(it))
|
||||
.collect(toList());
|
||||
OneDev.getInstance(IssueChangeManager.class).changeMilestones(issue, milestones);
|
||||
OneDev.getInstance(IssueChangeManager.class).changeIterations(issue, iterations);
|
||||
((BasePage) getPage()).notifyObservablesChange(handler, issue.getChangeObservables(true));
|
||||
}
|
||||
|
||||
@ -75,15 +75,15 @@ public abstract class MilestoneCrumbPanel extends Panel {
|
||||
};
|
||||
add(editLink);
|
||||
|
||||
var milestonesView = new RepeatingView("milestones");
|
||||
var iterationsView = new RepeatingView("iterations");
|
||||
for (var schedule: getIssue().getSchedules()) {
|
||||
var child = new BookmarkablePageLink<Void>(milestonesView.newChildId(),
|
||||
MilestoneIssuesPage.class,
|
||||
MilestoneIssuesPage.paramsOf(getIssue().getProject(), schedule.getMilestone()));
|
||||
milestonesView.add(child);
|
||||
child.add(new Label("name", schedule.getMilestone().getName()));
|
||||
var child = new BookmarkablePageLink<Void>(iterationsView.newChildId(),
|
||||
IterationIssuesPage.class,
|
||||
IterationIssuesPage.paramsOf(getIssue().getProject(), schedule.getIteration()));
|
||||
iterationsView.add(child);
|
||||
child.add(new Label("name", schedule.getIteration().getName()));
|
||||
}
|
||||
editLink.add(milestonesView);
|
||||
editLink.add(iterationsView);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -95,7 +95,7 @@ public abstract class MilestoneCrumbPanel extends Panel {
|
||||
@Override
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
super.renderHead(response);
|
||||
response.render(CssHeaderItem.forReference(new MilestoneCrumbCssResourceReference()));
|
||||
response.render(CssHeaderItem.forReference(new IterationCrumbCssResourceReference()));
|
||||
}
|
||||
|
||||
protected abstract Issue getIssue();
|
||||
@ -0,0 +1,24 @@
|
||||
package io.onedev.server.web.component.issue.iteration;
|
||||
|
||||
import io.onedev.server.annotation.Editable;
|
||||
import io.onedev.server.annotation.IterationChoice;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Editable
|
||||
public class IterationsBean implements Serializable {
|
||||
|
||||
private List<String> iterationNames;
|
||||
|
||||
@Editable
|
||||
@IterationChoice
|
||||
public List<String> getIterationNames() {
|
||||
return iterationNames;
|
||||
}
|
||||
|
||||
public void setIterationNames(List<String> iterationNames) {
|
||||
this.iterationNames = iterationNames;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
.milestone-crumb {
|
||||
.iteration-crumb {
|
||||
cursor: pointer;
|
||||
border-bottom: 2px dotted transparent;
|
||||
padding: 0 0.5rem 0.5rem 0;
|
||||
}
|
||||
.milestone-crumb:hover {
|
||||
.iteration-crumb:hover {
|
||||
border-color: var(--gray);
|
||||
}
|
||||
@ -20,7 +20,7 @@
|
||||
<input wicket:id="confidentialCheck" type="checkbox"> Confidential
|
||||
</label>
|
||||
<label class="checkbox flex-shrink-0 mr-3 mb-4">
|
||||
<input wicket:id="milestoneCheck" type="checkbox"> Milestone
|
||||
<input wicket:id="iterationCheck" type="checkbox"> Iteration
|
||||
</label>
|
||||
<label wicket:id="customFields" class="checkbox flex-shrink-0 mr-3 mb-4">
|
||||
<input wicket:id="customFieldCheck" type="checkbox"> <wicket:container wicket:id="name"></wicket:container>
|
||||
|
||||
@ -36,7 +36,7 @@ import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.IssueChangeManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.support.administration.GlobalIssueSetting;
|
||||
import io.onedev.server.buildspecmodel.inputspec.InputContext;
|
||||
@ -145,7 +145,7 @@ abstract class BatchEditPanel extends Panel implements InputContext {
|
||||
|
||||
}).add(newOnChangeBehavior(form)));
|
||||
|
||||
form.add(new CheckBox("milestoneCheck", new IModel<Boolean>() {
|
||||
form.add(new CheckBox("iterationCheck", new IModel<Boolean>() {
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
@ -153,15 +153,15 @@ abstract class BatchEditPanel extends Panel implements InputContext {
|
||||
|
||||
@Override
|
||||
public Boolean getObject() {
|
||||
return selectedFields.contains(NAME_MILESTONES);
|
||||
return selectedFields.contains(NAME_ITERATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(Boolean object) {
|
||||
if (object)
|
||||
selectedFields.add(NAME_MILESTONES);
|
||||
selectedFields.add(NAME_ITERATION);
|
||||
else
|
||||
selectedFields.remove(NAME_MILESTONES);
|
||||
selectedFields.remove(NAME_ITERATION);
|
||||
}
|
||||
|
||||
}).add(newOnChangeBehavior(form)));
|
||||
@ -207,7 +207,7 @@ abstract class BatchEditPanel extends Panel implements InputContext {
|
||||
issue.setProject(getProject());
|
||||
if (getIssueQuery() != null && getIssueQuery().getCriteria() != null) {
|
||||
getIssueQuery().getCriteria().fill(issue);
|
||||
builtInFieldsBean.setMilestones(issue.getMilestones().stream()
|
||||
builtInFieldsBean.setIterations(issue.getIterations().stream()
|
||||
.map(it->it.getName()).collect(Collectors.toList()));
|
||||
customFieldsBean = issue.getFieldBean(fieldBeanClass, false);
|
||||
} else {
|
||||
@ -225,8 +225,8 @@ abstract class BatchEditPanel extends Panel implements InputContext {
|
||||
excludedProperties.add(PROP_STATE);
|
||||
if (!selectedFields.contains(NAME_CONFIDENTIAL))
|
||||
excludedProperties.add(PROP_CONFIDENTIAL);
|
||||
if (!selectedFields.contains(NAME_MILESTONES))
|
||||
excludedProperties.add(PROP_MILESTONES);
|
||||
if (!selectedFields.contains(NAME_ITERATION))
|
||||
excludedProperties.add(PROP_ITERATIONS);
|
||||
|
||||
builtInFieldsEditor = BeanContext.edit("builtInFieldsEditor", builtInFieldsBean, excludedProperties, true);
|
||||
form.add(builtInFieldsEditor);
|
||||
@ -284,22 +284,22 @@ abstract class BatchEditPanel extends Panel implements InputContext {
|
||||
else
|
||||
confidential = null;
|
||||
|
||||
Collection<Milestone> milestones;
|
||||
if (selectedFields.contains(NAME_MILESTONES)) {
|
||||
milestones = new ArrayList<>();
|
||||
for (String each: builtInFieldsBean.getMilestones()) {
|
||||
Milestone milestone = getProject().getHierarchyMilestone(each);
|
||||
if (milestone != null)
|
||||
milestones.add(milestone);
|
||||
Collection<Iteration> iterations;
|
||||
if (selectedFields.contains(NAME_ITERATION)) {
|
||||
iterations = new ArrayList<>();
|
||||
for (String each: builtInFieldsBean.getIterations()) {
|
||||
Iteration iteration = getProject().getHierarchyIteration(each);
|
||||
if (iteration != null)
|
||||
iterations.add(iteration);
|
||||
}
|
||||
} else {
|
||||
milestones = null;
|
||||
iterations = null;
|
||||
}
|
||||
|
||||
Map<String, Object> fieldValues = FieldUtils.getFieldValues(customFieldsEditor.newComponentContext(),
|
||||
customFieldsBean, selectedFields);
|
||||
OneDev.getInstance(IssueChangeManager.class).batchUpdate(
|
||||
getIssueIterator(), state, confidential, milestones, fieldValues, comment);
|
||||
getIssueIterator(), state, confidential, iterations, fieldValues, comment);
|
||||
onUpdated(target);
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.model.support.administration.GlobalIssueSetting;
|
||||
import io.onedev.server.annotation.ChoiceProvider;
|
||||
import io.onedev.server.annotation.Editable;
|
||||
import io.onedev.server.annotation.MilestoneChoice;
|
||||
import io.onedev.server.annotation.IterationChoice;
|
||||
|
||||
@Editable
|
||||
public class BuiltInFieldsBean implements Serializable {
|
||||
@ -22,19 +22,19 @@ public class BuiltInFieldsBean implements Serializable {
|
||||
|
||||
public static final String NAME_CONFIDENTIAL = "Confidential";
|
||||
|
||||
public static final String NAME_MILESTONES = "Milestones";
|
||||
public static final String NAME_ITERATION = "Iterations";
|
||||
|
||||
public static final String PROP_STATE = "state";
|
||||
|
||||
public static final String PROP_CONFIDENTIAL = "confidential";
|
||||
|
||||
public static final String PROP_MILESTONES = "milestones";
|
||||
public static final String PROP_ITERATIONS = "iterations";
|
||||
|
||||
private String state;
|
||||
|
||||
private boolean confidential;
|
||||
|
||||
private List<String> milestones;
|
||||
private List<String> iterations;
|
||||
|
||||
@Editable(order=100)
|
||||
@ChoiceProvider("getStateChoices")
|
||||
@ -57,13 +57,13 @@ public class BuiltInFieldsBean implements Serializable {
|
||||
}
|
||||
|
||||
@Editable(order=200)
|
||||
@MilestoneChoice
|
||||
public List<String> getMilestones() {
|
||||
return milestones;
|
||||
@IterationChoice
|
||||
public List<String> getIterations() {
|
||||
return iterations;
|
||||
}
|
||||
|
||||
public void setMilestones(List<String> milestones) {
|
||||
this.milestones = milestones;
|
||||
public void setIterations(List<String> iterations) {
|
||||
this.iterations = iterations;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@ -39,7 +39,7 @@ public class FieldsAndLinksBean implements Serializable {
|
||||
choices.add(Issue.NAME_STATE);
|
||||
for (String fieldName: OneDev.getInstance(SettingManager.class).getIssueSetting().getFieldNames())
|
||||
choices.add(fieldName);
|
||||
choices.add(IssueSchedule.NAME_MILESTONE);
|
||||
choices.add(IssueSchedule.NAME_ITERATION);
|
||||
return choices;
|
||||
}
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ import io.onedev.server.web.component.floating.FloatingPanel;
|
||||
import io.onedev.server.web.component.issue.IssueStateBadge;
|
||||
import io.onedev.server.web.component.issue.fieldvalues.FieldValuesPanel;
|
||||
import io.onedev.server.web.component.issue.link.IssueLinksPanel;
|
||||
import io.onedev.server.web.component.issue.milestone.MilestoneCrumbPanel;
|
||||
import io.onedev.server.web.component.issue.iteration.IterationCrumbPanel;
|
||||
import io.onedev.server.web.component.issue.operation.TransitionMenuLink;
|
||||
import io.onedev.server.web.component.issue.progress.IssueProgressPanel;
|
||||
import io.onedev.server.web.component.issue.progress.QueriedIssuesProgressPanel;
|
||||
@ -1668,8 +1668,8 @@ public abstract class IssueListPanel extends Panel {
|
||||
stateFragment.add(transitLink);
|
||||
|
||||
fieldsView.add(stateFragment.setOutputMarkupId(true));
|
||||
} else if (field.equals(IssueSchedule.NAME_MILESTONE)) {
|
||||
fieldsView.add(new MilestoneCrumbPanel(fieldsView.newChildId()) {
|
||||
} else if (field.equals(IssueSchedule.NAME_ITERATION)) {
|
||||
fieldsView.add(new IterationCrumbPanel(fieldsView.newChildId()) {
|
||||
@Override
|
||||
protected Issue getIssue() {
|
||||
return (Issue) fragment.getDefaultModelObject();
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
package io.onedev.server.web.component.issue.milestone;
|
||||
|
||||
import io.onedev.server.web.page.base.BaseDependentCssResourceReference;
|
||||
|
||||
public class MilestoneCrumbCssResourceReference extends BaseDependentCssResourceReference {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MilestoneCrumbCssResourceReference() {
|
||||
super(MilestoneCrumbCssResourceReference.class, "milestone-crumb.css");
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
<wicket:panel>
|
||||
<div wicket:id="edit" class="milestone-crumb">
|
||||
<a wicket:id="milestones" onclick="event.stopPropagation();">
|
||||
<wicket:svg href="milestone" class="icon"/>
|
||||
<span wicket:id="name"></span>
|
||||
</a>
|
||||
</div>
|
||||
</wicket:panel>
|
||||
@ -1,24 +0,0 @@
|
||||
package io.onedev.server.web.component.issue.milestone;
|
||||
|
||||
import io.onedev.server.annotation.Editable;
|
||||
import io.onedev.server.annotation.MilestoneChoice;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
@Editable
|
||||
public class MilestonesBean implements Serializable {
|
||||
|
||||
private List<String> milestoneNames;
|
||||
|
||||
@Editable
|
||||
@MilestoneChoice
|
||||
public List<String> getMilestoneNames() {
|
||||
return milestoneNames;
|
||||
}
|
||||
|
||||
public void setMilestoneNames(List<String> milestoneNames) {
|
||||
this.milestoneNames = milestoneNames;
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,13 +20,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</wicket:enclosure>
|
||||
<div wicket:id="milestones" class="milestones">
|
||||
<div class="head">Milestones</div>
|
||||
<div wicket:id="iterations" class="iterations">
|
||||
<div class="head">Iterations</div>
|
||||
<ul class="body list-unstyled mb-0">
|
||||
<li wicket:id="milestones" class="bg-light rounded p-3">
|
||||
<li wicket:id="iterations" class="bg-light rounded p-3">
|
||||
<div class="d-flex">
|
||||
<a wicket:id="link" class="mr-2"><span wicket:id="label"></span></a>
|
||||
<a wicket:id="delete" class="mr-2 flex-shrink-0" title="Remove issue from this milestone"><wicket:svg href="trash" class="icon icon-sm"></wicket:svg></a>
|
||||
<a wicket:id="delete" class="mr-2 flex-shrink-0" title="Remove issue from this iteration"><wicket:svg href="trash" class="icon icon-sm"></wicket:svg></a>
|
||||
<span wicket:id="status" class="status ml-auto"></span>
|
||||
</div>
|
||||
<div wicket:id="progress" class="mt-3"></div>
|
||||
|
||||
@ -31,9 +31,9 @@ import io.onedev.server.web.component.issue.fieldvalues.FieldValuesPanel;
|
||||
import io.onedev.server.web.component.issue.operation.TransitionMenuLink;
|
||||
import io.onedev.server.web.component.issue.statestats.StateStatsBar;
|
||||
import io.onedev.server.web.component.link.ViewStateAwarePageLink;
|
||||
import io.onedev.server.web.component.milestone.MilestoneStatusLabel;
|
||||
import io.onedev.server.web.component.milestone.choice.AbstractMilestoneChoiceProvider;
|
||||
import io.onedev.server.web.component.milestone.choice.MilestoneChoiceResourceReference;
|
||||
import io.onedev.server.web.component.iteration.IterationStatusLabel;
|
||||
import io.onedev.server.web.component.iteration.choice.AbstractIterationChoiceProvider;
|
||||
import io.onedev.server.web.component.iteration.choice.IterationChoiceResourceReference;
|
||||
import io.onedev.server.web.component.modal.ModalLink;
|
||||
import io.onedev.server.web.component.modal.ModalPanel;
|
||||
import io.onedev.server.web.component.select2.Response;
|
||||
@ -45,7 +45,7 @@ import io.onedev.server.web.component.user.list.SimpleUserListLink;
|
||||
import io.onedev.server.web.editable.InplacePropertyEditLink;
|
||||
import io.onedev.server.web.page.base.BasePage;
|
||||
import io.onedev.server.web.page.project.issues.detail.IssueActivitiesPage;
|
||||
import io.onedev.server.web.page.project.issues.milestones.MilestoneIssuesPage;
|
||||
import io.onedev.server.web.page.project.issues.iteration.IterationIssuesPage;
|
||||
import io.onedev.server.web.page.simple.security.LoginPage;
|
||||
import org.apache.wicket.Component;
|
||||
import org.apache.wicket.RestartResponseAtInterceptPageException;
|
||||
@ -111,7 +111,7 @@ public abstract class IssueSidePanel extends Panel {
|
||||
protected void onBeforeRender() {
|
||||
addOrReplace(newFieldsContainer());
|
||||
addOrReplace(newConfidentialContainer());
|
||||
addOrReplace(newMilestonesContainer());
|
||||
addOrReplace(newIterationsContainer());
|
||||
addOrReplace(newLinksContainer());
|
||||
addOrReplace(newVotesContainer());
|
||||
|
||||
@ -593,8 +593,8 @@ public abstract class IssueSidePanel extends Panel {
|
||||
}
|
||||
}
|
||||
|
||||
private Component newMilestonesContainer() {
|
||||
WebMarkupContainer container = new WebMarkupContainer("milestones") {
|
||||
private Component newIterationsContainer() {
|
||||
WebMarkupContainer container = new WebMarkupContainer("iterations") {
|
||||
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
@ -604,24 +604,24 @@ public abstract class IssueSidePanel extends Panel {
|
||||
|
||||
};
|
||||
|
||||
container.add(new ListView<Milestone>("milestones", new AbstractReadOnlyModel<List<Milestone>>() {
|
||||
container.add(new ListView<Iteration>("iterations", new AbstractReadOnlyModel<List<Iteration>>() {
|
||||
|
||||
@Override
|
||||
public List<Milestone> getObject() {
|
||||
return getIssue().getMilestones().stream()
|
||||
.sorted(new Milestone.DatesAndStatusComparator())
|
||||
public List<Iteration> getObject() {
|
||||
return getIssue().getIterations().stream()
|
||||
.sorted(new Iteration.DatesAndStatusComparator())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}) {
|
||||
|
||||
@Override
|
||||
protected void populateItem(ListItem<Milestone> item) {
|
||||
Milestone milestone = item.getModelObject();
|
||||
protected void populateItem(ListItem<Iteration> item) {
|
||||
Iteration iteration = item.getModelObject();
|
||||
|
||||
Link<Void> link = new BookmarkablePageLink<Void>("link", MilestoneIssuesPage.class,
|
||||
MilestoneIssuesPage.paramsOf(getIssue().getProject(), milestone, null));
|
||||
link.add(new Label("label", milestone.getName()));
|
||||
Link<Void> link = new BookmarkablePageLink<Void>("link", IterationIssuesPage.class,
|
||||
IterationIssuesPage.paramsOf(getIssue().getProject(), iteration, null));
|
||||
link.add(new Label("label", iteration.getName()));
|
||||
item.add(link);
|
||||
|
||||
item.add(new StateStatsBar("progress", new AbstractReadOnlyModel<Map<String, Integer>>() {
|
||||
@ -636,16 +636,16 @@ public abstract class IssueSidePanel extends Panel {
|
||||
@Override
|
||||
protected Link<Void> newStateLink(String componentId, String state) {
|
||||
String query = new IssueQuery(new StateCriteria(state, IssueQueryLexer.Is)).toString();
|
||||
PageParameters params = MilestoneIssuesPage.paramsOf(getIssue().getProject(),
|
||||
PageParameters params = IterationIssuesPage.paramsOf(getIssue().getProject(),
|
||||
item.getModelObject(), query);
|
||||
return new ViewStateAwarePageLink<Void>(componentId, MilestoneIssuesPage.class, params);
|
||||
return new ViewStateAwarePageLink<Void>(componentId, IterationIssuesPage.class, params);
|
||||
}
|
||||
|
||||
});
|
||||
item.add(new MilestoneStatusLabel("status", new AbstractReadOnlyModel<Milestone>() {
|
||||
item.add(new IterationStatusLabel("status", new AbstractReadOnlyModel<Iteration>() {
|
||||
|
||||
@Override
|
||||
public Milestone getObject() {
|
||||
public Iteration getObject() {
|
||||
return item.getModelObject();
|
||||
}
|
||||
|
||||
@ -658,7 +658,7 @@ public abstract class IssueSidePanel extends Panel {
|
||||
super.updateAjaxAttributes(attributes);
|
||||
if (!getIssue().isNew()) {
|
||||
attributes.getAjaxCallListeners().add(new ConfirmClickListener("Do you really want to "
|
||||
+ "remove the issue from milestone '" + item.getModelObject().getName() + "'?"));
|
||||
+ "remove the issue from iteration '" + item.getModelObject().getName() + "'?"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -679,22 +679,22 @@ public abstract class IssueSidePanel extends Panel {
|
||||
|
||||
});
|
||||
|
||||
container.add(new SelectToActChoice<Milestone>("add", new AbstractMilestoneChoiceProvider() {
|
||||
container.add(new SelectToActChoice<Iteration>("add", new AbstractIterationChoiceProvider() {
|
||||
|
||||
@Override
|
||||
public void query(String term, int page, Response<Milestone> response) {
|
||||
List<Milestone> milestones = getProject().getSortedHierarchyMilestones();
|
||||
milestones.removeAll(getIssue().getMilestones());
|
||||
public void query(String term, int page, Response<Iteration> response) {
|
||||
List<Iteration> iterations = getProject().getSortedHierarchyIterations();
|
||||
iterations.removeAll(getIssue().getIterations());
|
||||
|
||||
milestones = new Similarities<Milestone>(milestones) {
|
||||
iterations = new Similarities<Iteration>(iterations) {
|
||||
|
||||
@Override
|
||||
public double getSimilarScore(Milestone object) {
|
||||
public double getSimilarScore(Iteration object) {
|
||||
return Similarities.getSimilarScore(object.getName(), term);
|
||||
}
|
||||
|
||||
};
|
||||
new ResponseFiller<>(response).fill(milestones, page, WebConstants.PAGE_SIZE);
|
||||
new ResponseFiller<>(response).fill(iterations, page, WebConstants.PAGE_SIZE);
|
||||
}
|
||||
|
||||
}) {
|
||||
@ -703,10 +703,10 @@ public abstract class IssueSidePanel extends Panel {
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
getSettings().setPlaceholder("Add to milestone...");
|
||||
getSettings().setFormatResult("onedev.server.milestoneChoiceFormatter.formatResult");
|
||||
getSettings().setFormatSelection("onedev.server.milestoneChoiceFormatter.formatSelection");
|
||||
getSettings().setEscapeMarkup("onedev.server.milestoneChoiceFormatter.escapeMarkup");
|
||||
getSettings().setPlaceholder("Add to iteration...");
|
||||
getSettings().setFormatResult("onedev.server.iterationChoiceFormatter.formatResult");
|
||||
getSettings().setFormatSelection("onedev.server.iterationChoiceFormatter.formatSelection");
|
||||
getSettings().setEscapeMarkup("onedev.server.iterationChoiceFormatter.escapeMarkup");
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -718,12 +718,12 @@ public abstract class IssueSidePanel extends Panel {
|
||||
@Override
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
super.renderHead(response);
|
||||
response.render(JavaScriptHeaderItem.forReference(new MilestoneChoiceResourceReference()));
|
||||
response.render(JavaScriptHeaderItem.forReference(new IterationChoiceResourceReference()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSelect(AjaxRequestTarget target, Milestone milestone) {
|
||||
getIssueChangeManager().addSchedule(getIssue(), milestone);
|
||||
protected void onSelect(AjaxRequestTarget target, Iteration iteration) {
|
||||
getIssueChangeManager().addSchedule(getIssue(), iteration);
|
||||
notifyIssueChange(target, getIssue());
|
||||
}
|
||||
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
.issue-side>.fields .field-values.editable {
|
||||
border-color: transparent;
|
||||
}
|
||||
.issue-side>.milestones li+li, .issue-side>.links li+li {
|
||||
.issue-side>.iterations li+li, .issue-side>.links li+li {
|
||||
margin-top: 1.2rem;
|
||||
}
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ public abstract class StateStatsBar extends GenericPanel<Map<String, Integer>> {
|
||||
|
||||
}.setEscapeModelStrings(false));
|
||||
|
||||
add(AttributeAppender.append("title", "No issues in milestone"));
|
||||
add(AttributeAppender.append("title", "No issues in iteration"));
|
||||
}
|
||||
|
||||
setOutputMarkupId(true);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package io.onedev.server.web.component.milestone;
|
||||
package io.onedev.server.web.component.iteration;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@ -7,43 +7,43 @@ import org.apache.wicket.markup.html.basic.Label;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.util.DateUtils;
|
||||
import io.onedev.server.web.component.svg.SpriteImage;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MilestoneDateLabel extends Label {
|
||||
public class IterationDateLabel extends Label {
|
||||
|
||||
private final IModel<Milestone> milestoneModel;
|
||||
private final IModel<Iteration> iterationIModel;
|
||||
|
||||
public MilestoneDateLabel(String id, IModel<Milestone> milestoneModel) {
|
||||
public IterationDateLabel(String id, IModel<Iteration> iterationIModel) {
|
||||
super(id, new LoadableDetachableModel<String>() {
|
||||
|
||||
@Override
|
||||
protected String load() {
|
||||
String arrow = "<svg class='icon'><use xlink:href='" + SpriteImage.getVersionedHref("arrow3") + "'/></svg>";
|
||||
Milestone milestone = milestoneModel.getObject();
|
||||
if (milestone.getStartDate() != null && milestone.getDueDate() != null) {
|
||||
Iteration iteration = iterationIModel.getObject();
|
||||
if (iteration.getStartDate() != null && iteration.getDueDate() != null) {
|
||||
return ""
|
||||
+ "<span title='Start date'>" + DateUtils.formatDate(milestone.getStartDate()) + "</span>"
|
||||
+ "<span title='Start date'>" + DateUtils.formatDate(iteration.getStartDate()) + "</span>"
|
||||
+ " " + arrow + " "
|
||||
+ "<span title='Due date'>" + DateUtils.formatDate(milestone.getDueDate()) + "</span>";
|
||||
} else if (milestone.getStartDate() != null) {
|
||||
return "<span title='Start date'>" + DateUtils.formatDate(milestone.getStartDate()) + "</span> " + arrow;
|
||||
} else if (milestone.getDueDate() != null) {
|
||||
return arrow + " <span title='Due date'>" + DateUtils.formatDate(milestone.getDueDate()) + "</span>";
|
||||
+ "<span title='Due date'>" + DateUtils.formatDate(iteration.getDueDate()) + "</span>";
|
||||
} else if (iteration.getStartDate() != null) {
|
||||
return "<span title='Start date'>" + DateUtils.formatDate(iteration.getStartDate()) + "</span> " + arrow;
|
||||
} else if (iteration.getDueDate() != null) {
|
||||
return arrow + " <span title='Due date'>" + DateUtils.formatDate(iteration.getDueDate()) + "</span>";
|
||||
} else {
|
||||
return "<i> No Start/Due Date</i>";
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
this.milestoneModel = milestoneModel;
|
||||
this.iterationIModel = iterationIModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach() {
|
||||
milestoneModel.detach();
|
||||
iterationIModel.detach();
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@ -55,23 +55,23 @@ public class MilestoneDateLabel extends Label {
|
||||
|
||||
@Override
|
||||
protected String load() {
|
||||
Milestone milestone = milestoneModel.getObject();
|
||||
if (!milestone.isClosed()) {
|
||||
Iteration iteration = iterationIModel.getObject();
|
||||
if (!iteration.isClosed()) {
|
||||
Date now = new Date();
|
||||
if (milestone.getStartDate() != null && milestone.getDueDate() != null) {
|
||||
if (now.before(milestone.getStartDate()))
|
||||
if (iteration.getStartDate() != null && iteration.getDueDate() != null) {
|
||||
if (now.before(iteration.getStartDate()))
|
||||
return "text-info";
|
||||
else if (now.after(milestone.getStartDate()) && now.before(milestone.getDueDate()))
|
||||
else if (now.after(iteration.getStartDate()) && now.before(iteration.getDueDate()))
|
||||
return "text-warning";
|
||||
else
|
||||
return "text-danger";
|
||||
} else if (milestone.getStartDate() != null) {
|
||||
if (now.before(milestone.getStartDate()))
|
||||
} else if (iteration.getStartDate() != null) {
|
||||
if (now.before(iteration.getStartDate()))
|
||||
return "text-info";
|
||||
else
|
||||
return "text-warning";
|
||||
} else if (milestone.getDueDate() != null) {
|
||||
if (now.before(milestone.getDueDate()))
|
||||
} else if (iteration.getDueDate() != null) {
|
||||
if (now.before(iteration.getDueDate()))
|
||||
return "text-info";
|
||||
else
|
||||
return "text-danger";
|
||||
@ -1,4 +1,4 @@
|
||||
package io.onedev.server.web.component.milestone;
|
||||
package io.onedev.server.web.component.iteration;
|
||||
|
||||
import org.apache.wicket.behavior.AttributeAppender;
|
||||
import org.apache.wicket.markup.html.basic.Label;
|
||||
@ -6,28 +6,28 @@ import org.apache.wicket.model.AbstractReadOnlyModel;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.LoadableDetachableModel;
|
||||
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MilestoneStatusLabel extends Label {
|
||||
public class IterationStatusLabel extends Label {
|
||||
|
||||
private final IModel<Milestone> milestoneModel;
|
||||
private final IModel<Iteration> iterationModel;
|
||||
|
||||
public MilestoneStatusLabel(String id, IModel<Milestone> milestoneModel) {
|
||||
public IterationStatusLabel(String id, IModel<Iteration> iterationModel) {
|
||||
super(id, new LoadableDetachableModel<String>() {
|
||||
|
||||
@Override
|
||||
protected String load() {
|
||||
return milestoneModel.getObject().getStatusName();
|
||||
return iterationModel.getObject().getStatusName();
|
||||
}
|
||||
|
||||
});
|
||||
this.milestoneModel = milestoneModel;
|
||||
this.iterationModel = iterationModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach() {
|
||||
milestoneModel.detach();
|
||||
iterationModel.detach();
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ public class MilestoneStatusLabel extends Label {
|
||||
|
||||
@Override
|
||||
public String getObject() {
|
||||
return "badge badge-" + (milestoneModel.getObject().isClosed()? "success": "warning");
|
||||
return "badge badge-" + (iterationModel.getObject().isClosed()? "success": "warning");
|
||||
}
|
||||
|
||||
}));
|
||||
@ -0,0 +1,10 @@
|
||||
<wicket:panel>
|
||||
<span class="actions d-inline-flex align-items-center">
|
||||
<a wicket:id="reopen" title="Reopen this iteration">Reopen</a>
|
||||
<a wicket:id="close" title="Close this iteration">Close</a>
|
||||
<span class="dot mx-2"></span>
|
||||
<a wicket:id="edit" title="Edit this iteration">Edit</a>
|
||||
<span class="dot mx-2"></span>
|
||||
<a wicket:id="delete" title="Delete this iteration">Delete</a>
|
||||
</span>
|
||||
</wicket:panel>
|
||||
@ -1,4 +1,4 @@
|
||||
package io.onedev.server.web.component.milestone.actions;
|
||||
package io.onedev.server.web.component.iteration.actions;
|
||||
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
|
||||
@ -8,19 +8,19 @@ import org.apache.wicket.markup.html.panel.GenericPanel;
|
||||
import org.apache.wicket.model.IModel;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.entitymanager.IterationManager;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.web.ajaxlistener.ConfirmClickListener;
|
||||
import io.onedev.server.web.page.project.issues.milestones.MilestoneEditPage;
|
||||
import io.onedev.server.web.page.project.issues.iteration.IterationEditPage;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class MilestoneActionsPanel extends GenericPanel<Milestone> {
|
||||
public abstract class IterationActionsPanel extends GenericPanel<Iteration> {
|
||||
|
||||
public MilestoneActionsPanel(String id, IModel<Milestone> model) {
|
||||
public IterationActionsPanel(String id, IModel<Iteration> model) {
|
||||
super(id, model);
|
||||
}
|
||||
|
||||
private Milestone getMilestone() {
|
||||
private Iteration getIteration() {
|
||||
return getModelObject();
|
||||
}
|
||||
|
||||
@ -32,17 +32,17 @@ public abstract class MilestoneActionsPanel extends GenericPanel<Milestone> {
|
||||
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
getMilestone().setClosed(false);
|
||||
getMilestoneManager().createOrUpdate(getMilestone());
|
||||
target.add(MilestoneActionsPanel.this);
|
||||
getIteration().setClosed(false);
|
||||
getIterationManager().createOrUpdate(getIteration());
|
||||
target.add(IterationActionsPanel.this);
|
||||
onUpdated(target);
|
||||
getSession().success("Milestone '" + getMilestone().getName() + "' reopened");
|
||||
getSession().success("Iteratioin '" + getIteration().getName() + "' reopened");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
super.onConfigure();
|
||||
setVisible(getMilestone().isClosed());
|
||||
setVisible(getIteration().isClosed());
|
||||
}
|
||||
|
||||
});
|
||||
@ -52,22 +52,22 @@ public abstract class MilestoneActionsPanel extends GenericPanel<Milestone> {
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
super.onConfigure();
|
||||
setVisible(!getMilestone().isClosed());
|
||||
setVisible(!getIteration().isClosed());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
getMilestone().setClosed(true);
|
||||
getMilestoneManager().createOrUpdate(getMilestone());
|
||||
target.add(MilestoneActionsPanel.this);
|
||||
getIteration().setClosed(true);
|
||||
getIterationManager().createOrUpdate(getIteration());
|
||||
target.add(IterationActionsPanel.this);
|
||||
onUpdated(target);
|
||||
getSession().success("Milestone '" + getMilestone().getName() + "' closed");
|
||||
getSession().success("Iteration '" + getIteration().getName() + "' closed");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
add(new BookmarkablePageLink<Void>("edit", MilestoneEditPage.class,
|
||||
MilestoneEditPage.paramsOf(getMilestone())));
|
||||
add(new BookmarkablePageLink<Void>("edit", IterationEditPage.class,
|
||||
IterationEditPage.paramsOf(getIteration())));
|
||||
|
||||
add(new AjaxLink<Void>("delete") {
|
||||
|
||||
@ -75,15 +75,15 @@ public abstract class MilestoneActionsPanel extends GenericPanel<Milestone> {
|
||||
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
|
||||
super.updateAjaxAttributes(attributes);
|
||||
attributes.getAjaxCallListeners().add(new ConfirmClickListener(
|
||||
"Do you really want to delete milestone '" + getMilestone().getName() + "'?"));
|
||||
"Do you really want to delete iteration '" + getIteration().getName() + "'?"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
getMilestoneManager().delete(getMilestone());
|
||||
target.add(MilestoneActionsPanel.this);
|
||||
getIterationManager().delete(getIteration());
|
||||
target.add(IterationActionsPanel.this);
|
||||
onDeleted(target);
|
||||
getSession().success("Milestone '" + getMilestone().getName() + "' deleted");
|
||||
getSession().success("Iteration '" + getIteration().getName() + "' deleted");
|
||||
}
|
||||
|
||||
});
|
||||
@ -91,8 +91,8 @@ public abstract class MilestoneActionsPanel extends GenericPanel<Milestone> {
|
||||
setOutputMarkupId(true);
|
||||
}
|
||||
|
||||
private MilestoneManager getMilestoneManager() {
|
||||
return OneDev.getInstance(MilestoneManager.class);
|
||||
private IterationManager getIterationManager() {
|
||||
return OneDev.getInstance(IterationManager.class);
|
||||
}
|
||||
|
||||
protected abstract void onDeleted(AjaxRequestTarget target);
|
||||
@ -1,4 +1,4 @@
|
||||
package io.onedev.server.web.component.milestone.burndown;
|
||||
package io.onedev.server.web.component.iteration.burndown;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.buildspecmodel.inputspec.InputSpec;
|
||||
@ -1,18 +1,17 @@
|
||||
package io.onedev.server.web.component.milestone.burndown;
|
||||
package io.onedev.server.web.component.iteration.burndown;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.xodus.IssueInfoManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.support.administration.GlobalIssueSetting;
|
||||
import io.onedev.server.model.support.issue.StateSpec;
|
||||
import io.onedev.server.model.support.issue.field.spec.WorkingPeriodField;
|
||||
import io.onedev.server.web.component.chart.line.Line;
|
||||
import io.onedev.server.web.component.chart.line.LineChartPanel;
|
||||
import io.onedev.server.web.component.chart.line.LineSeries;
|
||||
import org.apache.wicket.behavior.AttributeAppender;
|
||||
import org.apache.wicket.markup.html.basic.Label;
|
||||
import org.apache.wicket.markup.html.panel.Fragment;
|
||||
import org.apache.wicket.markup.html.panel.GenericPanel;
|
||||
@ -25,16 +24,16 @@ import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
|
||||
import static io.onedev.server.util.DateUtils.toLocalDate;
|
||||
import static io.onedev.server.web.component.milestone.burndown.BurndownIndicators.*;
|
||||
import static io.onedev.server.web.component.iteration.burndown.BurndownIndicators.*;
|
||||
|
||||
public class MilestoneBurndownPanel extends GenericPanel<Milestone> {
|
||||
public class IterationBurndownPanel extends GenericPanel<Iteration> {
|
||||
|
||||
private static final int MAX_DAYS = 365;
|
||||
|
||||
private final String indicator;
|
||||
|
||||
public MilestoneBurndownPanel(String id, IModel<Milestone> milestoneModel, @Nullable String indicator) {
|
||||
super(id, milestoneModel);
|
||||
public IterationBurndownPanel(String id, IModel<Iteration> iterationModel, @Nullable String indicator) {
|
||||
super(id, iterationModel);
|
||||
this.indicator = indicator;
|
||||
}
|
||||
|
||||
@ -47,17 +46,17 @@ public class MilestoneBurndownPanel extends GenericPanel<Milestone> {
|
||||
super.onInitialize();
|
||||
|
||||
String message = null;
|
||||
if (getMilestone().getStartDate() != null && getMilestone().getDueDate() != null) {
|
||||
if (getMilestone().getStartDate().before(getMilestone().getDueDate())) {
|
||||
long startDay = toLocalDate(getMilestone().getStartDate()).toEpochDay();
|
||||
long dueDay = toLocalDate(getMilestone().getDueDate()).toEpochDay();
|
||||
if (getIteration().getStartDate() != null && getIteration().getDueDate() != null) {
|
||||
if (getIteration().getStartDate().before(getIteration().getDueDate())) {
|
||||
long startDay = toLocalDate(getIteration().getStartDate()).toEpochDay();
|
||||
long dueDay = toLocalDate(getIteration().getDueDate()).toEpochDay();
|
||||
if (dueDay - startDay >= MAX_DAYS)
|
||||
message = "Milestone spans too long to show burndown chart";
|
||||
message = "Iteration spans too long to show burndown chart";
|
||||
} else {
|
||||
message = "Milestone start date should be before due date";
|
||||
message = "Iteration start date should be before due date";
|
||||
}
|
||||
} else {
|
||||
message = "Milestone start and due date should be specified to show burndown chart";
|
||||
message = "Iteration start and due date should be specified to show burndown chart";
|
||||
}
|
||||
if (message != null) {
|
||||
var fragment = new Fragment("content", "messageFrag", this);
|
||||
@ -73,11 +72,11 @@ public class MilestoneBurndownPanel extends GenericPanel<Milestone> {
|
||||
|
||||
@Override
|
||||
protected LineSeries load() {
|
||||
long startDay = toLocalDate(getMilestone().getStartDate()).toEpochDay();
|
||||
long dueDay = toLocalDate(getMilestone().getDueDate()).toEpochDay();
|
||||
long startDay = toLocalDate(getIteration().getStartDate()).toEpochDay();
|
||||
long dueDay = toLocalDate(getIteration().getDueDate()).toEpochDay();
|
||||
|
||||
Map<Long, Map<String, Integer>> dailyStateMetrics = new LinkedHashMap<>();
|
||||
for (IssueSchedule schedule : getMilestone().getSchedules()) {
|
||||
for (IssueSchedule schedule : getIteration().getSchedules()) {
|
||||
Issue issue = schedule.getIssue();
|
||||
long scheduleDay = toLocalDate(schedule.getDate()).toEpochDay();
|
||||
|
||||
@ -186,7 +185,7 @@ public class MilestoneBurndownPanel extends GenericPanel<Milestone> {
|
||||
if (indicator != null)
|
||||
return indicator;
|
||||
else
|
||||
return getDefault(getMilestone().getProject());
|
||||
return getDefault(getIteration().getProject());
|
||||
}
|
||||
|
||||
private int getIndicatorValue(Issue issue) {
|
||||
@ -207,7 +206,7 @@ public class MilestoneBurndownPanel extends GenericPanel<Milestone> {
|
||||
}
|
||||
}
|
||||
|
||||
private Milestone getMilestone() {
|
||||
private Iteration getIteration() {
|
||||
return getModelObject();
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package io.onedev.server.web.component.milestone.choice;
|
||||
package io.onedev.server.web.component.iteration.choice;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@ -11,16 +11,16 @@ import org.unbescape.html.HtmlEscape;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.entitymanager.IterationManager;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.web.component.select2.ChoiceProvider;
|
||||
|
||||
public abstract class AbstractMilestoneChoiceProvider extends ChoiceProvider<Milestone> {
|
||||
public abstract class AbstractIterationChoiceProvider extends ChoiceProvider<Iteration> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public void toJson(Milestone choice, JSONWriter writer) throws JSONException {
|
||||
public void toJson(Iteration choice, JSONWriter writer) throws JSONException {
|
||||
writer.key("id").value(choice.getId()).key("name").value(HtmlEscape.escapeHtml5(choice.getName()));
|
||||
writer.key("statusName").value(choice.getStatusName());
|
||||
if (choice.isClosed())
|
||||
@ -30,16 +30,16 @@ public abstract class AbstractMilestoneChoiceProvider extends ChoiceProvider<Mil
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Milestone> toChoices(Collection<String> ids) {
|
||||
List<Milestone> milestones = Lists.newArrayList();
|
||||
MilestoneManager milestoneManager = OneDev.getInstance(MilestoneManager.class);
|
||||
public Collection<Iteration> toChoices(Collection<String> ids) {
|
||||
List<Iteration> iterations = Lists.newArrayList();
|
||||
IterationManager iterationManager = OneDev.getInstance(IterationManager.class);
|
||||
for (String each : ids) {
|
||||
Milestone milestone = milestoneManager.load(Long.valueOf(each));
|
||||
Hibernate.initialize(milestone);
|
||||
milestones.add(milestone);
|
||||
Iteration iteration = iterationManager.load(Long.valueOf(each));
|
||||
Hibernate.initialize(iteration);
|
||||
iterations.add(iteration);
|
||||
}
|
||||
|
||||
return milestones;
|
||||
return iterations;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,23 +1,23 @@
|
||||
package io.onedev.server.web.component.milestone.choice;
|
||||
package io.onedev.server.web.component.iteration.choice;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.wicket.model.IModel;
|
||||
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.util.Similarities;
|
||||
import io.onedev.server.web.WebConstants;
|
||||
import io.onedev.server.web.component.select2.Response;
|
||||
import io.onedev.server.web.component.select2.ResponseFiller;
|
||||
|
||||
public class MilestoneChoiceProvider extends AbstractMilestoneChoiceProvider {
|
||||
public class IterationChoiceProvider extends AbstractIterationChoiceProvider {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final IModel<Collection<Milestone>> choicesModel;
|
||||
private final IModel<Collection<Iteration>> choicesModel;
|
||||
|
||||
public MilestoneChoiceProvider(IModel<Collection<Milestone>> choicesModel) {
|
||||
public IterationChoiceProvider(IModel<Collection<Iteration>> choicesModel) {
|
||||
this.choicesModel = choicesModel;
|
||||
}
|
||||
|
||||
@ -28,18 +28,18 @@ public class MilestoneChoiceProvider extends AbstractMilestoneChoiceProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void query(String term, int page, Response<Milestone> response) {
|
||||
List<Milestone> milestones = new Similarities<Milestone>(choicesModel.getObject()) {
|
||||
public void query(String term, int page, Response<Iteration> response) {
|
||||
List<Iteration> iterations = new Similarities<Iteration>(choicesModel.getObject()) {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public double getSimilarScore(Milestone object) {
|
||||
public double getSimilarScore(Iteration object) {
|
||||
return Similarities.getSimilarScore(object.getName(), term);
|
||||
}
|
||||
|
||||
};
|
||||
new ResponseFiller<Milestone>(response).fill(milestones, page, WebConstants.PAGE_SIZE);
|
||||
new ResponseFiller<Iteration>(response).fill(iterations, page, WebConstants.PAGE_SIZE);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package io.onedev.server.web.component.milestone.choice;
|
||||
package io.onedev.server.web.component.iteration.choice;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -8,19 +8,19 @@ import org.apache.wicket.request.resource.CssResourceReference;
|
||||
|
||||
import io.onedev.server.web.page.base.BaseDependentResourceReference;
|
||||
|
||||
public class MilestoneChoiceResourceReference extends BaseDependentResourceReference {
|
||||
public class IterationChoiceResourceReference extends BaseDependentResourceReference {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MilestoneChoiceResourceReference() {
|
||||
super(MilestoneChoiceResourceReference.class, "milestone-choice.js");
|
||||
public IterationChoiceResourceReference() {
|
||||
super(IterationChoiceResourceReference.class, "iteration-choice.js");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeaderItem> getDependencies() {
|
||||
List<HeaderItem> dependencies = super.getDependencies();
|
||||
dependencies.add(CssHeaderItem.forReference(
|
||||
new CssResourceReference(MilestoneChoiceResourceReference.class, "milestone-choice.css")));
|
||||
new CssResourceReference(IterationChoiceResourceReference.class, "iteration-choice.css")));
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
package io.onedev.server.web.component.iteration.choice;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.wicket.markup.head.IHeaderResponse;
|
||||
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
|
||||
import org.apache.wicket.model.IModel;
|
||||
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.web.component.select2.Select2MultiChoice;
|
||||
|
||||
public class IterationMultiChoice extends Select2MultiChoice<Iteration> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public IterationMultiChoice(String id, IModel<Collection<Iteration>> selectionsModel, IModel<Collection<Iteration>>choicesModel) {
|
||||
super(id, selectionsModel, new IterationChoiceProvider(choicesModel));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
if (isRequired())
|
||||
getSettings().setPlaceholder("Choose iterations...");
|
||||
else
|
||||
getSettings().setPlaceholder("Not specified");
|
||||
getSettings().setFormatResult("onedev.server.iterationChoiceFormatter.formatResult");
|
||||
getSettings().setFormatSelection("onedev.server.iterationChoiceFormatter.formatSelection");
|
||||
getSettings().setEscapeMarkup("onedev.server.iterationChoiceFormatter.escapeMarkup");
|
||||
setConvertEmptyInputStringToNull(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
super.renderHead(response);
|
||||
|
||||
response.render(JavaScriptHeaderItem.forReference(new IterationChoiceResourceReference()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package io.onedev.server.web.component.iteration.choice;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.wicket.markup.head.IHeaderResponse;
|
||||
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
|
||||
import org.apache.wicket.model.IModel;
|
||||
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.web.component.select2.Select2Choice;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class IterationSingleChoice extends Select2Choice<Iteration> {
|
||||
|
||||
public IterationSingleChoice(String id, IModel<Iteration> selectionModel, IModel<Collection<Iteration>> iterationsModel) {
|
||||
super(id, selectionModel, new IterationChoiceProvider(iterationsModel));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
getSettings().setAllowClear(!isRequired());
|
||||
if (isRequired())
|
||||
getSettings().setPlaceholder("Choose iteration...");
|
||||
else
|
||||
getSettings().setPlaceholder("Not specified");
|
||||
getSettings().setFormatResult("onedev.server.iterationChoiceFormatter.formatResult");
|
||||
getSettings().setFormatSelection("onedev.server.iterationChoiceFormatter.formatSelection");
|
||||
getSettings().setEscapeMarkup("onedev.server.iterationChoiceFormatter.escapeMarkup");
|
||||
setConvertEmptyInputStringToNull(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
super.renderHead(response);
|
||||
|
||||
response.render(JavaScriptHeaderItem.forReference(new IterationChoiceResourceReference()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
.select2-results .iteration .label {
|
||||
margin-left: 6px;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
onedev.server.iterationChoiceFormatter = {
|
||||
formatSelection: function(iteration) {
|
||||
return iteration.name;
|
||||
},
|
||||
|
||||
formatResult: function(iteration) {
|
||||
return "<span class='iteration'>" + iteration.name + " <span class='ml-2 badge " + iteration.statusClass + "'>" + iteration.statusName + "</span></span>";
|
||||
},
|
||||
|
||||
escapeMarkup: function(m) {
|
||||
return m;
|
||||
},
|
||||
|
||||
};
|
||||
@ -1,5 +1,5 @@
|
||||
<wicket:panel>
|
||||
<div class="card milestone-list">
|
||||
<div class="card iteration-list">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center flex-wrap">
|
||||
<span wicket:id="states" class="btn-group mr-4 mb-4">
|
||||
@ -9,9 +9,9 @@
|
||||
<div class="mr-4 mb-4">
|
||||
<a wicket:id="sort" class="btn btn-outline-secondary">Sort <wicket:svg href="arrow" class="icon rotate-90"></wicket:svg></a>
|
||||
</div>
|
||||
<a wicket:id="newMilestone" class="btn btn-primary btn-icon mb-4"><wicket:svg href="plus" class="icon mr-1"></wicket:svg></a>
|
||||
<a wicket:id="newIteration" class="btn btn-primary btn-icon mb-4"><wicket:svg href="plus" class="icon mr-1"></wicket:svg></a>
|
||||
</div>
|
||||
<table wicket:id="milestones" class="table"></table>
|
||||
<table wicket:id="iterations" class="table"></table>
|
||||
</div>
|
||||
</div>
|
||||
<wicket:fragment wicket:id="issueStatsFrag">
|
||||
@ -1,9 +1,9 @@
|
||||
package io.onedev.server.web.component.milestone.list;
|
||||
package io.onedev.server.web.component.iteration.list;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.IssueManager;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.entitymanager.IterationManager;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import io.onedev.server.persistence.dao.EntityCriteria;
|
||||
@ -11,8 +11,8 @@ import io.onedev.server.search.entity.issue.IssueQuery;
|
||||
import io.onedev.server.search.entity.issue.IssueQueryLexer;
|
||||
import io.onedev.server.search.entity.issue.StateCriteria;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.util.MilestoneAndIssueState;
|
||||
import io.onedev.server.util.MilestoneSort;
|
||||
import io.onedev.server.util.IterationAndIssueState;
|
||||
import io.onedev.server.util.IterationSort;
|
||||
import io.onedev.server.web.WebConstants;
|
||||
import io.onedev.server.web.WebSession;
|
||||
import io.onedev.server.web.component.datatable.DefaultDataTable;
|
||||
@ -22,10 +22,10 @@ import io.onedev.server.web.component.link.ActionablePageLink;
|
||||
import io.onedev.server.web.component.link.ViewStateAwarePageLink;
|
||||
import io.onedev.server.web.component.menu.MenuItem;
|
||||
import io.onedev.server.web.component.menu.MenuLink;
|
||||
import io.onedev.server.web.component.milestone.MilestoneDateLabel;
|
||||
import io.onedev.server.web.component.milestone.actions.MilestoneActionsPanel;
|
||||
import io.onedev.server.web.page.project.issues.milestones.MilestoneIssuesPage;
|
||||
import io.onedev.server.web.page.project.issues.milestones.NewMilestonePage;
|
||||
import io.onedev.server.web.component.iteration.IterationDateLabel;
|
||||
import io.onedev.server.web.component.iteration.actions.IterationActionsPanel;
|
||||
import io.onedev.server.web.page.project.issues.iteration.IterationIssuesPage;
|
||||
import io.onedev.server.web.page.project.issues.iteration.NewIterationPage;
|
||||
import io.onedev.server.web.util.LoadableDetachableDataProvider;
|
||||
import io.onedev.server.web.util.PagingHistorySupport;
|
||||
import org.apache.wicket.Component;
|
||||
@ -56,40 +56,40 @@ import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
public class IterationListPanel extends GenericPanel<Project> {
|
||||
|
||||
private boolean closed;
|
||||
|
||||
private MilestoneSort sort;
|
||||
private IterationSort sort;
|
||||
|
||||
private final PagingHistorySupport pagingHistorySupport;
|
||||
|
||||
private final IModel<Collection<MilestoneAndIssueState>> milestoneAndStatesModel =
|
||||
private final IModel<Collection<IterationAndIssueState>> iterationAndStatesModel =
|
||||
new LoadableDetachableModel<>() {
|
||||
|
||||
@Override
|
||||
protected Collection<MilestoneAndIssueState> load() {
|
||||
List<Milestone> milestones = new ArrayList<>();
|
||||
for (Component row : (WebMarkupContainer) milestonesTable.get("body").get("rows")) {
|
||||
Milestone milestone = (Milestone) row.getDefaultModelObject();
|
||||
milestones.add(milestone);
|
||||
protected Collection<IterationAndIssueState> load() {
|
||||
List<Iteration> iterations = new ArrayList<>();
|
||||
for (Component row : (WebMarkupContainer) iterationsTable.get("body").get("rows")) {
|
||||
Iteration iteration = (Iteration) row.getDefaultModelObject();
|
||||
iterations.add(iteration);
|
||||
}
|
||||
return OneDev.getInstance(IssueManager.class).queryMilestoneAndIssueStates(getProject(), milestones);
|
||||
return OneDev.getInstance(IssueManager.class).queryIterationAndIssueStates(getProject(), iterations);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private DataTable<Milestone, Void> milestonesTable;
|
||||
private DataTable<Iteration, Void> iterationsTable;
|
||||
|
||||
private EntityCriteria<Milestone> getCriteria(boolean closed) {
|
||||
EntityCriteria<Milestone> criteria = EntityCriteria.of(Milestone.class);
|
||||
private EntityCriteria<Iteration> getCriteria(boolean closed) {
|
||||
EntityCriteria<Iteration> criteria = EntityCriteria.of(Iteration.class);
|
||||
criteria.add(Restrictions.in("project", getProject().getSelfAndAncestors()));
|
||||
criteria.add(Restrictions.eq("closed", closed));
|
||||
return criteria;
|
||||
}
|
||||
|
||||
public MilestoneListPanel(String id, IModel<Project> model, boolean closed, MilestoneSort sort,
|
||||
@Nullable PagingHistorySupport pagingHistorySupport) {
|
||||
public IterationListPanel(String id, IModel<Project> model, boolean closed, IterationSort sort,
|
||||
@Nullable PagingHistorySupport pagingHistorySupport) {
|
||||
super(id, model);
|
||||
|
||||
this.closed = closed;
|
||||
@ -111,7 +111,7 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
closed = false;
|
||||
target.add(statesContainer);
|
||||
target.add(milestonesTable);
|
||||
target.add(iterationsTable);
|
||||
onStateChanged(target, closed);
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
closed = true;
|
||||
target.add(statesContainer);
|
||||
target.add(milestonesTable);
|
||||
target.add(iterationsTable);
|
||||
onStateChanged(target, closed);
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
@Override
|
||||
protected List<MenuItem> getMenuItems(FloatingPanel dropdown) {
|
||||
List<MenuItem> menuItems = new ArrayList<>();
|
||||
for (MilestoneSort sort: MilestoneSort.values()) {
|
||||
for (IterationSort sort: IterationSort.values()) {
|
||||
menuItems.add(new MenuItem() {
|
||||
|
||||
@Override
|
||||
@ -162,7 +162,7 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
|
||||
@Override
|
||||
public String getIconHref() {
|
||||
if (sort == MilestoneListPanel.this.sort)
|
||||
if (sort == IterationListPanel.this.sort)
|
||||
return "tick";
|
||||
else
|
||||
return null;
|
||||
@ -175,14 +175,14 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
dropdown.close();
|
||||
target.add(milestonesTable);
|
||||
MilestoneListPanel.this.sort = sort;
|
||||
target.add(iterationsTable);
|
||||
IterationListPanel.this.sort = sort;
|
||||
onSortChanged(target, sort);
|
||||
}
|
||||
|
||||
};
|
||||
link.add(AttributeAppender.append("class", "milestone-sort"));
|
||||
if (sort == MilestoneListPanel.this.sort)
|
||||
link.add(AttributeAppender.append("class", "iteration-sort"));
|
||||
if (sort == IterationListPanel.this.sort)
|
||||
link.add(AttributeAppender.append("class", "active"));
|
||||
return link;
|
||||
}
|
||||
@ -194,7 +194,7 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
|
||||
});
|
||||
|
||||
add(new BookmarkablePageLink<Void>("newMilestone", NewMilestonePage.class, NewMilestonePage.paramsOf(getProject())) {
|
||||
add(new BookmarkablePageLink<Void>("newIteration", NewIterationPage.class, NewIterationPage.paramsOf(getProject())) {
|
||||
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
@ -204,7 +204,7 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
|
||||
});
|
||||
|
||||
List<IColumn<Milestone, Void>> columns = new ArrayList<>();
|
||||
List<IColumn<Iteration, Void>> columns = new ArrayList<>();
|
||||
|
||||
columns.add(new AbstractColumn<>(Model.of("Name")) {
|
||||
|
||||
@ -214,22 +214,22 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateItem(Item<ICellPopulator<Milestone>> cellItem, String componentId,
|
||||
IModel<Milestone> rowModel) {
|
||||
Milestone milestone = rowModel.getObject();
|
||||
Fragment fragment = new Fragment(componentId, "nameFrag", MilestoneListPanel.this);
|
||||
WebMarkupContainer link = new ActionablePageLink("link", MilestoneIssuesPage.class,
|
||||
MilestoneIssuesPage.paramsOf(getProject(), milestone, null)) {
|
||||
public void populateItem(Item<ICellPopulator<Iteration>> cellItem, String componentId,
|
||||
IModel<Iteration> rowModel) {
|
||||
Iteration iteration = rowModel.getObject();
|
||||
Fragment fragment = new Fragment(componentId, "nameFrag", IterationListPanel.this);
|
||||
WebMarkupContainer link = new ActionablePageLink("link", IterationIssuesPage.class,
|
||||
IterationIssuesPage.paramsOf(getProject(), iteration, null)) {
|
||||
|
||||
@Override
|
||||
protected void doBeforeNav(AjaxRequestTarget target) {
|
||||
String redirectUrlAfterDelete = RequestCycle.get().urlFor(
|
||||
getPage().getClass(), getPage().getPageParameters()).toString();
|
||||
WebSession.get().setRedirectUrlAfterDelete(Milestone.class, redirectUrlAfterDelete);
|
||||
WebSession.get().setRedirectUrlAfterDelete(Iteration.class, redirectUrlAfterDelete);
|
||||
}
|
||||
|
||||
};
|
||||
link.add(new Label("label", milestone.getName()));
|
||||
link.add(new Label("label", iteration.getName()));
|
||||
fragment.add(link);
|
||||
fragment.add(new WebMarkupContainer("inherited") {
|
||||
|
||||
@ -253,9 +253,9 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateItem(Item<ICellPopulator<Milestone>> cellItem, String componentId,
|
||||
IModel<Milestone> rowModel) {
|
||||
cellItem.add(new MilestoneDateLabel(componentId, rowModel));
|
||||
public void populateItem(Item<ICellPopulator<Iteration>> cellItem, String componentId,
|
||||
IModel<Iteration> rowModel) {
|
||||
cellItem.add(new IterationDateLabel(componentId, rowModel));
|
||||
}
|
||||
|
||||
});
|
||||
@ -268,29 +268,29 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateItem(Item<ICellPopulator<Milestone>> cellItem, String componentId,
|
||||
IModel<Milestone> rowModel) {
|
||||
Fragment fragment = new Fragment(componentId, "issueStatsFrag", MilestoneListPanel.this) {
|
||||
public void populateItem(Item<ICellPopulator<Iteration>> cellItem, String componentId,
|
||||
IModel<Iteration> rowModel) {
|
||||
Fragment fragment = new Fragment(componentId, "issueStatsFrag", IterationListPanel.this) {
|
||||
|
||||
@Override
|
||||
protected void onBeforeRender() {
|
||||
/*
|
||||
* Create StateStatsBar here as it requires to access the milestoneAndStatsModel which can
|
||||
* only be calculated correctly after the milestone table is initialized
|
||||
* Create StateStatsBar here as it requires to access the iterationAndStatsModel which can
|
||||
* only be calculated correctly after the iteration table is initialized
|
||||
*/
|
||||
addOrReplace(new StateStatsBar("content", new LoadableDetachableModel<Map<String, Integer>>() {
|
||||
|
||||
@Override
|
||||
protected Map<String, Integer> load() {
|
||||
Map<String, Integer> stateStats = new HashMap<>();
|
||||
for (MilestoneAndIssueState milestoneAndState : milestoneAndStatesModel.getObject()) {
|
||||
if (milestoneAndState.getMilestoneId().equals(rowModel.getObject().getId())) {
|
||||
Integer count = stateStats.get(milestoneAndState.getIssueState());
|
||||
for (IterationAndIssueState iterationAndState : iterationAndStatesModel.getObject()) {
|
||||
if (iterationAndState.getIterationId().equals(rowModel.getObject().getId())) {
|
||||
Integer count = stateStats.get(iterationAndState.getIssueState());
|
||||
if (count != null)
|
||||
count++;
|
||||
else
|
||||
count = 1;
|
||||
stateStats.put(milestoneAndState.getIssueState(), count);
|
||||
stateStats.put(iterationAndState.getIssueState(), count);
|
||||
}
|
||||
}
|
||||
return stateStats;
|
||||
@ -301,8 +301,8 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
@Override
|
||||
protected Link<Void> newStateLink(String componentId, String state) {
|
||||
String query = new IssueQuery(new StateCriteria(state, IssueQueryLexer.Is)).toString();
|
||||
PageParameters params = MilestoneIssuesPage.paramsOf(getProject(), rowModel.getObject(), query);
|
||||
return new ViewStateAwarePageLink<Void>(componentId, MilestoneIssuesPage.class, params);
|
||||
PageParameters params = IterationIssuesPage.paramsOf(getProject(), rowModel.getObject(), query);
|
||||
return new ViewStateAwarePageLink<Void>(componentId, IterationIssuesPage.class, params);
|
||||
}
|
||||
|
||||
});
|
||||
@ -324,19 +324,19 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateItem(Item<ICellPopulator<Milestone>> cellItem, String componentId,
|
||||
IModel<Milestone> rowModel) {
|
||||
public void populateItem(Item<ICellPopulator<Iteration>> cellItem, String componentId,
|
||||
IModel<Iteration> rowModel) {
|
||||
if (rowModel.getObject().getProject().equals(getProject())) {
|
||||
cellItem.add(new MilestoneActionsPanel(componentId, rowModel) {
|
||||
cellItem.add(new IterationActionsPanel(componentId, rowModel) {
|
||||
|
||||
@Override
|
||||
protected void onUpdated(AjaxRequestTarget target) {
|
||||
target.add(milestonesTable);
|
||||
target.add(iterationsTable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeleted(AjaxRequestTarget target) {
|
||||
target.add(milestonesTable);
|
||||
target.add(iterationsTable);
|
||||
}
|
||||
|
||||
});
|
||||
@ -348,11 +348,11 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
});
|
||||
}
|
||||
|
||||
SortableDataProvider<Milestone, Void> dataProvider = new LoadableDetachableDataProvider<>() {
|
||||
SortableDataProvider<Iteration, Void> dataProvider = new LoadableDetachableDataProvider<>() {
|
||||
|
||||
@Override
|
||||
public Iterator<? extends Milestone> iterator(long first, long count) {
|
||||
EntityCriteria<Milestone> criteria = getCriteria(closed);
|
||||
public Iterator<? extends Iteration> iterator(long first, long count) {
|
||||
EntityCriteria<Iteration> criteria = getCriteria(closed);
|
||||
criteria.addOrder(sort.getOrder(closed));
|
||||
return OneDev.getInstance(Dao.class).query(criteria, (int) first, (int) count).iterator();
|
||||
}
|
||||
@ -363,27 +363,27 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IModel<Milestone> model(Milestone object) {
|
||||
public IModel<Iteration> model(Iteration object) {
|
||||
Long id = object.getId();
|
||||
return new LoadableDetachableModel<>() {
|
||||
|
||||
@Override
|
||||
protected Milestone load() {
|
||||
return OneDev.getInstance(MilestoneManager.class).load(id);
|
||||
protected Iteration load() {
|
||||
return OneDev.getInstance(IterationManager.class).load(id);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
add(milestonesTable = new DefaultDataTable<>("milestones", columns, dataProvider,
|
||||
add(iterationsTable = new DefaultDataTable<>("iterations", columns, dataProvider,
|
||||
WebConstants.PAGE_SIZE, pagingHistorySupport));
|
||||
milestonesTable.setOutputMarkupId(true);
|
||||
iterationsTable.setOutputMarkupId(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetach() {
|
||||
milestoneAndStatesModel.detach();
|
||||
iterationAndStatesModel.detach();
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@ -391,7 +391,7 @@ public class MilestoneListPanel extends GenericPanel<Project> {
|
||||
return getModelObject();
|
||||
}
|
||||
|
||||
protected void onSortChanged(AjaxRequestTarget target, MilestoneSort sort) {
|
||||
protected void onSortChanged(AjaxRequestTarget target, IterationSort sort) {
|
||||
}
|
||||
|
||||
protected void onStateChanged(AjaxRequestTarget target, boolean closed) {
|
||||
@ -1,10 +0,0 @@
|
||||
<wicket:panel>
|
||||
<span class="actions d-inline-flex align-items-center">
|
||||
<a wicket:id="reopen" title="Reopen this milestone">Reopen</a>
|
||||
<a wicket:id="close" title="Close this milestone">Close</a>
|
||||
<span class="dot mx-2"></span>
|
||||
<a wicket:id="edit" title="Edit this milestone">Edit</a>
|
||||
<span class="dot mx-2"></span>
|
||||
<a wicket:id="delete" title="Delete this milestone">Delete</a>
|
||||
</span>
|
||||
</wicket:panel>
|
||||
@ -1,40 +0,0 @@
|
||||
package io.onedev.server.web.component.milestone.choice;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.wicket.markup.head.IHeaderResponse;
|
||||
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
|
||||
import org.apache.wicket.model.IModel;
|
||||
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.web.component.select2.Select2MultiChoice;
|
||||
|
||||
public class MilestoneMultiChoice extends Select2MultiChoice<Milestone> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public MilestoneMultiChoice(String id, IModel<Collection<Milestone>> selectionsModel, IModel<Collection<Milestone>>choicesModel) {
|
||||
super(id, selectionsModel, new MilestoneChoiceProvider(choicesModel));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
if (isRequired())
|
||||
getSettings().setPlaceholder("Choose milestones...");
|
||||
else
|
||||
getSettings().setPlaceholder("Not specified");
|
||||
getSettings().setFormatResult("onedev.server.milestoneChoiceFormatter.formatResult");
|
||||
getSettings().setFormatSelection("onedev.server.milestoneChoiceFormatter.formatSelection");
|
||||
getSettings().setEscapeMarkup("onedev.server.milestoneChoiceFormatter.escapeMarkup");
|
||||
setConvertEmptyInputStringToNull(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
super.renderHead(response);
|
||||
|
||||
response.render(JavaScriptHeaderItem.forReference(new MilestoneChoiceResourceReference()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
package io.onedev.server.web.component.milestone.choice;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.wicket.markup.head.IHeaderResponse;
|
||||
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
|
||||
import org.apache.wicket.model.IModel;
|
||||
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.web.component.select2.Select2Choice;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MilestoneSingleChoice extends Select2Choice<Milestone> {
|
||||
|
||||
public MilestoneSingleChoice(String id, IModel<Milestone> selectionModel, IModel<Collection<Milestone>> milestonesModel) {
|
||||
super(id, selectionModel, new MilestoneChoiceProvider(milestonesModel));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
getSettings().setAllowClear(!isRequired());
|
||||
if (isRequired())
|
||||
getSettings().setPlaceholder("Choose milestone...");
|
||||
else
|
||||
getSettings().setPlaceholder("Not specified");
|
||||
getSettings().setFormatResult("onedev.server.milestoneChoiceFormatter.formatResult");
|
||||
getSettings().setFormatSelection("onedev.server.milestoneChoiceFormatter.formatSelection");
|
||||
getSettings().setEscapeMarkup("onedev.server.milestoneChoiceFormatter.escapeMarkup");
|
||||
setConvertEmptyInputStringToNull(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderHead(IHeaderResponse response) {
|
||||
super.renderHead(response);
|
||||
|
||||
response.render(JavaScriptHeaderItem.forReference(new MilestoneChoiceResourceReference()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
.select2-results .milestone .label {
|
||||
margin-left: 6px;
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
onedev.server.milestoneChoiceFormatter = {
|
||||
formatSelection: function(milestone) {
|
||||
return milestone.name;
|
||||
},
|
||||
|
||||
formatResult: function(milestone) {
|
||||
return "<span class='milestone'>" + milestone.name + " <span class='ml-2 badge " + milestone.statusClass + "'>" + milestone.statusName + "</span></span>";
|
||||
},
|
||||
|
||||
escapeMarkup: function(m) {
|
||||
return m;
|
||||
},
|
||||
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
package io.onedev.server.web.editable.milestonechoice;
|
||||
package io.onedev.server.web.editable.iterationchoice;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
@ -16,16 +16,16 @@ import io.onedev.server.web.editable.PropertyContext;
|
||||
import io.onedev.server.web.editable.PropertyDescriptor;
|
||||
import io.onedev.server.web.editable.PropertyEditor;
|
||||
import io.onedev.server.web.editable.PropertyViewer;
|
||||
import io.onedev.server.annotation.MilestoneChoice;
|
||||
import io.onedev.server.annotation.IterationChoice;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MilestoneChoiceEditSupport implements EditSupport {
|
||||
public class IterationChoiceEditSupport implements EditSupport {
|
||||
|
||||
@Override
|
||||
public PropertyContext<?> getEditContext(PropertyDescriptor descriptor) {
|
||||
Method propertyGetter = descriptor.getPropertyGetter();
|
||||
MilestoneChoice milestoneChoice = propertyGetter.getAnnotation(MilestoneChoice.class);
|
||||
if (milestoneChoice != null) {
|
||||
IterationChoice iterationChoice = propertyGetter.getAnnotation(IterationChoice.class);
|
||||
if (iterationChoice != null) {
|
||||
if (List.class.isAssignableFrom(propertyGetter.getReturnType())
|
||||
&& ReflectionUtils.getCollectionElementClass(propertyGetter.getGenericReturnType()) == String.class) {
|
||||
return new PropertyContext<List<String>>(descriptor) {
|
||||
@ -36,9 +36,9 @@ public class MilestoneChoiceEditSupport implements EditSupport {
|
||||
|
||||
@Override
|
||||
protected Component newContent(String id, PropertyDescriptor propertyDescriptor) {
|
||||
List<String> milestoneNames = model.getObject();
|
||||
if (milestoneNames != null && !milestoneNames.isEmpty()) {
|
||||
return new Label(id, StringUtils.join(milestoneNames, ", " ));
|
||||
List<String> iterationNames = model.getObject();
|
||||
if (iterationNames != null && !iterationNames.isEmpty()) {
|
||||
return new Label(id, StringUtils.join(iterationNames, ", " ));
|
||||
} else {
|
||||
return new EmptyValueLabel(id) {
|
||||
|
||||
@ -56,7 +56,7 @@ public class MilestoneChoiceEditSupport implements EditSupport {
|
||||
|
||||
@Override
|
||||
public PropertyEditor<List<String>> renderForEdit(String componentId, IModel<List<String>> model) {
|
||||
return new MilestoneMultiChoiceEditor(componentId, descriptor, model);
|
||||
return new IterationMultiChoiceEditor(componentId, descriptor, model);
|
||||
}
|
||||
|
||||
};
|
||||
@ -88,12 +88,12 @@ public class MilestoneChoiceEditSupport implements EditSupport {
|
||||
|
||||
@Override
|
||||
public PropertyEditor<String> renderForEdit(String componentId, IModel<String> model) {
|
||||
return new MilestoneSingleChoiceEditor(componentId, descriptor, model);
|
||||
return new IterationSingleChoiceEditor(componentId, descriptor, model);
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
throw new RuntimeException("Annotation 'MilestoneChoice' should be applied to property with type 'String' or 'List<String>'");
|
||||
throw new RuntimeException("Annotation 'IterationChoice' should be applied to property with type 'String' or 'List<String>'");
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
@ -1,36 +1,32 @@
|
||||
package io.onedev.server.web.editable.milestonechoice;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
package io.onedev.server.web.editable.iterationchoice;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.onedev.server.annotation.IterationChoice;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.util.ComponentContext;
|
||||
import io.onedev.server.util.ReflectionUtils;
|
||||
import io.onedev.server.web.component.iteration.choice.IterationMultiChoice;
|
||||
import io.onedev.server.web.editable.PropertyDescriptor;
|
||||
import io.onedev.server.web.editable.PropertyEditor;
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.apache.wicket.util.convert.ConversionException;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entitymanager.MilestoneManager;
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.util.ComponentContext;
|
||||
import io.onedev.server.util.ReflectionUtils;
|
||||
import io.onedev.server.web.component.milestone.choice.MilestoneMultiChoice;
|
||||
import io.onedev.server.web.editable.PropertyDescriptor;
|
||||
import io.onedev.server.web.editable.PropertyEditor;
|
||||
import io.onedev.server.annotation.MilestoneChoice;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MilestoneMultiChoiceEditor extends PropertyEditor<List<String>> {
|
||||
public class IterationMultiChoiceEditor extends PropertyEditor<List<String>> {
|
||||
|
||||
private MilestoneMultiChoice input;
|
||||
private IterationMultiChoice input;
|
||||
|
||||
public MilestoneMultiChoiceEditor(String id, PropertyDescriptor propertyDescriptor,
|
||||
IModel<List<String>> propertyModel) {
|
||||
public IterationMultiChoiceEditor(String id, PropertyDescriptor propertyDescriptor,
|
||||
IModel<List<String>> propertyModel) {
|
||||
super(id, propertyDescriptor, propertyModel);
|
||||
}
|
||||
|
||||
@ -39,34 +35,30 @@ public class MilestoneMultiChoiceEditor extends PropertyEditor<List<String>> {
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
List<Milestone> choices = new ArrayList<>();
|
||||
List<Milestone> selections = new ArrayList<>();
|
||||
List<Iteration> choices = new ArrayList<>();
|
||||
List<Iteration> selections = new ArrayList<>();
|
||||
|
||||
ComponentContext componentContext = new ComponentContext(this);
|
||||
ComponentContext.push(componentContext);
|
||||
try {
|
||||
MilestoneChoice milestoneChoice = descriptor.getPropertyGetter().getAnnotation(MilestoneChoice.class);
|
||||
Preconditions.checkNotNull(milestoneChoice);
|
||||
if (milestoneChoice.value().length() != 0) {
|
||||
choices.addAll((List<Milestone>)ReflectionUtils
|
||||
.invokeStaticMethod(descriptor.getBeanClass(), milestoneChoice.value()));
|
||||
IterationChoice iterationChoice = descriptor.getPropertyGetter().getAnnotation(IterationChoice.class);
|
||||
Preconditions.checkNotNull(iterationChoice);
|
||||
if (iterationChoice.value().length() != 0) {
|
||||
choices.addAll((List<Iteration>)ReflectionUtils
|
||||
.invokeStaticMethod(descriptor.getBeanClass(), iterationChoice.value()));
|
||||
} else if (Project.get() != null) {
|
||||
choices.addAll(Project.get().getSortedHierarchyMilestones());
|
||||
choices.addAll(Project.get().getSortedHierarchyIterations());
|
||||
}
|
||||
|
||||
if (getModelObject() != null && Project.get() != null) {
|
||||
MilestoneManager milestoneManager = OneDev.getInstance(MilestoneManager.class);
|
||||
for (String milestoneName: getModelObject()) {
|
||||
Milestone milestone = milestoneManager.findInHierarchy(Project.get(), milestoneName);
|
||||
if (milestone != null && choices.contains(milestone))
|
||||
selections.add(milestone);
|
||||
}
|
||||
}
|
||||
for (var choice: choices) {
|
||||
if (getModelObject() != null && getModelObject().contains(choice.getName()))
|
||||
selections.add(choice);
|
||||
}
|
||||
} finally {
|
||||
ComponentContext.pop();
|
||||
}
|
||||
|
||||
input = new MilestoneMultiChoice("input", Model.of(selections), Model.of(choices)) {
|
||||
input = new IterationMultiChoice("input", Model.of(selections), Model.of(choices)) {
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
@ -92,9 +84,9 @@ public class MilestoneMultiChoiceEditor extends PropertyEditor<List<String>> {
|
||||
|
||||
@Override
|
||||
protected List<String> convertInputToValue() throws ConversionException {
|
||||
Collection<Milestone> milestones = input.getConvertedInput();
|
||||
if (milestones != null)
|
||||
return milestones.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
Collection<Iteration> iterations = input.getConvertedInput();
|
||||
if (iterations != null)
|
||||
return iterations.stream().map(it->it.getName()).collect(Collectors.toList());
|
||||
else
|
||||
return new ArrayList<>();
|
||||
}
|
||||
@ -1,32 +1,30 @@
|
||||
package io.onedev.server.web.editable.milestonechoice;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
package io.onedev.server.web.editable.iterationchoice;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.onedev.server.annotation.IterationChoice;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.util.ComponentContext;
|
||||
import io.onedev.server.util.ReflectionUtils;
|
||||
import io.onedev.server.web.component.iteration.choice.IterationSingleChoice;
|
||||
import io.onedev.server.web.editable.PropertyDescriptor;
|
||||
import io.onedev.server.web.editable.PropertyEditor;
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import org.apache.wicket.util.convert.ConversionException;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.onedev.server.model.Milestone;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.util.ComponentContext;
|
||||
import io.onedev.server.util.ReflectionUtils;
|
||||
import io.onedev.server.web.component.milestone.choice.MilestoneSingleChoice;
|
||||
import io.onedev.server.web.editable.PropertyDescriptor;
|
||||
import io.onedev.server.web.editable.PropertyEditor;
|
||||
import io.onedev.server.annotation.MilestoneChoice;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class MilestoneSingleChoiceEditor extends PropertyEditor<String> {
|
||||
public class IterationSingleChoiceEditor extends PropertyEditor<String> {
|
||||
|
||||
private MilestoneSingleChoice input;
|
||||
private IterationSingleChoice input;
|
||||
|
||||
public MilestoneSingleChoiceEditor(String id, PropertyDescriptor propertyDescriptor,
|
||||
IModel<String> propertyModel) {
|
||||
public IterationSingleChoiceEditor(String id, PropertyDescriptor propertyDescriptor,
|
||||
IModel<String> propertyModel) {
|
||||
super(id, propertyDescriptor, propertyModel);
|
||||
}
|
||||
|
||||
@ -35,33 +33,32 @@ public class MilestoneSingleChoiceEditor extends PropertyEditor<String> {
|
||||
protected void onInitialize() {
|
||||
super.onInitialize();
|
||||
|
||||
List<Milestone> choices = new ArrayList<>();
|
||||
Milestone selection;
|
||||
List<Iteration> choices = new ArrayList<>();
|
||||
Iteration selection = null;
|
||||
|
||||
ComponentContext componentContext = new ComponentContext(this);
|
||||
ComponentContext.push(componentContext);
|
||||
try {
|
||||
MilestoneChoice milestoneChoice = descriptor.getPropertyGetter().getAnnotation(MilestoneChoice.class);
|
||||
Preconditions.checkNotNull(milestoneChoice);
|
||||
if (milestoneChoice.value().length() != 0) {
|
||||
choices.addAll((List<Milestone>)ReflectionUtils
|
||||
.invokeStaticMethod(descriptor.getBeanClass(), milestoneChoice.value()));
|
||||
IterationChoice iterationChoice = descriptor.getPropertyGetter().getAnnotation(IterationChoice.class);
|
||||
Preconditions.checkNotNull(iterationChoice);
|
||||
if (iterationChoice.value().length() != 0) {
|
||||
choices.addAll((List<Iteration>)ReflectionUtils
|
||||
.invokeStaticMethod(descriptor.getBeanClass(), iterationChoice.value()));
|
||||
} else if (Project.get() != null) {
|
||||
choices.addAll(Project.get().getSortedHierarchyMilestones());
|
||||
choices.addAll(Project.get().getSortedHierarchyIterations());
|
||||
}
|
||||
|
||||
for (var choice: choices) {
|
||||
if (choice.getName().equals(getModelObject())) {
|
||||
selection = choice;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Project.get() != null && getModelObject() != null)
|
||||
selection = Project.get().getHierarchyMilestone(getModelObject());
|
||||
else
|
||||
selection = null;
|
||||
|
||||
if (selection != null && !choices.contains(selection))
|
||||
selection = null;
|
||||
} finally {
|
||||
ComponentContext.pop();
|
||||
}
|
||||
|
||||
input = new MilestoneSingleChoice("input", Model.of(selection), Model.of(choices)) {
|
||||
input = new IterationSingleChoice("input", Model.of(selection), Model.of(choices)) {
|
||||
|
||||
@Override
|
||||
protected void onInitialize() {
|
||||
@ -87,9 +84,9 @@ public class MilestoneSingleChoiceEditor extends PropertyEditor<String> {
|
||||
|
||||
@Override
|
||||
protected String convertInputToValue() throws ConversionException {
|
||||
Milestone milestone = input.getConvertedInput();
|
||||
if (milestone != null)
|
||||
return milestone.getName();
|
||||
Iteration iteration = input.getConvertedInput();
|
||||
if (iteration != null)
|
||||
return iteration.getName();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
@ -97,7 +97,7 @@ import io.onedev.server.web.page.project.issues.create.NewIssuePage;
|
||||
import io.onedev.server.web.page.project.issues.detail.*;
|
||||
import io.onedev.server.web.page.project.issues.imports.IssueImportPage;
|
||||
import io.onedev.server.web.page.project.issues.list.ProjectIssueListPage;
|
||||
import io.onedev.server.web.page.project.issues.milestones.*;
|
||||
import io.onedev.server.web.page.project.issues.iteration.*;
|
||||
import io.onedev.server.web.page.project.packs.ProjectPacksPage;
|
||||
import io.onedev.server.web.page.project.packs.detail.PackDetailPage;
|
||||
import io.onedev.server.web.page.project.pullrequests.InvalidPullRequestPage;
|
||||
@ -344,11 +344,11 @@ public class BaseUrlMapper extends CompoundRequestMapper {
|
||||
add(new ProjectPageMapper("${project}/~issues/${issue}/authorizations", IssueAuthorizationsPage.class));
|
||||
add(new ProjectPageMapper("${project}/~issues/new", NewIssuePage.class));
|
||||
add(new ProjectPageMapper("${project}/~issues/import/${importer}", IssueImportPage.class));
|
||||
add(new ProjectPageMapper("${project}/~milestones", MilestoneListPage.class));
|
||||
add(new ProjectPageMapper("${project}/~milestones/${milestone}", MilestoneIssuesPage.class));
|
||||
add(new ProjectPageMapper("${project}/~milestones/${milestone}/burndown", MilestoneBurndownPage.class));
|
||||
add(new ProjectPageMapper("${project}/~milestones/${milestone}/edit", MilestoneEditPage.class));
|
||||
add(new ProjectPageMapper("${project}/~milestones/new", NewMilestonePage.class));
|
||||
add(new ProjectPageMapper("${project}/~iterations", IterationListPage.class));
|
||||
add(new ProjectPageMapper("${project}/~iterations/${iteration}", IterationIssuesPage.class));
|
||||
add(new ProjectPageMapper("${project}/~iterations/${iteration}/burndown", IterationBurndownPage.class));
|
||||
add(new ProjectPageMapper("${project}/~iterations/${iteration}/edit", IterationEditPage.class));
|
||||
add(new ProjectPageMapper("${project}/~iterations/new", NewIterationPage.class));
|
||||
|
||||
add(new ProjectPageMapper("${project}/~builds", ProjectBuildsPage.class));
|
||||
add(new ProjectPageMapper("${project}/~builds/${build}", BuildDashboardPage.class));
|
||||
|
||||
@ -48,12 +48,6 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.milestone-type-management .drag-indicator,
|
||||
.milestone-type-management th.property-id,
|
||||
.milestone-type-management td.property-id {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.branding-setting .logo-preview>div {
|
||||
background-color: white !important;
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
<tbody>
|
||||
<tr wicket:id="methods" class="method">
|
||||
<td>
|
||||
<a wicket:id="link" class="title`"><span wicket:id="label"></span></a>
|
||||
<a wicket:id="link" class="title"><span wicket:id="label"></span></a>
|
||||
</td>
|
||||
<td wicket:id="httpMethod"></td>
|
||||
<td wicket:id="path"></td>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user