From 59b817a55b04b4bd8c5950a2d97998d3af6d44e3 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 30 Nov 2012 18:05:35 -0500 Subject: [PATCH] Support alternate compressed download formats (issue-174) --- distrib/gitblit.properties | 13 + docs/04_releases.mkd | 3 + src/com/gitblit/DownloadZipServlet.java | 58 +++- src/com/gitblit/utils/CompressionUtils.java | 315 ++++++++++++++++++ src/com/gitblit/utils/JGitUtils.java | 69 ---- src/com/gitblit/wicket/pages/CommitPage.html | 2 +- src/com/gitblit/wicket/pages/CommitPage.java | 10 +- src/com/gitblit/wicket/pages/TreePage.html | 6 +- src/com/gitblit/wicket/pages/TreePage.java | 23 +- .../panels/CompressedDownloadsPanel.html | 12 + .../panels/CompressedDownloadsPanel.java | 77 +++++ tests/com/gitblit/tests/JGitUtilsTest.java | 7 +- 12 files changed, 492 insertions(+), 103 deletions(-) create mode 100644 src/com/gitblit/utils/CompressionUtils.java create mode 100644 src/com/gitblit/wicket/panels/CompressedDownloadsPanel.html create mode 100644 src/com/gitblit/wicket/panels/CompressedDownloadsPanel.java diff --git a/distrib/gitblit.properties b/distrib/gitblit.properties index ee050a48..233fdfad 100644 --- a/distrib/gitblit.properties +++ b/distrib/gitblit.properties @@ -482,6 +482,19 @@ web.allowGravatar = true # SINCE 0.5.0 web.allowZipDownloads = true +# If *web.allowZipDownloads=true* the following formats will be displayed for +# download compressed archive links: +# +# zip = standard .zip +# tar = standard tar format (preserves *nix permissions and symlinks) +# gz = gz-compressed tar +# xz = xz-compressed tar +# bzip2 = bzip2-compressed tar +# +# SPACE-DELIMITED +# SINCE 1.2.0 +web.compressedDownloads = zip gz + # Allow optional Lucene integration. Lucene indexing is an opt-in feature. # A repository may specify branches to index with Lucene instead of using Git # commit traversal. There are scenarios where you may want to completely disable diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index 70a853fc..3f03160a 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -60,6 +60,8 @@ This is extreme and should be considered carefully since it affects every https - Added Gitblit Certificate Authority, an X509 certificate generation tool for Gitblit GO to encourage use of client certificate authentication. - Added setting to control length of shortened commit ids **New:** *web.shortCommitIdLength=8* +- Added alternate compressed download formats: tar.gz, tar.xz, tar.bzip2 (issue 174) + **New:** *web.compressedDownloads = zip gz* - Added simple project pages. A project is a subfolder off the *git.repositoriesFolder*. - Added support for X-Forwarded-Context for Apache subdomain proxy configurations (issue 135) - Delete branch feature (issue 121, Github/ajermakovics) @@ -70,6 +72,7 @@ This is extreme and should be considered carefully since it affects every https #### changes +- Access restricted servlets (e.g. DownloadZip, RSS, etc) will try to authenticate any Gitblit cookie found in the request before resorting to BASIC authentication. - Added *groovy* and *scala* to *web.prettyPrintExtensions* - Added short commit id column to log and history tables (issue 168) - Teams can now specify the *admin*, *create*, and *fork* roles to simplify user administration diff --git a/src/com/gitblit/DownloadZipServlet.java b/src/com/gitblit/DownloadZipServlet.java index 26559344..0feee879 100644 --- a/src/com/gitblit/DownloadZipServlet.java +++ b/src/com/gitblit/DownloadZipServlet.java @@ -29,6 +29,7 @@ import org.eclipse.jgit.revwalk.RevCommit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.gitblit.utils.CompressionUtils; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.MarkdownUtils; import com.gitblit.utils.StringUtils; @@ -45,6 +46,25 @@ public class DownloadZipServlet extends HttpServlet { private static final long serialVersionUID = 1L; private transient Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class); + + public static enum Format { + zip(".zip"), tar(".tar"), gz(".tar.gz"), xz(".tar.xz"), bzip2(".tar.bzip2"); + + public final String extension; + + Format(String ext) { + this.extension = ext; + } + + public static Format fromName(String name) { + for (Format format : values()) { + if (format.name().equalsIgnoreCase(name)) { + return format; + } + } + return zip; + } + } public DownloadZipServlet() { super(); @@ -57,15 +77,17 @@ public class DownloadZipServlet extends HttpServlet { * @param repository * @param objectId * @param path + * @param format * @return an url */ - public static String asLink(String baseURL, String repository, String objectId, String path) { + public static String asLink(String baseURL, String repository, String objectId, String path, Format format) { if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') { baseURL = baseURL.substring(0, baseURL.length() - 1); } return baseURL + Constants.ZIP_PATH + "?r=" + repository + (path == null ? "" : ("&p=" + path)) - + (objectId == null ? "" : ("&h=" + objectId)); + + (objectId == null ? "" : ("&h=" + objectId)) + + (format == null ? "" : ("&format=" + format.name())); } /** @@ -84,16 +106,22 @@ public class DownloadZipServlet extends HttpServlet { response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } - + + Format format = Format.zip; String repository = request.getParameter("r"); String basePath = request.getParameter("p"); String objectId = request.getParameter("h"); - + String f = request.getParameter("format"); + if (!StringUtils.isEmpty(f)) { + format = Format.fromName(f); + } + try { String name = repository; if (name.indexOf('/') > -1) { name = name.substring(name.lastIndexOf('/') + 1); } + name = StringUtils.stripDotGit(name); if (!StringUtils.isEmpty(basePath)) { name += "-" + basePath.replace('/', '_'); @@ -122,15 +150,31 @@ public class DownloadZipServlet extends HttpServlet { String contentType = "application/octet-stream"; response.setContentType(contentType + "; charset=" + response.getCharacterEncoding()); - response.setHeader("Content-Disposition", "attachment; filename=\"" + name + ".zip" - + "\""); + response.setHeader("Content-Disposition", "attachment; filename=\"" + name + format.extension + "\""); 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()); + switch (format) { + case zip: + CompressionUtils.zip(r, basePath, objectId, response.getOutputStream()); + break; + case tar: + CompressionUtils.tar(r, basePath, objectId, response.getOutputStream()); + break; + case gz: + CompressionUtils.gz(r, basePath, objectId, response.getOutputStream()); + break; + case xz: + CompressionUtils.xz(r, basePath, objectId, response.getOutputStream()); + break; + case bzip2: + CompressionUtils.bzip2(r, basePath, objectId, response.getOutputStream()); + break; + } + response.flushBuffer(); } catch (Throwable t) { logger.error("Failed to write attachment to client", t); diff --git a/src/com/gitblit/utils/CompressionUtils.java b/src/com/gitblit/utils/CompressionUtils.java new file mode 100644 index 00000000..7b0d0471 --- /dev/null +++ b/src/com/gitblit/utils/CompressionUtils.java @@ -0,0 +1,315 @@ +/* + * Copyright 2012 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.utils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; +import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; +import org.apache.commons.compress.compressors.CompressorException; +import org.apache.commons.compress.compressors.CompressorStreamFactory; +import org.apache.commons.compress.utils.IOUtils; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevBlob; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.treewalk.filter.PathFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Collection of static methods for retrieving information from a repository. + * + * @author James Moger + * + */ +public class CompressionUtils { + + static final Logger LOGGER = LoggerFactory.getLogger(CompressionUtils.class); + + /** + * Log an error message and exception. + * + * @param t + * @param repository + * if repository is not null it MUST be the {0} parameter in the + * pattern. + * @param pattern + * @param objects + */ + private static void error(Throwable t, Repository repository, String pattern, Object... objects) { + List parameters = new ArrayList(); + if (objects != null && objects.length > 0) { + for (Object o : objects) { + parameters.add(o); + } + } + if (repository != null) { + parameters.add(0, repository.getDirectory().getAbsolutePath()); + } + LOGGER.error(MessageFormat.format(pattern, parameters.toArray()), t); + } + + /** + * Zips the contents of the tree at the (optionally) specified revision and + * the (optionally) specified basepath to the supplied outputstream. + * + * @param repository + * @param basePath + * if unspecified, entire repository is assumed. + * @param objectId + * if unspecified, HEAD is assumed. + * @param os + * @return true if repository was successfully zipped to supplied output + * stream + */ + public static boolean zip(Repository repository, String basePath, String objectId, + OutputStream os) { + RevCommit commit = JGitUtils.getCommit(repository, objectId); + if (commit == null) { + return false; + } + boolean success = false; + RevWalk rw = new RevWalk(repository); + TreeWalk tw = new TreeWalk(repository); + try { + tw.addTree(commit.getTree()); + ZipOutputStream zos = new ZipOutputStream(os); + zos.setComment("Generated by Gitblit"); + if (!StringUtils.isEmpty(basePath)) { + PathFilter f = PathFilter.create(basePath); + tw.setFilter(f); + } + tw.setRecursive(true); + while (tw.next()) { + if (tw.getFileMode(0) == FileMode.GITLINK) { + continue; + } + ZipEntry entry = new ZipEntry(tw.getPathString()); + entry.setSize(tw.getObjectReader().getObjectSize(tw.getObjectId(0), + Constants.OBJ_BLOB)); + entry.setComment(commit.getName()); + zos.putNextEntry(entry); + + ObjectId entid = tw.getObjectId(0); + FileMode entmode = tw.getFileMode(0); + RevBlob blob = (RevBlob) rw.lookupAny(entid, entmode.getObjectType()); + rw.parseBody(blob); + + ObjectLoader ldr = repository.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(); + success = true; + } catch (IOException e) { + error(e, repository, "{0} failed to zip files from commit {1}", commit.getName()); + } finally { + tw.release(); + rw.dispose(); + } + return success; + } + + /** + * tar the contents of the tree at the (optionally) specified revision and + * the (optionally) specified basepath to the supplied outputstream. + * + * @param repository + * @param basePath + * if unspecified, entire repository is assumed. + * @param objectId + * if unspecified, HEAD is assumed. + * @param os + * @return true if repository was successfully zipped to supplied output + * stream + */ + public static boolean tar(Repository repository, String basePath, String objectId, + OutputStream os) { + return tar(null, repository, basePath, objectId, os); + } + + /** + * tar.gz the contents of the tree at the (optionally) specified revision and + * the (optionally) specified basepath to the supplied outputstream. + * + * @param repository + * @param basePath + * if unspecified, entire repository is assumed. + * @param objectId + * if unspecified, HEAD is assumed. + * @param os + * @return true if repository was successfully zipped to supplied output + * stream + */ + public static boolean gz(Repository repository, String basePath, String objectId, + OutputStream os) { + return tar(CompressorStreamFactory.GZIP, repository, basePath, objectId, os); + } + + /** + * tar.xz the contents of the tree at the (optionally) specified revision and + * the (optionally) specified basepath to the supplied outputstream. + * + * @param repository + * @param basePath + * if unspecified, entire repository is assumed. + * @param objectId + * if unspecified, HEAD is assumed. + * @param os + * @return true if repository was successfully zipped to supplied output + * stream + */ + public static boolean xz(Repository repository, String basePath, String objectId, + OutputStream os) { + return tar(CompressorStreamFactory.XZ, repository, basePath, objectId, os); + } + + /** + * tar.bzip2 the contents of the tree at the (optionally) specified revision and + * the (optionally) specified basepath to the supplied outputstream. + * + * @param repository + * @param basePath + * if unspecified, entire repository is assumed. + * @param objectId + * if unspecified, HEAD is assumed. + * @param os + * @return true if repository was successfully zipped to supplied output + * stream + */ + public static boolean bzip2(Repository repository, String basePath, String objectId, + OutputStream os) { + + return tar(CompressorStreamFactory.BZIP2, repository, basePath, objectId, os); + } + + /** + * Compresses/archives the contents of the tree at the (optionally) + * specified revision and the (optionally) specified basepath to the + * supplied outputstream. + * + * @param algorithm + * compression algorithm for tar (optional) + * @param repository + * @param basePath + * if unspecified, entire repository is assumed. + * @param objectId + * if unspecified, HEAD is assumed. + * @param os + * @return true if repository was successfully zipped to supplied output + * stream + */ + private static boolean tar(String algorithm, Repository repository, String basePath, String objectId, + OutputStream os) { + RevCommit commit = JGitUtils.getCommit(repository, objectId); + if (commit == null) { + return false; + } + + OutputStream cos = os; + if (!StringUtils.isEmpty(algorithm)) { + try { + cos = new CompressorStreamFactory().createCompressorOutputStream(algorithm, os); + } catch (CompressorException e1) { + error(e1, repository, "{0} failed to open {1} stream", algorithm); + } + } + boolean success = false; + RevWalk rw = new RevWalk(repository); + TreeWalk tw = new TreeWalk(repository); + try { + tw.addTree(commit.getTree()); + TarArchiveOutputStream tos = new TarArchiveOutputStream(cos); + tos.setAddPaxHeadersForNonAsciiNames(true); + tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX); + if (!StringUtils.isEmpty(basePath)) { + PathFilter f = PathFilter.create(basePath); + tw.setFilter(f); + } + tw.setRecursive(true); + while (tw.next()) { + FileMode mode = tw.getFileMode(0); + if (mode == FileMode.GITLINK) { + continue; + } + ObjectId id = tw.getObjectId(0); + + // new entry + TarArchiveEntry entry = new TarArchiveEntry(tw.getPathString()); + entry.setSize(tw.getObjectReader().getObjectSize(id, Constants.OBJ_BLOB)); + + if (FileMode.SYMLINK.equals(mode)) { + // symlink + entry.setMode(mode.getBits()); + + // read the symlink target + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + RevBlob blob = (RevBlob) rw.lookupAny(id, mode.getObjectType()); + rw.parseBody(blob); + ObjectLoader ldr = repository.open(blob.getId(), Constants.OBJ_BLOB); + IOUtils.copy(ldr.openStream(), bs); + entry.setLinkName(bs.toString("UTF-8")); + } else { + // regular file or executable file + entry.setMode(mode.getBits()); + } + entry.setModTime(commit.getAuthorIdent().getWhen()); + + tos.putArchiveEntry(entry); + + if (!FileMode.SYMLINK.equals(mode)) { + // write the blob + RevBlob blob = (RevBlob) rw.lookupAny(id, mode.getObjectType()); + rw.parseBody(blob); + ObjectLoader ldr = repository.open(blob.getId(), Constants.OBJ_BLOB); + IOUtils.copy(ldr.openStream(), tos); + } + + // close entry + tos.closeArchiveEntry(); + } + tos.finish(); + tos.close(); + cos.close(); + success = true; + } catch (IOException e) { + error(e, repository, "{0} failed to {1} stream files from commit {2}", algorithm, commit.getName()); + } finally { + tw.release(); + rw.dispose(); + } + return success; + } +} diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java index bc44f00f..9cfb37fd 100644 --- a/src/com/gitblit/utils/JGitUtils.java +++ b/src/com/gitblit/utils/JGitUtils.java @@ -19,7 +19,6 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; @@ -30,8 +29,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Pattern; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; import org.eclipse.jgit.api.CloneCommand; import org.eclipse.jgit.api.FetchCommand; @@ -1723,70 +1720,4 @@ public class JGitUtils { } return success; } - - /** - * Zips the contents of the tree at the (optionally) specified revision and - * the (optionally) specified basepath to the supplied outputstream. - * - * @param repository - * @param basePath - * if unspecified, entire repository is assumed. - * @param objectId - * if unspecified, HEAD is assumed. - * @param os - * @return true if repository was successfully zipped to supplied output - * stream - */ - public static boolean zip(Repository repository, String basePath, String objectId, - OutputStream os) { - RevCommit commit = getCommit(repository, objectId); - if (commit == null) { - return false; - } - boolean success = false; - RevWalk rw = new RevWalk(repository); - TreeWalk tw = new TreeWalk(repository); - try { - tw.addTree(commit.getTree()); - ZipOutputStream zos = new ZipOutputStream(os); - zos.setComment("Generated by Gitblit"); - if (!StringUtils.isEmpty(basePath)) { - PathFilter f = PathFilter.create(basePath); - tw.setFilter(f); - } - tw.setRecursive(true); - while (tw.next()) { - if (tw.getFileMode(0) == FileMode.GITLINK) { - continue; - } - ZipEntry entry = new ZipEntry(tw.getPathString()); - entry.setSize(tw.getObjectReader().getObjectSize(tw.getObjectId(0), - Constants.OBJ_BLOB)); - entry.setComment(commit.getName()); - zos.putNextEntry(entry); - - ObjectId entid = tw.getObjectId(0); - FileMode entmode = tw.getFileMode(0); - RevBlob blob = (RevBlob) rw.lookupAny(entid, entmode.getObjectType()); - rw.parseBody(blob); - - ObjectLoader ldr = repository.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(); - success = true; - } catch (IOException e) { - error(e, repository, "{0} failed to zip files from commit {1}", commit.getName()); - } finally { - tw.release(); - rw.dispose(); - } - return success; - } } diff --git a/src/com/gitblit/wicket/pages/CommitPage.html b/src/com/gitblit/wicket/pages/CommitPage.html index 20e6b607..79a038c9 100644 --- a/src/com/gitblit/wicket/pages/CommitPage.html +++ b/src/com/gitblit/wicket/pages/CommitPage.html @@ -30,7 +30,7 @@ tree [commit tree] - | + | parent diff --git a/src/com/gitblit/wicket/pages/CommitPage.java b/src/com/gitblit/wicket/pages/CommitPage.java index 7bc6b41d..b2a8112b 100644 --- a/src/com/gitblit/wicket/pages/CommitPage.java +++ b/src/com/gitblit/wicket/pages/CommitPage.java @@ -22,7 +22,6 @@ import java.util.List; 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; @@ -32,16 +31,15 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.Constants; -import com.gitblit.DownloadZipServlet; import com.gitblit.GitBlit; -import com.gitblit.Keys; import com.gitblit.models.GitNote; -import com.gitblit.models.SubmoduleModel; import com.gitblit.models.PathModel.PathChangeModel; +import com.gitblit.models.SubmoduleModel; import com.gitblit.utils.JGitUtils; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.CommitHeaderPanel; import com.gitblit.wicket.panels.CommitLegendPanel; +import com.gitblit.wicket.panels.CompressedDownloadsPanel; import com.gitblit.wicket.panels.GravatarImage; import com.gitblit.wicket.panels.LinkPanel; import com.gitblit.wicket.panels.RefsPanel; @@ -95,8 +93,8 @@ public class CommitPage extends RepositoryPage { newCommitParameter())); add(new BookmarkablePageLink("treeLink", TreePage.class, newCommitParameter())); final String baseUrl = WicketUtils.getGitblitURL(getRequest()); - add(new ExternalLink("zipLink", DownloadZipServlet.asLink(baseUrl, repositoryName, - objectId, null)).setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true))); + + add(new CompressedDownloadsPanel("compressedLinks", baseUrl, repositoryName, objectId, null)); // Parent Commits ListDataProvider parentsDp = new ListDataProvider(parents); diff --git a/src/com/gitblit/wicket/pages/TreePage.html b/src/com/gitblit/wicket/pages/TreePage.html index 0047ff0d..b7e55ed6 100644 --- a/src/com/gitblit/wicket/pages/TreePage.html +++ b/src/com/gitblit/wicket/pages/TreePage.html @@ -9,7 +9,7 @@ @@ -32,14 +32,14 @@ - | | | + | | | - | | + | | diff --git a/src/com/gitblit/wicket/pages/TreePage.java b/src/com/gitblit/wicket/pages/TreePage.java index 973634b7..345814f4 100644 --- a/src/com/gitblit/wicket/pages/TreePage.java +++ b/src/com/gitblit/wicket/pages/TreePage.java @@ -20,7 +20,6 @@ import java.util.List; 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; @@ -30,15 +29,13 @@ import org.eclipse.jgit.lib.FileMode; 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.models.PathModel; import com.gitblit.models.SubmoduleModel; import com.gitblit.utils.ByteFormat; import com.gitblit.utils.JGitUtils; import com.gitblit.wicket.WicketUtils; import com.gitblit.wicket.panels.CommitHeaderPanel; +import com.gitblit.wicket.panels.CompressedDownloadsPanel; import com.gitblit.wicket.panels.LinkPanel; import com.gitblit.wicket.panels.PathBreadcrumbsPanel; @@ -58,9 +55,8 @@ public class TreePage extends RepositoryPage { WicketUtils.newPathParameter(repositoryName, objectId, path))); add(new BookmarkablePageLink("headLink", TreePage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, path))); - add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest() - .getRelativePathPrefixToContextRoot(), repositoryName, objectId, path)) - .setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true))); + add(new CompressedDownloadsPanel("compressedLinks", getRequest() + .getRelativePathPrefixToContextRoot(), repositoryName, objectId, path)); add(new CommitHeaderPanel("commitHeader", repositoryName, commit)); @@ -114,10 +110,10 @@ public class TreePage extends RepositoryPage { entry.path))); links.add(new BookmarkablePageLink("history", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, - entry.path))); - links.add(new ExternalLink("zip", DownloadZipServlet.asLink(baseUrl, - repositoryName, objectId, entry.path)).setVisible(GitBlit - .getBoolean(Keys.web.allowZipDownloads, true))); + entry.path))); + links.add(new CompressedDownloadsPanel("compressedLinks", baseUrl, + repositoryName, objectId, entry.path)); + item.add(links); } else if (entry.isSubmodule()) { // submodule @@ -143,9 +139,8 @@ public class TreePage extends RepositoryPage { links.add(new BookmarkablePageLink("history", HistoryPage.class, WicketUtils.newPathParameter(submodulePath, submoduleId, "")).setEnabled(hasSubmodule)); - links.add(new ExternalLink("zip", DownloadZipServlet.asLink(baseUrl, - submodulePath, submoduleId, "")).setVisible(GitBlit - .getBoolean(Keys.web.allowZipDownloads, true)).setEnabled(hasSubmodule)); + links.add(new CompressedDownloadsPanel("compressedLinks", baseUrl, + submodulePath, submoduleId, "").setEnabled(hasSubmodule)); item.add(links); } else { // blob link diff --git a/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.html b/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.html new file mode 100644 index 00000000..7123d0a7 --- /dev/null +++ b/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.html @@ -0,0 +1,12 @@ + + + + + + |ref + + + \ No newline at end of file diff --git a/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.java b/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.java new file mode 100644 index 00000000..b22c7587 --- /dev/null +++ b/src/com/gitblit/wicket/panels/CompressedDownloadsPanel.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012 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.util.List; + +import org.apache.wicket.Component; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.markup.repeater.Item; +import org.apache.wicket.markup.repeater.data.DataView; +import org.apache.wicket.markup.repeater.data.ListDataProvider; + +import com.gitblit.DownloadZipServlet; +import com.gitblit.DownloadZipServlet.Format; +import com.gitblit.GitBlit; +import com.gitblit.Keys; + +public class CompressedDownloadsPanel extends Panel { + + private static final long serialVersionUID = 1L; + + public CompressedDownloadsPanel(String id, final String baseUrl, final String repositoryName, final String objectId, final String path) { + super(id); + + List types = GitBlit.getStrings(Keys.web.compressedDownloads); + if (types.isEmpty()) { + types.add(Format.zip.name()); + types.add(Format.gz.name()); + } + + ListDataProvider refsDp = new ListDataProvider(types); + DataView refsView = new DataView("compressedLinks", refsDp) { + private static final long serialVersionUID = 1L; + int counter; + + @Override + protected void onBeforeRender() { + super.onBeforeRender(); + counter = 0; + } + + @Override + public void populateItem(final Item item) { + String compressionType = item.getModelObject(); + Format format = Format.fromName(compressionType); + + String href = DownloadZipServlet.asLink(baseUrl, repositoryName, + objectId, path, format); + Component c = new LinkPanel("compressedLink", null, format.name(), href); + item.add(c); + Label lb = new Label("linkSep", "|"); + lb.setVisible(counter > 0); + lb.setRenderBodyOnly(true); + item.add(lb.setEscapeModelStrings(false)); + item.setRenderBodyOnly(true); + counter++; + } + }; + add(refsView); + + setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true)); + } +} \ No newline at end of file diff --git a/tests/com/gitblit/tests/JGitUtilsTest.java b/tests/com/gitblit/tests/JGitUtilsTest.java index 7e4d6309..ce72a46f 100644 --- a/tests/com/gitblit/tests/JGitUtilsTest.java +++ b/tests/com/gitblit/tests/JGitUtilsTest.java @@ -50,6 +50,7 @@ import com.gitblit.models.GitNote; import com.gitblit.models.PathModel; import com.gitblit.models.PathModel.PathChangeModel; import com.gitblit.models.RefModel; +import com.gitblit.utils.CompressionUtils; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.StringUtils; @@ -446,16 +447,16 @@ public class JGitUtilsTest { @Test public void testZip() throws Exception { - assertFalse(JGitUtils.zip(null, null, null, null)); + assertFalse(CompressionUtils.zip(null, null, null, null)); Repository repository = GitBlitSuite.getHelloworldRepository(); File zipFileA = new File(GitBlitSuite.REPOSITORIES, "helloworld.zip"); FileOutputStream fosA = new FileOutputStream(zipFileA); - boolean successA = JGitUtils.zip(repository, null, Constants.HEAD, fosA); + boolean successA = CompressionUtils.zip(repository, null, Constants.HEAD, fosA); fosA.close(); File zipFileB = new File(GitBlitSuite.REPOSITORIES, "helloworld-java.zip"); FileOutputStream fosB = new FileOutputStream(zipFileB); - boolean successB = JGitUtils.zip(repository, "java.java", Constants.HEAD, fosB); + boolean successB = CompressionUtils.zip(repository, "java.java", Constants.HEAD, fosB); fosB.close(); repository.close(); -- 2.39.5