@@ -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 |
@@ -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 |
@@ -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); |
@@ -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<Object> parameters = new ArrayList<Object>(); | |||
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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -30,7 +30,7 @@ | |||
<tr class="hidden-phone"><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> | |||
<a wicket:id="treeLink"><wicket:message key="gb.tree"></wicket:message></a> | <span wicket:id="compressedLinks"></span> | |||
</span> | |||
</td></tr> | |||
<tr class="hidden-phone"><th valign="top"><wicket:message key="gb.parent">parent</wicket:message></th> |
@@ -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<Void>("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<String> parentsDp = new ListDataProvider<String>(parents); |
@@ -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="zipLink"><wicket:message key="gb.zip"></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> | <span wicket:id="compressedLinks"></span> | |||
</div> | |||
<!-- commit header --> | |||
@@ -32,14 +32,14 @@ | |||
<!-- submodule links --> | |||
<wicket:fragment wicket:id="submoduleLinks"> | |||
<span class="link"> | |||
<a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="zip"><wicket:message key="gb.zip"></wicket:message></a> | |||
<a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <span wicket:id="compressedLinks"></span> | |||
</span> | |||
</wicket:fragment> | |||
<!-- tree links --> | |||
<wicket:fragment wicket:id="treeLinks"> | |||
<span class="link"> | |||
<span class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><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 class="hidden-phone"><a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | </span><a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <span wicket:id="compressedLinks"></span> | |||
</span> | |||
</wicket:fragment> | |||
@@ -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<Void>("headLink", TreePage.class, | |||
WicketUtils.newPathParameter(repositoryName, Constants.HEAD, path))); | |||
add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest() | |||
.getRelativePathPrefixToContextRoot(), repositoryName, objectId, path)) | |||
.setVisible(GitBlit.getBoolean(Keys.web.allowZipDownloads, true))); | |||
add(new 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<Void>("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<Void>("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 |
@@ -0,0 +1,12 @@ | |||
<!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"> | |||
<wicket:panel> | |||
<span wicket:id="compressedLinks"> | |||
<span wicket:id="linkSep">|</span><span wicket:id="compressedLink">ref</span> | |||
</span> | |||
</wicket:panel> | |||
</html> |
@@ -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<String> types = GitBlit.getStrings(Keys.web.compressedDownloads); | |||
if (types.isEmpty()) { | |||
types.add(Format.zip.name()); | |||
types.add(Format.gz.name()); | |||
} | |||
ListDataProvider<String> refsDp = new ListDataProvider<String>(types); | |||
DataView<String> refsView = new DataView<String>("compressedLinks", refsDp) { | |||
private static final long serialVersionUID = 1L; | |||
int counter; | |||
@Override | |||
protected void onBeforeRender() { | |||
super.onBeforeRender(); | |||
counter = 0; | |||
} | |||
@Override | |||
public void populateItem(final Item<String> 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)); | |||
} | |||
} |
@@ -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(); |