James Moger
2011-05-26 2a7306a1d92522569a8bb6e5a7c0bcdd5cf4cfaa
Findbugs. CodePro Audit. Checkstyle. Unit test refactoring.
4 files added
75 files modified
1 files deleted
2861 ■■■■■ changed files
.checkstyle 10 ●●●●● patch | view | raw | blame | history
.classpath 6 ●●●●● patch | view | raw | blame | history
.gitignore 3 ●●●● patch | view | raw | blame | history
.project 6 ●●●●● patch | view | raw | blame | history
NOTICE 16 ●●●●● patch | view | raw | blame | history
build.xml 10 ●●●●● patch | view | raw | blame | history
checkstyle.xml 109 ●●●●● patch | view | raw | blame | history
distrib/gitblit.properties 2 ●●● patch | view | raw | blame | history
docs/00_index.mkd 4 ●●●● patch | view | raw | blame | history
docs/page_footer.html 1 ●●●● patch | view | raw | blame | history
src/com/gitblit/Build.java 200 ●●●● patch | view | raw | blame | history
src/com/gitblit/BuildSite.java 27 ●●●●● patch | view | raw | blame | history
src/com/gitblit/Constants.java 34 ●●●●● patch | view | raw | blame | history
src/com/gitblit/DownloadZipServlet.java 41 ●●●●● patch | view | raw | blame | history
src/com/gitblit/FileSettings.java 25 ●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlit.java 93 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlitServer.java 149 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlitServlet.java 32 ●●●●● patch | view | raw | blame | history
src/com/gitblit/ILoginService.java 18 ●●●● patch | view | raw | blame | history
src/com/gitblit/IStoredSettings.java 16 ●●●● patch | view | raw | blame | history
src/com/gitblit/JettyLoginService.java 69 ●●●●● patch | view | raw | blame | history
src/com/gitblit/Launcher.java 52 ●●●● patch | view | raw | blame | history
src/com/gitblit/MakeCertificate.java 79 ●●●●● patch | view | raw | blame | history
src/com/gitblit/WebXmlSettings.java 6 ●●●● patch | view | raw | blame | history
src/com/gitblit/tests/JGitUtilsTest.java 145 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/ByteFormat.java 5 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/GitBlitDiffFormatter.java 15 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/GitWebDiffFormatter.java 6 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/JGitUtils.java 127 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/MarkdownUtils.java 11 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/PatchFormatter.java 33 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/StringUtils.java 11 ●●●● patch | view | raw | blame | history
src/com/gitblit/utils/TimeUtils.java 51 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/AuthorizationStrategy.java 24 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/BasePage.java 16 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.java 58 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebSession.java 14 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/LinkPanel.java 11 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/LoginPage.java 9 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/RepositoryPage.java 105 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/WicketUtils.java 49 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/models/PathModel.java 33 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/models/RefModel.java 28 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/models/RepositoryModel.java 4 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/models/TicketModel.java 32 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/models/UserModel.java 33 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/BlobDiffPage.java 23 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/BlobPage.java 24 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/CommitDiffPage.java 29 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/CommitPage.java 51 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/DocsPage.java 18 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/EditRepositoryPage.java 39 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/EditUserPage.java 36 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/HistoryPage.java 27 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/LogPage.java 27 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/MarkdownPage.java 15 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/PatchPage.java 14 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/RawPage.java 7 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/RepositoriesPage.java 15 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SearchPage.java 31 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SummaryPage.java 42 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TagPage.java 15 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TicketPage.java 11 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TicketsPage.java 14 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TreePage.java 48 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/BasePanel.java 8 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/BranchesPanel.java 27 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/CommitHeaderPanel.java 5 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/CommitLegendPanel.java 11 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/HistoryPanel.java 40 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/LogPanel.java 44 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/PathBreadcrumbsPanel.java 14 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/RefsPanel.java 16 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/RepositoriesPanel.java 91 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/SearchPanel.java 34 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/TagsPanel.java 43 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/UsersPanel.java 16 ●●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/GitBlitSuite.java 64 ●●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/JGitUtilsTest.java 130 ●●●●● patch | view | raw | blame | history
.checkstyle
New file
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<fileset-config file-format-version="1.2.0" simple-config="true" sync-formatter="false">
  <local-check-config name="Gitblit" location="checkstyle.xml" type="project" description="">
    <additional-data name="protect-config-file" value="false"/>
  </local-check-config>
  <fileset name="all" enabled="true" check-config-name="Gitblit" local="true">
    <file-match-pattern match-pattern="." include-pattern="true"/>
  </fileset>
</fileset-config>
.classpath
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
    <classpathentry kind="src" path="src"/>
    <classpathentry kind="src" path="tests"/>
    <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
    <classpathentry kind="lib" path="ext/log4j-1.2.16.jar" sourcepath="ext/log4j-1.2.16-sources.jar">
        <attributes>
@@ -74,5 +75,10 @@
            <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/jsch-0.1.44-1.jar" sourcepath="ext/jsch-0.1.44-1-sources.jar">
        <attributes>
            <attribute name="javadoc_location" value="jar:platform:/resource/gitblit/ext/jsch-0.1.44-1-javadoc.jar!/"/>
        </attributes>
    </classpathentry>
    <classpathentry kind="output" path="bin"/>
</classpath>
.gitignore
@@ -6,4 +6,5 @@
/*.zip
/gitblit.properties
/users.properties
/site
/site
/git
.project
@@ -10,8 +10,14 @@
            <arguments>
            </arguments>
        </buildCommand>
        <buildCommand>
            <name>net.sf.eclipsecs.core.CheckstyleBuilder</name>
            <arguments>
            </arguments>
        </buildCommand>
    </buildSpec>
    <natures>
        <nature>org.eclipse.jdt.core.javanature</nature>
        <nature>net.sf.eclipsecs.core.CheckstyleNature</nature>
    </natures>
</projectDescription>
NOTICE
@@ -88,6 +88,22 @@
   http://www.bouncycastle.org
   
---------------------------------------------------------------------------
JSch
---------------------------------------------------------------------------
   JSch - Java Secure Channel, released under the
   BSD License.
   http://www.jcraft.com/jsch
---------------------------------------------------------------------------
JUnit
---------------------------------------------------------------------------
   JUnit, released under the
   Common Public License.
   http://junit.org
---------------------------------------------------------------------------
Fancybox image viewer
---------------------------------------------------------------------------
   Fancybox image viewer, released under the
build.xml
@@ -17,11 +17,11 @@
        <loadfile property="gb.version" srcfile="${basedir}/src/com/gitblit/Constants.java">
            <filterchain>
                <linecontains>
                    <contains value="public final static String VERSION = " />
                    <contains value="public static final String VERSION = " />
                </linecontains>
                <striplinebreaks />
                <tokenfilter>
                    <replacestring from="public final static String VERSION = &quot;" to="" />
                    <replacestring from="public static final String VERSION = &quot;" to="" />
                    <replacestring from="&quot;;" to="" />
                    <trim />
                </tokenfilter>
@@ -32,11 +32,11 @@
        <loadfile property="jgit.version" srcfile="${basedir}/src/com/gitblit/Constants.java">
            <filterchain>
                <linecontains>
                    <contains value="public final static String JGIT_VERSION = " />
                    <contains value="public static final String JGIT_VERSION = " />
                </linecontains>
                <striplinebreaks />
                <tokenfilter>
                    <replacestring from="public final static String JGIT_VERSION = &quot;" to="" />
                    <replacestring from="public static final String JGIT_VERSION = &quot;" to="" />
                    <replacestring from="&quot;;" to="" />
                    <trim />
                </tokenfilter>
@@ -201,5 +201,7 @@
            <arg value="%JGIT%=${jgit.version}" />
        </java>
        <!-- Cleanup -->
        <delete dir="${project.build.dir}" />
    </target>
</project>
checkstyle.xml
New file
@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN" "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<!--
    This configuration file was written by the eclipse-cs plugin
    configuration editor
-->
<!--
    Checkstyle-Configuration: gitblit Description: none
-->
<module name="Checker">
    <property name="severity" value="warning" />
    <module name="TreeWalker">
        <property name="tabWidth" value="4" />
        <module name="ConstantName" />
        <module name="LocalFinalVariableName" />
        <module name="LocalVariableName" />
        <module name="MemberName" />
        <module name="MethodName" />
        <module name="PackageName" />
        <module name="ParameterName" />
        <module name="StaticVariableName" />
        <module name="TypeName" />
        <module name="AvoidStarImport" />
        <module name="IllegalImport" />
        <module name="RedundantImport" />
        <module name="UnusedImports" />
        <module name="EmptyForIteratorPad" />
        <module name="MethodParamPad" />
        <module name="NoWhitespaceAfter">
            <property name="tokens"
                value="BNOT,DEC,DOT,INC,LNOT,UNARY_MINUS,UNARY_PLUS" />
        </module>
        <module name="NoWhitespaceBefore" />
        <module name="OperatorWrap">
            <property name="severity" value="ignore" />
            <property name="tokens"
                value="BAND,BOR,BSR,BXOR,COLON,DIV,EQUAL,GE,GT,LAND,LE,LITERAL_INSTANCEOF,LT,MINUS,MOD,NOT_EQUAL,SL,SR,STAR" />
            <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity"
                value="inherit" />
        </module>
        <module name="ParenPad" />
        <module name="WhitespaceAfter" />
        <module name="WhitespaceAround">
            <property name="tokens"
                value="ASSIGN,BAND,BAND_ASSIGN,BOR,BOR_ASSIGN,BSR,BSR_ASSIGN,BXOR,BXOR_ASSIGN,DIV_ASSIGN,EQUAL,GE,GT,LAND,LE,LITERAL_ASSERT,LITERAL_CATCH,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_RETURN,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,LOR,LT,MINUS,MINUS_ASSIGN,MOD,MOD_ASSIGN,NOT_EQUAL,PLUS_ASSIGN,SL,SLIST,SL_ASSIGN,SR,SR_ASSIGN,STAR,STAR_ASSIGN,LITERAL_ASSERT,TYPE_EXTENSION_AND,WILDCARD_TYPE" />
        </module>
        <module name="ModifierOrder" />
        <module name="RedundantModifier" />
        <module name="LeftCurly">
            <property name="tokens"
                value="CTOR_DEF,INTERFACE_DEF,LITERAL_CATCH,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,METHOD_DEF" />
        </module>
        <module name="NeedBraces" />
        <module name="RightCurly" />
        <module name="DoubleCheckedLocking" />
        <module name="EmptyStatement" />
        <module name="EqualsHashCode" />
        <module name="IllegalInstantiation" />
        <module name="RedundantThrows">
            <property name="allowUnchecked" value="true" />
            <property name="allowSubclasses" value="true" />
            <property name="logLoadErrors" value="true" />
            <property name="suppressLoadErrors" value="true" />
        </module>
        <module name="SimplifyBooleanExpression" />
        <module name="SimplifyBooleanReturn" />
        <module name="InterfaceIsType" />
        <module name="ArrayTypeStyle" />
        <module name="GenericIllegalRegexp">
            <property name="severity" value="ignore" />
            <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity"
                value="inherit" />
        </module>
        <module name="TodoComment">
            <property name="severity" value="ignore" />
            <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity"
                value="inherit" />
        </module>
        <module name="UpperEll" />
        <module name="JavadocType" />
        <module name="EmptyForInitializerPad" />
        <module name="CovariantEquals" />
        <module name="DefaultComesLast" />
        <module name="DeclarationOrder" />
        <module name="ExplicitInitialization" />
        <module name="FallThrough" />
        <module name="IllegalThrows" />
        <module name="SuperClone" />
        <module name="UnnecessaryParentheses" />
        <module name="TrailingComment" />
        <module name="PackageHtml">
            <property name="severity" value="ignore" />
            <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity"
                value="inherit" />
        </module>
    </module>
    <module name="FileTabCharacter">
        <property name="severity" value="ignore" />
        <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity"
            value="inherit" />
    </module>
    <module name="NewlineAtEndOfFile">
        <property name="severity" value="ignore" />
        <metadata name="net.sf.eclipsecs.core.lastEnabledSeverity"
            value="inherit" />
    </module>
    <module name="Translation" />
</module>
distrib/gitblit.properties
@@ -7,7 +7,7 @@
# Base folder for repositories
# Use forward slashes even on Windows!!
git.repositoriesFolder = c:/git
git.repositoriesFolder = git
# Export all repositories
# if false, each exported repository must have a .git/git-daemon-export-ok file
docs/00_index.mkd
@@ -104,10 +104,14 @@
### Other Build Dependencies
- [Fancybox image viewer](http://fancybox.net) (MIT and GPL dual-licensed)
- [JSch - Java Secure Channel](http://www.jcraft.com/jsch) (BSD)
- [JUnit](http://junit.org) (Common Public License)
## Building
[Eclipse](http://eclipse.org) is recommended for development as the project settings are preconfigured.
Additionally, [Google CodePro AnalytiX](http://code.google.com/javadevtools), [eclipse-cs](http://eclipse-cs.sourceforge.net), and [FindBugs](http://findbugs.sourceforge.net) are recommended development tools.
1. Clone the git repository from [Github][gitbltsrc].
2. Import the gitblit project into your Eclipse workspace.<br/>
*There will be lots of build errors.*
docs/page_footer.html
@@ -1,6 +1,7 @@
    </div>
    <div style="margin-top:10px" class="page_footer">
        <div style="float:right;">{0}</div>
    The content of this page is licensed under the <a href="http://creativecommons.org/licenses/by/3.0">Creative Commons Attribution 3.0 License</a>.
    </div>
</body>
</html>
src/com/gitblit/Build.java
@@ -19,6 +19,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
@@ -80,6 +81,9 @@
        downloadFromApache(MavenObject.BOUNCYCASTLE, BuildType.COMPILETIME);
        downloadFromApache(MavenObject.BOUNCYCASTLE_MAIL, BuildType.COMPILETIME);
        
        downloadFromApache(MavenObject.JSCH, BuildType.RUNTIME);
        downloadFromApache(MavenObject.JSCH, BuildType.COMPILETIME);
        downloadFromEclipse(MavenObject.JGIT, BuildType.COMPILETIME);
        downloadFromEclipse(MavenObject.JGIT_HTTP, BuildType.COMPILETIME);
    }
@@ -87,10 +91,20 @@
    public static void buildSettingKeys() {
        // Load all keys
        Properties properties = new Properties();
        FileInputStream is = null;
        try {
            properties.load(new FileInputStream(Constants.PROPERTIES_FILE));
            is = new FileInputStream(Constants.PROPERTIES_FILE);
            properties.load(is);
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (Throwable t) {
                    // IGNORE
                }
            }
        }
        List<String> keys = new ArrayList<String>(properties.stringPropertyNames());
        Collections.sort(keys);
@@ -114,13 +128,13 @@
        // Assemble Keys source file
        StringBuilder sb = new StringBuilder();
        sb.append("package com.gitblit;\n");
        sb.append("\n");
        sb.append('\n');
        sb.append("/*\n");
        sb.append(" * This class is auto-generated from the properties file.\n");
        sb.append(" * Do not version control!\n");
        sb.append(" */\n");
        sb.append("public final class Keys {\n");
        sb.append("\n");
        sb.append('\n');
        List<String> classSet = new ArrayList<String>(staticClasses.keySet());
        Collections.sort(classSet);
        for (String clazz : classSet) {
@@ -128,27 +142,31 @@
            if (clazz.equals("")) {
                // root keys
                for (String key : keySet) {
                    sb.append(MessageFormat.format("\tpublic static final String {0} = \"{1}\";\n\n", key.replace('.', '_'), key));
                    sb.append(MessageFormat.format(
                            "\tpublic static final String {0} = \"{1}\";\n\n",
                            key.replace('.', '_'), key));
                }
            } else {
                // class keys
                sb.append(MessageFormat.format("\tpublic static final class {0} '{'\n\n", clazz));
                sb.append(MessageFormat.format("\t\tpublic static final String _ROOT = \"{0}\";\n\n", clazz));
                sb.append(MessageFormat.format(
                        "\t\tpublic static final String _ROOT = \"{0}\";\n\n", clazz));
                for (String key : keySet) {
                    sb.append(MessageFormat.format("\t\tpublic static final String {0} = \"{1}\";\n\n", key.replace('.', '_'), clazz + "." + key));
                    sb.append(MessageFormat.format(
                            "\t\tpublic static final String {0} = \"{1}\";\n\n",
                            key.replace('.', '_'), clazz + "." + key));
                }
                sb.append("\t}\n\n");
            }
        }
        sb.append("}");
        sb.append('}');
        // Save Keys class definition
        try {
            File file = new File("src/com/gitblit/Keys.java");
            file.delete();
            RandomAccessFile raf = new RandomAccessFile(file, "rw");
            raf.writeBytes(sb.toString());
            raf.close();
            FileWriter fw = new FileWriter(file, false);
            fw.write(sb.toString());
            fw.close();
        } catch (Throwable t) {
            t.printStackTrace();
        }
@@ -217,7 +235,7 @@
                byte[] buffer = new byte[4096];
                int downloadedLen = 0;
                float lastProgress = 0f;
                updateDownload(0, targetFile);
                while (true) {
                    int len = in.read(buffer);
@@ -234,7 +252,7 @@
                }
                in.close();
                updateDownload(1f, targetFile);
            } catch (IOException e) {
                throw new RuntimeException("Error downloading " + mavenURL + " to " + targetFile, e);
            }
@@ -263,7 +281,7 @@
        }
        return downloads;
    }
    private static void updateDownload(float progress, File file) {
        updateProgress(progress, "d/l: " + file.getName());
    }
