@@ -95,6 +95,22 @@ JSch | |||
http://www.jcraft.com/jsch | |||
--------------------------------------------------------------------------- | |||
Rome | |||
--------------------------------------------------------------------------- | |||
Rome RSS and Atom Java Utilities, released under the | |||
Apache Software License, Version 1.1. | |||
http://rome.dev.java.net | |||
--------------------------------------------------------------------------- | |||
jdom | |||
--------------------------------------------------------------------------- | |||
jdom xml library, released under the | |||
Apache-style Software License. | |||
http://www.jdom.org | |||
--------------------------------------------------------------------------- | |||
JUnit | |||
--------------------------------------------------------------------------- |
@@ -64,6 +64,9 @@ web.allowAdministration = true | |||
# Allow dyanamic zip downloads. | |||
web.allowZipDownloads = true | |||
# Default number of entries to include in RSS/Atom Syndication links | |||
web.syndicationEntries = 25 | |||
# 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. |
@@ -32,6 +32,7 @@ sources @ [Github][gitbltsrc] | |||
- Automatically generates a self-signed certificate for https communications | |||
- Git-notes support | |||
- Branch metrics (uses Google Charts) | |||
- HEAD and branch RSS feeds | |||
- Blame annotations view | |||
- Dates can optionally be displayed using the browser's reported timezone | |||
- Display of Author and Committer email addresses can be disabled | |||
@@ -60,6 +61,7 @@ sources @ [Github][gitbltsrc] | |||
- Gitblit may have security holes. Patches welcome. :) | |||
### Todo List | |||
- Custom BASIC authentication servlet or servlet filter | |||
- Code documentation | |||
- Unit testing | |||
- Update Build.java to JGit 1.0.0, when its released | |||
@@ -110,6 +112,8 @@ The following dependencies are automatically downloaded from the Apache Maven re | |||
- [JCommander](http://jcommander.org) (Apache 2.0) | |||
- [BouncyCastle](http://www.bouncycastle.org) (MIT/X11) | |||
- [JSch - Java Secure Channel](http://www.jcraft.com/jsch) (BSD) | |||
- [Rome](http://rome.dev.java.net) (Apache 1.1) | |||
- [jdom](http://www.jdom.org) (Apache-style JDOM license) | |||
### Other Build Dependencies | |||
- [Fancybox image viewer](http://fancybox.net) (MIT and GPL dual-licensed) |
@@ -91,7 +91,7 @@ You must tell Git not to verify the self-signed certificate in order to perform | |||
3. <pre>Key = *http.sslVerify* | |||
Value = *false*</pre> | |||
- Command-line Git ([Git-Config Manual Page](http://www.kernel.org/pub/software/scm/git/docs/git-config.html)) | |||
<pre>git-config --global --bool --add http.sslVerify false</pre> | |||
<pre>git config --global --bool --add http.sslVerify false</pre> | |||
### Cloning an Access Restricted Repository | |||
- Eclipse/Egit<br/>Nothing special to configure, EGit figures out everything. |
@@ -100,5 +100,4 @@ Yes. Most messages are localized to a standard Java properties file. | |||
[jgit]: http://eclipse.org/jgit "Eclipse JGit Site" | |||
[git]: http://git-scm.com "Official Git Site" | |||
[mina]: http://mina.apache.org "Apache Mina" | |||
[bouncycastle]: http://bouncycastle.org "The Legion of the Bouncy Castle" | |||
[hg4j]: http://code.google.com/p/hg4j/ "hg4j" | |||
[bouncycastle]: http://bouncycastle.org "The Legion of the Bouncy Castle" |
@@ -61,6 +61,8 @@ public class Build { | |||
downloadFromApache(MavenObject.BOUNCYCASTLE, BuildType.RUNTIME); | |||
downloadFromApache(MavenObject.BOUNCYCASTLE_MAIL, BuildType.RUNTIME); | |||
downloadFromApache(MavenObject.JSCH, BuildType.RUNTIME); | |||
downloadFromApache(MavenObject.ROME, BuildType.RUNTIME); | |||
downloadFromApache(MavenObject.JDOM, BuildType.RUNTIME); | |||
downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME); | |||
downloadFromEclipse(MavenObject.JGIT_HTTP, BuildType.RUNTIME); | |||
@@ -82,7 +84,9 @@ public class Build { | |||
downloadFromApache(MavenObject.BOUNCYCASTLE, BuildType.COMPILETIME); | |||
downloadFromApache(MavenObject.BOUNCYCASTLE_MAIL, BuildType.COMPILETIME); | |||
downloadFromApache(MavenObject.JSCH, BuildType.COMPILETIME); | |||
downloadFromApache(MavenObject.ROME, BuildType.COMPILETIME); | |||
downloadFromApache(MavenObject.JDOM, BuildType.COMPILETIME); | |||
downloadFromEclipse(MavenObject.JGIT, BuildType.COMPILETIME); | |||
downloadFromEclipse(MavenObject.JGIT_HTTP, BuildType.COMPILETIME); | |||
@@ -401,6 +405,16 @@ public class Build { | |||
"1.4.0", 181000, 0, 0, "eb47e8cad2dd7f92fd7e77df1d1529cae87361f7", | |||
"", | |||
""); | |||
public static final MavenObject ROME = new MavenObject("rome", "rome", "rome", | |||
"0.9", 208000, 196000, 407000, "dee2705dd01e79a5a96a17225f5a1ae30470bb18", | |||
"226f851dc44fd94fe70b9c471881b71f88949cbf", | |||
"8d7d867b97eeb3a9196c3926da550ad042941c1b"); | |||
public static final MavenObject JDOM = new MavenObject("jdom", "org/jdom", "jdom", | |||
"1.1", 153000, 235000, 445000, "1d04c0f321ea337f3661cf7ede8f4c6f653a8fdd", | |||
"a7ed425c4c46605b8f2bf2ee118c1609682f4f2c", | |||
"f3df91edccba2f07a0fced70887c2f7b7836cb75"); | |||
public final String name; | |||
public final String group; |
@@ -36,6 +36,8 @@ public class Constants { | |||
public static final String GIT_SERVLET_PATH = "/git/"; | |||
public static final String ZIP_SERVLET_PATH = "/zip/"; | |||
public static final String SYNDICATION_SERVLET_PATH = "/feed/"; | |||
public static final String BORDER = "***********************************************************"; | |||
@@ -41,7 +41,10 @@ public class DownloadZipServlet extends HttpServlet { | |||
} | |||
public static String asLink(String baseURL, String repository, String objectId, String path) { | |||
return baseURL + (baseURL.endsWith("/") ? "" : "/") + "zip?r=" + repository | |||
if (baseURL.charAt(baseURL.length() - 1) == '/') { | |||
baseURL = baseURL.substring(0, baseURL.length() - 1); | |||
} | |||
return baseURL + Constants.ZIP_SERVLET_PATH + "?r=" + repository | |||
+ (path == null ? "" : ("&p=" + path)) | |||
+ (objectId == null ? "" : ("&h=" + objectId)); | |||
} |
@@ -240,6 +240,9 @@ public class GitBlitServer { | |||
// Zip Servlet | |||
rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*"); | |||
// Syndication Servlet | |||
rootContext.addServlet(SyndicationServlet.class, Constants.SYNDICATION_SERVLET_PATH + "*"); | |||
// Git Servlet | |||
ServletHolder gitServlet = null; | |||
String gitServletPathSpec = Constants.GIT_SERVLET_PATH + "*"; |
@@ -412,7 +412,7 @@ public class JettyLoginService extends MappedLoginService implements ILoginServi | |||
FileWriter writer = new FileWriter(realmFileCopy); | |||
properties | |||
.store(writer, | |||
"# Git:Blit realm file format: username=password,\\#permission,repository1,repository2..."); | |||
"# Gitblit realm file format: username=password,\\#permission,repository1,repository2..."); | |||
writer.close(); | |||
if (realmFileCopy.exists() && realmFileCopy.length() > 0) { | |||
if (realmFile.delete()) { |
@@ -0,0 +1,91 @@ | |||
/* | |||
* Copyright 2011 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit; | |||
import java.util.List; | |||
import javax.servlet.http.HttpServlet; | |||
import org.eclipse.jgit.lib.Repository; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.utils.JGitUtils; | |||
import com.gitblit.utils.StringUtils; | |||
import com.gitblit.utils.SyndicationUtils; | |||
public class SyndicationServlet extends HttpServlet { | |||
private static final long serialVersionUID = 1L; | |||
private transient Logger logger = LoggerFactory.getLogger(SyndicationServlet.class); | |||
public static String asLink(String baseURL, String repository, String objectId, int length) { | |||
if (baseURL.charAt(baseURL.length() - 1) == '/') { | |||
baseURL = baseURL.substring(0, baseURL.length() - 1); | |||
} | |||
return baseURL + Constants.SYNDICATION_SERVLET_PATH + "?r=" + repository | |||
+ (objectId == null ? "" : ("&h=" + objectId)) + (length > 0 ? "&l=" + length : ""); | |||
} | |||
private void processRequest(javax.servlet.http.HttpServletRequest request, | |||
javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, | |||
java.io.IOException { | |||
String hostUrl = request.getRequestURL().toString(); | |||
String servlet = request.getServletPath(); | |||
hostUrl = hostUrl.substring(0, hostUrl.indexOf(servlet)); | |||
String repositoryName = request.getParameter("r"); | |||
String objectId = request.getParameter("h"); | |||
String l = request.getParameter("l"); | |||
int length = GitBlit.getInteger(Keys.web.syndicationEntries, 25); | |||
if (StringUtils.isEmpty(objectId)) { | |||
objectId = org.eclipse.jgit.lib.Constants.HEAD; | |||
} | |||
if (!StringUtils.isEmpty(l)) { | |||
try { | |||
length = Integer.parseInt(l); | |||
} catch (NumberFormatException x) { | |||
} | |||
} | |||
// TODO confirm repository is accessible!! | |||
Repository repository = GitBlit.self().getRepository(repositoryName); | |||
RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName); | |||
List<RevCommit> commits = JGitUtils.getRevLog(repository, objectId, 0, length); | |||
try { | |||
SyndicationUtils.toRSS(hostUrl, model.name + " " + objectId, model.description, model.name, commits, response.getOutputStream()); | |||
} catch (Exception e) { | |||
logger.error("An error occurred during feed generation", e); | |||
} | |||
} | |||
@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); | |||
} | |||
} |
@@ -0,0 +1,69 @@ | |||
/* | |||
* Copyright 2011 gitblit.com. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.gitblit.utils; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.io.OutputStreamWriter; | |||
import java.text.MessageFormat; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.eclipse.jgit.revwalk.RevCommit; | |||
import com.sun.syndication.feed.synd.SyndContent; | |||
import com.sun.syndication.feed.synd.SyndContentImpl; | |||
import com.sun.syndication.feed.synd.SyndEntry; | |||
import com.sun.syndication.feed.synd.SyndEntryImpl; | |||
import com.sun.syndication.feed.synd.SyndFeed; | |||
import com.sun.syndication.feed.synd.SyndFeedImpl; | |||
import com.sun.syndication.io.FeedException; | |||
import com.sun.syndication.io.SyndFeedOutput; | |||
public class SyndicationUtils { | |||
public static void toRSS(String hostUrl, String title, String description, String repository, List<RevCommit> commits, OutputStream os) | |||
throws IOException, FeedException { | |||
SyndFeed feed = new SyndFeedImpl(); | |||
feed.setFeedType("rss_1.0"); | |||
feed.setTitle(title); | |||
feed.setLink(MessageFormat.format("{0}/summary/{1}", hostUrl, repository)); | |||
feed.setDescription(description); | |||
List<SyndEntry> entries = new ArrayList<SyndEntry>(); | |||
for (RevCommit commit : commits) { | |||
SyndEntry entry = new SyndEntryImpl(); | |||
entry.setTitle(commit.getShortMessage()); | |||
entry.setAuthor(commit.getAuthorIdent().getName()); | |||
entry.setLink(MessageFormat.format("{0}/commit/{1}/{2}", hostUrl, repository, commit.getName())); | |||
entry.setPublishedDate(commit.getCommitterIdent().getWhen()); | |||
SyndContent content = new SyndContentImpl(); | |||
content.setType("text/html"); | |||
String html = StringUtils.escapeForHtml(commit.getFullMessage(), false); | |||
content.setValue(StringUtils.breakLinesForHtml(html)); | |||
entry.setDescription(content); | |||
entries.add(entry); | |||
} | |||
feed.setEntries(entries); | |||
OutputStreamWriter writer = new OutputStreamWriter(os); | |||
SyndFeedOutput output = new SyndFeedOutput(); | |||
output.output(feed, writer); | |||
writer.close(); | |||
} | |||
} |
@@ -98,4 +98,5 @@ gb.ownerDescription = the owner may edit repository settings | |||
gb.blob = blob | |||
gb.commitActivityTrend = commit activity trend | |||
gb.commitActivityDOW = commit activity by day of week | |||
gb.commitActivityAuthors = primary authors by commit activity | |||
gb.commitActivityAuthors = primary authors by commit activity | |||
gb.feed = feed |
@@ -134,7 +134,7 @@ public class MetricsPage extends RepositoryPage { | |||
SimpleDateFormat sdf = new SimpleDateFormat("E"); | |||
Calendar cal = Calendar.getInstance(); | |||
List<Metric> sorted = new ArrayList<Metric>(7); | |||
List<Metric> sorted = new ArrayList<Metric>(); | |||
int firstDayOfWeek = cal.getFirstDayOfWeek(); | |||
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); | |||
@@ -144,7 +144,7 @@ public class MetricsPage extends RepositoryPage { | |||
String day = sdf.format(cal.getTime()); | |||
for (Metric metric : list) { | |||
if (metric.name.equals(day)) { | |||
sorted.add(i, metric); | |||
sorted.add(metric); | |||
list.remove(metric); | |||
break; | |||
} |
@@ -9,15 +9,18 @@ | |||
<!-- page header bar --> | |||
<div> | |||
<!-- floating search form on right --> | |||
<form wicket:id="searchForm"> | |||
<div class="search"> | |||
<div class="search"> | |||
<form wicket:id="searchForm"> | |||
<select wicket:id="searchType"/> | |||
<input type="text" id="searchBox" wicket:id="searchBox" size="25" value=""/> | |||
</div> | |||
</form> | |||
</form> | |||
</div> | |||
<!-- page nav links --> | |||
<div class="page_nav"> | |||
<div class="page_nav"> | |||
<a style="text-decoration: none;" wicket:id="syndication"> | |||
<img style="border:0px;vertical-align:middle;" src="/com/gitblit/wicket/resources/feed_16x16.png"></img> | |||
</a> | |||
<a wicket:id="summary"><wicket:message key="gb.summary"></wicket:message></a> | <a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="branches"><wicket:message key="gb.branches"></wicket:message></a> | <a wicket:id="tags"><wicket:message key="gb.tags"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> <span wicket:id="extra"><span wicket:id="extraSeparator"></span><span wicket:id="extraLink"></span></span> | |||
</div> | |||
</div> |
@@ -30,6 +30,7 @@ import org.apache.wicket.markup.html.form.DropDownChoice; | |||
import org.apache.wicket.markup.html.form.StatelessForm; | |||
import org.apache.wicket.markup.html.form.TextField; | |||
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; | |||
@@ -45,6 +46,7 @@ import org.slf4j.LoggerFactory; | |||
import com.gitblit.GitBlit; | |||
import com.gitblit.Keys; | |||
import com.gitblit.SyndicationServlet; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.utils.JGitUtils; | |||
import com.gitblit.utils.JGitUtils.SearchType; | |||
@@ -157,6 +159,9 @@ public abstract class RepositoryPage extends BasePage { | |||
} | |||
}; | |||
add(extrasView); | |||
add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest() | |||
.getRelativePathPrefixToContextRoot(), repositoryName, null, 0))); | |||
// disable current page | |||
disablePageLink(getPageName()); |
@@ -28,7 +28,7 @@ | |||
<!-- branch page links --> | |||
<wicket:fragment wicket:id="branchPageLinks"> | |||
<span class="link"> | |||
<a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="metrics"><wicket:message key="gb.metrics"></wicket:message></a> | |||
<a wicket:id="log"><wicket:message key="gb.log"></wicket:message></a> | <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="metrics"><wicket:message key="gb.metrics"></wicket:message></a> | <a wicket:id="syndication"><wicket:message key="gb.feed"></wicket:message></a> | |||
</span> | |||
</wicket:fragment> | |||
@@ -21,6 +21,7 @@ import java.util.List; | |||
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; | |||
@@ -29,6 +30,7 @@ import org.apache.wicket.model.StringResourceModel; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.Repository; | |||
import com.gitblit.SyndicationServlet; | |||
import com.gitblit.models.RefModel; | |||
import com.gitblit.models.RepositoryModel; | |||
import com.gitblit.utils.JGitUtils; | |||
@@ -101,6 +103,8 @@ public class BranchesPanel extends BasePanel { | |||
.newObjectParameter(model.name, entry.getName()))); | |||
fragment.add(new BookmarkablePageLink<Void>("metrics", MetricsPage.class, | |||
WicketUtils.newObjectParameter(model.name, entry.getName()))); | |||
fragment.add(new ExternalLink("syndication", SyndicationServlet.asLink(getRequest() | |||
.getRelativePathPrefixToContextRoot(), model.name, entry.getName(), 0))); | |||
item.add(fragment); | |||
} else { | |||
Fragment fragment = new Fragment("branchLinks", "branchPanelLinks", this); |
@@ -486,6 +486,10 @@ table.plain { | |||
padding: 8px; | |||
} | |||
table.plain td { | |||
white-space: nowrap; | |||
} | |||
table.plain td.edit { | |||
padding: 3px; | |||
} |