Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
<classpathentry kind="lib" path="ext/pf4j-0.9.0.jar" sourcepath="ext/src/pf4j-0.9.0.jar" />
<classpathentry kind="lib" path="ext/tika-core-1.5.jar" sourcepath="ext/src/tika-core-1.5.jar" />
<classpathentry kind="lib" path="ext/jsoup-1.7.3.jar" sourcepath="ext/src/jsoup-1.7.3.jar" />
<classpathentry kind="lib" path="ext/javax.activation-api-1.2.0.jar" sourcepath="ext/src/javax.activation-api-1.2.0.jar" />
<classpathentry kind="lib" path="ext/junit-4.12.jar" sourcepath="ext/src/junit-4.12.jar" />
<classpathentry kind="lib" path="ext/hamcrest-core-1.3.jar" sourcepath="ext/src/hamcrest-core-1.3.jar" />
<classpathentry kind="lib" path="ext/selenium-java-2.28.0.jar" sourcepath="ext/src/selenium-java-2.28.0.jar" />
Expand Down
1 change: 1 addition & 0 deletions build.moxie
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ dependencies:
- compile 'ro.fortsoft.pf4j:pf4j:0.9.0' :war
- compile 'org.apache.tika:tika-core:1.5' :war
- compile 'org.jsoup:jsoup:1.7.3' :war
- compile 'javax.activation:javax.activation-api:1.2.0'
- test 'junit:junit:4.12'
# Dependencies for Selenium web page testing
- test 'org.seleniumhq.selenium:selenium-java:${selenium.version}' @jar
Expand Down
11 changes: 11 additions & 0 deletions gitblit.iml
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,17 @@
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library">
<library name="javax.activation-api-1.2.0.jar">
<CLASSES>
<root url="jar://$MODULE_DIR$/ext/javax.activation-api-1.2.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$MODULE_DIR$/ext/src/javax.activation-api-1.2.0.jar!/" />
</SOURCES>
</library>
</orderEntry>
<orderEntry type="module-library" scope="TEST">
<library name="junit-4.12.jar">
<CLASSES>
Expand Down
14 changes: 14 additions & 0 deletions src/main/distrib/data/defaults.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2147,3 +2147,17 @@ filestore.storageFolder = ${baseFolder}/lfs
# Common unit suffixes of k, m, or g are supported.
# SINCE 1.7.0
filestore.maxUploadSize = -1

# Specify the behaviour of the Repository groups on the "Repositories"
# page, specifically whether they can be collapsed and expanded, and
# their default state on loading the page.
# Only on repositoryListType grouped
#
# Values (case-insensitive):
# disabled - Repository groups cannot collapsed; maintains behaviour
# from previous versions of GitBlit.
# expanded - On loading the page all repository groups are expanded.
# collapsed - On loading the page all repository groups are collapsed.
#
# SINCE 1.9.0
web.collapsibleRepositoryGroups = disabled
178 changes: 178 additions & 0 deletions src/main/java/com/gitblit/models/TreeNodeModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package com.gitblit.models;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.gitblit.utils.StringUtils;

