@@ -81,6 +81,7 @@ import com.gitblit.wicket.pages.TicketsPage; | |||
import com.gitblit.wicket.pages.TreePage; | |||
import com.gitblit.wicket.pages.UserPage; | |||
import com.gitblit.wicket.pages.UsersPage; | |||
import com.gitblit.wicket.pages.MyTicketsPage; | |||
public class GitBlitWebApp extends WebApplication { | |||
@@ -191,6 +192,7 @@ public class GitBlitWebApp extends WebApplication { | |||
mount("/tickets/export", ExportTicketPage.class, "r", "h"); | |||
mount("/milestones/new", NewMilestonePage.class, "r"); | |||
mount("/milestones/edit", EditMilestonePage.class, "r", "h"); | |||
mount("/mytickets", MyTicketsPage.class, "r", "h"); | |||
// setup the markup document urls | |||
mount("/docs", DocsPage.class, "r"); |
@@ -672,9 +672,11 @@ gb.ticketIsClosed = This ticket is closed. | |||
gb.mergeToDescription = default integration branch for merging ticket patchsets | |||
gb.anonymousCanNotPropose = Anonymous users can not propose patchsets. | |||
gb.youDoNotHaveClonePermission = You are not permitted to clone this repository. | |||
gb.myTickets = my tickets | |||
gb.yourAssignedTickets = assigned to you | |||
gb.newMilestone = new milestone | |||
gb.editMilestone = edit milestone | |||
gb.notifyChangedOpenTickets = send notification for changed open tickets | |||
gb.overdue = overdue | |||
gb.openMilestones = open milestones | |||
gb.closedMilestones = closed milestones | |||
gb.closedMilestones = closed milestones |
@@ -670,3 +670,5 @@ gb.repositoryDoesNotAcceptPatchsets = Ce d | |||
gb.serverDoesNotAcceptPatchsets = Ce serveur n'accepte pas de patchsets. | |||
gb.ticketIsClosed = Ce ticket est clos. | |||
gb.mergeToDescription = default integration branch for merging ticket patchsets | |||
gb.myTickets = mes tickets | |||
gb.yourAssignedTickets = dont vous êtes responsable |
@@ -22,6 +22,7 @@ import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.ComponentTag; | |||
import org.apache.wicket.markup.MarkupStream; | |||
import org.apache.wicket.markup.html.form.StatelessForm; | |||
import org.apache.wicket.protocol.http.RequestUtils; | |||
import org.apache.wicket.protocol.http.WicketURLDecoder; | |||
import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy; | |||
import org.apache.wicket.util.string.AppendingStringBuffer; | |||
@@ -53,9 +54,9 @@ public class SessionlessForm<T> extends StatelessForm<T> { | |||
private static final String HIDDEN_DIV_START = "<div style=\"width:0px;height:0px;position:absolute;left:-100px;top:-100px;overflow:hidden\">"; | |||
private final Class<? extends BasePage> pageClass; | |||
protected final Class<? extends BasePage> pageClass; | |||
private final PageParameters pageParameters; | |||
protected final PageParameters pageParameters; | |||
private final Logger log = LoggerFactory.getLogger(SessionlessForm.class); | |||
@@ -145,4 +146,14 @@ public class SessionlessForm<T> extends StatelessForm<T> { | |||
String un = WicketURLDecoder.QUERY_INSTANCE.decode(s); | |||
return Strings.escapeMarkup(un).toString(); | |||
} | |||
protected String getAbsoluteUrl() { | |||
return getAbsoluteUrl(pageClass, pageParameters); | |||
} | |||
protected String getAbsoluteUrl(Class<? extends BasePage> pageClass, PageParameters pageParameters) { | |||
String relativeUrl = urlFor(pageClass, pageParameters).toString(); | |||
String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl); | |||
return absoluteUrl; | |||
} | |||
} |
@@ -0,0 +1,211 @@ | |||
/* | |||
* 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.wicket; | |||
import java.io.Serializable; | |||
import java.text.MessageFormat; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import com.gitblit.models.TicketModel; | |||
import com.gitblit.models.TicketModel.Status; | |||
import com.gitblit.models.TicketModel.Type; | |||
import com.gitblit.utils.StringUtils; | |||
/** | |||
* Common tickets ui methods and classes. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
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); | |||
} | |||
public static Label getStateIcon(String wicketId, Type type, Status state) { | |||
Label label = new Label(wicketId); | |||
if (type == null) { | |||
type = Type.defaultType; | |||
} | |||
switch (type) { | |||
case Proposal: | |||
WicketUtils.setCssClass(label, "fa fa-code-fork"); | |||
break; | |||
case Bug: | |||
WicketUtils.setCssClass(label, "fa fa-bug"); | |||
break; | |||
case Enhancement: | |||
WicketUtils.setCssClass(label, "fa fa-magic"); | |||
break; | |||
case Question: | |||
WicketUtils.setCssClass(label, "fa fa-question"); | |||
break; | |||
default: | |||
// standard ticket | |||
WicketUtils.setCssClass(label, "fa fa-ticket"); | |||
} | |||
WicketUtils.setHtmlTooltip(label, getTypeState(type, state)); | |||
return label; | |||
} | |||
public static String getTypeState(Type type, Status state) { | |||
return state.toString() + " " + type.toString(); | |||
} | |||
public static String getLozengeClass(Status status, boolean subtle) { | |||
if (status == null) { | |||
status = Status.New; | |||
} | |||
String css = ""; | |||
switch (status) { | |||
case Declined: | |||
case Duplicate: | |||
case Invalid: | |||
case Wontfix: | |||
case Abandoned: | |||
css = "aui-lozenge-error"; | |||
break; | |||
case Fixed: | |||
case Merged: | |||
case Resolved: | |||
css = "aui-lozenge-success"; | |||
break; | |||
case New: | |||
css = "aui-lozenge-complete"; | |||
break; | |||
case On_Hold: | |||
css = "aui-lozenge-current"; | |||
break; | |||
default: | |||
css = ""; | |||
break; | |||
} | |||
return "aui-lozenge" + (subtle ? " aui-lozenge-subtle": "") + (css.isEmpty() ? "" : " ") + css; | |||
} | |||
public static String getStatusClass(Status status) { | |||
String css = ""; | |||
switch (status) { | |||
case Declined: | |||
case Duplicate: | |||
case Invalid: | |||
case Wontfix: | |||
case Abandoned: | |||
css = "resolution-error"; | |||
break; | |||
case Fixed: | |||
case Merged: | |||
case Resolved: | |||
css = "resolution-success"; | |||
break; | |||
case New: | |||
css = "resolution-complete"; | |||
break; | |||
case On_Hold: | |||
css = "resolution-current"; | |||
break; | |||
default: | |||
css = ""; | |||
break; | |||
} | |||
return "resolution" + (css.isEmpty() ? "" : " ") + css; | |||
} | |||
public static class TicketSort implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
public final String name; | |||
public final String sortBy; | |||
public final boolean desc; | |||
public TicketSort(String name, String sortBy, boolean desc) { | |||
this.name = name; | |||
this.sortBy = sortBy; | |||
this.desc = desc; | |||
} | |||
} | |||
public static class Indicator implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
public final String css; | |||
public final int count; | |||
public final String tooltip; | |||
public Indicator(String css, String tooltip) { | |||
this.css = css; | |||
this.tooltip = tooltip; | |||
this.count = 0; | |||
} | |||
public Indicator(String css, int count, String pattern) { | |||
this.css = css; | |||
this.count = count; | |||
this.tooltip = StringUtils.isEmpty(pattern) ? "" : MessageFormat.format(pattern, count); | |||
} | |||
public String getTooltip() { | |||
return tooltip; | |||
} | |||
} | |||
public static class TicketQuery implements Serializable, Comparable<TicketQuery> { | |||
private static final long serialVersionUID = 1L; | |||
public final String name; | |||
public final String query; | |||
public String color; | |||
public TicketQuery(String name, String query) { | |||
this.name = name; | |||
this.query = query; | |||
} | |||
public TicketQuery color(String value) { | |||
this.color = value; | |||
return this; | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (o instanceof TicketQuery) { | |||
return ((TicketQuery) o).query.equals(query); | |||
} | |||
return false; | |||
} | |||
@Override | |||
public int hashCode() { | |||
return query.hashCode(); | |||
} | |||
@Override | |||
public int compareTo(TicketQuery o) { | |||
return query.compareTo(o.query); | |||
} | |||
} | |||
} |
@@ -300,7 +300,9 @@ public class WicketUtils { | |||
public static PageParameters newRepositoryParameter(String repositoryName) { | |||
Map<String, String> parameterMap = new HashMap<String, String>(); | |||
parameterMap.put("r", repositoryName); | |||
if (!StringUtils.isEmpty(repositoryName)) { | |||
parameterMap.put("r", repositoryName); | |||
} | |||
return new PageParameters(parameterMap); | |||
} | |||
@@ -0,0 +1,84 @@ | |||
<!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:extend> | |||
<div class="container"> | |||
<div class="row" style="padding-top:15px;min-height:500px;" > | |||
<div class="tab-pane active" id="tickets"> | |||
<!-- query controls --> | |||
<div class="span3"> | |||
<div class="hidden-phone"> | |||
<div wicket:id="userTitlePanel"></div> | |||
<!-- search tickets form --> | |||
<form class="form-search" style="margin: 10px 0px;" wicket:id="ticketSearchForm"> | |||
<div class="input-append"> | |||
<input type="text" class="input-medium search-query" style="border-radius: 14px 0 0 14px; padding-left: 14px;" id="ticketSearchBox" wicket:id="ticketSearchBox" value=""/> | |||
<button class="btn" style="border-radius: 0 14px 14px 0px;margin-left:-5px;" type="submit"><i class="icon-search"></i></button> | |||
</div> | |||
</form> | |||
<!-- query list --> | |||
<ul class="nav nav-list"> | |||
<li class="nav-header"><wicket:message key="gb.queries"></wicket:message></li> | |||
<li><a wicket:id="changesQuery"><i class="fa fa-code-fork"></i> <wicket:message key="gb.proposalTickets"></wicket:message></a></li> | |||
<li><a wicket:id="bugsQuery"><i class="fa fa-bug"></i> <wicket:message key="gb.bugTickets"></wicket:message></a></li> | |||
<li><a wicket:id="enhancementsQuery"><i class="fa fa-magic"></i> <wicket:message key="gb.enhancementTickets"></wicket:message></a></li> | |||
<li><a wicket:id="tasksQuery"><i class="fa fa-ticket"></i> <wicket:message key="gb.taskTickets"></wicket:message></a></li> | |||
<li><a wicket:id="questionsQuery"><i class="fa fa-question"></i> <wicket:message key="gb.questionTickets"></wicket:message></a></li> | |||
<li wicket:id="userDivider" class="divider"></li> | |||
<li><a wicket:id="createdQuery"><i class="fa fa-user"></i> <wicket:message key="gb.yourCreatedTickets"></wicket:message></a></li> | |||
<li><a wicket:id="responsibleQuery"><i class="fa fa-user"></i> <wicket:message key="gb.yourAssignedTickets"></wicket:message></a></li> | |||
<li><a wicket:id="watchedQuery"><i class="fa fa-eye"></i> <wicket:message key="gb.yourWatchedTickets"></wicket:message></a></li> | |||
<li><a wicket:id="mentionsQuery"><i class="fa fa-comment"></i> <wicket:message key="gb.mentionsMeTickets"></wicket:message></a></li> | |||
<li class="divider"></li> | |||
<li><a wicket:id="resetQuery"><i class="fa fa-bolt"></i> <wicket:message key="gb.reset"></wicket:message></a></li> | |||
</ul> | |||
</div> | |||
</div> | |||
<!-- tickets --> | |||
<div class="span9"> | |||
<div class="btn-toolbar" style="margin-top: 0px;"> | |||
<div class="btn-group"> | |||
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#"><wicket:message key="gb.status"></wicket:message>: <span style="font-weight:bold;" wicket:id="selectedStatii"></span> <span class="caret"></span></a> | |||
<ul class="dropdown-menu"> | |||
<li><a wicket:id="openTickets"><wicket:message key="gb.open"></wicket:message></a></li> | |||
<li><a wicket:id="closedTickets"><wicket:message key="gb.closed"></wicket:message></a></li> | |||
<li><a wicket:id="allTickets"><wicket:message key="gb.all"></wicket:message></a></li> | |||
<li class="divider"></li> | |||
<li wicket:id="statii"><span wicket:id="statusLink"></span></li> | |||
</ul> | |||
</div> | |||
<div class="btn-group"> | |||
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#"><i class="fa fa-sort"></i> <wicket:message key="gb.sort"></wicket:message>: <span style="font-weight:bold;" wicket:id="currentSort"></span> <span class="caret"></span></a> | |||
<ul class="dropdown-menu"> | |||
<li wicket:id="sort"><span wicket:id="sortLink"></span></li> | |||
</ul> | |||
</div> | |||
<div class="btn-group pull-right"> | |||
<div class="pagination pagination-right pagination-small"> | |||
<ul> | |||
<li><a wicket:id="prevLink"><i class="fa fa-angle-double-left"></i></a></li> | |||
<li wicket:id="pageLink"><span wicket:id="page"></span></li> | |||
<li><a wicket:id="nextLink"><i class="fa fa-angle-double-right"></i></a></li> | |||
</ul> | |||
</div> | |||
</div> | |||
</div> | |||
<div wicket:id="ticketList"></div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</wicket:extend> | |||
</body> | |||
</html> |
@@ -0,0 +1,392 @@ | |||
/* | |||
* 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.wicket.pages; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import org.apache.wicket.markup.html.link.BookmarkablePageLink; | |||
import org.apache.wicket.markup.repeater.Item; | |||
import org.apache.wicket.markup.repeater.data.DataView; | |||
import org.apache.wicket.markup.repeater.data.ListDataProvider; | |||
import com.gitblit.Keys; | |||
import com.gitblit.models.TicketModel; | |||
import com.gitblit.models.TicketModel.Status; | |||
import com.gitblit.models.UserModel; | |||
import com.gitblit.tickets.QueryBuilder; | |||
import com.gitblit.tickets.QueryResult; | |||
import com.gitblit.tickets.TicketIndexer.Lucene; | |||
import com.gitblit.utils.ArrayUtils; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.wicket.GitBlitWebSession; | |||
import com.gitblit.wicket.TicketsUI; | |||
import com.gitblit.wicket.TicketsUI.TicketSort; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.panels.LinkPanel; | |||
import com.gitblit.wicket.panels.TicketListPanel; | |||
import com.gitblit.wicket.panels.TicketSearchForm; | |||
import com.gitblit.wicket.panels.UserTitlePanel; | |||
/** | |||
* My Tickets page | |||
* | |||
* @author Christian Buisson | |||
* @author James Moger | |||
*/ | |||
public class MyTicketsPage extends RootPage { | |||
public MyTicketsPage() { | |||
this(null); | |||
} | |||
public MyTicketsPage(PageParameters params) { | |||
super(params); | |||
setupPage("", getString("gb.myTickets")); | |||
UserModel currentUser = GitBlitWebSession.get().getUser(); | |||
if (currentUser == null || UserModel.ANONYMOUS.equals(currentUser)) { | |||
setRedirect(true); | |||
setResponsePage(getApplication().getHomePage()); | |||
return; | |||
} | |||
final String username = currentUser.getName(); | |||
final String[] statiiParam = (params == null) ? TicketsUI.openStatii : params.getStringArray(Lucene.status.name()); | |||
final String assignedToParam = (params == null) ? "" : params.getString(Lucene.responsible.name(), null); | |||
final String milestoneParam = (params == null) ? "" : params.getString(Lucene.milestone.name(), null); | |||
final String queryParam = (params == null || StringUtils.isEmpty(params.getString("q", null))) ? "watchedby:" + username : params.getString("q", null); | |||
final String searchParam = (params == null) ? "" : params.getString("s", null); | |||
final String sortBy = (params == null) ? "" : Lucene.fromString(params.getString("sort", Lucene.created.name())).name(); | |||
final boolean desc = (params == null) ? true : !"asc".equals(params.getString("direction", "desc")); | |||
// add the user title panel | |||
add(new UserTitlePanel("userTitlePanel", currentUser, getString("gb.myTickets"))); | |||
// add search form | |||
add(new TicketSearchForm("ticketSearchForm", null, searchParam, getClass(), params)); | |||
// standard queries | |||
add(new BookmarkablePageLink<Void>("changesQuery", MyTicketsPage.class, | |||
queryParameters( | |||
Lucene.type.matches(TicketModel.Type.Proposal.name()), | |||
milestoneParam, | |||
statiiParam, | |||
assignedToParam, | |||
sortBy, | |||
desc, | |||
1))); | |||
add(new BookmarkablePageLink<Void>("bugsQuery", MyTicketsPage.class, | |||
queryParameters( | |||
Lucene.type.matches(TicketModel.Type.Bug.name()), | |||
milestoneParam, | |||
statiiParam, | |||
assignedToParam, | |||
sortBy, | |||
desc, | |||
1))); | |||
add(new BookmarkablePageLink<Void>("enhancementsQuery", MyTicketsPage.class, | |||
queryParameters( | |||
Lucene.type.matches(TicketModel.Type.Enhancement.name()), | |||
milestoneParam, | |||
statiiParam, | |||
assignedToParam, | |||
sortBy, | |||
desc, | |||
1))); | |||
add(new BookmarkablePageLink<Void>("tasksQuery", MyTicketsPage.class, | |||
queryParameters( | |||
Lucene.type.matches(TicketModel.Type.Task.name()), | |||
milestoneParam, | |||
statiiParam, | |||
assignedToParam, | |||
sortBy, | |||
desc, | |||
1))); | |||
add(new BookmarkablePageLink<Void>("questionsQuery", MyTicketsPage.class, | |||
queryParameters( | |||
Lucene.type.matches(TicketModel.Type.Question.name()), | |||
milestoneParam, | |||
statiiParam, | |||
assignedToParam, | |||
sortBy, | |||
desc, | |||
1))); | |||
add(new BookmarkablePageLink<Void>("resetQuery", MyTicketsPage.class, | |||
queryParameters( | |||
null, | |||
milestoneParam, | |||
TicketsUI.openStatii, | |||
null, | |||
null, | |||
true, | |||
1))); | |||
add(new Label("userDivider")); | |||
add(new BookmarkablePageLink<Void>("createdQuery", MyTicketsPage.class, | |||
queryParameters( | |||
Lucene.createdby.matches(username), | |||
milestoneParam, | |||
statiiParam, | |||
assignedToParam, | |||
sortBy, | |||
desc, | |||
1))); | |||
add(new BookmarkablePageLink<Void>("watchedQuery", MyTicketsPage.class, | |||
queryParameters( | |||
Lucene.watchedby.matches(username), | |||
milestoneParam, | |||
statiiParam, | |||
assignedToParam, | |||
sortBy, | |||
desc, | |||
1))); | |||
add(new BookmarkablePageLink<Void>("mentionsQuery", MyTicketsPage.class, | |||
queryParameters( | |||
Lucene.mentions.matches(username), | |||
milestoneParam, | |||
statiiParam, | |||
assignedToParam, | |||
sortBy, | |||
desc, | |||
1))); | |||
add(new BookmarkablePageLink<Void>("responsibleQuery", MyTicketsPage.class, | |||
queryParameters( | |||
Lucene.responsible.matches(username), | |||
milestoneParam, | |||
statiiParam, | |||
assignedToParam, | |||
sortBy, | |||
desc, | |||
1))); | |||
// states | |||
if (ArrayUtils.isEmpty(statiiParam)) { | |||
add(new Label("selectedStatii", getString("gb.all"))); | |||
} else { | |||
add(new Label("selectedStatii", StringUtils.flattenStrings(Arrays.asList(statiiParam), ","))); | |||
} | |||
add(new BookmarkablePageLink<Void>("openTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.openStatii, assignedToParam, sortBy, desc, 1))); | |||
add(new BookmarkablePageLink<Void>("closedTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.closedStatii, assignedToParam, sortBy, desc, 1))); | |||
add(new BookmarkablePageLink<Void>("allTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, null, assignedToParam, sortBy, desc, 1))); | |||
// by status | |||
List<Status> statii = new ArrayList<Status>(Arrays.asList(Status.values())); | |||
statii.remove(Status.Closed); | |||
ListDataProvider<Status> resolutionsDp = new ListDataProvider<Status>(statii); | |||
DataView<Status> statiiLinks = new DataView<Status>("statii", resolutionsDp) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void populateItem(final Item<Status> item) { | |||
final Status status = item.getModelObject(); | |||
PageParameters p = queryParameters(queryParam, milestoneParam, new String [] { status.name().toLowerCase() }, assignedToParam, sortBy, desc, 1); | |||
String css = TicketsUI.getStatusClass(status); | |||
item.add(new LinkPanel("statusLink", css, status.toString(), MyTicketsPage.class, p).setRenderBodyOnly(true)); | |||
} | |||
}; | |||
add(statiiLinks); | |||
List<TicketSort> sortChoices = new ArrayList<TicketSort>(); | |||
sortChoices.add(new TicketSort(getString("gb.sortNewest"), Lucene.created.name(), true)); | |||
sortChoices.add(new TicketSort(getString("gb.sortOldest"), Lucene.created.name(), false)); | |||
sortChoices.add(new TicketSort(getString("gb.sortMostRecentlyUpdated"), Lucene.updated.name(), true)); | |||
sortChoices.add(new TicketSort(getString("gb.sortLeastRecentlyUpdated"), Lucene.updated.name(), false)); | |||
sortChoices.add(new TicketSort(getString("gb.sortMostComments"), Lucene.comments.name(), true)); | |||
sortChoices.add(new TicketSort(getString("gb.sortLeastComments"), Lucene.comments.name(), false)); | |||
sortChoices.add(new TicketSort(getString("gb.sortMostPatchsetRevisions"), Lucene.patchsets.name(), true)); | |||
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)); | |||
TicketSort currentSort = sortChoices.get(0); | |||
for (TicketSort ts : sortChoices) { | |||
if (ts.sortBy.equals(sortBy) && desc == ts.desc) { | |||
currentSort = ts; | |||
break; | |||
} | |||
} | |||
add(new Label("currentSort", currentSort.name)); | |||
ListDataProvider<TicketSort> sortChoicesDp = new ListDataProvider<TicketSort>(sortChoices); | |||
DataView<TicketSort> sortMenu = new DataView<TicketSort>("sort", sortChoicesDp) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void populateItem(final Item<TicketSort> item) { | |||
final TicketSort ts = item.getModelObject(); | |||
PageParameters params = queryParameters(queryParam, milestoneParam, statiiParam, assignedToParam, ts.sortBy, ts.desc, 1); | |||
item.add(new LinkPanel("sortLink", null, ts.name, MyTicketsPage.class, params).setRenderBodyOnly(true)); | |||
} | |||
}; | |||
add(sortMenu); | |||
// Build Query here | |||
QueryBuilder qb = new QueryBuilder(queryParam); | |||
if (!qb.containsField(Lucene.status.name()) && !ArrayUtils.isEmpty(statiiParam)) { | |||
// specify the states | |||
boolean not = false; | |||
QueryBuilder q = new QueryBuilder(); | |||
for (String state : statiiParam) { | |||
if (state.charAt(0) == '!') { | |||
not = true; | |||
q.and(Lucene.status.doesNotMatch(state.substring(1))); | |||
} else { | |||
q.or(Lucene.status.matches(state)); | |||
} | |||
} | |||
if (not) { | |||
qb.and(q.toString()); | |||
} else { | |||
qb.and(q.toSubquery().toString()); | |||
} | |||
} | |||
final String luceneQuery; | |||
if (qb.containsField(Lucene.createdby.name()) | |||
|| qb.containsField(Lucene.responsible.name()) | |||
|| qb.containsField(Lucene.watchedby.name())) { | |||
// focused "my tickets" query | |||
luceneQuery = qb.build(); | |||
} else { | |||
// general "my tickets" query | |||
QueryBuilder myQuery = new QueryBuilder(); | |||
myQuery.or(Lucene.createdby.matches(username)); | |||
myQuery.or(Lucene.responsible.matches(username)); | |||
myQuery.or(Lucene.watchedby.matches(username)); | |||
myQuery.and(qb.toSubquery().toString()); | |||
luceneQuery = myQuery.build(); | |||
} | |||
// paging links | |||
int page = (params != null) ? Math.max(1, WicketUtils.getPage(params)) : 1; | |||
int pageSize = app().settings().getInteger(Keys.tickets.perPage, 25); | |||
List<QueryResult> results; | |||
if(StringUtils.isEmpty(searchParam)) { | |||
results = app().tickets().queryFor(luceneQuery, page, pageSize, sortBy, desc); | |||
} else { | |||
results = app().tickets().searchFor(null, searchParam, page, pageSize); | |||
} | |||
int totalResults = results.size() == 0 ? 0 : results.get(0).totalResults; | |||
buildPager(queryParam, milestoneParam, statiiParam, assignedToParam, sortBy, desc, page, pageSize, results.size(), totalResults); | |||
final boolean showSwatch = app().settings().getBoolean(Keys.web.repositoryListSwatches, true); | |||
add(new TicketListPanel("ticketList", results, showSwatch, true)); | |||
} | |||
protected PageParameters queryParameters( | |||
String query, | |||
String milestone, | |||
String[] states, | |||
String assignedTo, | |||
String sort, | |||
boolean descending, | |||
int page) { | |||
PageParameters params = WicketUtils.newRepositoryParameter(""); | |||
if (!StringUtils.isEmpty(query)) { | |||
params.add("q", query); | |||
} | |||
if (!StringUtils.isEmpty(milestone)) { | |||
params.add(Lucene.milestone.name(), milestone); | |||
} | |||
if (!ArrayUtils.isEmpty(states)) { | |||
for (String state : states) { | |||
params.add(Lucene.status.name(), state); | |||
} | |||
} | |||
if (!StringUtils.isEmpty(assignedTo)) { | |||
params.add(Lucene.responsible.name(), assignedTo); | |||
} | |||
if (!StringUtils.isEmpty(sort)) { | |||
params.add("sort", sort); | |||
} | |||
if (!descending) { | |||
params.add("direction", "asc"); | |||
} | |||
if (page > 1) { | |||
params.add("pg", "" + page); | |||
} | |||
return params; | |||
} | |||
protected void buildPager( | |||
final String query, | |||
final String milestone, | |||
final String [] states, | |||
final String assignedTo, | |||
final String sort, | |||
final boolean desc, | |||
final int page, | |||
int pageSize, | |||
int count, | |||
int total) { | |||
boolean showNav = total > (2 * pageSize); | |||
boolean allowPrev = page > 1; | |||
boolean allowNext = (pageSize * (page - 1) + count) < total; | |||
add(new BookmarkablePageLink<Void>("prevLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, page - 1)).setEnabled(allowPrev).setVisible(showNav)); | |||
add(new BookmarkablePageLink<Void>("nextLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, page + 1)).setEnabled(allowNext).setVisible(showNav)); | |||
if (total <= pageSize) { | |||
add(new Label("pageLink").setVisible(false)); | |||
return; | |||
} | |||
// determine page numbers to display | |||
int pages = count == 0 ? 0 : ((total / pageSize) + (total % pageSize == 0 ? 0 : 1)); | |||
// preferred number of pagelinks | |||
int segments = 5; | |||
if (pages < segments) { | |||
// not enough data for preferred number of page links | |||
segments = pages; | |||
} | |||
int minpage = Math.min(Math.max(1, page - 2), pages - (segments - 1)); | |||
int maxpage = Math.min(pages, minpage + (segments - 1)); | |||
List<Integer> sequence = new ArrayList<Integer>(); | |||
for (int i = minpage; i <= maxpage; i++) { | |||
sequence.add(i); | |||
} | |||
ListDataProvider<Integer> pagesDp = new ListDataProvider<Integer>(sequence); | |||
DataView<Integer> pagesView = new DataView<Integer>("pageLink", pagesDp) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void populateItem(final Item<Integer> item) { | |||
final Integer i = item.getModelObject(); | |||
LinkPanel link = new LinkPanel("page", null, "" + i, MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, i)); | |||
link.setRenderBodyOnly(true); | |||
if (i == page) { | |||
WicketUtils.setCssClass(item, "active"); | |||
} | |||
item.add(link); | |||
} | |||
}; | |||
add(pagesView); | |||
} | |||
} |
@@ -17,7 +17,7 @@ | |||
<form class="form-search" style="margin: 0px;" wicket:id="searchForm"> | |||
<div class="input-append"> | |||
<select class="span2" style="border-radius: 4px;" wicket:id="searchType"/> | |||
<input type="text" class="search-query" style="width: 170px;border-radius: 14px 0 0 14px; padding-left: 14px;" id="searchBox" wicket:id="searchBox" value=""/> | |||
<input type="text" class="input-medium search-query" style="border-radius: 14px 0 0 14px; padding-left: 14px;" id="searchBox" wicket:id="searchBox" value=""/> | |||
<button class="btn" style="border-radius: 0 14px 14px 0px;margin-left:-5px;" type="submit"><i class="icon-search"></i></button> | |||
</div> | |||
</form> |
@@ -69,6 +69,7 @@ import com.gitblit.wicket.GitBlitWebSession; | |||
import com.gitblit.wicket.PageRegistration; | |||
import com.gitblit.wicket.PageRegistration.OtherPageLink; | |||
import com.gitblit.wicket.SessionlessForm; | |||
import com.gitblit.wicket.TicketsUI; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.panels.LinkPanel; | |||
import com.gitblit.wicket.panels.NavigationPanel; | |||
@@ -204,7 +205,7 @@ public abstract class RepositoryPage extends RootPage { | |||
pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params)); | |||
if (app().tickets().isReady() && (app().tickets().isAcceptingNewTickets(getRepositoryModel()) || app().tickets().hasTickets(getRepositoryModel()))) { | |||
PageParameters tParams = new PageParameters(params); | |||
for (String state : TicketsPage.openStatii) { | |||
for (String state : TicketsUI.openStatii) { | |||
tParams.add(Lucene.status.name(), state); | |||
} | |||
pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, tParams)); |
@@ -134,6 +134,8 @@ public abstract class RootPage extends BasePage { | |||
boolean authenticateView = app().settings().getBoolean(Keys.web.authenticateViewPages, false); | |||
boolean authenticateAdmin = app().settings().getBoolean(Keys.web.authenticateAdminPages, true); | |||
boolean allowAdmin = app().settings().getBoolean(Keys.web.allowAdministration, true); | |||
boolean allowLucene = app().settings().getBoolean(Keys.web.allowLuceneIndexing, true); | |||
boolean isLoggedIn = GitBlitWebSession.get().isLoggedIn(); | |||
if (authenticateAdmin) { | |||
showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin(); | |||
@@ -151,7 +153,7 @@ public abstract class RootPage extends BasePage { | |||
} | |||
if (authenticateView || authenticateAdmin) { | |||
if (GitBlitWebSession.get().isLoggedIn()) { | |||
if (isLoggedIn) { | |||
UserMenu userFragment = new UserMenu("userPanel", "userMenuFragment", RootPage.this); | |||
add(userFragment); | |||
} else { | |||
@@ -167,13 +169,16 @@ public abstract class RootPage extends BasePage { | |||
// navigation links | |||
List<PageRegistration> pages = new ArrayList<PageRegistration>(); | |||
if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) { | |||
pages.add(new PageRegistration(GitBlitWebSession.get().isLoggedIn() ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class, | |||
if (!authenticateView || (authenticateView && isLoggedIn)) { | |||
pages.add(new PageRegistration(isLoggedIn ? "gb.myDashboard" : "gb.dashboard", MyDashboardPage.class, | |||
getRootPageParameters())); | |||
if (isLoggedIn && app().tickets().isReady()) { | |||
pages.add(new PageRegistration("gb.myTickets", MyTicketsPage.class)); | |||
} | |||
pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class, | |||
getRootPageParameters())); | |||
pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters())); | |||
if (app().settings().getBoolean(Keys.web.allowLuceneIndexing, true)) { | |||
if (allowLucene) { | |||
pages.add(new PageRegistration("gb.search", LuceneSearchPage.class)); | |||
} | |||
if (showAdmin) { | |||
@@ -183,7 +188,7 @@ public abstract class RootPage extends BasePage { | |||
pages.add(new PageRegistration("gb.federation", FederationPage.class)); | |||
} | |||
if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) { | |||
if (!authenticateView || (authenticateView && isLoggedIn)) { | |||
addDropDownMenus(pages); | |||
} | |||
} |
@@ -1,126 +0,0 @@ | |||
/* | |||
* Copyright 2013 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.wicket.pages; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import com.gitblit.models.TicketModel; | |||
import com.gitblit.models.TicketModel.Status; | |||
import com.gitblit.models.TicketModel.Type; | |||
import com.gitblit.wicket.WicketUtils; | |||
public abstract class TicketBasePage extends RepositoryPage { | |||
public TicketBasePage(PageParameters params) { | |||
super(params); | |||
} | |||
protected Label getStateIcon(String wicketId, TicketModel ticket) { | |||
return getStateIcon(wicketId, ticket.type, ticket.status); | |||
} | |||
protected Label getStateIcon(String wicketId, Type type, Status state) { | |||
Label label = new Label(wicketId); | |||
if (type == null) { | |||
type = Type.defaultType; | |||
} | |||
switch (type) { | |||
case Proposal: | |||
WicketUtils.setCssClass(label, "fa fa-code-fork"); | |||
break; | |||
case Bug: | |||
WicketUtils.setCssClass(label, "fa fa-bug"); | |||
break; | |||
case Enhancement: | |||
WicketUtils.setCssClass(label, "fa fa-magic"); | |||
break; | |||
case Question: | |||
WicketUtils.setCssClass(label, "fa fa-question"); | |||
break; | |||
default: | |||
// standard ticket | |||
WicketUtils.setCssClass(label, "fa fa-ticket"); | |||
} | |||
WicketUtils.setHtmlTooltip(label, getTypeState(type, state)); | |||
return label; | |||
} | |||
protected String getTypeState(Type type, Status state) { | |||
return state.toString() + " " + type.toString(); | |||
} | |||
protected String getLozengeClass(Status status, boolean subtle) { | |||
if (status == null) { | |||
status = Status.New; | |||
} | |||
String css = ""; | |||
switch (status) { | |||
case Declined: | |||
case Duplicate: | |||
case Invalid: | |||
case Wontfix: | |||
case Abandoned: | |||
css = "aui-lozenge-error"; | |||
break; | |||
case Fixed: | |||
case Merged: | |||
case Resolved: | |||
css = "aui-lozenge-success"; | |||
break; | |||
case New: | |||
css = "aui-lozenge-complete"; | |||
break; | |||
case On_Hold: | |||
css = "aui-lozenge-current"; | |||
break; | |||
default: | |||
css = ""; | |||
break; | |||
} | |||
return "aui-lozenge" + (subtle ? " aui-lozenge-subtle": "") + (css.isEmpty() ? "" : " ") + css; | |||
} | |||
protected String getStatusClass(Status status) { | |||
String css = ""; | |||
switch (status) { | |||
case Declined: | |||
case Duplicate: | |||
case Invalid: | |||
case Wontfix: | |||
case Abandoned: | |||
css = "resolution-error"; | |||
break; | |||
case Fixed: | |||
case Merged: | |||
case Resolved: | |||
css = "resolution-success"; | |||
break; | |||
case New: | |||
css = "resolution-complete"; | |||
break; | |||
case On_Hold: | |||
css = "resolution-current"; | |||
break; | |||
default: | |||
css = ""; | |||
break; | |||
} | |||
return "resolution" + (css.isEmpty() ? "" : " ") + css; | |||
} | |||
} |
@@ -86,6 +86,7 @@ import com.gitblit.utils.MarkdownUtils; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.utils.TimeUtils; | |||
import com.gitblit.wicket.GitBlitWebSession; | |||
import com.gitblit.wicket.TicketsUI; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.panels.BasePanel.JavascriptTextPrompt; | |||
import com.gitblit.wicket.panels.CommentPanel; | |||
@@ -102,7 +103,7 @@ import com.gitblit.wicket.panels.SimpleAjaxLink; | |||
* @author James Moger | |||
* | |||
*/ | |||
public class TicketPage extends TicketBasePage { | |||
public class TicketPage extends RepositoryPage { | |||
static final String NIL = "<nil>"; | |||
@@ -154,7 +155,7 @@ public class TicketPage extends TicketBasePage { | |||
String href = urlFor(TicketsPage.class, params).toString(); | |||
add(new ExternalLink("ticketNumber", href, "#" + ticket.number)); | |||
Label headerStatus = new Label("headerStatus", ticket.status.toString()); | |||
WicketUtils.setCssClass(headerStatus, getLozengeClass(ticket.status, false)); | |||
WicketUtils.setCssClass(headerStatus, TicketsUI.getLozengeClass(ticket.status, false)); | |||
add(headerStatus); | |||
add(new Label("ticketTitle", ticket.title)); | |||
if (currentPatchset == null) { | |||
@@ -317,10 +318,10 @@ public class TicketPage extends TicketBasePage { | |||
* LARGE STATUS INDICATOR WITH ICON (DISCUSSION TAB->SIDE BAR) | |||
*/ | |||
Fragment ticketStatus = new Fragment("ticketStatus", "ticketStatusFragment", this); | |||
Label ticketIcon = getStateIcon("ticketIcon", ticket); | |||
Label ticketIcon = TicketsUI.getStateIcon("ticketIcon", ticket); | |||
ticketStatus.add(ticketIcon); | |||
ticketStatus.add(new Label("ticketStatus", ticket.status.toString())); | |||
WicketUtils.setCssClass(ticketStatus, getLozengeClass(ticket.status, false)); | |||
WicketUtils.setCssClass(ticketStatus, TicketsUI.getLozengeClass(ticket.status, false)); | |||
add(ticketStatus); | |||
@@ -370,7 +371,7 @@ public class TicketPage extends TicketBasePage { | |||
setResponsePage(TicketsPage.class, getPageParameters()); | |||
} | |||
}; | |||
String css = getStatusClass(item.getModel().getObject()); | |||
String css = TicketsUI.getStatusClass(item.getModel().getObject()); | |||
WicketUtils.setCssClass(link, css); | |||
item.add(link); | |||
} | |||
@@ -665,7 +666,7 @@ public class TicketPage extends TicketBasePage { | |||
*/ | |||
Fragment frag = new Fragment("entry", "statusFragment", this); | |||
Label status = new Label("statusChange", entry.getStatus().toString()); | |||
String css = getLozengeClass(entry.getStatus(), false); | |||
String css = TicketsUI.getLozengeClass(entry.getStatus(), false); | |||
WicketUtils.setCssClass(status, css); | |||
for (IBehavior b : status.getBehaviors()) { | |||
if (b instanceof SimpleAttributeModifier) { | |||
@@ -936,7 +937,7 @@ public class TicketPage extends TicketBasePage { | |||
case status: | |||
// special handling for status | |||
Status status = event.getStatus(); | |||
String css = getLozengeClass(status, true); | |||
String css = TicketsUI.getLozengeClass(status, true); | |||
value = String.format("<span class=\"%1$s\">%2$s</span>", css, status.toString()); | |||
break; | |||
default: | |||
@@ -1525,14 +1526,14 @@ public class TicketPage extends TicketBasePage { | |||
switch (type) { | |||
case Rebase: | |||
case Rebase_Squash: | |||
typeCss = getLozengeClass(Status.Declined, false); | |||
typeCss = TicketsUI.getLozengeClass(Status.Declined, false); | |||
break; | |||
case Squash: | |||
case Amend: | |||
typeCss = getLozengeClass(Status.On_Hold, false); | |||
typeCss = TicketsUI.getLozengeClass(Status.On_Hold, false); | |||
break; | |||
case Proposal: | |||
typeCss = getLozengeClass(Status.New, false); | |||
typeCss = TicketsUI.getLozengeClass(Status.New, false); | |||
break; | |||
case FastForward: | |||
default: |
@@ -11,7 +11,7 @@ | |||
<div class="hidden-phone pull-right"> | |||
<form class="form-search" style="margin: 0px;" wicket:id="ticketSearchForm"> | |||
<div class="input-append"> | |||
<input type="text" class="search-query" style="width: 170px;border-radius: 14px 0 0 14px; padding-left: 14px;" id="ticketSearchBox" wicket:id="ticketSearchBox" value=""/> | |||
<input type="text" class="input-medium search-query" style="border-radius: 14px 0 0 14px; padding-left: 14px;" id="ticketSearchBox" wicket:id="ticketSearchBox" value=""/> | |||
<button class="btn" style="border-radius: 0 14px 14px 0px;margin-left:-5px;" type="submit"><i class="icon-search"></i></button> | |||
</div> | |||
</form> | |||
@@ -88,42 +88,7 @@ | |||
</div> | |||
</div> | |||
<table class="table tickets"> | |||
<tbody> | |||
<tr wicket:id="ticket"> | |||
<td class="ticket-list-icon"> | |||
<i wicket:id="state"></i> | |||
</td> | |||
<td> | |||
<span wicket:id="title">[title]</span> <span wicket:id="labels" style="font-weight: normal;color:white;"><span class="label" wicket:id="label"></span></span> | |||
<div class="ticket-list-details"> | |||
<span style="padding-right: 10px;" class="hidden-phone"> | |||
<wicket:message key="gb.createdBy"></wicket:message> | |||
<span style="padding: 0px 2px" wicket:id="createdBy">[createdBy]</span> <span class="date" wicket:id="createDate">[create date]</span> | |||
</span> | |||
<span wicket:id="indicators" style="white-space:nowrap;"><i wicket:id="icon"></i> <span style="padding-right:10px;" wicket:id="count"></span></span> | |||
</div> | |||
<div class="hidden-phone" wicket:id="updated"></div> | |||
</td> | |||
<td class="ticket-list-state"> | |||
<span class="badge badge-info" wicket:id="votes"></span> | |||
</td> | |||
<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-state"> | |||
<div wicket:id="status"></div> | |||
</td> | |||
<td class="indicators"> | |||
<div> | |||
<b>#<span wicket:id="id">[id]</span></b> | |||
</div> | |||
<div wicket:id="responsible"></div> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
<div wicket:id="ticketList"></div> | |||
<div class="btn-group pull-right"> | |||
<div class="pagination pagination-right pagination-small"> | |||
@@ -240,13 +205,6 @@ | |||
</ul> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="updatedFragment"> | |||
<div class="ticket-list-details"> | |||
<wicket:message key="gb.updatedBy"></wicket:message> | |||
<span style="padding: 0px 2px" wicket:id="updatedBy">[updatedBy]</span> <span class="date" wicket:id="updateDate">[update date]</span> | |||
</div> | |||
</wicket:fragment> | |||
</wicket:extend> | |||
</body> | |||
</html> |
@@ -15,7 +15,6 @@ | |||
*/ | |||
package com.gitblit.wicket.pages; | |||
import java.io.Serializable; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
@@ -29,17 +28,12 @@ import org.apache.wicket.Component; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.behavior.SimpleAttributeModifier; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import org.apache.wicket.markup.html.form.TextField; | |||
import org.apache.wicket.markup.html.link.BookmarkablePageLink; | |||
import org.apache.wicket.markup.html.panel.Fragment; | |||
import org.apache.wicket.markup.repeater.Item; | |||
import org.apache.wicket.markup.repeater.data.DataView; | |||
import org.apache.wicket.markup.repeater.data.ListDataProvider; | |||
import org.apache.wicket.model.IModel; | |||
import org.apache.wicket.model.Model; | |||
import org.apache.wicket.request.target.basic.RedirectRequestTarget; | |||
import com.gitblit.Constants; | |||
import com.gitblit.Constants.AccessPermission; | |||
import com.gitblit.Keys; | |||
import com.gitblit.models.RegistrantAccessPermission; | |||
@@ -56,19 +50,18 @@ import com.gitblit.tickets.TicketResponsible; | |||
import com.gitblit.utils.ArrayUtils; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.wicket.GitBlitWebSession; | |||
import com.gitblit.wicket.SessionlessForm; | |||
import com.gitblit.wicket.TicketsUI; | |||
import com.gitblit.wicket.TicketsUI.TicketQuery; | |||
import com.gitblit.wicket.TicketsUI.TicketSort; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.panels.GravatarImage; | |||
import com.gitblit.wicket.panels.LinkPanel; | |||
import com.gitblit.wicket.panels.TicketListPanel; | |||
import com.gitblit.wicket.panels.TicketSearchForm; | |||
public class TicketsPage extends TicketBasePage { | |||
public class TicketsPage extends RepositoryPage { | |||
final TicketResponsible any; | |||
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 TicketsPage(PageParameters params) { | |||
super(params); | |||
@@ -102,11 +95,8 @@ public class TicketsPage extends TicketBasePage { | |||
final String sortBy = Lucene.fromString(params.getString("sort", Lucene.created.name())).name(); | |||
final boolean desc = !"asc".equals(params.getString("direction", "desc")); | |||
// add search form | |||
TicketSearchForm searchForm = new TicketSearchForm("ticketSearchForm", repositoryName, searchParam); | |||
add(searchForm); | |||
searchForm.setTranslatedAttributes(); | |||
add(new TicketSearchForm("ticketSearchForm", repositoryName, searchParam, getClass(), params)); | |||
final String activeQuery; | |||
if (!StringUtils.isEmpty(searchParam)) { | |||
@@ -192,12 +182,12 @@ public class TicketsPage extends TicketBasePage { | |||
milestonePanel.add(new LinkPanel("openTickets", null, | |||
MessageFormat.format(getString("gb.nOpenTickets"), currentMilestone.getOpenTickets()), | |||
TicketsPage.class, | |||
queryParameters(null, currentMilestone.name, openStatii, null, sortBy, desc, 1))); | |||
queryParameters(null, currentMilestone.name, TicketsUI.openStatii, null, sortBy, desc, 1))); | |||
milestonePanel.add(new LinkPanel("closedTickets", null, | |||
MessageFormat.format(getString("gb.nClosedTickets"), currentMilestone.getClosedTickets()), | |||
TicketsPage.class, | |||
queryParameters(null, currentMilestone.name, closedStatii, null, sortBy, desc, 1))); | |||
queryParameters(null, currentMilestone.name, TicketsUI.closedStatii, null, sortBy, desc, 1))); | |||
milestonePanel.add(new Label("totalTickets", MessageFormat.format(getString("gb.nTotalTickets"), currentMilestone.getTotalTickets()))); | |||
add(milestonePanel); | |||
@@ -287,7 +277,7 @@ public class TicketsPage extends TicketBasePage { | |||
queryParameters( | |||
null, | |||
milestoneParam, | |||
openStatii, | |||
TicketsUI.openStatii, | |||
null, | |||
null, | |||
true, | |||
@@ -397,8 +387,8 @@ public class TicketsPage extends TicketBasePage { | |||
} else { | |||
add(new Label("selectedStatii", StringUtils.flattenStrings(Arrays.asList(statiiParam), ","))); | |||
} | |||
add(new BookmarkablePageLink<Void>("openTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, openStatii, assignedToParam, sortBy, desc, 1))); | |||
add(new BookmarkablePageLink<Void>("closedTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, closedStatii, assignedToParam, sortBy, desc, 1))); | |||
add(new BookmarkablePageLink<Void>("openTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.openStatii, assignedToParam, sortBy, desc, 1))); | |||
add(new BookmarkablePageLink<Void>("closedTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.closedStatii, assignedToParam, sortBy, desc, 1))); | |||
add(new BookmarkablePageLink<Void>("allTickets", TicketsPage.class, queryParameters(queryParam, milestoneParam, null, assignedToParam, sortBy, desc, 1))); | |||
// by status | |||
@@ -412,7 +402,7 @@ public class TicketsPage extends TicketBasePage { | |||
public void populateItem(final Item<Status> item) { | |||
final Status status = item.getModelObject(); | |||
PageParameters p = queryParameters(queryParam, milestoneParam, new String [] { status.name().toLowerCase() }, assignedToParam, sortBy, desc, 1); | |||
String css = getStatusClass(status); | |||
String css = TicketsUI.getStatusClass(status); | |||
item.add(new LinkPanel("statusLink", css, status.toString(), TicketsPage.class, p).setRenderBodyOnly(true)); | |||
} | |||
}; | |||
@@ -491,162 +481,7 @@ public class TicketsPage extends TicketBasePage { | |||
// paging links | |||
buildPager(queryParam, milestoneParam, statiiParam, assignedToParam, sortBy, desc, page, pageSize, results.size(), totalResults); | |||
ListDataProvider<QueryResult> resultsDataProvider = new ListDataProvider<QueryResult>(results); | |||
DataView<QueryResult> ticketsView = new DataView<QueryResult>("ticket", resultsDataProvider) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void populateItem(final Item<QueryResult> item) { | |||
final QueryResult ticket = item.getModelObject(); | |||
item.add(getStateIcon("state", ticket.type, ticket.status)); | |||
item.add(new Label("id", "" + ticket.number)); | |||
UserModel creator = app().users().getUserModel(ticket.createdBy); | |||
if (creator != null) { | |||
item.add(new LinkPanel("createdBy", null, creator.getDisplayName(), | |||
UserPage.class, WicketUtils.newUsernameParameter(ticket.createdBy))); | |||
} else { | |||
item.add(new Label("createdBy", ticket.createdBy)); | |||
} | |||
item.add(WicketUtils.createDateLabel("createDate", ticket.createdAt, GitBlitWebSession | |||
.get().getTimezone(), getTimeUtils(), false)); | |||
if (ticket.updatedAt == null) { | |||
item.add(new Label("updated").setVisible(false)); | |||
} else { | |||
Fragment updated = new Fragment("updated", "updatedFragment", this); | |||
UserModel updater = app().users().getUserModel(ticket.updatedBy); | |||
if (updater != null) { | |||
updated.add(new LinkPanel("updatedBy", null, updater.getDisplayName(), | |||
UserPage.class, WicketUtils.newUsernameParameter(ticket.updatedBy))); | |||
} else { | |||
updated.add(new Label("updatedBy", ticket.updatedBy)); | |||
} | |||
updated.add(WicketUtils.createDateLabel("updateDate", ticket.updatedAt, GitBlitWebSession | |||
.get().getTimezone(), getTimeUtils(), false)); | |||
item.add(updated); | |||
} | |||
item.add(new LinkPanel("title", "list subject", StringUtils.trimString( | |||
ticket.title, Constants.LEN_SHORTLOG), TicketsPage.class, newTicketParameter(ticket))); | |||
ListDataProvider<String> labelsProvider = new ListDataProvider<String>(ticket.getLabels()); | |||
DataView<String> labelsView = new DataView<String>("labels", labelsProvider) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void populateItem(final Item<String> labelItem) { | |||
String content = bugtraqProcessor().processPlainCommitMessage(getRepository(), repositoryName, labelItem.getModelObject()); | |||
Label label = new Label("label", content); | |||
label.setEscapeModelStrings(false); | |||
TicketLabel tLabel = app().tickets().getLabel(getRepositoryModel(), labelItem.getModelObject()); | |||
String background = MessageFormat.format("background-color:{0};", tLabel.color); | |||
label.add(new SimpleAttributeModifier("style", background)); | |||
labelItem.add(label); | |||
} | |||
}; | |||
item.add(labelsView); | |||
if (StringUtils.isEmpty(ticket.responsible)) { | |||
item.add(new Label("responsible").setVisible(false)); | |||
} else { | |||
UserModel responsible = app().users().getUserModel(ticket.responsible); | |||
if (responsible == null) { | |||
responsible = new UserModel(ticket.responsible); | |||
} | |||
GravatarImage avatar = new GravatarImage("responsible", responsible.getDisplayName(), | |||
responsible.emailAddress, null, 16, true); | |||
avatar.setTooltip(getString("gb.responsible") + ": " + responsible.getDisplayName()); | |||
item.add(avatar); | |||
} | |||
// votes indicator | |||
Label v = new Label("votes", "" + ticket.votesCount); | |||
WicketUtils.setHtmlTooltip(v, getString("gb.votes")); | |||
item.add(v.setVisible(ticket.votesCount > 0)); | |||
// watching indicator | |||
item.add(new Label("watching").setVisible(ticket.isWatching(GitBlitWebSession.get().getUsername()))); | |||
// status indicator | |||
String css = getLozengeClass(ticket.status, true); | |||
Label l = new Label("status", ticket.status.toString()); | |||
WicketUtils.setCssClass(l, css); | |||
item.add(l); | |||
// add the ticket indicators/icons | |||
List<Indicator> indicators = new ArrayList<Indicator>(); | |||
// comments | |||
if (ticket.commentsCount > 0) { | |||
int count = ticket.commentsCount; | |||
String pattern = "gb.nComments"; | |||
if (count == 1) { | |||
pattern = "gb.oneComment"; | |||
} | |||
indicators.add(new Indicator("fa fa-comment", count, pattern)); | |||
} | |||
// participants | |||
if (!ArrayUtils.isEmpty(ticket.participants)) { | |||
int count = ticket.participants.size(); | |||
if (count > 1) { | |||
String pattern = "gb.nParticipants"; | |||
indicators.add(new Indicator("fa fa-user", count, pattern)); | |||
} | |||
} | |||
// attachments | |||
if (!ArrayUtils.isEmpty(ticket.attachments)) { | |||
int count = ticket.attachments.size(); | |||
String pattern = "gb.nAttachments"; | |||
if (count == 1) { | |||
pattern = "gb.oneAttachment"; | |||
} | |||
indicators.add(new Indicator("fa fa-file", count, pattern)); | |||
} | |||
// patchset revisions | |||
if (ticket.patchset != null) { | |||
int count = ticket.patchset.commits; | |||
String pattern = "gb.nCommits"; | |||
if (count == 1) { | |||
pattern = "gb.oneCommit"; | |||
} | |||
indicators.add(new Indicator("fa fa-code", count, pattern)); | |||
} | |||
// milestone | |||
if (!StringUtils.isEmpty(ticket.milestone)) { | |||
indicators.add(new Indicator("fa fa-bullseye", ticket.milestone)); | |||
} | |||
ListDataProvider<Indicator> indicatorsDp = new ListDataProvider<Indicator>(indicators); | |||
DataView<Indicator> indicatorsView = new DataView<Indicator>("indicators", indicatorsDp) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void populateItem(final Item<Indicator> item) { | |||
Indicator indicator = item.getModelObject(); | |||
String tooltip = indicator.getTooltip(); | |||
Label icon = new Label("icon"); | |||
WicketUtils.setCssClass(icon, indicator.css); | |||
item.add(icon); | |||
if (indicator.count > 0) { | |||
Label count = new Label("count", "" + indicator.count); | |||
item.add(count.setVisible(!StringUtils.isEmpty(tooltip))); | |||
} else { | |||
item.add(new Label("count").setVisible(false)); | |||
} | |||
WicketUtils.setHtmlTooltip(item, tooltip); | |||
} | |||
}; | |||
item.add(indicatorsView); | |||
} | |||
}; | |||
add(ticketsView); | |||
add(new TicketListPanel("ticketList", results, false, false)); | |||
// new milestone link | |||
RepositoryModel repositoryModel = getRepositoryModel(); | |||
@@ -747,12 +582,12 @@ public class TicketsPage extends TicketBasePage { | |||
milestonePanel.add(new LinkPanel("openTickets", null, | |||
MessageFormat.format(getString("gb.nOpenTickets"), m.getOpenTickets()), | |||
TicketsPage.class, | |||
queryParameters(null, tm.name, openStatii, null, null, true, 1))); | |||
queryParameters(null, tm.name, TicketsUI.openStatii, null, null, true, 1))); | |||
milestonePanel.add(new LinkPanel("closedTickets", null, | |||
MessageFormat.format(getString("gb.nClosedTickets"), m.getClosedTickets()), | |||
TicketsPage.class, | |||
queryParameters(null, tm.name, closedStatii, null, null, true, 1))); | |||
queryParameters(null, tm.name, TicketsUI.closedStatii, null, null, true, 1))); | |||
milestonePanel.add(new Label("totalTickets", MessageFormat.format(getString("gb.nTotalTickets"), m.getTotalTickets()))); | |||
entryPanel.add(milestonePanel); | |||
@@ -864,123 +699,4 @@ public class TicketsPage extends TicketBasePage { | |||
}; | |||
add(pagesView); | |||
} | |||
private class Indicator implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
final String css; | |||
final int count; | |||
final String tooltip; | |||
Indicator(String css, String tooltip) { | |||
this.css = css; | |||
this.tooltip = tooltip; | |||
this.count = 0; | |||
} | |||
Indicator(String css, int count, String pattern) { | |||
this.css = css; | |||
this.count = count; | |||
this.tooltip = StringUtils.isEmpty(pattern) ? "" : MessageFormat.format(getString(pattern), count); | |||
} | |||
String getTooltip() { | |||
return tooltip; | |||
} | |||
} | |||
private class TicketQuery implements Serializable, Comparable<TicketQuery> { | |||
private static final long serialVersionUID = 1L; | |||
final String name; | |||
final String query; | |||
String color; | |||
TicketQuery(String name, String query) { | |||
this.name = name; | |||
this.query = query; | |||
} | |||
TicketQuery color(String value) { | |||
this.color = value; | |||
return this; | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (o instanceof TicketQuery) { | |||
return ((TicketQuery) o).query.equals(query); | |||
} | |||
return false; | |||
} | |||
@Override | |||
public int hashCode() { | |||
return query.hashCode(); | |||
} | |||
@Override | |||
public int compareTo(TicketQuery o) { | |||
return query.compareTo(o.query); | |||
} | |||
} | |||
private class TicketSort implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
final String name; | |||
final String sortBy; | |||
final boolean desc; | |||
TicketSort(String name, String sortBy, boolean desc) { | |||
this.name = name; | |||
this.sortBy = sortBy; | |||
this.desc = desc; | |||
} | |||
} | |||
private class TicketSearchForm extends SessionlessForm<Void> implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
private final String repositoryName; | |||
private final IModel<String> searchBoxModel;; | |||
public TicketSearchForm(String id, String repositoryName, String text) { | |||
super(id, TicketsPage.this.getClass(), TicketsPage.this.getPageParameters()); | |||
this.repositoryName = repositoryName; | |||
this.searchBoxModel = new Model<String>(text == null ? "" : text); | |||
TextField<String> searchBox = new TextField<String>("ticketSearchBox", searchBoxModel); | |||
add(searchBox); | |||
} | |||
void setTranslatedAttributes() { | |||
WicketUtils.setHtmlTooltip(get("ticketSearchBox"), | |||
MessageFormat.format(getString("gb.searchTicketsTooltip"), repositoryName)); | |||
WicketUtils.setInputPlaceholder(get("ticketSearchBox"), getString("gb.searchTickets")); | |||
} | |||
@Override | |||
public void onSubmit() { | |||
String searchString = searchBoxModel.getObject(); | |||
if (StringUtils.isEmpty(searchString)) { | |||
// redirect to self to avoid wicket page update bug | |||
String absoluteUrl = getCanonicalUrl(); | |||
getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl)); | |||
return; | |||
} | |||
// use an absolute url to workaround Wicket-Tomcat problems with | |||
// mounted url parameters (issue-111) | |||
PageParameters params = WicketUtils.newRepositoryParameter(repositoryName); | |||
params.add("s", searchString); | |||
String absoluteUrl = getCanonicalUrl(TicketsPage.class, params); | |||
getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl)); | |||
} | |||
} | |||
} |
@@ -0,0 +1,55 @@ | |||
<!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> | |||
<table class="table tickets"> | |||
<tbody> | |||
<tr wicket:id="row"> | |||
<td class="ticket-list-icon"> | |||
<i wicket:id="state"></i> | |||
</td> | |||
<td> | |||
<span wicket:id="title">[title]</span> <span wicket:id="labels" style="font-weight: normal;color:white;"><span class="label" wicket:id="label"></span></span> | |||
<div class="ticket-list-details"> | |||
<span style="padding-right: 10px;" class="hidden-phone"> | |||
<wicket:message key="gb.createdBy"></wicket:message> | |||
<span style="padding: 0px 2px" wicket:id="createdBy">[createdBy]</span> <span class="date" wicket:id="createDate">[create date]</span> | |||
</span> | |||
<span wicket:id="indicators" style="white-space:nowrap;"><i wicket:id="icon"></i> <span style="padding-right:10px;" wicket:id="count"></span></span> | |||
</div> | |||
<div class="hidden-phone" wicket:id="updated"></div> | |||
<div class="ticket-list-details"><span class="activitySwatch" wicket:id="repositoryLink">[repository link]</span></div> | |||
</td> | |||
<td class="ticket-list-state"> | |||
<span class="badge badge-info" wicket:id="votes"></span> | |||
</td> | |||
<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-state"> | |||
<div wicket:id="status"></div> | |||
</td> | |||
<td class="indicators"> | |||
<div> | |||
<b>#<span wicket:id="id">[id]</span></b> | |||
</div> | |||
<div wicket:id="responsible"></div> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
<wicket:fragment wicket:id="updatedFragment"> | |||
<div class="ticket-list-details"> | |||
<wicket:message key="gb.updatedBy"></wicket:message> | |||
<span style="padding: 0px 2px" wicket:id="updatedBy">[updatedBy]</span> <span class="date" wicket:id="updateDate">[update date]</span> | |||
</div> | |||
</wicket:fragment> | |||
</wicket:panel> | |||
</body> | |||
</html> |
@@ -0,0 +1,243 @@ | |||
/* | |||
* 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.wicket.panels; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.behavior.SimpleAttributeModifier; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import org.apache.wicket.markup.html.panel.Fragment; | |||
import org.apache.wicket.markup.repeater.Item; | |||
import org.apache.wicket.markup.repeater.data.DataView; | |||
import org.apache.wicket.markup.repeater.data.ListDataProvider; | |||
import org.eclipse.jgit.lib.Repository; | |||
import com.gitblit.Constants; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.models.UserModel; | |||
import com.gitblit.tickets.QueryResult; | |||
import com.gitblit.tickets.TicketLabel; | |||
import com.gitblit.utils.ArrayUtils; | |||
import com.gitblit.utils.BugtraqProcessor; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.wicket.GitBlitWebSession; | |||
import com.gitblit.wicket.TicketsUI; | |||
import com.gitblit.wicket.TicketsUI.Indicator; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.pages.SummaryPage; | |||
import com.gitblit.wicket.pages.TicketsPage; | |||
import com.gitblit.wicket.pages.UserPage; | |||
/** | |||
* | |||
* The ticket list panel lists tickets in a table. | |||
* | |||
* @author James Moger | |||
* | |||
*/ | |||
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); | |||
final ListDataProvider<QueryResult> dp = new ListDataProvider<QueryResult>(list); | |||
DataView<QueryResult> dataView = new DataView<QueryResult>("row", dp) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
protected void populateItem(Item<QueryResult> item) { | |||
final QueryResult ticket = item.getModelObject(); | |||
final RepositoryModel repository = app().repositories().getRepositoryModel(ticket.repository); | |||
if (showSwatch) { | |||
// set repository color | |||
String color = StringUtils.getColor(StringUtils.stripDotGit(repository.name)); | |||
WicketUtils.setCssStyle(item, MessageFormat.format("border-left: 2px solid {0};", color)); | |||
} | |||
PageParameters rp = WicketUtils.newRepositoryParameter(ticket.repository); | |||
PageParameters tp = WicketUtils.newObjectParameter(ticket.repository, "" + ticket.number); | |||
if (showRepository) { | |||
String name = StringUtils.stripDotGit(ticket.repository); | |||
LinkPanel link = new LinkPanel("repositoryLink", null, name, SummaryPage.class, rp); | |||
WicketUtils.setCssBackground(link, name); | |||
item.add(link); | |||
} else { | |||
item.add(new Label("repositoryLink").setVisible(false)); | |||
} | |||
item.add(TicketsUI.getStateIcon("state", ticket.type, ticket.status)); | |||
item.add(new Label("id", "" + ticket.number)); | |||
UserModel creator = app().users().getUserModel(ticket.createdBy); | |||
if (creator != null) { | |||
item.add(new LinkPanel("createdBy", null, creator.getDisplayName(), | |||
UserPage.class, WicketUtils.newUsernameParameter(ticket.createdBy))); | |||
} else { | |||
item.add(new Label("createdBy", ticket.createdBy)); | |||
} | |||
item.add(WicketUtils.createDateLabel("createDate", ticket.createdAt, GitBlitWebSession | |||
.get().getTimezone(), getTimeUtils(), false)); | |||
if (ticket.updatedAt == null) { | |||
item.add(new Label("updated").setVisible(false)); | |||
} else { | |||
Fragment updated = new Fragment("updated", "updatedFragment", this); | |||
UserModel updater = app().users().getUserModel(ticket.updatedBy); | |||
if (updater != null) { | |||
updated.add(new LinkPanel("updatedBy", null, updater.getDisplayName(), | |||
UserPage.class, WicketUtils.newUsernameParameter(ticket.updatedBy))); | |||
} else { | |||
updated.add(new Label("updatedBy", ticket.updatedBy)); | |||
} | |||
updated.add(WicketUtils.createDateLabel("updateDate", ticket.updatedAt, GitBlitWebSession | |||
.get().getTimezone(), getTimeUtils(), false)); | |||
item.add(updated); | |||
} | |||
item.add(new LinkPanel("title", "list subject", StringUtils.trimString( | |||
ticket.title, Constants.LEN_SHORTLOG), TicketsPage.class, tp)); | |||
ListDataProvider<String> labelsProvider = new ListDataProvider<String>(ticket.getLabels()); | |||
DataView<String> labelsView = new DataView<String>("labels", labelsProvider) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void populateItem(final Item<String> labelItem) { | |||
BugtraqProcessor btp = new BugtraqProcessor(app().settings()); | |||
Repository db = app().repositories().getRepository(repository.name); | |||
String content = btp.processPlainCommitMessage(db, repository.name, labelItem.getModelObject()); | |||
db.close(); | |||
Label label = new Label("label", content); | |||
label.setEscapeModelStrings(false); | |||
TicketLabel tLabel = app().tickets().getLabel(repository, labelItem.getModelObject()); | |||
String background = MessageFormat.format("background-color:{0};", tLabel.color); | |||
label.add(new SimpleAttributeModifier("style", background)); | |||
labelItem.add(label); | |||
} | |||
}; | |||
item.add(labelsView); | |||
if (StringUtils.isEmpty(ticket.responsible)) { | |||
item.add(new Label("responsible").setVisible(false)); | |||
} else { | |||
UserModel responsible = app().users().getUserModel(ticket.responsible); | |||
if (responsible == null) { | |||
responsible = new UserModel(ticket.responsible); | |||
} | |||
GravatarImage avatar = new GravatarImage("responsible", responsible.getDisplayName(), | |||
responsible.emailAddress, null, 16, true); | |||
avatar.setTooltip(getString("gb.responsible") + ": " + responsible.getDisplayName()); | |||
item.add(avatar); | |||
} | |||
// votes indicator | |||
Label v = new Label("votes", "" + ticket.votesCount); | |||
WicketUtils.setHtmlTooltip(v, getString("gb.votes")); | |||
item.add(v.setVisible(ticket.votesCount > 0)); | |||
// watching indicator | |||
item.add(new Label("watching").setVisible(ticket.isWatching(GitBlitWebSession.get().getUsername()))); | |||
// status indicator | |||
String css = TicketsUI.getLozengeClass(ticket.status, true); | |||
Label l = new Label("status", ticket.status.toString()); | |||
WicketUtils.setCssClass(l, css); | |||
item.add(l); | |||
// add the ticket indicators/icons | |||
List<Indicator> indicators = new ArrayList<Indicator>(); | |||
// comments | |||
if (ticket.commentsCount > 0) { | |||
int count = ticket.commentsCount; | |||
String pattern = getString("gb.nComments"); | |||
if (count == 1) { | |||
pattern = getString("gb.oneComment"); | |||
} | |||
indicators.add(new Indicator("fa fa-comment", count, pattern)); | |||
} | |||
// participants | |||
if (!ArrayUtils.isEmpty(ticket.participants)) { | |||
int count = ticket.participants.size(); | |||
if (count > 1) { | |||
String pattern = getString("gb.nParticipants"); | |||
indicators.add(new Indicator("fa fa-user", count, pattern)); | |||
} | |||
} | |||
// attachments | |||
if (!ArrayUtils.isEmpty(ticket.attachments)) { | |||
int count = ticket.attachments.size(); | |||
String pattern = getString("gb.nAttachments"); | |||
if (count == 1) { | |||
pattern = getString("gb.oneAttachment"); | |||
} | |||
indicators.add(new Indicator("fa fa-file", count, pattern)); | |||
} | |||
// patchset revisions | |||
if (ticket.patchset != null) { | |||
int count = ticket.patchset.commits; | |||
String pattern = getString("gb.nCommits"); | |||
if (count == 1) { | |||
pattern = getString("gb.oneCommit"); | |||
} | |||
indicators.add(new Indicator("fa fa-code", count, pattern)); | |||
} | |||
// milestone | |||
if (!StringUtils.isEmpty(ticket.milestone)) { | |||
indicators.add(new Indicator("fa fa-bullseye", ticket.milestone)); | |||
} | |||
ListDataProvider<Indicator> indicatorsDp = new ListDataProvider<Indicator>(indicators); | |||
DataView<Indicator> indicatorsView = new DataView<Indicator>("indicators", indicatorsDp) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
public void populateItem(final Item<Indicator> item) { | |||
Indicator indicator = item.getModelObject(); | |||
String tooltip = indicator.getTooltip(); | |||
Label icon = new Label("icon"); | |||
WicketUtils.setCssClass(icon, indicator.css); | |||
item.add(icon); | |||
if (indicator.count > 0) { | |||
Label count = new Label("count", "" + indicator.count); | |||
item.add(count.setVisible(!StringUtils.isEmpty(tooltip))); | |||
} else { | |||
item.add(new Label("count").setVisible(false)); | |||
} | |||
WicketUtils.setHtmlTooltip(item, tooltip); | |||
} | |||
}; | |||
item.add(indicatorsView); | |||
} | |||
}; | |||
add(dataView); | |||
} | |||
} | |||
@@ -0,0 +1,78 @@ | |||
/* | |||
* 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.wicket.panels; | |||
import java.io.Serializable; | |||
import java.text.MessageFormat; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.markup.html.form.TextField; | |||
import org.apache.wicket.model.IModel; | |||
import org.apache.wicket.model.Model; | |||
import org.apache.wicket.request.target.basic.RedirectRequestTarget; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.wicket.SessionlessForm; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.pages.BasePage; | |||
public class TicketSearchForm extends SessionlessForm<Void> implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
private final String repositoryName; | |||
private final IModel<String> searchBoxModel; | |||
public TicketSearchForm(String id, String repositoryName, String text, | |||
Class<? extends BasePage> pageClass, PageParameters params) { | |||
super(id, pageClass, params); | |||
this.repositoryName = repositoryName; | |||
this.searchBoxModel = new Model<String>(text == null ? "" : text); | |||
TextField<String> searchBox = new TextField<String>("ticketSearchBox", searchBoxModel); | |||
add(searchBox); | |||
} | |||
@Override | |||
protected | |||
void onInitialize() { | |||
super.onInitialize(); | |||
WicketUtils.setHtmlTooltip(get("ticketSearchBox"), | |||
MessageFormat.format(getString("gb.searchTicketsTooltip"), "")); | |||
WicketUtils.setInputPlaceholder(get("ticketSearchBox"), getString("gb.searchTickets")); | |||
} | |||
@Override | |||
public void onSubmit() { | |||
String searchString = searchBoxModel.getObject(); | |||
if (StringUtils.isEmpty(searchString)) { | |||
// redirect to self to avoid wicket page update bug | |||
String absoluteUrl = getAbsoluteUrl(); | |||
getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl)); | |||
return; | |||
} | |||
// use an absolute url to workaround Wicket-Tomcat problems with | |||
// mounted url parameters (issue-111) | |||
PageParameters params = WicketUtils.newRepositoryParameter(repositoryName); | |||
params.add("s", searchString); | |||
String absoluteUrl = getAbsoluteUrl(pageClass, params); | |||
getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl)); | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
<!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> | |||
<div style="display:inline-block;vertical-align:top;padding: 0px 2px 2px;"><img wicket:id="userGravatar"></img></div> | |||
<div style="display:inline-block;"> | |||
<div style="font-size:1.5em;" wicket:id="userDisplayName"></div> | |||
<div style="color:#888;font-size:1.2em;padding-top:4px;"><span wicket:id="userTitle"></span></div> | |||
</div> | |||
</wicket:panel> | |||
</body> | |||
</html> |
@@ -0,0 +1,32 @@ | |||
/* | |||
* 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.wicket.panels; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
import com.gitblit.models.UserModel; | |||
public class UserTitlePanel extends BasePanel { | |||
private static final long serialVersionUID = 1L; | |||
public UserTitlePanel(String wicketId, UserModel user, String title) { | |||
super(wicketId); | |||
add(new GravatarImage("userGravatar", user, "gravatar", 36, false)); | |||
add(new Label("userDisplayName", user.getDisplayName())); | |||
add(new Label("userTitle", title)); | |||
} | |||
} |