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.

RepositoriesPanel.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /*
  2. * Copyright 2011 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.panels;
  17. import java.text.MessageFormat;
  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.Comparator;
  21. import java.util.Date;
  22. import java.util.HashMap;
  23. import java.util.Iterator;
  24. import java.util.List;
  25. import java.util.Map;
  26. import org.apache.wicket.PageParameters;
  27. import org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByBorder;
  28. import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
  29. import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
  30. import org.apache.wicket.markup.html.basic.Label;
  31. import org.apache.wicket.markup.html.link.BookmarkablePageLink;
  32. import org.apache.wicket.markup.html.link.Link;
  33. import org.apache.wicket.markup.html.panel.Fragment;
  34. import org.apache.wicket.markup.repeater.Item;
  35. import org.apache.wicket.markup.repeater.data.DataView;
  36. import org.apache.wicket.markup.repeater.data.IDataProvider;
  37. import org.apache.wicket.markup.repeater.data.ListDataProvider;
  38. import org.apache.wicket.model.IModel;
  39. import org.apache.wicket.model.Model;
  40. import com.gitblit.Constants.AccessRestrictionType;
  41. import com.gitblit.GitBlit;
  42. import com.gitblit.Keys;
  43. import com.gitblit.utils.StringUtils;
  44. import com.gitblit.utils.TimeUtils;
  45. import com.gitblit.wicket.GitBlitWebSession;
  46. import com.gitblit.wicket.LinkPanel;
  47. import com.gitblit.wicket.WicketUtils;
  48. import com.gitblit.wicket.models.RepositoryModel;
  49. import com.gitblit.wicket.models.UserModel;
  50. import com.gitblit.wicket.pages.EditRepositoryPage;
  51. import com.gitblit.wicket.pages.SummaryPage;
  52. public class RepositoriesPanel extends BasePanel {
  53. private static final long serialVersionUID = 1L;
  54. public RepositoriesPanel(String wicketId, final boolean showAdmin, final Map<AccessRestrictionType, String> accessRestrictionTranslations) {
  55. super(wicketId);
  56. final UserModel user = GitBlitWebSession.get().getUser();
  57. List<RepositoryModel> models = GitBlit.self().getRepositoryModels(user);
  58. final IDataProvider<RepositoryModel> dp;
  59. Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this);
  60. adminLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
  61. add(adminLinks.setVisible(showAdmin));
  62. if (GitBlit.self().settings().getString(Keys.web.repositoryListType, "flat").equalsIgnoreCase("grouped")) {
  63. Map<String, List<RepositoryModel>> groups = new HashMap<String, List<RepositoryModel>>();
  64. for (RepositoryModel model : models) {
  65. String rootPath = StringUtils.getRootPath(model.name);
  66. if (StringUtils.isEmpty(rootPath)) {
  67. rootPath = GitBlit.self().settings().getString(Keys.web.repositoryRootGroupName, " ");
  68. }
  69. if (!groups.containsKey(rootPath)) {
  70. groups.put(rootPath, new ArrayList<RepositoryModel>());
  71. }
  72. groups.get(rootPath).add(model);
  73. }
  74. List<String> roots = new ArrayList<String>(groups.keySet());
  75. Collections.sort(roots);
  76. List<RepositoryModel> groupedModels = new ArrayList<RepositoryModel>();
  77. for (String root : roots) {
  78. List<RepositoryModel> subModels = groups.get(root);
  79. groupedModels.add(new GroupRepositoryModel(root, subModels.size()));
  80. groupedModels.addAll(subModels);
  81. }
  82. dp = new RepositoriesProvider(groupedModels);
  83. } else {
  84. dp = new SortableRepositoriesProvider(models);
  85. }
  86. DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) {
  87. private static final long serialVersionUID = 1L;
  88. int counter = 0;
  89. @Override
  90. protected void onBeforeRender() {
  91. super.onBeforeRender();
  92. counter = 0;
  93. }
  94. public void populateItem(final Item<RepositoryModel> item) {
  95. final RepositoryModel entry = item.getModelObject();
  96. if (entry instanceof GroupRepositoryModel) {
  97. Fragment row = new Fragment("rowContent", "groupRepositoryRow", this);
  98. item.add(row);
  99. row.add(new Label("groupName", entry.toString()));
  100. WicketUtils.setCssClass(item, "group");
  101. return;
  102. }
  103. Fragment row = new Fragment("rowContent", "repositoryRow", this);
  104. item.add(row);
  105. if (entry.hasCommits) {
  106. // Existing repository
  107. PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);
  108. row.add(new LinkPanel("repositoryName", "list", entry.name, SummaryPage.class, pp));
  109. row.add(new LinkPanel("repositoryDescription", "list", entry.description, SummaryPage.class, pp));
  110. } else {
  111. // New repository
  112. row.add(new Label("repositoryName", entry.name + "<span class='empty'>(empty)</span>").setEscapeModelStrings(false));
  113. row.add(new Label("repositoryDescription", entry.description));
  114. }
  115. if (entry.useTickets) {
  116. row.add(WicketUtils.newImage("ticketsIcon", "bug_16x16.png", getString("gb.tickets")));
  117. } else {
  118. row.add(WicketUtils.newBlankImage("ticketsIcon"));
  119. }
  120. if (entry.useDocs) {
  121. row.add(WicketUtils.newImage("docsIcon", "book_16x16.png", getString("gb.docs")));
  122. } else {
  123. row.add(WicketUtils.newBlankImage("docsIcon"));
  124. }
  125. if (entry.isFrozen) {
  126. row.add(WicketUtils.newImage("frozenIcon", "cold_16x16.png", getString("gb.isFrozen")));
  127. } else {
  128. row.add(WicketUtils.newClearPixel("frozenIcon").setVisible(false));
  129. }
  130. switch (entry.accessRestriction) {
  131. case NONE:
  132. row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
  133. break;
  134. case PUSH:
  135. row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction)));
  136. break;
  137. case CLONE:
  138. row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction)));
  139. break;
  140. case VIEW:
  141. row.add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction)));
  142. break;
  143. default:
  144. row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
  145. }
  146. row.add(new Label("repositoryOwner", entry.owner));
  147. String lastChange = TimeUtils.timeAgo(entry.lastChange);
  148. Label lastChangeLabel = new Label("repositoryLastChange", lastChange);
  149. row.add(lastChangeLabel);
  150. WicketUtils.setCssClass(lastChangeLabel, TimeUtils.timeAgoCss(entry.lastChange));
  151. boolean showOwner = user != null && user.getUsername().equalsIgnoreCase(entry.owner);
  152. if (showAdmin) {
  153. Fragment repositoryLinks = new Fragment("repositoryLinks", "repositoryAdminLinks", this);
  154. repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name)));
  155. Link<Void> deleteLink = new Link<Void>("deleteRepository") {
  156. private static final long serialVersionUID = 1L;
  157. @Override
  158. public void onClick() {
  159. if (GitBlit.self().deleteRepositoryModel(entry)) {
  160. info(MessageFormat.format("Repository ''{0}'' deleted.", entry));
  161. if (dp instanceof SortableRepositoriesProvider) {
  162. ((SortableRepositoriesProvider) dp).remove(entry);
  163. } else {
  164. ((RepositoriesProvider) dp).remove(entry);
  165. }
  166. } else {
  167. error(MessageFormat.format("Failed to delete repository ''{0}''!", entry));
  168. }
  169. }
  170. };
  171. deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format("Delete repository \"{0}\"?", entry)));
  172. repositoryLinks.add(deleteLink);
  173. row.add(repositoryLinks);
  174. } else if (showOwner) {
  175. Fragment repositoryLinks = new Fragment("repositoryLinks", "repositoryOwnerLinks", this);
  176. repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name)));
  177. row.add(repositoryLinks);
  178. } else {
  179. row.add(new Label("repositoryLinks"));
  180. }
  181. WicketUtils.setAlternatingBackground(item, counter);
  182. counter++;
  183. }
  184. };
  185. add(dataView);
  186. if (dp instanceof SortableDataProvider<?>) {
  187. // add sortable header
  188. SortableDataProvider<?> sdp = (SortableDataProvider<?>) dp;
  189. Fragment fragment = new Fragment("headerContent", "flatRepositoryHeader", this);
  190. fragment.add(newSort("orderByRepository", SortBy.repository, sdp, dataView));
  191. fragment.add(newSort("orderByDescription", SortBy.description, sdp, dataView));
  192. fragment.add(newSort("orderByOwner", SortBy.owner, sdp, dataView));
  193. fragment.add(newSort("orderByDate", SortBy.date, sdp, dataView));
  194. add(fragment);
  195. } else {
  196. // not sortable
  197. Fragment fragment = new Fragment("headerContent", "groupRepositoryHeader", this);
  198. add(fragment);
  199. }
  200. }
  201. private class GroupRepositoryModel extends RepositoryModel {
  202. private static final long serialVersionUID = 1L;
  203. int count = 0;
  204. GroupRepositoryModel(String name, int count) {
  205. super(name, "", "", new Date(0));
  206. this.count = count;
  207. }
  208. @Override
  209. public String toString() {
  210. return name + " (" + count + ")";
  211. }
  212. }
  213. protected enum SortBy {
  214. repository, description, owner, date;
  215. }
  216. protected OrderByBorder newSort(String wicketId, SortBy field, SortableDataProvider<?> dp, final DataView<?> dataView) {
  217. return new OrderByBorder(wicketId, field.name(), dp) {
  218. private static final long serialVersionUID = 1L;
  219. @Override
  220. protected void onSortChanged() {
  221. dataView.setCurrentPage(0);
  222. }
  223. };
  224. }
  225. private class RepositoriesProvider extends ListDataProvider<RepositoryModel> {
  226. private static final long serialVersionUID = 1L;
  227. public RepositoriesProvider(List<RepositoryModel> list) {
  228. super(list);
  229. }
  230. @Override
  231. public List<RepositoryModel> getData() {
  232. return super.getData();
  233. }
  234. public void remove(RepositoryModel model) {
  235. int index = getData().indexOf(model);
  236. RepositoryModel groupModel = null;
  237. if (index == (getData().size() - 1)) {
  238. // last element
  239. if (index > 0) {
  240. // previous element is group header, then this is last
  241. // repository in group. remove group too.
  242. if (getData().get(index - 1) instanceof GroupRepositoryModel) {
  243. groupModel = getData().get(index - 1);
  244. }
  245. }
  246. } else if (index < (getData().size() - 1)) {
  247. // not last element. check next element for group match.
  248. if (getData().get(index - 1) instanceof GroupRepositoryModel && getData().get(index + 1) instanceof GroupRepositoryModel) {
  249. // repository is sandwiched by group headers so this
  250. // repository is the only element in the group. remove
  251. // group.
  252. groupModel = getData().get(index - 1);
  253. }
  254. }
  255. if (groupModel == null) {
  256. // Find the group and decrement the count
  257. for (int i = index; i >= 0; i--) {
  258. if (getData().get(i) instanceof GroupRepositoryModel) {
  259. ((GroupRepositoryModel) getData().get(i)).count--;
  260. break;
  261. }
  262. }
  263. } else {
  264. // Remove the group header
  265. getData().remove(groupModel);
  266. }
  267. getData().remove(model);
  268. }
  269. }
  270. private class SortableRepositoriesProvider extends SortableDataProvider<RepositoryModel> {
  271. private static final long serialVersionUID = 1L;
  272. private List<RepositoryModel> list = null;
  273. protected SortableRepositoriesProvider(List<RepositoryModel> list) {
  274. this.list = list;
  275. setSort(SortBy.date.name(), false);
  276. }
  277. public void remove(RepositoryModel model) {
  278. list.remove(model);
  279. }
  280. @Override
  281. public int size() {
  282. if (list == null)
  283. return 0;
  284. return list.size();
  285. }
  286. @Override
  287. public IModel<RepositoryModel> model(RepositoryModel header) {
  288. return new Model<RepositoryModel>(header);
  289. }
  290. @Override
  291. public Iterator<RepositoryModel> iterator(int first, int count) {
  292. SortParam sp = getSort();
  293. String prop = sp.getProperty();
  294. final boolean asc = sp.isAscending();
  295. if (prop == null || prop.equals(SortBy.date.name())) {
  296. Collections.sort(list, new Comparator<RepositoryModel>() {
  297. @Override
  298. public int compare(RepositoryModel o1, RepositoryModel o2) {
  299. if (asc)
  300. return o1.lastChange.compareTo(o2.lastChange);
  301. return o2.lastChange.compareTo(o1.lastChange);
  302. }
  303. });
  304. } else if (prop.equals(SortBy.repository.name())) {
  305. Collections.sort(list, new Comparator<RepositoryModel>() {
  306. @Override
  307. public int compare(RepositoryModel o1, RepositoryModel o2) {
  308. if (asc)
  309. return o1.name.compareTo(o2.name);
  310. return o2.name.compareTo(o1.name);
  311. }
  312. });
  313. } else if (prop.equals(SortBy.owner.name())) {
  314. Collections.sort(list, new Comparator<RepositoryModel>() {
  315. @Override
  316. public int compare(RepositoryModel o1, RepositoryModel o2) {
  317. if (asc)
  318. return o1.owner.compareTo(o2.owner);
  319. return o2.owner.compareTo(o1.owner);
  320. }
  321. });
  322. } else if (prop.equals(SortBy.description.name())) {
  323. Collections.sort(list, new Comparator<RepositoryModel>() {
  324. @Override
  325. public int compare(RepositoryModel o1, RepositoryModel o2) {
  326. if (asc)
  327. return o1.description.compareTo(o2.description);
  328. return o2.description.compareTo(o1.description);
  329. }
  330. });
  331. }
  332. return list.subList(first, first + count).iterator();
  333. }
  334. }
  335. }