public class TreeNodeModel implements Serializable, Comparable<TreeNodeModel> {

private static final long serialVersionUID = 1L;
final TreeNodeModel parent;
final String name;
final List<TreeNodeModel> subFolders = new ArrayList<>();
final List<RepositoryModel> repositories = new ArrayList<>();

/**
* Create a new tree root
*/
public TreeNodeModel() {
this.name = "/";
this.parent = null;
}

protected TreeNodeModel(String name, TreeNodeModel parent) {
this.name = name;
this.parent = parent;
}

public int getDepth() {
if(parent == null) {
return 0;
}else {
return parent.getDepth() +1;
}
}

/**
* Add a new sub folder to the current folder
*
* @param subFolder the subFolder to create
* @return the newly created folder to allow chaining
*/
public TreeNodeModel add(String subFolder) {
TreeNodeModel n = new TreeNodeModel(subFolder, this);
subFolders.add(n);
Collections.sort(subFolders);
return n;
}

/**
* Add the given repo to the current folder
*
* @param repo
*/
public void add(RepositoryModel repo) {
repositories.add(repo);
Collections.sort(repositories);
}

/**
* Adds the given repository model within the given path. Creates parent folders if they do not exist
*
* @param path
* @param model
*/
public void add(String path, RepositoryModel model) {
TreeNodeModel folder = getSubTreeNode(this, path, true);
folder.add(model);
}

@Override
public String toString() {
String string = name + "\n";
for(TreeNodeModel n : subFolders) {
string += "f";
for(int i = 0; i < n.getDepth(); i++) {
string += "-";
}
string += n.toString();
}

for(RepositoryModel n : repositories) {
string += "r";
for(int i = 0; i < getDepth()+1; i++) {
string += "-";
}
string += n.toString() + "\n";
}

return string;
}

public boolean containsSubFolder(String path) {
return containsSubFolder(this, path);
}

public TreeNodeModel getSubFolder(String path) {
return getSubTreeNode(this, path, false);
}

public List<Serializable> getTreeAsListForFrontend(){
List<Serializable> l = new ArrayList<>();
getTreeAsListForFrontend(l, this);
return l;
}

private static void getTreeAsListForFrontend(List<Serializable> list, TreeNodeModel node) {
list.add(node);
for(TreeNodeModel t : node.subFolders) {
getTreeAsListForFrontend(list, t);
}
for(RepositoryModel r : node.repositories) {
list.add(r);
}
}

private static TreeNodeModel getSubTreeNode(TreeNodeModel node, String path, boolean create) {
if(!StringUtils.isEmpty(path)) {
boolean isPathInCurrentHierarchyLevel = path.lastIndexOf('/') < 0;
if(isPathInCurrentHierarchyLevel) {
for(TreeNodeModel t : node.subFolders) {
if(t.name.equals(path) ) {
return t;
}
}

if(create) {
node.add(path);
return getSubTreeNode(node, path, true);
}
}else {
//traverse into subFolder
String folderInCurrentHierarchyLevel = StringUtils.getFirstPathElement(path);

for(TreeNodeModel t : node.subFolders) {
if(t.name.equals(folderInCurrentHierarchyLevel) ) {
String folderInNextHierarchyLevel = path.substring(path.indexOf('/') + 1, path.length());
return getSubTreeNode(t, folderInNextHierarchyLevel, create);
}
}

if(create) {
String folderInNextHierarchyLevel = path.substring(path.indexOf('/') + 1, path.length());
return getSubTreeNode(node.add(folderInCurrentHierarchyLevel), folderInNextHierarchyLevel, true);
}
}
}

return null;
}

private static boolean containsSubFolder(TreeNodeModel node, String path) {
return getSubTreeNode(node, path, false) != null;
}

@Override
public int compareTo(TreeNodeModel t) {
return StringUtils.compareRepositoryNames(name, t.name);
}

public TreeNodeModel getParent() {
return parent;
}

public String getName() {
return name;
}

public List<TreeNodeModel> getSubFolders() {
return subFolders;
}

public List<RepositoryModel> getRepositories() {
return repositories;
}
}
3 changes: 2 additions & 1 deletion src/main/java/com/gitblit/wicket/pages/BasePage.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@