@@ -273,72 +291,140 @@
        int width = Math.round(anim.length() * progress);
        System.out.print("\r[");
        System.out.print(anim.substring(0, width));
        for (int i = 0; (i < anim.length() - width); i++) {
            System.out.print(" ");
        for (int i = 0; i < anim.length() - width; i++) {
            System.out.print(' ');
        }
        System.out.print("] " + url);
    }
    private static class MavenObject {
        public static final MavenObject JCOMMANDER = new MavenObject("jCommander", "com/beust", "jcommander", "1.17", 34000, 32000, 141000, "219a3540f3b27d7cc3b1d91d6ea046cd8723290e", "0bb50eec177acf0e94d58e0cf07262fe5164331d", "c7adc475ca40c288c93054e0f4fe58f3a98c0cb5");
        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.4.1.v20110513", 1500000, 1000000, 4100000, "1e2de9ed25a7c6ae38717d5ffdc7cfcd6be4bd46", "7b6279d16ce8f663537d9faf55ea353e748dbbaa", "fa06212e751296f1a7abc15c843b135bf49a112b");
        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);
        public static final MavenObject SERVLET = new MavenObject("Servlet 2.5", "javax/servlet",
                "servlet-api", "2.5", 105000, 158000, 0,
                "5959582d97d8b61f4d154ca9e495aafd16726e34",
                "021599814ad9a605b86f3e6381571beccd861a32", null);
        public static final MavenObject SLF4JAPI = new MavenObject("SLF4J API", "org/slf4j", "slf4j-api", "1.6.1", 25500, 45000, 182000, "6f3b8a24bf970f17289b234284c94f43eb42f0e4", "46a386136c901748e6a3af67ebde6c22bc6b4524", "e223571d77769cdafde59040da235842f3326453");
        public static final MavenObject SLF4JAPI = new MavenObject("SLF4J API", "org/slf4j",
                "slf4j-api", "1.6.1", 25500, 45000, 182000,
                "6f3b8a24bf970f17289b234284c94f43eb42f0e4",
                "46a386136c901748e6a3af67ebde6c22bc6b4524",
                "e223571d77769cdafde59040da235842f3326453");
        public static final MavenObject SLF4LOG4J = new MavenObject("SLF4J LOG4J", "org/slf4j", "slf4j-log4j12", "1.6.1", 9800, 9500, 52400, "bd245d6746cdd4e6203e976e21d597a46f115802", "7a26b08b265f55622fa1fed3bda68bbd37a465ba", "6e4b16bce7994e3692e82002f322a0dd2f32741e");
        public static final MavenObject SLF4LOG4J = new MavenObject("SLF4J LOG4J", "org/slf4j",
                "slf4j-log4j12", "1.6.1", 9800, 9500, 52400,
                "bd245d6746cdd4e6203e976e21d597a46f115802",
                "7a26b08b265f55622fa1fed3bda68bbd37a465ba",
                "6e4b16bce7994e3692e82002f322a0dd2f32741e");
        public static final MavenObject LOG4J = new MavenObject("Apache LOG4J", "log4j", "log4j", "1.2.16", 481000, 471000, 1455000, "7999a63bfccbc7c247a9aea10d83d4272bd492c6", "bf945d1dc995be7fe64923625f842fbb6bf443be", "78aa1cbf0fa3b259abdc7d87f9f6788d785aac2a");
        public static final MavenObject LOG4J = new MavenObject("Apache LOG4J", "log4j", "log4j",
                "1.2.16", 481000, 471000, 1455000, "7999a63bfccbc7c247a9aea10d83d4272bd492c6",
                "bf945d1dc995be7fe64923625f842fbb6bf443be",
                "78aa1cbf0fa3b259abdc7d87f9f6788d785aac2a");
        public static final MavenObject WICKET = new MavenObject("Apache Wicket", "org/apache/wicket", "wicket", "1.4.17", 1960000, 1906000, 6818000, "39815e37a6f56465b2d2c3d3017c4f3bf17db50a", "a792ebae4123253ffd039c3be49e773f8622f94e", "f2f244ca72d10081529b017e89d6276eab62c621");
        public static final MavenObject WICKET = new MavenObject("Apache Wicket",
                "org/apache/wicket", "wicket", "1.4.17", 1960000, 1906000, 6818000,
                "39815e37a6f56465b2d2c3d3017c4f3bf17db50a",
                "a792ebae4123253ffd039c3be49e773f8622f94e",
                "f2f244ca72d10081529b017e89d6276eab62c621");
        public static final MavenObject WICKET_EXT = new MavenObject("Apache Wicket Extensions", "org/apache/wicket", "wicket-extensions", "1.4.17", 1180000, 1118000, 1458000, "01111d0dbffdc425581b006a43864c22797ce72a", "f194f40ea6e361bb745dfa22e2f9171eb63a9355", "bd42e5ba9444a426bb2d7cacce91c6033b663b57");
        public static final MavenObject WICKET_EXT = new MavenObject("Apache Wicket Extensions",
                "org/apache/wicket", "wicket-extensions", "1.4.17", 1180000, 1118000, 1458000,
                "01111d0dbffdc425581b006a43864c22797ce72a",
                "f194f40ea6e361bb745dfa22e2f9171eb63a9355",
                "bd42e5ba9444a426bb2d7cacce91c6033b663b57");
        public static final MavenObject WICKET_AUTH_ROLES = new MavenObject("Apache Wicket Auth Roles", "org/apache/wicket", "wicket-auth-roles", "1.4.17", 44000, 45000, 166000, "86d20ff32f62d3026213ff11a78555da643bc676", "37e815350a2d6b97734b250a8a03d8bf3712bba7", "ac3896368bfb372d178041a4ac3ee2c44f62e21c");
        public static final MavenObject WICKET_AUTH_ROLES = new MavenObject(
                "Apache Wicket Auth Roles", "org/apache/wicket", "wicket-auth-roles", "1.4.17",
                44000, 45000, 166000, "86d20ff32f62d3026213ff11a78555da643bc676",
                "37e815350a2d6b97734b250a8a03d8bf3712bba7",
                "ac3896368bfb372d178041a4ac3ee2c44f62e21c");
        public static final MavenObject WICKET_GOOGLE_CHARTS = new MavenObject("Apache Wicket Google Charts Add-On", "org/wicketstuff", "googlecharts", "1.4.17", 34000, 18750, 161000, "c567b98b0c5efe4147e77ef2d0d3c2d45c49dea5", "3d32d958b2f7aa58388af5701ea3aafc433e573f", "c37518b67ea85af485dd61fe854137eeacc50318");
        public static final MavenObject WICKET_GOOGLE_CHARTS = new MavenObject(
                "Apache Wicket Google Charts Add-On", "org/wicketstuff", "googlecharts", "1.4.17",
                34000, 18750, 161000, "c567b98b0c5efe4147e77ef2d0d3c2d45c49dea5",
                "3d32d958b2f7aa58388af5701ea3aafc433e573f",
                "c37518b67ea85af485dd61fe854137eeacc50318");
        public static final MavenObject JUNIT = new MavenObject("JUnit", "junit", "junit", "3.8.2", 120000, 0, 0, "07e4cde26b53a9a0e3fe5b00d1dbbc7cc1d46060", "", "");
        public static final MavenObject JUNIT = new MavenObject("JUnit", "junit", "junit", "3.8.2",
                120000, 0, 0, "07e4cde26b53a9a0e3fe5b00d1dbbc7cc1d46060", "", "");
        public static final MavenObject MARKDOWNPAPERS = new MavenObject("MarkdownPapers", "org/tautua/markdownpapers", "markdownpapers-core", "1.0.0", 87000, 58000, 278000, "feda63bd149f3315da210e397d45d02277038ad5", "a9a6c4d163af81e265a15138fcaeafa9829c6054", "f932656266a7f9593488d3f89e815d0af44d0853");
        public static final MavenObject MARKDOWNPAPERS = new MavenObject("MarkdownPapers",
                "org/tautua/markdownpapers", "markdownpapers-core", "1.0.0", 87000, 58000, 278000,
                "feda63bd149f3315da210e397d45d02277038ad5",
                "a9a6c4d163af81e265a15138fcaeafa9829c6054",
                "f932656266a7f9593488d3f89e815d0af44d0853");
        public static final MavenObject BOUNCYCASTLE = new MavenObject("BouncyCastle",
                "org/bouncycastle", "bcprov-jdk16", "1.46", 1900000, 1400000, 4670000,
                "ce091790943599535cbb4de8ede84535b0c1260c",
                "d2b70567594225923450d7e3f80cd022c852725e",
                "873a6fe765f33fc27df498a5d1f5bf077e503b2f");
        public static final MavenObject BOUNCYCASTLE_MAIL = new MavenObject("BouncyCastle Mail",
                "org/bouncycastle", "bcmail-jdk16", "1.46", 502000, 420000, 482000,
                "08a9233bfd6ad38ea32df5e6ff91035b650584b9",
                "3ebd62bc56854767512dc5deec0a17795f2e671d",
                "3b7c5f3938f202311bdca0bf7ed46bc0118af081");
        public static final MavenObject JGIT = new MavenObject("JGit", "org/eclipse/jgit",
                "org.eclipse.jgit", "0.12.1", 1318000, 1354000, 2993000,
                "fd77699699b9651d2fc31c7ed63af98b14fc1975",
                "c8b3d84922c7802cfe6a661e13a002641a78583d",
                "5609aa3ce3ac3d52030befd27ddd2941f6c07570");
        public static final MavenObject JGIT_HTTP = new MavenObject("JGit", "org/eclipse/jgit",
                "org.eclipse.jgit.http.server", "0.12.1", 68000, 62000, 99000,
                "384058ca906dffb8b8708c2db8849c9754359b28",
                "7b026658ed8de2eccc2d11d647d43d7c84a56911",
                "6c19e37b3caafd70c1b7b024ae1858c725181688");
        
        public static final MavenObject BOUNCYCASTLE = new MavenObject("BouncyCastle", "org/bouncycastle", "bcprov-jdk16", "1.46", 1900000, 1400000, 4670000, "ce091790943599535cbb4de8ede84535b0c1260c", "d2b70567594225923450d7e3f80cd022c852725e", "873a6fe765f33fc27df498a5d1f5bf077e503b2f");
        public static final MavenObject BOUNCYCASTLE_MAIL = new MavenObject("BouncyCastle Mail", "org/bouncycastle", "bcmail-jdk16", "1.46", 502000, 420000, 482000, "08a9233bfd6ad38ea32df5e6ff91035b650584b9", "3ebd62bc56854767512dc5deec0a17795f2e671d", "3b7c5f3938f202311bdca0bf7ed46bc0118af081");
        public static final MavenObject JGIT = new MavenObject("JGit", "org/eclipse/jgit", "org.eclipse.jgit", "0.12.1", 1318000, 1354000, 2993000, "fd77699699b9651d2fc31c7ed63af98b14fc1975", "c8b3d84922c7802cfe6a661e13a002641a78583d", "5609aa3ce3ac3d52030befd27ddd2941f6c07570");
        public static final MavenObject JGIT_HTTP = new MavenObject("JGit", "org/eclipse/jgit", "org.eclipse.jgit.http.server", "0.12.1", 68000, 62000, 99000, "384058ca906dffb8b8708c2db8849c9754359b28", "7b026658ed8de2eccc2d11d647d43d7c84a56911", "6c19e37b3caafd70c1b7b024ae1858c725181688");
        public static final MavenObject JSCH = new MavenObject("JSch", "com/jcraft",
                "jsch", "0.1.44-1", 214000, 211000, 413000,
                "2e9ae08de5a71bd0e0d3ba2558598181bfa71d4e",
                "e528f593b19b04d500992606f58b87fcfded8883",
                "d0ffadd0a4ab909d94a577b5aad43c13b617ddcb");
        public final String name;
        public final String group;
        public final String artifact;
        public final String version;
        public final int len_library;
        public final int len_sources;
        public final int len_javadoc;
        public final String sha1_library;
        public final String sha1_sources;
        public final String sha1_javadoc;
        public final int approxLibraryLen;
        public final int approxSourcesLen;
        public final int approxJavadocLen;
        public final String librarySHA1;
        public final String sourcesSHA1;
        public final String javadocSHA1;
        private MavenObject(String name, String group, String artifact, String version, int len_library, int len_sources, int len_javadoc, String sha1_library, String sha1_sources, String sha1_javadoc) {
        private MavenObject(String name, String group, String artifact, String version,
                int approxLibraryLen, int approxSourcesLen, int approxJavadocLen,
                String librarySHA1, String sourcesSHA1, String javadocSHA1) {
            this.name = name;
            this.group = group;
            this.artifact = artifact;
            this.version = version;
            this.len_library = len_library;
            this.len_sources = len_sources;
            this.len_javadoc = len_javadoc;
            this.sha1_library = sha1_library;
            this.sha1_sources = sha1_sources;
            this.sha1_javadoc = sha1_javadoc;
            this.approxLibraryLen = approxLibraryLen;
            this.approxSourcesLen = approxSourcesLen;
            this.approxJavadocLen = approxJavadocLen;
            this.librarySHA1 = librarySHA1;
            this.sourcesSHA1 = sourcesSHA1;
            this.javadocSHA1 = javadocSHA1;
        }
        private String getRepositoryPath(String jar) {
            return group + "/" + artifact + "/" + version + "/" + artifact + "-" + version + jar + ".jar";
            return group + "/" + artifact + "/" + version + "/" + artifact + "-" + version + jar
                    + ".jar";
        }
        private File getLocalFile(String basePath, String jar) {
@@ -347,24 +433,24 @@
        private String getSHA1(String jar) {
            if (jar.equals("")) {
                return sha1_library;
                return librarySHA1;
            } else if (jar.equals("-sources")) {
                return sha1_sources;
                return sourcesSHA1;
            } else if (jar.equals("-javadoc")) {
                return sha1_javadoc;
                return javadocSHA1;
            }
            return sha1_library;
            return librarySHA1;
        }
        private int getApproximateLength(String jar) {
            if (jar.equals("")) {
                return len_library;
                return approxLibraryLen;
            } else if (jar.equals("-sources")) {
                return len_sources;
                return approxSourcesLen;
            } else if (jar.equals("-javadoc")) {
                return len_javadoc;
                return approxJavadocLen;
            }
            return len_library;
            return approxLibraryLen;
        }
        @Override
src/com/gitblit/BuildSite.java
@@ -66,7 +66,8 @@
            aliasMap.put(values[0], values[1]);
        }
        System.out.println(MessageFormat.format("Generating site from {0} Markdown Docs in {1} ", markdownFiles.length, sourceFolder.getAbsolutePath()));
        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();
        for (File file : markdownFiles) {
@@ -82,24 +83,26 @@
        sb.setLength(sb.length() - 3);
        sb.trimToSize();
        String html_header = readContent(new File(params.pageHeader));
        String html_footer = readContent(new File(params.pageFooter));
        String htmlHeader = readContent(new File(params.pageHeader));
        String htmlFooter = readContent(new File(params.pageFooter));
        final String links = sb.toString();
        final String header = MessageFormat.format(html_header, Constants.FULL_NAME, links);
        final String header = MessageFormat.format(htmlHeader, Constants.FULL_NAME, links);
        final String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        final String footer = MessageFormat.format(html_footer, "generated " + date);
        final String footer = MessageFormat.format(htmlFooter, "generated " + date);
        for (File file : markdownFiles) {
            try {
                String documentName = getDocumentName(file);
                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"));
                InputStreamReader reader = new InputStreamReader(new FileInputStream(file),
                        Charset.forName("UTF-8"));
                String content = MarkdownUtils.transformMarkdown(reader);
                for (String token : params.substitutions) {
                    String [] kv = token.split("=");
                    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"));
                OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(new File(
                        destinationFolder, fileName)), Charset.forName("UTF-8"));
                writer.write(header);
                writer.write(content);
                writer.write(footer);
@@ -115,7 +118,8 @@
    private static String readContent(File file) {
        StringBuilder sb = new StringBuilder();
        try {
            InputStreamReader is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"));
            InputStreamReader is = new InputStreamReader(new FileInputStream(file),
                    Charset.forName("UTF-8"));
            BufferedReader reader = new BufferedReader(is);
            String line = null;
            while ((line = reader.readLine()) != null) {
@@ -130,13 +134,14 @@
    }
    private static String getDocumentName(File file) {
        String displayName = file.getName().substring(0, file.getName().lastIndexOf('.')).toLowerCase();
        String displayName = file.getName().substring(0, file.getName().lastIndexOf('.'))
                .toLowerCase();
        // 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(Constants.getGitBlitVersion());
        System.out.println();
        if (t != null) {
            System.out.println(t.getMessage());
src/com/gitblit/Constants.java
@@ -17,25 +17,25 @@
public class Constants {
    public final static String NAME = "Gitblit";
    public final static String FULL_NAME = "Gitblit - a pure Java Git solution";
    public static final String NAME = "Gitblit";
    // 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 VERSION = "0.1.0-SNAPSHOT";
    public static final String FULL_NAME = "Gitblit - a pure Java Git solution";
    // 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 static final String VERSION = "0.1.0-SNAPSHOT";
    public final static String ADMIN_ROLE = "#admin";
    // 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 static final String JGIT_VERSION = "JGit 0.12.1";
    public final static String PROPERTIES_FILE = "gitblit.properties";
    public final static String GIT_SERVLET_PATH = "/git/";
    public final static String ZIP_SERVLET_PATH = "/zip/";
    public static final String ADMIN_ROLE = "#admin";
    public static final String PROPERTIES_FILE = "gitblit.properties";
    public static final String GIT_SERVLET_PATH = "/git/";
    public static final String ZIP_SERVLET_PATH = "/zip/";
    public static enum AccessRestrictionType {
        NONE, PUSH, CLONE, VIEW;
@@ -64,13 +64,5 @@
    public static String getGitBlitVersion() {
        return NAME + " v" + VERSION;
    }
    public static String getJGitVersion() {
        return JGIT_VERSION;
    }
    public static String getRunningVersion() {
        return getGitBlitVersion();
    }
}
src/com/gitblit/DownloadZipServlet.java
@@ -32,30 +32,24 @@
public class DownloadZipServlet extends HttpServlet {
    public static String asLink(String baseURL, String repository, String objectId, String path) {
        return baseURL + (baseURL.endsWith("/") ? "" : "/") + "zip?r=" + repository + (path == null ? "" : ("&p=" + path)) + (objectId == null ? "" : ("&h=" + objectId));
    }
    private static final long serialVersionUID = 1L;
    private final static Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);
    private transient Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);
    public DownloadZipServlet() {
        super();
    }
    @Override
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
        processRequest(request, response);
    public static String asLink(String baseURL, String repository, String objectId, String path) {
        return baseURL + (baseURL.endsWith("/") ? "" : "/") + "zip?r=" + repository
                + (path == null ? "" : ("&p=" + path))
                + (objectId == null ? "" : ("&h=" + objectId));
    }
    @Override
    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
        processRequest(request, response);
    }
    private void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
        if (!GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)) {
    private void processRequest(javax.servlet.http.HttpServletRequest request,
            javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
            java.io.IOException {
        if (!GitBlit.getBoolean(Keys.web.allowZipDownloads, true)) {
            logger.warn("Zip downloads are disabled");
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
@@ -96,7 +90,8 @@
            String contentType = "application/octet-stream";
            response.setContentType(contentType + "; charset=" + response.getCharacterEncoding());
            // response.setContentLength(attachment.getFileSize());
            response.setHeader("Content-Disposition", "attachment; filename=\"" + name + ".zip" + "\"");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + name + ".zip"
                    + "\"");
            response.setDateHeader("Last-Modified", date.getTime());
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Pragma", "no-cache");
@@ -112,4 +107,18 @@
            logger.error("Failed to write attachment to client", t);
        }
    }
    @Override
    protected void doPost(javax.servlet.http.HttpServletRequest request,
            javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
            java.io.IOException {
        processRequest(request, response);
    }
    @Override
    protected void doGet(javax.servlet.http.HttpServletRequest request,
            javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
            java.io.IOException {
        processRequest(request, response);
    }
}
src/com/gitblit/FileSettings.java
@@ -21,6 +21,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.regex.PatternSyntaxException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,11 +32,11 @@
 */
public class FileSettings implements IStoredSettings {
    private final Logger logger = LoggerFactory.getLogger(FileSettings.class);
    private Properties properties = new Properties();
    private long lastread = 0;
    private final Logger logger = LoggerFactory.getLogger(FileSettings.class);
    private long lastread;
    @Override
    public List<String> getAllKeys(String startingWith) {
@@ -131,7 +132,8 @@
                    strings.add(chunk);
                }
            }
        } catch (Exception e) {
        } catch (PatternSyntaxException e) {
            logger.error("Failed to parse " + value, e);
        }
        return strings;
    }
@@ -139,18 +141,29 @@
    private synchronized Properties read() {
        File file = new File(Constants.PROPERTIES_FILE);
        if (file.exists() && (file.lastModified() > lastread)) {
            FileInputStream is = null;
            try {
                properties = new Properties();
                properties.load(new FileInputStream(Constants.PROPERTIES_FILE));
                is = new FileInputStream(Constants.PROPERTIES_FILE);
                properties.load(is);
                lastread = file.lastModified();
            } catch (FileNotFoundException f) {
                // IGNORE - won't happen because file.exists() check above
            } catch (Throwable t) {
                t.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (Throwable t) {
                        // IGNORE
                    }
                }
            }
        }
        return properties;
    }
    @Override
    public String toString() {
        return new File(Constants.PROPERTIES_FILE).getAbsolutePath();
src/com/gitblit/GitBlit.java
@@ -42,7 +42,7 @@
public class GitBlit implements ServletContextListener {
    private final static GitBlit gitblit;
    private static final GitBlit GITBLIT;
    private final Logger logger = LoggerFactory.getLogger(GitBlit.class);
@@ -57,18 +57,34 @@
    private IStoredSettings storedSettings;
    static {
        gitblit = new GitBlit();
    }
    public static GitBlit self() {
        return gitblit;
        GITBLIT = new GitBlit();
    }
    private GitBlit() {
    }
    public IStoredSettings settings() {
        return storedSettings;
    public static GitBlit self() {
        return GITBLIT;
    }
    public static boolean getBoolean(String key, boolean defaultValue) {
        return GITBLIT.storedSettings.getBoolean(key, defaultValue);
    }
    public static int getInteger(String key, int defaultValue) {
        return GITBLIT.storedSettings.getInteger(key, defaultValue);
    }
    public static String getString(String key, String defaultValue) {
        return GITBLIT.storedSettings.getString(key, defaultValue);
    }
    public static List<String> getStrings(String key) {
        return GITBLIT.storedSettings.getStrings(key);
    }
    public static List<String> getAllKeys(String startingWith) {
        return GITBLIT.storedSettings.getAllKeys(startingWith);
    }
    public boolean isDebugMode() {
@@ -117,14 +133,16 @@
        return loginService.setUsernamesForRole(repository.name, repositoryUsers);
    }
    public void editUserModel(String username, UserModel user, boolean isCreate) throws GitBlitException {
    public void editUserModel(String username, UserModel user, boolean isCreate)
            throws GitBlitException {
        if (!loginService.updateUserModel(username, user)) {
            throw new GitBlitException(isCreate ? "Failed to add user!" : "Failed to update user!");
        }
    }
    public List<String> getRepositoryList() {
        return JGitUtils.getRepositoryList(repositoriesFolder, exportAll, storedSettings.getBoolean(Keys.git.nestedRepositories, true));
        return JGitUtils.getRepositoryList(repositoriesFolder, exportAll,
                storedSettings.getBoolean(Keys.git.nestedRepositories, true));
    }
    public Repository getRepository(String repositoryName) {
@@ -133,7 +151,8 @@
            r = repositoryResolver.open(null, repositoryName);
        } catch (RepositoryNotFoundException e) {
            r = null;
            logger.error("GitBlit.getRepository(String) failed to find repository " + repositoryName);
            logger.error("GitBlit.getRepository(String) failed to find repository "
                    + repositoryName);
        } catch (ServiceNotEnabledException e) {
            r = null;
            e.printStackTrace();
@@ -177,7 +196,8 @@
            model.owner = getConfig(config, "owner", "");
            model.useTickets = getConfig(config, "useTickets", false);
            model.useDocs = getConfig(config, "useDocs", false);
            model.accessRestriction = AccessRestrictionType.fromName(getConfig(config, "accessRestriction", null));
            model.accessRestriction = AccessRestrictionType.fromName(getConfig(config,
                    "accessRestriction", null));
            model.showRemoteBranches = getConfig(config, "showRemoteBranches", false);
            model.isFrozen = getConfig(config, "isFrozen", false);
        }
@@ -197,11 +217,14 @@
        return config.getBoolean("gitblit", field, defaultValue);
    }
    public void editRepositoryModel(String repositoryName, RepositoryModel repository, boolean isCreate) throws GitBlitException {
    public void editRepositoryModel(String repositoryName, RepositoryModel repository,
            boolean isCreate) throws GitBlitException {
        Repository r = null;
        if (isCreate) {
            if (new File(repositoriesFolder, repository.name).exists()) {
                throw new GitBlitException(MessageFormat.format("Can not create repository ''{0}'' because it already exists.", repository.name));
                throw new GitBlitException(MessageFormat.format(
                        "Can not create repository ''{0}'' because it already exists.",
                        repository.name));
            }
            // create repository
            logger.info("create repository " + repository.name);
@@ -212,14 +235,21 @@
                File folder = new File(repositoriesFolder, repositoryName);
                File destFolder = new File(repositoriesFolder, repository.name);
                if (destFolder.exists()) {
                    throw new GitBlitException(MessageFormat.format("Can not rename repository ''{0}'' to ''{1}'' because ''{1}'' already exists.", repositoryName, repository.name));
                    throw new GitBlitException(
                            MessageFormat
                                    .format("Can not rename repository ''{0}'' to ''{1}'' because ''{1}'' already exists.",
                                            repositoryName, repository.name));
                }
                if (!folder.renameTo(destFolder)) {
                    throw new GitBlitException(MessageFormat.format("Failed to rename repository ''{0}'' to ''{1}''.", repositoryName, repository.name));
                    throw new GitBlitException(MessageFormat.format(
                            "Failed to rename repository ''{0}'' to ''{1}''.", repositoryName,
                            repository.name));
                }
                // rename the roles
                if (!loginService.renameRole(repositoryName, repository.name)) {
                    throw new GitBlitException(MessageFormat.format("Failed to rename repository permissions ''{0}'' to ''{1}''.", repositoryName, repository.name));
                    throw new GitBlitException(MessageFormat.format(
                            "Failed to rename repository permissions ''{0}'' to ''{1}''.",
                            repositoryName, repository.name));
                }
            }
@@ -235,20 +265,23 @@
        }
        // update settings
        StoredConfig config = JGitUtils.readConfig(r);
        config.setString("gitblit", null, "description", repository.description);
        config.setString("gitblit", null, "owner", repository.owner);
        config.setBoolean("gitblit", null, "useTickets", repository.useTickets);
        config.setBoolean("gitblit", null, "useDocs", repository.useDocs);
        config.setString("gitblit", null, "accessRestriction", repository.accessRestriction.name());
        config.setBoolean("gitblit", null, "showRemoteBranches", repository.showRemoteBranches);
        config.setBoolean("gitblit", null, "isFrozen", repository.isFrozen);
        try {
            config.save();
        } catch (IOException e) {
            logger.error("Failed to save repository config!", e);
        if (r != null) {
            StoredConfig config = JGitUtils.readConfig(r);
            config.setString("gitblit", null, "description", repository.description);
            config.setString("gitblit", null, "owner", repository.owner);
            config.setBoolean("gitblit", null, "useTickets", repository.useTickets);
            config.setBoolean("gitblit", null, "useDocs", repository.useDocs);
            config.setString("gitblit", null, "accessRestriction",
                    repository.accessRestriction.name());
            config.setBoolean("gitblit", null, "showRemoteBranches", repository.showRemoteBranches);
            config.setBoolean("gitblit", null, "isFrozen", repository.isFrozen);
            try {
                config.save();
            } catch (IOException e) {
                logger.error("Failed to save repository config!", e);
            }
            r.close();
        }
        r.close();
    }
    public boolean deleteRepositoryModel(RepositoryModel model) {
src/com/gitblit/GitBlitServer.java
@@ -51,10 +51,10 @@
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
@@ -65,32 +65,35 @@
public class GitBlitServer {
    private final static Logger logger = Log.getLogger(GitBlitServer.class.getSimpleName());
    private final static String border_star = "***********************************************************";
    private static final String BORDER = "***********************************************************";
    private final static FileSettings fileSettings = new FileSettings();
    private static Logger logger;
    private static final FileSettings FILESETTINGS = new FileSettings();
    public static void main(String[] args) {
        Params params = new Params();
        JCommander jc = new JCommander(params);
        try {
            jc.parse(args);
            if (params.help)
            if (params.help) {
                usage(jc, null);
            }
        } catch (ParameterException t) {
            usage(jc, t);
        }
        if (params.stop)
        if (params.stop) {
            stop(params);
        else
        } else {
            start(params);
        }
    }
    private static void usage(JCommander jc, ParameterException t) {
        System.out.println(border_star);
        System.out.println(Constants.getRunningVersion());
        System.out.println(border_star);
        System.out.println(BORDER);
        System.out.println(Constants.getGitBlitVersion());
        System.out.println(BORDER);
        System.out.println();
        if (t != null) {
            System.out.println(t.getMessage());
@@ -98,7 +101,8 @@
        }
        if (jc != null) {
            jc.usage();
            System.out.println("\nExample:\n  java -server -Xmx1024M -jar gitblit.jar --repos c:\\git --port 80 --securePort 443");
            System.out
                    .println("\nExample:\n  java -server -Xmx1024M -jar gitblit.jar --repos c:\\git --port 80 --securePort 443");
        }
        System.exit(0);
    }
@@ -111,7 +115,7 @@
            Socket s = new Socket(InetAddress.getByName("127.0.0.1"), params.shutdownPort);
            OutputStream out = s.getOutputStream();
            System.out.println("Sending Shutdown Request to " + Constants.NAME);
            out.write(("\r\n").getBytes());
            out.write("\r\n".getBytes());
            out.flush();
            s.close();
        } catch (UnknownHostException e) {
@@ -125,17 +129,18 @@
     * Start Server.
     */
    private static void start(Params params) {
        String pattern = fileSettings.getString(Keys.server.log4jPattern, "%-5p %d{MM-dd HH:mm:ss.SSS}  %-20.20c{1}  %m%n");
        String pattern = FILESETTINGS.getString(Keys.server.log4jPattern,
                "%-5p %d{MM-dd HH:mm:ss.SSS}  %-20.20c{1}  %m%n");
        // allow os override of logging pattern
        String os = System.getProperty("os.name").toLowerCase();
        if (os.indexOf("windows") > -1) {
            String winPattern = fileSettings.getString(Keys.server.log4jPattern_windows, pattern);
            String winPattern = FILESETTINGS.getString(Keys.server.log4jPattern_windows, pattern);
            if (!StringUtils.isEmpty(winPattern)) {
                pattern = winPattern;
            }
        } else if (os.indexOf("linux") > -1) {
            String linuxPattern = fileSettings.getString(Keys.server.log4jPattern_linux, pattern);
            String linuxPattern = FILESETTINGS.getString(Keys.server.log4jPattern_linux, pattern);
            if (!StringUtils.isEmpty(linuxPattern)) {
                pattern = linuxPattern;
            }
@@ -145,9 +150,10 @@
        org.apache.log4j.Logger rootLogger = org.apache.log4j.Logger.getRootLogger();
        rootLogger.addAppender(new ConsoleAppender(layout));
        logger.info(border_star);
        logger.info(Constants.getRunningVersion());
        logger.info(border_star);
        logger = LoggerFactory.getLogger(GitBlitServer.class);
        logger.info(BORDER);
        logger.info(Constants.getGitBlitVersion());
        logger.info(BORDER);
        String osname = System.getProperty("os.name");
        String osversion = System.getProperty("os.version");
@@ -157,9 +163,10 @@
        List<Connector> connectors = new ArrayList<Connector>();
        if (params.port > 0) {
            Connector httpConnector = createConnector(params.useNIO, params.port);
            String bindInterface = fileSettings.getString(Keys.server.httpBindInterface, null);
            String bindInterface = FILESETTINGS.getString(Keys.server.httpBindInterface, null);
            if (!StringUtils.isEmpty(bindInterface)) {
                logger.warn(MessageFormat.format("Binding connector on port {0} to {1}", params.port, bindInterface));
                logger.warn(MessageFormat.format("Binding connector on port {0} to {1}",
                        params.port, bindInterface));
                httpConnector.setHost(bindInterface);
            }
            connectors.add(httpConnector);
@@ -169,13 +176,16 @@
            File keystore = new File("keystore");
            if (!keystore.exists()) {
                logger.info("Generating self-signed SSL certificate");
                MakeCertificate.generateSelfSignedCertificate("localhost", keystore, params.storePassword);
                MakeCertificate.generateSelfSignedCertificate("localhost", keystore,
                        params.storePassword);
            }
            if (keystore.exists()) {
                Connector secureConnector = createSSLConnector(keystore, params.storePassword, params.useNIO, params.securePort);
                String bindInterface = fileSettings.getString(Keys.server.httpsBindInterface, null);
                Connector secureConnector = createSSLConnector(keystore, params.storePassword,
                        params.useNIO, params.securePort);
                String bindInterface = FILESETTINGS.getString(Keys.server.httpsBindInterface, null);
                if (!StringUtils.isEmpty(bindInterface)) {
                    logger.warn(MessageFormat.format("Binding ssl connector on port {0} to {1}", params.securePort, bindInterface));
                    logger.warn(MessageFormat.format("Binding ssl connector on port {0} to {1}",
                            params.securePort, bindInterface));
                    secureConnector.setHost(bindInterface);
                }
                connectors.add(secureConnector);
@@ -189,9 +199,14 @@
        // * WebApp is expanded
        //
        File tempDir = new File(params.temp);
        if (tempDir.exists())
            deleteRecursively(tempDir);
        tempDir.mkdirs();
        if (tempDir.exists()) {
            if (!deleteRecursively(tempDir)) {
                logger.warn("Failed to delete temp dir " + tempDir.getAbsolutePath());
            }
        }
        if (!tempDir.mkdirs()) {
            logger.warn("Failed to create temp dir " + tempDir.getAbsolutePath());
        }
        Server server = new Server();
        server.setStopAtShutdown(true);
@@ -208,11 +223,8 @@
        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
        // Set cookies HttpOnly so they are not accessible to JavaScript engines
        HashSessionManager sessionManager = new HashSessionManager();
        sessionManager.setHttpOnly(true);
        // Use secure cookies if only serving https
@@ -222,29 +234,31 @@
        // Wicket Filter
        String wicketPathSpec = "/*";
        FilterHolder wicketFilter = new FilterHolder(WicketFilter.class);
        wicketFilter.setInitParameter(ContextParamWebApplicationFactory.APP_CLASS_PARAM, GitBlitWebApp.class.getName());
        wicketFilter.setInitParameter(ContextParamWebApplicationFactory.APP_CLASS_PARAM,
                GitBlitWebApp.class.getName());
        wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, wicketPathSpec);
        wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/");
        rootContext.addFilter(wicketFilter, wicketPathSpec, FilterMapping.DEFAULT);
        // Zip Servlet
        rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*");
        // Git Servlet
        ServletHolder gitServlet = null;
        String gitServletPathSpec = Constants.GIT_SERVLET_PATH + "*";
        if (fileSettings.getBoolean(Keys.git.enableGitServlet, true)) {
        if (FILESETTINGS.getBoolean(Keys.git.enableGitServlet, true)) {
            gitServlet = rootContext.addServlet(GitBlitServlet.class, gitServletPathSpec);
            gitServlet.setInitParameter("base-path", params.repositoriesFolder);
            gitServlet.setInitParameter("export-all", fileSettings.getBoolean(Keys.git.exportAll, true) ? "1" : "0");
            gitServlet.setInitParameter("export-all",
                    FILESETTINGS.getBoolean(Keys.git.exportAll, true) ? "1" : "0");
        }
        // Login Service
        LoginService loginService = null;
        String realmUsers = params.realmFile;
        if (!StringUtils.isEmpty(realmUsers)) {
        String realmUsers = params.realmFile;
        if (!StringUtils.isEmpty(realmUsers)) {
            File realmFile = new File(realmUsers);
            if (realmFile.exists()) {
            if (realmFile.exists()) {
                logger.info("Setting up login service from " + realmUsers);
                JettyLoginService jettyLoginService = new JettyLoginService(realmFile);
                GitBlit.self().setLoginService(jettyLoginService);
@@ -261,13 +275,13 @@
                Constraint constraint = new Constraint();
                constraint.setAuthenticate(true);
                constraint.setRoles(new String [] { "*" });
                constraint.setRoles(new String[] { "*" });
                ConstraintMapping mapping = new ConstraintMapping();
                mapping.setPathSpec(gitServletPathSpec);
                mapping.setConstraint(constraint);
                ConstraintSecurityHandler security = new ConstraintSecurityHandler();
                ConstraintSecurityHandler security = new ConstraintSecurityHandler();
                security.addConstraintMapping(mapping);
                security.setAuthenticator(new BasicAuthenticator());
                security.setLoginService(loginService);
@@ -291,7 +305,7 @@
        // Setup the GitBlit context
        GitBlit gitblit = GitBlit.self();
        gitblit.configureContext(fileSettings);
        gitblit.configureContext(FILESETTINGS);
        rootContext.addEventListener(gitblit);
        // Start the Server
@@ -327,13 +341,14 @@
        return connector;
    }
    private static Connector createSSLConnector(File keystore, String password, boolean useNIO, int port) {
    private static Connector createSSLConnector(File keystore, String password, boolean useNIO,
            int port) {
        SslConnector connector;
        if (useNIO) {
            logger.info("Setting up NIO SslSelectChannelConnector on port " + port);
            SslSelectChannelConnector ssl = new SslSelectChannelConnector();
            ssl.setSoLingerTime(-1);
            ssl.setThreadPool(new QueuedThreadPool(20));
            ssl.setThreadPool(new QueuedThreadPool(20));
            connector = ssl;
        } else {
            logger.info("Setting up NIO SslSocketConnector on port " + port);
@@ -347,20 +362,22 @@
        connector.setMaxIdleTime(30000);
        return connector;
    }
    /**
     * Recursively delete a folder and its contents.
     * 
     * @param folder
     */
    private static void deleteRecursively(File folder) {
    private static boolean deleteRecursively(File folder) {
        boolean deleted = true;
        for (File file : folder.listFiles()) {
            if (file.isDirectory())
                deleteRecursively(file);
            else
                file.delete();
            if (file.isDirectory()) {
                deleted &= deleteRecursively(file);
            } else {
                deleted &= file.delete();
            }
        }
        folder.delete();
        return deleted && folder.delete();
    }
    private static class ShutdownMonitorThread extends Thread {
@@ -377,7 +394,7 @@
            try {
                skt = new ServerSocket(params.shutdownPort, 1, InetAddress.getByName("127.0.0.1"));
            } catch (Exception e) {
                logger.warn(e);
                logger.warn("Could not open shutdown monitor on port " + params.shutdownPort, e);
            }
            socket = skt;
        }
@@ -388,11 +405,12 @@
            Socket accept;
            try {
                accept = socket.accept();
                BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream()));
                BufferedReader reader = new BufferedReader(new InputStreamReader(
                        accept.getInputStream()));
                reader.readLine();
                logger.info(border_star);
                logger.info(BORDER);
                logger.info("Stopping " + Constants.NAME);
                logger.info(border_star);
                logger.info(BORDER);
                server.stop();
                server.setStopAtShutdown(false);
                accept.close();
@@ -416,37 +434,38 @@
        public Boolean stop = false;
        @Parameter(names = { "--tempFolder" }, description = "Server temp folder")
        public String temp = fileSettings.getString(Keys.server.tempFolder, "temp");
        public String temp = FILESETTINGS.getString(Keys.server.tempFolder, "temp");
        /*
         * GIT Servlet Parameters
         */
        @Parameter(names = { "--repositoriesFolder" }, description = "Git Repositories Folder")
        public String repositoriesFolder = fileSettings.getString(Keys.git.repositoriesFolder, "repos");
        public String repositoriesFolder = FILESETTINGS.getString(Keys.git.repositoriesFolder,
                "repos");
        /*
         * Authentication Parameters
         */
        @Parameter(names = { "--realmFile" }, description = "Users Realm Hash File")
        public String realmFile = fileSettings.getString(Keys.realm.realmFile, "users.properties");
        public String realmFile = FILESETTINGS.getString(Keys.realm.realmFile, "users.properties");
        /*
         * JETTY Parameters
         */
        @Parameter(names = { "--useNio" }, description = "Use NIO Connector else use Socket Connector.")
        public Boolean useNIO = fileSettings.getBoolean(Keys.server.useNio, true);
        public Boolean useNIO = FILESETTINGS.getBoolean(Keys.server.useNio, true);
        @Parameter(names = "--httpPort", description = "HTTP port for to serve. (port <= 0 will disable this connector)")
        public Integer port = fileSettings.getInteger(Keys.server.httpPort, 80);
        public Integer port = FILESETTINGS.getInteger(Keys.server.httpPort, 80);
        @Parameter(names = "--httpsPort", description = "HTTPS port to serve.  (port <= 0 will disable this connector)")
        public Integer securePort = fileSettings.getInteger(Keys.server.httpsPort, 443);
        public Integer securePort = FILESETTINGS.getInteger(Keys.server.httpsPort, 443);
        @Parameter(names = "--storePassword", description = "Password for SSL (https) keystore.")
        public String storePassword = fileSettings.getString(Keys.server.storePassword, "");
        public String storePassword = FILESETTINGS.getString(Keys.server.storePassword, "");
        @Parameter(names = "--shutdownPort", description = "Port for Shutdown Monitor to listen on. (port <= 0 will disable this monitor)")
        public Integer shutdownPort = fileSettings.getInteger(Keys.server.shutdownPort, 8081);
        public Integer shutdownPort = FILESETTINGS.getInteger(Keys.server.shutdownPort, 8081);
    }
}
src/com/gitblit/GitBlitServlet.java
@@ -33,14 +33,15 @@
    private static final long serialVersionUID = 1L;
    private final Logger logger = LoggerFactory.getLogger(GitBlitServlet.class);
    private transient Logger logger = LoggerFactory.getLogger(GitBlitServlet.class);
    public GitBlitServlet() {
        super();
    }
    @Override
    protected void service(final HttpServletRequest req, final HttpServletResponse rsp) throws ServletException, IOException {
    protected void service(final HttpServletRequest req, final HttpServletResponse rsp)
            throws ServletException, IOException {
        // admins have full git access to all repositories
        if (req.isUserInRole(Constants.ADMIN_ROLE)) {
            // admins can do whatever
@@ -57,12 +58,13 @@
        if (forwardSlash > -1) {
            String repository = url.substring(0, forwardSlash);
            String function = url.substring(forwardSlash + 1);
            String query = req.getQueryString() == null ? "":req.getQueryString();
            String query = req.getQueryString() == null ? "" : req.getQueryString();
            RepositoryModel model = GitBlit.self().getRepositoryModel(repository);
            if (model != null) {
            if (model != null) {
                if (model.isFrozen || model.accessRestriction.atLeast(AccessRestrictionType.PUSH)) {
                    boolean authorizedUser = req.isUserInRole(repository);
                    if (function.startsWith("git-receive-pack") || (query.indexOf("service=git-receive-pack") > -1)) {
                    if (function.startsWith("git-receive-pack")
                            || (query.indexOf("service=git-receive-pack") > -1)) {
                        // Push request
                        if (!model.isFrozen && authorizedUser) {
                            // clone-restricted or push-authorized
@@ -70,21 +72,29 @@
                            return;
                        } else {
                            // user is unauthorized to push to this repository
                            logger.warn(MessageFormat.format("user {0} is not authorized to push to {1} ", req.getUserPrincipal().getName(), repository));
                            rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format("you are not authorized to push to {0} ", repository));
                            logger.warn(MessageFormat.format(
                                    "user {0} is not authorized to push to {1}", req
                                            .getUserPrincipal().getName(), repository));
                            rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format(
                                    "you are not authorized to push to {0}", repository));
                            return;
                        }
                    } else if (function.startsWith("git-upload-pack") || (query.indexOf("service=git-upload-pack") > -1)) {
                    } else if (function.startsWith("git-upload-pack")
                            || (query.indexOf("service=git-upload-pack") > -1)) {
                        // Clone request
                        boolean cloneRestricted = model.accessRestriction.atLeast(AccessRestrictionType.CLONE);
                        boolean cloneRestricted = model.accessRestriction
                                .atLeast(AccessRestrictionType.CLONE);
                        if (!cloneRestricted || (cloneRestricted && authorizedUser)) {
                            // push-restricted or clone-authorized
                            super.service(req, rsp);
                            return;
                        } else {
                            // user is unauthorized to clone this repository
                            logger.warn(MessageFormat.format("user {0} is not authorized to clone {1} ", req.getUserPrincipal().getName(), repository));
                            rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format("you are not authorized to clone {0} ", repository));
                            logger.warn(MessageFormat.format(
                                    "user {0} is not authorized to clone {1}", req
                                            .getUserPrincipal().getName(), repository));
                            rsp.sendError(HttpServletResponse.SC_FORBIDDEN, MessageFormat.format(
                                    "you are not authorized to clone {0}", repository));
                            return;
                        }
                    }
src/com/gitblit/ILoginService.java
@@ -24,22 +24,22 @@
    UserModel authenticate(String username, char[] password);
    UserModel getUserModel(String username);
    boolean updateUserModel(UserModel model);
    boolean updateUserModel(String username, UserModel model);
    boolean deleteUserModel(UserModel model);
    boolean deleteUser(String username);
    List<String> getAllUsernames();
    List<String> getUsernamesForRole(String role);
    boolean setUsernamesForRole(String role, List<String> usernames);
    boolean renameRole(String oldRole, String newRole);
    boolean deleteRole(String role);
}
src/com/gitblit/IStoredSettings.java
@@ -19,20 +19,20 @@
public interface IStoredSettings {
    public abstract List<String> getAllKeys(String startingWith);
    List<String> getAllKeys(String startingWith);
    public abstract boolean getBoolean(String name, boolean defaultValue);
    boolean getBoolean(String name, boolean defaultValue);
    public abstract int getInteger(String name, int defaultValue);
    int getInteger(String name, int defaultValue);
    public abstract String getString(String name, String defaultValue);
    String getString(String name, String defaultValue);
    public abstract List<String> getStrings(String name);
    List<String> getStrings(String name);
    public abstract List<String> getStringsFromValue(String value);
    List<String> getStringsFromValue(String value);
    public abstract List<String> getStrings(String name, String separator);
    List<String> getStrings(String name, String separator);
    public abstract List<String> getStringsFromValue(String value, String separator);
    List<String> getStringsFromValue(String value, String separator);
}
src/com/gitblit/JettyLoginService.java
@@ -59,7 +59,7 @@
            return null;
        }
        UserModel user = new UserModel(username);
        user.canAdmin(identity.isUserInRole(Constants.ADMIN_ROLE, null));
        user.canAdmin = identity.isUserInRole(Constants.ADMIN_ROLE, null);
        // Add repositories
        for (Principal principal : identity.getSubject().getPrincipals()) {
@@ -90,7 +90,7 @@
                case '#':
                    // Permissions
                    if (name.equalsIgnoreCase(Constants.ADMIN_ROLE)) {
                        model.canAdmin(true);
                        model.canAdmin = true;
                    }
                    break;
                default:
@@ -105,7 +105,7 @@
            Properties allUsers = readRealmFile();
            String value = allUsers.getProperty(username);
            String password = value.split(",")[0];
            model.setPassword(password);
            model.password = password;
        } catch (Throwable t) {
            logger.error(MessageFormat.format("Failed to read password for user {0}!", username), t);
        }
@@ -114,22 +114,22 @@
    @Override
    public boolean updateUserModel(UserModel model) {
        return updateUserModel(model.getUsername(), model);
        return updateUserModel(model.username, model);
    }
    @Override
    public boolean updateUserModel(String username, UserModel model) {
        try {
            Properties allUsers = readRealmFile();
            ArrayList<String> roles = new ArrayList<String>(model.getRepositories());
            ArrayList<String> roles = new ArrayList<String>(model.repositories);
            // Permissions
            if (model.canAdmin()) {
            if (model.canAdmin) {
                roles.add(Constants.ADMIN_ROLE);
            }
            StringBuilder sb = new StringBuilder();
            sb.append(model.getPassword());
            sb.append(model.password);
            sb.append(',');
            for (String role : roles) {
                sb.append(role);
@@ -138,23 +138,25 @@
            // trim trailing comma
            sb.setLength(sb.length() - 1);
            allUsers.remove(username);
            allUsers.put(model.getUsername(), sb.toString());
            allUsers.put(model.username, sb.toString());
            writeRealmFile(allUsers);
            // Update login service
            removeUser(username);
            putUser(model.getUsername(), Credential.getCredential(model.getPassword()), roles.toArray(new String[0]));
            putUser(model.username, Credential.getCredential(model.password),
                    roles.toArray(new String[0]));
            return true;
        } catch (Throwable t) {
            logger.error(MessageFormat.format("Failed to update user model {0}!", model.getUsername()), t);
            logger.error(MessageFormat.format("Failed to update user model {0}!", model.username),
                    t);
        }
        return false;
    }
    @Override
    public boolean deleteUserModel(UserModel model) {
        return deleteUser(model.getUsername());
        return deleteUser(model.username);
    }
    @Override
@@ -173,7 +175,7 @@
        }
        return false;
    }
    @Override
    public List<String> getAllUsernames() {
        List<String> list = new ArrayList<String>();
@@ -235,7 +237,7 @@
            // add roles to users
            for (String user : needsAddRole) {
                String userValues = allUsers.getProperty(user);
                userValues += ("," + role);
                userValues += "," + role;
                allUsers.put(user, userValues);
                String[] values = userValues.split(",");
                String password = values[0];
@@ -267,7 +269,8 @@
                allUsers.put(user, sb.toString());
                // update memory
                putUser(user, Credential.getCredential(password), revisedRoles.toArray(new String[0]));
                putUser(user, Credential.getCredential(password),
                        revisedRoles.toArray(new String[0]));
            }
            // persist changes
@@ -324,14 +327,16 @@
                allUsers.put(user, sb.toString());
                // update memory
                putUser(user, Credential.getCredential(password), revisedRoles.toArray(new String[0]));
                putUser(user, Credential.getCredential(password),
                        revisedRoles.toArray(new String[0]));
            }
            // persist changes
            writeRealmFile(allUsers);
            return true;
        } catch (Throwable t) {
            logger.error(MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t);
            logger.error(
                    MessageFormat.format("Failed to rename role {0} to {1}!", oldRole, newRole), t);
        }
        return false;
    }
@@ -380,7 +385,8 @@
                allUsers.put(user, sb.toString());
                // update memory
                putUser(user, Credential.getCredential(password), revisedRoles.toArray(new String[0]));
                putUser(user, Credential.getCredential(password),
                        revisedRoles.toArray(new String[0]));
            }
            // persist changes
@@ -404,24 +410,36 @@
        // Update realm file
        File realmFileCopy = new File(realmFile.getAbsolutePath() + ".tmp");
        FileWriter writer = new FileWriter(realmFileCopy);
        properties.store(writer, "# Git:Blit realm file format: username=password,\\#permission,repository1,repository2...");
        properties
                .store(writer,
                        "# Git:Blit realm file format: username=password,\\#permission,repository1,repository2...");
        writer.close();
        if (realmFileCopy.exists() && realmFileCopy.length() > 0) {
            realmFile.delete();
            realmFileCopy.renameTo(realmFile);
            if (realmFile.delete()) {
                if (!realmFileCopy.renameTo(realmFile)) {
                    throw new IOException(MessageFormat.format("Failed to rename {0} to {1}!",
                            realmFileCopy.getAbsolutePath(), realmFile.getAbsolutePath()));
                }
            } else {
                throw new IOException(MessageFormat.format("Failed to delete (0)!",
                        realmFile.getAbsolutePath()));
            }
        } else {
            throw new IOException("Failed to save realmfile!");
            throw new IOException(MessageFormat.format("Failed to save {0}!",
                    realmFileCopy.getAbsolutePath()));
        }
    }
    /* ------------------------------------------------------------ */
    @Override
    public void loadUsers() throws IOException {
        if (realmFile == null)
        if (realmFile == null) {
            return;
        }
        if (Log.isDebugEnabled())
        if (Log.isDebugEnabled()) {
            Log.debug("Load " + this + " from " + realmFile);
        }
        Properties allUsers = readRealmFile();
        // Map Users
@@ -435,7 +453,8 @@
                credentials = credentials.substring(0, c).trim();
            }
            if (username != null && username.length() > 0 && credentials != null && credentials.length() > 0) {
            if (username != null && username.length() > 0 && credentials != null
                    && credentials.length() > 0) {
                String[] roleArray = IdentityService.NO_ROLES;
                if (roles != null && roles.length() > 0) {
                    roleArray = roles.split(",");
src/com/gitblit/Launcher.java
@@ -22,6 +22,7 @@
import java.net.URL;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -34,16 +35,20 @@
 */
public class Launcher {
    public final static boolean debug = false;
    public static final boolean DEBUG = false;
    /**
     * Parameters of the method to add an URL to the System classes.
     */
    private static final Class<?>[] PARAMETERS = new Class[] { URL.class };
    public static void main(String[] args) {
        if (debug)
        if (DEBUG) {
            System.out.println("jcp=" + System.getProperty("java.class.path"));
        ProtectionDomain protectionDomain = Launcher.class.getProtectionDomain();
        final String launchJar = protectionDomain.getCodeSource().getLocation().toExternalForm();
        if (debug)
            System.out.println("launcher=" + launchJar);
            ProtectionDomain protectionDomain = Launcher.class.getProtectionDomain();
            System.out.println("launcher="
                    + protectionDomain.getCodeSource().getLocation().toExternalForm());
        }
        Build.runtime();
@@ -51,16 +56,15 @@
        String[] folders = new String[] { "lib", "ext" };
        List<File> jars = new ArrayList<File>();
        for (String folder : folders) {
            if (folder == null)
            if (folder == null) {
                continue;
            File libFolder = new File(folder);
            if (!libFolder.exists())
                continue;
            try {
                libFolder = libFolder.getCanonicalFile();
            } catch (IOException iox) {
            }
            jars.addAll(findJars(libFolder));
            File libFolder = new File(folder);
            if (!libFolder.exists()) {
                continue;
            }
            List<File> found = findJars(libFolder.getAbsoluteFile());
            jars.addAll(found);
        }
        if (jars.size() == 0) {
@@ -94,19 +98,15 @@
            });
            if (libs != null && libs.length > 0) {
                jars.addAll(Arrays.asList(libs));
                if (debug) {
                    for (File jar : jars)
                if (DEBUG) {
                    for (File jar : jars) {
                        System.out.println("found " + jar);
                    }
                }
            }
        }
        return jars;
    }
    /**
     * Parameters of the method to add an URL to the System classes.
     */
    private static final Class<?>[] parameters = new Class[] { URL.class };
    /**
     * Adds a file to the classpath
@@ -121,16 +121,18 @@
            return;
        }
        URL u = f.toURI().toURL();
        if (debug)
        if (DEBUG) {
            System.out.println("load=" + u.toExternalForm());
        }
        URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
        Class<?> sysclass = URLClassLoader.class;
        try {
            Method method = sysclass.getDeclaredMethod("addURL", parameters);
            Method method = sysclass.getDeclaredMethod("addURL", PARAMETERS);
            method.setAccessible(true);
            method.invoke(sysloader, new Object[] { u });
        } catch (Throwable t) {
            throw new IOException("Error, could not add " + f.getPath() + " to system classloader", t);
            throw new IOException(MessageFormat.format(
                    "Error, could not add {0} to system classloader", f.getPath()), t);
        }
    }
}
src/com/gitblit/MakeCertificate.java
@@ -41,10 +41,13 @@
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.Parameters;
import com.gitblit.utils.TimeUtils;
public class MakeCertificate {
    private final static FileSettings fileSettings = new FileSettings();
    private static final FileSettings FILESETTINGS = new FileSettings();
    private static final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
    public static void main(String... args) {
        Params params = new Params();
@@ -52,18 +55,18 @@
        try {
            jc.parse(args);
        } catch (ParameterException t) {
            System.err.println(t.getMessage());
            jc.usage();
        }
        File keystore = new File("keystore");
        generateSelfSignedCertificate(params.alias, keystore, params.storePassword, params.subject);
    }
    public static void generateSelfSignedCertificate(String hostname, File keystore, String keystorePassword) {
    public static void generateSelfSignedCertificate(String hostname, File keystore,
            String keystorePassword) {
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
            KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
            kpGen.initialize(1024, new SecureRandom());
            KeyPair pair = kpGen.generateKeyPair();
@@ -74,82 +77,94 @@
            builder.addRDN(BCStyle.O, Constants.NAME);
            builder.addRDN(BCStyle.CN, hostname);
            Date notBefore = new Date(System.currentTimeMillis() - 1*24*60*60*1000l);
            Date notAfter = new Date(System.currentTimeMillis() + 10*365*24*60*60*1000l);
            Date notBefore = new Date(System.currentTimeMillis() - TimeUtils.ONEDAY);
            Date notAfter = new Date(System.currentTimeMillis() + 10 * TimeUtils.ONEYEAR);
            BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
            X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), serial, notBefore, notAfter, builder.build(), pair.getPublic());
            ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(pair.getPrivate());
            X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
            X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(),
                    serial, notBefore, notAfter, builder.build(), pair.getPublic());
            ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
                    .setProvider(BC).build(pair.getPrivate());
            X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
                    .getCertificate(certGen.build(sigGen));
            cert.checkValidity(new Date());
            cert.verify(cert.getPublicKey());
            // Save to keystore
            // Save to keystore
            KeyStore store = KeyStore.getInstance("JKS");
            if (keystore.exists()) {
                FileInputStream fis = new FileInputStream(keystore);
                store.load(fis, keystorePassword.toCharArray());
                fis.close();
            } else {
                store.load(null);
            }
            store.setKeyEntry(hostname, pair.getPrivate(), keystorePassword.toCharArray(), new java.security.cert.Certificate[] { cert });
            store.store(new FileOutputStream(keystore), keystorePassword.toCharArray());
            store.setKeyEntry(hostname, pair.getPrivate(), keystorePassword.toCharArray(),
                    new java.security.cert.Certificate[] { cert });
            FileOutputStream fos = new FileOutputStream(keystore);
            store.store(fos, keystorePassword.toCharArray());
            fos.close();
        } catch (Throwable t) {
            t.printStackTrace();
            throw new RuntimeException("Failed to generate self-signed certificate!", t);
        }
    }
    public static void generateSelfSignedCertificate(String hostname, File keystore, String keystorePassword, String info) {
    public static void generateSelfSignedCertificate(String hostname, File keystore,
            String keystorePassword, String info) {
        try {
            Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
            final String BC = org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME;
            KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
            kpGen.initialize(1024, new SecureRandom());
            KeyPair pair = kpGen.generateKeyPair();
            // Generate self-signed certificate
            X500Principal principal = new X500Principal(info);
            Date notBefore = new Date(System.currentTimeMillis() - 1*24*60*60*1000l);
            Date notAfter = new Date(System.currentTimeMillis() + 10*365*24*60*60*1000l);
            Date notBefore = new Date(System.currentTimeMillis() - TimeUtils.ONEDAY);
            Date notAfter = new Date(System.currentTimeMillis() + 10 * TimeUtils.ONEYEAR);
            BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());
            X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(principal, serial, notBefore, notAfter, principal, pair.getPublic());
            ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider(BC).build(pair.getPrivate());
            X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
            X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(principal, serial,
                    notBefore, notAfter, principal, pair.getPublic());
            ContentSigner sigGen = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
                    .setProvider(BC).build(pair.getPrivate());
            X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC)
                    .getCertificate(certGen.build(sigGen));
            cert.checkValidity(new Date());
            cert.verify(cert.getPublicKey());
            // Save to keystore
            // Save to keystore
            KeyStore store = KeyStore.getInstance("JKS");
            if (keystore.exists()) {
                FileInputStream fis = new FileInputStream(keystore);
                store.load(fis, keystorePassword.toCharArray());
                fis.close();
            } else {
                store.load(null);
            }
            store.setKeyEntry(hostname, pair.getPrivate(), keystorePassword.toCharArray(), new java.security.cert.Certificate[] { cert });
            store.store(new FileOutputStream(keystore), keystorePassword.toCharArray());
            store.setKeyEntry(hostname, pair.getPrivate(), keystorePassword.toCharArray(),
                    new java.security.cert.Certificate[] { cert });
            FileOutputStream fos = new FileOutputStream(keystore);
            store.store(fos, keystorePassword.toCharArray());
            fos.close();
        } catch (Throwable t) {
            t.printStackTrace();
            throw new RuntimeException("Failed to generate self-signed certificate!", t);
        }
    }
    @Parameters(separators = " ")
    private static class Params {
        @Parameter(names = { "--alias" }, description = "Server alias", required = true)
        public String alias = null;
        public String alias;
        @Parameter(names = { "--subject" }, description = "Certificate subject", required = true)
        public String subject = null;
        public String subject;
        @Parameter(names = "--storePassword", description = "Password for SSL (https) keystore.")
        public String storePassword = fileSettings.getString(Keys.server.storePassword, "");
        public String storePassword = FILESETTINGS.getString(Keys.server.storePassword, "");
    }
}
src/com/gitblit/WebXmlSettings.java
@@ -22,9 +22,9 @@
public class WebXmlSettings implements IStoredSettings {
    public WebXmlSettings(ServletContext context) {
    }
    @Override
    public List<String> getAllKeys(String startingWith) {
        // TODO Auto-generated method stub
@@ -72,7 +72,7 @@
        // TODO Auto-generated method stub
        return null;
    }
    @Override
    public String toString() {
        return "WEB.XML";
src/com/gitblit/tests/JGitUtilsTest.java
File was deleted
src/com/gitblit/utils/ByteFormat.java
@@ -32,8 +32,6 @@
    public ByteFormat() {
    }
    // Implemented from the Format class
    /**
     * Formats a long which represent a number of bytes.
     */
@@ -73,7 +71,8 @@
                buf.append(formatter.format((double) numBytes / (1024.0 * 1024.0))).append(" MB");
            } else {
                DecimalFormat formatter = new DecimalFormat("#,##0.0");
                buf.append(formatter.format((double) numBytes / (1024.0 * 1024.0 * 1024.0))).append(" GB");
                buf.append(formatter.format((double) numBytes / (1024.0 * 1024.0 * 1024.0)))
                        .append(" GB");
            }
        }
        return buf;
src/com/gitblit/utils/GitBlitDiffFormatter.java
@@ -25,7 +25,7 @@
    private final OutputStream os;
    private int left = 0, right = 0;
    private int left, right;
    public GitBlitDiffFormatter(OutputStream os) {
        super(os);
@@ -46,7 +46,8 @@
     * @throws IOException
     */
    @Override
    protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, int bEndLine) throws IOException {
    protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, int bEndLine)
            throws IOException {
        os.write("<tr><th>..</th><th>..</th><td class='hunk_header'>".getBytes());
        os.write('@');
        os.write('@');
@@ -61,7 +62,8 @@
    }
    @Override
    protected void writeLine(final char prefix, final RawText text, final int cur) throws IOException {
    protected void writeLine(final char prefix, final RawText text, final int cur)
            throws IOException {
        os.write("<tr>".getBytes());
        switch (prefix) {
        case '+':
@@ -104,7 +106,7 @@
    public String getHtml() {
        String html = os.toString();
        String[] lines = html.split("\n");
        StringBuilder sb = new StringBuilder();
        StringBuilder sb = new StringBuilder();
        boolean inFile = false;
        String oldnull = "a/dev/null";
        for (String line : lines) {
@@ -120,7 +122,8 @@
                if (line.indexOf(oldnull) > -1) {
                    // a is null, use b
                    line = line.substring(("diff --git " + oldnull).length()).trim();
                    line = line.substring(2); // trim b/
                    // trim b/
                    line = line.substring(2);
                } else {
                    // use a
                    line = line.substring("diff --git a/".length()).trim();
@@ -131,7 +134,7 @@
                    inFile = false;
                }
                sb.append("<div class='header'>").append(line).append("</div>");
                sb.append("<div class=\"diff\">");
                sb.append("<div class=\"diff\">");
                sb.append("<table><tbody>");
                inFile = true;
            } else {
src/com/gitblit/utils/GitWebDiffFormatter.java
@@ -47,7 +47,8 @@
     * @throws IOException
     */
    @Override
    protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, int bEndLine) throws IOException {
    protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, int bEndLine)
            throws IOException {
        os.write("<div class=\"diff hunk_header\"><span class=\"diff hunk_info\">".getBytes());
        os.write('@');
        os.write('@');
@@ -93,7 +94,8 @@
    }
    @Override
    protected void writeLine(final char prefix, final RawText text, final int cur) throws IOException {
    protected void writeLine(final char prefix, final RawText text, final int cur)
            throws IOException {
        switch (prefix) {
        case '+':
            os.write("<span class=\"diff add\">".getBytes());
src/com/gitblit/utils/JGitUtils.java
@@ -31,6 +31,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
@@ -81,21 +82,24 @@
public class JGitUtils {
    private final static Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);
    private static final Logger LOGGER = LoggerFactory.getLogger(JGitUtils.class);
    public static Repository createRepository(File repositoriesFolder, String name, boolean bare) {
        Git git = Git.init().setDirectory(new File(repositoriesFolder, name)).setBare(bare).call();
        return git.getRepository();
    }
    public static List<String> getRepositoryList(File repositoriesFolder, boolean exportAll, boolean readNested) {
    public static List<String> getRepositoryList(File repositoriesFolder, boolean exportAll,
            boolean readNested) {
        List<String> list = new ArrayList<String>();
        list.addAll(getNestedRepositories(repositoriesFolder, repositoriesFolder, exportAll, readNested));
        list.addAll(getNestedRepositories(repositoriesFolder, repositoriesFolder, exportAll,
                readNested));
        Collections.sort(list);
        return list;
    }
    public static List<String> getNestedRepositories(File repositoriesFolder, File folder, boolean exportAll, boolean readNested) {
    public static List<String> getNestedRepositories(File repositoriesFolder, File folder,
            boolean exportAll, boolean readNested) {
        String basefile = repositoriesFolder.getAbsolutePath();
        List<String> list = new ArrayList<String>();
        if (folder == null || !folder.exists()) {
@@ -112,12 +116,16 @@
                // then look for folder.git/HEAD or folder/HEAD and
                // folder/config
                if (!isGitRepository) {
                    if ((file.getName().endsWith(Constants.DOT_GIT_EXT) && new File(file, Constants.HEAD).exists()) || (new File(file, "config").exists() && new File(file, Constants.HEAD).exists())) {
                    if ((file.getName().endsWith(Constants.DOT_GIT_EXT) && new File(file,
                            Constants.HEAD).exists())
                            || (new File(file, "config").exists() && new File(file, Constants.HEAD)
                                    .exists())) {
                        gitFolder = file;
                        isGitRepository = true;
                    }
                }
                boolean exportRepository = isGitRepository && (exportAll || new File(gitFolder, "git-daemon-export-ok").exists());
                boolean exportRepository = isGitRepository
                        && (exportAll || new File(gitFolder, "git-daemon-export-ok").exists());
                if (exportRepository) {
                    // determine repository name relative to repositories folder
@@ -131,7 +139,8 @@
                // look for nested repositories
                if (readNested) {
                    list.addAll(getNestedRepositories(repositoriesFolder, file, exportAll, readNested));
                    list.addAll(getNestedRepositories(repositoriesFolder, file, exportAll,
                            readNested));
                }
            }
        }
@@ -209,13 +218,13 @@
    public static Map<ObjectId, List<String>> getAllRefs(Repository r) {
        Map<ObjectId, List<String>> refs = new HashMap<ObjectId, List<String>>();
        Map<AnyObjectId, Set<Ref>> allRefs = r.getAllRefsByPeeledObjectId();
        for (AnyObjectId id : allRefs.keySet()) {
        for (Entry<AnyObjectId, Set<Ref>> setRefs : allRefs.entrySet()) {
            List<String> list = new ArrayList<String>();
            for (Ref setRef : allRefs.get(id)) {
            for (Ref setRef : setRefs.getValue()) {
                String name = setRef.getName();
                list.add(name);
            }
            refs.put(id.toObjectId(), list);
            refs.put(setRefs.getKey().toObjectId(), list);
        }
        return refs;
    }
@@ -223,15 +232,15 @@
    public static Map<ObjectId, List<String>> getRefs(Repository r, String baseRef) {
        Map<ObjectId, List<String>> refs = new HashMap<ObjectId, List<String>>();
        Map<AnyObjectId, Set<Ref>> allRefs = r.getAllRefsByPeeledObjectId();
        for (AnyObjectId id : allRefs.keySet()) {
        for (Entry<AnyObjectId, Set<Ref>> setRefs : allRefs.entrySet()) {
            List<String> list = new ArrayList<String>();
            for (Ref setRef : allRefs.get(id)) {
            for (Ref setRef : setRefs.getValue()) {
                String name = setRef.getName();
                if (name.startsWith(baseRef)) {
                    list.add(name);
                }
            }
            refs.put(id.toObjectId(), list);
            refs.put(setRefs.getKey().toObjectId(), list);
        }
        return refs;
    }
@@ -378,9 +387,11 @@
            List<DiffEntry> diffs = df.scan(parentTree, commitTree);
            for (DiffEntry diff : diffs) {
                if (diff.getChangeType().equals(ChangeType.DELETE)) {
                    list.add(new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff.getNewMode().getBits(), commit.getId().getName(), diff.getChangeType()));
                    list.add(new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff
                            .getNewMode().getBits(), commit.getId().getName(), diff.getChangeType()));
                } else {
                    list.add(new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff.getNewMode().getBits(), commit.getId().getName(), diff.getChangeType()));
                    list.add(new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff
                            .getNewMode().getBits(), commit.getId().getName(), diff.getChangeType()));
                }
            }
        } catch (Throwable t) {
@@ -453,15 +464,18 @@
        return getCommitDiff(r, null, commit, null, outputType);
    }
    public static String getCommitDiff(Repository r, RevCommit commit, String path, DiffOutputType outputType) {
    public static String getCommitDiff(Repository r, RevCommit commit, String path,
            DiffOutputType outputType) {
        return getCommitDiff(r, null, commit, path, outputType);
    }
    public static String getCommitDiff(Repository r, RevCommit baseCommit, RevCommit commit, DiffOutputType outputType) {
    public static String getCommitDiff(Repository r, RevCommit baseCommit, RevCommit commit,
            DiffOutputType outputType) {
        return getCommitDiff(r, baseCommit, commit, null, outputType);
    }
    public static String getCommitDiff(Repository r, RevCommit baseCommit, RevCommit commit, String path, DiffOutputType outputType) {
    public static String getCommitDiff(Repository r, RevCommit baseCommit, RevCommit commit,
            String path, DiffOutputType outputType) {
        try {
            RevTree baseTree;
            if (baseCommit == null) {
@@ -526,15 +540,8 @@
        return null;
    }
    public static String getCommitPatch(Repository r, RevCommit commit) {
        return getCommitPatch(r, commit);
    }
    public static String getCommitPatch(Repository r, RevCommit commit, String path) {
        return getCommitPatch(r, null, commit, path);
    }
    public static String getCommitPatch(Repository r, RevCommit baseCommit, RevCommit commit, String path) {
    public static String getCommitPatch(Repository r, RevCommit baseCommit, RevCommit commit,
            String path) {
        try {
            RevTree baseTree;
            if (baseCommit == null) {
@@ -593,12 +600,14 @@
        }
        try {
            if (!walk.isSubtree()) {
                size = walk.getObjectReader().getObjectSize(walk.getObjectId(0), Constants.OBJ_BLOB);
                size = walk.getObjectReader()
                        .getObjectSize(walk.getObjectId(0), Constants.OBJ_BLOB);
            }
        } catch (Throwable t) {
            LOGGER.error("Failed to retrieve blob size", t);
        }
        return new PathModel(name, walk.getPathString(), size, walk.getFileMode(0).getBits(), commit.getName());
        return new PathModel(name, walk.getPathString(), size, walk.getFileMode(0).getBits(),
                commit.getName());
    }
    public static String getPermissionsFromMode(int mode) {
@@ -633,7 +642,8 @@
        return getRevLog(r, objectId, null, offset, maxCount);
    }
    public static List<RevCommit> getRevLog(Repository r, String objectId, String path, int offset, int maxCount) {
    public static List<RevCommit> getRevLog(Repository r, String objectId, String path, int offset,
            int maxCount) {
        List<RevCommit> list = new ArrayList<RevCommit>();
        if (!hasCommits(r)) {
            return list;
@@ -646,7 +656,9 @@
            ObjectId object = r.resolve(objectId);
            walk.markStart(walk.parseCommit(object));
            if (!StringUtils.isEmpty(path)) {
                TreeFilter filter = AndTreeFilter.create(PathFilterGroup.createFromStrings(Collections.singleton(path)), TreeFilter.ANY_DIFF);
                TreeFilter filter = AndTreeFilter.create(
                        PathFilterGroup.createFromStrings(Collections.singleton(path)),
                        TreeFilter.ANY_DIFF);
                walk.setTreeFilter(filter);
            }
            Iterable<RevCommit> revlog = walk;
@@ -693,7 +705,8 @@
        }
    }
    public static List<RevCommit> searchRevlogs(Repository r, String objectId, String value, final SearchType type, int offset, int maxCount) {
    public static List<RevCommit> searchRevlogs(Repository r, String objectId, String value,
            final SearchType type, int offset, int maxCount) {
        final String lcValue = value.toLowerCase();
        List<RevCommit> list = new ArrayList<RevCommit>();
        if (!hasCommits(r)) {
@@ -712,12 +725,17 @@
                }
                @Override
                public boolean include(RevWalk walker, RevCommit commit) throws StopWalkException, MissingObjectException, IncorrectObjectTypeException, IOException {
                public boolean include(RevWalk walker, RevCommit commit) throws StopWalkException,
                        MissingObjectException, IncorrectObjectTypeException, IOException {
                    switch (type) {
                    case AUTHOR:
                        return (commit.getAuthorIdent().getName().toLowerCase().indexOf(lcValue) > -1) || (commit.getAuthorIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);
                        return (commit.getAuthorIdent().getName().toLowerCase().indexOf(lcValue) > -1)
                                || (commit.getAuthorIdent().getEmailAddress().toLowerCase()
                                        .indexOf(lcValue) > -1);
                    case COMMITTER:
                        return (commit.getCommitterIdent().getName().toLowerCase().indexOf(lcValue) > -1) || (commit.getCommitterIdent().getEmailAddress().toLowerCase().indexOf(lcValue) > -1);
                        return (commit.getCommitterIdent().getName().toLowerCase().indexOf(lcValue) > -1)
                                || (commit.getCommitterIdent().getEmailAddress().toLowerCase()
                                        .indexOf(lcValue) > -1);
                    case COMMIT:
                        return commit.getFullMessage().toLowerCase().indexOf(lcValue) > -1;
                    }
@@ -770,10 +788,10 @@
        List<RefModel> list = new ArrayList<RefModel>();
        try {
            Map<String, Ref> map = r.getRefDatabase().getRefs(refs);
            for (String name : map.keySet()) {
                Ref ref = map.get(name);
            for (Entry<String, Ref> entry : map.entrySet()) {
                Ref ref = entry.getValue();
                RevCommit commit = getCommit(r, ref.getObjectId().getName());
                list.add(new RefModel(name, ref, commit));
                list.add(new RefModel(entry.getKey(), ref, commit));
            }
            Collections.sort(list);
            Collections.reverse(list);
@@ -787,10 +805,11 @@
    }
    public static Ref getRef(Repository r, String id) {
        // FIXME
        try {
            Map<String, Ref> map = r.getRefDatabase().getRefs(id);
            for (String name : map.keySet()) {
                return map.get(name);
            for (Entry<String, Ref> entry : map.entrySet()) {
                return entry.getValue();
            }
        } catch (IOException e) {
            LOGGER.error("Failed to retrieve ref " + id, e);
@@ -799,7 +818,7 @@
    }
    public static Date getCommitDate(RevCommit commit) {
        return new Date(commit.getCommitTime() * 1000l);
        return new Date(commit.getCommitTime() * 1000L);
    }
    public static String getDisplayName(PersonIdent person) {
@@ -807,7 +826,7 @@
        r.append(person.getName());
        r.append(" <");
        r.append(person.getEmailAddress());
        r.append(">");
        r.append('>');
        return r.toString();
    }
@@ -826,7 +845,8 @@
        return null;
    }
    public static boolean zip(Repository r, String basePath, String objectId, OutputStream os) throws Exception {
    public static boolean zip(Repository r, String basePath, String objectId, OutputStream os)
            throws Exception {
        RevCommit commit = getCommit(r, objectId);
        if (commit == null) {
            return false;
@@ -844,7 +864,8 @@
            walk.setRecursive(true);
            while (walk.next()) {
                ZipEntry entry = new ZipEntry(walk.getPathString());
                entry.setSize(walk.getObjectReader().getObjectSize(walk.getObjectId(0), Constants.OBJ_BLOB));
                entry.setSize(walk.getObjectReader().getObjectSize(walk.getObjectId(0),
                        Constants.OBJ_BLOB));
                entry.setComment(commit.getName());
                zos.putNextEntry(entry);
@@ -889,7 +910,8 @@
                RevCommit firstCommit = getFirstCommit(r, Constants.HEAD);
                RevCommit lastCommit = walk.parseCommit(object);
                int diffDays = (lastCommit.getCommitTime() - firstCommit.getCommitTime()) / (60 * 60 * 24);
                int diffDays = (lastCommit.getCommitTime() - firstCommit.getCommitTime())
                        / (60 * 60 * 24);
                total.duration = diffDays;
                DateFormat df;
                if (diffDays <= 90) {
@@ -908,8 +930,9 @@
                for (RevCommit rev : revlog) {
                    Date d = getCommitDate(rev);
                    String p = df.format(d);
                    if (!metricMap.containsKey(p))
                    if (!metricMap.containsKey(p)) {
                        metricMap.put(p, new Metric(p));
                    }
                    Metric m = metricMap.get(p);
                    m.count++;
                    total.count++;
@@ -937,7 +960,7 @@
        try {
            // search for ticgit branch in local heads
            for (RefModel ref : getLocalBranches(r, -1)) {
                if (ref.getDisplayName().endsWith("ticgit")) {
                if (ref.displayName.endsWith("ticgit")) {
                    ticgitBranch = ref;
                    break;
                }
@@ -946,7 +969,7 @@
            // search for ticgit branch in remote heads
            if (ticgitBranch == null) {
                for (RefModel ref : getRemoteBranches(r, -1)) {
                    if (ref.getDisplayName().endsWith("ticgit")) {
                    if (ref.displayName.endsWith("ticgit")) {
                        ticgitBranch = ref;
                        break;
                    }
@@ -960,7 +983,7 @@
    public static List<TicketModel> getTickets(Repository r) {
        RefModel ticgitBranch = getTicketsBranch(r);
        List<PathModel> paths = getFilesInPath(r, null, ticgitBranch.getCommit());
        List<PathModel> paths = getFilesInPath(r, null, ticgitBranch.commit);
        List<TicketModel> tickets = new ArrayList<TicketModel>();
        for (PathModel ticketFolder : paths) {
            if (ticketFolder.isTree()) {
@@ -993,9 +1016,9 @@
    }
    private static void readTicketContents(Repository r, RefModel ticketsBranch, TicketModel ticket) {
        List<PathModel> ticketFiles = getFilesInPath(r, ticket.name, ticketsBranch.getCommit());
        List<PathModel> ticketFiles = getFilesInPath(r, ticket.name, ticketsBranch.commit);
        for (PathModel file : ticketFiles) {
            String content = getRawContentAsString(r, ticketsBranch.getCommit(), file.path).trim();
            String content = getRawContentAsString(r, ticketsBranch.commit, file.path).trim();
            if (file.name.equals("TICKET_ID")) {
                ticket.id = content;
            } else if (file.name.equals("TITLE")) {
@@ -1028,7 +1051,7 @@
    public static String getTicketContent(Repository r, String filePath) {
        RefModel ticketsBranch = getTicketsBranch(r);
        if (ticketsBranch != null) {
            return getRawContentAsString(r, ticketsBranch.getCommit(), filePath);
            return getRawContentAsString(r, ticketsBranch.commit, filePath);
        }
        return "";
    }
src/com/gitblit/utils/MarkdownUtils.java
@@ -26,41 +26,44 @@
public class MarkdownUtils {
    public static String transformMarkdown(String markdown) throws java.text.ParseException {
        // Read raw markdown content and transform it to html
        // Read raw markdown content and transform it to html
        StringReader reader = new StringReader(markdown);
        StringWriter writer = new StringWriter();
        try {
            Markdown md = new Markdown();
            md.transform(reader, writer);
            return writer.toString();
        } catch (ParseException p) {
        } catch (ParseException p) {
            throw new java.text.ParseException(p.getMessage(), 0);
        } finally {
            reader.close();
            try {
                writer.close();
            } catch (IOException e) {
                // IGNORE
            }
        }
    }
    public static String transformMarkdown(Reader markdownReader) throws java.text.ParseException {
        // Read raw markdown content and transform it to html
        // Read raw markdown content and transform it to html
        StringWriter writer = new StringWriter();
        try {
            Markdown md = new Markdown();
            md.transform(markdownReader, writer);
            return writer.toString();
        } catch (ParseException p) {
        } catch (ParseException p) {
            throw new java.text.ParseException(p.getMessage(), 0);
        } finally {
            try {
                markdownReader.close();
            } catch (IOException e) {
                // IGNORE
            }
            try {
                writer.close();
            } catch (IOException e) {
                // IGNORE
            }
        }
    }
src/com/gitblit/utils/PatchFormatter.java
@@ -34,9 +34,9 @@
    private final OutputStream os;
    private PatchTouple currentTouple = null;
    private Map<String, PatchTouple> changes = new HashMap<String, PatchTouple>();
    Map<String, PatchTouple> changes = new HashMap<String, PatchTouple>();
    private PatchTouple currentTouple;
    public PatchFormatter(OutputStream os) {
        super(os);
@@ -50,7 +50,8 @@
    }
    @Override
    protected void writeLine(final char prefix, final RawText text, final int cur) throws IOException {
    protected void writeLine(final char prefix, final RawText text, final int cur)
            throws IOException {
        switch (prefix) {
        case '+':
            currentTouple.insertions++;
@@ -68,9 +69,11 @@
        // I have no idea why that is there. it seems to be a constant.
        patch.append("From " + commit.getName() + " Mon Sep 17 00:00:00 2001" + "\n");
        patch.append("From: " + JGitUtils.getDisplayName(commit.getAuthorIdent()) + "\n");
        patch.append("Date: " + (new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z").format(new Date(commit.getCommitTime() * 1000l))) + "\n");
        patch.append("Date: "
                + (new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z").format(new Date(commit
                        .getCommitTime() * 1000L))) + "\n");
        patch.append("Subject: [PATCH] " + commit.getShortMessage() + "\n");
        patch.append("\n");
        patch.append('\n');
        patch.append("---");
        int maxPathLen = 0;
        int files = 0;
@@ -93,26 +96,30 @@
        }
        for (String path : changes.keySet()) {
            PatchTouple touple = changes.get(path);
            patch.append("\n " + StringUtils.rightPad(path, maxPathLen, ' ') + " | " + StringUtils.leftPad("" + touple.total(), 4, ' ') + " " + touple.relativeScale(unit));
            patch.append("\n " + StringUtils.rightPad(path, maxPathLen, ' ') + " | "
                    + StringUtils.leftPad("" + touple.total(), 4, ' ') + " "
                    + touple.relativeScale(unit));
        }
        patch.append(MessageFormat.format("\n {0} files changed, {1} insertions(+), {2} deletions(-)\n\n", files, insertions, deletions));
        patch.append(MessageFormat.format(
                "\n {0} files changed, {1} insertions(+), {2} deletions(-)\n\n", files, insertions,
                deletions));
        patch.append(os.toString());
        patch.append("\n--\n");
        patch.append(Constants.getRunningVersion());
        patch.append(Constants.getGitBlitVersion());
        return patch.toString();
    }
    private class PatchTouple {
        int insertions = 0;
        int deletions = 0;
    private static class PatchTouple {
        int insertions;
        int deletions;
        int total() {
            return insertions + deletions;
        }
        String relativeScale(int unit) {
            int plus = (insertions / unit);
            int minus = (deletions / unit);
            int plus = insertions / unit;
            int minus = deletions / unit;
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < plus; i++) {
                sb.append('+');
src/com/gitblit/utils/StringUtils.java
@@ -20,7 +20,6 @@
import java.security.NoSuchAlgorithmException;
import java.util.List;
public class StringUtils {
    public static boolean isEmpty(String value) {
@@ -47,8 +46,9 @@
                retStr.append("&nbsp;");
            } else if (changeSpace && inStr.charAt(i) == '\t') {
                retStr.append(" &nbsp; &nbsp;");
            } else
            } else {
                retStr.append(inStr.charAt(i));
            }
            i++;
        }
        return retStr.toString();
@@ -117,8 +117,9 @@
            byte[] sha1hash = md.digest();
            StringBuilder sb = new StringBuilder(sha1hash.length * 2);
            for (int i = 0; i < sha1hash.length; i++) {
                if (((int) sha1hash[i] & 0xff) < 0x10)
                    sb.append("0");
                if (((int) sha1hash[i] & 0xff) < 0x10) {
                    sb.append('0');
                }
                sb.append(Long.toString((int) sha1hash[i] & 0xff, 16));
            }
            return sb.toString();
@@ -126,7 +127,7 @@
            throw new RuntimeException(t);
        }
    }
    public static String getRootPath(String path) {
        if (path.indexOf('/') > -1) {
            return path.substring(0, path.indexOf('/'));
src/com/gitblit/utils/TimeUtils.java
@@ -18,24 +18,28 @@
import java.util.Date;
public class TimeUtils {
    private final static long min = 1000 * 60l;
    public static final long MIN = 1000 * 60L;
    private final static long halfhour = min * 30l;
    public static final long HALFHOUR = MIN * 30L;
    private final static long onehour = halfhour * 2;
    public static final long ONEHOUR = HALFHOUR * 2;
    private final static long oneday = onehour * 24l;
    public static final long ONEDAY = ONEHOUR * 24L;
    public static final long ONEYEAR = ONEDAY * 365L;
    @SuppressWarnings("deprecation")
    public static boolean isToday(Date date) {
        Date now = new Date();
        return now.getDate() == date.getDate() && now.getMonth() == date.getMonth() && now.getYear() == date.getYear();
        return now.getDate() == date.getDate() && now.getMonth() == date.getMonth()
                && now.getYear() == date.getYear();
    }
    @SuppressWarnings("deprecation")
    public static boolean isYesterday(Date date) {
        Date now = new Date();
        return now.getDate() == (date.getDate() + 1) && now.getMonth() == date.getMonth() && now.getYear() == date.getYear();
        return now.getDate() == (date.getDate() + 1) && now.getMonth() == date.getMonth()
                && now.getYear() == date.getYear();
    }
    public static String duration(int days) {
@@ -56,17 +60,18 @@
                }
            } else {
                int months = rem / 30;
                int remDays = (rem % 30);
                int remDays = rem % 30;
                String monthsString;
                if (months == 0) {
                    monthsString = yearsString;
                } else {
                    monthsString = yearsString + ", " + months + (months > 1 ? " months" : " month");
                    monthsString = yearsString + ", " + months
                            + (months > 1 ? " months" : " month");
                }
                if (remDays == 0) {
                    return  monthsString;
                    return monthsString;
                } else {
                    return monthsString + ", " + remDays + (remDays > 1 ? " days":" day");
                    return monthsString + ", " + remDays + (remDays > 1 ? " days" : " day");
                }
            }
        }
@@ -74,9 +79,10 @@
    public static int minutesAgo(Date date, long endTime, boolean roundup) {
        long diff = endTime - date.getTime();
        int mins = (int) (diff / min);
        if (roundup && (diff % min) >= 30)
        int mins = (int) (diff / MIN);
        if (roundup && (diff % MIN) >= 30) {
            mins++;
        }
        return mins;
    }
@@ -86,17 +92,19 @@
    public static int hoursAgo(Date date, boolean roundup) {
        long diff = System.currentTimeMillis() - date.getTime();
        int hours = (int) (diff / onehour);
        if (roundup && (diff % onehour) >= halfhour)
        int hours = (int) (diff / ONEHOUR);
        if (roundup && (diff % ONEHOUR) >= HALFHOUR) {
            hours++;
        }
        return hours;
    }
    public static int daysAgo(Date date, boolean roundup) {
        long diff = System.currentTimeMillis() - date.getTime();
        int days = (int) (diff / oneday);
        if (roundup && (diff % oneday) > 0)
        int days = (int) (diff / ONEDAY);
        if (roundup && (diff % ONEDAY) > 0) {
            days++;
        }
        return days;
    }
@@ -138,18 +146,21 @@
                    ago = days + " day" + (days > 1 ? "s" : "") + " ago";
                } else if (days <= 90) {
                    int weeks = days / 7;
                    if (weeks == 12)
                    if (weeks == 12) {
                        ago = "3 months ago";
                    else
                    } else {
                        ago = weeks + " weeks ago";
                    }
                } else if (days > 90) {
                    int months = days / 30;
                    int weeks = (days % 30) / 7;
                    if (weeks >= 2)
                    if (weeks >= 2) {
                        months++;
                    }
                    ago = months + " month" + (months > 1 ? "s" : "") + " ago";
                } else
                } else {
                    ago = days + " day" + (days > 1 ? "s" : "") + " ago";
                }
            } else if (days == 365) {
                ago = "1 year ago";
            } else {
src/com/gitblit/wicket/AuthorizationStrategy.java
@@ -25,7 +25,8 @@
import com.gitblit.wicket.models.UserModel;
import com.gitblit.wicket.pages.RepositoriesPage;
public class AuthorizationStrategy extends AbstractPageAuthorizationStrategy implements IUnauthorizedComponentInstantiationListener {
public class AuthorizationStrategy extends AbstractPageAuthorizationStrategy implements
        IUnauthorizedComponentInstantiationListener {
    public AuthorizationStrategy() {
    }
@@ -34,16 +35,16 @@
    @Override
    protected boolean isPageAuthorized(Class pageClass) {
        if (BasePage.class.isAssignableFrom(pageClass)) {
            boolean authenticateView = GitBlit.self().settings().getBoolean(Keys.web.authenticateViewPages, true);
            boolean authenticateAdmin = GitBlit.self().settings().getBoolean(Keys.web.authenticateAdminPages, true);
            boolean allowAdmin = GitBlit.self().settings().getBoolean(Keys.web.allowAdministration, true);
            GitBlitWebSession session = GitBlitWebSession.get();
            boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);
            boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
            boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, true);
            GitBlitWebSession session = GitBlitWebSession.get();
            if (authenticateView && !session.isLoggedIn()) {
                // authentication required
                return false;
            }
            UserModel user = session.getUser();
            if (pageClass.isAnnotationPresent(AdminPage.class)) {
                // admin page
@@ -51,7 +52,7 @@
                    if (authenticateAdmin) {
                        // authenticate admin
                        if (user != null) {
                            return user.canAdmin();
                            return user.canAdmin;
                        }
                        return false;
                    } else {
@@ -59,7 +60,7 @@
                        return true;
                    }
                } else {
                    //admin prohibited
                    // admin prohibited
                    return false;
                }
            }
@@ -71,10 +72,11 @@
    public void onUnauthorizedInstantiation(Component component) {
        if (component instanceof BasePage) {
            GitBlitWebSession session = GitBlitWebSession.get();
            if (!session.isLoggedIn())
            if (!session.isLoggedIn()) {
                throw new RestartResponseAtInterceptPageException(LoginPage.class);
            else
            } else {
                throw new RestartResponseAtInterceptPageException(RepositoriesPage.class);
            }
        }
    }
}
src/com/gitblit/wicket/BasePage.java
@@ -57,22 +57,25 @@
            add(new Label("title", getServerName()));
        }
        // header
        String siteName = GitBlit.self().settings().getString(Keys.web.siteName, Constants.NAME);
        String siteName = GitBlit.getString(Keys.web.siteName, Constants.NAME);
        if (siteName == null || siteName.trim().length() == 0) {
            siteName = Constants.NAME;
        }
        add(new Label("siteName", siteName));
        add(new LinkPanel("repositoryName", null, repositoryName, SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
        add(new LinkPanel("repositoryName", null, repositoryName, SummaryPage.class,
                WicketUtils.newRepositoryParameter(repositoryName)));
        add(new Label("pageName", pageName));
        // Feedback panel for info, warning, and non-fatal error messages
        add(new FeedbackPanel("feedback"));
        // footer
        if (GitBlit.self().settings().getBoolean(Keys.web.authenticateViewPages, true) || GitBlit.self().settings().getBoolean(Keys.web.authenticateAdminPages, true)) {
        if (GitBlit.getBoolean(Keys.web.authenticateViewPages, true)
                || GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) {
            if (GitBlitWebSession.get().isLoggedIn()) {
                // logout
                add(new LinkPanel("userPanel", null, getString("gb.logout") + " " + GitBlitWebSession.get().getUser().toString(), LogoutPage.class));
                add(new LinkPanel("userPanel", null, getString("gb.logout") + " "
                        + GitBlitWebSession.get().getUser().toString(), LogoutPage.class));
            } else {
                // login
                add(new LinkPanel("userPanel", null, getString("gb.login"), LoginPage.class));
@@ -81,7 +84,7 @@
            add(new Label("userPanel", ""));
        }
        add(new Label("gbVersion", "v" + Constants.VERSION));
        if (GitBlit.self().settings().getBoolean(Keys.web.aggressiveHeapManagement, false)) {
        if (GitBlit.getBoolean(Keys.web.aggressiveHeapManagement, false)) {
            System.gc();
        }
    }
@@ -108,7 +111,8 @@
    }
    protected TimeZone getTimeZone() {
        return GitBlit.self().settings().getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get().getTimezone() : TimeZone.getDefault();
        return GitBlit.getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get()
                .getTimezone() : TimeZone.getDefault();
    }
    protected String getServerName() {
src/com/gitblit/wicket/GitBlitWebApp.java
@@ -20,6 +20,7 @@
import org.apache.wicket.Request;
import org.apache.wicket.Response;
import org.apache.wicket.Session;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.request.target.coding.MixedParamUrlCodingStrategy;
@@ -52,7 +53,8 @@
        super.init();
        // Setup page authorization mechanism
        boolean useAuthentication = GitBlit.self().settings().getBoolean(Keys.web.authenticateViewPages, false) || GitBlit.self().settings().getBoolean(Keys.web.authenticateAdminPages, false);
        boolean useAuthentication = GitBlit.getBoolean(Keys.web.authenticateViewPages, false)
                || GitBlit.getBoolean(Keys.web.authenticateAdminPages, false);
        if (useAuthentication) {
            AuthorizationStrategy authStrategy = new AuthorizationStrategy();
            getSecuritySettings().setAuthorizationStrategy(authStrategy);
@@ -60,39 +62,46 @@
        }
        // Grab Browser info (like timezone, etc)
        if (GitBlit.self().settings().getBoolean(Keys.web.useClientTimezone, false)) {
        if (GitBlit.getBoolean(Keys.web.useClientTimezone, false)) {
            getRequestCycleSettings().setGatherExtendedBrowserInfo(true);
        }
        // setup the standard gitweb-ish urls
        mount(new MixedParamUrlCodingStrategy("/summary", SummaryPage.class, new String[] { "r" }));
        mount(new MixedParamUrlCodingStrategy("/log", LogPage.class, new String[] { "r", "h" }));
        mount(new MixedParamUrlCodingStrategy("/tags", TagsPage.class, new String[] { "r" }));
        mount(new MixedParamUrlCodingStrategy("/branches", BranchesPage.class, new String[] { "r" }));
        mount(new MixedParamUrlCodingStrategy("/commit", CommitPage.class, new String[] { "r", "h" }));
        mount(new MixedParamUrlCodingStrategy("/tag", TagPage.class, new String[] { "r", "h" }));
        mount(new MixedParamUrlCodingStrategy("/tree", TreePage.class, new String[] { "r", "h", "f" }));
        mount(new MixedParamUrlCodingStrategy("/blob", BlobPage.class, new String[] { "r", "h", "f" }));
        mount(new MixedParamUrlCodingStrategy("/raw", RawPage.class, new String[] { "r", "h", "f" }));
        mount(new MixedParamUrlCodingStrategy("/blobdiff", BlobDiffPage.class, new String[] { "r", "h", "f" }));
        mount(new MixedParamUrlCodingStrategy("/commitdiff", CommitDiffPage.class, new String[] { "r", "h" }));
        mount(new MixedParamUrlCodingStrategy("/patch", PatchPage.class, new String[] { "r", "h", "f" }));
        mount(new MixedParamUrlCodingStrategy("/history", HistoryPage.class, new String[] { "r", "h", "f" }));
        mount(new MixedParamUrlCodingStrategy("/search", SearchPage.class, new String[] { }));
        mount("/summary", SummaryPage.class, "r");
        mount("/log", LogPage.class, "r", "h");
        mount("/tags", TagsPage.class, "r");
        mount("/branches", BranchesPage.class, "r");
        mount("/commit", CommitPage.class, "r", "h");
        mount("/tag", TagPage.class, "r", "h");
        mount("/tree", TreePage.class, "r", "h", "f");
        mount("/blob", BlobPage.class, "r", "h", "f");
        mount("/raw", RawPage.class, "r", "h", "f");
        mount("/blobdiff", BlobDiffPage.class, "r", "h", "f");
        mount("/commitdiff", CommitDiffPage.class, "r", "h");
        mount("/patch", PatchPage.class, "r", "h", "f");
        mount("/history", HistoryPage.class, "r", "h", "f");
        mount("/search", SearchPage.class);
        // setup ticket urls
        mount(new MixedParamUrlCodingStrategy("/tickets", TicketsPage.class, new String[] { "r" }));
        mount(new MixedParamUrlCodingStrategy("/ticket", TicketPage.class, new String[] { "r", "h", "f" }));
        mount("/tickets", TicketsPage.class, "r");
        mount("/ticket", TicketPage.class, "r", "h", "f");
        // setup the markdown urls
        mount(new MixedParamUrlCodingStrategy("/docs", DocsPage.class, new String[] { "r" }));
        mount(new MixedParamUrlCodingStrategy("/markdown", MarkdownPage.class, new String[] { "r", "h", "f" }));
        mount("/docs", DocsPage.class, "r");
        mount("/markdown", MarkdownPage.class, "r", "h", "f");
        // setup login/logout urls, if we are using authentication
        if (useAuthentication) {
            mount(new MixedParamUrlCodingStrategy("/login", LoginPage.class, new String[] {}));
            mount(new MixedParamUrlCodingStrategy("/logout", LogoutPage.class, new String[] {}));
            mount("/login", LoginPage.class);
            mount("/logout", LogoutPage.class);
        }
    }
    private void mount(String location, Class<? extends WebPage> clazz, String... parameters) {
        if (parameters == null) {
            parameters = new String[] {};
        }
        mount(new MixedParamUrlCodingStrategy(location, clazz, parameters));
    }
    @Override
@@ -107,8 +116,9 @@
    @Override
    public final String getConfigurationType() {
        if (GitBlit.self().isDebugMode())
        if (GitBlit.self().isDebugMode()) {
            return Application.DEVELOPMENT;
        }
        return Application.DEPLOYMENT;
    }
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -38,7 +38,6 @@
gb.pageFirst = first
gb.pagePrevious prev
gb.pageNext = next
gb.parent = parent
gb.head = HEAD
gb.blame = blame
gb.login = Login
@@ -67,18 +66,15 @@
gb.edit = edit
gb.searchTypeTooltip = Select Search Type
gb.searchTooltip = Search Git:Blit
gb.rename = rename
gb.delete = delete
gb.docs = docs
gb.accessRestriction = access restriction
gb.name = name
gb.description = description
gb.enableTickets = enable tickets
gb.enableDocs = enable docs
gb.save = save
gb.showRemoteBranches = show remote branches
gb.editUsers = edit users
gb.password = password
gb.confirmPassword = confirm password
gb.restrictedRepositories = restricted repositories
gb.canAdmin can admin
src/com/gitblit/wicket/GitBlitWebSession.java
@@ -28,11 +28,11 @@
    private static final long serialVersionUID = 1L;
    protected TimeZone timezone = null;
    protected TimeZone timezone;
    private UserModel user = null;
    private String errorMessage = null;
    private UserModel user;
    private String errorMessage;
    public GitBlitWebSession(Request request) {
        super(request);
@@ -51,7 +51,7 @@
        if (user == null) {
            return false;
        }
        return user.canAdmin();
        return user.canAdmin;
    }
    public UserModel getUser() {
@@ -72,11 +72,11 @@
        }
        return timezone;
    }
    public void cacheErrorMessage(String message) {
        this.errorMessage = message;
    }
    public String clearErrorMessage() {
        String msg = errorMessage;
        errorMessage = null;
src/com/gitblit/wicket/LinkPanel.java
@@ -31,15 +31,18 @@
    private final IModel<String> labelModel;
    public LinkPanel(String wicketId, String linkCssClass, String label, Class<? extends WebPage> clazz) {
    public LinkPanel(String wicketId, String linkCssClass, String label,
            Class<? extends WebPage> clazz) {
        this(wicketId, linkCssClass, new Model<String>(label), clazz, null);
    }
    public LinkPanel(String wicketId, String linkCssClass, String label, Class<? extends WebPage> clazz, PageParameters parameters) {
    public LinkPanel(String wicketId, String linkCssClass, String label,
            Class<? extends WebPage> clazz, PageParameters parameters) {
        this(wicketId, linkCssClass, new Model<String>(label), clazz, parameters);
    }
    public LinkPanel(String wicketId, String linkCssClass, IModel<String> model, Class<? extends WebPage> clazz, PageParameters parameters) {
    public LinkPanel(String wicketId, String linkCssClass, IModel<String> model,
            Class<? extends WebPage> clazz, PageParameters parameters) {
        super(wicketId);
        this.labelModel = model;
        Link<Void> link = null;
src/com/gitblit/wicket/LoginPage.java
@@ -39,7 +39,7 @@
    public LoginPage(PageParameters params) {
        super(params);
        add(new Label("title", GitBlit.self().settings().getString(Keys.web.siteName, Constants.NAME)));
        add(new Label("title", GitBlit.getString(Keys.web.siteName, Constants.NAME)));
        add(new Label("name", Constants.NAME));
        Form<Void> loginForm = new LoginForm("loginForm");
@@ -69,13 +69,14 @@
            char[] password = LoginPage.this.password.getObject().toCharArray();
            UserModel user = GitBlit.self().authenticate(username, password);
            if (user == null)
            if (user == null) {
                error("Invalid username or password!");
            else
            } else {
                loginUser(user);
            }
        }
    }
    private void loginUser(UserModel user) {
        if (user != null) {
            // Set the user into the session
src/com/gitblit/wicket/RepositoryPage.java
@@ -21,6 +21,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
@@ -64,9 +65,9 @@
    protected final String repositoryName;
    protected final String objectId;
    private transient Repository r = null;
    private transient Repository r;
    private RepositoryModel m = null;
    private RepositoryModel m;
    private final Logger logger = LoggerFactory.getLogger(RepositoryPage.class);
@@ -96,13 +97,18 @@
        Repository r = getRepository();
        RepositoryModel model = getRepositoryModel();
        // standard page links
        add(new BookmarkablePageLink<Void>("summary", SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
        add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
        add(new BookmarkablePageLink<Void>("branches", BranchesPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
        add(new BookmarkablePageLink<Void>("tags", TagsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
        add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newRepositoryParameter(repositoryName)));
        add(new BookmarkablePageLink<Void>("summary", SummaryPage.class,
                WicketUtils.newRepositoryParameter(repositoryName)));
        add(new BookmarkablePageLink<Void>("log", LogPage.class,
                WicketUtils.newRepositoryParameter(repositoryName)));
        add(new BookmarkablePageLink<Void>("branches", BranchesPage.class,
                WicketUtils.newRepositoryParameter(repositoryName)));
        add(new BookmarkablePageLink<Void>("tags", TagsPage.class,
                WicketUtils.newRepositoryParameter(repositoryName)));
        add(new BookmarkablePageLink<Void>("tree", TreePage.class,
                WicketUtils.newRepositoryParameter(repositoryName)));
        // per-repository extra page links
        List<String> extraPageLinks = new ArrayList<String>();
@@ -118,15 +124,18 @@
        }
        final boolean showAdmin;
        if (GitBlit.self().settings().getBoolean(Keys.web.authenticateAdminPages, true)) {
            boolean allowAdmin = GitBlit.self().settings().getBoolean(Keys.web.allowAdministration, false);
        if (GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) {
            boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
            showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
        } else {
            showAdmin = GitBlit.self().settings().getBoolean(Keys.web.allowAdministration, false);
            showAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
        }
        // Conditionally add edit link
        if (showAdmin || GitBlitWebSession.get().isLoggedIn() && (model.owner != null && model.owner.equalsIgnoreCase(GitBlitWebSession.get().getUser().getUsername()))) {
        if (showAdmin
                || GitBlitWebSession.get().isLoggedIn()
                && (model.owner != null && model.owner.equalsIgnoreCase(GitBlitWebSession.get()
                        .getUser().username))) {
            extraPageLinks.add("edit");
        }
@@ -138,13 +147,17 @@
                String extra = item.getModelObject();
                if (extra.equals("tickets")) {
                    item.add(new Label("extraSeparator", " | "));
                    item.add(new LinkPanel("extraLink", null, getString("gb.tickets"), TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
                    item.add(new LinkPanel("extraLink", null, getString("gb.tickets"),
                            TicketsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
                } else if (extra.equals("docs")) {
                    item.add(new Label("extraSeparator", " | "));
                    item.add(new LinkPanel("extraLink", null, getString("gb.docs"), DocsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
                    item.add(new LinkPanel("extraLink", null, getString("gb.docs"), DocsPage.class,
                            WicketUtils.newRepositoryParameter(repositoryName)));
                } else if (extra.equals("edit")) {
                    item.add(new Label("extraSeparator", " | "));
                    item.add(new LinkPanel("extraLink", null, getString("gb.edit"), EditRepositoryPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
                    item.add(new LinkPanel("extraLink", null, getString("gb.edit"),
                            EditRepositoryPage.class, WicketUtils
                                    .newRepositoryParameter(repositoryName)));
                }
            }
        };
@@ -190,9 +203,10 @@
    protected RepositoryModel getRepositoryModel() {
        if (m == null) {
            RepositoryModel model = GitBlit.self().getRepositoryModel(GitBlitWebSession.get().getUser(), repositoryName);
            RepositoryModel model = GitBlit.self().getRepositoryModel(
                    GitBlitWebSession.get().getUser(), repositoryName);
            if (model == null) {
                error("Unauthorized access for repository " + repositoryName, true);
                error("Unauthorized access for repository " + repositoryName, true);
                return null;
            }
            m = model;
@@ -203,7 +217,8 @@
    protected RevCommit getCommit() {
        RevCommit commit = JGitUtils.getCommit(r, objectId);
        if (commit == null) {
            error(MessageFormat.format("Failed to find commit \"{0}\" in {1} for {2} page!", objectId, repositoryName, getPageName()), true);
            error(MessageFormat.format("Failed to find commit \"{0}\" in {1} for {2} page!",
                    objectId, repositoryName, getPageName()), true);
        }
        return commit;
    }
@@ -217,29 +232,32 @@
        if (substituteRegex) {
            Map<String, String> map = new HashMap<String, String>();
            // global regex keys
            if (GitBlit.self().settings().getBoolean(Keys.regex.global, false)) {
                for (String key : GitBlit.self().settings().getAllKeys(Keys.regex.global)) {
            if (GitBlit.getBoolean(Keys.regex.global, false)) {
                for (String key : GitBlit.getAllKeys(Keys.regex.global)) {
                    if (!key.equals(Keys.regex.global)) {
                        String subKey = key.substring(key.lastIndexOf('.') + 1);
                        map.put(subKey, GitBlit.self().settings().getString(key, ""));
                        map.put(subKey, GitBlit.getString(key, ""));
                    }
                }
            }
            // repository-specific regex keys
            List<String> keys = GitBlit.self().settings().getAllKeys(Keys.regex._ROOT + "." + repositoryName.toLowerCase());
            List<String> keys = GitBlit.getAllKeys(Keys.regex._ROOT + "."
                    + repositoryName.toLowerCase());
            for (String key : keys) {
                String subKey = key.substring(key.lastIndexOf('.') + 1);
                map.put(subKey, GitBlit.self().settings().getString(key, ""));
                map.put(subKey, GitBlit.getString(key, ""));
            }
            for (String key : map.keySet()) {
                String definition = map.get(key).trim();
            for (Entry<String, String> entry : map.entrySet()) {
                String definition = entry.getValue().trim();
                String[] chunks = definition.split("!!!");
                if (chunks.length == 2) {
                    html = html.replaceAll(chunks[0], chunks[1]);
                } else {
                    logger.warn(key + " improperly formatted.  Use !!! to separate match from replacement: " + definition);
                    logger.warn(entry.getKey()
                            + " improperly formatted.  Use !!! to separate match from replacement: "
                            + definition);
                }
            }
        }
@@ -248,9 +266,11 @@
    protected abstract String getPageName();
    protected Component createPersonPanel(String wicketId, PersonIdent identity, SearchType searchType) {
        boolean showEmail = GitBlit.self().settings().getBoolean(Keys.web.showEmailAddresses, false);
        if (!showEmail || StringUtils.isEmpty(identity.getName()) || StringUtils.isEmpty(identity.getEmailAddress())) {
    protected Component createPersonPanel(String wicketId, PersonIdent identity,
            SearchType searchType) {
        boolean showEmail = GitBlit.getBoolean(Keys.web.showEmailAddresses, false);
        if (!showEmail || StringUtils.isEmpty(identity.getName())
                || StringUtils.isEmpty(identity.getEmailAddress())) {
            String value = identity.getName();
            if (StringUtils.isEmpty(value)) {
                if (showEmail) {
@@ -260,17 +280,23 @@
                }
            }
            Fragment partial = new Fragment(wicketId, "partialPersonIdent", this);
            LinkPanel link = new LinkPanel("personName", "list", value, SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType));
            LinkPanel link = new LinkPanel("personName", "list", value, SearchPage.class,
                    WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType));
            setPersonSearchTooltip(link, value, searchType);
            partial.add(link);
            return partial;
        } else {
            Fragment fullPerson = new Fragment(wicketId, "fullPersonIdent", this);
            LinkPanel nameLink = new LinkPanel("personName", "list", identity.getName(), SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, identity.getName(), searchType));
            LinkPanel nameLink = new LinkPanel("personName", "list", identity.getName(),
                    SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId,
                            identity.getName(), searchType));
            setPersonSearchTooltip(nameLink, identity.getName(), searchType);
            fullPerson.add(nameLink);
            LinkPanel addressLink = new LinkPanel("personAddress", "list", "<" + identity.getEmailAddress() + ">", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, identity.getEmailAddress(), searchType));
            LinkPanel addressLink = new LinkPanel("personAddress", "list", "<"
                    + identity.getEmailAddress() + ">", SearchPage.class,
                    WicketUtils.newSearchParameter(repositoryName, objectId,
                            identity.getEmailAddress(), searchType));
            setPersonSearchTooltip(addressLink, identity.getEmailAddress(), searchType);
            fullPerson.add(addressLink);
            return fullPerson;
@@ -331,7 +357,7 @@
        return WicketUtils.newPathParameter(repositoryName, objectId, path);
    }
    class SearchForm extends StatelessForm<Void> {
    private static class SearchForm extends StatelessForm<Void> {
        private static final long serialVersionUID = 1L;
        private final String repositoryName;
@@ -343,9 +369,10 @@
        public SearchForm(String id, String repositoryName) {
            super(id);
            this.repositoryName = repositoryName;
            DropDownChoice<SearchType> searchType = new DropDownChoice<SearchType>("searchType", Arrays.asList(SearchType.values()));
            DropDownChoice<SearchType> searchType = new DropDownChoice<SearchType>("searchType",
                    Arrays.asList(SearchType.values()));
            searchType.setModel(searchTypeModel);
            add(searchType.setVisible(GitBlit.self().settings().getBoolean(Keys.web.showSearchTypeSelection, false)));
            add(searchType.setVisible(GitBlit.getBoolean(Keys.web.showSearchTypeSelection, false)));
            TextField<String> searchBox = new TextField<String>("searchBox", searchBoxModel);
            add(searchBox);
        }
@@ -363,11 +390,13 @@
            for (SearchType type : SearchType.values()) {
                if (searchString.toLowerCase().startsWith(type.name().toLowerCase() + ":")) {
                    searchType = type;
                    searchString = searchString.substring(type.name().toLowerCase().length() + 1).trim();
                    searchString = searchString.substring(type.name().toLowerCase().length() + 1)
                            .trim();
                    break;
                }
            }
            setResponsePage(SearchPage.class, WicketUtils.newSearchParameter(repositoryName, null, searchString, searchType));
            setResponsePage(SearchPage.class,
                    WicketUtils.newSearchParameter(repositoryName, null, searchString, searchType));
        }
    }
}
src/com/gitblit/wicket/WicketUtils.java
@@ -117,7 +117,8 @@
            return newImage(wicketId, "file_h_16x16.png");
        } else if (filename.endsWith(".sln")) {
            return newImage(wicketId, "file_vs_16x16.png");
        } else if (filename.endsWith(".csv") || filename.endsWith(".xls") || filename.endsWith(".xlsx")) {
        } else if (filename.endsWith(".csv") || filename.endsWith(".xls")
                || filename.endsWith(".xlsx")) {
            return newImage(wicketId, "file_excel_16x16.png");
        } else if (filename.endsWith(".doc") || filename.endsWith(".docx")) {
            return newImage(wicketId, "file_word_16x16.png");
@@ -135,7 +136,7 @@
            return newImage(wicketId, "file_settings_16x16.png");
        }
        List<String> mdExtensions = GitBlit.self().settings().getStrings(Keys.web.markdownExtensions);
        List<String> mdExtensions = GitBlit.getStrings(Keys.web.markdownExtensions);
        for (String ext : mdExtensions) {
            if (filename.endsWith('.' + ext.toLowerCase())) {
                return newImage(wicketId, "file_world_16x16.png");
@@ -183,40 +184,50 @@
        return new PageParameters("r=" + repositoryName + ",h=" + objectId);
    }
    public static PageParameters newPathParameter(String repositoryName, String objectId, String path) {
    public static PageParameters newPathParameter(String repositoryName, String objectId,
            String path) {
        if (StringUtils.isEmpty(path)) {
            return newObjectParameter(repositoryName, objectId);
        }
        return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",f=" + path);
    }
    public static PageParameters newLogPageParameter(String repositoryName, String objectId, int pageNumber) {
    public static PageParameters newLogPageParameter(String repositoryName, String objectId,
            int pageNumber) {
        if (pageNumber <= 1) {
            return newObjectParameter(repositoryName, objectId);
        }
        return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",page=" + pageNumber);
    }
    public static PageParameters newHistoryPageParameter(String repositoryName, String objectId, String path, int pageNumber) {
    public static PageParameters newHistoryPageParameter(String repositoryName, String objectId,
            String path, int pageNumber) {
        if (pageNumber <= 1) {
            return newObjectParameter(repositoryName, objectId);
        }
        return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",f=" + path + ",page=" + pageNumber);
        return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",f=" + path
                + ",page=" + pageNumber);
    }
    public static PageParameters newBlobDiffParameter(String repositoryName, String baseCommitId, String commitId, String path) {
        return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",f=" + path + ",hb=" + baseCommitId);
    public static PageParameters newBlobDiffParameter(String repositoryName, String baseCommitId,
            String commitId, String path) {
        return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",f=" + path + ",hb="
                + baseCommitId);
    }
    public static PageParameters newSearchParameter(String repositoryName, String commitId, String search, SearchType type) {
    public static PageParameters newSearchParameter(String repositoryName, String commitId,
            String search, SearchType type) {
        if (StringUtils.isEmpty(commitId)) {
            return new PageParameters("r=" + repositoryName + ",s=" + search + ",st=" + type.name());
        }
        return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",s=" + search + ",st=" + type.name());
        return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",s=" + search
                + ",st=" + type.name());
    }
    public static PageParameters newSearchParameter(String repositoryName, String commitId, String search, SearchType type, int pageNumber) {
        return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",s=" + search + ",st=" + type.name() + ",page=" + pageNumber);
    public static PageParameters newSearchParameter(String repositoryName, String commitId,
            String search, SearchType type, int pageNumber) {
        return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",s=" + search
                + ",st=" + type.name() + ",page=" + pageNumber);
    }
    public static String getRepositoryName(PageParameters params) {
@@ -244,21 +255,23 @@
    }
    public static int getPage(PageParameters params) {
        return params.getInt("page", 1); // index from 1
        // index from 1
        return params.getInt("page", 1);
    }
    public static String getUsername(PageParameters params) {
        return params.getString("user", "");
    }
    public static Label createDateLabel(String wicketId, Date date, TimeZone timeZone) {
        DateFormat df = new SimpleDateFormat(GitBlit.self().settings().getString(Keys.web.datestampShortFormat, "MM/dd/yy"));
        String format = GitBlit.getString(Keys.web.datestampShortFormat, "MM/dd/yy");
        DateFormat df = new SimpleDateFormat(format);
        if (timeZone != null) {
            df.setTimeZone(timeZone);
        }
        String dateString = df.format(date);
        String title = TimeUtils.timeAgo(date);
        if ((System.currentTimeMillis() - date.getTime()) < 10 * 24 * 60 * 60 * 1000l) {
        if ((System.currentTimeMillis() - date.getTime()) < 10 * 24 * 60 * 60 * 1000L) {
            String tmp = dateString;
            dateString = title;
            title = tmp;
@@ -270,7 +283,9 @@
    }
    public static Label createTimestampLabel(String wicketId, Date date, TimeZone timeZone) {
        DateFormat df = new SimpleDateFormat(GitBlit.self().settings().getString(Keys.web.datetimestampLongFormat, "EEEE, MMMM d, yyyy h:mm a z"));
        String format = GitBlit.getString(Keys.web.datetimestampLongFormat,
                "EEEE, MMMM d, yyyy h:mm a z");
        DateFormat df = new SimpleDateFormat(format);
        if (timeZone != null) {
            df.setTimeZone(timeZone);
        }
src/com/gitblit/wicket/models/PathModel.java
@@ -49,9 +49,23 @@
        if (basePath.lastIndexOf('/') > -1) {
            parentPath = basePath.substring(0, basePath.lastIndexOf('/'));
        }
        PathModel model = new PathModel("..", parentPath, 0, 0040000, commitId);
        PathModel model = new PathModel("..", parentPath, 0, 40000, commitId);
        model.isParentPath = true;
        return model;
    }
    @Override
    public int hashCode() {
        return commitId.hashCode() + path.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        if (o instanceof PathModel) {
            PathModel other = (PathModel) o;
            return this.path.equals(other.path);
        }
        return super.equals(o);
    }
    @Override
@@ -69,14 +83,25 @@
    }
    public static class PathChangeModel extends PathModel {
        private static final long serialVersionUID = 1L;
        public final ChangeType changeType;
        public PathChangeModel(String name, String path, long size, int mode, String commitId, ChangeType type) {
        public PathChangeModel(String name, String path, long size, int mode, String commitId,
                ChangeType type) {
            super(name, path, size, mode, commitId);
            this.changeType = type;
        }
        @Override
        public int hashCode() {
            return super.hashCode();
        }
        @Override
        public boolean equals(Object o) {
            return super.equals(o);
        }
    }
}
src/com/gitblit/wicket/models/RefModel.java
@@ -27,9 +27,9 @@
public class RefModel implements Serializable, Comparable<RefModel> {
    private static final long serialVersionUID = 1L;
    final String displayName;
    transient Ref ref;
    final RevCommit commit;
    public final String displayName;
    public final RevCommit commit;
    public transient Ref ref;
    public RefModel(String displayName, Ref ref, RevCommit commit) {
        this.displayName = displayName;
@@ -41,16 +41,8 @@
        return JGitUtils.getCommitDate(commit);
    }
    public String getDisplayName() {
        return displayName;
    }
    public String getName() {
        return ref.getName();
    }
    public RevCommit getCommit() {
        return commit;
    }
    public ObjectId getCommitId() {
@@ -71,6 +63,20 @@
    }
    @Override
    public int hashCode() {
        return getCommitId().hashCode() + getName().hashCode();
    }
    @Override
    public boolean equals(Object o) {
        if (o instanceof RefModel) {
            RefModel other = (RefModel) o;
            return getName().equals(other.getName());
        }
        return super.equals(o);
    }
    @Override
    public int compareTo(RefModel o) {
        return getDate().compareTo(o.getDate());
    }
src/com/gitblit/wicket/models/RepositoryModel.java
@@ -23,6 +23,8 @@
public class RepositoryModel implements Serializable {
    private static final long serialVersionUID = 1L;
    // field names are reflectively mapped in EditRepository page
    public String name;
    public String description;
    public String owner;
@@ -49,7 +51,7 @@
        this.lastChange = lastchange;
        this.accessRestriction = AccessRestrictionType.NONE;
    }
    @Override
    public String toString() {
        return name;
src/com/gitblit/wicket/models/TicketModel.java
@@ -51,7 +51,7 @@
        String[] chunks = name.split("_");
        if (chunks.length == 3) {
            date = new Date(Long.parseLong(chunks[0]) * 1000l);
            date = new Date(Long.parseLong(chunks[0]) * 1000L);
            title = chunks[1].replace('-', ' ');
        }
    }
@@ -71,9 +71,23 @@
        public Comment(String filename, String content) throws ParseException {
            String[] chunks = filename.split("_", -1);
            this.date = new Date(Long.parseLong(chunks[1]) * 1000l);
            this.date = new Date(Long.parseLong(chunks[1]) * 1000L);
            this.author = chunks[2];
            this.text = content;
        }
        @Override
        public int hashCode() {
            return text.hashCode();
        }
        @Override
        public boolean equals(Object o) {
            if (o instanceof Comment) {
                Comment other = (Comment) o;
                return text.equals(other.text);
            }
            return super.equals(o);
        }
        @Override
@@ -83,6 +97,20 @@
    }
    @Override
    public int hashCode() {
        return id.hashCode();
    }
    @Override
    public boolean equals(Object o) {
        if (o instanceof TicketModel) {
            TicketModel other = (TicketModel) o;
            return id.equals(other.id);
        }
        return super.equals(o);
    }
    @Override
    public int compareTo(TicketModel o) {
        return date.compareTo(o.date);
    }
src/com/gitblit/wicket/models/UserModel.java
@@ -23,33 +23,14 @@
    private static final long serialVersionUID = 1L;
    private String username;
    private String password;
    private boolean canAdmin = false;
    private List<String> repositories = new ArrayList<String>();
    // field names are reflectively mapped in EditUser page
    public String username;
    public String password;
    public boolean canAdmin;
    public final List<String> repositories = new ArrayList<String>();
    public UserModel(String username) {
        this.username = username;
    }
    public String getUsername() {
        return username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public void canAdmin(boolean value) {
        canAdmin = value;
    }
    public boolean canAdmin() {
        return canAdmin;
    }
    public boolean canAccessRepository(String repositoryName) {
@@ -63,10 +44,6 @@
    public void addRepository(String name) {
        repositories.add(name.toLowerCase());
    }
    public List<String> getRepositories() {
        return repositories;
    }
    @Override
src/com/gitblit/wicket/pages/BlobDiffPage.java
@@ -41,27 +41,34 @@
        Repository r = getRepository();
        RevCommit commit = getCommit();
        DiffOutputType diffType = DiffOutputType.forName(GitBlit.self().settings().getString(Keys.web.diffStyle, DiffOutputType.GITBLIT.name()));
        DiffOutputType diffType = DiffOutputType.forName(GitBlit.getString(Keys.web.diffStyle,
                DiffOutputType.GITBLIT.name()));
        String diff;
        if (StringUtils.isEmpty(baseObjectId)) {
            // use first parent
            diff = JGitUtils.getCommitDiff(r, commit, blobPath, diffType);
            add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
            add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
                    WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
        } else {
            // base commit specified
            RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId);
            diff = JGitUtils.getCommitDiff(r, baseCommit, commit, blobPath, diffType);
            add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class, WicketUtils.newBlobDiffParameter(repositoryName, baseObjectId, objectId, blobPath)));
            add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
                    WicketUtils.newBlobDiffParameter(repositoryName, baseObjectId, objectId,
                            blobPath)));
        }
        add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
        add(new BookmarkablePageLink<Void>("commitDiffLink", CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
        add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class,
                WicketUtils.newObjectParameter(repositoryName, objectId)));
        add(new BookmarkablePageLink<Void>("commitDiffLink", CommitDiffPage.class,
                WicketUtils.newObjectParameter(repositoryName, objectId)));
        // diff page links
        add(new Label("blameLink", getString("gb.blame")));
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
                WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
        add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
src/com/gitblit/wicket/pages/BlobPage.java
@@ -45,24 +45,27 @@
        if (blobPath.lastIndexOf('.') > -1) {
            extension = blobPath.substring(blobPath.lastIndexOf('.') + 1).toLowerCase();
        }
        // see if we should redirect to the markdown page
        for (String ext : GitBlit.self().settings().getStrings(Keys.web.markdownExtensions)) {
        for (String ext : GitBlit.getStrings(Keys.web.markdownExtensions)) {
            if (ext.equals(extension)) {
                setResponsePage(MarkdownPage.class, params);
                return;
            }
        }
        // standard blob view
        Repository r = getRepository();
        RevCommit commit = getCommit();
        // blob page links
        add(new Label("blameLink", getString("gb.blame")));
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
        add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
        add(new BookmarkablePageLink<Void>("headLink", BlobPage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, blobPath)));
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
                WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
        add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(
                repositoryName, objectId, blobPath)));
        add(new BookmarkablePageLink<Void>("headLink", BlobPage.class,
                WicketUtils.newPathParameter(repositoryName, Constants.HEAD, blobPath)));
        add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
@@ -70,13 +73,13 @@
        // Map the extensions to types
        Map<String, Integer> map = new HashMap<String, Integer>();
        for (String ext : GitBlit.self().settings().getStrings(Keys.web.prettyPrintExtensions)) {
        for (String ext : GitBlit.getStrings(Keys.web.prettyPrintExtensions)) {
            map.put(ext.toLowerCase(), 1);
        }
        for (String ext : GitBlit.self().settings().getStrings(Keys.web.imageExtensions)) {
        for (String ext : GitBlit.getStrings(Keys.web.imageExtensions)) {
            map.put(ext.toLowerCase(), 2);
        }
        for (String ext : GitBlit.self().settings().getStrings(Keys.web.binaryExtensions)) {
        for (String ext : GitBlit.getStrings(Keys.web.binaryExtensions)) {
            map.put(ext.toLowerCase(), 3);
        }
@@ -108,7 +111,8 @@
            add(c);
        } else {
            // plain text
            Label blobLabel = new Label("blobText", JGitUtils.getRawContentAsString(r, commit, blobPath));
            Label blobLabel = new Label("blobText", JGitUtils.getRawContentAsString(r, commit,
                    blobPath));
            WicketUtils.setCssClass(blobLabel, "plainprint");
            add(blobLabel);
        }
src/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -45,7 +45,8 @@
        Repository r = getRepository();
        RevCommit commit = getCommit();
        DiffOutputType diffType = DiffOutputType.forName(GitBlit.self().settings().getString(Keys.web.diffStyle, DiffOutputType.GITBLIT.name()));
        DiffOutputType diffType = DiffOutputType.forName(GitBlit.getString(Keys.web.diffStyle,
                DiffOutputType.GITBLIT.name()));
        String diff = JGitUtils.getCommitDiff(r, commit, diffType);
        List<String> parents = new ArrayList<String>();
@@ -59,10 +60,13 @@
        if (parents.size() == 0) {
            add(new Label("parentLink", "none"));
        } else {
            add(new LinkPanel("parentLink", null, parents.get(0).substring(0, 8), CommitDiffPage.class, newCommitParameter(parents.get(0))));
            add(new LinkPanel("parentLink", null, parents.get(0).substring(0, 8),
                    CommitDiffPage.class, newCommitParameter(parents.get(0))));
        }
        add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
        add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
        add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
                WicketUtils.newObjectParameter(repositoryName, objectId)));
        add(new BookmarkablePageLink<Void>("commitLink", CommitPage.class,
                WicketUtils.newObjectParameter(repositoryName, objectId)));
        add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
@@ -72,7 +76,7 @@
        ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(paths);
        DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<PathChangeModel> item) {
                final PathChangeModel entry = item.getModelObject();
@@ -82,15 +86,20 @@
                item.add(changeType);
                if (entry.isTree()) {
                    item.add(new LinkPanel("pathName", null, entry.path, TreePage.class, newPathParameter(entry.path)));
                    item.add(new LinkPanel("pathName", null, entry.path, TreePage.class,
                            newPathParameter(entry.path)));
                } else {
                    item.add(new LinkPanel("pathName", "list", entry.path, BlobPage.class, newPathParameter(entry.path)));
                    item.add(new LinkPanel("pathName", "list", entry.path, BlobPage.class,
                            newPathParameter(entry.path)));
                }
                item.add(new BookmarkablePageLink<Void>("patch", PatchPage.class, newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("patch", PatchPage.class,
                        newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("view", BlobPage.class,
                        newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("blame", BlobPage.class).setEnabled(false));
                item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
                        newPathParameter(entry.path)));
                WicketUtils.setAlternatingBackground(item, counter);
                counter++;
src/com/gitblit/wicket/pages/CommitPage.java
@@ -61,10 +61,14 @@
            add(new Label("parentLink", "none"));
            add(new Label("commitdiffLink", getString("gb.commitdiff")));
        } else {
            add(new LinkPanel("parentLink", null, parents.get(0).substring(0, 8), CommitPage.class, newCommitParameter(parents.get(0))));
            add(new LinkPanel("commitdiffLink", null, new StringResourceModel("gb.commitdiff", this, null), CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
            add(new LinkPanel("parentLink", null, parents.get(0).substring(0, 8), CommitPage.class,
                    newCommitParameter(parents.get(0))));
            add(new LinkPanel("commitdiffLink", null, new StringResourceModel("gb.commitdiff",
                    this, null), CommitDiffPage.class, WicketUtils.newObjectParameter(
                    repositoryName, objectId)));
        }
        add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)));
        add(new BookmarkablePageLink<Void>("patchLink", PatchPage.class,
                WicketUtils.newObjectParameter(repositoryName, objectId)));
        add(new CommitHeaderPanel("commitHeader", repositoryName, c));
@@ -72,17 +76,22 @@
        // author
        add(createPersonPanel("commitAuthor", c.getAuthorIdent(), SearchType.AUTHOR));
        add(WicketUtils.createTimestampLabel("commitAuthorDate", c.getAuthorIdent().getWhen(), getTimeZone()));
        add(WicketUtils.createTimestampLabel("commitAuthorDate", c.getAuthorIdent().getWhen(),
                getTimeZone()));
        // committer
        add(createPersonPanel("commitCommitter", c.getCommitterIdent(), SearchType.COMMITTER));
        add(WicketUtils.createTimestampLabel("commitCommitterDate", c.getCommitterIdent().getWhen(), getTimeZone()));
        add(WicketUtils.createTimestampLabel("commitCommitterDate",
                c.getCommitterIdent().getWhen(), getTimeZone()));
        add(new Label("commitId", c.getName()));
        add(new LinkPanel("commitTree", "list", c.getTree().getName(), TreePage.class, newCommitParameter()));
        add(new LinkPanel("commitTree", "list", c.getTree().getName(), TreePage.class,
                newCommitParameter()));
        add(new BookmarkablePageLink<Void>("treeLink", TreePage.class, newCommitParameter()));
        add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, null)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));
        add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest()
                .getRelativePathPrefixToContextRoot(), repositoryName, objectId, null))
                .setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true)));
        // Parent Commits
        ListDataProvider<String> parentsDp = new ListDataProvider<String>(parents);
@@ -91,9 +100,12 @@
            public void populateItem(final Item<String> item) {
                String entry = item.getModelObject();
                item.add(new LinkPanel("commitParent", "list", entry, CommitPage.class, newCommitParameter(entry)));
                item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, newCommitParameter(entry)));
                item.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class, newCommitParameter(entry)));
                item.add(new LinkPanel("commitParent", "list", entry, CommitPage.class,
                        newCommitParameter(entry)));
                item.add(new BookmarkablePageLink<Void>("view", CommitPage.class,
                        newCommitParameter(entry)));
                item.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class,
                        newCommitParameter(entry)));
            }
        };
        add(parentsView);
@@ -101,12 +113,12 @@
        addFullText("fullMessage", c.getFullMessage(), true);
        // changed paths list
        List<PathChangeModel> paths = JGitUtils.getFilesInCommit(r, c);
        List<PathChangeModel> paths = JGitUtils.getFilesInCommit(r, c);
        add(new CommitLegendPanel("commitLegend", paths));
        ListDataProvider<PathChangeModel> pathsDp = new ListDataProvider<PathChangeModel>(paths);
        DataView<PathChangeModel> pathsView = new DataView<PathChangeModel>("changedPath", pathsDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<PathChangeModel> item) {
                final PathChangeModel entry = item.getModelObject();
@@ -115,15 +127,20 @@
                setChangeTypeTooltip(changeType, entry.changeType);
                item.add(changeType);
                if (entry.isTree()) {
                    item.add(new LinkPanel("pathName", null, entry.path, TreePage.class, newPathParameter(entry.path)));
                    item.add(new LinkPanel("pathName", null, entry.path, TreePage.class,
                            newPathParameter(entry.path)));
                } else {
                    item.add(new LinkPanel("pathName", "list", entry.path, BlobPage.class, newPathParameter(entry.path)));
                    item.add(new LinkPanel("pathName", "list", entry.path, BlobPage.class,
                            newPathParameter(entry.path)));
                }
                item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class, newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("diff", BlobDiffPage.class,
                        newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("view", BlobPage.class,
                        newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("blame", BlobPage.class).setEnabled(false));
                item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, newPathParameter(entry.path)));
                item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
                        newPathParameter(entry.path)));
                WicketUtils.setAlternatingBackground(item, counter);
                counter++;
src/com/gitblit/wicket/pages/DocsPage.java
@@ -40,30 +40,34 @@
        super(params);
        Repository r = getRepository();
        List<String> extensions = GitBlit.self().settings().getStrings(Keys.web.markdownExtensions);
        List<String> extensions = GitBlit.getStrings(Keys.web.markdownExtensions);
        List<PathModel> paths = JGitUtils.getDocuments(r, extensions);
        final ByteFormat byteFormat = new ByteFormat();
        add(new Label("header", getString("gb.docs")));
        // documents list
        ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths);
        DataView<PathModel> pathsView = new DataView<PathModel>("document", pathsDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<PathModel> item) {
                PathModel entry = item.getModelObject();
                item.add(WicketUtils.newImage("docIcon", "file_world_16x16.png"));
                item.add(new Label("docSize", byteFormat.format(entry.size)));
                item.add(new LinkPanel("docName", "list", entry.name, BlobPage.class, newPathParameter(entry.path)));
                item.add(new LinkPanel("docName", "list", entry.name, BlobPage.class,
                        newPathParameter(entry.path)));
                // links
                item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
                        .newPathParameter(repositoryName, entry.commitId, entry.path)));
                item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
                        .newPathParameter(repositoryName, entry.commitId, entry.path)));
                item.add(new BookmarkablePageLink<Void>("blame", BlobPage.class).setEnabled(false));
                item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
                        .newPathParameter(repositoryName, entry.commitId, entry.path)));
                WicketUtils.setAlternatingBackground(item, counter);
                counter++;
            }
src/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -51,8 +51,8 @@
    private final boolean isCreate;
    private boolean isAdmin = false;
    private boolean isAdmin;
    public EditRepositoryPage() {
        // create constructor
        super();
@@ -72,7 +72,7 @@
    protected void setupPage(final RepositoryModel repositoryModel) {
        // ensure this user can create or edit this repository
        checkPermissions(repositoryModel);
        List<String> repositoryUsers = new ArrayList<String>();
        if (isCreate) {
            super.setupPage("", getString("gb.newRepository"));
@@ -85,8 +85,11 @@
        }
        final String oldName = repositoryModel.name;
        final Palette<String> usersPalette = new Palette<String>("users", new ListModel<String>(repositoryUsers), new CollectionModel<String>(GitBlit.self().getAllUsernames()), new ChoiceRenderer<String>("", ""), 10, false);
        CompoundPropertyModel<RepositoryModel> model = new CompoundPropertyModel<RepositoryModel>(repositoryModel);
        final Palette<String> usersPalette = new Palette<String>("users", new ListModel<String>(
                repositoryUsers), new CollectionModel<String>(GitBlit.self().getAllUsernames()),
                new ChoiceRenderer<String>("", ""), 10, false);
        CompoundPropertyModel<RepositoryModel> model = new CompoundPropertyModel<RepositoryModel>(
                repositoryModel);
        Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", model) {
            private static final long serialVersionUID = 1L;
@@ -112,7 +115,8 @@
                                ok |= c == vc;
                            }
                            if (!ok) {
                                error(MessageFormat.format("Illegal character ''{0}'' in repository name!", c));
                                error(MessageFormat.format(
                                        "Illegal character ''{0}'' in repository name!", c));
                                return;
                            }
                        }
@@ -135,7 +139,8 @@
                            repositoryUsers.add(users.next());
                        }
                        // ensure the owner is added to the user list
                        if (repositoryModel.owner != null && !repositoryUsers.contains(repositoryModel.owner)) {
                        if (repositoryModel.owner != null
                                && !repositoryUsers.contains(repositoryModel.owner)) {
                            repositoryUsers.add(repositoryModel.owner);
                        }
                        GitBlit.self().setRepositoryUsers(repositoryModel, repositoryUsers);
@@ -152,8 +157,10 @@
        // field names reflective match RepositoryModel fields
        form.add(new TextField<String>("name").setEnabled(isCreate || isAdmin));
        form.add(new TextField<String>("description"));
        form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames()).setEnabled(GitBlitWebSession.get().canAdmin()));
        form.add(new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays.asList(AccessRestrictionType.values()), new AccessRestrictionRenderer()));
        form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames())
                .setEnabled(GitBlitWebSession.get().canAdmin()));
        form.add(new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays
                .asList(AccessRestrictionType.values()), new AccessRestrictionRenderer()));
        form.add(new CheckBox("isFrozen"));
        form.add(new CheckBox("useTickets"));
        form.add(new CheckBox("useDocs"));
@@ -162,7 +169,7 @@
        add(form);
    }
    /**
     * Unfortunately must repeat part of AuthorizaitonStrategy here because that
     * mechanism does not take PageParameters into consideration, only page
@@ -171,8 +178,8 @@
     * Repository Owners should be able to edit their repository.
     */
    private void checkPermissions(RepositoryModel model) {
        boolean authenticateAdmin = GitBlit.self().settings().getBoolean(Keys.web.authenticateAdminPages, true);
        boolean allowAdmin = GitBlit.self().settings().getBoolean(Keys.web.allowAdministration, true);
        boolean authenticateAdmin = GitBlit.getBoolean(Keys.web.authenticateAdminPages, true);
        boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, true);
        GitBlitWebSession session = GitBlitWebSession.get();
        UserModel user = session.getUser();
@@ -185,22 +192,22 @@
                }
                if (isCreate) {
                    // Create Repository
                    if (!user.canAdmin()) {
                    if (!user.canAdmin) {
                        // Only Administrators May Create
                        error("Only an administrator may create a repository", true);
                    }
                } else {
                    // Edit Repository
                    if (user.canAdmin()) {
                    if (user.canAdmin) {
                        // Admins can edit everything
                        isAdmin = true;
                        return;
                    } else {
                        if (!model.owner.equalsIgnoreCase(user.getUsername())) {
                        if (!model.owner.equalsIgnoreCase(user.username)) {
                            // User is not an Admin nor Owner
                            error("Only an administrator or the owner may edit a repository", true);
                        }
                    }
                    }
                }
            }
        } else {
src/com/gitblit/wicket/pages/EditUserPage.java
@@ -72,7 +72,8 @@
        } else {
            super.setupPage("", getString("gb.edit"));
        }
        final Model<String> confirmPassword = new Model<String>(StringUtils.isEmpty(userModel.getPassword()) ? "" : userModel.getPassword());
        final Model<String> confirmPassword = new Model<String>(
                StringUtils.isEmpty(userModel.password) ? "" : userModel.password);
        CompoundPropertyModel<UserModel> model = new CompoundPropertyModel<UserModel>(userModel);
        List<String> repos = new ArrayList<String>();
@@ -82,8 +83,10 @@
                repos.add(repo);
            }
        }
        final String oldName = userModel.getUsername();
        final Palette<String> repositories = new Palette<String>("repositories", new ListModel<String>(userModel.getRepositories()), new CollectionModel<String>(repos), new ChoiceRenderer<String>("", ""), 10, false);
        final String oldName = userModel.username;
        final Palette<String> repositories = new Palette<String>("repositories",
                new ListModel<String>(userModel.repositories), new CollectionModel<String>(repos),
                new ChoiceRenderer<String>("", ""), 10, false);
        Form<UserModel> form = new Form<UserModel>("editForm", model) {
            private static final long serialVersionUID = 1L;
@@ -95,7 +98,7 @@
             */
            @Override
            protected void onSubmit() {
                String username = userModel.getUsername();
                String username = userModel.username;
                if (StringUtils.isEmpty(username)) {
                    error("Please enter a username!");
                    return;
@@ -107,28 +110,31 @@
                        return;
                    }
                }
                if (!userModel.getPassword().equals(confirmPassword.getObject())) {
                if (!userModel.password.equals(confirmPassword.getObject())) {
                    error("Passwords do not match!");
                    return;
                }
                String password = userModel.getPassword();
                if (!password.toUpperCase().startsWith(Crypt.__TYPE) && !password.toUpperCase().startsWith(MD5.__TYPE)) {
                String password = userModel.password;
                if (!password.toUpperCase().startsWith(Crypt.__TYPE)
                        && !password.toUpperCase().startsWith(MD5.__TYPE)) {
                    // This is a plain text password.
                    // Check length.
                    int minLength = GitBlit.self().settings().getInteger(Keys.realm.minPasswordLength, 5);
                    int minLength = GitBlit.getInteger(Keys.realm.minPasswordLength, 5);
                    if (minLength < 4) {
                        minLength = 4;
                    }
                    if (password.trim().length() < minLength) {
                        error(MessageFormat.format("Password is too short. Minimum length is {0} characters.", minLength));
                        error(MessageFormat.format(
                                "Password is too short. Minimum length is {0} characters.",
                                minLength));
                        return;
                    }
                    // Optionally store the password MD5 digest.
                    String type = GitBlit.self().settings().getString(Keys.realm.passwordStorage, "md5");
                    String type = GitBlit.getString(Keys.realm.passwordStorage, "md5");
                    if (type.equalsIgnoreCase("md5")) {
                        // store MD5 digest of password
                        userModel.setPassword(MD5.digest(userModel.getPassword()));
                        userModel.password = MD5.digest(userModel.password);
                    }
                }
@@ -147,7 +153,8 @@
                setRedirect(false);
                if (isCreate) {
                    // create another user
                    info(MessageFormat.format("New user ''{0}'' successfully created.", userModel.getUsername()));
                    info(MessageFormat.format("New user ''{0}'' successfully created.",
                            userModel.username));
                    setResponsePage(EditUserPage.class);
                } else {
                    // back to home
@@ -161,7 +168,8 @@
        PasswordTextField passwordField = new PasswordTextField("password");
        passwordField.setResetPassword(false);
        form.add(passwordField);
        PasswordTextField confirmPasswordField = new PasswordTextField("confirmPassword", confirmPassword);
        PasswordTextField confirmPasswordField = new PasswordTextField("confirmPassword",
                confirmPassword);
        confirmPasswordField.setResetPassword(false);
        form.add(confirmPasswordField);
        form.add(new CheckBox("canAdmin"));
src/com/gitblit/wicket/pages/HistoryPage.java
@@ -32,17 +32,30 @@
        int prevPage = Math.max(0, pageNumber - 1);
        int nextPage = pageNumber + 1;
        HistoryPanel history = new HistoryPanel("historyPanel", repositoryName, objectId, path, getRepository(), -1, pageNumber - 1);
        HistoryPanel history = new HistoryPanel("historyPanel", repositoryName, objectId, path,
                getRepository(), -1, pageNumber - 1);
        boolean hasMore = history.hasMore();
        add(history);
        add(new BookmarkablePageLink<Void>("firstPageTop", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, path)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageTop", HistoryPage.class, WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, prevPage)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageTop", HistoryPage.class, WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, nextPage)).setEnabled(hasMore));
        add(new BookmarkablePageLink<Void>("firstPageTop", HistoryPage.class,
                WicketUtils.newPathParameter(repositoryName, objectId, path))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageTop", HistoryPage.class,
                WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, prevPage))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageTop", HistoryPage.class,
                WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, nextPage))
                .setEnabled(hasMore));
        add(new BookmarkablePageLink<Void>("firstPageBottom", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, path)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageBottom", HistoryPage.class, WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, prevPage)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageBottom", HistoryPage.class, WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, nextPage)).setEnabled(hasMore));
        add(new BookmarkablePageLink<Void>("firstPageBottom", HistoryPage.class,
                WicketUtils.newPathParameter(repositoryName, objectId, path))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageBottom", HistoryPage.class,
                WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, prevPage))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageBottom", HistoryPage.class,
                WicketUtils.newHistoryPageParameter(repositoryName, objectId, path, nextPage))
                .setEnabled(hasMore));
    }
src/com/gitblit/wicket/pages/LogPage.java
@@ -30,17 +30,30 @@
        int pageNumber = WicketUtils.getPage(params);
        int prevPage = Math.max(0, pageNumber - 1);
        int nextPage = pageNumber + 1;
        LogPanel logPanel = new LogPanel("logPanel", repositoryName, objectId, getRepository(), -1, pageNumber - 1);
        LogPanel logPanel = new LogPanel("logPanel", repositoryName, objectId, getRepository(), -1,
                pageNumber - 1);
        boolean hasMore = logPanel.hasMore();
        add(logPanel);
        add(new BookmarkablePageLink<Void>("firstPageTop", LogPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageTop", LogPage.class, WicketUtils.newLogPageParameter(repositoryName, objectId, prevPage)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageTop", LogPage.class, WicketUtils.newLogPageParameter(repositoryName, objectId, nextPage)).setEnabled(hasMore));
        add(new BookmarkablePageLink<Void>("firstPageTop", LogPage.class,
                WicketUtils.newObjectParameter(repositoryName, objectId))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageTop", LogPage.class,
                WicketUtils.newLogPageParameter(repositoryName, objectId, prevPage))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageTop", LogPage.class,
                WicketUtils.newLogPageParameter(repositoryName, objectId, nextPage))
                .setEnabled(hasMore));
        add(new BookmarkablePageLink<Void>("firstPageBottom", LogPage.class, WicketUtils.newObjectParameter(repositoryName, objectId)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageBottom", LogPage.class, WicketUtils.newLogPageParameter(repositoryName, objectId, prevPage)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageBottom", LogPage.class, WicketUtils.newLogPageParameter(repositoryName, objectId, nextPage)).setEnabled(hasMore));
        add(new BookmarkablePageLink<Void>("firstPageBottom", LogPage.class,
                WicketUtils.newObjectParameter(repositoryName, objectId))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageBottom", LogPage.class,
                WicketUtils.newLogPageParameter(repositoryName, objectId, prevPage))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageBottom", LogPage.class,
                WicketUtils.newLogPageParameter(repositoryName, objectId, nextPage))
                .setEnabled(hasMore));
    }
    @Override
src/com/gitblit/wicket/pages/MarkdownPage.java
@@ -30,7 +30,7 @@
import com.gitblit.wicket.WicketUtils;
public class MarkdownPage extends RepositoryPage {
    public MarkdownPage(PageParameters params) {
        super(params);
@@ -41,11 +41,14 @@
        // markdown page links
        add(new Label("blameLink", getString("gb.blame")));
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, markdownPath)));
        add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(repositoryName, objectId, markdownPath)));
        add(new BookmarkablePageLink<Void>("headLink", MarkdownPage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, markdownPath)));
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
                WicketUtils.newPathParameter(repositoryName, objectId, markdownPath)));
        add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(
                repositoryName, objectId, markdownPath)));
        add(new BookmarkablePageLink<Void>("headLink", MarkdownPage.class,
                WicketUtils.newPathParameter(repositoryName, Constants.HEAD, markdownPath)));
        // Read raw markdown content and transform it to html
        // Read raw markdown content and transform it to html
        String markdownText = JGitUtils.getRawContentAsString(r, commit, markdownPath);
        String htmlText;
        try {
@@ -54,7 +57,7 @@
            error(p.getMessage());
            htmlText = markdownText;
        }
        // Add the html to the page
        add(new Label("markdownText", htmlText).setEscapeModelStrings(false));
    }
src/com/gitblit/wicket/pages/PatchPage.java
@@ -37,7 +37,7 @@
            redirectToInterceptPage(new RepositoriesPage());
            return;
        }
        final String repositoryName = WicketUtils.getRepositoryName(params);
        final String baseObjectId = WicketUtils.getBaseObjectId(params);
        final String objectId = WicketUtils.getObject(params);
@@ -56,14 +56,12 @@
            redirectToInterceptPage(new RepositoriesPage());
            return;
        }
        String patch;
        if (StringUtils.isEmpty(baseObjectId)) {
            patch = JGitUtils.getCommitPatch(r, commit, blobPath);
        } else {
            RevCommit baseCommit = JGitUtils.getCommit(r, baseObjectId);
            patch = JGitUtils.getCommitPatch(r, baseCommit, commit, blobPath);
        RevCommit baseCommit = null;
        if (!StringUtils.isEmpty(baseObjectId)) {
            baseCommit = JGitUtils.getCommit(r, baseObjectId);
        }
        String patch = JGitUtils.getCommitPatch(r, baseCommit, commit, blobPath);
        add(new Label("patchText", patch));
        r.close();
    }
src/com/gitblit/wicket/pages/RawPage.java
@@ -59,10 +59,10 @@
        // Map the extensions to types
        Map<String, Integer> map = new HashMap<String, Integer>();
        for (String ext : GitBlit.self().settings().getStrings(Keys.web.imageExtensions)) {
        for (String ext : GitBlit.getStrings(Keys.web.imageExtensions)) {
            map.put(ext.toLowerCase(), 2);
        }
        for (String ext : GitBlit.self().settings().getStrings(Keys.web.binaryExtensions)) {
        for (String ext : GitBlit.getStrings(Keys.web.binaryExtensions)) {
            map.put(ext.toLowerCase(), 3);
        }
@@ -89,7 +89,8 @@
            add(c);
        } else {
            // plain text
            Label blobLabel = new Label("rawText", JGitUtils.getRawContentAsString(r, commit, blobPath));
            Label blobLabel = new Label("rawText", JGitUtils.getRawContentAsString(r, commit,
                    blobPath));
            WicketUtils.setCssClass(blobLabel, "plainprint");
            add(blobLabel);
        }
src/com/gitblit/wicket/pages/RepositoriesPage.java
@@ -41,14 +41,14 @@
        setupPage("", "");
        final boolean showAdmin;
        if (GitBlit.self().settings().getBoolean(Keys.web.authenticateAdminPages, true)) {
            boolean allowAdmin = GitBlit.self().settings().getBoolean(Keys.web.allowAdministration, false);
        if (GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) {
            boolean allowAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
            showAdmin = allowAdmin && GitBlitWebSession.get().canAdmin();
            // authentication requires state and session
            setStatelessHint(false);
        } else {
            showAdmin = GitBlit.self().settings().getBoolean(Keys.web.allowAdministration, false);
            if (GitBlit.self().settings().getBoolean(Keys.web.authenticateViewPages, false)) {
            showAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
            if (GitBlit.getBoolean(Keys.web.authenticateViewPages, false)) {
                // authentication requires state and session
                setStatelessHint(false);
            } else {
@@ -64,7 +64,7 @@
        }
        // Load the markdown welcome message
        String messageSource = GitBlit.self().settings().getString(Keys.web.repositoriesMessage, "gitblit");
        String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");
        String message = "<br/>";
        if (messageSource.equalsIgnoreCase("gitblit")) {
            // Read default welcome message
@@ -94,9 +94,10 @@
                }
            }
        }
        Component repositoriesMessage = new Label("repositoriesMessage", message).setEscapeModelStrings(false);
        Component repositoriesMessage = new Label("repositoriesMessage", message)
                .setEscapeModelStrings(false);
        add(repositoriesMessage);
        add(new RepositoriesPanel("repositoriesPanel", showAdmin, getAccessRestrictions()));
        add(new RepositoriesPanel("repositoriesPanel", showAdmin, getAccessRestrictions()));
        add(new UsersPanel("usersPanel", showAdmin).setVisible(showAdmin));
    }
}
src/com/gitblit/wicket/pages/SearchPage.java
@@ -24,29 +24,42 @@
import com.gitblit.wicket.panels.SearchPanel;
public class SearchPage extends RepositoryPage {
    public SearchPage(PageParameters params) {
        super(params);
        String value = WicketUtils.getSearchString(params);
        String type = WicketUtils.getSearchType(params);
        SearchType searchType = SearchType.forName(type);
        int pageNumber = WicketUtils.getPage(params);
        int prevPage = Math.max(0, pageNumber - 1);
        int nextPage = pageNumber + 1;
        SearchPanel search = new SearchPanel("searchPanel", repositoryName, objectId, value, searchType, getRepository(), -1, pageNumber - 1);
        SearchPanel search = new SearchPanel("searchPanel", repositoryName, objectId, value,
                searchType, getRepository(), -1, pageNumber - 1);
        boolean hasMore = search.hasMore();
        add(search);
        add(new BookmarkablePageLink<Void>("firstPageTop", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageTop", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType, prevPage)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageTop", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType, nextPage)).setEnabled(hasMore));
        add(new BookmarkablePageLink<Void>("firstPageTop", SearchPage.class,
                WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageTop", SearchPage.class,
                WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType,
                        prevPage)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageTop", SearchPage.class,
                WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType,
                        nextPage)).setEnabled(hasMore));
        add(new BookmarkablePageLink<Void>("firstPageBottom", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageBottom", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType, prevPage)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageBottom", SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType, nextPage)).setEnabled(hasMore));
        add(new BookmarkablePageLink<Void>("firstPageBottom", SearchPage.class,
                WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType))
                .setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("prevPageBottom", SearchPage.class,
                WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType,
                        prevPage)).setEnabled(pageNumber > 1));
        add(new BookmarkablePageLink<Void>("nextPageBottom", SearchPage.class,
                WicketUtils.newSearchParameter(repositoryName, objectId, value, searchType,
                        nextPage)).setEnabled(hasMore));
    }
src/com/gitblit/wicket/pages/SummaryPage.java
@@ -60,12 +60,12 @@
        int numCommitsDef = 20;
        int numRefsDef = 5;
        int numberCommits = GitBlit.self().settings().getInteger(Keys.web.summaryCommitCount, numCommitsDef);
        int numberCommits = GitBlit.getInteger(Keys.web.summaryCommitCount, numCommitsDef);
        if (numberCommits <= 0) {
            numberCommits = numCommitsDef;
        }
        int numberRefs = GitBlit.self().settings().getInteger(Keys.web.summaryRefsCount, numRefsDef);
        int numberRefs = GitBlit.getInteger(Keys.web.summaryRefsCount, numRefsDef);
        if (numberRefs <= 0) {
            numberRefs = numRefsDef;
        }
@@ -73,7 +73,7 @@
        Repository r = getRepository();
        List<Metric> metrics = null;
        Metric metricsTotal = null;
        if (GitBlit.self().settings().getBoolean(Keys.web.generateActivityGraph, true)) {
        if (GitBlit.getBoolean(Keys.web.generateActivityGraph, true)) {
            metrics = JGitUtils.getDateMetrics(r);
            metricsTotal = metrics.remove(0);
        }
@@ -82,40 +82,48 @@
        add(new Label("repositoryDescription", getRepositoryModel().description));
        add(new Label("repositoryOwner", getRepositoryModel().owner));
        add(WicketUtils.createTimestampLabel("repositoryLastChange", JGitUtils.getLastChange(r), getTimeZone()));
        add(WicketUtils.createTimestampLabel("repositoryLastChange", JGitUtils.getLastChange(r),
                getTimeZone()));
        if (metricsTotal == null) {
            add(new Label("repositoryStats", ""));
        } else {
            add(new Label("repositoryStats", MessageFormat.format("{0} commits and {1} tags in {2}", metricsTotal.count, metricsTotal.tag, TimeUtils.duration(metricsTotal.duration))));
            add(new Label("repositoryStats", MessageFormat.format(
                    "{0} commits and {1} tags in {2}", metricsTotal.count, metricsTotal.tag,
                    TimeUtils.duration(metricsTotal.duration))));
        }
        List<String> repositoryUrls = new ArrayList<String>();
        if (GitBlit.self().settings().getBoolean(Keys.git.enableGitServlet, true)) {
        if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)) {
            AccessRestrictionType accessRestriction = getRepositoryModel().accessRestriction;
            switch (accessRestriction) {
            case NONE:
                add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false));
                break;
            case PUSH:
                add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png", getAccessRestrictions().get(accessRestriction)));
                add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",
                        getAccessRestrictions().get(accessRestriction)));
                break;
            case CLONE:
                add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png", getAccessRestrictions().get(accessRestriction)));
                add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",
                        getAccessRestrictions().get(accessRestriction)));
                break;
            case VIEW:
                add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png", getAccessRestrictions().get(accessRestriction)));
                add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",
                        getAccessRestrictions().get(accessRestriction)));
                break;
            default:
                add(WicketUtils.newClearPixel("accessRestrictionIcon").setVisible(false));
            }
            HttpServletRequest req = ((WebRequest) getRequestCycle().getRequest()).getHttpServletRequest();
            HttpServletRequest req = ((WebRequest) getRequestCycle().getRequest())
                    .getHttpServletRequest();
            StringBuilder sb = new StringBuilder();
            sb.append(req.getScheme());
            sb.append("://");
            sb.append(req.getServerName());
            if ((req.getScheme().equals("http") && req.getServerPort() != 80) || (req.getScheme().equals("https") && req.getServerPort() != 443)) {
            if ((req.getScheme().equals("http") && req.getServerPort() != 80)
                    || (req.getScheme().equals("https") && req.getServerPort() != 443)) {
                sb.append(":" + req.getServerPort());
            }
            sb.append(Constants.GIT_SERVLET_PATH);
@@ -126,7 +134,8 @@
        }
        repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(repositoryName));
        add(new Label("repositoryCloneUrl", StringUtils.flattenStrings(repositoryUrls, "<br/>")).setEscapeModelStrings(false));
        add(new Label("repositoryCloneUrl", StringUtils.flattenStrings(repositoryUrls, "<br/>"))
                .setEscapeModelStrings(false));
        add(new LogPanel("commitsPanel", repositoryName, null, r, numberCommits, 0));
        add(new TagsPanel("tagsPanel", repositoryName, r, numberRefs));
@@ -142,12 +151,15 @@
    }
    private void insertActivityGraph(List<Metric> metrics) {
        if (metrics.size() > 0 && GitBlit.self().settings().getBoolean(Keys.web.generateActivityGraph, true)) {
        if ((metrics != null) && (metrics.size() > 0)
                && GitBlit.getBoolean(Keys.web.generateActivityGraph, true)) {
            IChartData data = getChartData(metrics);
            ChartProvider provider = new ChartProvider(new Dimension(400, 100), ChartType.LINE, data);
            ChartProvider provider = new ChartProvider(new Dimension(400, 100), ChartType.LINE,
                    data);
            ChartAxis dateAxis = new ChartAxis(ChartAxisType.BOTTOM);
            dateAxis.setLabels(new String[] { metrics.get(0).name, metrics.get(metrics.size() / 2).name, metrics.get(metrics.size() - 1).name });
            dateAxis.setLabels(new String[] { metrics.get(0).name,
                    metrics.get(metrics.size() / 2).name, metrics.get(metrics.size() - 1).name });
            provider.addAxis(dateAxis);
            ChartAxis commitAxis = new ChartAxis(ChartAxisType.LEFT);
src/com/gitblit/wicket/pages/TagPage.java
@@ -48,16 +48,21 @@
        if (tagRef == null) {
            // point to commit
            add(new LinkPanel("commit", "title", c.getShortMessage(), CommitPage.class, newCommitParameter()));
            add(new LinkPanel("tagId", "list", c.getName(), CommitPage.class, newCommitParameter(c.getName())));
            add(new LinkPanel("commit", "title", c.getShortMessage(), CommitPage.class,
                    newCommitParameter()));
            add(new LinkPanel("tagId", "list", c.getName(), CommitPage.class,
                    newCommitParameter(c.getName())));
        } else {
            // TODO commit or tree or blob?
            add(new LinkPanel("commit", "title", tagRef.getDisplayName(), CommitPage.class, newCommitParameter()));
            add(new LinkPanel("tagId", "list", c.getName(), CommitPage.class, newCommitParameter(c.getName())));
            add(new LinkPanel("commit", "title", tagRef.displayName, CommitPage.class,
                    newCommitParameter()));
            add(new LinkPanel("tagId", "list", c.getName(), CommitPage.class,
                    newCommitParameter(c.getName())));
        }
        add(createPersonPanel("tagAuthor", c.getAuthorIdent(), SearchType.AUTHOR));
        add(WicketUtils.createTimestampLabel("tagDate", c.getAuthorIdent().getWhen(), getTimeZone()));
        add(WicketUtils
                .createTimestampLabel("tagDate", c.getAuthorIdent().getWhen(), getTimeZone()));
        addFullText("fullMessage", c.getFullMessage(), true);
    }
src/com/gitblit/wicket/pages/TicketPage.java
@@ -52,13 +52,15 @@
        ListDataProvider<Comment> commentsDp = new ListDataProvider<Comment>(t.comments);
        DataView<Comment> commentsView = new DataView<Comment>("comment", commentsDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<Comment> item) {
                final Comment entry = item.getModelObject();
                item.add(WicketUtils.createDateLabel("commentDate", entry.date, GitBlitWebSession.get().getTimezone()));
                item.add(WicketUtils.createDateLabel("commentDate", entry.date, GitBlitWebSession
                        .get().getTimezone()));
                item.add(new Label("commentAuthor", entry.author.toLowerCase()));
                item.add(new Label("commentText", prepareComment(entry.text)).setEscapeModelStrings(false));
                item.add(new Label("commentText", prepareComment(entry.text))
                        .setEscapeModelStrings(false));
                WicketUtils.setAlternatingBackground(item, counter);
                counter++;
            }
@@ -74,6 +76,7 @@
    private String prepareComment(String comment) {
        String html = StringUtils.escapeForHtml(comment, false);
        html = StringUtils.breakLinesForHtml(comment).trim();
        return html.replaceAll("\\bcommit\\s*([A-Za-z0-9]*)\\b", "<a href=\"/commit/" + repositoryName + "/$1\">commit $1</a>");
        return html.replaceAll("\\bcommit\\s*([A-Za-z0-9]*)\\b", "<a href=\"/commit/"
                + repositoryName + "/$1\">commit $1</a>");
    }
}
src/com/gitblit/wicket/pages/TicketsPage.java
@@ -39,21 +39,25 @@
        List<TicketModel> tickets = JGitUtils.getTickets(getRepository());
        // header
        add(new LinkPanel("header", "title", repositoryName, SummaryPage.class, newRepositoryParameter()));
        add(new LinkPanel("header", "title", repositoryName, SummaryPage.class,
                newRepositoryParameter()));
        ListDataProvider<TicketModel> ticketsDp = new ListDataProvider<TicketModel>(tickets);
        DataView<TicketModel> ticketsView = new DataView<TicketModel>("ticket", ticketsDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<TicketModel> item) {
                final TicketModel entry = item.getModelObject();
                Label stateLabel = new Label("ticketState", entry.state);
                WicketUtils.setTicketCssClass(stateLabel, entry.state);
                item.add(stateLabel);
                item.add(WicketUtils.createDateLabel("ticketDate", entry.date, GitBlitWebSession.get().getTimezone()));
                item.add(new Label("ticketHandler", StringUtils.trimString(entry.handler.toLowerCase(), 30)));
                item.add(new LinkPanel("ticketTitle", "list subject", StringUtils.trimString(entry.title, 80), TicketPage.class, newPathParameter(entry.name)));
                item.add(WicketUtils.createDateLabel("ticketDate", entry.date, GitBlitWebSession
                        .get().getTimezone()));
                item.add(new Label("ticketHandler", StringUtils.trimString(
                        entry.handler.toLowerCase(), 30)));
                item.add(new LinkPanel("ticketTitle", "list subject", StringUtils.trimString(
                        entry.title, 80), TicketPage.class, newPathParameter(entry.name)));
                WicketUtils.setAlternatingBackground(item, counter);
                counter++;
src/com/gitblit/wicket/pages/TreePage.java
@@ -53,9 +53,13 @@
        List<PathModel> paths = JGitUtils.getFilesInPath(r, path, commit);
        // tree page links
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, path)));
        add(new BookmarkablePageLink<Void>("headLink", TreePage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, path)));
        add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, path)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
                WicketUtils.newPathParameter(repositoryName, objectId, path)));
        add(new BookmarkablePageLink<Void>("headLink", TreePage.class,
                WicketUtils.newPathParameter(repositoryName, Constants.HEAD, path)));
        add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest()
                .getRelativePathPrefixToContextRoot(), repositoryName, objectId, path))
                .setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true)));
        add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
