+ 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-<x> & severity-<x> + UITicketTest created to generate tickets of all types to ease debuggingtags/v1.7.0
@@ -91,6 +91,10 @@ public class TicketModel implements Serializable, Comparable<TicketModel> { | |||
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<TicketModel> { | |||
changes = new ArrayList<Change>(); | |||
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<TicketModel> { | |||
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<TicketModel> { | |||
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<TicketModel> { | |||
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; | |||
} | |||
} | |||
} |
@@ -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; |
@@ -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<String> attachments = new ArrayList<String>(); | |||
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 |
@@ -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 | |||
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 |
@@ -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(); | |||
} |
@@ -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<String>(value), " ")); | |||
} | |||
public static void setCssStyle(Component container, String value) { | |||
container.add(new SimpleAttributeModifier("style", value)); | |||
} |
@@ -41,6 +41,8 @@ | |||
<tr wicket:id="status"></tr> | |||
<tr wicket:id="responsible"></tr> | |||
<tr wicket:id="milestone"></tr> | |||
<tr><th><wicket:message key="gb.priority"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="priority"></select></td></tr> | |||
<tr><th><wicket:message key="gb.severity"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="severity"></select></td></tr> | |||
<tr wicket:id="mergeto"></tr> | |||
</table> | |||
</div> |
@@ -83,6 +83,10 @@ public class EditTicketPage extends RepositoryPage { | |||
private IModel<TicketMilestone> milestoneModel; | |||
private Label descriptionPreview; | |||
private IModel<TicketModel.Priority> priorityModel; | |||
private IModel<TicketModel.Severity> 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<TicketMilestone>("milestone", milestoneModel, milestones)); | |||
form.add(milestone.setVisible(!milestones.isEmpty())); | |||
// priority | |||
List<TicketModel.Priority> priorityChoices = Arrays.asList(TicketModel.Priority.choices()); | |||
form.add(new DropDownChoice<TicketModel.Priority>("priority", priorityModel, priorityChoices)); | |||
// severity | |||
List<TicketModel.Severity> severityChoices = Arrays.asList(TicketModel.Severity.choices()); | |||
form.add(new DropDownChoice<TicketModel.Severity>("severity", severityModel, severityChoices)); | |||
// mergeTo (integration branch) | |||
List<String> branches = new ArrayList<String>(); | |||
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))) { |
@@ -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) { |
@@ -41,6 +41,8 @@ | |||
<tr><th><wicket:message key="gb.type"></wicket:message><span style="color:red;">*</span></th><td class="edit"><select class="input-large" wicket:id="type"></select></td></tr> | |||
<tr wicket:id="responsible"></tr> | |||
<tr wicket:id="milestone"></tr> | |||
<tr><th><wicket:message key="gb.priority"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="priority"></select></td></tr> | |||
<tr><th><wicket:message key="gb.severity"></wicket:message></th><td class="edit"><select class="input-large" wicket:id="severity"></select></td></tr> | |||
<tr wicket:id="mergeto"></tr> | |||
</table> | |||
</div> |
@@ -75,6 +75,10 @@ public class NewTicketPage extends RepositoryPage { | |||
private IModel<TicketMilestone> milestoneModel; | |||
private Label descriptionPreview; | |||
private IModel<TicketModel.Priority> priorityModel; | |||
private IModel<TicketModel.Severity> 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<TicketMilestone>("milestone", milestoneModel, milestones)); | |||
form.add(milestone.setVisible(!milestones.isEmpty())); | |||
// priority | |||
form.add(new DropDownChoice<TicketModel.Priority>("priority", priorityModel, Arrays.asList(TicketModel.Priority.choices()))); | |||
//severity | |||
form.add(new DropDownChoice<TicketModel.Severity>("severity", severityModel, Arrays.asList(TicketModel.Severity.choices()))); | |||
// integration branch | |||
List<String> branches = new ArrayList<String>(); | |||
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(); |
@@ -67,6 +67,8 @@ | |||
<div style="border: 1px solid #ccc;padding: 10px;margin: 5px 0px;"> | |||
<table class="summary" style="width: 100%"> | |||
<tr><th><wicket:message key="gb.type"></wicket:message></th><td><span wicket:id="ticketType">[type]</span></td></tr> | |||
<tr><th><wicket:message key="gb.priority"></wicket:message></th><td><span wicket:id="priority">[priority]</span></td></tr> | |||
<tr><th><wicket:message key="gb.severity"></wicket:message></th><td><span wicket:id="severity">[severity]</span></td></tr> | |||
<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> |
@@ -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)); | |||
} | |||
/* |
@@ -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) { |
@@ -30,6 +30,9 @@ | |||
<td class="hidden-phone ticket-list-state"> | |||
<i wicket:message="title:gb.watching" style="color:#888;" class="fa fa-eye" wicket:id="watching"></i> | |||
</td> | |||
<td class="ticket-list-priority"> | |||
<div wicket:id="priority"></div> | |||
</td> | |||
<td class="ticket-list-state"> | |||
<div wicket:id="status"></div> | |||
</td> |
@@ -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<QueryResult> 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()); |
@@ -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; | |||
} | |||
} | |||
.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; | |||
} |
@@ -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"); |
@@ -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); |
@@ -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<String, Object> map = new HashMap<String, Object>(); | |||
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; | |||
} | |||
} |