Skip to content

Commit 0bace7e

Browse files
committed
Add support nested groups on the Repositories page
Fix for #725. Also covers #527. This is a squashed commit of the following commits, merging and closing pull request #1267: commit 55fee41 Author: Martin Spielmann <[email protected]> Date: Mon Nov 6 17:19:53 2017 +0100 declared local variable final to fix travis build commit 131e4d1 Author: Martin Spielmann <[email protected]> Date: Mon Nov 6 14:11:55 2017 +0100 fix formatting (use tab for identation) commit 8da5f6d Author: Martin Spielmann <[email protected]> Date: Mon Nov 6 13:45:39 2017 +0100 Add repositoryListType tree. Addresses #725, 527 and includes #1224 commit 6c06165 Merge: f365daa 40ee965 Author: Martin Spielmann <[email protected]> Date: Sat Nov 4 13:19:08 2017 +0100 Merge remote-tracking branch 'collapsible/ticket/527' into 725_nested_repos commit f365daa Author: Martin Spielmann <[email protected]> Date: Sat Nov 4 13:10:24 2017 +0100 first working version of tree model
1 parent 150ff28 commit 0bace7e

File tree

7 files changed

+642
-13
lines changed

7 files changed

+642
-13
lines changed
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package com.gitblit.models;
2+
3+
import java.io.Serializable;
4+
import java.util.ArrayList;
5+
import java.util.Collections;
6+
import java.util.List;
7+
8+
import com.gitblit.utils.StringUtils;
9+
10+
public class TreeNodeModel implements Serializable, Comparable<TreeNodeModel> {
11+
12+
private static final long serialVersionUID = 1L;
13+
final TreeNodeModel parent;
14+
final String name;
15+
final List<TreeNodeModel> subFolders = new ArrayList<>();
16+
final List<RepositoryModel> repositories = new ArrayList<>();
17+
18+
/**
19+
* Create a new tree root
20+
*/
21+
public TreeNodeModel() {
22+
this.name = "/";
23+
this.parent = null;
24+
}
25+
26+
protected TreeNodeModel(String name, TreeNodeModel parent) {
27+
this.name = name;
28+
this.parent = parent;
29+
}
30+
31+
public int getDepth() {
32+
if(parent == null) {
33+
return 0;
34+
}else {
35+
return parent.getDepth() +1;
36+
}
37+
}
38+
39+
/**
40+
* Add a new sub folder to the current folder
41+
*
42+
* @param subFolder the subFolder to create
43+
* @return the newly created folder to allow chaining
44+
*/
45+
public TreeNodeModel add(String subFolder) {
46+
TreeNodeModel n = new TreeNodeModel(subFolder, this);
47+
subFolders.add(n);
48+
Collections.sort(subFolders);
49+
return n;
50+
}
51+
52+
/**
53+
* Add the given repo to the current folder
54+
*
55+
* @param repo
56+
*/
57+
public void add(RepositoryModel repo) {
58+
repositories.add(repo);
59+
Collections.sort(repositories);
60+
}
61+
62+
/**
63+
* Adds the given repository model within the given path. Creates parent folders if they do not exist
64+
*
65+
* @param path
66+
* @param model
67+
*/
68+
public void add(String path, RepositoryModel model) {
69+
TreeNodeModel folder = getSubTreeNode(this, path, true);
70+
folder.add(model);
71+
}
72+
73+
@Override
74+
public String toString() {
75+
String string = name + "\n";
76+
for(TreeNodeModel n : subFolders) {
77+
string += "f";
78+
for(int i = 0; i < n.getDepth(); i++) {
79+
string += "-";
80+
}
81+
string += n.toString();
82+
}
83+
84+
for(RepositoryModel n : repositories) {
85+
string += "r";
86+
for(int i = 0; i < getDepth()+1; i++) {
87+
string += "-";
88+
}
89+
string += n.toString() + "\n";
90+
}
91+
92+
return string;
93+
}
94+
95+
public boolean containsSubFolder(String path) {
96+
return containsSubFolder(this, path);
97+
}
98+
99+
public TreeNodeModel getSubFolder(String path) {
100+
return getSubTreeNode(this, path, false);
101+
}
102+
103+
public List<Serializable> getTreeAsListForFrontend(){
104+
List<Serializable> l = new ArrayList<>();
105+
getTreeAsListForFrontend(l, this);
106+
return l;
107+
}
108+
109+
private static void getTreeAsListForFrontend(List<Serializable> list, TreeNodeModel node) {
110+
list.add(node);
111+
for(TreeNodeModel t : node.subFolders) {
112+
getTreeAsListForFrontend(list, t);
113+
}
114+
for(RepositoryModel r : node.repositories) {
115+
list.add(r);
116+
}
117+
}
118+
119+
private static TreeNodeModel getSubTreeNode(TreeNodeModel node, String path, boolean create) {
120+
if(!StringUtils.isEmpty(path)) {
121+
boolean isPathInCurrentHierarchyLevel = path.lastIndexOf('/') < 0;
122+
if(isPathInCurrentHierarchyLevel) {
123+
for(TreeNodeModel t : node.subFolders) {
124+
if(t.name.equals(path) ) {
125+
return t;
126+
}
127+
}
128+
129+
if(create) {
130+
node.add(path);
131+
return getSubTreeNode(node, path, true);
132+
}
133+
}else {
134+
//traverse into subFolder
135+
String folderInCurrentHierarchyLevel = StringUtils.getFirstPathElement(path);
136+
137+
for(TreeNodeModel t : node.subFolders) {
138+
if(t.name.equals(folderInCurrentHierarchyLevel) ) {
139+
String folderInNextHierarchyLevel = path.substring(path.indexOf('/') + 1, path.length());
140+
return getSubTreeNode(t, folderInNextHierarchyLevel, create);
141+
}
142+
}
143+
144+
if(create) {
145+
String folderInNextHierarchyLevel = path.substring(path.indexOf('/') + 1, path.length());
146+
return getSubTreeNode(node.add(folderInCurrentHierarchyLevel), folderInNextHierarchyLevel, true);
147+
}
148+
}
149+
}
150+
151+
return null;
152+
}
153+
154+
private static boolean containsSubFolder(TreeNodeModel node, String path) {
155+
return getSubTreeNode(node, path, false) != null;
156+
}
157+
158+
@Override
159+
public int compareTo(TreeNodeModel t) {
160+
return StringUtils.compareRepositoryNames(name, t.name);
161+
}
162+
163+
public TreeNodeModel getParent() {
164+
return parent;
165+
}
166+
167+
public String getName() {
168+
return name;
169+
}
170+
171+
public List<TreeNodeModel> getSubFolders() {
172+
return subFolders;
173+
}
174+
175+
public List<RepositoryModel> getRepositories() {
176+
return repositories;
177+
}
178+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2+
<html xmlns="http://www.w3.org/1999/xhtml"
3+
xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
4+
xml:lang="en" lang="en">
5+
6+
<body>
7+
<wicket:panel>
8+
<tr style="background-color: #bbb" wicket:id="nodeHeader" data-row-type="folder"></tr>
9+
<tr wicket:id="subFolders">
10+
<span wicket:id="rowContent"></span>
11+
</tr>
12+
<wicket:container wicket:id="repositories">
13+
<tr wicket:id="rowContent" data-row-type="repo">
14+
<td wicket:id="firstColumn" class="left"
15+
style="padding-left: 3px;">
16+
<div style="border-left: 1px solid black; margin-left:6px; width: 19px;display: inline-block;float: left;"
17+
wicket:id="depth">&nbsp;</div>
18+
<span wicket:id="repoIcon"></span><span
19+
style="padding-left: 3px;" wicket:id="repositoryName">[repository
20+
name]</span>
21+
</td>
22+
<td class="hidden-phone"><span class="list"
23+
wicket:id="repositoryDescription">[repository description]</span></td>
24+
<td class="hidden-tablet hidden-phone author"><span
25+
wicket:id="repositoryOwner">[repository owner]</span></td>
26+
<td class="hidden-phone"
27+
style="text-align: right; padding-right: 10px;"><img
28+
class="inlineIcon" wicket:id="sparkleshareIcon" /><img
29+
class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon"
30+
wicket:id="federatedIcon" /><img class="inlineIcon"
31+
wicket:id="accessRestrictionIcon" /></td>
32+
<td><span wicket:id="repositoryLastChange">[last change]</span></td>
33+
<td class="rightAlign hidden-phone"
34+
style="text-align: right; padding-right: 15px;"><span
35+
style="font-size: 0.8em;" wicket:id="repositorySize">[repository
36+
size]</span></td>
37+
</tr>
38+
</wicket:container>
39+
40+
<wicket:fragment wicket:id="emptyFragment">
41+
</wicket:fragment>
42+
43+
<wicket:fragment wicket:id="repoIconFragment">
44+
<span class="octicon octicon-centered octicon-repo"></span>
45+
</wicket:fragment>
46+
47+
<wicket:fragment wicket:id="mirrorIconFragment">
48+
<span class="octicon octicon-centered octicon-mirror"></span>
49+
</wicket:fragment>
50+
51+
<wicket:fragment wicket:id="forkIconFragment">
52+
<span class="octicon octicon-centered octicon-repo-forked"></span>
53+
</wicket:fragment>
54+
55+
<wicket:fragment wicket:id="cloneIconFragment">
56+
<span class="octicon octicon-centered octicon-repo-push"
57+
wicket:message="title:gb.workingCopyWarning"></span>
58+
</wicket:fragment>
59+
60+
<wicket:fragment wicket:id="tableGroupMinusCollapsible">
61+
<i title="Click to expand/collapse" class="fa fa-minus-square-o table-group-collapsible" aria-hidden="true" style="padding-right:3px;cursor:pointer;"></i>
62+
</wicket:fragment>
63+
64+
<wicket:fragment wicket:id="tableGroupPlusCollapsible">
65+
<i title="Click to expand/collapse" class="fa fa-plus-square-o table-group-collapsible" aria-hidden="true" style="padding-right:3px;cursor:pointer;"></i>
66+
</wicket:fragment>
67+
68+
<wicket:fragment wicket:id="tableAllCollapsible">
69+
<i title="Click to expand all"
70+
class="fa fa-plus-square-o table-openall-collapsible"
71+
aria-hidden="true" style="padding-right: 3px; cursor: pointer;"></i>
72+
<i title="Click to collapse all"
73+
class="fa fa-minus-square-o table-closeall-collapsible"
74+
aria-hidden="true" style="padding-right: 3px; cursor: pointer;"></i>
75+
</wicket:fragment>
76+
77+
<wicket:fragment wicket:id="groupRepositoryHeader">
78+
<tr>
79+
<th class="left"><span wicket:id="allCollapsible"></span> <img
80+
style="vertical-align: middle;" src="git-black-16x16.png" /> <wicket:message
81+
key="gb.repository">Repository</wicket:message></th>
82+
<th class="hidden-phone"><span><wicket:message
83+
key="gb.description">Description</wicket:message></span></th>
84+
<th class="hidden-tablet hidden-phone"><span><wicket:message
85+
key="gb.owner">Owner</wicket:message></span></th>
86+
<th class="hidden-phone"></th>
87+
<th><wicket:message key="gb.lastChange">Last Change</wicket:message></th>
88+
<th class="right hidden-phone"></th>
89+
</tr>
90+
</wicket:fragment>
91+
92+
<wicket:fragment wicket:id="groupRepositoryRow">
93+
<td wicket:id="firstColumn" style="" colspan="1">
94+
<div style="border-left: 1px solid black; margin-left:6px; width: 19px; display: inline-block;float: left;"
95+
wicket:id="depth">&nbsp;</div>
96+
<span
97+
wicket:id="groupCollapsible"></span><span wicket:id="groupName">[group
98+
name]</span></td>
99+
<td colspan="6" style="padding: 2px;"><span class="hidden-phone"
100+
style="font-weight: normal; color: #666;"
101+
wicket:id="groupDescription">[description]</span></td>
102+
</wicket:fragment>
103+
104+
</wicket:panel>
105+
</body>
106+
</html>

0 commit comments

Comments
 (0)