@@ -71,7 +75,7 @@
        ListDataProvider<PathModel> pathsDp = new ListDataProvider<PathModel>(paths);
        DataView<PathModel> pathsView = new DataView<PathModel>("changedPath", pathsDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<PathModel> item) {
                PathModel entry = item.getModelObject();
@@ -80,33 +84,49 @@
                    // parent .. path
                    item.add(WicketUtils.newBlankImage("pathIcon"));
                    item.add(new Label("pathSize", ""));
                    item.add(new LinkPanel("pathName", null, entry.name, TreePage.class, newPathParameter(entry.path)));
                    item.add(new LinkPanel("pathName", null, entry.name, TreePage.class,
                            newPathParameter(entry.path)));
                    item.add(new Label("pathLinks", ""));
                } else {
                    if (entry.isTree()) {
                        // folder/tree link
                        item.add(WicketUtils.newImage("pathIcon", "folder_16x16.png"));
                        item.add(new Label("pathSize", ""));
                        item.add(new LinkPanel("pathName", "list", entry.name, TreePage.class, newPathParameter(entry.path)));
                        item.add(new LinkPanel("pathName", "list", entry.name, TreePage.class,
                                newPathParameter(entry.path)));
                        // links
                        Fragment links = new Fragment("pathLinks", "treeLinks", this);
                        links.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                        links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                        links.add(new ExternalLink("zip", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, entry.path)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));
                        links.add(new BookmarkablePageLink<Void>("tree", TreePage.class,
                                WicketUtils.newPathParameter(repositoryName, entry.commitId,
                                        entry.path)));
                        links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
                                WicketUtils.newPathParameter(repositoryName, entry.commitId,
                                        entry.path)));
                        links.add(new ExternalLink("zip", DownloadZipServlet.asLink(getRequest()
                                .getRelativePathPrefixToContextRoot(), repositoryName, objectId,
                                entry.path)).setVisible(GitBlit.getBoolean(
                                Keys.web.allowZipDownloads, true)));
                        item.add(links);
                    } else {
                        // blob link
                        item.add(WicketUtils.getFileImage("pathIcon", entry.name));
                        item.add(new Label("pathSize", byteFormat.format(entry.size)));
                        item.add(new LinkPanel("pathName", "list", entry.name, BlobPage.class, newPathParameter(entry.path)));
                        item.add(new LinkPanel("pathName", "list", entry.name, BlobPage.class,
                                newPathParameter(entry.path)));
                        // links
                        Fragment links = new Fragment("pathLinks", "blobLinks", this);
                        links.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                        links.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                        links.add(new BookmarkablePageLink<Void>("blame", BlobPage.class).setEnabled(false));
                        links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                        links.add(new BookmarkablePageLink<Void>("view", BlobPage.class,
                                WicketUtils.newPathParameter(repositoryName, entry.commitId,
                                        entry.path)));
                        links.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
                                .newPathParameter(repositoryName, entry.commitId, entry.path)));
                        links.add(new BookmarkablePageLink<Void>("blame", BlobPage.class)
                                .setEnabled(false));
                        links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class,
                                WicketUtils.newPathParameter(repositoryName, entry.commitId,
                                        entry.path)));
                        item.add(links);
                    }
                }