<!-- Include scripts at end for faster page loading -->
<script type="text/javascript" src="bootstrap/js/jquery.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.js"></script>
<script type="text/javascript" src="bootstrap/js/bootstrap.js"></script>
<script type="text/javascript" src="gitblit/js/collapsible-table.js"></script>
<wicket:container wicket:id="bottomScripts"></wicket:container>
</body>
</html>
106 changes: 106 additions & 0 deletions src/main/java/com/gitblit/wicket/panels/NestedRepositoryTreePanel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<!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>
<tr style="background-color: #bbb" wicket:id="nodeHeader" data-row-type="folder"></tr>
<tr wicket:id="subFolders">
<span wicket:id="rowContent"></span>
</tr>
<wicket:container wicket:id="repositories">
<tr wicket:id="rowContent" data-row-type="repo">
<td wicket:id="firstColumn" class="left"
style="padding-left: 3px;">
<div style="border-left: 1px solid black; margin-left:6px; width: 19px;display: inline-block;float: left;"
wicket:id="depth">&nbsp;</div>
<span wicket:id="repoIcon"></span><span
style="padding-left: 3px;" wicket:id="repositoryName">[repository
name]</span>
</td>
<td class="hidden-phone"><span class="list"
wicket:id="repositoryDescription">[repository description]</span></td>
<td class="hidden-tablet hidden-phone author"><span
wicket:id="repositoryOwner">[repository owner]</span></td>
<td class="hidden-phone"
style="text-align: right; padding-right: 10px;"><img
class="inlineIcon" wicket:id="sparkleshareIcon" /><img
class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon"
wicket:id="federatedIcon" /><img class="inlineIcon"
wicket:id="accessRestrictionIcon" /></td>
<td><span wicket:id="repositoryLastChange">[last change]</span></td>
<td class="rightAlign hidden-phone"
style="text-align: right; padding-right: 15px;"><span
style="font-size: 0.8em;" wicket:id="repositorySize">[repository
size]</span></td>
</tr>
</wicket:container>

<wicket:fragment wicket:id="emptyFragment">
</wicket:fragment>

<wicket:fragment wicket:id="repoIconFragment">
<span class="octicon octicon-centered octicon-repo"></span>
</wicket:fragment>

<wicket:fragment wicket:id="mirrorIconFragment">
<span class="octicon octicon-centered octicon-mirror"></span>
</wicket:fragment>

<wicket:fragment wicket:id="forkIconFragment">
<span class="octicon octicon-centered octicon-repo-forked"></span>
</wicket:fragment>

<wicket:fragment wicket:id="cloneIconFragment">
<span class="octicon octicon-centered octicon-repo-push"
wicket:message="title:gb.workingCopyWarning"></span>
</wicket:fragment>

<wicket:fragment wicket:id="tableGroupMinusCollapsible">
<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>
</wicket:fragment>

<wicket:fragment wicket:id="tableGroupPlusCollapsible">
<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>
</wicket:fragment>

<wicket:fragment wicket:id="tableAllCollapsible">
<i title="Click to expand all"
class="fa fa-plus-square-o table-openall-collapsible"
aria-hidden="true" style="padding-right: 3px; cursor: pointer;"></i>
<i title="Click to collapse all"
class="fa fa-minus-square-o table-closeall-collapsible"
aria-hidden="true" style="padding-right: 3px; cursor: pointer;"></i>
</wicket:fragment>

<wicket:fragment wicket:id="groupRepositoryHeader">
<tr>
<th class="left"><span wicket:id="allCollapsible"></span> <img
style="vertical-align: middle;" src="git-black-16x16.png" /> <wicket:message
key="gb.repository">Repository</wicket:message></th>
<th class="hidden-phone"><span><wicket:message
key="gb.description">Description</wicket:message></span></th>
<th class="hidden-tablet hidden-phone"><span><wicket:message
key="gb.owner">Owner</wicket:message></span></th>
<th class="hidden-phone"></th>
<th><wicket:message key="gb.lastChange">Last Change</wicket:message></th>
<th class="right hidden-phone"></th>
</tr>
</wicket:fragment>

<wicket:fragment wicket:id="groupRepositoryRow">
<td wicket:id="firstColumn" style="" colspan="1">
<div style="border-left: 1px solid black; margin-left:6px; width: 19px; display: inline-block;float: left;"
wicket:id="depth">&nbsp;</div>
<span
wicket:id="groupCollapsible"></span><span wicket:id="groupName">[group
name]</span></td>
<td colspan="6" style="padding: 2px;"><span class="hidden-phone"
style="font-weight: normal; color: #666;"
wicket:id="groupDescription">[description]</span></td>
</wicket:fragment>

</wicket:panel>
</body>
</html>
Loading