James Moger
2013-05-19 6ef8d7cd37dcf8b742f23d461266ea7e94f0312d
Overhauled navigation and flattened look
8 files added
60 files modified
1529 ■■■■ changed files
src/main/java/com/gitblit/git/GitDaemonClient.java 1 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/AuthorizationStrategy.java 7 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.java 9 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties 6 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/ActivityPage.html 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/BasePage.html 14 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/BasePage.java 54 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/BlamePage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/BlobPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/CommitPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditTeamPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditUserPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/FederationPage.html 4 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/GitSearchPage.html 6 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/GitSearchPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.html 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/HistoryPage.html 6 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/HistoryPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/HomePage.html 16 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/HomePage.java 182 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/LogPage.html 6 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.html 4 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/MarkdownPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/MetricsPage.java 7 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/OverviewPage.html 60 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/OverviewPage.java 144 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/ProjectPage.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/ProjectsPage.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/RepositoriesPage.html 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/RepositoryPage.html 99 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java 65 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/RootPage.html 42 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/RootPage.java 170 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/RootSubPage.html 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/SendProposalPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/SummaryPage.html 6 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/SummaryPage.java 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/TagPage.java 8 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/TagsPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/TicketPage.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/TicketsPage.html 3 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/TicketsPage.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/UserPage.html 3 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/UsersPage.html 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/GravatarImage.java 14 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/PushesPanel.html 37 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/PushesPanel.java 206 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/RefsPanel.java 4 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html 27 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java 48 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/TagsPanel.java 4 ●●●● patch | view | raw | blame | history
src/main/resources/arrow_page.png patch | view | raw | blame | history
src/main/resources/arrow_project.png patch | view | raw | blame | history
src/main/resources/fork-black_16x16.png patch | view | raw | blame | history
src/main/resources/gitblit.css 143 ●●●● patch | view | raw | blame | history
src/main/resources/gitblt-favicon.png patch | view | raw | blame | history
src/main/java/com/gitblit/git/GitDaemonClient.java
@@ -50,6 +50,7 @@
import java.net.InetAddress;
import java.net.Socket;
import org.eclipse.jgit.transport.Daemon;
import org.eclipse.jgit.transport.PacketLineIn;
import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
src/main/java/com/gitblit/wicket/AuthorizationStrategy.java
@@ -24,7 +24,6 @@
import com.gitblit.Keys;
import com.gitblit.models.UserModel;
import com.gitblit.wicket.pages.BasePage;
import com.gitblit.wicket.pages.RepositoriesPage;
public class AuthorizationStrategy extends AbstractPageAuthorizationStrategy implements
        IUnauthorizedComponentInstantiationListener {
@@ -35,8 +34,8 @@
    @SuppressWarnings({ "unchecked", "rawtypes" })
    @Override
    protected boolean isPageAuthorized(Class pageClass) {
        if (RepositoriesPage.class.equals(pageClass)) {
            // allow all requests to get to the RepositoriesPage with its inline
        if (GitBlitWebApp.HOME_PAGE_CLASS.equals(pageClass)) {
            // allow all requests to get to the HomePage with its inline
            // authentication form
            return true;
        }
@@ -80,7 +79,7 @@
    public void onUnauthorizedInstantiation(Component component) {
        
        if (component instanceof BasePage) {
            throw new RestartResponseException(RepositoriesPage.class);
            throw new RestartResponseException(GitBlitWebApp.HOME_PAGE_CLASS);
        }
    }
}
src/main/java/com/gitblit/wicket/GitBlitWebApp.java
@@ -29,6 +29,7 @@
import com.gitblit.Keys;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.pages.ActivityPage;
import com.gitblit.wicket.pages.BasePage;
import com.gitblit.wicket.pages.BlamePage;
import com.gitblit.wicket.pages.BlobDiffPage;
import com.gitblit.wicket.pages.BlobPage;
@@ -47,6 +48,7 @@
import com.gitblit.wicket.pages.LuceneSearchPage;
import com.gitblit.wicket.pages.MarkdownPage;
import com.gitblit.wicket.pages.MetricsPage;
import com.gitblit.wicket.pages.OverviewPage;
import com.gitblit.wicket.pages.PatchPage;
import com.gitblit.wicket.pages.ProjectPage;
import com.gitblit.wicket.pages.ProjectsPage;
@@ -64,6 +66,8 @@
public class GitBlitWebApp extends WebApplication {
    public final static Class<? extends BasePage> HOME_PAGE_CLASS = RepositoriesPage.class;
    @Override
    public void init() {
        super.init();
@@ -88,7 +92,10 @@
        }
        // setup the standard gitweb-ish urls
//        mount("/repositories", RepositoriesPage.class);
        mount("/overview", OverviewPage.class, "r", "h");
        mount("/summary", SummaryPage.class, "r");
        mount("/commits", LogPage.class, "r", "h");
        mount("/log", LogPage.class, "r", "h");
        mount("/tags", TagsPage.class, "r");
        mount("/branches", BranchesPage.class, "r");
@@ -141,7 +148,7 @@
    @Override
    public Class<? extends Page> getHomePage() {
        return RepositoriesPage.class;
        return HOME_PAGE_CLASS;
    }
    
    @Override
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -450,4 +450,8 @@
gb.useIncrementalPushTagsDescription = on push, automatically tag each branch tip with an incremental revision number
gb.incrementalPushTagMessage = Auto-tagged [{0}] branch on push
gb.externalPermissions = {0} access permissions are externally maintained
gb.viewAccess = You do not have Gitblit read or write access
gb.viewAccess = You do not have Gitblit read or write access
gb.overview = overview
gb.home = home
gb.monthlyActivity = monthly activity
gb.myProfile = my profile
src/main/java/com/gitblit/wicket/pages/ActivityPage.html
@@ -5,6 +5,7 @@
      lang="en"> 
<body>
<wicket:extend>
    <div class="container">
    <div class="pageTitle">
        <h2><wicket:message key="gb.recentActivity"></wicket:message><small> <span class="hidden-phone">/ <span wicket:id="subheader">[days back]</span></span></small></h2>
    </div>
@@ -18,6 +19,7 @@
        </table>
    </div>
    <div wicket:id="activityPanel" style="padding-top:5px;" >[activity panel]</div>
    </div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/BasePage.html
@@ -27,7 +27,6 @@
                        <span wicket:id="gbVersion"></span>
                    </a> 
                </p>
                <div wicket:id="userPanel">[user panel]</div>
            </footer>
        </div>
@@ -35,7 +34,7 @@
        <style>
        @media (max-width: 979px) {
            .nav-collapse .nav > li > a:hover, .nav-collapse .dropdown-menu a:hover {
                background-color: #000070;
                background-color: #002060;
            }
            
            .navbar div > ul .dropdown-menu li a {
@@ -48,15 +47,4 @@
        <script type="text/javascript" src="bootstrap/js/jquery.js"></script>
        <script type="text/javascript" src="bootstrap/js/bootstrap.js"></script>        
    </body>
    <!-- user fragment -->
    <wicket:fragment wicket:id="userFragment">
        <span class="userPanel" wicket:id="username"></span>
        <span class="userPanel" wicket:id="loginLink"></span>
        <span class="hidden-phone">
            <span class="userPanel" wicket:id="separator"></span>
            <span class="userPanel"><a wicket:id="changePasswordLink"><wicket:message key="gb.changePassword"></wicket:message></a></span>
        </span>
    </wicket:fragment>
</html>
src/main/java/com/gitblit/wicket/pages/BasePage.java
@@ -32,16 +32,13 @@
import javax.servlet.http.HttpServletRequest;
import org.apache.wicket.Application;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.PageParameters;
import org.apache.wicket.RedirectToUrlException;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.protocol.http.RequestUtils;
import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
import org.slf4j.Logger;
@@ -59,9 +56,9 @@
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
import com.gitblit.wicket.GitBlitWebApp;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.LinkPanel;
public abstract class BasePage extends SessionPage {
@@ -134,21 +131,12 @@
            add(new Label("title", siteName));
        }
        ExternalLink rootLink = new ExternalLink("rootLink", urlFor(RepositoriesPage.class, null).toString());
        WicketUtils.setHtmlTooltip(rootLink, siteName);
        ExternalLink rootLink = new ExternalLink("rootLink", urlFor(GitBlitWebApp.HOME_PAGE_CLASS, null).toString());
        WicketUtils.setHtmlTooltip(rootLink, GitBlit.getString(Keys.web.siteName, Constants.NAME));
        add(rootLink);
        // Feedback panel for info, warning, and non-fatal error messages
        add(new FeedbackPanel("feedback"));
        // footer
        if (GitBlit.getBoolean(Keys.web.authenticateViewPages, true)
                || GitBlit.getBoolean(Keys.web.authenticateAdminPages, true)) {
            UserFragment userFragment = new UserFragment("userPanel", "userFragment", BasePage.this);
            add(userFragment);
        } else {
            add(new Label("userPanel", ""));
        }
        add(new Label("gbVersion", "v" + Constants.getVersion()));
        if (GitBlit.getBoolean(Keys.web.aggressiveHeapManagement, false)) {
@@ -371,41 +359,5 @@
            GitBlitWebSession.get().cacheRequest(getClass());
        }
        error(message, true);
    }
    /**
     * Panel fragment for displaying login or logout/change_password links.
     *
     */
    static class UserFragment extends Fragment {
        private static final long serialVersionUID = 1L;
        public UserFragment(String id, String markupId, MarkupContainer markupProvider) {
            super(id, markupId, markupProvider);
            GitBlitWebSession session = GitBlitWebSession.get();
            if (session.isLoggedIn()) {
                UserModel user = session.getUser();
                boolean editCredentials = GitBlit.self().supportsCredentialChanges(user);
                boolean standardLogin = session.authenticationType.isStandard();
                // username, logout, and change password
                add(new Label("username", user.getDisplayName() + ":"));
                add(new LinkPanel("loginLink", null, markupProvider.getString("gb.logout"),
                        LogoutPage.class).setVisible(standardLogin));
                // quick and dirty hack for showing a separator
                add(new Label("separator", "|").setVisible(standardLogin && editCredentials));
                add(new BookmarkablePageLink<Void>("changePasswordLink",
                        ChangePasswordPage.class).setVisible(editCredentials));
            } else {
                // login
                add(new Label("username").setVisible(false));
                add(new Label("loginLink").setVisible(false));
                add(new Label("separator").setVisible(false));
                add(new Label("changePasswordLink").setVisible(false));
            }
        }
    }
}
src/main/java/com/gitblit/wicket/pages/BlamePage.java
@@ -147,6 +147,11 @@
        return getString("gb.blame");
    }
    
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return TreePage.class;
    }
    protected String missingBlob(String blobPath, RevCommit commit) {
        StringBuilder sb = new StringBuilder();
        sb.append("<div class=\"alert alert-error\">");
src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java
@@ -82,4 +82,9 @@
    protected String getPageName() {
        return getString("gb.diff");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return TreePage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/BlobPage.java
@@ -213,4 +213,9 @@
    protected String getPageName() {
        return getString("gb.view");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return TreePage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -180,6 +180,11 @@
    protected String getPageName() {
        return getString("gb.commitdiff");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return LogPage.class;
    }
    private RevCommit getCommit(Repository r, String rev)
    {
src/main/java/com/gitblit/wicket/pages/CommitPage.java
@@ -222,4 +222,9 @@
    protected String getPageName() {
        return getString("gb.commit");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return LogPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -120,6 +120,11 @@
    protected boolean requiresPageMap() {
        return true;
    }
    @Override
    protected Class<? extends BasePage> getRootNavPageClass() {
        return RepositoriesPage.class;
    }
    protected void setupPage(RepositoryModel model) {
        this.repositoryModel = model;
src/main/java/com/gitblit/wicket/pages/EditTeamPage.java
@@ -80,6 +80,11 @@
    protected boolean requiresPageMap() {
        return true;
    }
    @Override
    protected Class<? extends BasePage> getRootNavPageClass() {
        return UsersPage.class;
    }
    protected void setupPage(final TeamModel teamModel) {
        if (isCreate) {
src/main/java/com/gitblit/wicket/pages/EditUserPage.java
@@ -80,6 +80,11 @@
    protected boolean requiresPageMap() {
        return true;
    }
    @Override
    protected Class<? extends BasePage> getRootNavPageClass() {
        return UsersPage.class;
    }
    protected void setupPage(final UserModel userModel) {
        if (isCreate) {
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.html
@@ -6,7 +6,7 @@
<body>
<wicket:extend>
<div class="container">
    <h2>Empty Repository</h2>
    <p></p>
        <div class="row">
@@ -49,6 +49,7 @@
            <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - A free Git and Mercurial client for Windows & Mac</li>
            <li><a href="http://www.git-tower.com/">Tower</a> - a Mac OS X Git client</li>
        </ul>
</div>
</wicket:extend>    
</body>
</html>
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage.java
@@ -61,4 +61,9 @@
        add(new Label("cloneSyntax", MessageFormat.format("git clone {0}", primaryUrl)));
        add(new Label("remoteSyntax", MessageFormat.format("git remote add gitblit {0}\ngit push gitblit master", primaryUrl)));
    }
    @Override
    protected Class<? extends BasePage> getRootNavPageClass() {
        return RepositoriesPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_es.html
@@ -8,7 +8,7 @@
<body>
<wicket:extend>
<div class="container">
        <h2>Repositorio Vac&iacute;o</h2>
        <p></p>
        <div class="row">
@@ -51,6 +51,7 @@
            <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - Un cliente Git gratuito para Mac, Mercurial, y SVN</li>
            <li><a href="http://www.git-tower.com/">Tower</a> - Cliente Git para Mac OS X</li>
        </ul>
</div>
</wicket:extend>    
</body>
</html>
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_ko.html
@@ -6,7 +6,7 @@
<body>
<wicket:extend>
<div class="container">
        <h2>비어있는 저장소</h2>
        <p></p>
        <div class="row">
@@ -53,6 +53,7 @@
            <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - A free Mac Client for Git, Mercurial, and SVN</li>
            <li><a href="http://www.git-tower.com/">Tower</a> - a Mac OS X Git client</li>
        </ul>
</div>
</wicket:extend>    
</body>
</html>
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_nl.html
@@ -6,7 +6,7 @@
<body>
<wicket:extend>
<div class="container">
    <h2>Empty Repository</h2>
    <p></p>
        <div class="row">
@@ -49,6 +49,7 @@
            <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - Een gratis Mac Client voor Git, Mercurial, en SVN</li>
            <li><a href="http://www.git-tower.com/">Tower</a> - een Mac OS X Git client</li>
        </ul>
</div>
</wicket:extend>    
</body>
</html>
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pl.html
@@ -8,7 +8,7 @@
<body>
<wicket:extend>
<div class="container">
        <h2>Puste repozytorium</h2>
        <p></p>
        <div class="row">
@@ -51,6 +51,7 @@
            <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - darmowy klient GIT, Mercurial i SVN na Mac OS X</li>
            <li><a href="http://www.git-tower.com/">Tower</a> - klient GIT na Mac OS X</li>
        </ul>
</div>
</wicket:extend>    
</body>
</html>
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_pt_BR.html
@@ -6,7 +6,7 @@
<body>
<wicket:extend>
<div class="container">
    <h2>Repositório Vazio</h2>
    <p></p>
        <div class="row">
@@ -49,6 +49,7 @@
            <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - Client gratuito para o Mac que suporta Git, Mercurial e SVN</li>
            <li><a href="http://www.git-tower.com/">Tower</a> - um Cliente do Git para Mac OS X</li>
        </ul>
</div>
</wicket:extend>    
</body>
</html>
src/main/java/com/gitblit/wicket/pages/EmptyRepositoryPage_zh_CN.html
@@ -6,7 +6,7 @@
<body>
<wicket:extend>
<div class="container">
    <h2>空版本库</h2>
    <p></p>
        <div class="row">
@@ -51,6 +51,7 @@
            <li><a href="http://www.sourcetreeapp.com/">SourceTree</a> - 免费的 Mac Git Mercurial 以及 SVN 客户端, Mercurial, and SVN</li>
            <li><a href="http://www.git-tower.com/">Tower</a> - Mac OS X Git 客户端</li>
        </ul>
</div>
</wicket:extend>    
</body>
</html>
src/main/java/com/gitblit/wicket/pages/FederationPage.html
@@ -5,13 +5,13 @@
      lang="en"> 
<body>
<wicket:extend>
<div class="container">
    <div wicket:id="federationTokensPanel">[federation tokens panel]</div>
        
    <div style="padding-top: 10px;" wicket:id="federationProposalsPanel">[federation proposals panel]</div>
    <div style="padding-top: 10px;" wicket:id="federationRegistrationsPanel">[federation registrations panel]</div>
</div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/FederationRegistrationPage.java
@@ -92,4 +92,9 @@
        };
        add(dataView);
    }
    @Override
    protected Class<? extends BasePage> getRootNavPageClass() {
        return FederationPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/GitSearchPage.html
@@ -8,8 +8,8 @@
<wicket:extend>
    <!-- pager links -->
    <div style="padding-top:5px;">
        <a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message></a>
    <div class="page_nav2">
        <a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a>
    </div>
    
    <!-- history -->
@@ -17,7 +17,7 @@
    <!-- pager links -->
    <div style="padding-bottom:5px;">
        <a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message></a>
        <a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a>
    </div>
</wicket:extend>
src/main/java/com/gitblit/wicket/pages/GitSearchPage.java
@@ -66,4 +66,9 @@
    protected String getPageName() {
        return getString("gb.search");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return LogPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/GravatarProfilePage.html
@@ -5,6 +5,7 @@
      lang="en"> 
<body>
<wicket:extend>
<div class="container">
    <div class="pageTitle">
        <h2>Gravatar<small> / <span wicket:id="username">[username]</span></small></h2>
    </div>
@@ -15,6 +16,7 @@
    <p></p>
    <a wicket:id="profileLink"><wicket:message key="gb.completeGravatarProfile">[Complete profile on Gravatar.com]</wicket:message></a>
    <p></p>
</div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/HistoryPage.html
@@ -8,8 +8,8 @@
<wicket:extend>
    <!-- pager links -->
    <div style="padding-top:5px;">
        <a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message></a>
    <div class="page_nav2">
        <a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a>
    </div>
    
    <!-- history -->
@@ -17,7 +17,7 @@
    <!-- pager links -->
    <div style="padding-bottom:5px;">
        <a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message></a>
        <a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a>
    </div>
</wicket:extend>
src/main/java/com/gitblit/wicket/pages/HistoryPage.java
@@ -62,4 +62,9 @@
    protected String getPageName() {
        return getString("gb.history");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return TreePage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/HomePage.html
New file
@@ -0,0 +1,16 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
      xml:lang="en"
      lang="en">
<body>
<wicket:extend>
<div class="container">
    <div class="markdown" style="padding-bottom:5px;" wicket:id="repositoriesMessage">[repositories message]</div>
<!--     <div wicket:id="repositoriesPanel">[repositories panel]</div> -->
</div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/HomePage.java
New file
@@ -0,0 +1,182 @@
/*
 * 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.wicket.pages;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.eclipse.jgit.lib.Constants;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebApp;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.PageRegistration;
import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;
public class HomePage extends RootPage {
    public HomePage() {
        super();
        setup(null);
    }
    public HomePage(PageParameters params) {
        super(params);
        setup(params);
    }
    @Override
    protected boolean reusePageParameters() {
        return true;
    }
    private void setup(PageParameters params) {
        setupPage("", "");
        // check to see if we should display a login message
        boolean authenticateView = GitBlit.getBoolean(Keys.web.authenticateViewPages, true);
        if (authenticateView && !GitBlitWebSession.get().isLoggedIn()) {
            String messageSource = GitBlit.getString(Keys.web.loginMessage, "gitblit");
            String message = readMarkdown(messageSource, "login.mkd");
            Component repositoriesMessage = new Label("repositoriesMessage", message);
            add(repositoriesMessage.setEscapeModelStrings(false));
            add(new Label("repositoriesPanel"));
            return;
        }
        // Load the markdown welcome message
        String messageSource = GitBlit.getString(Keys.web.repositoriesMessage, "gitblit");
        String message = readMarkdown(messageSource, "welcome.mkd");
        Component repositoriesMessage = new Label("repositoriesMessage", message)
                .setEscapeModelStrings(false).setVisible(message.length() > 0);
        add(repositoriesMessage);
//        List<RepositoryModel> repositories = getRepositories(params);
//
//        RepositoriesPanel repositoriesPanel = new RepositoriesPanel("repositoriesPanel", showAdmin,
//                true, repositories, true, getAccessRestrictions());
//        // push the panel down if we are hiding the admin controls and the
//        // welcome message
//        if (!showAdmin && !repositoriesMessage.isVisible()) {
//            WicketUtils.setCssStyle(repositoriesPanel, "padding-top:5px;");
//        }
//        add(repositoriesPanel);
    }
    @Override
    protected void addDropDownMenus(List<PageRegistration> pages) {
        PageParameters params = getPageParameters();
        DropDownMenuRegistration menu = new DropDownMenuRegistration("gb.filters",
                GitBlitWebApp.HOME_PAGE_CLASS);
        // preserve time filter option on repository choices
        menu.menuItems.addAll(getRepositoryFilterItems(params));
        // preserve repository filter option on time choices
        menu.menuItems.addAll(getTimeFilterItems(params));
        if (menu.menuItems.size() > 0) {
            // Reset Filter
            menu.menuItems.add(new DropDownMenuItem(getString("gb.reset"), null, null));
        }
        pages.add(menu);
    }
    private String readMarkdown(String messageSource, String resource) {
        String message = "";
        if (messageSource.equalsIgnoreCase("gitblit")) {
            // Read default message
            message = readDefaultMarkdown(resource);
        } else {
            // Read user-supplied message
            if (!StringUtils.isEmpty(messageSource)) {
                File file = GitBlit.getFileOrFolder(messageSource);
                if (file.exists()) {
                    try {
                        FileInputStream fis = new FileInputStream(file);
                        InputStreamReader reader = new InputStreamReader(fis,
                                Constants.CHARACTER_ENCODING);
                        message = MarkdownUtils.transformMarkdown(reader);
                        reader.close();
                    } catch (Throwable t) {
                        message = getString("gb.failedToRead") + " " + file;
                        warn(message, t);
                    }
                } else {
                    message = messageSource + " " + getString("gb.isNotValidFile");
                }
            }
        }
        return message;
    }
    private String readDefaultMarkdown(String file) {
        String base = file.substring(0, file.lastIndexOf('.'));
        String ext = file.substring(file.lastIndexOf('.'));
        String lc = getLanguageCode();
        String cc = getCountryCode();
        // try to read file_en-us.ext, file_en.ext, file.ext
        List<String> files = new ArrayList<String>();
        if (!StringUtils.isEmpty(lc)) {
            if (!StringUtils.isEmpty(cc)) {
                files.add(base + "_" + lc + "-" + cc + ext);
                files.add(base + "_" + lc + "_" + cc + ext);
            }
            files.add(base + "_" + lc + ext);
        }
        files.add(file);
        for (String name : files) {
            String message;
            InputStreamReader reader = null;
            try {
                InputStream is = getClass().getResourceAsStream("/" + name);
                if (is == null) {
                    continue;
                }
                reader = new InputStreamReader(is, Constants.CHARACTER_ENCODING);
                message = MarkdownUtils.transformMarkdown(reader);
                reader.close();
                return message;
            } catch (Throwable t) {
                message = MessageFormat.format(getString("gb.failedToReadMessage"), file);
                error(message, t, false);
                return message;
            } finally {
                if (reader != null) {
                    try {
                        reader.close();
                    } catch (Exception e) {
                    }
                }
            }
        }
        return MessageFormat.format(getString("gb.failedToReadMessage"), file);
    }
}
src/main/java/com/gitblit/wicket/pages/LogPage.html
@@ -8,8 +8,8 @@
<wicket:extend>
    <!-- pager links -->
    <div style="padding-top:5px;">
        <a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message></a>
    <div class="page_nav2">
        <a wicket:id="firstPageTop"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageTop">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageTop"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a>
    </div>
    
    <!-- log -->
@@ -17,7 +17,7 @@
    <!-- pager links -->
    <div style="padding-bottom:5px;">
        <a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom"><wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message></a>
        <a wicket:id="firstPageBottom"><wicket:message key="gb.pageFirst"></wicket:message></a> | <a wicket:id="prevPageBottom">&laquo; <wicket:message key="gb.pagePrevious"></wicket:message></a> | <a wicket:id="nextPageBottom"><wicket:message key="gb.pageNext"></wicket:message> &raquo;</a>
    </div>
    
</wicket:extend>
src/main/java/com/gitblit/wicket/pages/LuceneSearchPage.html
@@ -14,6 +14,7 @@
      
<wicket:extend>
<body onload="document.getElementById('query').focus(); prettyPrint();">
<div class="container">
    <div class="pageTitle">
        <h2><wicket:message key="gb.search"></wicket:message></h2>
    </div>
@@ -79,7 +80,8 @@
    <!-- pager links -->
    <div wicket:id="bottomPager"></div>
    </div>
    </div>
    </div>
</body>
    <wicket:fragment wicket:id="tagsPanel">
src/main/java/com/gitblit/wicket/pages/MarkdownPage.java
@@ -70,4 +70,9 @@
    protected String getPageName() {
        return getString("gb.markdown");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return DocsPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/MetricsPage.java
@@ -85,7 +85,7 @@
            provider.addAxis(commitAxis);
            provider.setLineStyles(new LineStyle[] { new LineStyle(2, 4, 0), new LineStyle(0, 4, 1) });
            provider.addShapeMarker(new ShapeMarker(MarkerType.CIRCLE, Color.BLUE, 1, -1, 5));
            provider.addShapeMarker(new ShapeMarker(MarkerType.CIRCLE, Color.decode("#002060"), 1, -1, 5));
            add(new Chart(wicketId, provider));
        } else {
@@ -181,4 +181,9 @@
    protected String getPageName() {
        return getString("gb.metrics");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return SummaryPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/OverviewPage.html
New file
@@ -0,0 +1,60 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
      xml:lang="en"
      lang="en">
<body>
<wicket:extend>
    <div class="row" style="clear:both;">
    <div class="span6 hidden-phone" style="border-right: 1px solid #ddd; margin-right: -1px;">
        <!-- Repository info -->
        <div>
            <table class="summary">
                <tr><th><wicket:message key="gb.description">[description]</wicket:message></th><td><span wicket:id="repositoryDescription">[repository description]</span></td></tr>
                <tr><th><wicket:message key="gb.owners">[owner]</wicket:message></th><td><span wicket:id="repositoryOwners"><span wicket:id="owner"></span><span wicket:id="comma"></span></span></td></tr>
                <tr><th><wicket:message key="gb.lastChange">[last change]</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr>
                <tr><th><wicket:message key="gb.size">[size]</wicket:message></th><td><span wicket:id="repositorySize">[repository size]</span></td></tr>
                <tr class="hidden-phone hidden-tablet"><th style="vertical-align: top;"><wicket:message key="gb.stats">[stats]</wicket:message></th>
                <td><div><span wicket:id="branchStats">[branch stats]</span> <span class="link"><a wicket:id="metrics"><wicket:message key="gb.metrics">[metrics]</wicket:message></a></span></div>
                    <span class="hidden-tablet" style="margin: 10px 0px;" id="chartDaily"></span>
                </td></tr>
            </table>
            <ul class="nav nav-tabs" style="padding-top:10px;">
                <li class="active"><a data-toggle="tab" href="#branches"><wicket:message key="gb.branches">[branches]</wicket:message></a></li>
                <li><a data-toggle="tab" href="#tags"><wicket:message key="gb.tags">[tags]</wicket:message></a></li>
                <li><a data-toggle="tab" href="#forks"><wicket:message key="gb.forks">[forks]</wicket:message></a></li>
            </ul>
            <div class="tab-content">
                <div class="tab-pane active" id="branches">
                    <!-- branches -->
                    <div style="padding: 0 15px 15px 0px;" wicket:id="branchesPanel">[branches panel]</div>
                </div>
                <div class="tab-pane" id="tags">
                    <!-- tags -->
                    <div style="padding: 0 15px 15px 0px;" wicket:id="tagsPanel">[tags panel]</div>
                </div>
                <div class="tab-pane" id="forks">
                </div>
            </div>
        </div>
    </div>
        <!-- pushes -->
        <div class="span6">
            <div class="hidden-tablet" style="padding-bottom: 10px; margin-bottom: 10px; border-bottom: 1px solid #ddd;" wicket:id="repositoryUrlPanel">[repository url panel]</div>
            <div wicket:id="pushesPanel">[pushes panel]</div>
        </div>
    </div>
    <wicket:fragment wicket:id="ownersFragment">
    </wicket:fragment>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/OverviewPage.java
New file
@@ -0,0 +1,144 @@
/*
 * 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.wicket.pages;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.eclipse.jgit.lib.Repository;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.Metric;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.charting.GoogleChart;
import com.gitblit.wicket.charting.GoogleCharts;
import com.gitblit.wicket.charting.GoogleLineChart;
import com.gitblit.wicket.panels.BranchesPanel;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.PushesPanel;
import com.gitblit.wicket.panels.RepositoryUrlPanel;
import com.gitblit.wicket.panels.TagsPanel;
public class OverviewPage extends RepositoryPage {
    public OverviewPage(PageParameters params) {
        super(params);
        int numberRefs = GitBlit.getInteger(Keys.web.summaryRefsCount, 5);
        Repository r = getRepository();
        final RepositoryModel model = getRepositoryModel();
        UserModel user = GitBlitWebSession.get().getUser();
        if (user == null) {
            user = UserModel.ANONYMOUS;
        }
        List<Metric> metrics = null;
        Metric metricsTotal = null;
        if (!model.skipSummaryMetrics && GitBlit.getBoolean(Keys.web.generateActivityGraph, true)) {
            metrics = GitBlit.self().getRepositoryDefaultMetrics(model, r);
            metricsTotal = metrics.remove(0);
        }
        addSyndicationDiscoveryLink();
        // repository description
        add(new Label("repositoryDescription", getRepositoryModel().description));
        // owner links
        final List<String> owners = new ArrayList<String>(getRepositoryModel().owners);
        ListDataProvider<String> ownersDp = new ListDataProvider<String>(owners);
        DataView<String> ownersView = new DataView<String>("repositoryOwners", ownersDp) {
            private static final long serialVersionUID = 1L;
            int counter = 0;
            public void populateItem(final Item<String> item) {
                UserModel ownerModel = GitBlit.self().getUserModel(item.getModelObject());
                if (ownerModel != null) {
                    item.add(new LinkPanel("owner", null, ownerModel.getDisplayName(), UserPage.class,
                            WicketUtils.newUsernameParameter(ownerModel.username)).setRenderBodyOnly(true));
                } else {
                    item.add(new Label("owner").setVisible(false));
                }
                counter++;
                item.add(new Label("comma", ",").setVisible(counter < owners.size()));
                item.setRenderBodyOnly(true);
            }
        };
        ownersView.setRenderBodyOnly(true);
        add(ownersView);
        add(WicketUtils.createTimestampLabel("repositoryLastChange",
                JGitUtils.getLastChange(r), getTimeZone(), getTimeUtils()));
        add(new Label("repositorySize", model.size));
        if (metricsTotal == null) {
            add(new Label("branchStats", ""));
        } else {
            add(new Label("branchStats",
                    MessageFormat.format(getString("gb.branchStats"), metricsTotal.count,
                            metricsTotal.tag, getTimeUtils().duration(metricsTotal.duration))));
        }
        add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class,
                WicketUtils.newRepositoryParameter(repositoryName)));
        add(new RepositoryUrlPanel("repositoryUrlPanel", false, user, model));
        PushesPanel pushes = new PushesPanel("pushesPanel", getRepositoryModel(), r, 10, 0);
        add(pushes);
        add(new TagsPanel("tagsPanel", repositoryName, r, numberRefs).hideIfEmpty());
        add(new BranchesPanel("branchesPanel", getRepositoryModel(), r, numberRefs, false).hideIfEmpty());
        // Display an activity line graph
        insertActivityGraph(metrics);
    }
    @Override
    protected String getPageName() {
        return getString("gb.overview");
    }
    private void insertActivityGraph(List<Metric> metrics) {
        if ((metrics != null) && (metrics.size() > 0)
                && GitBlit.getBoolean(Keys.web.generateActivityGraph, true)) {
            // daily line chart
            GoogleChart chart = new GoogleLineChart("chartDaily", "", "unit",
                    getString("gb.commits"));
            for (Metric metric : metrics) {
                chart.addValue(metric.name, metric.count);
            }
            chart.setWidth(375);
            chart.setHeight(150);
            GoogleCharts charts = new GoogleCharts();
            charts.addChart(chart);
            add(new HeaderContributor(charts));
        }
    }
}
src/main/java/com/gitblit/wicket/pages/ProjectPage.html
@@ -7,7 +7,7 @@
<body>
<wicket:extend>
<div class="container">
    <div class="row">
        <div class="span12">
            <h2><span wicket:id="projectTitle"></span> <small><span wicket:id="projectDescription"></span></small>
@@ -65,6 +65,7 @@
        
        </div>
    </div>
    </div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/ProjectsPage.html
@@ -6,6 +6,7 @@
<body>
<wicket:extend>
<div class="container">
    <div class="markdown" style="padding-bottom:5px;" wicket:id="projectsMessage">[projects message]</div>
    
    <table class="repositories">
@@ -31,7 +32,7 @@
               </tr>
        </tbody>
    </table>
</div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/RepositoriesPage.html
@@ -6,9 +6,11 @@
<body>
<wicket:extend>
<div class="container">
    <div class="markdown" style="padding-bottom:5px;" wicket:id="repositoriesMessage">[repositories message]</div>
    
    <div wicket:id="repositoriesPanel">[repositories panel]</div>
</div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/RepositoryPage.html
@@ -6,77 +6,58 @@
<body>
    <wicket:extend>
        <!-- page nav links -->
        <div class="navbar navbar-fixed-top">
            <div class="navbar-inner">
        <div class="repositorynavbar">
            <div class="repositorynavbar-inner">
                <div class="container">
                    <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                      </a>
                    <a class="brand" wicket:id="rootLink">
                        <img src="gitblt_25_white.png" width="79" height="25" alt="gitblit" class="logo"/>
                    </a>
                    <div class="nav-collapse" wicket:id="navPanel"></div>
                    <a class="hidden-phone hidden-tablet brand" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
                        <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>
                    </a>
                    <form class="hidden-phone hidden-tablet pull-right" style="margin-top:10px;" wicket:id="searchForm">
                        <span class="search">
                            <select class="small" wicket:id="searchType"/>
                            <input type="text" id="searchBox" wicket:id="searchBox" value=""/>
                        </span>
                    </form>
                    <div class="title">
                        <div class="row">
                            <!-- search form -->
                            <div class="hidden-phone pull-right">
                                <form class="form-search" style="margin: 0px;" wicket:id="searchForm">
                                    <div class="input-append">
                                        <select class="span2" style="border-radius: 4px;" wicket:id="searchType"/>
                                        <input type="text" class="search-query" style="width: 170px;border-radius: 14px 0 0 14px; padding-left: 14px;" id="searchBox" wicket:id="searchBox" value=""/>
                                        <button class="btn" style="border-radius: 0 14px 14px 0px;margin-left:-5px;" type="submit"><i class="icon-search"></i></button>
                                    </div>
                                </form>
                            </div>
                            <div class="span7">
                                <div>
                                    <span class="project" wicket:id="projectTitle">[project title]</span>/<span class="repository" wicket:id="repositoryName">[repository name]</span>
                                    <a class="hidden-phone hidden-tablet" style="text-decoration: none;" wicket:id="syndication" wicket:message="title:gb.feed">
                                        <img style="border:0px;vertical-align:baseline;" src="feed_16x16.png"></img>
                                    </a>
                                </div>
                                <span wicket:id="originRepository">[origin repository]</span>
                            </div>
                        </div>
                    </div>
                    <div>
                        <div class="hidden-phone btn-group pull-right" style="margin-top:5px;">
                            <!-- future spot for other repo buttons -->
<!--                             <a class="btn"><i class="icon-star-empty" style="padding-right:3px;"></i>star</a> -->
<!--                             <a class="btn"><i class="icon-envelope" style="padding-right:3px;"></i>subscribe</a> -->
                            <a class="btn" wicket:id="myForkLink"><img style="border:0px;vertical-align:middle;" src="fork-black_16x16.png"></img> <wicket:message key="gb.myFork"></wicket:message></a>
                            <a class="btn" wicket:id="forkLink"><img style="border:0px;vertical-align:middle;" src="fork-black_16x16.png"></img> <wicket:message key="gb.fork"></wicket:message></a>
                        </div>
                        <div wicket:id="repositoryNavPanel"></div>
                    </div>
                </div>
            </div>
        </div>
                
        <!-- page content -->
        <div class="container">
            <div style="text-align:center;" wicket:id="feedback">[Feedback Panel]</div>
            <!-- page header -->
            <div class="pageTitle">
                <div class="row">
                    <div class="controls">
                        <span wicket:id="workingCopyIndicator"></span>
                        <span class="hidden-phone hidden-tablet" wicket:id="forksProhibitedIndicator"></span>
                        <div class="hidden-phone btn-group pull-right">
                            <!-- future spot for other repo buttons -->
                            <a class="btn btn-info" wicket:id="myForkLink"><img style="border:0px;vertical-align:middle;" src="fork_16x16.png"></img> <wicket:message key="gb.myFork"></wicket:message></a>
                            <a class="btn btn-info" wicket:id="forkLink"><img style="border:0px;vertical-align:middle;" src="fork_16x16.png"></img> <wicket:message key="gb.fork"></wicket:message></a>
                        </div>
                    </div>
                    <div class="span7">
                        <div><span class="project" wicket:id="projectTitle">[project title]</span>/<span class="repository" wicket:id="repositoryName">[repository name]</span> <span class="hidden-phone"><span wicket:id="pageName">[page name]</span></span></div>
                        <span wicket:id="originRepository">[origin repository]</span>
                    </div>
                </div>
            </div>
            <wicket:child />
        </div>
        
        <wicket:fragment wicket:id="originFragment">
            <p class="originRepository"><wicket:message key="gb.forkedFrom">[forked from]</wicket:message> <span wicket:id="originRepository">[origin repository]</span></p>
        </wicket:fragment>
        <wicket:fragment wicket:id="workingCopyFragment">
            <div class="pull-right" style="padding-top:0px;margin-bottom:0px;padding-left:5px">
                <span class="alert alert-info" style="padding: 6px 14px 6px 14px;vertical-align: middle;"><i class="icon-exclamation-sign"></i>&nbsp;<span class="hidden-phone" wicket:id="workingCopy" style="font-weight:bold;">[working copy]</span></span>
            </div>
        </wicket:fragment>
        <wicket:fragment wicket:id="forksProhibitedFragment">
            <div class="pull-right" style="padding-top:0px;margin-bottom:0px;padding-left:5px">
                <span class="alert alert-error" style="padding: 6px 14px 6px 14px;vertical-align: middle;"><i class="icon-ban-circle"></i>&nbsp;<span class="hidden-phone" wicket:id="forksProhibited" style="font-weight:bold;">[forks prohibited]</span></span>
            </div>
        </wicket:fragment>
    </wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
@@ -54,6 +54,7 @@
import com.gitblit.models.UserModel;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.PushLogUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TicgitUtils;
import com.gitblit.wicket.GitBlitWebSession;
@@ -65,7 +66,7 @@
import com.gitblit.wicket.panels.NavigationPanel;
import com.gitblit.wicket.panels.RefsPanel;
public abstract class RepositoryPage extends BasePage {
public abstract class RepositoryPage extends RootPage {
    protected final String projectName;
    protected final String repositoryName;
@@ -125,7 +126,7 @@
        // standard page links
        List<PageRegistration> pages = new ArrayList<PageRegistration>(registeredPages.values());
        NavigationPanel navigationPanel = new NavigationPanel("navPanel", getClass(), pages);
        NavigationPanel navigationPanel = new NavigationPanel("repositoryNavPanel", getRepoNavPageClass(), pages);
        add(navigationPanel);
        add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest()
@@ -139,7 +140,16 @@
        // set stateless page preference
        setStatelessHint(true);
    }
    @Override
    protected Class<? extends BasePage> getRootNavPageClass() {
        return RepositoriesPage.class;
    }
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return getClass();
    }
    private Map<String, PageRegistration> registerPages() {
        PageParameters params = null;
        if (!StringUtils.isEmpty(repositoryName)) {
@@ -147,26 +157,28 @@
        }
        Map<String, PageRegistration> pages = new LinkedHashMap<String, PageRegistration>();
        Repository r = getRepository();
        RepositoryModel model = getRepositoryModel();
        // standard links
        pages.put("repositories", new PageRegistration("gb.repositories", RepositoriesPage.class));
        pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
        pages.put("log", new PageRegistration("gb.log", LogPage.class, params));
        pages.put("branches", new PageRegistration("gb.branches", BranchesPage.class, params));
        pages.put("tags", new PageRegistration("gb.tags", TagsPage.class, params));
        if (PushLogUtils.getPushLogBranch(r) == null) {
            pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
        } else {
            pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
//            pages.put("overview", new PageRegistration("gb.overview", OverviewPage.class, params));
        }
        pages.put("commits", new PageRegistration("gb.commits", LogPage.class, params));
        pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params));
        if (GitBlit.getBoolean(Keys.web.allowForking, true)) {
            pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params));
        }
        // conditional links
        Repository r = getRepository();
        RepositoryModel model = getRepositoryModel();
        // per-repository extra page links
        if (model.useTickets && TicgitUtils.getTicketsBranch(r) != null) {
            pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, params));
        }
        if (model.useDocs) {
        if (model.showReadme || model.useDocs) {
            pages.put("docs", new PageRegistration("gb.docs", DocsPage.class, params));
        }
        if (JGitUtils.getPagesBranch(r) != null) {
@@ -246,22 +258,11 @@
            }
        }
        
        if (getRepositoryModel().isBare) {
            add(new Label("workingCopyIndicator").setVisible(false));
        } else {
            Fragment wc = new Fragment("workingCopyIndicator", "workingCopyFragment", this);
            Label lbl = new Label("workingCopy", getString("gb.workingCopy"));
            WicketUtils.setHtmlTooltip(lbl,  getString("gb.workingCopyWarning"));
            wc.add(lbl);
            add(wc);
        }
        // fork controls
        if (!allowForkControls() || user == null || !user.isAuthenticated) {
            // must be logged-in to fork, hide all fork controls
            add(new ExternalLink("forkLink", "").setVisible(false));
            add(new ExternalLink("myForkLink", "").setVisible(false));
            add(new Label("forksProhibitedIndicator").setVisible(false));
        } else {
            String fork = GitBlit.self().getFork(user.username, model.name);
            boolean hasFork = fork != null;
@@ -270,18 +271,6 @@
            if (hasFork || !canFork) {
                // user not allowed to fork or fork already exists or repo forbids forking
                add(new ExternalLink("forkLink", "").setVisible(false));
                if (user.canFork() && !model.allowForks) {
                    // show forks prohibited indicator
                    Fragment wc = new Fragment("forksProhibitedIndicator", "forksProhibitedFragment", this);
                    Label lbl = new Label("forksProhibited", getString("gb.forksProhibited"));
                    WicketUtils.setHtmlTooltip(lbl,  getString("gb.forksProhibitedWarning"));
                    wc.add(lbl);
                    add(wc);
                } else {
                    // can not fork, no need for forks prohibited indicator
                    add(new Label("forksProhibitedIndicator").setVisible(false));
                }
                
                if (hasFork && !fork.equals(model.name)) {
                    // user has fork, view my fork link
@@ -293,7 +282,6 @@
                }
            } else if (canFork) {
                // can fork and we do not have one
                add(new Label("forksProhibitedIndicator").setVisible(false));
                add(new ExternalLink("myForkLink", "").setVisible(false));
                String url = getRequestCycle().urlFor(ForkPage.class, WicketUtils.newRepositoryParameter(model.name)).toString();
                add(new ExternalLink("forkLink", url));
@@ -571,7 +559,12 @@
        public void onSubmit() {
            Constants.SearchType searchType = searchTypeModel.getObject();
            String searchString = searchBoxModel.getObject();
            if (searchString == null) {
            if (StringUtils.isEmpty(searchString)) {
                // redirect to self to avoid wicket page update bug
                PageParameters params = RepositoryPage.this.getPageParameters();
                String relativeUrl = urlFor(RepositoryPage.this.getClass(), params).toString();
                String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);
                getRequestCycle().setRequestTarget(new RedirectRequestTarget(absoluteUrl));
                return;
            }
            for (Constants.SearchType type : Constants.SearchType.values()) {
src/main/java/com/gitblit/wicket/pages/RootPage.html
@@ -19,13 +19,9 @@
                
                <div class="nav-collapse" wicket:id="navPanel"></div>
                
                <form class="pull-right" wicket:id="loginForm" style="padding-top:3px;">
                    <span class="form-search">
                        <input wicket:id="username" class="input-small" type="text" />
                        <input wicket:id="password" class="input-small" type="password" />
                        <button class="btn btn-primary" type="submit"><wicket:message key="gb.login"></wicket:message></button>
                    </span>
                </form>
                <ul class="nav pull-right">
                     <span wicket:id="userPanel"></span>
                </ul>
            </div>
        </div>
    </div>
@@ -33,9 +29,37 @@
    <!-- subclass content -->
    <div class="container">
        <div style="text-align:center" wicket:id="feedback">[Feedback Panel]</div>
        <wicket:child/>
    </div>
    <wicket:child/>
    <wicket:fragment wicket:id="loginFormFragment">
        <li>
            <form class="pull-right" wicket:id="loginForm">
                <span class="form-search">
                    <input wicket:id="username" class="input-small" type="text" />
                    <input wicket:id="password" class="input-small" type="password" />
                    <button class="btn btn-primary" type="submit"><wicket:message key="gb.login"></wicket:message></button>
                </span>
            </form>
        </li>
    </wicket:fragment>
    <!-- user fragment -->
    <wicket:fragment wicket:id="userMenuFragment">
        <li class="dropdown">
            <a data-toggle="dropdown" class="dropdown-toggle" href="#"><span wicket:id="username"></span> <b class="caret"></b></a>
            <ul class="dropdown-menu">
                <li style="color:#ccc;padding-left:15px;font-weight:bold;"><span wicket:id="displayName"></span></li>
                <li class="divider"></li>
                <li><a wicket:id="myProfile"><wicket:message key="gb.myProfile"></wicket:message></a></li>
                <li><a wicket:id="changePassword"><wicket:message key="gb.changePassword"></wicket:message></a></li>
                <li class="divider"></li>
                <li><a wicket:id="logout"><wicket:message key="gb.logout"></wicket:message></a></li>
            </ul>
        </li>
    </wicket:fragment>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/RootPage.java
@@ -30,9 +30,13 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.PasswordTextField;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.protocol.http.WebResponse;
@@ -49,6 +53,7 @@
import com.gitblit.wicket.PageRegistration.DropDownMenuItem;
import com.gitblit.wicket.SessionlessForm;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.GravatarImage;
import com.gitblit.wicket.panels.NavigationPanel;
/**
@@ -94,67 +99,47 @@
                setStatelessHint(true);
            }
        }
        if (authenticateView || authenticateAdmin) {
            if (GitBlitWebSession.get().isLoggedIn()) {
                UserMenu userFragment = new UserMenu("userPanel", "userMenuFragment", RootPage.this);
                add(userFragment);
            } else {
                LoginForm loginForm = new LoginForm("userPanel", "loginFormFragment", RootPage.this);
                add(loginForm);
            }
        } else {
            add(new Label("userPanel").setVisible(false));
        }
        boolean showRegistrations = GitBlit.canFederate()
                && GitBlit.getBoolean(Keys.web.showFederationRegistrations, false);
        // navigation links
        List<PageRegistration> pages = new ArrayList<PageRegistration>();
        pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,
                getRootPageParameters()));
        pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters()));
        if (GitBlit.getBoolean(Keys.web.allowLuceneIndexing, true)) {
            pages.add(new PageRegistration("gb.search", LuceneSearchPage.class));
        }
        if (showAdmin) {
            pages.add(new PageRegistration("gb.users", UsersPage.class));
        }
        if (showAdmin || showRegistrations) {
            pages.add(new PageRegistration("gb.federation", FederationPage.class));
        }
        if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
            addDropDownMenus(pages);
        }
        NavigationPanel navPanel = new NavigationPanel("navPanel", getClass(), pages);
        add(navPanel);
        // login form
        SessionlessForm<Void> loginForm = new SessionlessForm<Void>("loginForm", getClass(), getPageParameters()) {
            private static final long serialVersionUID = 1L;
            @Override
            public void onSubmit() {
                String username = RootPage.this.username.getObject();
                char[] password = RootPage.this.password.getObject().toCharArray();
                UserModel user = GitBlit.self().authenticate(username, password);
                if (user == null) {
                    error(getString("gb.invalidUsernameOrPassword"));
                } else if (user.username.equals(Constants.FEDERATION_USER)) {
                    // disallow the federation user from logging in via the
                    // web ui
                    error(getString("gb.invalidUsernameOrPassword"));
                    user = null;
                } else {
                    loginUser(user);
                }
//            pages.add(new PageRegistration("gb.home", HomePage.class,
//                    getRootPageParameters()));
            pages.add(new PageRegistration("gb.repositories", RepositoriesPage.class,
                    getRootPageParameters()));
            pages.add(new PageRegistration("gb.activity", ActivityPage.class, getRootPageParameters()));
            if (GitBlit.getBoolean(Keys.web.allowLuceneIndexing, true)) {
                pages.add(new PageRegistration("gb.search", LuceneSearchPage.class));
            }
        };
        TextField<String> unameField = new TextField<String>("username", username);
        WicketUtils.setInputPlaceholder(unameField, getString("gb.username"));
        loginForm.add(unameField);
        PasswordTextField pwField = new PasswordTextField("password", password);
        WicketUtils.setInputPlaceholder(pwField, getString("gb.password"));
        loginForm.add(pwField);
        add(loginForm);
            if (showAdmin) {
                pages.add(new PageRegistration("gb.users", UsersPage.class));
            }
            if (showAdmin || showRegistrations) {
                pages.add(new PageRegistration("gb.federation", FederationPage.class));
            }
        if (authenticateView || authenticateAdmin) {
            loginForm.setVisible(!GitBlitWebSession.get().isLoggedIn());
        } else {
            loginForm.setVisible(false);
            if (!authenticateView || (authenticateView && GitBlitWebSession.get().isLoggedIn())) {
                addDropDownMenus(pages);
            }
        }
        NavigationPanel navPanel = new NavigationPanel("navPanel", getRootNavPageClass(), pages);
        add(navPanel);
        // display an error message cached from a redirect
        String cachedMessage = GitBlitWebSession.get().clearErrorMessage();
@@ -171,6 +156,10 @@
        }
        super.setupPage(repositoryName, pageName);
    }
    protected Class<? extends BasePage> getRootNavPageClass() {
        return getClass();
    }
    private PageParameters getRootPageParameters() {
@@ -451,4 +440,81 @@
        Collections.sort(list);
        return list;
    }
    /**
     * Inline login form.
     */
    private class LoginForm extends Fragment {
        private static final long serialVersionUID = 1L;
        public LoginForm(String id, String markupId, MarkupContainer markupProvider) {
            super(id, markupId, markupProvider);
            setRenderBodyOnly(true);
            SessionlessForm<Void> loginForm = new SessionlessForm<Void>("loginForm", RootPage.this.getClass(), getPageParameters()) {
                private static final long serialVersionUID = 1L;
                @Override
                public void onSubmit() {
                    String username = RootPage.this.username.getObject();
                    char[] password = RootPage.this.password.getObject().toCharArray();
                    UserModel user = GitBlit.self().authenticate(username, password);
                    if (user == null) {
                        error(getString("gb.invalidUsernameOrPassword"));
                    } else if (user.username.equals(Constants.FEDERATION_USER)) {
                        // disallow the federation user from logging in via the
                        // web ui
                        error(getString("gb.invalidUsernameOrPassword"));
                        user = null;
                    } else {
                        loginUser(user);
                    }
                }
            };
            TextField<String> unameField = new TextField<String>("username", username);
            WicketUtils.setInputPlaceholder(unameField, markupProvider.getString("gb.username"));
            loginForm.add(unameField);
            PasswordTextField pwField = new PasswordTextField("password", password);
            WicketUtils.setInputPlaceholder(pwField, markupProvider.getString("gb.password"));
            loginForm.add(pwField);
            add(loginForm);
        }
    }
    /**
     * Menu for the authenticated user.
     */
    static class UserMenu extends Fragment {
        private static final long serialVersionUID = 1L;
        public UserMenu(String id, String markupId, MarkupContainer markupProvider) {
            super(id, markupId, markupProvider);
            setRenderBodyOnly(true);
            GitBlitWebSession session = GitBlitWebSession.get();
            UserModel user = session.getUser();
            boolean editCredentials = GitBlit.self().supportsCredentialChanges(user);
            boolean standardLogin = session.authenticationType.isStandard();
            if (GitBlit.getBoolean(Keys.web.allowGravatar, true)) {
                add(new GravatarImage("username", user.getDisplayName(), user.emailAddress, "navbarGravatar", 20, false));
            } else {
                add(new Label("username", user.getDisplayName()));
            }
            add(new Label("displayName", user.getDisplayName()));
            add(new BookmarkablePageLink<Void>("myProfile",
                    UserPage.class, WicketUtils.newUsernameParameter(user.username)));
            add(new BookmarkablePageLink<Void>("changePassword",
                    ChangePasswordPage.class).setVisible(editCredentials));
            add(new BookmarkablePageLink<Void>("logout",
                    LogoutPage.class).setVisible(standardLogin));
        }
    }
}
src/main/java/com/gitblit/wicket/pages/RootSubPage.html
@@ -6,6 +6,7 @@
<body>
<wicket:extend>
<div class="container">
    <!-- page header -->
    <div class="pageTitle">
        <h2><span wicket:id="pageName">[page name]</span> <small><span wicket:id="pageSubName">[sub name]</span></small></h2>
@@ -13,6 +14,7 @@
    
    <!-- Subclass Content -->
    <wicket:child/>
</div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/SendProposalPage.java
@@ -149,4 +149,9 @@
                false, repositories, false, getAccessRestrictions());
        add(repositoriesPanel);
    }
    @Override
    protected Class<? extends BasePage> getRootNavPageClass() {
        return FederationPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/SummaryPage.html
@@ -14,10 +14,11 @@
    
        <!-- Repository info -->
        <div class="hidden-phone"> 
            <table class="plain">
            <table class="summary">
                <tr><th><wicket:message key="gb.description">[description]</wicket:message></th><td><span wicket:id="repositoryDescription">[repository description]</span></td></tr>
                <tr><th><wicket:message key="gb.owners">[owner]</wicket:message></th><td><span wicket:id="repositoryOwners"><span wicket:id="owner"></span><span wicket:id="comma"></span></span></td></tr>
                <tr><th><wicket:message key="gb.lastChange">[last change]</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr>
                <tr><th><wicket:message key="gb.size">[size]</wicket:message></th><td><span wicket:id="repositorySize">[repository size]</span></td></tr>
                <tr><th><wicket:message key="gb.stats">[stats]</wicket:message></th><td><span wicket:id="branchStats">[branch stats]</span> <span class="link"><a wicket:id="metrics"><wicket:message key="gb.metrics">[metrics]</wicket:message></a></span></td></tr>
                <tr><th style="vertical-align:top;padding-top:4px;"><wicket:message key="gb.repositoryUrl">[URL]</wicket:message></th>
                    <td><div wicket:id="repositoryUrlPanel">[repository url panel]</div></td>
@@ -27,7 +28,7 @@
    </div>
    <!-- commits -->
    <div style="padding-bottom:15px;" wicket:id="commitsPanel">[commits panel]</div>
    <div style="padding-bottom:15px;padding-top:5px;" wicket:id="commitsPanel">[commits panel]</div>
    <!-- tags -->
    <div style="padding-bottom:15px;" wicket:id="tagsPanel">[tags panel]</div>
@@ -51,6 +52,7 @@
    <wicket:fragment wicket:id="ownersFragment">
        
    </wicket:fragment>
</wicket:extend>    
</body>
</html>
src/main/java/com/gitblit/wicket/pages/SummaryPage.java
@@ -113,6 +113,7 @@
        
        add(WicketUtils.createTimestampLabel("repositoryLastChange",
                JGitUtils.getLastChange(r), getTimeZone(), getTimeUtils()));
        add(new Label("repositorySize", getRepositoryModel().size));
        if (metricsTotal == null) {
            add(new Label("branchStats", ""));
        } else {
@@ -197,7 +198,7 @@
                    String.valueOf((int) WicketUtils.maxValue(metrics)) });
            provider.addAxis(commitAxis);
            provider.setLineStyles(new LineStyle[] { new LineStyle(2, 4, 0), new LineStyle(0, 4, 1) });
            provider.addShapeMarker(new ShapeMarker(MarkerType.CIRCLE, Color.BLUE, 1, -1, 5));
            provider.addShapeMarker(new ShapeMarker(MarkerType.CIRCLE, Color.decode("#002060"), 1, -1, 5));
            add(new Chart("commitsChart", provider));
        } else {
src/main/java/com/gitblit/wicket/pages/TagPage.java
@@ -21,6 +21,7 @@
import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
@@ -55,7 +56,7 @@
        }
        // Display tag.
        Class<? extends RepositoryPage> linkClass;
        Class<? extends WebPage> linkClass;
        PageParameters linkParameters = newCommitParameter(tagRef.getReferencedObjectId().getName());
        String typeKey;
        switch (tagRef.getReferencedObjectType()) {
@@ -96,4 +97,9 @@
    protected String getPageName() {
        return getString("gb.tag");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return LogPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/TagsPage.java
@@ -32,4 +32,9 @@
    protected String getPageName() {
        return getString("gb.tags");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return LogPage.class;
    }
}
src/main/java/com/gitblit/wicket/pages/TicketPage.java
@@ -71,6 +71,11 @@
    protected String getPageName() {
        return getString("gb.ticket");
    }
    @Override
    protected Class<? extends BasePage> getRepoNavPageClass() {
        return TicketsPage.class;
    }
    private String prepareComment(String comment) {
        String html = StringUtils.escapeForHtml(comment, false);
src/main/java/com/gitblit/wicket/pages/TicketsPage.html
@@ -7,9 +7,6 @@
<body>
<wicket:extend>
    <!-- header -->
    <div style="margin-top:5px;" class="header" wicket:id="header">[header]</div>
    <!-- tickets -->    
    <table class="pretty">
        <tbody>
src/main/java/com/gitblit/wicket/pages/TicketsPage.java
@@ -37,10 +37,6 @@
        List<TicketModel> tickets = TicgitUtils.getTickets(getRepository());
        // header
        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;
src/main/java/com/gitblit/wicket/pages/UserPage.html
@@ -6,7 +6,7 @@
<body>
<wicket:extend>
<div class="container">
    <div class="row">
        <div class="span4">
            <div wicket:id="gravatar"></div>
@@ -45,6 +45,7 @@
            </div>
        </div>
    </div>
</div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/pages/UsersPage.html
@@ -5,9 +5,11 @@
      lang="en"> 
<body>
<wicket:extend>
<div class="container">
    <div wicket:id="teamsPanel">[teams panel]</div>
    <div wicket:id="usersPanel">[users panel]</div>
</div>
</wicket:extend>
</body>
</html>
src/main/java/com/gitblit/wicket/panels/GravatarImage.java
@@ -50,22 +50,28 @@
    }
    public GravatarImage(String id, PersonIdent person, int width, boolean linked) {
        this(id, person.getName(), person.getEmailAddress(), "gravatar", width, linked);
    }
    public GravatarImage(String id, String username, String emailaddress, String cssClass, int width, boolean linked) {
        super(id);
        String email = person.getEmailAddress() == null ? person.getName().toLowerCase() : person.getEmailAddress().toLowerCase();
        String email = emailaddress == null ? username.toLowerCase() : emailaddress.toLowerCase();
        String hash = StringUtils.getMD5(email);
        Link<Void> link = new BookmarkablePageLink<Void>("link", GravatarProfilePage.class,
                WicketUtils.newObjectParameter(hash));
        link.add(new SimpleAttributeModifier("target", "_blank"));
        String url = ActivityUtils.getGravatarThumbnailUrl(email, width);
        ExternalImage image = new ExternalImage("image", url);
        WicketUtils.setCssClass(image, "gravatar");
        if (cssClass != null) {
            WicketUtils.setCssClass(image, cssClass);
        }
        link.add(image);
        if (linked) {
            WicketUtils.setHtmlTooltip(link,
                MessageFormat.format("View Gravatar profile for {0}", person.getName()));
                MessageFormat.format("View Gravatar profile for {0}", username));
        } else {
            WicketUtils.setHtmlTooltip(link, person.getName());
            WicketUtils.setHtmlTooltip(link, username);
        }
        add(link.setEnabled(linked));
        setVisible(GitBlit.getBoolean(Keys.web.allowGravatar, true));
src/main/java/com/gitblit/wicket/panels/PushesPanel.html
New file
@@ -0,0 +1,37 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
      xml:lang="en"
      lang="en">
<body>
<wicket:panel>
<div wicket:id="push">
    <table style="padding: 3px 0px;">
    <tr>
        <td class="hidden-phone" style="vertical-align: top;"><span wicket:id="whoAvatar"></span></td>
        <td style="padding-left: 7px;">
            <div><span wicket:id="whoPushed">[pusher]</span> <span wicket:id="whatPushed"></span><span wicket:id="wherePushed"></span></div>
            <div wicket:id="whenPushed"></div>
            <button type="button" class="btn btn-mini" style="padding: 1px 3px;line-height: 12px;" data-toggle="collapse" data-target="#demo"><span class="caret"></span></button>
            <div id="demo" class="collapse">
                <div style="padding: 10px 0px;">
                    <table>
                        <tr wicket:id="commit" style="border-left: 1px solid #ddd;">
                            <td style="vertical-align:top;"><span wicket:id="hashLink" style="padding-left: 10px;">[hash link]</span></td>
                            <td><img wicket:id="commitIcon" /></td>
                            <td style="vertical-align:top;">
                                <div wicket:id="commitShortMessage">[commit short message]</div>
                                <div wicket:id="commitRefs">[commit refs]</div>
                            </td>
                        </tr>
                    </table>
                </div>
            </div>
        </td>
    </tr>
    </table>
</div>
</wicket:panel>
</body>
</html>
src/main/java/com/gitblit/wicket/panels/PushesPanel.java
New file
@@ -0,0 +1,206 @@
/*
 * 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.wicket.panels;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.eclipse.jgit.lib.Repository;
import com.gitblit.Constants;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.models.PushLogEntry;
import com.gitblit.models.RepositoryCommit;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.PushLogUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.CommitPage;
import com.gitblit.wicket.pages.GitSearchPage;
import com.gitblit.wicket.pages.SummaryPage;
import com.gitblit.wicket.pages.UserPage;
public class PushesPanel extends BasePanel {
    private static final long serialVersionUID = 1L;
    private final boolean hasPushes;
    private boolean hasMore;
    public PushesPanel(String wicketId, final RepositoryModel model, Repository r, int limit, int pageOffset) {
        super(wicketId);
        boolean pageResults = limit <= 0;
        int itemsPerPage = GitBlit.getInteger(Keys.web.itemsPerPage, 50);
        if (itemsPerPage <= 1) {
            itemsPerPage = 50;
        }
        final Map<String, String> usernameLookup = new HashMap<String, String>();
        final int hashLen = GitBlit.getInteger(Keys.web.shortCommitIdLength, 6);
        List<PushLogEntry> entries = PushLogUtils.getPushLog(model.name, r, limit);
        // establish pusher identities
        for (PushLogEntry push : entries) {
            // handle push logs with email address instead of account name
            String username = push.user.username;
            if (push.user.username.indexOf('@') > -1) {
                // push username is an email address, reverse lookup for account
                if (!usernameLookup.containsKey(push.user.username)) {
                    for (UserModel user : GitBlit.self().getAllUsers()) {
                        if (push.user.username.equals(user.emailAddress)) {
                            username = user.username;
                            usernameLookup.put(push.user.username, username);
                            break;
                        }
                    }
                } else {
                    username = usernameLookup.get(push.user.username);
                }
            } else {
                // push username is an account name, lookup for email address
                if (!usernameLookup.containsKey(push.user.username)) {
                    UserModel user = GitBlit.self().getUserModel(push.user.username);
                    if (user != null) {
                        push.user.emailAddress = user.emailAddress;
                        usernameLookup.put(push.user.username, user.emailAddress);
                    }
                } else {
                    push.user.emailAddress = usernameLookup.get(push.user.username);
                }
            }
        }
        hasPushes = entries.size() > 0;
        ListDataProvider<PushLogEntry> dp = new ListDataProvider<PushLogEntry>(entries);
        DataView<PushLogEntry> pushView = new DataView<PushLogEntry>("push", dp) {
            private static final long serialVersionUID = 1L;
            public void populateItem(final Item<PushLogEntry> pushItem) {
                final PushLogEntry push = pushItem.getModelObject();
                pushItem.add(new GravatarImage("whoAvatar", push.getCommitterIdent(), 40));
                pushItem.add(new LinkPanel("whoPushed", null, push.user.getDisplayName(),
                        UserPage.class, WicketUtils.newUsernameParameter(push.user.username)));
                pushItem.add(new Label("whatPushed",
                        MessageFormat.format(push.getCommitCount() > 1 ? "pushed {0} commits to":"pushed 1 commit to", push.getCommitCount())));
                String repoName = StringUtils.stripDotGit(model.name);
                pushItem.add(new LinkPanel("wherePushed", null, repoName,
                        SummaryPage.class, WicketUtils.newRepositoryParameter(model.name)));
                pushItem.add(WicketUtils.createDateLabel("whenPushed", push.date, getTimeZone(), getTimeUtils()));
                ListDataProvider<RepositoryCommit> cdp = new ListDataProvider<RepositoryCommit>(push.getCommits());
                DataView<RepositoryCommit> commitsView = new DataView<RepositoryCommit>("commit", cdp) {
                    private static final long serialVersionUID = 1L;
                    public void populateItem(final Item<RepositoryCommit> commitItem) {
                        final RepositoryCommit commit = commitItem.getModelObject();
                        // author search link
                        String author = commit.getAuthorIdent().getName();
                        LinkPanel authorLink = new LinkPanel("commitAuthor", "list", author,
                                GitSearchPage.class, WicketUtils.newSearchParameter(model.name,
                                        null, author, Constants.SearchType.AUTHOR));
                        setPersonSearchTooltip(authorLink, author, Constants.SearchType.AUTHOR);
                        commitItem.add(authorLink);
                        // merge icon
                        if (commit.getParentCount() > 1) {
                            commitItem.add(WicketUtils.newImage("commitIcon", "commit_merge_16x16.png"));
                        } else {
                            commitItem.add(WicketUtils.newBlankImage("commitIcon"));
                        }
                        // short message
                        String shortMessage = commit.getShortMessage();
                        String trimmedMessage = shortMessage;
                        if (commit.getRefs() != null && commit.getRefs().size() > 0) {
                            trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG_REFS);
                        } else {
                            trimmedMessage = StringUtils.trimString(shortMessage, Constants.LEN_SHORTLOG);
                        }
                        LinkPanel shortlog = new LinkPanel("commitShortMessage", "list",
                                trimmedMessage, CommitPage.class, WicketUtils.newObjectParameter(
                                        model.name, commit.getName()));
                        if (!shortMessage.equals(trimmedMessage)) {
                            WicketUtils.setHtmlTooltip(shortlog, shortMessage);
                        }
                        commitItem.add(shortlog);
                        commitItem.add(new RefsPanel("commitRefs", commit.repository, commit.getRefs()));
                        // commit hash link
                        LinkPanel commitHash = new LinkPanel("hashLink", null, commit.getName().substring(0, hashLen),
                                CommitPage.class, WicketUtils.newObjectParameter(
                                        model.name, commit.getName()));
                        WicketUtils.setCssClass(commitHash, "shortsha1");
                        WicketUtils.setHtmlTooltip(commitHash, commit.getName());
                        commitItem.add(commitHash);
//                        item.add(new BookmarkablePageLink<Void>("diff", CommitDiffPage.class, WicketUtils
//                                .newObjectParameter(repositoryName, entry.getName())).setEnabled(entry
//                                .getParentCount() > 0));
//                        item.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils
//                                .newObjectParameter(repositoryName, entry.getName())));
                    }
                };
                pushItem.add(commitsView);
            }
        };
        add(pushView);
        // determine to show pager, more, or neither
//        if (limit <= 0) {
//            // no display limit
//            add(new Label("moreLogs", "").setVisible(false));
//        } else {
//            if (pageResults) {
//                // paging
//                add(new Label("moreLogs", "").setVisible(false));
//            } else {
//                // more
//                if (commits.size() == limit) {
//                    // show more
//                    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));
//                }
//            }
//        }
    }
    public boolean hasMore() {
        return hasMore;
    }
    public boolean hideIfEmpty() {
        setVisible(hasPushes);
        return hasPushes;
    }
}
src/main/java/com/gitblit/wicket/panels/RefsPanel.java
@@ -22,6 +22,7 @@
import java.util.Map;
import org.apache.wicket.Component;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
@@ -35,7 +36,6 @@
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.pages.CommitPage;
import com.gitblit.wicket.pages.LogPage;
import com.gitblit.wicket.pages.RepositoryPage;
import com.gitblit.wicket.pages.TagPage;
public class RefsPanel extends Panel {
@@ -99,7 +99,7 @@
                String name = entry.displayName;
                String objectid = entry.getReferencedObjectId().getName();
                boolean breakLine = false;
                Class<? extends RepositoryPage> linkClass = CommitPage.class;
                Class<? extends WebPage> linkClass = CommitPage.class;
                String cssClass = "";
                if (name.startsWith(Constants.R_HEADS)) {
                    // local branch
src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.html
@@ -8,19 +8,26 @@
    <div wicket:id="repositoryUrlPanel"></div>
    <div wicket:id="applicationMenusPanel"></div>
    <div wicket:id="repositoryIndicators"></div>
    
    <wicket:fragment wicket:id="repositoryUrlFragment">
        <div class="btn-toolbar" style="margin: 0px;">
            <div class="btn-group repositoryUrlContainer">
                <img style="vertical-align: middle;padding: 0px 0px 1px 3px;" wicket:id="accessRestrictionIcon"></img>
                <span wicket:id="menu"></span>
                   <span class="repositoryUrl">
                   <div class="repositoryUrl">
                       <span wicket:id="primaryUrl">[repository primary url]</span>
                       <span class="hidden-phone hidden-tablet" wicket:id="copyFunction"></span>
                   </span>
                   </div>
                   <span class="hidden-phone hidden-tablet repositoryUrlRightCap" wicket:id="primaryUrlPermission">[repository primary url permission]</span>
               </div>
        </div>
    </wicket:fragment>
    <wicket:fragment wicket:id="indicatorsFragment">
        <div>
               <div wicket:id="workingCopyIndicator"></div>
            <div wicket:id="forksProhibitedIndicator"></div>
        </div>
    </wicket:fragment>
    
@@ -90,6 +97,18 @@
               scale="noscale"
               allowScriptAccess="always"></object>
    </wicket:fragment>
    <wicket:fragment wicket:id="workingCopyFragment">
        <div class="repositoryIndicator">
            <span class="alert alert-info"><i class="icon-exclamation-sign"></i>&nbsp;<span class="hidden-phone" wicket:id="workingCopy">[working copy]</span></span>
        </div>
    </wicket:fragment>
    <wicket:fragment wicket:id="forksProhibitedFragment">
        <div class="repositoryIndicator">
            <span class="alert alert-error"><i class="icon-ban-circle"></i>&nbsp;<span class="hidden-phone" wicket:id="forksProhibited">[forks prohibited]</span></span>
        </div>
    </wicket:fragment>
</wicket:panel>
</html>
src/main/java/com/gitblit/wicket/panels/RepositoryUrlPanel.java
@@ -91,11 +91,18 @@
            // no urls, nothing to show.
            add(new Label("repositoryUrlPanel").setVisible(false));
            add(new Label("applicationMenusPanel").setVisible(false));
            add(new Label("repositoryIndicators").setVisible(false));
            return;
        }
        
        // display primary url
        add(createPrimaryUrlPanel("repositoryUrlPanel", repository, repositoryUrls));
        if (onlyUrls) {
            add(new Label("repositoryIndicators").setVisible(false));
        } else {
            add(createRepositoryIndicators(repository));
        }
        boolean allowAppLinks = GitBlit.getBoolean(Keys.web.allowAppCloneLinks, true);
        if (onlyUrls || !canClone || !allowAppLinks) {
@@ -425,4 +432,45 @@
        }
        return accessRestrictionsMap;
    }
    protected Component createRepositoryIndicators(RepositoryModel repository) {
        Fragment fragment = new Fragment("repositoryIndicators", "indicatorsFragment", this);
        if (repository.isBare) {
            fragment.add(new Label("workingCopyIndicator").setVisible(false));
        } else {
            Fragment wc = new Fragment("workingCopyIndicator", "workingCopyFragment", this);
            Label lbl = new Label("workingCopy", getString("gb.workingCopy"));
            WicketUtils.setHtmlTooltip(lbl,  getString("gb.workingCopyWarning"));
            wc.add(lbl);
            fragment.add(wc);
        }
        boolean allowForking = GitBlit.getBoolean(Keys.web.allowForking, true);
        if (!allowForking || user == null || !user.isAuthenticated) {
            // must be logged-in to fork, hide all fork controls
            fragment.add(new Label("forksProhibitedIndicator").setVisible(false));
        } else {
            String fork = GitBlit.self().getFork(user.username, repository.name);
            boolean hasFork = fork != null;
            boolean canFork = user.canFork(repository);
            if (hasFork || !canFork) {
                if (user.canFork() && !repository.allowForks) {
                    // show forks prohibited indicator
                    Fragment wc = new Fragment("forksProhibitedIndicator", "forksProhibitedFragment", this);
                    Label lbl = new Label("forksProhibited", getString("gb.forksProhibited"));
                    WicketUtils.setHtmlTooltip(lbl,  getString("gb.forksProhibitedWarning"));
                    wc.add(lbl);
                    fragment.add(wc);
                } else {
                    // can not fork, no need for forks prohibited indicator
                    fragment.add(new Label("forksProhibitedIndicator").setVisible(false));
                }
            } else if (canFork) {
                // can fork and we do not have one
                fragment.add(new Label("forksProhibitedIndicator").setVisible(false));
            }
        }
        return fragment;
    }
}
src/main/java/com/gitblit/wicket/panels/TagsPanel.java
@@ -17,6 +17,7 @@
import java.util.List;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.panel.Fragment;
@@ -35,7 +36,6 @@
import com.gitblit.wicket.pages.CommitPage;
import com.gitblit.wicket.pages.LogPage;
import com.gitblit.wicket.pages.RawPage;
import com.gitblit.wicket.pages.RepositoryPage;
import com.gitblit.wicket.pages.TagPage;
import com.gitblit.wicket.pages.TagsPage;
import com.gitblit.wicket.pages.TreePage;
@@ -71,7 +71,7 @@
                item.add(WicketUtils.createDateLabel("tagDate", entry.getDate(), getTimeZone(), getTimeUtils()));
                Class<? extends RepositoryPage> linkClass;
                Class<? extends WebPage> linkClass;
                switch (entry.getReferencedObjectType()) {
                case Constants.OBJ_BLOB:
                    linkClass = BlobPage.class;
src/main/resources/arrow_page.png

src/main/resources/arrow_project.png
src/main/resources/fork-black_16x16.png
src/main/resources/gitblit.css
@@ -16,6 +16,15 @@
ul, ol {
    margin-bottom: 10px !important;
}
a {
    color: #2F58A0;
}
a:hover {
    color: #002060;
}
a:focus {
    outline: none;
}
@@ -110,6 +119,65 @@
    color: #ffffff !important;
}
.repositorynavbar {
    background-color: #fbfbfb;
    border-bottom: 1px solid #ccc;
    margin-top: -8px;
    margin-bottom: 10px;
}
.repositorynavbar .title {
    line-height: 32px;
}
.repositorynavbar .repository {
    font-weight: bold;
}
.repositorynavbar .project a, .repositorynavbar .repository a {
    font-family: Helvetica,arial,freesans,clean,sans-serif;
    font-size: 22px;
    color: #002060;
}
.repositorynavbar .repositorynavbar-inner {
}
.repositorynavbar ul {
    list-style: none outside;
    display: block;
    position: relative;
    border-top: 1px solid #ccc;
}
.repositorynavbar ul li {
    display: block;
    float: left;
    padding: 10px;
}
.repositorynavbar ul li:focus, .repositorynavbar .active {
    color: black;
    background-repeat:no-repeat;
    background-image: url(arrow_project.png);
    background-position: center bottom;
    font-weight: bold;
    outline: 0;
}
.repositorynavbar ul a {
    color: #002060;
}
.repositorynavbar ul li:hover {
    background-color: #eee;
}
.repositorynavbar ul li a:hover {
    background-color: inherit;
    text-decoration: underline;
}
.btn-appmenu {
    border-radius: 4px !important;
    background-color: #002060;
@@ -141,8 +209,8 @@
}
.breadcrumb {
    margin-top: 5px !important;
    margin-bottom: 5px !important;
    margin-top: 10px !important;
    margin-bottom: 10px !important;
}
.pageTitle {    
@@ -246,12 +314,26 @@
    font-family:menlo,consolas,monospace;
}
span.repositoryUrl {
div.repositoryUrl {
    display: inline-block;
    font-size: 1em;
    padding: 2px 4px 3px 4px;
    padding: 1px 4px 2px 4px;
    background-color: #fff;
    border-left: 1px solid #ddd;
    border-right: 1px solid #ddd;
    border: 1px solid #ddd;
    margin: 1px;
}
div.repositoryIndicator {
    display:inline;
    padding-top:0px;
    margin-bottom:0px;
}
div.repositoryIndicator span.alert {
    padding: 2px 7px 2px 7px;
    vertical-align: middle;
    font-size:0.85em;
    font-weight:normal;
}
ul.urlMenu {
@@ -553,6 +635,10 @@
    padding: 2px;
}
img.navbarGravatar {
    border: 1px solid #fff;
}
div.searchResult {
    padding: 10px 5px 10px 5px;
}
@@ -608,19 +694,7 @@
}
div.header, div.commitHeader, table.repositories th {
    background-color:#e0e0e0;
    background-repeat:repeat-x;
    background-image:-khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#e0e0e0));
    background-image:-moz-linear-gradient(top, #ffffff, #e0e0e0);
    background-image:-ms-linear-gradient(top, #ffffff, #e0e0e0);
    background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #e0e0e0));
    background-image:-webkit-linear-gradient(top, #ffffff, #e0e0e0);
    background-image:-o-linear-gradient(top, #ffffff, #e0e0e0);
    background-image:linear-gradient(top, #ffffff, #e0e0e0);
    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e0e0e0', GradientType=0);
    -webkit-box-shadow:inset 0 1px 0 #ffffff;
    -moz-box-shadow:inset 0 1px 0 #ffffff;
    box-shadow:inset 0 1px 0 #ffffff;
    background-color: #fbfbfb;
}
div.header {
@@ -651,7 +725,19 @@
}
div.page_nav2 {
    padding: 2px 5px 7px 5px;
    padding: 5px 10px;
    margin: -10px 0px 10px;
    border-left: 1px solid #ccc;
    border-right: 1px solid #ccc;
    border-bottom: 1px solid #ccc;
    border-radius: 0px 0px 3px 3px;
    background-color: #ECF1F4;
    color: #666;
    text-align: left;
}
div.page_nav2 a {
    color: #002060;
}
div.admin_nav {
@@ -913,15 +999,26 @@
    text-align: right;
}
table.plain {
table.plain, table.summary {
    width: 0 !important;
    border: 0;
}
table.plain th, table.plain td {
table.plain th, table.plain td, table.summary th, table.summary td {
    white-space: nowrap;
    padding: 1px 3px;
    border: 0;
}
table.summary {
    margin: 0px;
}
table.summary th {
    color: #999;
    padding-right: 10px;
    text-align: right;
    font-weight: normal;
}
table.pretty {
@@ -1087,7 +1184,7 @@
}
tr.dark {
    background-color: #f5f5f5;
    background-color: #f6f6f6;
}
/* currently both use the same, but it can change */
src/main/resources/gitblt-favicon.png