src/com/gitblit/wicket/panels/BasePanel.java
@@ -37,7 +37,8 @@
    }
    protected TimeZone getTimeZone() {
        return GitBlit.self().settings().getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get().getTimezone() : TimeZone.getDefault();
        return GitBlit.getBoolean(Keys.web.useClientTimezone, false) ? GitBlitWebSession.get()
                .getTimezone() : TimeZone.getDefault();
    }
    protected void setPersonSearchTooltip(Component component, String value, SearchType searchType) {
@@ -48,7 +49,7 @@
        }
    }
    public class JavascriptEventConfirmation extends AttributeModifier {
    public static class JavascriptEventConfirmation extends AttributeModifier {
        private static final long serialVersionUID = 1L;
@@ -57,7 +58,8 @@
        }
        protected String newValue(final String currentValue, final String replacementValue) {
            String prefix = "var conf = confirm('" + replacementValue + "'); " + "if (!conf) return false; ";
            String prefix = "var conf = confirm('" + replacementValue + "'); "
                    + "if (!conf) return false; ";
            String result = prefix;
            if (currentValue != null) {
                result = prefix + currentValue;
src/com/gitblit/wicket/panels/BranchesPanel.java
@@ -43,7 +43,8 @@
    private static final long serialVersionUID = 1L;
    public BranchesPanel(String wicketId, final RepositoryModel model, Repository r, final int maxCount) {
    public BranchesPanel(String wicketId, final RepositoryModel model, Repository r,
            final int maxCount) {
        super(wicketId);
        // branches
@@ -61,31 +62,38 @@
        if (maxCount > 0) {
            // summary page
            // show branches page link
            add(new LinkPanel("branches", "title", new StringResourceModel("gb.branches", this, null), BranchesPage.class, WicketUtils.newRepositoryParameter(model.name)));
            add(new LinkPanel("branches", "title", new StringResourceModel("gb.branches", this,
                    null), BranchesPage.class, WicketUtils.newRepositoryParameter(model.name)));
        } else {
            // branches page
            // show repository summary page link
            add(new LinkPanel("branches", "title", model.name, SummaryPage.class, WicketUtils.newRepositoryParameter(model.name)));
            add(new LinkPanel("branches", "title", model.name, SummaryPage.class,
                    WicketUtils.newRepositoryParameter(model.name)));
        }
        ListDataProvider<RefModel> branchesDp = new ListDataProvider<RefModel>(branches);
        DataView<RefModel> branchesView = new DataView<RefModel>("branch", branchesDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<RefModel> item) {
                final RefModel entry = item.getModelObject();
                item.add(WicketUtils.createDateLabel("branchDate", entry.getDate(), getTimeZone()));
                item.add(new LinkPanel("branchName", "list name", StringUtils.trimString(entry.getDisplayName(), 28), LogPage.class, WicketUtils.newObjectParameter(model.name, entry.getName())));
                item.add(new LinkPanel("branchName", "list name", StringUtils.trimString(
                        entry.displayName, 28), LogPage.class, WicketUtils.newObjectParameter(
                        model.name, entry.getName())));
                // only show branch type on the branches page
                boolean remote = entry.getName().startsWith(Constants.R_REMOTES);
                item.add(new Label("branchType", remote ? getString("gb.remote") : getString("gb.local")).setVisible(maxCount <= 0));
                item.add(new Label("branchType", remote ? getString("gb.remote")
                        : getString("gb.local")).setVisible(maxCount <= 0));
                item.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils.newObjectParameter(model.name, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newObjectParameter(model.name, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils
                        .newObjectParameter(model.name, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils
                        .newObjectParameter(model.name, entry.getName())));
                WicketUtils.setAlternatingBackground(item, counter);
                counter++;
@@ -95,7 +103,8 @@
        if (branches.size() < maxCount || maxCount <= 0) {
            add(new Label("allBranches", "").setVisible(false));
        } else {
            add(new LinkPanel("allBranches", "link", new StringResourceModel("gb.allBranches", this, null), BranchesPage.class, WicketUtils.newRepositoryParameter(model.name)));
            add(new LinkPanel("allBranches", "link", new StringResourceModel("gb.allBranches",
                    this, null), BranchesPage.class, WicketUtils.newRepositoryParameter(model.name)));
        }
    }
}
src/com/gitblit/wicket/panels/CommitHeaderPanel.java
@@ -28,8 +28,9 @@
    public CommitHeaderPanel(String id, String repositoryName, RevCommit c) {
        super(id);
        add(new LinkPanel("shortmessage", "title", c.getShortMessage(), CommitPage.class, WicketUtils.newObjectParameter(repositoryName, c.getName())));
        add(new Label("commitid", "(" + c.getName().substring(0, 8) + ")"));
        add(new LinkPanel("shortmessage", "title", c.getShortMessage(), CommitPage.class,
                WicketUtils.newObjectParameter(repositoryName, c.getName())));
        add(new Label("commitid", "(" + c.getName().substring(0, 8) + ")"));
        add(new Label("author", c.getAuthorIdent().getName()));
        add(WicketUtils.createDateLabel("date", c.getAuthorIdent().getWhen(), getTimeZone()));
    }
src/com/gitblit/wicket/panels/CommitLegendPanel.java
@@ -38,8 +38,9 @@
    public CommitLegendPanel(String id, List<PathChangeModel> paths) {
        super(id);
        final Map<ChangeType, AtomicInteger> stats = JGitUtils.getChangedPathsStats(paths);
        ListDataProvider<ChangeType> legendDp = new ListDataProvider<ChangeType>(new ArrayList<ChangeType>(stats.keySet()));
        final Map<ChangeType, AtomicInteger> stats = JGitUtils.getChangedPathsStats(paths);
        ListDataProvider<ChangeType> legendDp = new ListDataProvider<ChangeType>(
                new ArrayList<ChangeType>(stats.keySet()));
        DataView<ChangeType> legendsView = new DataView<ChangeType>("legend", legendDp) {
            private static final long serialVersionUID = 1L;
@@ -50,8 +51,8 @@
                WicketUtils.setChangeTypeCssClass(changeType, entry);
                item.add(changeType);
                int count = stats.get(entry).intValue();
                String description  = "";
                switch(entry) {
                String description = "";
                switch (entry) {
                case ADD:
                    description = MessageFormat.format(getString("gb.filesAdded"), count);
                    break;
@@ -67,7 +68,7 @@
                case RENAME:
                    description = MessageFormat.format(getString("gb.filesRenamed"), count);
                    break;
                }
                }
                item.add(new Label("description", description));
            }
        };
src/com/gitblit/wicket/panels/HistoryPanel.java
@@ -51,12 +51,13 @@
    private static final long serialVersionUID = 1L;
    private boolean hasMore = false;
    private boolean hasMore;
    public HistoryPanel(String wicketId, final String repositoryName, final String objectId, final String path, Repository r, int limit, int pageOffset) {
    public HistoryPanel(String wicketId, final String repositoryName, final String objectId,
            final String path, Repository r, int limit, int pageOffset) {
        super(wicketId);
        boolean pageResults = limit <= 0;
        int itemsPerPage = GitBlit.self().settings().getInteger(Keys.web.itemsPerPage, 50);
        int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);
        if (itemsPerPage <= 1) {
            itemsPerPage = 50;
        }
@@ -77,7 +78,8 @@
        List<RevCommit> commits;
        if (pageResults) {
            // Paging result set
            commits = JGitUtils.getRevLog(r, objectId, path, pageOffset * itemsPerPage, itemsPerPage);
            commits = JGitUtils.getRevLog(r, objectId, path, pageOffset * itemsPerPage,
                    itemsPerPage);
        } else {
            // Fixed size result set
            commits = JGitUtils.getRevLog(r, objectId, path, 0, limit);
@@ -95,7 +97,7 @@
        ListDataProvider<RevCommit> dp = new ListDataProvider<RevCommit>(commits);
        DataView<RevCommit> logView = new DataView<RevCommit>("commit", dp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<RevCommit> item) {
                final RevCommit entry = item.getModelObject();
@@ -105,7 +107,9 @@
                // author search link
                String author = entry.getAuthorIdent().getName();
                LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author, SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, author, SearchType.AUTHOR));
                LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author,
                        SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId,
                                author, SearchType.AUTHOR));
                setPersonSearchTooltip(authorLink, author, SearchType.AUTHOR);
                item.add(authorLink);
@@ -118,7 +122,9 @@
                String shortMessage = entry.getShortMessage();
                String trimmedMessage = StringUtils.trimShortLog(shortMessage);
                LinkPanel shortlog = new LinkPanel("commitShortMessage", "list subject", trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName()));
                LinkPanel shortlog = new LinkPanel("commitShortMessage", "list subject",
                        trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
                                repositoryName, entry.getName()));
                if (!shortMessage.equals(trimmedMessage)) {
                    WicketUtils.setHtmlTooltip(shortlog, shortMessage);
                }
@@ -128,14 +134,20 @@
                if (isTree) {
                    Fragment links = new Fragment("historyLinks", "treeLinks", this);
                    links.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                    links.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                    links.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils
                            .newObjectParameter(repositoryName, entry.getName())));
                    links.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                    item.add(links);
                } else {
                    Fragment links = new Fragment("historyLinks", "blobLinks", this);
                    links.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils.newPathParameter(repositoryName, entry.getName(), path)));
                    links.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                    links.add(new BookmarkablePageLink<Void>("difftocurrent", BlobDiffPage.class, WicketUtils.newBlobDiffParameter(repositoryName, entry.getName(), objectId, path)).setEnabled(counter > 0));
                    links.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
                            .newPathParameter(repositoryName, entry.getName(), path)));
                    links.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                    links.add(new BookmarkablePageLink<Void>("difftocurrent", BlobDiffPage.class,
                            WicketUtils.newBlobDiffParameter(repositoryName, entry.getName(),
                                    objectId, path)).setEnabled(counter > 0));
                    item.add(links);
                }
