@@ -18,11 +18,6 @@ | |||
<attribute name="javadoc_location" value="jar:platform:/resource/gitblit/ext/slf4j-log4j12-1.6.1-javadoc.jar!/"/> | |||
</attributes> | |||
</classpathentry> | |||
<classpathentry kind="lib" path="ext/jetty-all-7.2.2.v20101205.jar" sourcepath="ext/jetty-all-7.2.2.v20101205-sources.jar"> | |||
<attributes> | |||
<attribute name="javadoc_location" value="jar:platform:/resource/gitblit/ext/jetty-all-7.2.2.v20101205-javadoc.jar!/"/> | |||
</attributes> | |||
</classpathentry> | |||
<classpathentry kind="lib" path="ext/jcommander-1.17.jar" sourcepath="ext/jcommander-1.17-sources.jar"> | |||
<attributes> | |||
<attribute name="javadoc_location" value="jar:platform:/resource/gitblit/ext/jcommander-1.17-javadoc.jar!/"/> | |||
@@ -74,5 +69,10 @@ | |||
<attribute name="javadoc_location" value="jar:platform:/resource/gitblit/ext/bcmail-jdk16-1.46-javadoc.jar!/"/> | |||
</attributes> | |||
</classpathentry> | |||
<classpathentry kind="lib" path="ext/jetty-all-7.4.1.v20110513.jar" sourcepath="ext/jetty-all-7.4.1.v20110513-sources.jar"> | |||
<attributes> | |||
<attribute name="javadoc_location" value="jar:platform:/resource/gitblit/ext/jetty-all-7.2.2.v20101205-javadoc.jar!/"/> | |||
</attributes> | |||
</classpathentry> | |||
<classpathentry kind="output" path="bin"/> | |||
</classpath> |
@@ -8,7 +8,12 @@ | |||
<target name="main"> | |||
<!-- extract version number from source code --> | |||
<!-- build dsate --> | |||
<tstamp> | |||
<format property="gb.buildDate" pattern="yyyy-MM-dd" /> | |||
</tstamp> | |||
<!-- extract Git:Blit version number from source code --> | |||
<loadfile property="gb.version" srcfile="${basedir}/src/com/gitblit/Constants.java"> | |||
<filterchain> | |||
<linecontains> | |||
@@ -20,6 +25,21 @@ | |||
<replacestring from="";" to="" /> | |||
<trim /> | |||
</tokenfilter> | |||
</filterchain> | |||
</loadfile> | |||
<!-- extract JGit version number from source code --> | |||
<loadfile property="jgit.version" srcfile="${basedir}/src/com/gitblit/Constants.java"> | |||
<filterchain> | |||
<linecontains> | |||
<contains value="public final static String JGIT_VERSION = " /> | |||
</linecontains> | |||
<striplinebreaks /> | |||
<tokenfilter> | |||
<replacestring from="public final static String JGIT_VERSION = "" to="" /> | |||
<replacestring from="";" to="" /> | |||
<trim /> | |||
</tokenfilter> | |||
</filterchain> | |||
</loadfile> | |||
<echo>Building Git:Blit ${gb.version}</echo> | |||
@@ -118,22 +138,22 @@ | |||
<include name="book_16x16.png" /> | |||
<include name="blank.png" /> | |||
</fileset> | |||
<!-- Copy Doc images --> | |||
<fileset dir="${basedir}/docs"> | |||
<include name="*.png" /> | |||
<include name="*.js" /> | |||
</fileset> | |||
</copy> | |||
<!-- Copy Fancybox --> | |||
<mkdir dir="${basedir}/site/fancybox" /> | |||
<copy todir="${basedir}/site/fancybox"> | |||
<fileset dir="${basedir}/docs/fancybox" > | |||
<copy todir="${basedir}/site/fancybox"> | |||
<fileset dir="${basedir}/docs/fancybox"> | |||
<exclude name="thumbs.db" /> | |||
</fileset> | |||
</copy> | |||
<!-- Copy screenshot thumbnails --> | |||
<mkdir dir="${basedir}/site/thumbs" /> | |||
<copy todir="${basedir}/site/thumbs"> | |||
@@ -141,7 +161,7 @@ | |||
<include name="*.png" /> | |||
</fileset> | |||
</copy> | |||
<!-- Copy screenshots --> | |||
<mkdir dir="${basedir}/site/screenshots" /> | |||
<copy todir="${basedir}/site/screenshots"> | |||
@@ -150,11 +170,12 @@ | |||
</fileset> | |||
</copy> | |||
<!-- Build site pages --> | |||
<java classpath="${project.build.dir}" classname="com.gitblit.BuildSite"> | |||
<classpath refid="master-classpath" /> | |||
<arg value="--sourceFolder" /> | |||
<arg value="${basedir}/docs" /> | |||
<arg value="--outputFolder" /> | |||
<arg value="${basedir}/site" /> | |||
@@ -163,9 +184,21 @@ | |||
<arg value="--pageFooter" /> | |||
<arg value="${basedir}/docs/page_footer.html" /> | |||
<arg value="--alias" /> | |||
<arg value="index=overview" /> | |||
<arg value="--substitute" /> | |||
<arg value="%VERSION%=${gb.version}" /> | |||
<arg value="--substitute" /> | |||
<arg value="%DISTRIBUTION%=${distribution.zipfile}" /> | |||
<arg value="--substitute" /> | |||
<arg value="%BUILDDATE%=${gb.buildDate}" /> | |||
<arg value="--substitute" /> | |||
<arg value="%JGIT%=${jgit.version}" /> | |||
</java> | |||
</target> |
@@ -69,7 +69,7 @@ web.datetimestampLongFormat = EEEE, MMMM d, yyyy h:mm a z | |||
# Choose how to present the repositories list. | |||
# grouped = group nested/subfolder repositories together (no sorting) | |||
# flat = flat list of repositories (sorting allowed) | |||
web.repositoryListType = flat | |||
web.repositoryListType = grouped | |||
# If using a grouped repository list and there are repositories at the | |||
# root level of your repositories folder, you may specify the displayed |
@@ -1,19 +1,17 @@ | |||
## Overview | |||
Git:Blit is an open-source, integrated pure Java stack for managing, viewing, and serving [Git][git] repositories. | |||
Its designed primarily as a tool for small workgroups who want to host [Git][git] repositories on a Windows machine. | |||
Of course, since its pure Java it should run with any JVM on any platform, but there are already [many compelling Git solutions](https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools) for non-Windows environments. | |||
Its designed primarily as a tool for small workgroups who want to host [Git][git] repositories on a Windows machine. Having said that, it works equally well on any standard Linux distribution. | |||
### Current Version | |||
[{0}](http://gitblit.com/{1}) based on [{2}][jgit] (*{3}*) | |||
[%VERSION%](http://gitblit.com/%DISTRIBUTION%) based on [%JGIT%][jgit] (*%BUILDDATE%*) | |||
sources @ [Github][gitbltsrc] | |||
### Design Principles | |||
1. [KISS](http://en.wikipedia.org/wiki/KISS_principle) | |||
2. Offer useful features for serving Git repositories. If feature is complex, refer to #1. | |||
3. All dependencies must be retrievable from a publicly accessible Maven repository.<br/>This is to ensure authenticity of dependencies and to keep the Git:Blit distribution svelte. | |||
3. All dependencies must be retrievable from a publicly accessible [Maven](http://maven.apache.org) repository.<br/>This is to ensure authenticity of dependencies and to keep the Git:Blit distribution svelte. | |||
### Features | |||
- Out-of-the-box integrated stack requiring minimal configuration | |||
@@ -29,7 +27,7 @@ sources @ [Github][gitbltsrc] | |||
</ul> | |||
- Gitweb inspired UI | |||
- Administrators may create, edit, rename, or delete repositories through the web UI | |||
- Administrators may create, edit, rename, or delete users through the web UI | |||
- Administrators may create, edit, or delete users through the web UI | |||
- Repository Owners may edit repositories through the web UI | |||
- Automatically generates a self-signed certificate for https communications | |||
- Dates can optionally be displayed using the browser''s reported timezone | |||
@@ -53,8 +51,6 @@ sources @ [Github][gitbltsrc] | |||
- Git:Blit is an integrated, full-stack solution. There is no WAR build at this time. | |||
### Todo List | |||
- Manual certificate generation with BouncyCastle | |||
- User list with edit and delete links | |||
- Review spots where Git:Blit can cache data instead of abusing the disk | |||
- stats | |||
- users.properties access | |||
@@ -90,8 +86,9 @@ The following dependencies are bundled with the Git:Blit zip distribution file. | |||
- [google-code-prettify](http://code.google.com/p/google-code-prettify) (Apache 2.0) | |||
- [JavaService](http://forge.ow2.org/projects/javaservice) (BSD and LGPL) | |||
- icons courtesy of [FatCow Hosting](http://www.fatcow.com/free-icons) (Creative Commons CC-BY) | |||
- magnifying glass search icon courtesy of [Gnome](http://gnome.org) (Creative Commons CC-BY) | |||
- modified Git logo originally designed by [Henrik Nyh](http://henrik.nyh.se/2007/06/alternative-git-logo-and-favicon) | |||
- other icons courtesy of [FatCow Hosting](http://www.fatcow.com/free-icons) (Creative Commons CC-BY) | |||
### Downloaded Dependencies | |||
The following dependencies are automatically downloaded from the Apache Maven repository and from the Eclipse Maven repository when Git:Blit is launched for the first time. |
@@ -1,6 +1,6 @@ | |||
## Setup and Configuration | |||
1. Download and unzip Git:Blit.<br/> | |||
1. Download and unzip [%VERSION%](http://gitblit.com/%DISTRIBUTION%).<br/> | |||
*Its best to eliminate spaces in the path name as that can cause troubleshooting headaches.* | |||
2. The server itself is configured through a simple text file.<br/> | |||
Open `gitblit.properties` in your favorite text editor and make sure to review and set: |
@@ -10,10 +10,17 @@ Its small. Its portable. Its easy to manage. | |||
No. Git:Blit is based on [JGit][jgit] which is a pure Java implementation of the [Git version control system][git].<br/> | |||
Everything you need for Git:Blit is either in the zip distribution file or automatically downloaded on execution. | |||
### Does Git:Blit use a database to store its data? | |||
No. Git:Blit stores its repository configuration information within the `.git/config` file and its user information in `users.properties` or whatever filename is configured in `gitblit.properties`. | |||
### I want to deploy Git:Blit into my own servlet container. Where is the WAR? | |||
At this time there is no WAR build available. | |||
### Why doesn't Git:Blit support SSH? | |||
Git:Blit could integrate [Apache Mina](http://mina.apache.org) to provide SSH access. However, doing so violates design principle #1: KISS. SSH supports requires creating, exchanging, and managing SSH keys. While this is doable, its not simple like JGit's SmartHTTP implementation. | |||
Git:Blit could integrate [Apache Mina][mina] to provide SSH access. However, doing so violates Git:Blit's first design principle: [KISS](http://en.wikipedia.org/wiki/KISS_principle).<br/> | |||
SSH supports requires creating, exchanging, and managing SSH keys. While this is doable, its not simple like JGit's SmartHTTP implementation. | |||
You might consider [Gerrit](http://gerrit.googlecode.org) which supports SSH. | |||
You might consider running [Gerrit](http://gerrit.googlecode.org) which does integrate [Apache Mina][mina] and supports SSH or you might consider serving [Git][git] on Linux which would offer real SSH support and also allow use of [many other compelling Git solutions](https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools). | |||
### What types of Search does Git:Blit support? | |||
Git:Blit supports case-insensitive searches of *commit message* (default), *author*, and *committer*.<br/> | |||
@@ -26,9 +33,10 @@ To search by *author* or *committer* use the following syntax in the search box: | |||
Alternatively, you could enable the search type dropdown list in your `gitblit.properties` file. | |||
### How do I run Git:Blit on port 80 or 443 in Linux? | |||
Tricky. Linux requires root permissions to serve on ports < 1024.<br/> | |||
Linux requires root permissions to serve on ports < 1024.<br/> | |||
Run the server as *root* (security concern) or change the ports you are serving to 8080 (http) or 8443 (https). | |||
[bitblt]: http://en.wikipedia.org/wiki/Bit_blit "Wikipedia Bitblt" | |||
[jgit]: http://eclipse.org/jgit "Eclipse JGit Site" | |||
[git]: http://git-scm.com "Official Git Site" | |||
[git]: http://git-scm.com "Official Git Site" | |||
[mina]: http://mina.apache.org " Apache Mina" |
@@ -1,8 +1,8 @@ | |||
## Screenshots | |||
<table class="screenshots"> | |||
<tr><td> | |||
<a rel="screenshots_group" href="screenshots/00.png" title="Repository List">![Repositories](thumbs/00.png)</a> | |||
<br/>Repository List | |||
<a rel="screenshots_group" href="screenshots/00.png" title="Repository & User List">![Repositories](thumbs/00.png)</a> | |||
<br/>Repository & User List | |||
</td><td> | |||
<a rel="screenshots_group" href="screenshots/01.png" title="New User">![New User](thumbs/01.png)</a> | |||
<br/>New User |
@@ -268,7 +268,7 @@ public class Build { | |||
public static final MavenObject JCOMMANDER = new MavenObject("jCommander", "com/beust", "jcommander", "1.17", 34000, 32000, 141000, "219a3540f3b27d7cc3b1d91d6ea046cd8723290e", "0bb50eec177acf0e94d58e0cf07262fe5164331d", "c7adc475ca40c288c93054e0f4fe58f3a98c0cb5"); | |||
public static final MavenObject JETTY = new MavenObject("Jetty", "org/eclipse/jetty/aggregate", "jetty-all", "7.2.2.v20101205", 1430000, 965000, 3871000, "b9b7c812a732721c427e208c54fbb71ca17a2ee1", "cbc4fc72c4a646d8822bf7369c2101d4d5d1ff98", "34c87e11bba426fe97bfe23ccff12eda477c8f57"); | |||
public static final MavenObject JETTY = new MavenObject("Jetty", "org/eclipse/jetty/aggregate", "jetty-all", "7.4.1.v20110513", 1500000, 1000000, 4100000, "1e2de9ed25a7c6ae38717d5ffdc7cfcd6be4bd46", "7b6279d16ce8f663537d9faf55ea353e748dbbaa", "fa06212e751296f1a7abc15c843b135bf49a112b"); | |||
public static final MavenObject SERVLET = new MavenObject("Servlet 2.5", "javax/servlet", "servlet-api", "2.5", 105000, 158000, 0, "5959582d97d8b61f4d154ca9e495aafd16726e34", "021599814ad9a605b86f3e6381571beccd861a32", null); | |||
@@ -46,11 +46,11 @@ public class BuildSite { | |||
Arrays.sort(markdownFiles); | |||
Map<String, String> aliasMap = new HashMap<String, String>(); | |||
for (String alias:params.aliases) { | |||
String [] values = alias.split("="); | |||
for (String alias : params.aliases) { | |||
String[] values = alias.split("="); | |||
aliasMap.put(values[0], values[1]); | |||
} | |||
System.out.println(MessageFormat.format("Generating site from {0} Markdown Docs in {1} ", markdownFiles.length, sourceFolder.getAbsolutePath())); | |||
String linkPattern = "<a href=''{0}''>{1}</a>"; | |||
StringBuilder sb = new StringBuilder(); | |||
@@ -66,7 +66,7 @@ public class BuildSite { | |||
} | |||
sb.setLength(sb.length() - 3); | |||
sb.trimToSize(); | |||
String html_header = readContent(new File(params.pageHeader)); | |||
String html_footer = readContent(new File(params.pageFooter)); | |||
final String links = sb.toString(); | |||
@@ -76,16 +76,13 @@ public class BuildSite { | |||
for (File file : markdownFiles) { | |||
try { | |||
String documentName = getDocumentName(file); | |||
String displayName = documentName; | |||
if (aliasMap.containsKey(documentName)) { | |||
displayName = aliasMap.get(documentName); | |||
} | |||
String fileName = documentName + ".html"; | |||
System.out.println(MessageFormat.format(" {0} => {1}", file.getName(), fileName)); | |||
InputStreamReader reader = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")); | |||
String content = MarkdownUtils.transformMarkdown(reader); | |||
if (displayName.equalsIgnoreCase("overview")) { | |||
content = MessageFormat.format(content, Constants.VERSION, "gitblit-" + Constants.VERSION + ".zip", Constants.getJGitVersion(), date); | |||
for (String token : params.substitutions) { | |||
String [] kv = token.split("="); | |||
content = content.replace(kv[0], kv[1]); | |||
} | |||
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(destinationFolder, fileName)), Charset.forName("UTF-8")); | |||
writer.write(header); | |||
@@ -122,7 +119,7 @@ public class BuildSite { | |||
// trim leading ##_ which is to control display order | |||
return displayName.substring(3); | |||
} | |||
private static void usage(JCommander jc, ParameterException t) { | |||
System.out.println(Constants.getRunningVersion()); | |||
System.out.println(); | |||
@@ -135,7 +132,7 @@ public class BuildSite { | |||
} | |||
System.exit(0); | |||
} | |||
@Parameters(separators = " ") | |||
private static class Params { | |||
@@ -154,5 +151,8 @@ public class BuildSite { | |||
@Parameter(names = { "--alias" }, description = "Filename=Linkname aliases", required = false) | |||
public List<String> aliases = new ArrayList<String>(); | |||
@Parameter(names = { "--substitute" }, description = "@TOKEN@=value", required = false) | |||
public List<String> substitutions = new ArrayList<String>(); | |||
} | |||
} |
@@ -10,6 +10,10 @@ public class Constants { | |||
// and only use A-Z a-z 0-9 .-_ in the string. | |||
public final static String VERSION = "0.1.0-SNAPSHOT"; | |||
// The build script extracts this exact line so be careful editing it | |||
// and only use A-Z a-z 0-9 .-_ in the string. | |||
public final static String JGIT_VERSION = "JGit 0.12.1"; | |||
public final static String ADMIN_ROLE = "#admin"; | |||
public final static String PROPERTIES_FILE = "gitblit.properties"; | |||
@@ -44,7 +48,7 @@ public class Constants { | |||
} | |||
public static String getJGitVersion() { | |||
return "JGit 0.12.1"; | |||
return JGIT_VERSION; | |||
} | |||
public static String getRunningVersion() { |
@@ -29,6 +29,7 @@ import org.eclipse.jetty.server.Handler; | |||
import org.eclipse.jetty.server.Server; | |||
import org.eclipse.jetty.server.bio.SocketConnector; | |||
import org.eclipse.jetty.server.nio.SelectChannelConnector; | |||
import org.eclipse.jetty.server.session.HashSessionManager; | |||
import org.eclipse.jetty.server.ssl.SslConnector; | |||
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; | |||
import org.eclipse.jetty.server.ssl.SslSocketConnector; | |||
@@ -192,6 +193,16 @@ public class GitBlitServer { | |||
rootContext.setServer(server); | |||
rootContext.setWar(location.toExternalForm()); | |||
rootContext.setTempDirectory(tempDir); | |||
// Mark all cookies HttpOnly so they are not accessible to JavaScript | |||
// engines. | |||
// http://erlend.oftedal.no/blog/?blogid=33 | |||
// https://www.owasp.org/index.php/HttpOnly#Browsers_Supporting_HttpOnly | |||
HashSessionManager sessionManager = new HashSessionManager(); | |||
sessionManager.setHttpOnly(true); | |||
// Use secure cookies if only serving https | |||
sessionManager.setSecureCookies(params.port <= 0 && params.securePort > 0); | |||
rootContext.getSessionHandler().setSessionManager(sessionManager); | |||
// Wicket Filter | |||
String wicketPathSpec = "/*"; |
@@ -43,8 +43,8 @@ gb.head = HEAD | |||
gb.blame = blame | |||
gb.login = Login | |||
gb.logout = Logout | |||
gb.username = Username | |||
gb.password = Password | |||
gb.username = username | |||
gb.password = password | |||
gb.tagger = tagger | |||
gb.moreHistory = more history... | |||
gb.difftocurrent = diff to current |
@@ -153,6 +153,10 @@ public class WicketUtils { | |||
return new ContextRelativeResource("/com/gitblit/wicket/resources/" + file); | |||
} | |||
public static PageParameters newUsernameParameter(String username) { | |||
return new PageParameters("user=" + username); | |||
} | |||
public static PageParameters newRepositoryParameter(String repositoryName) { | |||
return new PageParameters("r=" + repositoryName); | |||
} |
@@ -15,7 +15,7 @@ | |||
<form wicket:id="editForm"> | |||
<table class="plain"> | |||
<tbody> | |||
<tr><th><wicket:message key="gb.name"></wicket:message></th><td class="edit"><input type="text" wicket:id="username" id="username" size="30" tabindex="1" /></td></tr> | |||
<tr><th><wicket:message key="gb.username"></wicket:message></th><td class="edit"><input type="text" wicket:id="username" id="username" size="30" tabindex="1" /></td></tr> | |||
<tr><th><wicket:message key="gb.password"></wicket:message></th><td class="edit"><input type="password" wicket:id="password" size="30" tabindex="2" /></td></tr> | |||
<tr><th><wicket:message key="gb.confirmPassword"></wicket:message></th><td class="edit"><input type="password" wicket:id="confirmPassword" size="30" tabindex="3" /></td></tr> | |||
<tr><th><wicket:message key="gb.canAdmin"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="canAdmin" tabindex="6" /> <i><wicket:message key="gb.canAdminDescription"></wicket:message></i></td></tr> |
@@ -18,67 +18,10 @@ | |||
<div class="markdown" style="margin-top:-0.5em;padding-bottom:5px;" wicket:id="repositoriesMessage">[repositories message]</div> | |||
<div wicket:id="adminPanel">[admin links]</div> | |||
<table class="repositories"> | |||
<span wicket:id="headerContent"></span> | |||
<tbody> | |||
<tr wicket:id="row"> | |||
<span wicket:id="rowContent"></span> | |||
</tr> | |||
</tbody> | |||
</table> | |||
<wicket:fragment wicket:id="adminLinks"> | |||
<!-- page nav links --> | |||
<div style="text-align: right;" class="admin_nav"> | |||
<a wicket:id="newRepository"><wicket:message key="gb.newRepository"></wicket:message></a> | <a wicket:id="newUser"><wicket:message key="gb.newUser"></wicket:message></a> | <a wicket:id="editUsers"><wicket:message key="gb.editUsers"></wicket:message></a> | |||
</div> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="repositoryAdminLinks"> | |||
<span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a> | <a wicket:id="renameRepository"><wicket:message key="gb.rename">[rename]</wicket:message></a> | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a></span> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="repositoryOwnerLinks"> | |||
<span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a></span> | |||
</wicket:fragment> | |||
<div wicket:id="repositoriesPanel">[repositories panel]</div> | |||
<wicket:fragment wicket:id="flatHeader"> | |||
<tr> | |||
<th wicket:id="orderByRepository"><wicket:message key="gb.repository">Repository</wicket:message></th> | |||
<th wicket:id="orderByDescription"><wicket:message key="gb.description">Description</wicket:message></th> | |||
<th wicket:id="orderByOwner"><wicket:message key="gb.owner">Owner</wicket:message></th> | |||
<th></th> | |||
<th wicket:id="orderByDate"><wicket:message key="gb.lastChange">Last Change</wicket:message></th> | |||
<th></th> | |||
</tr> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="groupHeader"> | |||
<tr> | |||
<th><wicket:message key="gb.repository">Repository</wicket:message></th> | |||
<th><wicket:message key="gb.description">Description</wicket:message></th> | |||
<th><wicket:message key="gb.owner">Owner</wicket:message></th> | |||
<th></th> | |||
<th><wicket:message key="gb.lastChange">Last Change</wicket:message></th> | |||
<th></th> | |||
</tr> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="groupRow"> | |||
<td colspan="6"><span wicket:id="groupName">[group name]</span></td> | |||
</wicket:fragment> | |||
<div style="padding-top: 10px;"wicket:id="usersPanel">[users panel]</div> | |||
<wicket:fragment wicket:id="repositoryRow"> | |||
<td><div class="list" wicket:id="repositoryName">[repository name]</div></td> | |||
<td><div class="list" wicket:id="repositoryDescription">[repository description]</div></td> | |||
<td class="author"><span wicket:id="repositoryOwner">[repository owner]</span></td> | |||
<td style="text-align: right;padding-right:10px;"><img class="inlineIcon" wicket:id="ticketsIcon" /><img class="inlineIcon" wicket:id="docsIcon" /><img class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon" wicket:id="accessRestrictionIcon" /></td> | |||
<td><span wicket:id="repositoryLastChange">[last change]</span></td> | |||
<td class="rightAlign"><span wicket:id="repositoryLinks"></span></td> | |||
</wicket:fragment> | |||
</wicket:extend> | |||
</body> | |||
</html> |
@@ -4,50 +4,27 @@ import java.io.File; | |||
import java.io.FileReader; | |||
import java.io.InputStream; | |||
import java.io.InputStreamReader; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.wicket.Component; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByBorder; | |||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; | |||
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
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.IDataProvider; | |||
import org.apache.wicket.markup.repeater.data.ListDataProvider; | |||
import org.apache.wicket.model.IModel; | |||
import org.apache.wicket.model.Model; | |||
import org.apache.wicket.resource.ContextRelativeResource; | |||
import com.gitblit.Constants.AccessRestrictionType; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.Keys; | |||
import com.gitblit.utils.MarkdownUtils; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.utils.TimeUtils; | |||
import com.gitblit.wicket.BasePage; | |||
import com.gitblit.wicket.GitBlitWebSession; | |||
import com.gitblit.wicket.LinkPanel; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.models.RepositoryModel; | |||
import com.gitblit.wicket.models.UserModel; | |||
import com.gitblit.wicket.panels.RepositoriesPanel; | |||
import com.gitblit.wicket.panels.UsersPanel; | |||
public class RepositoriesPage extends BasePage { | |||
public RepositoriesPage() { | |||
super(); | |||
setupPage("", ""); | |||
final boolean showAdmin; | |||
if (GitBlit.self().settings().getBoolean(Keys.web.authenticateAdminPages, true)) { | |||
boolean allowAdmin = GitBlit.self().settings().getBoolean(Keys.web.allowAdministration, false); | |||
@@ -65,12 +42,6 @@ public class RepositoriesPage extends BasePage { | |||
} | |||
} | |||
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this); | |||
adminLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class)); | |||
adminLinks.add(new BookmarkablePageLink<Void>("newUser", EditUserPage.class)); | |||
adminLinks.add(new BookmarkablePageLink<Void>("editUsers", RepositoriesPage.class)); | |||
add(adminLinks.setVisible(showAdmin)); | |||
// display an error message cached from a redirect | |||
String cachedMessage = GitBlitWebSession.get().clearErrorMessage(); | |||
if (!StringUtils.isEmpty(cachedMessage)) { | |||
@@ -111,228 +82,7 @@ public class RepositoriesPage extends BasePage { | |||
} | |||
Component repositoriesMessage = new Label("repositoriesMessage", message).setEscapeModelStrings(false); | |||
add(repositoriesMessage); | |||
final Map<AccessRestrictionType, String> accessRestrictionTranslations = getAccessRestrictions(); | |||
final UserModel user = GitBlitWebSession.get().getUser(); | |||
List<RepositoryModel> models = GitBlit.self().getRepositoryModels(user); | |||
IDataProvider<RepositoryModel> dp; | |||
if (GitBlit.self().settings().getString(Keys.web.repositoryListType, "flat").equalsIgnoreCase("grouped")) { | |||
Map<String, List<RepositoryModel>> groups = new HashMap<String, List<RepositoryModel>>(); | |||
for (RepositoryModel model : models) { | |||
String rootPath = StringUtils.getRootPath(model.name); | |||
if (StringUtils.isEmpty(rootPath)) { | |||
rootPath = GitBlit.self().settings().getString(Keys.web.repositoryRootGroupName, " "); | |||
} | |||
if (!groups.containsKey(rootPath)) { | |||
groups.put(rootPath, new ArrayList<RepositoryModel>()); | |||
} | |||
groups.get(rootPath).add(model); | |||
} | |||
List<String> roots = new ArrayList<String>(groups.keySet()); | |||
Collections.sort(roots); | |||
List<RepositoryModel> groupedModels = new ArrayList<RepositoryModel>(); | |||
for (String root : roots) { | |||
groupedModels.add(new GroupRepositoryModel(root)); | |||
groupedModels.addAll(groups.get(root)); | |||
} | |||
dp = new ListDataProvider<RepositoryModel>(groupedModels); | |||
} else { | |||
dp = new DataProvider(models); | |||
} | |||
DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) { | |||
private static final long serialVersionUID = 1L; | |||
int counter = 0; | |||
public void populateItem(final Item<RepositoryModel> item) { | |||
final RepositoryModel entry = item.getModelObject(); | |||
if (entry instanceof GroupRepositoryModel) { | |||
Fragment row = new Fragment("rowContent", "groupRow", this); | |||
item.add(row); | |||
row.add(new Label("groupName", entry.name)); | |||
WicketUtils.setCssClass(item, "group"); | |||
return; | |||
} | |||
Fragment row = new Fragment("rowContent", "repositoryRow", this); | |||
item.add(row); | |||
if (entry.hasCommits) { | |||
// Existing repository | |||
PageParameters pp = WicketUtils.newRepositoryParameter(entry.name); | |||
row.add(new LinkPanel("repositoryName", "list", entry.name, SummaryPage.class, pp)); | |||
row.add(new LinkPanel("repositoryDescription", "list", entry.description, SummaryPage.class, pp)); | |||
} else { | |||
// New repository | |||
row.add(new Label("repositoryName", entry.name + "<span class='empty'>(empty)</span>").setEscapeModelStrings(false)); | |||
row.add(new Label("repositoryDescription", entry.description)); | |||
} | |||
if (entry.useTickets) { | |||
row.add(WicketUtils.newImage("ticketsIcon", "bug_16x16.png", getString("gb.tickets"))); | |||
} else { | |||
row.add(WicketUtils.newBlankImage("ticketsIcon")); | |||
} | |||
if (entry.useDocs) { | |||
row.add(WicketUtils.newImage("docsIcon", "book_16x16.png", getString("gb.docs"))); | |||
} else { | |||
row.add(WicketUtils.newBlankImage("docsIcon")); | |||
} | |||
if (entry.isFrozen) { | |||
row.add(WicketUtils.newImage("frozenIcon", "cold_16x16.png", getString("gb.isFrozen"))); | |||
} else { | |||
row.add(WicketUtils.newClearPixel("frozenIcon").setVisible(false)); | |||
} | |||
switch (entry.accessRestriction) { | |||
case NONE: | |||
row.add(WicketUtils.newBlankImage("accessRestrictionIcon")); | |||
break; | |||
case PUSH: | |||
row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction))); | |||
break; | |||
case CLONE: | |||
row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction))); | |||
break; | |||
case VIEW: | |||
row.add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction))); | |||
break; | |||
default: | |||
row.add(WicketUtils.newBlankImage("accessRestrictionIcon")); | |||
} | |||
row.add(new Label("repositoryOwner", entry.owner)); | |||
String lastChange = TimeUtils.timeAgo(entry.lastChange); | |||
Label lastChangeLabel = new Label("repositoryLastChange", lastChange); | |||
row.add(lastChangeLabel); | |||
WicketUtils.setCssClass(lastChangeLabel, TimeUtils.timeAgoCss(entry.lastChange)); | |||
boolean showOwner = user != null && user.getUsername().equalsIgnoreCase(entry.owner); | |||
if (showAdmin) { | |||
Fragment repositoryLinks = new Fragment("repositoryLinks", "repositoryAdminLinks", this); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name))); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("renameRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name)).setEnabled(false)); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("deleteRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name)).setEnabled(false)); | |||
row.add(repositoryLinks); | |||
} else if (showOwner) { | |||
Fragment repositoryLinks = new Fragment("repositoryLinks", "repositoryOwnerLinks", this); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name))); | |||
row.add(repositoryLinks); | |||
} else { | |||
row.add(new Label("repositoryLinks")); | |||
} | |||
WicketUtils.setAlternatingBackground(item, counter); | |||
counter++; | |||
} | |||
}; | |||
add(dataView); | |||
if (dp instanceof SortableDataProvider<?>) { | |||
// add sortable header | |||
SortableDataProvider<?> sdp = (SortableDataProvider<?>) dp; | |||
Fragment fragment = new Fragment("headerContent", "flatHeader", this); | |||
fragment.add(newSort("orderByRepository", SortBy.repository, sdp, dataView)); | |||
fragment.add(newSort("orderByDescription", SortBy.description, sdp, dataView)); | |||
fragment.add(newSort("orderByOwner", SortBy.owner, sdp, dataView)); | |||
fragment.add(newSort("orderByDate", SortBy.date, sdp, dataView)); | |||
add(fragment); | |||
} else { | |||
// not sortable | |||
Fragment fragment = new Fragment("headerContent", "groupHeader", this); | |||
add(fragment); | |||
} | |||
} | |||
protected enum SortBy { | |||
repository, description, owner, date; | |||
} | |||
protected OrderByBorder newSort(String wicketId, SortBy field, SortableDataProvider<?> dp, final DataView<?> dataView) { | |||
return new OrderByBorder(wicketId, field.name(), dp) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
protected void onSortChanged() { | |||
dataView.setCurrentPage(0); | |||
} | |||
}; | |||
} | |||
private class DataProvider extends SortableDataProvider<RepositoryModel> { | |||
private static final long serialVersionUID = 1L; | |||
private List<RepositoryModel> list = null; | |||
protected DataProvider(List<RepositoryModel> list) { | |||
this.list = list; | |||
setSort(SortBy.date.name(), false); | |||
} | |||
@Override | |||
public int size() { | |||
if (list == null) | |||
return 0; | |||
return list.size(); | |||
} | |||
@Override | |||
public IModel<RepositoryModel> model(RepositoryModel header) { | |||
return new Model<RepositoryModel>(header); | |||
} | |||
@Override | |||
public Iterator<RepositoryModel> iterator(int first, int count) { | |||
SortParam sp = getSort(); | |||
String prop = sp.getProperty(); | |||
final boolean asc = sp.isAscending(); | |||
if (prop == null || prop.equals(SortBy.date.name())) { | |||
Collections.sort(list, new Comparator<RepositoryModel>() { | |||
@Override | |||
public int compare(RepositoryModel o1, RepositoryModel o2) { | |||
if (asc) | |||
return o1.lastChange.compareTo(o2.lastChange); | |||
return o2.lastChange.compareTo(o1.lastChange); | |||
} | |||
}); | |||
} else if (prop.equals(SortBy.repository.name())) { | |||
Collections.sort(list, new Comparator<RepositoryModel>() { | |||
@Override | |||
public int compare(RepositoryModel o1, RepositoryModel o2) { | |||
if (asc) | |||
return o1.name.compareTo(o2.name); | |||
return o2.name.compareTo(o1.name); | |||
} | |||
}); | |||
} else if (prop.equals(SortBy.owner.name())) { | |||
Collections.sort(list, new Comparator<RepositoryModel>() { | |||
@Override | |||
public int compare(RepositoryModel o1, RepositoryModel o2) { | |||
if (asc) | |||
return o1.owner.compareTo(o2.owner); | |||
return o2.owner.compareTo(o1.owner); | |||
} | |||
}); | |||
} else if (prop.equals(SortBy.description.name())) { | |||
Collections.sort(list, new Comparator<RepositoryModel>() { | |||
@Override | |||
public int compare(RepositoryModel o1, RepositoryModel o2) { | |||
if (asc) | |||
return o1.description.compareTo(o2.description); | |||
return o2.description.compareTo(o1.description); | |||
} | |||
}); | |||
} | |||
return list.subList(first, first + count).iterator(); | |||
} | |||
} | |||
private class GroupRepositoryModel extends RepositoryModel { | |||
private static final long serialVersionUID = 1L; | |||
GroupRepositoryModel(String name) { | |||
super(name, "", "", new Date(0)); | |||
} | |||
add(new RepositoriesPanel("repositoriesPanel", showAdmin, getAccessRestrictions())); | |||
add(new UsersPanel("usersPanel", showAdmin).setVisible(showAdmin)); | |||
} | |||
} |
@@ -0,0 +1,88 @@ | |||
<!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 wicket:id="adminPanel">[admin links]</div> | |||
<table class="repositories"> | |||
<span wicket:id="headerContent"></span> | |||
<tbody> | |||
<tr wicket:id="row"> | |||
<span wicket:id="rowContent"></span> | |||
</tr> | |||
</tbody> | |||
</table> | |||
<wicket:fragment wicket:id="adminLinks"> | |||
<!-- page nav links --> | |||
<div class="admin_nav"> | |||
<wicket:link> | |||
<img style="vertical-align: top;" src="/com/gitblit/wicket/resources/add_16x16.png"/> | |||
</wicket:link> | |||
<a wicket:id="newRepository"> | |||
<wicket:message key="gb.newRepository"></wicket:message> | |||
</a> | |||
</div> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="repositoryAdminLinks"> | |||
<span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a> | <a wicket:id="renameRepository"><wicket:message key="gb.rename">[rename]</wicket:message></a> | <a wicket:id="deleteRepository"><wicket:message key="gb.delete">[delete]</wicket:message></a></span> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="repositoryOwnerLinks"> | |||
<span class="link"><a wicket:id="editRepository"><wicket:message key="gb.edit">[edit]</wicket:message></a></span> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="flatRepositoryHeader"> | |||
<tr> | |||
<th class="left" wicket:id="orderByRepository"> | |||
<wicket:link> | |||
<img style="vertical-align: top; border: 1px solid #888;" src="/com/gitblit/wicket/resources/gitweb-favicon.png"/> | |||
</wicket:link> | |||
<wicket:message key="gb.repository">Repository</wicket:message> | |||
</th> | |||
<th wicket:id="orderByDescription"><wicket:message key="gb.description">Description</wicket:message></th> | |||
<th wicket:id="orderByOwner"><wicket:message key="gb.owner">Owner</wicket:message></th> | |||
<th></th> | |||
<th wicket:id="orderByDate"><wicket:message key="gb.lastChange">Last Change</wicket:message></th> | |||
<th clas="right"></th> | |||
</tr> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="groupRepositoryHeader"> | |||
<tr> | |||
<th class="left"> | |||
<wicket:link> | |||
<img style="vertical-align: top; border: 1px solid #888;" src="/com/gitblit/wicket/resources/gitweb-favicon.png"/> | |||
</wicket:link> | |||
<wicket:message key="gb.repository">Repository</wicket:message> | |||
</th> | |||
<th><wicket:message key="gb.description">Description</wicket:message></th> | |||
<th><wicket:message key="gb.owner">Owner</wicket:message></th> | |||
<th></th> | |||
<th><wicket:message key="gb.lastChange">Last Change</wicket:message></th> | |||
<th class="right"></th> | |||
</tr> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="groupRepositoryRow"> | |||
<td colspan="6"><span wicket:id="groupName">[group name]</span></td> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="repositoryRow"> | |||
<td class="left"><div class="list" wicket:id="repositoryName">[repository name]</div></td> | |||
<td><div class="list" wicket:id="repositoryDescription">[repository description]</div></td> | |||
<td class="author"><span wicket:id="repositoryOwner">[repository owner]</span></td> | |||
<td style="text-align: right;padding-right:10px;"><img class="inlineIcon" wicket:id="ticketsIcon" /><img class="inlineIcon" wicket:id="docsIcon" /><img class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon" wicket:id="accessRestrictionIcon" /></td> | |||
<td><span wicket:id="repositoryLastChange">[last change]</span></td> | |||
<td class="rightAlign"><span wicket:id="repositoryLinks"></span></td> | |||
</wicket:fragment> | |||
</wicket:panel> | |||
</body> | |||
</html> |
@@ -0,0 +1,274 @@ | |||
package com.gitblit.wicket.panels; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.Date; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.wicket.PageParameters; | |||
import org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByBorder; | |||
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam; | |||
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider; | |||
import org.apache.wicket.markup.html.basic.Label; | |||
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.IDataProvider; | |||
import org.apache.wicket.markup.repeater.data.ListDataProvider; | |||
import org.apache.wicket.model.IModel; | |||
import org.apache.wicket.model.Model; | |||
import com.gitblit.Constants.AccessRestrictionType; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.Keys; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.utils.TimeUtils; | |||
import com.gitblit.wicket.GitBlitWebSession; | |||
import com.gitblit.wicket.LinkPanel; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.models.RepositoryModel; | |||
import com.gitblit.wicket.models.UserModel; | |||
import com.gitblit.wicket.pages.EditRepositoryPage; | |||
import com.gitblit.wicket.pages.SummaryPage; | |||
public class RepositoriesPanel extends BasePanel { | |||
private static final long serialVersionUID = 1L; | |||
public RepositoriesPanel(String wicketId, final boolean showAdmin, final Map<AccessRestrictionType, String> accessRestrictionTranslations) { | |||
super(wicketId); | |||
final UserModel user = GitBlitWebSession.get().getUser(); | |||
List<RepositoryModel> models = GitBlit.self().getRepositoryModels(user); | |||
IDataProvider<RepositoryModel> dp; | |||
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this); | |||
adminLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class)); | |||
add(adminLinks.setVisible(showAdmin)); | |||
if (GitBlit.self().settings().getString(Keys.web.repositoryListType, "flat").equalsIgnoreCase("grouped")) { | |||
Map<String, List<RepositoryModel>> groups = new HashMap<String, List<RepositoryModel>>(); | |||
for (RepositoryModel model : models) { | |||
String rootPath = StringUtils.getRootPath(model.name); | |||
if (StringUtils.isEmpty(rootPath)) { | |||
rootPath = GitBlit.self().settings().getString(Keys.web.repositoryRootGroupName, " "); | |||
} | |||
if (!groups.containsKey(rootPath)) { | |||
groups.put(rootPath, new ArrayList<RepositoryModel>()); | |||
} | |||
groups.get(rootPath).add(model); | |||
} | |||
List<String> roots = new ArrayList<String>(groups.keySet()); | |||
Collections.sort(roots); | |||
List<RepositoryModel> groupedModels = new ArrayList<RepositoryModel>(); | |||
for (String root : roots) { | |||
List<RepositoryModel> subModels = groups.get(root); | |||
groupedModels.add(new GroupRepositoryModel(root + " (" + subModels.size() + ")")); | |||
groupedModels.addAll(subModels); | |||
} | |||
dp = new ListDataProvider<RepositoryModel>(groupedModels); | |||
} else { | |||
dp = new DataProvider(models); | |||
} | |||
DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) { | |||
private static final long serialVersionUID = 1L; | |||
int counter = 0; | |||
public void populateItem(final Item<RepositoryModel> item) { | |||
final RepositoryModel entry = item.getModelObject(); | |||
if (entry instanceof GroupRepositoryModel) { | |||
Fragment row = new Fragment("rowContent", "groupRepositoryRow", this); | |||
item.add(row); | |||
row.add(new Label("groupName", entry.name)); | |||
WicketUtils.setCssClass(item, "group"); | |||
return; | |||
} | |||
Fragment row = new Fragment("rowContent", "repositoryRow", this); | |||
item.add(row); | |||
if (entry.hasCommits) { | |||
// Existing repository | |||
PageParameters pp = WicketUtils.newRepositoryParameter(entry.name); | |||
row.add(new LinkPanel("repositoryName", "list", entry.name, SummaryPage.class, pp)); | |||
row.add(new LinkPanel("repositoryDescription", "list", entry.description, SummaryPage.class, pp)); | |||
} else { | |||
// New repository | |||
row.add(new Label("repositoryName", entry.name + "<span class='empty'>(empty)</span>").setEscapeModelStrings(false)); | |||
row.add(new Label("repositoryDescription", entry.description)); | |||
} | |||
if (entry.useTickets) { | |||
row.add(WicketUtils.newImage("ticketsIcon", "bug_16x16.png", getString("gb.tickets"))); | |||
} else { | |||
row.add(WicketUtils.newBlankImage("ticketsIcon")); | |||
} | |||
if (entry.useDocs) { | |||
row.add(WicketUtils.newImage("docsIcon", "book_16x16.png", getString("gb.docs"))); | |||
} else { | |||
row.add(WicketUtils.newBlankImage("docsIcon")); | |||
} | |||
if (entry.isFrozen) { | |||
row.add(WicketUtils.newImage("frozenIcon", "cold_16x16.png", getString("gb.isFrozen"))); | |||
} else { | |||
row.add(WicketUtils.newClearPixel("frozenIcon").setVisible(false)); | |||
} | |||
switch (entry.accessRestriction) { | |||
case NONE: | |||
row.add(WicketUtils.newBlankImage("accessRestrictionIcon")); | |||
break; | |||
case PUSH: | |||
row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction))); | |||
break; | |||
case CLONE: | |||
row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction))); | |||
break; | |||
case VIEW: | |||
row.add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction))); | |||
break; | |||
default: | |||
row.add(WicketUtils.newBlankImage("accessRestrictionIcon")); | |||
} | |||
row.add(new Label("repositoryOwner", entry.owner)); | |||
String lastChange = TimeUtils.timeAgo(entry.lastChange); | |||
Label lastChangeLabel = new Label("repositoryLastChange", lastChange); | |||
row.add(lastChangeLabel); | |||
WicketUtils.setCssClass(lastChangeLabel, TimeUtils.timeAgoCss(entry.lastChange)); | |||
boolean showOwner = user != null && user.getUsername().equalsIgnoreCase(entry.owner); | |||
if (showAdmin) { | |||
Fragment repositoryLinks = new Fragment("repositoryLinks", "repositoryAdminLinks", this); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name))); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("renameRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name)).setEnabled(false)); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("deleteRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name)).setEnabled(false)); | |||
row.add(repositoryLinks); | |||
} else if (showOwner) { | |||
Fragment repositoryLinks = new Fragment("repositoryLinks", "repositoryOwnerLinks", this); | |||
repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name))); | |||
row.add(repositoryLinks); | |||
} else { | |||
row.add(new Label("repositoryLinks")); | |||
} | |||
WicketUtils.setAlternatingBackground(item, counter); | |||
counter++; | |||
} | |||
}; | |||
add(dataView); | |||
if (dp instanceof SortableDataProvider<?>) { | |||
// add sortable header | |||
SortableDataProvider<?> sdp = (SortableDataProvider<?>) dp; | |||
Fragment fragment = new Fragment("headerContent", "flatRepositoryHeader", this); | |||
fragment.add(newSort("orderByRepository", SortBy.repository, sdp, dataView)); | |||
fragment.add(newSort("orderByDescription", SortBy.description, sdp, dataView)); | |||
fragment.add(newSort("orderByOwner", SortBy.owner, sdp, dataView)); | |||
fragment.add(newSort("orderByDate", SortBy.date, sdp, dataView)); | |||
add(fragment); | |||
} else { | |||
// not sortable | |||
Fragment fragment = new Fragment("headerContent", "groupRepositoryHeader", this); | |||
add(fragment); | |||
} | |||
} | |||
private class GroupRepositoryModel extends RepositoryModel { | |||
private static final long serialVersionUID = 1L; | |||
GroupRepositoryModel(String name) { | |||
super(name, "", "", new Date(0)); | |||
} | |||
} | |||
protected enum SortBy { | |||
repository, description, owner, date; | |||
} | |||
protected OrderByBorder newSort(String wicketId, SortBy field, SortableDataProvider<?> dp, final DataView<?> dataView) { | |||
return new OrderByBorder(wicketId, field.name(), dp) { | |||
private static final long serialVersionUID = 1L; | |||
@Override | |||
protected void onSortChanged() { | |||
dataView.setCurrentPage(0); | |||
} | |||
}; | |||
} | |||
private class DataProvider extends SortableDataProvider<RepositoryModel> { | |||
private static final long serialVersionUID = 1L; | |||
private List<RepositoryModel> list = null; | |||
protected DataProvider(List<RepositoryModel> list) { | |||
this.list = list; | |||
setSort(SortBy.date.name(), false); | |||
} | |||
@Override | |||
public int size() { | |||
if (list == null) | |||
return 0; | |||
return list.size(); | |||
} | |||
@Override | |||
public IModel<RepositoryModel> model(RepositoryModel header) { | |||
return new Model<RepositoryModel>(header); | |||
} | |||
@Override | |||
public Iterator<RepositoryModel> iterator(int first, int count) { | |||
SortParam sp = getSort(); | |||
String prop = sp.getProperty(); | |||
final boolean asc = sp.isAscending(); | |||
if (prop == null || prop.equals(SortBy.date.name())) { | |||
Collections.sort(list, new Comparator<RepositoryModel>() { | |||
@Override | |||
public int compare(RepositoryModel o1, RepositoryModel o2) { | |||
if (asc) | |||
return o1.lastChange.compareTo(o2.lastChange); | |||
return o2.lastChange.compareTo(o1.lastChange); | |||
} | |||
}); | |||
} else if (prop.equals(SortBy.repository.name())) { | |||
Collections.sort(list, new Comparator<RepositoryModel>() { | |||
@Override | |||
public int compare(RepositoryModel o1, RepositoryModel o2) { | |||
if (asc) | |||
return o1.name.compareTo(o2.name); | |||
return o2.name.compareTo(o1.name); | |||
} | |||
}); | |||
} else if (prop.equals(SortBy.owner.name())) { | |||
Collections.sort(list, new Comparator<RepositoryModel>() { | |||
@Override | |||
public int compare(RepositoryModel o1, RepositoryModel o2) { | |||
if (asc) | |||
return o1.owner.compareTo(o2.owner); | |||
return o2.owner.compareTo(o1.owner); | |||
} | |||
}); | |||
} else if (prop.equals(SortBy.description.name())) { | |||
Collections.sort(list, new Comparator<RepositoryModel>() { | |||
@Override | |||
public int compare(RepositoryModel o1, RepositoryModel o2) { | |||
if (asc) | |||
return o1.description.compareTo(o2.description); | |||
return o2.description.compareTo(o1.description); | |||
} | |||
}); | |||
} | |||
return list.subList(first, first + count).iterator(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
<!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 wicket:id="adminPanel">[admin links]</div> | |||
<table class="repositories"> | |||
<tr> | |||
<th class="left"> | |||
<wicket:link> | |||
<img style="vertical-align: top; border: 1px solid #888; background-color: white;" src="/com/gitblit/wicket/resources/user_16x16.png"/> | |||
</wicket:link> | |||
<wicket:message key="gb.username">[username]</wicket:message> | |||
</th> | |||
<th class="right"></th> | |||
</tr> | |||
<tbody> | |||
<tr wicket:id="userRow"> | |||
<td class="left" ><div class="list" wicket:id="username">[username]</div></td> | |||
<td class="rightAlign"><span wicket:id="userLinks"></span></td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
<wicket:fragment wicket:id="adminLinks"> | |||
<!-- page nav links --> | |||
<div class="admin_nav"> | |||
<wicket:link> | |||
<img style="vertical-align: top;" src="/com/gitblit/wicket/resources/add_16x16.png"/> | |||
</wicket:link> | |||
<a wicket:id="newUser"> | |||
<wicket:message key="gb.newUser"></wicket:message> | |||
</a> | |||
</div> | |||
</wicket:fragment> | |||
<wicket:fragment wicket:id="userAdminLinks"> | |||
<span class="link"><a wicket:id="editUser"><wicket:message key="gb.edit">[edit]</wicket:message></a> | <a wicket:id="deleteUser"><wicket:message key="gb.delete">[delete]</wicket:message></a></span> | |||
</wicket:fragment> | |||
</wicket:panel> | |||
</body> | |||
</html> |
@@ -0,0 +1,46 @@ | |||
package com.gitblit.wicket.panels; | |||
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 com.gitblit.GitBlit; | |||
import com.gitblit.wicket.LinkPanel; | |||
import com.gitblit.wicket.WicketUtils; | |||
import com.gitblit.wicket.pages.EditUserPage; | |||
import com.gitblit.wicket.pages.RepositoriesPage; | |||
public class UsersPanel extends BasePanel { | |||
private static final long serialVersionUID = 1L; | |||
public UsersPanel(String wicketId, final boolean showAdmin) { | |||
super(wicketId); | |||
Fragment adminLinks = new Fragment("adminPanel", "adminLinks", this); | |||
adminLinks.add(new BookmarkablePageLink<Void>("newUser", EditUserPage.class)); | |||
add(adminLinks.setVisible(showAdmin)); | |||
DataView<String> usersView = new DataView<String>("userRow", new ListDataProvider<String>(GitBlit.self().getAllUsernames())) { | |||
private static final long serialVersionUID = 1L; | |||
private int counter = 0; | |||
public void populateItem(final Item<String> item) { | |||
final String entry = item.getModelObject(); | |||
LinkPanel editLink = new LinkPanel("username", "list", entry, EditUserPage.class, WicketUtils.newUsernameParameter(entry)); | |||
WicketUtils.setHtmlTooltip(editLink, getString("gb.edit") + " " + entry); | |||
item.add(editLink); | |||
Fragment userLinks = new Fragment("userLinks", "userAdminLinks", this); | |||
userLinks.add(new BookmarkablePageLink<Void>("editUser", EditUserPage.class, WicketUtils.newUsernameParameter(entry))); | |||
userLinks.add(new BookmarkablePageLink<Void>("deleteUser", RepositoriesPage.class, WicketUtils.newUsernameParameter(entry)).setEnabled(false)); | |||
item.add(userLinks); | |||
WicketUtils.setAlternatingBackground(item, counter); | |||
counter++; | |||
} | |||
}; | |||
add(usersView.setVisible(showAdmin)); | |||
} | |||
} |
@@ -190,6 +190,10 @@ div.page_nav2 { | |||
} | |||
div.admin_nav { | |||
border: 1px solid #888; | |||
border-bottom: 0px; | |||
background:#dae0d2; | |||
text-align: right; | |||
padding: 5px 5px 5px 2px; | |||
} | |||
@@ -493,7 +497,7 @@ table.plain td.edit input:focus, table.plain td.edit input:hover{ | |||
border: 1px solid orange; | |||
} | |||
table.pretty, table.repositories, table.comments { | |||
table.pretty, table.comments { | |||
margin-bottom:5px; | |||
border-spacing: 0px; | |||
border-left: 1px solid #bbb; | |||
@@ -522,6 +526,11 @@ table.comments td { | |||
line-height: 17px; | |||
} | |||
table.repositories { | |||
margin-bottom:5px; | |||
border-spacing: 0px; | |||
} | |||
table.repositories th { | |||
background-color:#D2C3AF; | |||
padding: 4px; | |||
@@ -529,10 +538,28 @@ table.repositories th { | |||
border-bottom: 1px solid #808080; | |||
} | |||
table.repositories th.left, table.repositories td.left { | |||
border-left: 1px solid #808080; | |||
padding-left: 5px; | |||
} | |||
table.repositories td.left { | |||
padding-left: 10px; | |||
} | |||
table.repositories th.right, table.repositories td.right { | |||
border-right: 1px solid #808080; | |||
} | |||
table.repositories td { | |||
padding: 2px; | |||
} | |||
table.repositories td.rightAlign { | |||
text-align: right; | |||
border-right: 1px solid #808080; | |||
} | |||
table.repositories td.icon img { | |||
vertical-align: top; | |||
} | |||
@@ -552,6 +579,20 @@ table.repositories th.wicket_orderDown a, table.repositories th.wicket_orderUp a | |||
font-weight: bold; | |||
} | |||
table.repositories tr.group { | |||
background-color: #E66C2C; | |||
} | |||
table.repositories tr.group td { | |||
font-weight: bold; | |||
border-bottom: 1px solid orange; | |||
color: white; | |||
background-color: #E66C2C; | |||
border-left: 1px solid #808080; | |||
border-right: 1px solid #808080; | |||
padding-left: 5px; | |||
} | |||
table.palette { border:0;} | |||
table.palette td.header { | |||
font-weight: bold; | |||
@@ -570,17 +611,6 @@ tr th.wicket_orderDown a {background-image: url(arrow_down.png); } | |||
tr th.wicket_orderUp a { background-image: url(arrow_up.png); } | |||
tr th.wicket_orderNone a { background-image: url(arrow_off.png); } | |||
tr.group { | |||
background-color: #E66C2C; | |||
} | |||
tr.group td { | |||
font-weight: bold; | |||
border-bottom: 1px solid orange; | |||
color: white; | |||
background-color: #E66C2C; | |||
} | |||
tr.light { | |||
background-color: #ffffff; | |||
} |