Add 'dependency' to the ticket model and to the ui, with editing possibility with a separate relation editor panel
diff --git a/src/main/java/com/gitblit/models/TicketModel.java b/src/main/java/com/gitblit/models/TicketModel.java
index fd0b09e..f8813f0 100644
--- a/src/main/java/com/gitblit/models/TicketModel.java
+++ b/src/main/java/com/gitblit/models/TicketModel.java
@@ -233,6 +233,10 @@
public List<String> getLabels() {
return getList(Field.labels);
}
+
+ public List<String> getDependencies() {
+ return getList(Field.dependency);
+ }
public boolean isResponsible(String username) {
return username.equals(responsible);
@@ -746,6 +750,26 @@
fields.put(field, value.toString());
}
}
+
+ public void setDeltaField(Field field, List<String> base, List<String> newValues) {
+ List<String> result = new ArrayList<>();
+ for (String oldValue : base) {
+ if (!newValues.contains(oldValue)) {
+ result.add("-" + oldValue);
+ }
+ }
+ for (String newValue : newValues) {
+ if (!base.contains(newValue)) {
+ result.add("+" + newValue);
+ }
+ }
+ if (result.isEmpty()) {
+ // no change
+ remove(field);
+ } else {
+ setField(field, join(result, ","));
+ }
+ }
public void remove(Field field) {
if (fields != null) {
@@ -1195,7 +1219,7 @@
public static enum Field {
title, body, responsible, type, status, milestone, mergeSha, mergeTo,
- topic, labels, watchers, reviewers, voters, mentions, priority, severity;
+ topic, labels, watchers, reviewers, voters, mentions, priority, severity, dependency;
}
public static enum Type {
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
index 648ac2a..10bfe00 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -513,6 +513,9 @@
gb.docsWelcome2 = Commit a README.md or a HOME.md file to get started.
gb.createReadme = create a README
gb.responsible = responsible
+gb.dependency = dependency
+gb.dependencies = dependencies
+gb.remove = remove
gb.createdThisTicket = created this ticket
gb.proposedThisChange = proposed this change
gb.uploadedPatchsetN = uploaded patchset {0}
@@ -759,4 +762,4 @@
gb.diffTruncated = Diff truncated after the above file
gb.opacityAdjust = Adjust opacity
gb.blinkComparator = Blink comparator
-gb.imgdiffSubtract = Subtract (black = identical)
\ No newline at end of file
+gb.imgdiffSubtract = Subtract (black = identical)
diff --git a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html
index b12d0c7..764adc5 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html
@@ -43,6 +43,7 @@
<tr wicket:id="priority"></tr>
<tr wicket:id="responsible"></tr>
<tr wicket:id="milestone"></tr>
+ <span wicket:id="dependencies"></span>
<tr wicket:id="mergeto"></tr>
</table>
</div>
diff --git a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java
index 192b48c..61e1287 100644
--- a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java
@@ -30,10 +30,13 @@
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.eclipse.jgit.lib.Repository;
+import org.jsoup.helper.StringUtil;
import com.gitblit.Constants;
import com.gitblit.Constants.AccessPermission;
@@ -51,7 +54,10 @@
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.MarkdownTextArea;
+import com.gitblit.wicket.panels.SimpleAjaxLink;
+import com.gitblit.wicket.panels.TicketRelationEditorPanel;
import com.google.common.base.Optional;
/**
@@ -81,6 +87,8 @@
private IModel<TicketResponsible> responsibleModel;
private IModel<TicketMilestone> milestoneModel;
+
+ private IModel<List<String>> dependenciesModel;
private Label descriptionPreview;
@@ -123,6 +131,7 @@
statusModel = Model.of(ticket.status);
priorityModel = Model.of(ticket.priority);
severityModel = Model.of(ticket.severity);
+ dependenciesModel = Model.ofList((List) editable(ticket.getDependencies()));
setStatelessHint(false);
setOutputMarkupId(true);
@@ -141,6 +150,10 @@
form.add(new TextField<String>("title", titleModel));
form.add(new TextField<String>("topic", topicModel));
+ form.setOutputMarkupId(true);
+
+ form.add(new TicketRelationEditorPanel("dependencies", dependenciesModel, getRepositoryModel()));
+
final IModel<String> markdownPreviewModel = Model.of(ticket.body == null ? "" : ticket.body);
descriptionPreview = new Label("descriptionPreview", markdownPreviewModel);
descriptionPreview.setEscapeModelStrings(false);
@@ -311,6 +324,10 @@
change.setField(Field.topic, topic);
}
+ List<String> newDependencies = (List<String>) dependenciesModel.getObject();
+
+ change.setDeltaField(Field.dependency, ticket.getDependencies(), newDependencies);
+
TicketResponsible responsible = responsibleModel == null ? null : responsibleModel.getObject();
if (responsible != null && !responsible.username.equals(ticket.responsible)) {
// responsible change
@@ -382,6 +399,11 @@
form.add(cancel);
}
+ private List<String> editable(List<String> list) {
+ // need to copy, if it's an Collection.emptyList
+ return new ArrayList<String>(list);
+ }
+
@Override
protected String getPageName() {
return getString("gb.editTicket");
diff --git a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html
index 9b5af02..d0b7378 100644
--- a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html
@@ -43,6 +43,7 @@
<tr wicket:id="priority"></tr>
<tr wicket:id="responsible"></tr>
<tr wicket:id="milestone"></tr>
+ <span wicket:id="dependencies"></span>
<tr wicket:id="mergeto"></tr>
</table>
</div>
diff --git a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java
index 0c52505..3722be1 100644
--- a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java
@@ -51,6 +51,7 @@
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.MarkdownTextArea;
+import com.gitblit.wicket.panels.TicketRelationEditorPanel;
/**
* Page for creating a new ticket.
@@ -80,6 +81,8 @@
private IModel<TicketModel.Severity> severityModel;
+ private IModel<List<String>> dependenciesModel;
+
public NewTicketPage(PageParameters params) {
super(params);
@@ -101,6 +104,7 @@
milestoneModel = Model.of();
severityModel = Model.of(TicketModel.Severity.defaultSeverity);
priorityModel = Model.of(TicketModel.Priority.defaultPriority);
+ dependenciesModel = (IModel) Model.ofList(new ArrayList<String>());
setStatelessHint(false);
setOutputMarkupId(true);
@@ -122,6 +126,8 @@
descriptionEditor = new MarkdownTextArea("description", markdownPreviewModel, descriptionPreview);
descriptionEditor.setRepository(repositoryName);
form.add(descriptionEditor);
+
+ form.add(new TicketRelationEditorPanel("dependencies", dependenciesModel, getRepositoryModel()));
if (currentUser.canAdmin(null, getRepositoryModel())) {
// responsible
@@ -244,6 +250,8 @@
if (!StringUtils.isEmpty(mergeTo)) {
change.setField(Field.mergeTo, mergeTo);
}
+
+ change.setDeltaField(Field.dependency, Collections.<String>emptyList(), dependenciesModel.getObject());
TicketModel ticket = app().tickets().createTicket(getRepositoryModel(), 0L, change);
if (ticket != null) {
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.html b/src/main/java/com/gitblit/wicket/pages/TicketPage.html
index 5ae005e..e3d143d 100644
--- a/src/main/java/com/gitblit/wicket/pages/TicketPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.html
@@ -72,6 +72,7 @@
<tr><th><wicket:message key="gb.topic"></wicket:message></th><td><span wicket:id="ticketTopic">[topic]</span></td></tr>
<tr><th><wicket:message key="gb.responsible"></wicket:message></th><td><span wicket:id="responsible">[responsible]</span></td></tr>
<tr><th><wicket:message key="gb.milestone"></wicket:message></th><td><span wicket:id="milestone">[milestone]</span></td></tr>
+ <tr><th><wicket:message key="gb.dependencies"></wicket:message></th><td><span wicket:id="dependencies"><a wicket:id="dependencyLink">[link]</a></span></td></tr>
<tr><th><wicket:message key="gb.votes"></wicket:message></th><td><span wicket:id="votes" class="badge">1</span> <a style="padding-left:5px" wicket:id="voteLink" href="#">vote</a></td></tr>
<tr><th><wicket:message key="gb.watchers"></wicket:message></th><td><span wicket:id="watchers" class="badge">1</span> <a style="padding-left:5px" wicket:id="watchLink" href="#">watch</a></td></tr>
<tr><th><wicket:message key="gb.export"></wicket:message></th><td><a rel="nofollow" target="_blank" wicket:id="exportJson"></a></td></tr>
diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.java b/src/main/java/com/gitblit/wicket/pages/TicketPage.java
index 4890874..74484b0 100644
--- a/src/main/java/com/gitblit/wicket/pages/TicketPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.java
@@ -42,6 +42,8 @@
import org.apache.wicket.markup.html.image.ContextImage;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.ExternalLink;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
@@ -292,6 +294,18 @@
}
add(new Label("ticketDescription", desc).setEscapeModelStrings(false));
+ /*
+ * DEPENDENCY
+ */
+ List<String> dependencies = ticket.getDependencies();
+ add(new ListView<String>("dependencies", dependencies) {
+ @Override
+ protected void populateItem(ListItem<String> item) {
+ String ticketId= item.getModelObject();
+ PageParameters tp = WicketUtils.newObjectParameter(ticket.repository, ticketId);
+ item.add(new LinkPanel("dependencyLink", "list subject", "#"+ticketId, TicketsPage.class, tp));
+ }
+ });
/*
* PARTICIPANTS (DISCUSSION TAB)
diff --git a/src/main/java/com/gitblit/wicket/panels/TicketRelationEditorPanel.html b/src/main/java/com/gitblit/wicket/panels/TicketRelationEditorPanel.html
new file mode 100644
index 0000000..79c381f
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/panels/TicketRelationEditorPanel.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<body>
+<wicket:panel>
+
+ <span wicket:id="dependencyList">
+ <tr>
+ <th><wicket:message key="gb.dependency"></wicket:message></th>
+ <td class="edit">
+ <span wicket:id="dependencyLink" >[ticket label]</span>
+ <button type="submit" class="btn" wicket:id="removeDependencyLink"><wicket:message key="gb.remove"/></button>
+ </td>
+ </tr>
+ </span>
+ <th><wicket:message key="gb.dependency"></wicket:message></th>
+ <td class="edit">
+ <input class="input-large" type="text" wicket:id="addDependencyText"></input>
+ <button class="btn btn-appmenu" wicket:id="addDependency"><wicket:message key="gb.add"/></button>
+ </td>
+
+
+</wicket:panel>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/panels/TicketRelationEditorPanel.java b/src/main/java/com/gitblit/wicket/panels/TicketRelationEditorPanel.java
new file mode 100644
index 0000000..59cc909
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/panels/TicketRelationEditorPanel.java
@@ -0,0 +1,82 @@
+package com.gitblit.wicket.panels;
+
+import java.util.List;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.form.AjaxButton;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextField;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.jsoup.helper.StringUtil;
+
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.wicket.WicketUtils;
+import com.gitblit.wicket.pages.TicketsPage;
+
+public class TicketRelationEditorPanel extends BasePanel {
+
+ private static final long serialVersionUID = 1L;
+
+ private IModel<List<String>> dependenciesModel;
+ private IModel<String> addDependencyModel;
+
+
+ public TicketRelationEditorPanel(String wicketId, IModel<List<String>> pdependenciesModel, final RepositoryModel repositoryModel) {
+ super(wicketId);
+ this.dependenciesModel = pdependenciesModel;
+ this.addDependencyModel = Model.of();
+
+
+ add(new ListView<String>("dependencyList", dependenciesModel) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void populateItem(ListItem<String> item) {
+ final String ticketId = item.getModelObject();
+
+ PageParameters tp = WicketUtils.newObjectParameter(repositoryModel.name, ticketId);
+ item.add(new LinkPanel("dependencyLink", "list subject", "#"+ticketId, TicketsPage.class, tp));
+
+ item.add(new AjaxButton("removeDependencyLink") {
+ private static final long serialVersionUID = 1L;
+ @Override
+ public void onSubmit(AjaxRequestTarget target, Form<?> form) {
+ List<String> list = dependenciesModel.getObject();
+ list.remove(ticketId);
+ dependenciesModel.setObject(list);
+ target.addComponent(form);
+ }
+ });
+ }
+ });
+ add(new TextField<String>("addDependencyText", addDependencyModel));
+ add(new AjaxButton("addDependency") {
+ private static final long serialVersionUID = 1L;
+ @Override
+ protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
+ String ticketIdStr = addDependencyModel.getObject();
+ if (!StringUtil.isBlank(ticketIdStr)) {
+ try {
+ long ticketId = Long.parseLong(ticketIdStr);
+ if (app().tickets().hasTicket(repositoryModel, ticketId)) {
+ List<String> list = (List<String>) dependenciesModel.getObject();
+ list.add(String.valueOf(ticketId));
+ addDependencyModel.setObject("");
+ }
+ } catch (NumberFormatException e) {
+ // TODO : not allowed
+
+ }
+ }
+ target.addComponent(form);
+ }
+ });
+
+
+ }
+
+}