@@ -157,7 +169,9 @@
                // more
                if (commits.size() == limit) {
                    // show more
                    add(new LinkPanel("moreHistory", "link", new StringResourceModel("gb.moreHistory", this, null), HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, path)));
                    add(new LinkPanel("moreHistory", "link", new StringResourceModel(
                            "gb.moreHistory", this, null), HistoryPage.class,
                            WicketUtils.newPathParameter(repositoryName, objectId, path)));
                } else {
                    // no more
                    add(new Label("moreHistory", "").setVisible(false));
src/com/gitblit/wicket/panels/LogPanel.java
@@ -46,13 +46,14 @@
public class LogPanel extends BasePanel {
    private static final long serialVersionUID = 1L;
    private boolean hasMore = false;
    public LogPanel(String wicketId, final String repositoryName, final String objectId, Repository r, int limit, int pageOffset) {
    private boolean hasMore;
    public LogPanel(String wicketId, final String repositoryName, final String objectId,
            Repository r, int limit, int pageOffset) {
        super(wicketId);
        boolean pageResults = limit <= 0;
        int itemsPerPage = GitBlit.self().settings().getInteger(Keys.web.itemsPerPage, 50);
        int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);
        if (itemsPerPage <= 1) {
            itemsPerPage = 50;
        }
@@ -68,24 +69,26 @@
        }
        // inaccurate way to determine if there are more commits.
        // works unless commits.size() represents the exact end.
        // works unless commits.size() represents the exact end.
        hasMore = commits.size() >= itemsPerPage;
        // header
        if (pageResults) {
            // shortlog page
            // show repository summary page link
            add(new LinkPanel("header", "title", repositoryName, SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
            add(new LinkPanel("header", "title", repositoryName, SummaryPage.class,
                    WicketUtils.newRepositoryParameter(repositoryName)));
        } else {
            // summary page
            // show shortlog page link
            add(new LinkPanel("header", "title", new StringResourceModel("gb.log", this, null), LogPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
            add(new LinkPanel("header", "title", new StringResourceModel("gb.log", this, null),
                    LogPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
        }
        ListDataProvider<RevCommit> dp = new ListDataProvider<RevCommit>(commits);
        DataView<RevCommit> logView = new DataView<RevCommit>("commit", dp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<RevCommit> item) {
                final RevCommit entry = item.getModelObject();
@@ -95,7 +98,9 @@
                // author search link
                String author = entry.getAuthorIdent().getName();
                LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author, SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, author, SearchType.AUTHOR));
                LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author,
                        SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId,
                                author, SearchType.AUTHOR));
                setPersonSearchTooltip(authorLink, author, SearchType.AUTHOR);
                item.add(authorLink);
@@ -105,11 +110,13 @@
                } else {
                    item.add(WicketUtils.newBlankImage("commitIcon"));
                }
                // short message
                String shortMessage = entry.getShortMessage();
                String trimmedMessage = StringUtils.trimShortLog(shortMessage);
                LinkPanel shortlog = new LinkPanel("commitShortMessage", "list subject", trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName()));
                LinkPanel shortlog = new LinkPanel("commitShortMessage", "list subject",
                        trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
                                repositoryName, entry.getName()));
                if (!shortMessage.equals(trimmedMessage)) {
                    WicketUtils.setHtmlTooltip(shortlog, shortMessage);
                }
@@ -117,9 +124,12 @@
                item.add(new RefsPanel("commitRefs", repositoryName, entry, allRefs));
                item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("view", CommitPage.class, WicketUtils
                        .newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class, WicketUtils
                        .newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils
                        .newObjectParameter(repositoryName, entry.getName())));
                WicketUtils.setAlternatingBackground(item, counter);
                counter++;
@@ -139,7 +149,9 @@
                // more
                if (commits.size() == limit) {
                    // show more
                    add(new LinkPanel("moreLogs", "link", new StringResourceModel("gb.moreLogs", this, null), LogPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
                    add(new LinkPanel("moreLogs", "link", new StringResourceModel("gb.moreLogs",
                            this, null), LogPage.class,
                            WicketUtils.newRepositoryParameter(repositoryName)));
                } else {
                    // no more
                    add(new Label("moreLogs", "").setVisible(false));
@@ -147,7 +159,7 @@
            }
        }
    }
    public boolean hasMore() {
        return hasMore;
    }
src/com/gitblit/wicket/panels/PathBreadcrumbsPanel.java
@@ -33,9 +33,10 @@
    private static final long serialVersionUID = 1L;
    private final String ROOT = "--ROOT--";
    private static final String ROOT = "--ROOT--";
    public PathBreadcrumbsPanel(String id, final String repositoryName, String pathName, final String objectId) {
    public PathBreadcrumbsPanel(String id, final String repositoryName, String pathName,
            final String objectId) {
        super(id);
        List<BreadCrumb> crumbs = new ArrayList<BreadCrumb>();
        crumbs.add(new BreadCrumb("[" + repositoryName + "]", ROOT, false));
@@ -47,8 +48,8 @@
            for (int i = 0; i < paths.length; i++) {
                String path = paths[i];
                sb.append(path);
                crumbs.add(new BreadCrumb(path, sb.toString(), (i == (paths.length - 1))));
                sb.append("/");
                crumbs.add(new BreadCrumb(path, sb.toString(), i == (paths.length - 1)));
                sb.append('/');
            }
        }
@@ -63,7 +64,8 @@
                    item.add(new Label("pathLink", entry.name));
                    item.add(new Label("pathSeparator", "").setVisible(false));
                } else {
                    item.add(new LinkPanel("pathLink", null, entry.name, TreePage.class, WicketUtils.newPathParameter(repositoryName, objectId, path)));
                    item.add(new LinkPanel("pathLink", null, entry.name, TreePage.class,
                            WicketUtils.newPathParameter(repositoryName, objectId, path)));
                    item.add(new Label("pathSeparator", "/"));
                }
            }
