James Moger
2011-05-24 9197d340db81a245193dff1ecb44889b8e0dfe31
Download zip feature.
1 files added
11 files modified
223 ■■■■■ changed files
distrib/gitblit.properties 3 ●●●●● patch | view | raw | blame | history
src/com/gitblit/Constants.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/DownloadZipServlet.java 100 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlitServer.java 7 ●●●● patch | view | raw | blame | history
src/com/gitblit/tests/JGitUtilsTest.java 23 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/JGitUtils.java 52 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 3 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/CommitPage.html 7 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/CommitPage.java 6 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TreePage.html 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/TreePage.java 6 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/resources/gitblit.css 8 ●●●● patch | view | raw | blame | history
distrib/gitblit.properties
@@ -55,6 +55,9 @@
# If web.authenticate=false, any user can execute the aforementioned functions.  
web.allowAdministration = true
# Allow dyanamic zip downloads.
web.allowZipDownloads = true
# This is the message display above the repositories table.
# This can point to a file with Markdown content.
# Specifying "gitblit" uses the internal welcome message.
src/com/gitblit/Constants.java
@@ -17,6 +17,10 @@
    public final static String ADMIN_ROLE = "#admin";
    public final static String PROPERTIES_FILE = "gitblit.properties";
    public final static String GIT_SERVLET_PATH = "/git/";
    public final static String ZIP_SERVLET_PATH = "/zip/";
    public static enum AccessRestrictionType {
        NONE, PUSH, CLONE, VIEW;
src/com/gitblit/DownloadZipServlet.java
New file
@@ -0,0 +1,100 @@
package com.gitblit;
import java.util.Date;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.models.RepositoryModel;
public class DownloadZipServlet extends HttpServlet {
    public static String asLink(String baseURL, String repository, String objectId, String path) {
        return baseURL + (baseURL.endsWith("/") ? "" : "/") + "zip?r=" + repository + (path == null ? "" : ("&p=" + path)) + (objectId == null ? "" : ("&h=" + objectId));
    }
    private static final long serialVersionUID = 1L;
    private final static Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);
    public DownloadZipServlet() {
        super();
    }
    @Override
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
        processRequest(request, response);
    }
    @Override
    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
        processRequest(request, response);
    }
    private void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {
        if (!GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)) {
            logger.warn("Zip downloads are disabled");
            response.sendError(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        String repository = request.getParameter("r");
        String basePath = request.getParameter("p");
        String objectId = request.getParameter("h");
        try {
            String name = repository;
            if (name.indexOf('/') > -1) {
                name = name.substring(name.lastIndexOf('/') + 1);
            }
            // check roles first
            boolean authorized = request.isUserInRole(Constants.ADMIN_ROLE);
            authorized |= request.isUserInRole(repository);
            if (!authorized) {
                RepositoryModel model = GitBlit.self().getRepositoryModel(repository);
                if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {
                    logger.warn("Unauthorized access via zip servlet for " + model.name);
                    response.sendError(HttpServletResponse.SC_FORBIDDEN);
                    return;
                }
            }
            if (!StringUtils.isEmpty(basePath)) {
                name += "-" + basePath.replace('/', '_');
            }
            if (!StringUtils.isEmpty(objectId)) {
                name += "-" + objectId;
            }
            Repository r = GitBlit.self().getRepository(repository);
            RevCommit commit = JGitUtils.getCommit(r, objectId);
            Date date = JGitUtils.getCommitDate(commit);
            String contentType = "application/octet-stream";
            response.setContentType(contentType + "; charset=" + response.getCharacterEncoding());
            // response.setContentLength(attachment.getFileSize());
            response.setHeader("Content-Disposition", "attachment; filename=\"" + name + ".zip" + "\"");
            response.setDateHeader("Last-Modified", date.getTime());
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            try {
                JGitUtils.zip(r, basePath, objectId, response.getOutputStream());
                response.flushBuffer();
            } catch (Throwable t) {
                logger.error("Failed to write attachment to client", t);
            }
        } catch (Throwable t) {
            logger.error("Failed to write attachment to client", t);
        }
    }
}
src/com/gitblit/GitBlitServer.java
@@ -211,10 +211,13 @@
        wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, wicketPathSpec);
        wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/");
        rootContext.addFilter(wicketFilter, wicketPathSpec, FilterMapping.DEFAULT);
        // Zip Servlet
        rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*");
        // Git Servlet
        ServletHolder gitServlet = null;
        String gitServletPathSpec = "/git/*";
        String gitServletPathSpec = Constants.GIT_SERVLET_PATH + "*";
        if (fileSettings.getBoolean(Keys.git.enableGitServlet, true)) {
            gitServlet = rootContext.addServlet(GitBlitServlet.class, gitServletPathSpec);
            gitServlet.setInitParameter("base-path", params.repositoriesFolder);
src/com/gitblit/tests/JGitUtilsTest.java
@@ -1,6 +1,7 @@
package com.gitblit.tests;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Date;
import java.util.List;
@@ -103,5 +104,27 @@
        r.close();
        System.out.println(diff);
    }
    public void testZip() throws Exception {
        Repository r = new FileRepository(new File(repositoriesFolder, "gitblit.git/" + Constants.DOT_GIT));
        FileOutputStream fos = null;
        try {
            File zipFile = new File("c:/output.zip");
            zipFile.delete();
            fos = new FileOutputStream(zipFile);
            if (JGitUtils.zip(r, "src", Constants.HEAD, fos)) {
                System.out.println("zip = " + zipFile.length() + " bytes");
            } else {
                System.err.println("failed to generate zip file?!");
            }
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (Throwable t) {
                }
            }
        }
    }
}
src/com/gitblit/utils/JGitUtils.java
@@ -4,6 +4,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.ParseException;
@@ -17,6 +18,8 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.diff.DiffEntry;
@@ -808,10 +811,57 @@
        return null;
    }
    public static boolean zip(Repository r, String basePath, String objectId, OutputStream os) throws Exception {
        RevCommit commit = getCommit(r, objectId);
        if (commit == null) {
            return false;
        }
        final RevWalk rw = new RevWalk(r);
        final TreeWalk walk = new TreeWalk(r);
        try {
            walk.addTree(commit.getTree());
            ZipOutputStream zos = new ZipOutputStream(os);
            zos.setComment("Generated by Git:Blit");
            if (basePath != null && basePath.length() > 0) {
                PathFilter f = PathFilter.create(basePath);
                walk.setFilter(f);
            }
            walk.setRecursive(true);
            while (walk.next()) {
                ZipEntry entry = new ZipEntry(walk.getPathString());
                entry.setSize(walk.getObjectReader().getObjectSize(walk.getObjectId(0), Constants.OBJ_BLOB));
                entry.setComment(commit.getName());
                zos.putNextEntry(entry);
                ObjectId entid = walk.getObjectId(0);
                FileMode entmode = walk.getFileMode(0);
                RevBlob blob = (RevBlob) rw.lookupAny(entid, entmode.getObjectType());
                rw.parseBody(blob);
                ObjectLoader ldr = r.open(blob.getId(), Constants.OBJ_BLOB);
                byte[] tmp = new byte[4096];
                InputStream in = ldr.openStream();
                int n;
                while ((n = in.read(tmp)) > 0) {
                    zos.write(tmp, 0, n);
                }
                in.close();
            }
            zos.finish();
            return true;
        } catch (IOException e) {
            LOGGER.error("Failed to zip files from commit " + commit.getName(), e);
        } finally {
            walk.release();
            rw.dispose();
        }
        return false;
    }
    public static List<Metric> getDateMetrics(Repository r) {
        Metric total = new Metric("TOTAL");
        final Map<String, Metric> metricMap = new HashMap<String, Metric>();
        if (hasCommits(r)) {
            final List<RefModel> tags = getTags(r, -1);
            final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>();
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -92,4 +92,5 @@
gb.canAdminDescription = can administer Git:Blit server
gb.permittedUsers = permitted users
gb.isFrozen = is frozen
gb.isFrozenDescription = deny push operations
gb.isFrozenDescription = deny push operations
gb.zip = zip
src/com/gitblit/wicket/pages/CommitPage.html
@@ -23,7 +23,12 @@
        <tr><th><wicket:message key="gb.committer">committer</wicket:message></th><td><span class="sha1" wicket:id="commitCommitter">[committer]</span></td></tr>
        <tr><th></th><td><span class="sha1" wicket:id="commitCommitterDate">[commit date]</span></td></tr>
        <tr><th><wicket:message key="gb.commit">commit</wicket:message></th><td><span class="sha1" wicket:id="commitId">[commit id]</span></td></tr>
        <tr><th><wicket:message key="gb.tree">tree</wicket:message></th><td><span class="sha1" wicket:id="commitTree">[commit tree]</span></td></tr>
        <tr><th><wicket:message key="gb.tree">tree</wicket:message></th>
            <td><span class="sha1" wicket:id="commitTree">[commit tree]</span>
                <span class="link">
                    <a wicket:id="treeLink"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="zipLink"><wicket:message key="gb.zip"></wicket:message></a>
                </span>
            </td></tr>
        <tr><th valign="top"><wicket:message key="gb.parent">parent</wicket:message></th>
            <td>
                <span wicket:id="commitParents">
src/com/gitblit/wicket/pages/CommitPage.java
@@ -6,6 +6,7 @@
import org.apache.wicket.PageParameters;
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.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
@@ -13,6 +14,9 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.DownloadZipServlet;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JGitUtils.SearchType;
import com.gitblit.wicket.LinkPanel;
@@ -62,6 +66,8 @@
        add(new Label("commitId", c.getName()));
        add(new LinkPanel("commitTree", "list", c.getTree().getName(), TreePage.class, newCommitParameter()));
        add(new BookmarkablePageLink<Void>("treeLink", TreePage.class, newCommitParameter()));
        add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, null)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));
        // Parent Commits
        ListDataProvider<String> parentsDp = new ListDataProvider<String>(parents);
