]> source.dussan.org Git - gitblit.git/commitdiff
Download zip feature.
authorJames Moger <james.moger@gitblit.com>
Tue, 24 May 2011 21:39:38 +0000 (17:39 -0400)
committerJames Moger <james.moger@gitblit.com>
Tue, 24 May 2011 21:39:38 +0000 (17:39 -0400)
12 files changed:
distrib/gitblit.properties
src/com/gitblit/Constants.java
src/com/gitblit/DownloadZipServlet.java [new file with mode: 0644]
src/com/gitblit/GitBlitServer.java
src/com/gitblit/tests/JGitUtilsTest.java
src/com/gitblit/utils/JGitUtils.java
src/com/gitblit/wicket/GitBlitWebApp.properties
src/com/gitblit/wicket/pages/CommitPage.html
src/com/gitblit/wicket/pages/CommitPage.java
src/com/gitblit/wicket/pages/TreePage.html
src/com/gitblit/wicket/pages/TreePage.java
src/com/gitblit/wicket/resources/gitblit.css

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