@@ -71,7 +73,7 @@
        add(pathsView);
    }
    private class BreadCrumb implements Serializable {
    private static class BreadCrumb implements Serializable {
        private static final long serialVersionUID = 1L;
src/com/gitblit/wicket/panels/RefsPanel.java
@@ -39,7 +39,8 @@
    private static final long serialVersionUID = 1L;
    public RefsPanel(String id, final String repositoryName, RevCommit c, Map<ObjectId, List<String>> refs) {
    public RefsPanel(String id, final String repositoryName, RevCommit c,
            Map<ObjectId, List<String>> refs) {
        super(id);
        List<String> refNames = refs.get(c.getId());
        if (refNames == null) {
@@ -57,19 +58,24 @@
                Component c = null;
                if (entry.startsWith(Constants.R_HEADS)) {
                    // local head
                    c = new LinkPanel("refName", null, entry.substring(Constants.R_HEADS.length()), LogPage.class, WicketUtils.newObjectParameter(repositoryName, entry));
                    c = new LinkPanel("refName", null, entry.substring(Constants.R_HEADS.length()),
                            LogPage.class, WicketUtils.newObjectParameter(repositoryName, entry));
                    WicketUtils.setCssClass(c, "headRef");
                } else if (entry.startsWith(Constants.R_REMOTES)) {
                    // remote head
                    c = new LinkPanel("refName", null, entry.substring(Constants.R_REMOTES.length()), LogPage.class, WicketUtils.newObjectParameter(repositoryName, entry));
                    c = new LinkPanel("refName", null,
                            entry.substring(Constants.R_REMOTES.length()), LogPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry));
                    WicketUtils.setCssClass(c, "remoteRef");
                } else if (entry.startsWith(Constants.R_TAGS)) {
                    // tag
                    c = new LinkPanel("refName", null, entry.substring(Constants.R_TAGS.length()), TagPage.class, WicketUtils.newObjectParameter(repositoryName, entry));
                    c = new LinkPanel("refName", null, entry.substring(Constants.R_TAGS.length()),
                            TagPage.class, WicketUtils.newObjectParameter(repositoryName, entry));
                    WicketUtils.setCssClass(c, "tagRef");
                } else {
                    // other
                    c = new LinkPanel("refName", null, entry, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry));
                    c = new LinkPanel("refName", null, entry, CommitPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry));
                    WicketUtils.setCssClass(c, "otherRef");
                }
                WicketUtils.setHtmlTooltip(c, entry);
src/com/gitblit/wicket/panels/RepositoriesPanel.java
@@ -57,7 +57,8 @@
    private static final long serialVersionUID = 1L;
    public RepositoriesPanel(String wicketId, final boolean showAdmin, final Map<AccessRestrictionType, String> accessRestrictionTranslations) {
    public RepositoriesPanel(String wicketId, final boolean showAdmin,
            final Map<AccessRestrictionType, String> accessRestrictionTranslations) {
        super(wicketId);
        final UserModel user = GitBlitWebSession.get().getUser();
@@ -68,12 +69,12 @@
        adminLinks.add(new BookmarkablePageLink<Void>("newRepository", EditRepositoryPage.class));
        add(adminLinks.setVisible(showAdmin));
        if (GitBlit.self().settings().getString(Keys.web.repositoryListType, "flat").equalsIgnoreCase("grouped")) {
        if (GitBlit.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, " ");
                    rootPath = GitBlit.getString(Keys.web.repositoryRootGroupName, " ");
                }
                if (!groups.containsKey(rootPath)) {
                    groups.put(rootPath, new ArrayList<RepositoryModel>());
@@ -95,7 +96,7 @@
        DataView<RepositoryModel> dataView = new DataView<RepositoryModel>("row", dp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            @Override
            protected void onBeforeRender() {
@@ -117,28 +118,34 @@
                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));
                    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("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")));
                    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")));
                    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")));
                    row.add(WicketUtils.newImage("frozenIcon", "cold_16x16.png",
                            getString("gb.isFrozen")));
                } else {
                    row.add(WicketUtils.newClearPixel("frozenIcon").setVisible(false));
                }
@@ -147,13 +154,16 @@
                    row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
                    break;
                case PUSH:
                    row.add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png", accessRestrictionTranslations.get(entry.accessRestriction)));
                    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)));
                    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)));
                    row.add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",
                            accessRestrictionTranslations.get(entry.accessRestriction)));
                    break;
                default:
                    row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
