From a4d2498b7f94012cfdf481fcf151f8cfd7537a42 Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Mon, 23 May 2011 16:46:09 -0400 Subject: [PATCH] User list. Revised home page. Updated Jetty. Secure cookies. Docs. --- src/com/gitblit/Build.java | 2 src/com/gitblit/wicket/pages/RepositoriesPage.java | 260 --------------- .classpath | 10 src/com/gitblit/BuildSite.java | 24 src/com/gitblit/wicket/resources/gitweb-favicon.png | 0 src/com/gitblit/wicket/panels/RepositoriesPanel.java | 274 ++++++++++++++++ docs/00_setup.mkd | 2 docs/01_screenshots.mkd | 4 docs/01_faq.mkd | 18 src/com/gitblit/wicket/pages/EditUserPage.html | 2 src/com/gitblit/wicket/GitBlitWebApp.properties | 4 src/com/gitblit/wicket/WicketUtils.java | 4 src/com/gitblit/GitBlitServer.java | 11 src/com/gitblit/wicket/pages/RepositoriesPage.html | 61 --- src/com/gitblit/wicket/panels/UsersPanel.html | 48 ++ src/com/gitblit/wicket/panels/RepositoriesPanel.html | 88 +++++ docs/00_index.mkd | 15 src/com/gitblit/Constants.java | 6 src/com/gitblit/wicket/panels/UsersPanel.java | 46 ++ docs/screenshots/thumbs/00.png | 0 distrib/gitblit.properties | 2 src/com/gitblit/wicket/resources/gitblit.css | 54 ++ src/com/gitblit/wicket/resources/user_16x16.png | 0 src/com/gitblit/wicket/resources/add_16x16.png | 0 build.xml | 51 ++ docs/screenshots/00.png | 0 docs/screenshots/raw/00.png | 0 27 files changed, 611 insertions(+), 375 deletions(-) diff --git a/.classpath b/.classpath index 5cddd20..4e3fa85 100644 --- a/.classpath +++ b/.classpath @@ -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> diff --git a/build.xml b/build.xml index f47e5e9..a3394c6 100644 --- a/build.xml +++ b/build.xml @@ -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> @@ -17,6 +22,21 @@ <striplinebreaks /> <tokenfilter> <replacestring from="public final static String VERSION = "" to="" /> + <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> @@ -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> diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index de243d2..d0e6375 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -69,7 +69,7 @@ # 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 diff --git a/docs/00_index.mkd b/docs/00_index.mkd index 6fdc0a1..a43ffdc 100644 --- a/docs/00_index.mkd +++ b/docs/00_index.mkd @@ -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 @@ </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 @@ - 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 @@ - [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. diff --git a/docs/00_setup.mkd b/docs/00_setup.mkd index 415ad47..a590b59 100644 --- a/docs/00_setup.mkd +++ b/docs/00_setup.mkd @@ -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: diff --git a/docs/01_faq.mkd b/docs/01_faq.mkd index 94c6dbf..23ee2b2 100644 --- a/docs/01_faq.mkd +++ b/docs/01_faq.mkd @@ -10,10 +10,17 @@ 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. -### 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. +### 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`. -You might consider [Gerrit](http://gerrit.googlecode.org) which supports SSH. +### 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][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 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 @@ 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" \ No newline at end of file +[git]: http://git-scm.com "Official Git Site" +[mina]: http://mina.apache.org " Apache Mina" \ No newline at end of file diff --git a/docs/01_screenshots.mkd b/docs/01_screenshots.mkd index 3d2061e..14ec179 100644 --- a/docs/01_screenshots.mkd +++ b/docs/01_screenshots.mkd @@ -1,8 +1,8 @@ ## Screenshots <table class="screenshots"> <tr><td> - <a rel="screenshots_group" href="screenshots/00.png" title="Repository List"></a> - <br/>Repository List + <a rel="screenshots_group" href="screenshots/00.png" title="Repository & User List"></a> + <br/>Repository & User List </td><td> <a rel="screenshots_group" href="screenshots/01.png" title="New User"></a> <br/>New User diff --git a/docs/screenshots/00.png b/docs/screenshots/00.png index 616b31c..c9653f1 100644 --- a/docs/screenshots/00.png +++ b/docs/screenshots/00.png Binary files differ diff --git a/docs/screenshots/raw/00.png b/docs/screenshots/raw/00.png index 314f521..eb50149 100644 --- a/docs/screenshots/raw/00.png +++ b/docs/screenshots/raw/00.png Binary files differ diff --git a/docs/screenshots/thumbs/00.png b/docs/screenshots/thumbs/00.png index 6aea693..636631f 100644 --- a/docs/screenshots/thumbs/00.png +++ b/docs/screenshots/thumbs/00.png Binary files differ diff --git a/src/com/gitblit/Build.java b/src/com/gitblit/Build.java index 8952726..1d2c0f1 100644 --- a/src/com/gitblit/Build.java +++ b/src/com/gitblit/Build.java @@ -268,7 +268,7 @@ 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); diff --git a/src/com/gitblit/BuildSite.java b/src/com/gitblit/BuildSite.java index 6ea8048..dc42cf5 100644 --- a/src/com/gitblit/BuildSite.java +++ b/src/com/gitblit/BuildSite.java @@ -46,11 +46,11 @@ 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 @@ } 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 @@ 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 @@ // 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 @@ } System.exit(0); } - + @Parameters(separators = " ") private static class Params { @@ -154,5 +151,8 @@ @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>(); + } } diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index 7e19cef..46f3208 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -10,6 +10,10 @@ // 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 static String getJGitVersion() { - return "JGit 0.12.1"; + return JGIT_VERSION; } public static String getRunningVersion() { diff --git a/src/com/gitblit/GitBlitServer.java b/src/com/gitblit/GitBlitServer.java index 08c9b29..e9e4463 100644 --- a/src/com/gitblit/GitBlitServer.java +++ b/src/com/gitblit/GitBlitServer.java @@ -29,6 +29,7 @@ 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 @@ 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 = "/*"; diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index d07f0bc..b6dbc11 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -43,8 +43,8 @@ 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 diff --git a/src/com/gitblit/wicket/WicketUtils.java b/src/com/gitblit/wicket/WicketUtils.java index f0ccbf4..761595b 100644 --- a/src/com/gitblit/wicket/WicketUtils.java +++ b/src/com/gitblit/wicket/WicketUtils.java @@ -153,6 +153,10 @@ 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); } diff --git a/src/com/gitblit/wicket/pages/EditUserPage.html b/src/com/gitblit/wicket/pages/EditUserPage.html index c50bdba..a27b712 100644 --- a/src/com/gitblit/wicket/pages/EditUserPage.html +++ b/src/com/gitblit/wicket/pages/EditUserPage.html @@ -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> diff --git a/src/com/gitblit/wicket/pages/RepositoriesPage.html b/src/com/gitblit/wicket/pages/RepositoriesPage.html index d00c498..da91cb2 100644 --- a/src/com/gitblit/wicket/pages/RepositoriesPage.html +++ b/src/com/gitblit/wicket/pages/RepositoriesPage.html @@ -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> + <div wicket:id="repositoriesPanel">[repositories panel]</div> - <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="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> \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/RepositoriesPage.java b/src/com/gitblit/wicket/pages/RepositoriesPage.java index 14a5426..32552f7 100644 --- a/src/com/gitblit/wicket/pages/RepositoriesPage.java +++ b/src/com/gitblit/wicket/pages/RepositoriesPage.java @@ -4,50 +4,27 @@ 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); @@ -64,12 +41,6 @@ setStatelessHint(true); } } - - 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(); @@ -111,228 +82,7 @@ } 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)); } } diff --git a/src/com/gitblit/wicket/panels/RepositoriesPanel.html b/src/com/gitblit/wicket/panels/RepositoriesPanel.html new file mode 100644 index 0000000..a066b58 --- /dev/null +++ b/src/com/gitblit/wicket/panels/RepositoriesPanel.html @@ -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> \ No newline at end of file diff --git a/src/com/gitblit/wicket/panels/RepositoriesPanel.java b/src/com/gitblit/wicket/panels/RepositoriesPanel.java new file mode 100644 index 0000000..6d05888 --- /dev/null +++ b/src/com/gitblit/wicket/panels/RepositoriesPanel.java @@ -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(); + } + } +} diff --git a/src/com/gitblit/wicket/panels/UsersPanel.html b/src/com/gitblit/wicket/panels/UsersPanel.html new file mode 100644 index 0000000..39074b2 --- /dev/null +++ b/src/com/gitblit/wicket/panels/UsersPanel.html @@ -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> \ No newline at end of file diff --git a/src/com/gitblit/wicket/panels/UsersPanel.java b/src/com/gitblit/wicket/panels/UsersPanel.java new file mode 100644 index 0000000..1721272 --- /dev/null +++ b/src/com/gitblit/wicket/panels/UsersPanel.java @@ -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)); + } +} diff --git a/src/com/gitblit/wicket/resources/add_16x16.png b/src/com/gitblit/wicket/resources/add_16x16.png new file mode 100644 index 0000000..0ea124a --- /dev/null +++ b/src/com/gitblit/wicket/resources/add_16x16.png Binary files differ diff --git a/src/com/gitblit/wicket/resources/gitblit.css b/src/com/gitblit/wicket/resources/gitblit.css index 3fa1fcf..4a971a6 100644 --- a/src/com/gitblit/wicket/resources/gitblit.css +++ b/src/com/gitblit/wicket/resources/gitblit.css @@ -190,6 +190,10 @@ } div.admin_nav { + border: 1px solid #888; + border-bottom: 0px; + background:#dae0d2; + text-align: right; padding: 5px 5px 5px 2px; } @@ -493,7 +497,7 @@ 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 @@ line-height: 17px; } +table.repositories { + margin-bottom:5px; + border-spacing: 0px; +} + table.repositories th { background-color:#D2C3AF; padding: 4px; @@ -529,9 +538,27 @@ 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 @@ 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; @@ -569,17 +610,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; diff --git a/src/com/gitblit/wicket/resources/gitweb-favicon.png b/src/com/gitblit/wicket/resources/gitweb-favicon.png new file mode 100644 index 0000000..de637c0 --- /dev/null +++ b/src/com/gitblit/wicket/resources/gitweb-favicon.png Binary files differ diff --git a/src/com/gitblit/wicket/resources/user_16x16.png b/src/com/gitblit/wicket/resources/user_16x16.png new file mode 100644 index 0000000..d5edd4d --- /dev/null +++ b/src/com/gitblit/wicket/resources/user_16x16.png Binary files differ -- Gitblit v1.9.1