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); + } + }); + + + } + +}