You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MyTicketsPage.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /*
  2. * Copyright 2014 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.wicket.pages;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.List;
  20. import org.apache.wicket.PageParameters;
  21. import org.apache.wicket.markup.html.basic.Label;
  22. import org.apache.wicket.markup.html.link.BookmarkablePageLink;
  23. import org.apache.wicket.markup.repeater.Item;
  24. import org.apache.wicket.markup.repeater.data.DataView;
  25. import org.apache.wicket.markup.repeater.data.ListDataProvider;
  26. import com.gitblit.Keys;
  27. import com.gitblit.models.TicketModel;
  28. import com.gitblit.models.TicketModel.Status;
  29. import com.gitblit.models.UserModel;
  30. import com.gitblit.tickets.QueryBuilder;
  31. import com.gitblit.tickets.QueryResult;
  32. import com.gitblit.tickets.TicketIndexer.Lucene;
  33. import com.gitblit.utils.ArrayUtils;
  34. import com.gitblit.utils.StringUtils;
  35. import com.gitblit.wicket.GitBlitWebSession;
  36. import com.gitblit.wicket.TicketsUI;
  37. import com.gitblit.wicket.TicketsUI.TicketSort;
  38. import com.gitblit.wicket.WicketUtils;
  39. import com.gitblit.wicket.panels.LinkPanel;
  40. import com.gitblit.wicket.panels.TicketListPanel;
  41. import com.gitblit.wicket.panels.TicketSearchForm;
  42. import com.gitblit.wicket.panels.UserTitlePanel;
  43. /**
  44. * My Tickets page
  45. *
  46. * @author Christian Buisson
  47. * @author James Moger
  48. */
  49. public class MyTicketsPage extends RootPage {
  50. public MyTicketsPage() {
  51. this(null);
  52. }
  53. public MyTicketsPage(PageParameters params) {
  54. super(params);
  55. setupPage("", getString("gb.myTickets"));
  56. UserModel currentUser = GitBlitWebSession.get().getUser();
  57. if (currentUser == null || UserModel.ANONYMOUS.equals(currentUser)) {
  58. setRedirect(true);
  59. setResponsePage(getApplication().getHomePage());
  60. return;
  61. }
  62. final String username = currentUser.getName();
  63. final String[] statiiParam = (params == null) ? TicketsUI.openStatii : params.getStringArray(Lucene.status.name());
  64. final String assignedToParam = (params == null) ? "" : params.getString(Lucene.responsible.name(), null);
  65. final String milestoneParam = (params == null) ? "" : params.getString(Lucene.milestone.name(), null);
  66. final String queryParam = (params == null || StringUtils.isEmpty(params.getString("q", null))) ? "watchedby:" + username : params.getString("q", null);
  67. final String searchParam = (params == null) ? "" : params.getString("s", null);
  68. final String sortBy = (params == null) ? "" : Lucene.fromString(params.getString("sort", Lucene.created.name())).name();
  69. final boolean desc = (params == null) ? true : !"asc".equals(params.getString("direction", "desc"));
  70. // add the user title panel
  71. add(new UserTitlePanel("userTitlePanel", currentUser, getString("gb.myTickets")));
  72. // add search form
  73. add(new TicketSearchForm("ticketSearchForm", null, searchParam, getClass(), params));
  74. // standard queries
  75. add(new BookmarkablePageLink<Void>("changesQuery", MyTicketsPage.class,
  76. queryParameters(
  77. Lucene.type.matches(TicketModel.Type.Proposal.name()),
  78. milestoneParam,
  79. statiiParam,
  80. assignedToParam,
  81. sortBy,
  82. desc,
  83. 1)));
  84. add(new BookmarkablePageLink<Void>("bugsQuery", MyTicketsPage.class,
  85. queryParameters(
  86. Lucene.type.matches(TicketModel.Type.Bug.name()),
  87. milestoneParam,
  88. statiiParam,
  89. assignedToParam,
  90. sortBy,
  91. desc,
  92. 1)));
  93. add(new BookmarkablePageLink<Void>("enhancementsQuery", MyTicketsPage.class,
  94. queryParameters(
  95. Lucene.type.matches(TicketModel.Type.Enhancement.name()),
  96. milestoneParam,
  97. statiiParam,
  98. assignedToParam,
  99. sortBy,
  100. desc,
  101. 1)));
  102. add(new BookmarkablePageLink<Void>("tasksQuery", MyTicketsPage.class,
  103. queryParameters(
  104. Lucene.type.matches(TicketModel.Type.Task.name()),
  105. milestoneParam,
  106. statiiParam,
  107. assignedToParam,
  108. sortBy,
  109. desc,
  110. 1)));
  111. add(new BookmarkablePageLink<Void>("questionsQuery", MyTicketsPage.class,
  112. queryParameters(
  113. Lucene.type.matches(TicketModel.Type.Question.name()),
  114. milestoneParam,
  115. statiiParam,
  116. assignedToParam,
  117. sortBy,
  118. desc,
  119. 1)));
  120. add(new BookmarkablePageLink<Void>("maintenanceQuery", MyTicketsPage.class,
  121. queryParameters(
  122. Lucene.type.matches(TicketModel.Type.Maintenance.name()),
  123. milestoneParam,
  124. statiiParam,
  125. assignedToParam,
  126. sortBy,
  127. desc,
  128. 1)));
  129. add(new BookmarkablePageLink<Void>("resetQuery", MyTicketsPage.class,
  130. queryParameters(
  131. null,
  132. milestoneParam,
  133. TicketsUI.openStatii,
  134. null,
  135. null,
  136. true,
  137. 1)));
  138. add(new Label("userDivider"));
  139. add(new BookmarkablePageLink<Void>("createdQuery", MyTicketsPage.class,
  140. queryParameters(
  141. Lucene.createdby.matches(username),
  142. milestoneParam,
  143. statiiParam,
  144. assignedToParam,
  145. sortBy,
  146. desc,
  147. 1)));
  148. add(new BookmarkablePageLink<Void>("watchedQuery", MyTicketsPage.class,
  149. queryParameters(
  150. Lucene.watchedby.matches(username),
  151. milestoneParam,
  152. statiiParam,
  153. assignedToParam,
  154. sortBy,
  155. desc,
  156. 1)));
  157. add(new BookmarkablePageLink<Void>("mentionsQuery", MyTicketsPage.class,
  158. queryParameters(
  159. Lucene.mentions.matches(username),
  160. milestoneParam,
  161. statiiParam,
  162. assignedToParam,
  163. sortBy,
  164. desc,
  165. 1)));
  166. add(new BookmarkablePageLink<Void>("responsibleQuery", MyTicketsPage.class,
  167. queryParameters(
  168. Lucene.responsible.matches(username),
  169. milestoneParam,
  170. statiiParam,
  171. assignedToParam,
  172. sortBy,
  173. desc,
  174. 1)));
  175. // states
  176. if (ArrayUtils.isEmpty(statiiParam)) {
  177. add(new Label("selectedStatii", getString("gb.all")));
  178. } else {
  179. add(new Label("selectedStatii", StringUtils.flattenStrings(Arrays.asList(statiiParam), ",")));
  180. }
  181. add(new BookmarkablePageLink<Void>("openTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.openStatii, assignedToParam, sortBy, desc, 1)));
  182. add(new BookmarkablePageLink<Void>("closedTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, TicketsUI.closedStatii, assignedToParam, sortBy, desc, 1)));
  183. add(new BookmarkablePageLink<Void>("allTickets", MyTicketsPage.class, queryParameters(queryParam, milestoneParam, null, assignedToParam, sortBy, desc, 1)));
  184. // by status
  185. List<Status> statii = new ArrayList<Status>(Arrays.asList(Status.values()));
  186. statii.remove(Status.Closed);
  187. ListDataProvider<Status> resolutionsDp = new ListDataProvider<Status>(statii);
  188. DataView<Status> statiiLinks = new DataView<Status>("statii", resolutionsDp) {
  189. private static final long serialVersionUID = 1L;
  190. @Override
  191. public void populateItem(final Item<Status> item) {
  192. final Status status = item.getModelObject();
  193. PageParameters p = queryParameters(queryParam, milestoneParam, new String [] { status.name().toLowerCase() }, assignedToParam, sortBy, desc, 1);
  194. String css = TicketsUI.getStatusClass(status);
  195. item.add(new LinkPanel("statusLink", css, status.toString(), MyTicketsPage.class, p).setRenderBodyOnly(true));
  196. }
  197. };
  198. add(statiiLinks);
  199. List<TicketSort> sortChoices = new ArrayList<TicketSort>();
  200. sortChoices.add(new TicketSort(getString("gb.sortNewest"), Lucene.created.name(), true));
  201. sortChoices.add(new TicketSort(getString("gb.sortOldest"), Lucene.created.name(), false));
  202. sortChoices.add(new TicketSort(getString("gb.sortMostRecentlyUpdated"), Lucene.updated.name(), true));
  203. sortChoices.add(new TicketSort(getString("gb.sortLeastRecentlyUpdated"), Lucene.updated.name(), false));
  204. sortChoices.add(new TicketSort(getString("gb.sortMostComments"), Lucene.comments.name(), true));
  205. sortChoices.add(new TicketSort(getString("gb.sortLeastComments"), Lucene.comments.name(), false));
  206. sortChoices.add(new TicketSort(getString("gb.sortMostPatchsetRevisions"), Lucene.patchsets.name(), true));
  207. sortChoices.add(new TicketSort(getString("gb.sortLeastPatchsetRevisions"), Lucene.patchsets.name(), false));
  208. sortChoices.add(new TicketSort(getString("gb.sortMostVotes"), Lucene.votes.name(), true));
  209. sortChoices.add(new TicketSort(getString("gb.sortLeastVotes"), Lucene.votes.name(), false));
  210. sortChoices.add(new TicketSort(getString("gb.sortHighestPriority"), Lucene.priority.name(), true));
  211. sortChoices.add(new TicketSort(getString("gb.sortLowestPriority"), Lucene.priority.name(), false));
  212. sortChoices.add(new TicketSort(getString("gb.sortHighestSeverity"), Lucene.severity.name(), true));
  213. sortChoices.add(new TicketSort(getString("gb.sortLowestSeverity"), Lucene.severity.name(), false));
  214. TicketSort currentSort = sortChoices.get(0);
  215. for (TicketSort ts : sortChoices) {
  216. if (ts.sortBy.equals(sortBy) && desc == ts.desc) {
  217. currentSort = ts;
  218. break;
  219. }
  220. }
  221. add(new Label("currentSort", currentSort.name));
  222. ListDataProvider<TicketSort> sortChoicesDp = new ListDataProvider<TicketSort>(sortChoices);
  223. DataView<TicketSort> sortMenu = new DataView<TicketSort>("sort", sortChoicesDp) {
  224. private static final long serialVersionUID = 1L;
  225. @Override
  226. public void populateItem(final Item<TicketSort> item) {
  227. final TicketSort ts = item.getModelObject();
  228. PageParameters params = queryParameters(queryParam, milestoneParam, statiiParam, assignedToParam, ts.sortBy, ts.desc, 1);
  229. item.add(new LinkPanel("sortLink", null, ts.name, MyTicketsPage.class, params).setRenderBodyOnly(true));
  230. }
  231. };
  232. add(sortMenu);
  233. // Build Query here
  234. QueryBuilder qb = new QueryBuilder(queryParam);
  235. if (!qb.containsField(Lucene.status.name()) && !ArrayUtils.isEmpty(statiiParam)) {
  236. // specify the states
  237. boolean not = false;
  238. QueryBuilder q = new QueryBuilder();
  239. for (String state : statiiParam) {
  240. if (state.charAt(0) == '!') {
  241. not = true;
  242. q.and(Lucene.status.doesNotMatch(state.substring(1)));
  243. } else {
  244. q.or(Lucene.status.matches(state));
  245. }
  246. }
  247. if (not) {
  248. qb.and(q.toString());
  249. } else {
  250. qb.and(q.toSubquery().toString());
  251. }
  252. }
  253. final String luceneQuery;
  254. if (qb.containsField(Lucene.createdby.name())
  255. || qb.containsField(Lucene.responsible.name())
  256. || qb.containsField(Lucene.watchedby.name())) {
  257. // focused "my tickets" query
  258. luceneQuery = qb.build();
  259. } else {
  260. // general "my tickets" query
  261. QueryBuilder myQuery = new QueryBuilder();
  262. myQuery.or(Lucene.createdby.matches(username));
  263. myQuery.or(Lucene.responsible.matches(username));
  264. myQuery.or(Lucene.watchedby.matches(username));
  265. myQuery.and(qb.toSubquery().toString());
  266. luceneQuery = myQuery.build();
  267. }
  268. // paging links
  269. int page = (params != null) ? Math.max(1, WicketUtils.getPage(params)) : 1;
  270. int pageSize = app().settings().getInteger(Keys.tickets.perPage, 25);
  271. List<QueryResult> results;
  272. if(StringUtils.isEmpty(searchParam)) {
  273. results = app().tickets().queryFor(luceneQuery, page, pageSize, sortBy, desc);
  274. } else {
  275. results = app().tickets().searchFor(null, searchParam, page, pageSize);
  276. }
  277. int totalResults = results.size() == 0 ? 0 : results.get(0).totalResults;
  278. buildPager(queryParam, milestoneParam, statiiParam, assignedToParam, sortBy, desc, page, pageSize, results.size(), totalResults);
  279. final boolean showSwatch = app().settings().getBoolean(Keys.web.repositoryListSwatches, true);
  280. add(new TicketListPanel("ticketList", results, showSwatch, true));
  281. }
  282. protected PageParameters queryParameters(
  283. String query,
  284. String milestone,
  285. String[] states,
  286. String assignedTo,
  287. String sort,
  288. boolean descending,
  289. int page) {
  290. PageParameters params = WicketUtils.newRepositoryParameter("");
  291. if (!StringUtils.isEmpty(query)) {
  292. params.add("q", query);
  293. }
  294. if (!StringUtils.isEmpty(milestone)) {
  295. params.add(Lucene.milestone.name(), milestone);
  296. }
  297. if (!ArrayUtils.isEmpty(states)) {
  298. for (String state : states) {
  299. params.add(Lucene.status.name(), state);
  300. }
  301. }
  302. if (!StringUtils.isEmpty(assignedTo)) {
  303. params.add(Lucene.responsible.name(), assignedTo);
  304. }
  305. if (!StringUtils.isEmpty(sort)) {
  306. params.add("sort", sort);
  307. }
  308. if (!descending) {
  309. params.add("direction", "asc");
  310. }
  311. if (page > 1) {
  312. params.add("pg", "" + page);
  313. }
  314. return params;
  315. }
  316. protected void buildPager(
  317. final String query,
  318. final String milestone,
  319. final String [] states,
  320. final String assignedTo,
  321. final String sort,
  322. final boolean desc,
  323. final int page,
  324. int pageSize,
  325. int count,
  326. int total) {
  327. boolean showNav = total > (2 * pageSize);
  328. boolean allowPrev = page > 1;
  329. boolean allowNext = (pageSize * (page - 1) + count) < total;
  330. add(new BookmarkablePageLink<Void>("prevLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, page - 1)).setEnabled(allowPrev).setVisible(showNav));
  331. add(new BookmarkablePageLink<Void>("nextLink", MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, page + 1)).setEnabled(allowNext).setVisible(showNav));
  332. if (total <= pageSize) {
  333. add(new Label("pageLink").setVisible(false));
  334. return;
  335. }
  336. // determine page numbers to display
  337. int pages = count == 0 ? 0 : ((total / pageSize) + (total % pageSize == 0 ? 0 : 1));
  338. // preferred number of pagelinks
  339. int segments = 5;
  340. if (pages < segments) {
  341. // not enough data for preferred number of page links
  342. segments = pages;
  343. }
  344. int minpage = Math.min(Math.max(1, page - 2), pages - (segments - 1));
  345. int maxpage = Math.min(pages, minpage + (segments - 1));
  346. List<Integer> sequence = new ArrayList<Integer>();
  347. for (int i = minpage; i <= maxpage; i++) {
  348. sequence.add(i);
  349. }
  350. ListDataProvider<Integer> pagesDp = new ListDataProvider<Integer>(sequence);
  351. DataView<Integer> pagesView = new DataView<Integer>("pageLink", pagesDp) {
  352. private static final long serialVersionUID = 1L;
  353. @Override
  354. public void populateItem(final Item<Integer> item) {
  355. final Integer i = item.getModelObject();
  356. LinkPanel link = new LinkPanel("page", null, "" + i, MyTicketsPage.class, queryParameters(query, milestone, states, assignedTo, sort, desc, i));
  357. link.setRenderBodyOnly(true);
  358. if (i == page) {
  359. WicketUtils.setCssClass(item, "active");
  360. }
  361. item.add(link);
  362. }
  363. };
  364. add(pagesView);
  365. }
  366. }