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);
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) {
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 {
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}
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)
<tr wicket:id="priority"></tr>\r
<tr wicket:id="responsible"></tr>\r
<tr wicket:id="milestone"></tr>\r
+ <span wicket:id="dependencies"></span>\r
<tr wicket:id="mergeto"></tr>\r
</table>\r
</div>\r
import org.apache.wicket.markup.html.form.DropDownChoice;\r
import org.apache.wicket.markup.html.form.Form;\r
import org.apache.wicket.markup.html.form.TextField;\r
+import org.apache.wicket.markup.html.list.ListItem;\r
+import org.apache.wicket.markup.html.list.ListView;\r
import org.apache.wicket.markup.html.panel.Fragment;\r
import org.apache.wicket.model.IModel;\r
import org.apache.wicket.model.Model;\r
import org.eclipse.jgit.lib.Repository;\r
+import org.jsoup.helper.StringUtil;\r
\r
import com.gitblit.Constants;\r
import com.gitblit.Constants.AccessPermission;\r
import com.gitblit.utils.StringUtils;\r
import com.gitblit.wicket.GitBlitWebSession;\r
import com.gitblit.wicket.WicketUtils;\r
+import com.gitblit.wicket.panels.LinkPanel;\r
import com.gitblit.wicket.panels.MarkdownTextArea;\r
+import com.gitblit.wicket.panels.SimpleAjaxLink;\r
+import com.gitblit.wicket.panels.TicketRelationEditorPanel;\r
import com.google.common.base.Optional;\r
\r
/**\r
private IModel<TicketResponsible> responsibleModel;\r
\r
private IModel<TicketMilestone> milestoneModel;\r
+ \r
+ private IModel<List<String>> dependenciesModel;\r
\r
private Label descriptionPreview;\r
\r
statusModel = Model.of(ticket.status);\r
priorityModel = Model.of(ticket.priority);\r
severityModel = Model.of(ticket.severity);\r
+ dependenciesModel = Model.ofList((List) editable(ticket.getDependencies()));\r
\r
setStatelessHint(false);\r
setOutputMarkupId(true);\r
form.add(new TextField<String>("title", titleModel));\r
form.add(new TextField<String>("topic", topicModel));\r
\r
+ form.setOutputMarkupId(true);\r
+\r
+ form.add(new TicketRelationEditorPanel("dependencies", dependenciesModel, getRepositoryModel()));\r
+\r
final IModel<String> markdownPreviewModel = Model.of(ticket.body == null ? "" : ticket.body);\r
descriptionPreview = new Label("descriptionPreview", markdownPreviewModel);\r
descriptionPreview.setEscapeModelStrings(false);\r
change.setField(Field.topic, topic);\r
}\r
\r
+ List<String> newDependencies = (List<String>) dependenciesModel.getObject();\r
+ \r
+ change.setDeltaField(Field.dependency, ticket.getDependencies(), newDependencies);\r
+ \r
TicketResponsible responsible = responsibleModel == null ? null : responsibleModel.getObject();\r
if (responsible != null && !responsible.username.equals(ticket.responsible)) {\r
// responsible change\r
form.add(cancel);\r
}\r
\r
+ private List<String> editable(List<String> list) {\r
+ // need to copy, if it's an Collection.emptyList \r
+ return new ArrayList<String>(list);\r
+ }\r
+\r
@Override\r
protected String getPageName() {\r
return getString("gb.editTicket");\r
<tr wicket:id="priority"></tr>\r
<tr wicket:id="responsible"></tr>\r
<tr wicket:id="milestone"></tr>\r
+ <span wicket:id="dependencies"></span>\r
<tr wicket:id="mergeto"></tr>\r
</table>\r
</div>\r
import com.gitblit.wicket.GitBlitWebSession;\r
import com.gitblit.wicket.WicketUtils;\r
import com.gitblit.wicket.panels.MarkdownTextArea;\r
+import com.gitblit.wicket.panels.TicketRelationEditorPanel;\r
\r
/**\r
* Page for creating a new ticket.\r
\r
private IModel<TicketModel.Severity> severityModel;\r
\r
+ private IModel<List<String>> dependenciesModel;\r
+\r
public NewTicketPage(PageParameters params) {\r
super(params);\r
\r
milestoneModel = Model.of();\r
severityModel = Model.of(TicketModel.Severity.defaultSeverity);\r
priorityModel = Model.of(TicketModel.Priority.defaultPriority);\r
+ dependenciesModel = (IModel) Model.ofList(new ArrayList<String>());\r
\r
setStatelessHint(false);\r
setOutputMarkupId(true);\r
descriptionEditor = new MarkdownTextArea("description", markdownPreviewModel, descriptionPreview);\r
descriptionEditor.setRepository(repositoryName);\r
form.add(descriptionEditor);\r
+ \r
+ form.add(new TicketRelationEditorPanel("dependencies", dependenciesModel, getRepositoryModel()));\r
\r
if (currentUser.canAdmin(null, getRepositoryModel())) {\r
// responsible\r
if (!StringUtils.isEmpty(mergeTo)) {\r
change.setField(Field.mergeTo, mergeTo);\r
}\r
+ \r
+ change.setDeltaField(Field.dependency, Collections.<String>emptyList(), dependenciesModel.getObject());\r
\r
TicketModel ticket = app().tickets().createTicket(getRepositoryModel(), 0L, change);\r
if (ticket != null) {\r
<tr><th><wicket:message key="gb.topic"></wicket:message></th><td><span wicket:id="ticketTopic">[topic]</span></td></tr>\r
<tr><th><wicket:message key="gb.responsible"></wicket:message></th><td><span wicket:id="responsible">[responsible]</span></td></tr>\r
<tr><th><wicket:message key="gb.milestone"></wicket:message></th><td><span wicket:id="milestone">[milestone]</span></td></tr>\r
+ <tr><th><wicket:message key="gb.dependencies"></wicket:message></th><td><span wicket:id="dependencies"><a wicket:id="dependencyLink">[link]</a></span></td></tr>\r
<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>\r
<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>\r
<tr><th><wicket:message key="gb.export"></wicket:message></th><td><a rel="nofollow" target="_blank" wicket:id="exportJson"></a></td></tr>\r
import org.apache.wicket.markup.html.image.ContextImage;\r
import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
import org.apache.wicket.markup.html.link.ExternalLink;\r
+import org.apache.wicket.markup.html.list.ListItem;\r
+import org.apache.wicket.markup.html.list.ListView;\r
import org.apache.wicket.markup.html.panel.Fragment;\r
import org.apache.wicket.markup.repeater.Item;\r
import org.apache.wicket.markup.repeater.data.DataView;\r
}\r
add(new Label("ticketDescription", desc).setEscapeModelStrings(false));\r
\r
+ /*\r
+ * DEPENDENCY\r
+ */\r
+ List<String> dependencies = ticket.getDependencies();\r
+ add(new ListView<String>("dependencies", dependencies) {\r
+ @Override\r
+ protected void populateItem(ListItem<String> item) {\r
+ String ticketId= item.getModelObject();\r
+ PageParameters tp = WicketUtils.newObjectParameter(ticket.repository, ticketId);\r
+ item.add(new LinkPanel("dependencyLink", "list subject", "#"+ticketId, TicketsPage.class, tp));\r
+ }\r
+ });\r
\r
/*\r
* PARTICIPANTS (DISCUSSION TAB)\r
--- /dev/null
+<!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
--- /dev/null
+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);
+ }
+ });
+
+
+ }
+
+}