@@ -166,10 +176,13 @@
                row.add(lastChangeLabel);
                WicketUtils.setCssClass(lastChangeLabel, TimeUtils.timeAgoCss(entry.lastChange));
                boolean showOwner = user != null && user.getUsername().equalsIgnoreCase(entry.owner);
                boolean showOwner = user != null && user.username.equalsIgnoreCase(entry.owner);
                if (showAdmin) {
                    Fragment repositoryLinks = new Fragment("repositoryLinks", "repositoryAdminLinks", this);
                    repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class, WicketUtils.newRepositoryParameter(entry.name)));
                    Fragment repositoryLinks = new Fragment("repositoryLinks",
                            "repositoryAdminLinks", this);
                    repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",
                            EditRepositoryPage.class, WicketUtils
                                    .newRepositoryParameter(entry.name)));
                    Link<Void> deleteLink = new Link<Void>("deleteRepository") {
                        private static final long serialVersionUID = 1L;
@@ -184,16 +197,21 @@
                                    ((RepositoriesProvider) dp).remove(entry);
                                }
                            } else {
                                error(MessageFormat.format("Failed to delete repository ''{0}''!", entry));
                                error(MessageFormat.format("Failed to delete repository ''{0}''!",
                                        entry));
                            }
                        }
                    };
                    deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format("Delete repository \"{0}\"?", entry)));
                    deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
                            "Delete repository \"{0}\"?", entry)));
                    repositoryLinks.add(deleteLink);
                    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)));
                    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"));
