From f9c78c0ccc709509cdf7f83c45c898883d329db2 Mon Sep 17 00:00:00 2001 From: Paul Martin Date: Mon, 29 Sep 2014 22:10:20 +0100 Subject: Tickets - Priority, Severity options + Severity indicated via new character indicator and color of ticket icon on ticket list + Priority indicated via new priority icon and color on ticket list + Indexed as integers to provide sorting and maintain language neutral index + Colours and indicator text controlled through CSS classes priority- & severity- + UITicketTest created to generate tickets of all types to ease debugging --- src/main/java/com/gitblit/models/TicketModel.java | 120 +++++++++++++++- src/main/java/com/gitblit/tickets/QueryResult.java | 4 + .../java/com/gitblit/tickets/TicketIndexer.java | 9 +- .../com/gitblit/wicket/GitBlitWebApp.properties | 8 +- src/main/java/com/gitblit/wicket/TicketsUI.java | 47 ++++++- src/main/java/com/gitblit/wicket/WicketUtils.java | 6 + .../com/gitblit/wicket/pages/EditTicketPage.html | 2 + .../com/gitblit/wicket/pages/EditTicketPage.java | 26 ++++ .../com/gitblit/wicket/pages/MyTicketsPage.java | 4 + .../com/gitblit/wicket/pages/NewTicketPage.html | 2 + .../com/gitblit/wicket/pages/NewTicketPage.java | 26 ++++ .../java/com/gitblit/wicket/pages/TicketPage.html | 2 + .../java/com/gitblit/wicket/pages/TicketPage.java | 6 + .../java/com/gitblit/wicket/pages/TicketsPage.java | 6 +- .../com/gitblit/wicket/panels/TicketListPanel.html | 3 + .../com/gitblit/wicket/panels/TicketListPanel.java | 12 +- src/main/resources/gitblit.css | 74 +++++++++- src/test/java/com/gitblit/tests/GitBlitSuite.java | 2 +- .../java/com/gitblit/tests/TicketServiceTest.java | 44 +++++- src/test/java/com/gitblit/tests/UITicketTest.java | 151 +++++++++++++++++++++ 20 files changed, 536 insertions(+), 18 deletions(-) create mode 100644 src/test/java/com/gitblit/tests/UITicketTest.java (limited to 'src') diff --git a/src/main/java/com/gitblit/models/TicketModel.java b/src/main/java/com/gitblit/models/TicketModel.java index 9bdb2606..fd0b09eb 100644 --- a/src/main/java/com/gitblit/models/TicketModel.java +++ b/src/main/java/com/gitblit/models/TicketModel.java @@ -91,6 +91,10 @@ public class TicketModel implements Serializable, Comparable { public Integer deletions; + public Priority priority; + + public Severity severity; + /** * Builds an effective ticket from the collection of changes. A change may * Add or Subtract information from a ticket, but the collection of changes @@ -141,6 +145,8 @@ public class TicketModel implements Serializable, Comparable { changes = new ArrayList(); status = Status.New; type = Type.defaultType; + priority = Priority.defaultPriority; + severity = Severity.defaultSeverity; } public boolean isOpen() { @@ -517,6 +523,12 @@ public class TicketModel implements Serializable, Comparable { case mergeSha: mergeSha = toString(value); break; + case priority: + priority = TicketModel.Priority.fromObject(value, priority); + break; + case severity: + severity = TicketModel.Severity.fromObject(value, severity); + break; default: // unknown break; @@ -1183,7 +1195,7 @@ public class TicketModel implements Serializable, Comparable { public static enum Field { title, body, responsible, type, status, milestone, mergeSha, mergeTo, - topic, labels, watchers, reviewers, voters, mentions; + topic, labels, watchers, reviewers, voters, mentions, priority, severity; } public static enum Type { @@ -1310,4 +1322,110 @@ public class TicketModel implements Serializable, Comparable { return null; } } + + public static enum Priority { + Low(-1), Normal(0), High(1), Urgent(2); + + public static Priority defaultPriority = Normal; + + final int value; + + Priority(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Priority [] choices() { + return new Priority [] { Urgent, High, Normal, Low }; + } + + @Override + public String toString() { + return name().toLowerCase().replace('_', ' '); + } + + public static Priority fromObject(Object o, Priority defaultPriority) { + if (o instanceof Priority) { + // cast and return + return (Priority) o; + } else if (o instanceof String) { + // find by name + for (Priority priority : values()) { + String str = o.toString(); + if (priority.name().equalsIgnoreCase(str) + || priority.toString().equalsIgnoreCase(str)) { + return priority; + } + } + } else if (o instanceof Number) { + + switch (((Number) o).intValue()) { + case -1: return Priority.Low; + case 0: return Priority.Normal; + case 1: return Priority.High; + case 2: return Priority.Urgent; + default: return Priority.Normal; + } + } + + return defaultPriority; + } + } + + public static enum Severity { + Unrated(-1), Negligible(1), Minor(2), Serious(3), Critical(4), Catastrophic(5); + + public static Severity defaultSeverity = Unrated; + + final int value; + + Severity(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static Severity [] choices() { + return new Severity [] { Unrated, Negligible, Minor, Serious, Critical, Catastrophic }; + } + + @Override + public String toString() { + return name().toLowerCase().replace('_', ' '); + } + + public static Severity fromObject(Object o, Severity defaultSeverity) { + if (o instanceof Severity) { + // cast and return + return (Severity) o; + } else if (o instanceof String) { + // find by name + for (Severity severity : values()) { + String str = o.toString(); + if (severity.name().equalsIgnoreCase(str) + || severity.toString().equalsIgnoreCase(str)) { + return severity; + } + } + } else if (o instanceof Number) { + + switch (((Number) o).intValue()) { + case -1: return Severity.Unrated; + case 1: return Severity.Negligible; + case 2: return Severity.Minor; + case 3: return Severity.Serious; + case 4: return Severity.Critical; + case 5: return Severity.Catastrophic; + default: return Severity.Unrated; + } + } + + return defaultSeverity; + } + } } diff --git a/src/main/java/com/gitblit/tickets/QueryResult.java b/src/main/java/com/gitblit/tickets/QueryResult.java index 7a2b1abe..f8d6d123 100644 --- a/src/main/java/com/gitblit/tickets/QueryResult.java +++ b/src/main/java/com/gitblit/tickets/QueryResult.java @@ -24,6 +24,8 @@ import java.util.List; import com.gitblit.models.TicketModel.Patchset; import com.gitblit.models.TicketModel.Status; import com.gitblit.models.TicketModel.Type; +import com.gitblit.models.TicketModel.Priority; +import com.gitblit.models.TicketModel.Severity; import com.gitblit.utils.StringUtils; /** @@ -62,6 +64,8 @@ public class QueryResult implements Serializable { public int commentsCount; public int votesCount; public int approvalsCount; + public Priority priority; + public Severity severity; public int docId; public int totalResults; diff --git a/src/main/java/com/gitblit/tickets/TicketIndexer.java b/src/main/java/com/gitblit/tickets/TicketIndexer.java index 11ea3a73..e2d53af7 100644 --- a/src/main/java/com/gitblit/tickets/TicketIndexer.java +++ b/src/main/java/com/gitblit/tickets/TicketIndexer.java @@ -103,7 +103,10 @@ public class TicketIndexer { mergesha(Type.STRING), mergeto(Type.STRING), patchsets(Type.INT), - votes(Type.INT); + votes(Type.INT), + //NOTE: Indexing on the underlying value to allow flexibility on naming + priority(Type.INT), + severity(Type.INT); final Type fieldType; @@ -519,6 +522,8 @@ public class TicketIndexer { toDocField(doc, Lucene.watchedby, StringUtils.flattenStrings(ticket.getWatchers(), ";").toLowerCase()); toDocField(doc, Lucene.mentions, StringUtils.flattenStrings(ticket.getMentions(), ";").toLowerCase()); toDocField(doc, Lucene.votes, ticket.getVoters().size()); + toDocField(doc, Lucene.priority, ticket.priority.getValue()); + toDocField(doc, Lucene.severity, ticket.severity.getValue()); List attachments = new ArrayList(); for (Attachment attachment : ticket.getAttachments()) { @@ -600,6 +605,8 @@ public class TicketIndexer { result.participants = unpackStrings(doc, Lucene.participants); result.watchedby = unpackStrings(doc, Lucene.watchedby); result.mentions = unpackStrings(doc, Lucene.mentions); + result.priority = TicketModel.Priority.fromObject(unpackInt(doc, Lucene.priority), TicketModel.Priority.defaultPriority); + result.severity = TicketModel.Severity.fromObject(unpackInt(doc, Lucene.severity), TicketModel.Severity.defaultSeverity); if (!StringUtils.isEmpty(doc.get(Lucene.patchset.name()))) { // unpack most recent patchset diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index 2760f85f..5326042a 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -742,4 +742,10 @@ gb.sshKeyCommentDescription = Enter an optional comment. If blank, the comment w gb.permission = Permission gb.sshKeyPermissionDescription = Specify the access permission for the SSH key gb.transportPreference = Transport Preference -gb.transportPreferenceDescription = Set the transport that you prefer to use for cloning \ No newline at end of file +gb.transportPreferenceDescription = Set the transport that you prefer to use for cloning +gb.priority = priority +gb.severity = severity +gb.sortHighestPriority = highest priority +gb.sortLowestPriority = lowest priority +gb.sortHighestSeverity = highest severity +gb.sortLowestSeverity = lowest severity diff --git a/src/main/java/com/gitblit/wicket/TicketsUI.java b/src/main/java/com/gitblit/wicket/TicketsUI.java index 347ac44d..2367c984 100644 --- a/src/main/java/com/gitblit/wicket/TicketsUI.java +++ b/src/main/java/com/gitblit/wicket/TicketsUI.java @@ -21,6 +21,8 @@ import java.text.MessageFormat; import org.apache.wicket.markup.html.basic.Label; import com.gitblit.models.TicketModel; +import com.gitblit.models.TicketModel.Priority; +import com.gitblit.models.TicketModel.Severity; import com.gitblit.models.TicketModel.Status; import com.gitblit.models.TicketModel.Type; import com.gitblit.utils.StringUtils; @@ -36,7 +38,7 @@ public class TicketsUI { public static final String [] openStatii = new String [] { Status.New.name().toLowerCase(), Status.Open.name().toLowerCase() }; public static final String [] closedStatii = new String [] { "!" + Status.New.name().toLowerCase(), "!" + Status.Open.name().toLowerCase() }; - + public static Label getStateIcon(String wicketId, TicketModel ticket) { return getStateIcon(wicketId, ticket.type, ticket.status); } @@ -48,28 +50,59 @@ public class TicketsUI { } switch (type) { case Proposal: - WicketUtils.setCssClass(label, "fa fa-code-fork"); + WicketUtils.setCssClass(label, "fa fa-code-fork fa-fw"); break; case Bug: - WicketUtils.setCssClass(label, "fa fa-bug"); + WicketUtils.setCssClass(label, "fa fa-bug fa-fw"); break; case Enhancement: - WicketUtils.setCssClass(label, "fa fa-magic"); + WicketUtils.setCssClass(label, "fa fa-magic fa-fw"); break; case Question: - WicketUtils.setCssClass(label, "fa fa-question"); + WicketUtils.setCssClass(label, "fa fa-question fa-fw"); break; case Maintenance: - WicketUtils.setCssClass(label, "fa fa-cogs"); + WicketUtils.setCssClass(label, "fa fa-cogs fa-fw"); break; default: // standard ticket - WicketUtils.setCssClass(label, "fa fa-ticket"); + WicketUtils.setCssClass(label, "fa fa-ticket fa-fw"); } WicketUtils.setHtmlTooltip(label, getTypeState(type, state)); + + return label; + } + + public static Label getPriorityIcon(String wicketId, Priority priority) { + Label label = new Label(wicketId); + if (priority == null) { + priority = Priority.defaultPriority; + } + switch (priority) { + case Urgent: + WicketUtils.setCssClass(label, "fa fa-step-forward fa-rotate-270"); + break; + case High: + WicketUtils.setCssClass(label, "fa fa-caret-up fa-lg"); + break; + case Low: + WicketUtils.setCssClass(label, "fa fa-caret-down fa-lg"); + break; + default: + } + WicketUtils.setHtmlTooltip(label, priority.toString()); + return label; } + + public static String getPriorityClass(Priority priority) { + return String.format("priority-%s", priority); + } + public static String getSeverityClass(Severity severity) { + return String.format("severity-%s", severity); + } + public static String getTypeState(Type type, Status state) { return state.toString() + " " + type.toString(); } diff --git a/src/main/java/com/gitblit/wicket/WicketUtils.java b/src/main/java/com/gitblit/wicket/WicketUtils.java index d47390d4..9a40931d 100644 --- a/src/main/java/com/gitblit/wicket/WicketUtils.java +++ b/src/main/java/com/gitblit/wicket/WicketUtils.java @@ -29,12 +29,14 @@ import javax.servlet.http.HttpServletRequest; import org.apache.wicket.Component; import org.apache.wicket.PageParameters; import org.apache.wicket.Request; +import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.behavior.HeaderContributor; import org.apache.wicket.behavior.SimpleAttributeModifier; import org.apache.wicket.markup.html.IHeaderContributor; import org.apache.wicket.markup.html.IHeaderResponse; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.image.ContextImage; +import org.apache.wicket.model.Model; import org.apache.wicket.protocol.http.WebRequest; import org.apache.wicket.resource.ContextRelativeResource; import org.eclipse.jgit.diff.DiffEntry.ChangeType; @@ -56,6 +58,10 @@ public class WicketUtils { container.add(new SimpleAttributeModifier("class", value)); } + public static void addCssClass(Component container, String value) { + container.add(new AttributeAppender("class", new Model(value), " ")); + } + public static void setCssStyle(Component container, String value) { container.add(new SimpleAttributeModifier("style", value)); } diff --git a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html index b5fe0ae5..e11aed82 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html +++ b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.html @@ -41,6 +41,8 @@ + + diff --git a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java index c3d405bc..7c02c0be 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditTicketPage.java @@ -83,6 +83,10 @@ public class EditTicketPage extends RepositoryPage { private IModel milestoneModel; private Label descriptionPreview; + + private IModel priorityModel; + + private IModel severityModel; public EditTicketPage(PageParameters params) { super(params); @@ -117,6 +121,8 @@ public class EditTicketPage extends RepositoryPage { milestoneModel = Model.of(); mergeToModel = Model.of(ticket.mergeTo == null ? getRepositoryModel().mergeTo : ticket.mergeTo); statusModel = Model.of(ticket.status); + priorityModel = Model.of(ticket.priority); + severityModel = Model.of(ticket.severity); setStatelessHint(false); setOutputMarkupId(true); @@ -219,6 +225,14 @@ public class EditTicketPage extends RepositoryPage { milestone.add(new DropDownChoice("milestone", milestoneModel, milestones)); form.add(milestone.setVisible(!milestones.isEmpty())); + // priority + List priorityChoices = Arrays.asList(TicketModel.Priority.choices()); + form.add(new DropDownChoice("priority", priorityModel, priorityChoices)); + + // severity + List severityChoices = Arrays.asList(TicketModel.Severity.choices()); + form.add(new DropDownChoice("severity", severityModel, severityChoices)); + // mergeTo (integration branch) List branches = new ArrayList(); for (String branch : getRepositoryModel().getLocalBranches()) { @@ -315,7 +329,19 @@ public class EditTicketPage extends RepositoryPage { change.setField(Field.milestone, milestone.name); } } + + TicketModel.Priority priority = priorityModel.getObject(); + if (!ticket.priority.equals(priority)) + { + change.setField(Field.priority, priority); + } + TicketModel.Severity severity = severityModel.getObject(); + if (!ticket.severity.equals(severity)) + { + change.setField(Field.severity, severity); + } + String mergeTo = mergeToModel.getObject(); if ((StringUtils.isEmpty(ticket.mergeTo) && !StringUtils.isEmpty(mergeTo)) || (!StringUtils.isEmpty(mergeTo) && !mergeTo.equals(ticket.mergeTo))) { diff --git a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java index 1396a5e5..187302f5 100644 --- a/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/MyTicketsPage.java @@ -230,6 +230,10 @@ public class MyTicketsPage extends RootPage { sortChoices.add(new TicketSort(getString("gb.sortLeastPatchsetRevisions"), Lucene.patchsets.name(), false)); sortChoices.add(new TicketSort(getString("gb.sortMostVotes"), Lucene.votes.name(), true)); sortChoices.add(new TicketSort(getString("gb.sortLeastVotes"), Lucene.votes.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortHighestPriority"), Lucene.priority.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestPriority"), Lucene.priority.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortHighestSeverity"), Lucene.severity.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestSeverity"), Lucene.severity.name(), false)); TicketSort currentSort = sortChoices.get(0); for (TicketSort ts : sortChoices) { diff --git a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html index 447c6aa4..7b71ada0 100644 --- a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html +++ b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.html @@ -41,6 +41,8 @@ * + + diff --git a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java index 8f28055a..ea3e9602 100644 --- a/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/NewTicketPage.java @@ -75,6 +75,10 @@ public class NewTicketPage extends RepositoryPage { private IModel milestoneModel; private Label descriptionPreview; + + private IModel priorityModel; + + private IModel severityModel; public NewTicketPage(PageParameters params) { super(params); @@ -95,6 +99,8 @@ public class NewTicketPage extends RepositoryPage { mergeToModel = Model.of(Repository.shortenRefName(getRepositoryModel().mergeTo)); responsibleModel = Model.of(); milestoneModel = Model.of(); + severityModel = Model.of(TicketModel.Severity.defaultSeverity); + priorityModel = Model.of(TicketModel.Priority.defaultPriority); setStatelessHint(false); setOutputMarkupId(true); @@ -152,6 +158,12 @@ public class NewTicketPage extends RepositoryPage { milestone.add(new DropDownChoice("milestone", milestoneModel, milestones)); form.add(milestone.setVisible(!milestones.isEmpty())); + // priority + form.add(new DropDownChoice("priority", priorityModel, Arrays.asList(TicketModel.Priority.choices()))); + + //severity + form.add(new DropDownChoice("severity", severityModel, Arrays.asList(TicketModel.Severity.choices()))); + // integration branch List branches = new ArrayList(); for (String branch : getRepositoryModel().getLocalBranches()) { @@ -211,6 +223,20 @@ public class NewTicketPage extends RepositoryPage { if (milestone != null) { change.setField(Field.milestone, milestone.name); } + + // severity + TicketModel.Severity severity = TicketModel.Severity.defaultSeverity; + if (severityModel.getObject() != null) { + severity = severityModel.getObject(); + } + change.setField(Field.severity, severity); + + // priority + TicketModel.Priority priority = TicketModel.Priority.defaultPriority; + if (priorityModel.getObject() != null) { + priority = priorityModel.getObject(); + } + change.setField(Field.priority, priority); // integration branch String mergeTo = mergeToModel.getObject(); diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.html b/src/main/java/com/gitblit/wicket/pages/TicketPage.html index f3f38ec3..f3c6b2a8 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketPage.html +++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.html @@ -67,6 +67,8 @@
+ + diff --git a/src/main/java/com/gitblit/wicket/pages/TicketPage.java b/src/main/java/com/gitblit/wicket/pages/TicketPage.java index c5b0f31b..b140bfca 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketPage.java +++ b/src/main/java/com/gitblit/wicket/pages/TicketPage.java @@ -519,6 +519,10 @@ public class TicketPage extends RepositoryPage { * TICKET METADATA */ add(new Label("ticketType", ticket.type.toString())); + + add(new Label("priority", ticket.priority.toString())); + add(new Label("severity", ticket.severity.toString())); + if (StringUtils.isEmpty(ticket.topic)) { add(new Label("ticketTopic").setVisible(false)); } else { @@ -527,6 +531,8 @@ public class TicketPage extends RepositoryPage { String safeTopic = app().xssFilter().relaxed(topic); add(new Label("ticketTopic", safeTopic).setEscapeModelStrings(false)); } + + /* diff --git a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java index 745cabf4..ecfed250 100644 --- a/src/main/java/com/gitblit/wicket/pages/TicketsPage.java +++ b/src/main/java/com/gitblit/wicket/pages/TicketsPage.java @@ -464,7 +464,11 @@ public class TicketsPage extends RepositoryPage { sortChoices.add(new TicketSort(getString("gb.sortLeastPatchsetRevisions"), Lucene.patchsets.name(), false)); sortChoices.add(new TicketSort(getString("gb.sortMostVotes"), Lucene.votes.name(), true)); sortChoices.add(new TicketSort(getString("gb.sortLeastVotes"), Lucene.votes.name(), false)); - + sortChoices.add(new TicketSort(getString("gb.sortHighestPriority"), Lucene.priority.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestPriority"), Lucene.priority.name(), false)); + sortChoices.add(new TicketSort(getString("gb.sortHighestSeverity"), Lucene.severity.name(), true)); + sortChoices.add(new TicketSort(getString("gb.sortLowestSeverity"), Lucene.severity.name(), false)); + TicketSort currentSort = sortChoices.get(0); for (TicketSort ts : sortChoices) { if (ts.sortBy.equals(sortBy) && desc == ts.desc) { diff --git a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html index 30f50367..659baeac 100644 --- a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html +++ b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.html @@ -30,6 +30,9 @@ + diff --git a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java index cc0b57a8..b4c43cb8 100644 --- a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java @@ -53,7 +53,7 @@ import com.gitblit.wicket.pages.UserPage; public class TicketListPanel extends BasePanel { private static final long serialVersionUID = 1L; - + public TicketListPanel(String wicketId, List list, final boolean showSwatch, final boolean showRepository) { super(wicketId); @@ -83,7 +83,10 @@ public class TicketListPanel extends BasePanel { item.add(new Label("ticketsLink").setVisible(false)); } - item.add(TicketsUI.getStateIcon("state", ticket.type, ticket.status)); + Label icon = TicketsUI.getStateIcon("state", ticket.type, ticket.status); + WicketUtils.addCssClass(icon, TicketsUI.getSeverityClass(ticket.severity)); + item.add(icon); + item.add(new Label("id", "" + ticket.number)); UserModel creator = app().users().getUserModel(ticket.createdBy); if (creator != null) { @@ -167,6 +170,11 @@ public class TicketListPanel extends BasePanel { // watching indicator item.add(new Label("watching").setVisible(ticket.isWatching(GitBlitWebSession.get().getUsername()))); + // priority indicator + Label priorityIcon = TicketsUI.getPriorityIcon("priority", ticket.priority); + WicketUtils.addCssClass(priorityIcon, TicketsUI.getPriorityClass(ticket.priority)); + item.add(priorityIcon.setVisible(true)); + // status indicator String css = TicketsUI.getLozengeClass(ticket.status, true); Label l = new Label("status", ticket.status.toString()); diff --git a/src/main/resources/gitblit.css b/src/main/resources/gitblit.css index 748a3198..ba6f534a 100644 --- a/src/main/resources/gitblit.css +++ b/src/main/resources/gitblit.css @@ -782,6 +782,10 @@ pre.prettyprint ol { td.ticket-list-state { vertical-align: middle; +} + +td.ticket-list-priority { + vertical-align: middle; } .ticket-list-details { @@ -2079,4 +2083,72 @@ div.markdown table.text th, div.markdown table.text td { background-color: #fff; border-color: #ece7e2; color: #815b3a; -} \ No newline at end of file +} +.severity-catastrophic { + color:#CC79A7; +} +.severity-catastrophic:after { + font-family: Helvetica,arial,freesans,clean,sans-serif ; + content: "Ca"; + font-weight:900; + font-size:.6em; + font-variant:small-caps; + display:flex; +} +.severity-critical { + color:#D55E00; +} +.severity-critical:after { + font-family: Helvetica,arial,freesans,clean,sans-serif ; + content: "c"; + font-weight:900; + font-size:.6em; + font-variant:small-caps; + display:flex; +} +.severity-serious { + color:#E69F00; +} +.severity-serious:after { + font-family: Helvetica,arial,freesans,clean,sans-serif ; + content: "s"; + font-weight:900; + font-size:.6em; + font-variant:small-caps; + display:flex; +} +.severity-minor { + color:#0072B2; +} +.severity-minor:after { + font-family: Helvetica,arial,freesans,clean,sans-serif ; + content: "m"; + font-weight:900; + font-size:.6em; + font-variant:small-caps; + display:flex; +} +.severity-negligible { + color:#009E73; +} +.severity-negligible:after { + font-family: Helvetica,arial,freesans,clean,sans-serif ; + content: "n"; + font-weight:900; + font-size:.6em; + font-variant:small-caps; + display:flex; +} +.severity-unrated { +} +.priority-urgent { + color:#CC79A7; +} +.priority-high { + color:#D55E00; +} +.priority-normal { +} +.priority-low { + color:#0072B2; +} diff --git a/src/test/java/com/gitblit/tests/GitBlitSuite.java b/src/test/java/com/gitblit/tests/GitBlitSuite.java index 5a7dcea1..f2dfcc0c 100644 --- a/src/test/java/com/gitblit/tests/GitBlitSuite.java +++ b/src/test/java/com/gitblit/tests/GitBlitSuite.java @@ -65,7 +65,7 @@ import com.gitblit.utils.JGitUtils; FanoutServiceTest.class, Issue0259Test.class, Issue0271Test.class, HtpasswdAuthenticationTest.class, ModelUtilsTest.class, JnaUtilsTest.class, LdapSyncServiceTest.class, FileTicketServiceTest.class, BranchTicketServiceTest.class, RedisTicketServiceTest.class, AuthenticationManagerTest.class, - SshKeysDispatcherTest.class }) + SshKeysDispatcherTest.class, UITicketTest.class }) public class GitBlitSuite { public static final File BASEFOLDER = new File("data"); diff --git a/src/test/java/com/gitblit/tests/TicketServiceTest.java b/src/test/java/com/gitblit/tests/TicketServiceTest.java index 1676e341..c654383d 100644 --- a/src/test/java/com/gitblit/tests/TicketServiceTest.java +++ b/src/test/java/com/gitblit/tests/TicketServiceTest.java @@ -293,9 +293,47 @@ public abstract class TicketServiceTest extends GitblitUnitTest { assertTrue("failed to delete label " + label.name, service.deleteLabel(getRepository(), label.name, "lucifer")); } } - - - + + @Test + public void testPriorityAndSeverity() throws Exception { + // C1: create and insert a ticket + Change c1 = newChange("testPriorityAndSeverity() " + Long.toHexString(System.currentTimeMillis())); + TicketModel ticket = service.createTicket(getRepository(), c1); + assertTrue(ticket.number > 0); + assertEquals(TicketModel.Priority.Normal, ticket.priority); + assertEquals(TicketModel.Severity.Unrated, ticket.severity); + + TicketModel constructed = service.getTicket(getRepository(), ticket.number); + compare(ticket, constructed); + + // C2: Change Priority max + Change c2 = new Change("C2"); + c2.setField(Field.priority, TicketModel.Priority.Urgent); + constructed = service.updateTicket(getRepository(), ticket.number, c2); + assertNotNull(constructed); + assertEquals(2, constructed.changes.size()); + assertEquals(TicketModel.Priority.Urgent, constructed.priority); + assertEquals(TicketModel.Severity.Unrated, constructed.severity); + + // C3: Change Severity max + Change c3 = new Change("C3"); + c3.setField(Field.severity, TicketModel.Severity.Catastrophic); + constructed = service.updateTicket(getRepository(), ticket.number, c3); + assertNotNull(constructed); + assertEquals(3, constructed.changes.size()); + assertEquals(TicketModel.Priority.Urgent, constructed.priority); + assertEquals(TicketModel.Severity.Catastrophic, constructed.severity); + + // C4: Change Priority min + Change c4 = new Change("C3"); + c4.setField(Field.priority, TicketModel.Priority.Low); + constructed = service.updateTicket(getRepository(), ticket.number, c4); + assertNotNull(constructed); + assertEquals(4, constructed.changes.size()); + assertEquals(TicketModel.Priority.Low, constructed.priority); + assertEquals(TicketModel.Severity.Catastrophic, constructed.severity); + } + private Change newChange(String summary) { Change change = new Change("C1"); change.setField(Field.title, summary); diff --git a/src/test/java/com/gitblit/tests/UITicketTest.java b/src/test/java/com/gitblit/tests/UITicketTest.java new file mode 100644 index 00000000..54aa1e1e --- /dev/null +++ b/src/test/java/com/gitblit/tests/UITicketTest.java @@ -0,0 +1,151 @@ +/* + * Copyright 2014 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.tests; + +import java.io.File; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.bouncycastle.util.Arrays; +import org.eclipse.jgit.lib.Repository; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.gitblit.IStoredSettings; +import com.gitblit.Keys; +import com.gitblit.manager.INotificationManager; +import com.gitblit.manager.IPluginManager; +import com.gitblit.manager.IRepositoryManager; +import com.gitblit.manager.IRuntimeManager; +import com.gitblit.manager.IUserManager; +import com.gitblit.manager.NotificationManager; +import com.gitblit.manager.PluginManager; +import com.gitblit.manager.RepositoryManager; +import com.gitblit.manager.RuntimeManager; +import com.gitblit.manager.UserManager; +import com.gitblit.models.Mailing; +import com.gitblit.models.RepositoryModel; +import com.gitblit.models.TicketModel; +import com.gitblit.models.TicketModel.Attachment; +import com.gitblit.models.TicketModel.Change; +import com.gitblit.models.TicketModel.Field; +import com.gitblit.models.TicketModel.Patchset; +import com.gitblit.models.TicketModel.Priority; +import com.gitblit.models.TicketModel.Severity; +import com.gitblit.models.TicketModel.Status; +import com.gitblit.models.TicketModel.Type; +import com.gitblit.tests.mock.MemorySettings; +import com.gitblit.tickets.ITicketService; +import com.gitblit.tickets.ITicketService.TicketFilter; +import com.gitblit.tickets.QueryResult; +import com.gitblit.tickets.TicketIndexer.Lucene; +import com.gitblit.tickets.BranchTicketService; +import com.gitblit.tickets.TicketLabel; +import com.gitblit.tickets.TicketMilestone; +import com.gitblit.tickets.TicketNotifier; +import com.gitblit.utils.JGitUtils; +import com.gitblit.utils.XssFilter; +import com.gitblit.utils.XssFilter.AllowXssFilter; + +/** + * Generates the range of tickets to ease testing of the look and feel of tickets + */ +public class UITicketTest extends GitblitUnitTest { + + private ITicketService service; + final String repoName = "UITicketTest.git"; + final RepositoryModel repo = new RepositoryModel(repoName, null, null, null); + + protected ITicketService getService(boolean deleteAll) throws Exception { + + IStoredSettings settings = getSettings(deleteAll); + XssFilter xssFilter = new AllowXssFilter(); + IRuntimeManager runtimeManager = new RuntimeManager(settings, xssFilter).start(); + IPluginManager pluginManager = new PluginManager(runtimeManager).start(); + INotificationManager notificationManager = new NotificationManager(settings).start(); + IUserManager userManager = new UserManager(runtimeManager, pluginManager).start(); + IRepositoryManager repositoryManager = new RepositoryManager(runtimeManager, pluginManager, userManager).start(); + + BranchTicketService service = new BranchTicketService( + runtimeManager, + pluginManager, + notificationManager, + userManager, + repositoryManager).start(); + + if (deleteAll) { + service.deleteAll(repo); + } + return service; + } + + protected IStoredSettings getSettings(boolean deleteAll) throws Exception { + File dir = new File(GitBlitSuite.REPOSITORIES, repoName); + if (deleteAll) { + FileUtils.deleteDirectory(dir); + JGitUtils.createRepository(GitBlitSuite.REPOSITORIES, repoName).close(); + } + + File luceneDir = new File(dir, "tickets/lucene"); + luceneDir.mkdirs(); + + Map map = new HashMap(); + map.put(Keys.git.repositoriesFolder, GitBlitSuite.REPOSITORIES.getAbsolutePath()); + map.put(Keys.tickets.indexFolder, luceneDir.getAbsolutePath()); + + IStoredSettings settings = new MemorySettings(map); + return settings; + } + + @Before + public void setup() throws Exception { + service = getService(true); + } + + @After + public void cleanup() { + service.stop(); + } + + @Test + public void UITicketOptions() throws Exception { + + for (TicketModel.Type t : TicketModel.Type.values()) + { + for (TicketModel.Priority p : TicketModel.Priority.values()) + { + for (TicketModel.Severity s : TicketModel.Severity.values()) + { + assertNotNull(service.createTicket(repo, newChange(t, p, s))); + } + } + } + } + + private Change newChange(Type type, Priority priority, Severity severity) { + Change change = new Change("JUnit"); + change.setField(Field.title, String.format("Type: %s | Priority: %s | Severity: %s", type, priority, severity)); + change.setField(Field.type, type); + change.setField(Field.severity, severity); + change.setField(Field.priority, priority); + return change; + } + +} \ No newline at end of file -- cgit v1.2.3 From 997edf95e627b72c4ae2d17de5012dd2eec58f39 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 21 Oct 2014 09:33:29 -0400 Subject: Adjust priority & severity CSS - Severity: more severe are ROY, less severe are GB - Replaced first-letter of severity with S# and show # dots - Priority: higher priorityare RO, lower priority is B I really like the 'C, 'CA' idea but that will definitely lead to translation questions. The S# approach will still generate the same complaints, but at least it is more generic. --- src/main/resources/gitblit.css | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/main/resources/gitblit.css b/src/main/resources/gitblit.css index ba6f534a..e517cfa9 100644 --- a/src/main/resources/gitblit.css +++ b/src/main/resources/gitblit.css @@ -2085,67 +2085,72 @@ div.markdown table.text th, div.markdown table.text td { color: #815b3a; } .severity-catastrophic { - color:#CC79A7; + color:#D51900; } .severity-catastrophic:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: "Ca"; + content: "S5\a●●●●●"; font-weight:900; - font-size:.6em; + font-size:.45em; font-variant:small-caps; display:flex; + white-space: pre; } .severity-critical { - color:#D55E00; + color:#D55900; } .severity-critical:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: "c"; + content: "S4\a●●●●"; font-weight:900; - font-size:.6em; + font-size:.45em; font-variant:small-caps; display:flex; + white-space: pre; } .severity-serious { color:#E69F00; } .severity-serious:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: "s"; + content: " S3\a ●●●"; font-weight:900; - font-size:.6em; + font-size:.45em; font-variant:small-caps; display:flex; + white-space: pre; } .severity-minor { - color:#0072B2; + color:#009E73; } .severity-minor:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: "m"; + content: " S2\a ●●"; font-weight:900; - font-size:.6em; + font-size:.45em; font-variant:small-caps; display:flex; + white-space: pre; } .severity-negligible { - color:#009E73; + color:#0072B2; } .severity-negligible:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: "n"; + content: " S1\a ●"; font-weight:900; - font-size:.6em; + font-size:.45em; font-variant:small-caps; display:flex; + white-space: pre; } .severity-unrated { } .priority-urgent { - color:#CC79A7; + color:#D51900; } .priority-high { - color:#D55E00; + color:#D55900; } .priority-normal { } -- cgit v1.2.3 From ad80a949ae334a18d5757df729198801299011dc Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 21 Oct 2014 13:28:15 -0400 Subject: Remove S# text, set severity in tooltip --- src/main/java/com/gitblit/wicket/TicketsUI.java | 27 ++++++++++++---------- .../com/gitblit/wicket/panels/TicketListPanel.java | 8 +++---- src/main/resources/gitblit.css | 10 ++++---- 3 files changed, 24 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/main/java/com/gitblit/wicket/TicketsUI.java b/src/main/java/com/gitblit/wicket/TicketsUI.java index 2367c984..8d599523 100644 --- a/src/main/java/com/gitblit/wicket/TicketsUI.java +++ b/src/main/java/com/gitblit/wicket/TicketsUI.java @@ -38,12 +38,12 @@ public class TicketsUI { public static final String [] openStatii = new String [] { Status.New.name().toLowerCase(), Status.Open.name().toLowerCase() }; public static final String [] closedStatii = new String [] { "!" + Status.New.name().toLowerCase(), "!" + Status.Open.name().toLowerCase() }; - + public static Label getStateIcon(String wicketId, TicketModel ticket) { - return getStateIcon(wicketId, ticket.type, ticket.status); + return getStateIcon(wicketId, ticket.type, ticket.status, ticket.severity); } - public static Label getStateIcon(String wicketId, Type type, Status state) { + public static Label getStateIcon(String wicketId, Type type, Status state, Severity severity) { Label label = new Label(wicketId); if (type == null) { type = Type.defaultType; @@ -68,11 +68,11 @@ public class TicketsUI { // standard ticket WicketUtils.setCssClass(label, "fa fa-ticket fa-fw"); } - WicketUtils.setHtmlTooltip(label, getTypeState(type, state)); - + WicketUtils.setHtmlTooltip(label, getTypeState(type, state, severity)); + return label; } - + public static Label getPriorityIcon(String wicketId, Priority priority) { Label label = new Label(wicketId); if (priority == null) { @@ -91,20 +91,23 @@ public class TicketsUI { default: } WicketUtils.setHtmlTooltip(label, priority.toString()); - + return label; } - + public static String getPriorityClass(Priority priority) { return String.format("priority-%s", priority); } public static String getSeverityClass(Severity severity) { - return String.format("severity-%s", severity); + return String.format("severity-%s", severity); } - - public static String getTypeState(Type type, Status state) { - return state.toString() + " " + type.toString(); + + public static String getTypeState(Type type, Status state, Severity severity) { + if (Severity.Unrated == severity) { + return state.toString() + " " + type.toString(); + } + return state.toString() + " " + type.toString() + ", " + severity.toString(); } public static String getLozengeClass(Status status, boolean subtle) { diff --git a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java index b4c43cb8..2ac33d53 100644 --- a/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java +++ b/src/main/java/com/gitblit/wicket/panels/TicketListPanel.java @@ -53,7 +53,7 @@ import com.gitblit.wicket.pages.UserPage; public class TicketListPanel extends BasePanel { private static final long serialVersionUID = 1L; - + public TicketListPanel(String wicketId, List list, final boolean showSwatch, final boolean showRepository) { super(wicketId); @@ -83,10 +83,10 @@ public class TicketListPanel extends BasePanel { item.add(new Label("ticketsLink").setVisible(false)); } - Label icon = TicketsUI.getStateIcon("state", ticket.type, ticket.status); + Label icon = TicketsUI.getStateIcon("state", ticket.type, ticket.status, ticket.severity); WicketUtils.addCssClass(icon, TicketsUI.getSeverityClass(ticket.severity)); item.add(icon); - + item.add(new Label("id", "" + ticket.number)); UserModel creator = app().users().getUserModel(ticket.createdBy); if (creator != null) { @@ -174,7 +174,7 @@ public class TicketListPanel extends BasePanel { Label priorityIcon = TicketsUI.getPriorityIcon("priority", ticket.priority); WicketUtils.addCssClass(priorityIcon, TicketsUI.getPriorityClass(ticket.priority)); item.add(priorityIcon.setVisible(true)); - + // status indicator String css = TicketsUI.getLozengeClass(ticket.status, true); Label l = new Label("status", ticket.status.toString()); diff --git a/src/main/resources/gitblit.css b/src/main/resources/gitblit.css index e517cfa9..4cc9ab6b 100644 --- a/src/main/resources/gitblit.css +++ b/src/main/resources/gitblit.css @@ -2089,7 +2089,7 @@ div.markdown table.text th, div.markdown table.text td { } .severity-catastrophic:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: "S5\a●●●●●"; + content: "●●●●●"; font-weight:900; font-size:.45em; font-variant:small-caps; @@ -2101,7 +2101,7 @@ div.markdown table.text th, div.markdown table.text td { } .severity-critical:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: "S4\a●●●●"; + content: "●●●●"; font-weight:900; font-size:.45em; font-variant:small-caps; @@ -2113,7 +2113,7 @@ div.markdown table.text th, div.markdown table.text td { } .severity-serious:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: " S3\a ●●●"; + content: " ●●●"; font-weight:900; font-size:.45em; font-variant:small-caps; @@ -2125,7 +2125,7 @@ div.markdown table.text th, div.markdown table.text td { } .severity-minor:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: " S2\a ●●"; + content: " ●●"; font-weight:900; font-size:.45em; font-variant:small-caps; @@ -2137,7 +2137,7 @@ div.markdown table.text th, div.markdown table.text td { } .severity-negligible:after { font-family: Helvetica,arial,freesans,clean,sans-serif ; - content: " S1\a ●"; + content: " ●"; font-weight:900; font-size:.45em; font-variant:small-caps; -- cgit v1.2.3
[type]
[priority]
[severity]
[topic]
[responsible]
[milestone]
+
+