src/com/gitblit/wicket/pages/TreePage.html
@@ -9,7 +9,7 @@
    <!-- blob nav links -->    
    <div class="page_nav2">
        <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a>
        <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a> | <a wicket:id="zipLink"><wicket:message key="gb.zip"></wicket:message></a>
    </div>    
    
    <!-- commit header -->
@@ -32,7 +32,7 @@
    <!--  tree links -->
    <wicket:fragment wicket:id="treeLinks">
        <span class="link">
            <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
            <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="zip"><wicket:message key="gb.zip"></wicket:message></a>
        </span>
    </wicket:fragment>
    
src/com/gitblit/wicket/pages/TreePage.java
@@ -5,6 +5,7 @@
import org.apache.wicket.PageParameters;
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.Fragment;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
@@ -13,6 +14,9 @@
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.DownloadZipServlet;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.JGitUtils;
import com.gitblit.wicket.LinkPanel;
@@ -36,6 +40,7 @@
        // tree page links
        add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, path)));
        add(new BookmarkablePageLink<Void>("headLink", TreePage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, path)));
        add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, path)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));
        add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
@@ -73,6 +78,7 @@
                        Fragment links = new Fragment("pathLinks", "treeLinks", this);
                        links.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                        links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));
                        links.add(new ExternalLink("zip", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, entry.path)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));
                        item.add(links);
                    } else {
                        // blob link
src/com/gitblit/wicket/resources/gitblit.css
@@ -114,15 +114,19 @@
    color: #008000;
}
span.link {
    color: #888;
}
span.link, span.link a {
    font-family: sans-serif;
    font-size: 11px;
    font-size: 11px;
}
span.link em, div.link span em {
    font-style: normal;
    font-family: sans-serif;
    font-size: 11px;
    font-size: 11px;
}
div.page_header {