@@ -220,11 +238,11 @@
        }
    }
    private class GroupRepositoryModel extends RepositoryModel {
    private static class GroupRepositoryModel extends RepositoryModel {
        private static final long serialVersionUID = 1L;
        int count = 0;
        int count;
        GroupRepositoryModel(String name, int count) {
            super(name, "", "", new Date(0));
@@ -241,7 +259,8 @@
        repository, description, owner, date;
    }
    protected OrderByBorder newSort(String wicketId, SortBy field, SortableDataProvider<?> dp, final DataView<?> dataView) {
    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;
@@ -252,7 +271,7 @@
        };
    }
    private class RepositoriesProvider extends ListDataProvider<RepositoryModel> {
    private static class RepositoriesProvider extends ListDataProvider<RepositoryModel> {
        private static final long serialVersionUID = 1L;
@@ -279,7 +298,8 @@
                }
            } else if (index < (getData().size() - 1)) {
                // not last element. check next element for group match.
                if (getData().get(index - 1) instanceof GroupRepositoryModel && getData().get(index + 1) instanceof GroupRepositoryModel) {
                if (getData().get(index - 1) instanceof GroupRepositoryModel
                        && getData().get(index + 1) instanceof GroupRepositoryModel) {
                    // repository is sandwiched by group headers so this
                    // repository is the only element in the group. remove
                    // group.
@@ -304,9 +324,11 @@
        }
    }
    private class SortableRepositoriesProvider extends SortableDataProvider<RepositoryModel> {
    private static class SortableRepositoriesProvider extends SortableDataProvider<RepositoryModel> {
        private static final long serialVersionUID = 1L;
        private List<RepositoryModel> list = null;
        private List<RepositoryModel> list;
        protected SortableRepositoriesProvider(List<RepositoryModel> list) {
            this.list = list;
@@ -319,8 +341,9 @@
        @Override
        public int size() {
            if (list == null)
            if (list == null) {
                return 0;
            }
            return list.size();
        }
@@ -339,8 +362,9 @@
                Collections.sort(list, new Comparator<RepositoryModel>() {
                    @Override
                    public int compare(RepositoryModel o1, RepositoryModel o2) {
                        if (asc)
                        if (asc) {
                            return o1.lastChange.compareTo(o2.lastChange);
                        }
                        return o2.lastChange.compareTo(o1.lastChange);
                    }
                });
@@ -348,8 +372,9 @@
                Collections.sort(list, new Comparator<RepositoryModel>() {
                    @Override
                    public int compare(RepositoryModel o1, RepositoryModel o2) {
                        if (asc)
                        if (asc) {
                            return o1.name.compareTo(o2.name);
                        }
                        return o2.name.compareTo(o1.name);
                    }
                });
@@ -357,8 +382,9 @@
                Collections.sort(list, new Comparator<RepositoryModel>() {
                    @Override
                    public int compare(RepositoryModel o1, RepositoryModel o2) {
                        if (asc)
                        if (asc) {
                            return o1.owner.compareTo(o2.owner);
                        }
                        return o2.owner.compareTo(o1.owner);
                    }
                });
@@ -366,8 +392,9 @@
                Collections.sort(list, new Comparator<RepositoryModel>() {
                    @Override
                    public int compare(RepositoryModel o1, RepositoryModel o2) {
                        if (asc)
                        if (asc) {
                            return o1.description.compareTo(o2.description);
                        }
                        return o2.description.compareTo(o1.description);
                    }
                });
src/com/gitblit/wicket/panels/SearchPanel.java
@@ -43,12 +43,13 @@
    private static final long serialVersionUID = 1L;
    private boolean hasMore = false;
    private boolean hasMore;
    public SearchPanel(String wicketId, final String repositoryName, final String objectId, final String value, SearchType searchType, Repository r, int limit, int pageOffset) {
    public SearchPanel(String wicketId, final String repositoryName, final String objectId,
            final String value, SearchType searchType, Repository r, int limit, int pageOffset) {
        super(wicketId);
        boolean pageResults = limit <= 0;
        int itemsPerPage = GitBlit.self().settings().getInteger(Keys.web.itemsPerPage, 50);
        int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);
        if (itemsPerPage <= 1) {
            itemsPerPage = 50;
        }
@@ -59,7 +60,8 @@
        List<RevCommit> commits;
        if (pageResults) {
            // Paging result set
            commits = JGitUtils.searchRevlogs(r, objectId, value, searchType, pageOffset * itemsPerPage, itemsPerPage);
            commits = JGitUtils.searchRevlogs(r, objectId, value, searchType, pageOffset
                    * itemsPerPage, itemsPerPage);
        } else {
            // Fixed size result set
            commits = JGitUtils.searchRevlogs(r, objectId, value, searchType, 0, limit);
@@ -70,12 +72,14 @@
        hasMore = commits.size() >= itemsPerPage;
        // header
        add(new LinkPanel("header", "title", commit == null ? "":commit.getShortMessage(), CommitPage.class, WicketUtils.newObjectParameter(repositoryName, commit == null ? "":commit.getName())));
        add(new LinkPanel("header", "title", commit == null ? "" : commit.getShortMessage(),
                CommitPage.class, WicketUtils.newObjectParameter(repositoryName,
                        commit == null ? "" : commit.getName())));
        ListDataProvider<RevCommit> dp = new ListDataProvider<RevCommit>(commits);
        DataView<RevCommit> searchView = new DataView<RevCommit>("commit", dp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<RevCommit> item) {
                final RevCommit entry = item.getModelObject();
@@ -85,7 +89,9 @@
                // author search link
                String author = entry.getAuthorIdent().getName();
                LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author, SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId, author, SearchType.AUTHOR));
                LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author,
                        SearchPage.class, WicketUtils.newSearchParameter(repositoryName, objectId,
                                author, SearchType.AUTHOR));
                setPersonSearchTooltip(authorLink, author, SearchType.AUTHOR);
                item.add(authorLink);
@@ -98,8 +104,9 @@
                String shortMessage = entry.getShortMessage();
                String trimmedMessage = StringUtils.trimShortLog(shortMessage);
                // TODO highlight matches
                LinkPanel shortlog = new LinkPanel("commitShortMessage", "list subject", trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName()));
                LinkPanel shortlog = new LinkPanel("commitShortMessage", "list subject",
                        trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
                                repositoryName, entry.getName()));
                if (!shortMessage.equals(trimmedMessage)) {
                    WicketUtils.setHtmlTooltip(shortlog, shortMessage);
                }
@@ -107,9 +114,12 @@
                item.add(new RefsPanel("commitRefs", repositoryName, entry, allRefs));
                item.add(new BookmarkablePageLink<Void>("commit", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("commit", CommitPage.class, WicketUtils
                        .newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("commitdiff", CommitDiffPage.class,
                        WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils
                        .newObjectParameter(repositoryName, entry.getName())));
                WicketUtils.setAlternatingBackground(item, counter);
                counter++;
src/com/gitblit/wicket/panels/TagsPanel.java
@@ -49,17 +49,19 @@
        if (maxCount > 0) {
            // summary page
            // show tags page link
            add(new LinkPanel("header", "title", new StringResourceModel("gb.tags", this, null), TagsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
            add(new LinkPanel("header", "title", new StringResourceModel("gb.tags", this, null),
                    TagsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
        } else {
            // tags page
            // show repository summary page link
            add(new LinkPanel("header", "title", repositoryName, SummaryPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
            add(new LinkPanel("header", "title", repositoryName, SummaryPage.class,
                    WicketUtils.newRepositoryParameter(repositoryName)));
        }
        ListDataProvider<RefModel> tagsDp = new ListDataProvider<RefModel>(tags);
        DataView<RefModel> tagView = new DataView<RefModel>("tag", tagsDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            int counter;
            public void populateItem(final Item<RefModel> item) {
                RefModel entry = item.getModelObject();
@@ -72,8 +74,10 @@
                } else {
                    item.add(WicketUtils.newBlankImage("tagIcon"));
                }
                item.add(new LinkPanel("tagName", "list name", entry.getDisplayName(), CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getCommitId().getName())));
                item.add(new LinkPanel("tagName", "list name", entry.displayName, CommitPage.class,
                        WicketUtils.newObjectParameter(repositoryName, entry.getCommitId()
                                .getName())));
                String message;
                if (maxCount > 0) {
                    message = StringUtils.trimString(entry.getShortLog(), 40);
@@ -81,17 +85,29 @@
                    message = entry.getShortLog();
                }
                if (entry.isAnnotatedTag()) {
                    item.add(new LinkPanel("tagDescription", "list", message, TagPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getObjectId().getName())));
                    item.add(new LinkPanel("tagDescription", "list", message, TagPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getObjectId()
                                    .getName())));
                    Fragment fragment = new Fragment("tagLinks", "annotatedLinks", this);
                    fragment.add(new BookmarkablePageLink<Void>("view", TagPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getObjectId().getName())).setEnabled(entry.isAnnotatedTag()));
                    fragment.add(new BookmarkablePageLink<Void>("commit", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getCommitId().getName())));
                    fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                    fragment.add(new BookmarkablePageLink<Void>("view", TagPage.class, WicketUtils
                            .newObjectParameter(repositoryName, entry.getObjectId().getName()))
                            .setEnabled(entry.isAnnotatedTag()));
                    fragment.add(new BookmarkablePageLink<Void>("commit", CommitPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getCommitId()
                                    .getName())));
                    fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils
                            .newObjectParameter(repositoryName, entry.getName())));
                    item.add(fragment);
                } else {
                    item.add(new LinkPanel("tagDescription", "list", message, CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getObjectId().getName())));
                    item.add(new LinkPanel("tagDescription", "list", message, CommitPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getObjectId()
                                    .getName())));
                    Fragment fragment = new Fragment("tagLinks", "lightweightLinks", this);
                    fragment.add(new BookmarkablePageLink<Void>("commit", CommitPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getCommitId().getName())));
                    fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils.newObjectParameter(repositoryName, entry.getName())));
                    fragment.add(new BookmarkablePageLink<Void>("commit", CommitPage.class,
                            WicketUtils.newObjectParameter(repositoryName, entry.getCommitId()
                                    .getName())));
                    fragment.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils
                            .newObjectParameter(repositoryName, entry.getName())));
                    item.add(fragment);
                }
@@ -103,7 +119,8 @@
        if (tags.size() < maxCount || maxCount <= 0) {
            add(new Label("allTags", "").setVisible(false));
        } else {
            add(new LinkPanel("allTags", "link", new StringResourceModel("gb.allTags", this, null), TagsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
            add(new LinkPanel("allTags", "link", new StringResourceModel("gb.allTags", this, null),
                    TagsPage.class, WicketUtils.newRepositoryParameter(repositoryName)));
        }
    }
}
src/com/gitblit/wicket/panels/UsersPanel.java
@@ -42,10 +42,11 @@
        add(adminLinks.setVisible(showAdmin));
        final List<String> usernames = GitBlit.self().getAllUsernames();
        DataView<String> usersView = new DataView<String>("userRow", new ListDataProvider<String>(usernames)) {
        DataView<String> usersView = new DataView<String>("userRow", new ListDataProvider<String>(
                usernames)) {
            private static final long serialVersionUID = 1L;
            private int counter = 0;
            private int counter;
            @Override
            protected void onBeforeRender() {
                super.onBeforeRender();
@@ -54,11 +55,13 @@
            public void populateItem(final Item<String> item) {
                final String entry = item.getModelObject();
                LinkPanel editLink = new LinkPanel("username", "list", entry, EditUserPage.class, WicketUtils.newUsernameParameter(entry));
                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>("editUser", EditUserPage.class,
                        WicketUtils.newUsernameParameter(entry)));
                Link<Void> deleteLink = new Link<Void>("deleteUser") {
                    private static final long serialVersionUID = 1L;
@@ -73,7 +76,8 @@
                        }
                    }
                };
                deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format("Delete user \"{0}\"?", entry)));
                deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
                        "Delete user \"{0}\"?", entry)));
                userLinks.add(deleteLink);
                item.add(userLinks);
tests/com/gitblit/tests/GitBlitSuite.java
New file
@@ -0,0 +1,64 @@
package com.gitblit.tests;
import java.io.File;
import junit.extensions.TestSetup;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepository;
public class GitBlitSuite extends TestSetup {
    public static final File REPOSITORIES = new File("git");
    private GitBlitSuite(TestSuite suite) {
        super(suite);
    }
    public static Test suite() {
        TestSuite suite = new TestSuite();
        suite.addTestSuite(JGitUtilsTest.class);
        return new GitBlitSuite(suite);
    }
    public static Repository getHelloworldRepository() throws Exception {
        return new FileRepository(new File(REPOSITORIES, "helloworld.git"));
    }
    public static Repository getTicgitRepository() throws Exception {
        return new FileRepository(new File(REPOSITORIES, "ticgit.git"));
    }
    @Override
    protected void setUp() throws Exception {
        if (REPOSITORIES.exists() || REPOSITORIES.mkdirs()) {
            cloneOrFetch("helloworld.git", "https://github.com/git/hello-world.git", true);
            cloneOrFetch("nested/helloworld.git", "https://github.com/git/hello-world.git", true);
            cloneOrFetch("ticgit.git", "https://github.com/jeffWelling/ticgit.git", true);
        }
    }
    private void cloneOrFetch(String toFolder, String fromUrl, boolean bare) throws Exception {
        File folder = new File(REPOSITORIES, toFolder + (bare ? "" : "/.git"));
        if (folder.exists()) {
            System.out.print("Updating " + (bare ? "bare " : " ") + toFolder + "... ");
            FileRepository repository = new FileRepository(new File(REPOSITORIES, toFolder));
            Git git = new Git(repository);
            git.fetch().call();
            repository.close();
            System.out.println("done.");
        } else {
            System.out.println("Cloning " + (bare ? "bare " : " ") + toFolder + "... ");
            CloneCommand clone = new CloneCommand();
            clone.setBare(bare);
            clone.setCloneAllBranches(true);
            clone.setURI(fromUrl);
            clone.setDirectory(folder);
            clone.call();
            System.out.println("done.");
        }
    }
}
tests/com/gitblit/tests/JGitUtilsTest.java
New file
@@ -0,0 +1,130 @@
/*
 * Copyright 2011 gitblit.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitblit.tests;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Date;
import java.util.List;
import junit.framework.TestCase;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTree;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JGitUtils.DiffOutputType;
import com.gitblit.wicket.models.PathModel.PathChangeModel;
import com.gitblit.wicket.models.RefModel;
import com.gitblit.wicket.models.TicketModel;
public class JGitUtilsTest extends TestCase {
    private List<String> getRepositories() {
        return JGitUtils.getRepositoryList(GitBlitSuite.REPOSITORIES, true, true);
    }
    public void testFindRepositories() {
        List<String> list = getRepositories();
        assertTrue("No repositories found in " + GitBlitSuite.REPOSITORIES, list.size() > 0);
    }
    public void testOpenRepository() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        repository.close();
        assertTrue("Could not find repository!", repository != null);
    }
    public void testLastChangeRepository() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        Date date = JGitUtils.getLastChange(repository);
        repository.close();
        assertTrue("Could not get last repository change date!", date != null);
    }
    public void testFirstCommit() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        RevCommit commit = JGitUtils.getFirstCommit(repository, null);
        repository.close();
        assertTrue("Could not get first commit!", commit != null);
        assertTrue("Incorrect first commit!",
                commit.getName().equals("f554664a346629dc2b839f7292d06bad2db4aece"));
    }
    public void testRetrieveRevObject() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        RevCommit commit = JGitUtils.getCommit(repository, Constants.HEAD);
        RevTree tree = commit.getTree();
        RevObject object = JGitUtils.getRevObject(repository, tree, "java.java");
        repository.close();
        assertTrue("Object is null!", object != null);
    }
    public void testRetrieveStringContent() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        RevCommit commit = JGitUtils.getCommit(repository, Constants.HEAD);
        RevTree tree = commit.getTree();
        RevBlob blob = (RevBlob) JGitUtils.getRevObject(repository, tree, "java.java");
        String content = JGitUtils.getRawContentAsString(repository, blob);
        repository.close();
        assertTrue("Content is null!", content != null && content.length() > 0);
    }
    public void testFilesInCommit() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        RevCommit commit = JGitUtils.getCommit(repository,
                "1d0c2933a4ae69c362f76797d42d6bd182d05176");
        List<PathChangeModel> paths = JGitUtils.getFilesInCommit(repository, commit);
        repository.close();
        assertTrue("No changed paths found!", paths.size() == 1);
    }
    public void testCommitDiff() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        RevCommit commit = JGitUtils.getCommit(repository,
                "1d0c2933a4ae69c362f76797d42d6bd182d05176");
        String diff = JGitUtils.getCommitDiff(repository, commit, DiffOutputType.PLAIN);
        repository.close();
        assertTrue("Failed to generate diff!", diff != null && diff.length() > 0);
        String expected = "-        system.out.println(\"Hello World\");\n+        System.out.println(\"Hello World\"";
        assertTrue("Diff content mismatch!", diff.indexOf(expected) > -1);
    }
    public void testZip() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        File zipFile = new File(GitBlitSuite.REPOSITORIES, "helloworld.zip");
        FileOutputStream fos = new FileOutputStream(zipFile);
        boolean success = JGitUtils.zip(repository, null, Constants.HEAD, fos);
        assertTrue("Failed to generate zip file!", success);
        assertTrue(zipFile.length() > 0);
        fos.close();
        zipFile.delete();
        repository.close();
    }
    public void testTicGit() throws Exception {
        Repository repository = GitBlitSuite.getTicgitRepository();
        RefModel branch = JGitUtils.getTicketsBranch(repository);
        assertTrue("Ticgit branch does not exist!", branch != null);
        List<TicketModel> tickets = JGitUtils.getTickets(repository);
        repository.close();
        assertTrue("No tickets found!", tickets.size() > 0);
    }
}