From 4669a718dba6a8760f8a42d393acbe711735feae Mon Sep 17 00:00:00 2001
From: James Moger <james.moger@gitblit.com>
Date: Mon, 05 May 2014 09:56:06 -0400
Subject: [PATCH] Merged #49 "Split pages servlet into a raw branch servlet and a gh-pages servlet"

---
 src/main/java/com/gitblit/wicket/pages/TreePage.java       |    6 
 .classpath                                                 |    1 
 src/main/java/com/gitblit/wicket/panels/TagsPanel.java     |   11 
 src/main/java/WEB-INF/web.xml                              |   36 +
 src/main/java/com/gitblit/Constants.java                   |    2 
 src/main/java/com/gitblit/servlet/PagesFilter.java         |   99 -----
 src/main/java/com/gitblit/wicket/GitBlitWebApp.java        |    4 
 src/main/java/com/gitblit/servlet/PagesServlet.java        |  302 +--------------
 src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java |    5 
 src/main/java/com/gitblit/wicket/pages/BasePage.java       |    4 
 src/main/java/com/gitblit/wicket/pages/DocsPage.java       |    9 
 src/main/java/com/gitblit/wicket/pages/BlobPage.java       |   12 
 src/main/java/com/gitblit/wicket/pages/DocPage.java        |    6 
 src/main/java/com/gitblit/servlet/RawServlet.java          |  472 ++++++++++++++++++++++++
 releases.moxie                                             |    4 
 src/main/java/com/gitblit/wicket/pages/CommitPage.java     |    5 
 build.moxie                                                |    1 
 gitblit.iml                                                |   11 
 src/main/java/com/gitblit/servlet/RawFilter.java           |  126 ++++++
 src/main/java/com/gitblit/wicket/MarkupProcessor.java      |   11 
 src/main/java/com/gitblit/wicket/pages/ComparePage.java    |    5 
 21 files changed, 720 insertions(+), 412 deletions(-)

diff --git a/.classpath b/.classpath
index b2e98b1..e577b7c 100644
--- a/.classpath
+++ b/.classpath
@@ -76,6 +76,7 @@
 	<classpathentry kind="lib" path="ext/jedis-2.3.1.jar" sourcepath="ext/src/jedis-2.3.1.jar" />
 	<classpathentry kind="lib" path="ext/commons-pool2-2.0.jar" sourcepath="ext/src/commons-pool2-2.0.jar" />
 	<classpathentry kind="lib" path="ext/pf4j-0.8.0.jar" sourcepath="ext/src/pf4j-0.8.0.jar" />
+	<classpathentry kind="lib" path="ext/tika-core-1.5.jar" sourcepath="ext/src/tika-core-1.5.jar" />
 	<classpathentry kind="lib" path="ext/junit-4.11.jar" sourcepath="ext/src/junit-4.11.jar" />
 	<classpathentry kind="lib" path="ext/hamcrest-core-1.3.jar" sourcepath="ext/src/hamcrest-core-1.3.jar" />
 	<classpathentry kind="lib" path="ext/selenium-java-2.28.0.jar" sourcepath="ext/src/selenium-java-2.28.0.jar" />
diff --git a/build.moxie b/build.moxie
index 6d80c50..5f56053 100644
--- a/build.moxie
+++ b/build.moxie
@@ -174,6 +174,7 @@
 - compile 'commons-codec:commons-codec:1.7' :war
 - compile 'redis.clients:jedis:2.3.1' :war
 - compile 'ro.fortsoft.pf4j:pf4j:0.8.0' :war
+- compile 'org.apache.tika:tika-core:1.5' :war
 - test 'junit'
 # Dependencies for Selenium web page testing
 - test 'org.seleniumhq.selenium:selenium-java:${selenium.version}' @jar
diff --git a/gitblit.iml b/gitblit.iml
index 582127d..7e25249 100644
--- a/gitblit.iml
+++ b/gitblit.iml
@@ -790,6 +790,17 @@
         </SOURCES>
       </library>
     </orderEntry>
+    <orderEntry type="module-library">
+      <library name="tika-core-1.5.jar">
+        <CLASSES>
+          <root url="jar://$MODULE_DIR$/ext/tika-core-1.5.jar!/" />
+        </CLASSES>
+        <JAVADOC />
+        <SOURCES>
+          <root url="jar://$MODULE_DIR$/ext/src/tika-core-1.5.jar!/" />
+        </SOURCES>
+      </library>
+    </orderEntry>
     <orderEntry type="module-library" scope="TEST">
       <library name="junit-4.11.jar">
         <CLASSES>
diff --git a/releases.moxie b/releases.moxie
index 48f430e..62a71c6 100644
--- a/releases.moxie
+++ b/releases.moxie
@@ -18,10 +18,12 @@
     - Prevent submission from New|Edit ticket page with empty titles (ticket-53)
     changes:
     - improve French translation (pr-176)
-    - simplify current plugin release detection and ignore the currentRelease registry field 
+    - simplify current plugin release detection and ignore the currentRelease registry field
+    - split pages servlet into two servlets (issue-413)
     additions: ~
     dependencyChanges:
     - update to Apache MINA/SSHD 0.11.0 (issue-410)
+    - added Apache Tiki 1.5 (issue-413)
     contributors:
     - James Moger
     - Julien Kirch
diff --git a/src/main/java/WEB-INF/web.xml b/src/main/java/WEB-INF/web.xml
index 77456d4..cb483af 100644
--- a/src/main/java/WEB-INF/web.xml
+++ b/src/main/java/WEB-INF/web.xml
@@ -134,6 +134,21 @@
 	</servlet-mapping>	
 
 
+	<!-- Raw Servlet
+		 <url-pattern> MUST match: 
+			* RawFilter
+			* com.gitblit.Constants.RAW_PATH
+			* Wicket Filter ignorePaths parameter -->
+	<servlet>
+		<servlet-name>RawServlet</servlet-name>
+		<servlet-class>com.gitblit.servlet.RawServlet</servlet-class>
+	</servlet>
+	<servlet-mapping>
+		<servlet-name>RawServlet</servlet-name>		
+		<url-pattern>/raw/*</url-pattern>
+	</servlet-mapping>	
+
+
 	<!-- Pages Servlet
 		 <url-pattern> MUST match: 
 			* PagesFilter
@@ -263,7 +278,22 @@
 	</filter-mapping>
 
 
-	<!-- Pges Restriction Filter
+	<!-- Branch Restriction Filter
+		 <url-pattern> MUST match: 
+			* RawServlet
+			* com.gitblit.Constants.BRANCH_PATH
+			* Wicket Filter ignorePaths parameter -->
+	<filter>
+		<filter-name>RawFilter</filter-name>
+		<filter-class>com.gitblit.servlet.RawFilter</filter-class>
+	</filter>
+	<filter-mapping>
+		<filter-name>RawFilter</filter-name>
+		<url-pattern>/raw/*</url-pattern>
+	</filter-mapping>
+	
+
+	<!-- Pages Restriction Filter
 		 <url-pattern> MUST match: 
 			* PagesServlet
 			* com.gitblit.Constants.PAGES_PATH
@@ -310,10 +340,12 @@
              	* FederationServlet <url-pattern>
              	* RpcFilter <url-pattern>
              	* RpcServlet <url-pattern>
+             	* RawFilter <url-pattern>
+             	* RawServlet <url-pattern>
              	* PagesFilter <url-pattern>
              	* PagesServlet <url-pattern>
              	* com.gitblit.Constants.PAGES_PATH -->
-            <param-value>r/,git/,pt,feed/,zip/,federation/,rpc/,pages/,robots.txt,logo.png,graph/,sparkleshare/</param-value>
+            <param-value>r/,git/,pt,feed/,zip/,federation/,rpc/,raw/,pages/,robots.txt,logo.png,graph/,sparkleshare/</param-value>
         </init-param>
     </filter>
     <filter-mapping>
diff --git a/src/main/java/com/gitblit/Constants.java b/src/main/java/com/gitblit/Constants.java
index af53399..4b9755f 100644
--- a/src/main/java/com/gitblit/Constants.java
+++ b/src/main/java/com/gitblit/Constants.java
@@ -68,6 +68,8 @@
 
 	public static final String SPARKLESHARE_INVITE_PATH = "/sparkleshare/";
 
+	public static final String RAW_PATH = "/raw/";
+
 	public static final String BRANCH_GRAPH_PATH = "/graph/";
 
 	public static final String BORDER = "*****************************************************************";
diff --git a/src/main/java/com/gitblit/servlet/PagesFilter.java b/src/main/java/com/gitblit/servlet/PagesFilter.java
index 9e00916..e07d9b3 100644
--- a/src/main/java/com/gitblit/servlet/PagesFilter.java
+++ b/src/main/java/com/gitblit/servlet/PagesFilter.java
@@ -15,11 +15,6 @@
  */
 package com.gitblit.servlet;
 
-import org.eclipse.jgit.lib.Repository;
-
-import com.gitblit.Constants.AccessRestrictionType;
-import com.gitblit.models.RepositoryModel;
-import com.gitblit.models.UserModel;
 
 /**
  * The PagesFilter is an AccessRestrictionFilter which ensures the gh-pages
@@ -28,99 +23,7 @@
  * @author James Moger
  *
  */
-public class PagesFilter extends AccessRestrictionFilter {
+public class PagesFilter extends RawFilter {
 
-	/**
-	 * Extract the repository name from the url.
-	 *
-	 * @param url
-	 * @return repository name
-	 */
-	@Override
-	protected String extractRepositoryName(String url) {
-		// get the repository name from the url by finding a known url suffix
-		String repository = "";
-		Repository r = null;
-		int offset = 0;
-		while (r == null) {
-			int slash = url.indexOf('/', offset);
-			if (slash == -1) {
-				repository = url;
-			} else {
-				repository = url.substring(0, slash);
-			}
-			r = repositoryManager.getRepository(repository, false);
-			if (r == null) {
-				// try again
-				offset = slash + 1;
-			} else {
-				// close the repo
-				r.close();
-			}
-			if (repository.equals(url)) {
-				// either only repository in url or no repository found
-				break;
-			}
-		}
-		return repository;
-	}
 
-	/**
-	 * Analyze the url and returns the action of the request.
-	 *
-	 * @param cloneUrl
-	 * @return action of the request
-	 */
-	@Override
-	protected String getUrlRequestAction(String suffix) {
-		return "VIEW";
-	}
-
-	/**
-	 * Determine if a non-existing repository can be created using this filter.
-	 *
-	 * @return true if the filter allows repository creation
-	 */
-	@Override
-	protected boolean isCreationAllowed() {
-		return false;
-	}
-
-	/**
-	 * Determine if the action may be executed on the repository.
-	 *
-	 * @param repository
-	 * @param action
-	 * @return true if the action may be performed
-	 */
-	@Override
-	protected boolean isActionAllowed(RepositoryModel repository, String action) {
-		return true;
-	}
-
-	/**
-	 * Determine if the repository requires authentication.
-	 *
-	 * @param repository
-	 * @param action
-	 * @return true if authentication required
-	 */
-	@Override
-	protected boolean requiresAuthentication(RepositoryModel repository, String action) {
-		return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);
-	}
-
-	/**
-	 * Determine if the user can access the repository and perform the specified
-	 * action.
-	 *
-	 * @param repository
-	 * @param user
-	 * @param action
-	 * @return true if user may execute the action on the repository
-	 */
-	@Override
-	protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {
-		return user.canView(repository);
-	}
 }
diff --git a/src/main/java/com/gitblit/servlet/PagesServlet.java b/src/main/java/com/gitblit/servlet/PagesServlet.java
index 7e48f8e..f578f86 100644
--- a/src/main/java/com/gitblit/servlet/PagesServlet.java
+++ b/src/main/java/com/gitblit/servlet/PagesServlet.java
@@ -15,42 +15,10 @@
  */
 package com.gitblit.servlet;
 
-import java.io.IOException;
-import java.text.MessageFormat;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import org.eclipse.jgit.lib.FileMode;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevTree;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import com.gitblit.Constants;
-import com.gitblit.IStoredSettings;
-import com.gitblit.Keys;
-import com.gitblit.dagger.DaggerServlet;
-import com.gitblit.manager.IRepositoryManager;
-import com.gitblit.models.PathModel;
-import com.gitblit.models.RefModel;
-import com.gitblit.utils.ArrayUtils;
-import com.gitblit.utils.ByteFormat;
-import com.gitblit.utils.JGitUtils;
-import com.gitblit.utils.MarkdownUtils;
-import com.gitblit.utils.StringUtils;
-import com.gitblit.wicket.MarkupProcessor;
-import com.gitblit.wicket.MarkupProcessor.MarkupDocument;
-
-import dagger.ObjectGraph;
 
 /**
  * Serves the content of a gh-pages branch.
@@ -58,21 +26,10 @@
  * @author James Moger
  *
  */
-public class PagesServlet extends DaggerServlet {
+public class PagesServlet extends RawServlet {
 
 	private static final long serialVersionUID = 1L;
 
-	private transient Logger logger = LoggerFactory.getLogger(PagesServlet.class);
-
-	private IStoredSettings settings;
-
-	private IRepositoryManager repositoryManager;
-
-	@Override
-	protected void inject(ObjectGraph dagger) {
-		this.settings = dagger.get(IStoredSettings.class);
-		this.repositoryManager = dagger.get(IRepositoryManager.class);
-	}
 
 	/**
 	 * Returns an url to this servlet for the specified parameters.
@@ -89,248 +46,31 @@
 		return baseURL + Constants.PAGES + repository + "/" + (path == null ? "" : ("/" + path));
 	}
 
-	/**
-	 * Retrieves the specified resource from the gh-pages branch of the
-	 * repository.
-	 *
-	 * @param request
-	 * @param response
-	 * @throws javax.servlet.ServletException
-	 * @throws java.io.IOException
-	 */
-	private void processRequest(HttpServletRequest request, HttpServletResponse response)
-			throws ServletException, IOException {
-		String path = request.getPathInfo();
-		if (path.toLowerCase().endsWith(".git")) {
-			// forward to url with trailing /
-			// this is important for relative pages links
-			response.sendRedirect(request.getServletPath() + path + "/");
-			return;
-		}
-		if (path.charAt(0) == '/') {
-			// strip leading /
-			path = path.substring(1);
-		}
-
-		// determine repository and resource from url
-		String repository = "";
-		String resource = "";
-		Repository r = null;
-		int offset = 0;
-		while (r == null) {
-			int slash = path.indexOf('/', offset);
-			if (slash == -1) {
-				repository = path;
-			} else {
-				repository = path.substring(0, slash);
-			}
-			r = repositoryManager.getRepository(repository, false);
-			offset = slash + 1;
-			if (offset > 0) {
-				resource = path.substring(offset);
-			}
-			if (repository.equals(path)) {
-				// either only repository in url or no repository found
-				break;
-			}
-		}
-
-		ServletContext context = request.getSession().getServletContext();
-
-		try {
-			if (r == null) {
-				// repository not found!
-				String mkd = MessageFormat.format(
-						"# Error\nSorry, no valid **repository** specified in this url: {0}!",
-						repository);
-				error(response, mkd);
-				return;
-			}
-
-			// retrieve the content from the repository
-			RefModel pages = JGitUtils.getPagesBranch(r);
-			RevCommit commit = JGitUtils.getCommit(r, pages.getObjectId().getName());
-
-			if (commit == null) {
-				// branch not found!
-				String mkd = MessageFormat.format(
-						"# Error\nSorry, the repository {0} does not have a **gh-pages** branch!",
-						repository);
-				error(response, mkd);
-				return;
-			}
-
-			MarkupProcessor processor = new MarkupProcessor(settings);
-			String [] encodings = settings.getStrings(Keys.web.blobEncodings).toArray(new String[0]);
-
-			RevTree tree = commit.getTree();
-
-			String res = resource;
-			if (res.endsWith("/")) {
-				res = res.substring(0, res.length() - 1);
-			}
-
-			List<PathModel> pathEntries = JGitUtils.getFilesInPath(r, res, commit);
-
-			byte[] content = null;
-			if (pathEntries.isEmpty()) {
-				// not a path, a specific resource
-				try {
-					String contentType = context.getMimeType(res);
-					if (contentType == null) {
-						contentType = "text/plain";
-					}
-					if (contentType.startsWith("text")) {
-						content = JGitUtils.getStringContent(r, tree, res, encodings).getBytes(
-								Constants.ENCODING);
-					} else {
-						content = JGitUtils.getByteContent(r, tree, res, false);
-					}
-					response.setContentType(contentType);
-				} catch (Exception e) {
-				}
-			} else {
-				// path request
-				if (!request.getPathInfo().endsWith("/")) {
-					// redirect to trailing '/' url
-					response.sendRedirect(request.getServletPath() + request.getPathInfo() + "/");
-					return;
-				}
-
-				Map<String, String> names = new TreeMap<String, String>();
-				for (PathModel entry : pathEntries) {
-					names.put(entry.name.toLowerCase(), entry.name);
-				}
-
-				List<String> extensions = new ArrayList<String>();
-				extensions.add("html");
-				extensions.add("htm");
-				extensions.addAll(processor.getMarkupExtensions());
-				for (String ext : extensions) {
-					String key = "index." + ext;
-
-					if (names.containsKey(key)) {
-						String fileName = names.get(key);
-						String fullPath = fileName;
-						if (!res.isEmpty()) {
-							fullPath = res + "/" + fileName;
-						}
-						String stringContent = JGitUtils.getStringContent(r, tree, fullPath, encodings);
-						if (stringContent == null) {
-							continue;
-						}
-						content = stringContent.getBytes(Constants.ENCODING);
-						if (content != null) {
-							res = fullPath;
-							// assume text/html unless the servlet container
-							// overrides
-							response.setContentType("text/html; charset=" + Constants.ENCODING);
-							break;
-						}
-					}
-				}
-			}
-
-			// no content, document list or custom 404 page
-			if (ArrayUtils.isEmpty(content)) {
-				if (pathEntries.isEmpty()) {
-					// 404
-					String custom404 = JGitUtils.getStringContent(r, tree, "404.html", encodings);
-					if (!StringUtils.isEmpty(custom404)) {
-						content = custom404.getBytes(Constants.ENCODING);
-					}
-
-					// still no content
-					if (ArrayUtils.isEmpty(content)) {
-						String str = MessageFormat.format(
-								"# Error\nSorry, the requested resource **{0}** was not found.",
-								resource);
-						content = MarkdownUtils.transformMarkdown(str).getBytes(Constants.ENCODING);
-					}
-
-					try {
-						// output the content
-						logger.warn("Pages 404: " + resource);
-						response.setStatus(HttpServletResponse.SC_NOT_FOUND);
-						response.getOutputStream().write(content);
-						response.flushBuffer();
-					} catch (Throwable t) {
-						logger.error("Failed to write page to client", t);
-					}
-				} else {
-					// document list
-					response.setContentType("text/html");
-					response.getWriter().append("<style>table th, table td { min-width: 150px; text-align: left; }</style>");
-					response.getWriter().append("<table>");
-					response.getWriter().append("<thead><tr><th>path</th><th>mode</th><th>size</th></tr>");
-					response.getWriter().append("</thead>");
-					response.getWriter().append("<tbody>");
-					String pattern = "<tr><td><a href=\"{0}/{1}\">{1}</a></td><td>{2}</td><td>{3}</td></tr>";
-					final ByteFormat byteFormat = new ByteFormat();
-					if (!pathEntries.isEmpty()) {
-						if (pathEntries.get(0).path.indexOf('/') > -1) {
-							// we are in a subdirectory, add parent directory link
-							pathEntries.add(0, new PathModel("..", resource + "/..", 0, FileMode.TREE.getBits(), null, null));
-						}
-					}
-
-					String basePath = request.getServletPath() + request.getPathInfo();
-					if (basePath.charAt(basePath.length() - 1) == '/') {
-						// strip trailing slash
-						basePath = basePath.substring(0, basePath.length() - 1);
-					}
-					for (PathModel entry : pathEntries) {
-						response.getWriter().append(MessageFormat.format(pattern, basePath, entry.name,
-								JGitUtils.getPermissionsFromMode(entry.mode), byteFormat.format(entry.size)));
-					}
-					response.getWriter().append("</tbody>");
-					response.getWriter().append("</table>");
-				}
-				return;
-			}
-
-			// check to see if we should transform markup files
-			String ext = StringUtils.getFileExtension(resource);
-			if (processor.getMarkupExtensions().contains(ext)) {
-				String markup = new String(content, Constants.ENCODING);
-				MarkupDocument markupDoc = processor.parse(repository, commit.getName(), resource, markup);
-				content = markupDoc.html.getBytes("UTF-8");
-				response.setContentType("text/html; charset=" + Constants.ENCODING);
-			}
-
-			try {
-				// output the content
-				response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");
-				response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime());
-				response.getOutputStream().write(content);
-				response.flushBuffer();
-			} catch (Throwable t) {
-				logger.error("Failed to write page to client", t);
-			}
-
-		} catch (Throwable t) {
-			logger.error("Failed to write page to client", t);
-		} finally {
-			r.close();
-		}
-	}
-
-	private void error(HttpServletResponse response, String mkd) throws ServletException,
-			IOException, ParseException {
-		String content = MarkdownUtils.transformMarkdown(mkd);
-		response.setContentType("text/html; charset=" + Constants.ENCODING);
-		response.getWriter().write(content);
+	@Override
+	protected String getBranch(String repository, HttpServletRequest request) {
+		return "gh-pages";
 	}
 
 	@Override
-	protected void doPost(HttpServletRequest request, HttpServletResponse response)
-			throws ServletException, IOException {
-		processRequest(request, response);
+	protected String getPath(String repository, String branch, HttpServletRequest request) {
+		String pi = request.getPathInfo().substring(1);
+		if (pi.equals(repository)) {
+			return "";
+		}
+		String path = pi.substring(pi.indexOf(repository) + repository.length() + 1);
+		if (path.endsWith("/")) {
+			path = path.substring(0, path.length() - 1);
+		}
+		return path;
 	}
 
 	@Override
-	protected void doGet(HttpServletRequest request, HttpServletResponse response)
-			throws ServletException, IOException {
-		processRequest(request, response);
+	protected boolean renderIndex() {
+		return true;
+	}
+
+	@Override
+	protected void setContentType(HttpServletResponse response, String contentType) {
+		response.setContentType(contentType);;
 	}
 }
diff --git a/src/main/java/com/gitblit/servlet/RawFilter.java b/src/main/java/com/gitblit/servlet/RawFilter.java
new file mode 100644
index 0000000..34989c9
--- /dev/null
+++ b/src/main/java/com/gitblit/servlet/RawFilter.java
@@ -0,0 +1,126 @@
+/*
+ * 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.servlet;
+
+import org.eclipse.jgit.lib.Repository;
+
+import com.gitblit.Constants.AccessRestrictionType;
+import com.gitblit.models.RepositoryModel;
+import com.gitblit.models.UserModel;
+
+/**
+ * The RawFilter is an AccessRestrictionFilter which ensures http branch
+ * requests for a view-restricted repository are authenticated and authorized.
+ *
+ * @author James Moger
+ *
+ */
+public class RawFilter extends AccessRestrictionFilter {
+
+	/**
+	 * Extract the repository name from the url.
+	 *
+	 * @param url
+	 * @return repository name
+	 */
+	@Override
+	protected String extractRepositoryName(String url) {
+		// get the repository name from the url by finding a known url suffix
+		String repository = "";
+		Repository r = null;
+		int offset = 0;
+		while (r == null) {
+			int slash = url.indexOf('/', offset);
+			if (slash == -1) {
+				repository = url;
+			} else {
+				repository = url.substring(0, slash);
+			}
+			r = repositoryManager.getRepository(repository, false);
+			if (r == null) {
+				// try again
+				offset = slash + 1;
+			} else {
+				// close the repo
+				r.close();
+			}
+			if (repository.equals(url)) {
+				// either only repository in url or no repository found
+				break;
+			}
+		}
+		return repository;
+	}
+
+	/**
+	 * Analyze the url and returns the action of the request.
+	 *
+	 * @param cloneUrl
+	 * @return action of the request
+	 */
+	@Override
+	protected String getUrlRequestAction(String suffix) {
+		return "VIEW";
+	}
+
+	/**
+	 * Determine if a non-existing repository can be created using this filter.
+	 *
+	 * @return true if the filter allows repository creation
+	 */
+	@Override
+	protected boolean isCreationAllowed() {
+		return false;
+	}
+
+	/**
+	 * Determine if the action may be executed on the repository.
+	 *
+	 * @param repository
+	 * @param action
+	 * @return true if the action may be performed
+	 */
+	@Override
+	protected boolean isActionAllowed(RepositoryModel repository, String action) {
+		return true;
+	}
+
+	/**
+	 * Determine if the repository requires authentication.
+	 *
+	 * @param repository
+	 * @param action
+	 * @return true if authentication required
+	 */
+	@Override
+	protected boolean requiresAuthentication(RepositoryModel repository, String action) {
+		return repository.accessRestriction.atLeast(AccessRestrictionType.VIEW);
+	}
+
+	/**
+	 * Determine if the user can access the repository and perform the specified
+	 * action.
+	 *
+	 * @param repository
+	 * @param user
+	 * @param action
+	 * @return true if user may execute the action on the repository
+	 */
+	@Override
+	protected boolean canAccess(RepositoryModel repository, UserModel user, String action) {
+		return user.canView(repository);
+	}
+}
diff --git a/src/main/java/com/gitblit/servlet/RawServlet.java b/src/main/java/com/gitblit/servlet/RawServlet.java
new file mode 100644
index 0000000..cde7b2e
--- /dev/null
+++ b/src/main/java/com/gitblit/servlet/RawServlet.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright 2014 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.servlet;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.tika.Tika;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.MutableObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.Repository;
+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;
+
+import com.gitblit.Constants;
+import com.gitblit.Keys;
+import com.gitblit.dagger.DaggerServlet;
+import com.gitblit.manager.IRepositoryManager;
+import com.gitblit.manager.IRuntimeManager;
+import com.gitblit.models.PathModel;
+import com.gitblit.utils.ByteFormat;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.MarkdownUtils;
+import com.gitblit.utils.StringUtils;
+
+import dagger.ObjectGraph;
+
+/**
+ * Serves the content of a branch.
+ *
+ * @author James Moger
+ *
+ */
+public class RawServlet extends DaggerServlet {
+
+	private static final long serialVersionUID = 1L;
+
+	private transient Logger logger = LoggerFactory.getLogger(RawServlet.class);
+
+	private IRuntimeManager runtimeManager;
+
+	private IRepositoryManager repositoryManager;
+
+	@Override
+	protected void inject(ObjectGraph dagger) {
+		this.runtimeManager = dagger.get(IRuntimeManager.class);
+		this.repositoryManager = dagger.get(IRepositoryManager.class);
+	}
+
+	/**
+	 * Returns an url to this servlet for the specified parameters.
+	 *
+	 * @param baseURL
+	 * @param repository
+	 * @param branch
+	 * @param path
+	 * @return an url
+	 */
+	public static String asLink(String baseURL, String repository, String branch, String path) {
+		if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
+			baseURL = baseURL.substring(0, baseURL.length() - 1);
+		}
+		String encodedPath = path.replace(' ', '-');
+		try {
+			encodedPath = URLEncoder.encode(encodedPath, "UTF-8");
+		} catch (UnsupportedEncodingException e) {
+		}
+		return baseURL + Constants.RAW_PATH + repository + "/" + (branch == null ? "" : (branch + "/" + (path == null ? "" : encodedPath)));
+	}
+
+	protected String getBranch(String repository, HttpServletRequest request) {
+		String pi = request.getPathInfo();
+		String branch = pi.substring(pi.indexOf(repository) + repository.length() + 1);
+		int fs = branch.indexOf('/');
+		if (fs > -1) {
+			branch = branch.substring(0, fs);
+		}
+		return branch;
+	}
+
+	protected String getPath(String repository, String branch, HttpServletRequest request) {
+		String base = repository + "/" + branch;
+		String pi = request.getPathInfo().substring(1);
+		if (pi.equals(base)) {
+			return "";
+		}
+		String path = pi.substring(pi.indexOf(base) + base.length() + 1);
+		if (path.endsWith("/")) {
+			path = path.substring(0, path.length() - 1);
+		}
+		return path;
+	}
+
+	protected boolean renderIndex() {
+		return false;
+	}
+
+	/**
+	 * Retrieves the specified resource from the specified branch of the
+	 * repository.
+	 *
+	 * @param request
+	 * @param response
+	 * @throws javax.servlet.ServletException
+	 * @throws java.io.IOException
+	 */
+	private void processRequest(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		String path = request.getPathInfo();
+		if (path.toLowerCase().endsWith(".git")) {
+			// forward to url with trailing /
+			// this is important for relative pages links
+			response.sendRedirect(request.getServletPath() + path + "/");
+			return;
+		}
+		if (path.charAt(0) == '/') {
+			// strip leading /
+			path = path.substring(1);
+		}
+
+		// determine repository and resource from url
+		String repository = "";
+		Repository r = null;
+		int offset = 0;
+		while (r == null) {
+			int slash = path.indexOf('/', offset);
+			if (slash == -1) {
+				repository = path;
+			} else {
+				repository = path.substring(0, slash);
+			}
+			offset += slash;
+			r = repositoryManager.getRepository(repository, false);
+			if (repository.equals(path)) {
+				// either only repository in url or no repository found
+				break;
+			}
+		}
+
+		ServletContext context = request.getSession().getServletContext();
+
+		try {
+			if (r == null) {
+				// repository not found!
+				String mkd = MessageFormat.format(
+						"# Error\nSorry, no valid **repository** specified in this url: {0}!",
+						path);
+				error(response, mkd);
+				return;
+			}
+
+			// identify the branch
+			String branch = getBranch(repository, request);
+			if (StringUtils.isEmpty(branch)) {
+				branch = r.getBranch();
+				if (branch == null) {
+					// no branches found!  empty?
+					String mkd = MessageFormat.format(
+							"# Error\nSorry, no valid **branch** specified in this url: {0}!",
+							path);
+					error(response, mkd);
+				} else {
+					// redirect to default branch
+					String base = request.getRequestURI();
+					String url = base + branch + "/";
+					response.sendRedirect(url);
+				}
+				return;
+			}
+
+			// identify the requested path
+			String requestedPath = getPath(repository, branch, request);
+
+			// identify the commit
+			RevCommit commit = JGitUtils.getCommit(r, branch);
+			if (commit == null) {
+				// branch not found!
+				String mkd = MessageFormat.format(
+						"# Error\nSorry, the repository {0} does not have a **{1}** branch!",
+						repository, branch);
+				error(response, mkd);
+				return;
+			}
+
+
+			List<PathModel> pathEntries = JGitUtils.getFilesInPath(r, requestedPath, commit);
+			if (pathEntries.isEmpty()) {
+				// requested a specific resource
+				String file = StringUtils.getLastPathElement(requestedPath);
+				try {
+					// query Tika for the content type
+					Tika tika = new Tika();
+					String contentType = tika.detect(file);
+
+					if (contentType == null) {
+						// ask the container for the content type
+						contentType = context.getMimeType(requestedPath);
+
+						if (contentType == null) {
+							// still unknown content type, assume binary
+							contentType = "application/octet-stream";
+						}
+					}
+
+					setContentType(response, contentType);
+
+					if (isTextType(contentType)) {
+
+						// load, interpret, and serve text content as UTF-8
+						String [] encodings = runtimeManager.getSettings().getStrings(Keys.web.blobEncodings).toArray(new String[0]);
+						String content = JGitUtils.getStringContent(r, commit.getTree(), requestedPath, encodings);
+
+						byte [] bytes = content.getBytes(Constants.ENCODING);
+						response.setContentLength(bytes.length);
+						ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+						sendContent(response, JGitUtils.getCommitDate(commit), is);
+
+					} else {
+						// serve binary content
+						String filename = StringUtils.getLastPathElement(requestedPath);
+						try {
+					    	String userAgent = request.getHeader("User-Agent");
+							if (userAgent != null && userAgent.indexOf("MSIE 5.5") > -1) {
+							      response.setHeader("Content-Disposition", "filename=\""
+							    		  +  URLEncoder.encode(filename, Constants.ENCODING) + "\"");
+							} else if (userAgent != null && userAgent.indexOf("MSIE") > -1) {
+							      response.setHeader("Content-Disposition", "attachment; filename=\""
+							    		  +  URLEncoder.encode(filename, Constants.ENCODING) + "\"");
+							} else {
+									response.setHeader("Content-Disposition", "attachment; filename=\""
+									      + new String(filename.getBytes(Constants.ENCODING), "latin1") + "\"");
+							}
+						}
+						catch (UnsupportedEncodingException e) {
+							response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
+						}
+
+						// stream binary content directly from the repository
+						streamFromRepo(response, r, commit, requestedPath);
+					}
+					return;
+				} catch (Exception e) {
+					logger.error(null, e);
+				}
+			} else {
+				// path request
+				if (!request.getPathInfo().endsWith("/")) {
+					// redirect to trailing '/' url
+					response.sendRedirect(request.getServletPath() + request.getPathInfo() + "/");
+					return;
+				}
+
+				if (renderIndex()) {
+					// locate and render an index file
+					Map<String, String> names = new TreeMap<String, String>();
+					for (PathModel entry : pathEntries) {
+						names.put(entry.name.toLowerCase(), entry.name);
+					}
+
+					List<String> extensions = new ArrayList<String>();
+					extensions.add("html");
+					extensions.add("htm");
+
+					String content = null;
+					for (String ext : extensions) {
+						String key = "index." + ext;
+
+						if (names.containsKey(key)) {
+							String fileName = names.get(key);
+							String fullPath = fileName;
+							if (!requestedPath.isEmpty()) {
+								fullPath = requestedPath + "/" + fileName;
+							}
+
+							String [] encodings = runtimeManager.getSettings().getStrings(Keys.web.blobEncodings).toArray(new String[0]);
+							String stringContent = JGitUtils.getStringContent(r, commit.getTree(), fullPath, encodings);
+							if (stringContent == null) {
+								continue;
+							}
+							content = stringContent;
+							requestedPath = fullPath;
+							break;
+						}
+					}
+
+					response.setContentType("text/html; charset=" + Constants.ENCODING);
+					byte [] bytes = content.getBytes(Constants.ENCODING);
+					response.setContentLength(bytes.length);
+
+					ByteArrayInputStream is = new ByteArrayInputStream(bytes);
+					sendContent(response, JGitUtils.getCommitDate(commit), is);
+					return;
+				}
+			}
+
+			// no content, document list or 404 page
+			if (pathEntries.isEmpty()) {
+				// default 404 page
+				String str = MessageFormat.format(
+						"# Error\nSorry, the requested resource **{0}** was not found.",
+						requestedPath);
+				response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+				error(response, str);
+				return;
+			} else {
+				//
+				// directory list
+				//
+				response.setContentType("text/html");
+				response.getWriter().append("<style>table th, table td { min-width: 150px; text-align: left; }</style>");
+				response.getWriter().append("<table>");
+				response.getWriter().append("<thead><tr><th>path</th><th>mode</th><th>size</th></tr>");
+				response.getWriter().append("</thead>");
+				response.getWriter().append("<tbody>");
+				String pattern = "<tr><td><a href=\"{0}/{1}\">{1}</a></td><td>{2}</td><td>{3}</td></tr>";
+				final ByteFormat byteFormat = new ByteFormat();
+				if (!pathEntries.isEmpty()) {
+					if (pathEntries.get(0).path.indexOf('/') > -1) {
+						// we are in a subdirectory, add parent directory link
+						String pp = URLEncoder.encode(requestedPath, Constants.ENCODING);
+						pathEntries.add(0, new PathModel("..", pp + "/..", 0, FileMode.TREE.getBits(), null, null));
+					}
+				}
+
+				String basePath = request.getServletPath() + request.getPathInfo();
+				if (basePath.charAt(basePath.length() - 1) == '/') {
+					// strip trailing slash
+					basePath = basePath.substring(0, basePath.length() - 1);
+				}
+				for (PathModel entry : pathEntries) {
+					String pp = URLEncoder.encode(entry.name, Constants.ENCODING);
+					response.getWriter().append(MessageFormat.format(pattern, basePath, pp,
+							JGitUtils.getPermissionsFromMode(entry.mode),
+							entry.isFile() ? byteFormat.format(entry.size) : ""));
+				}
+				response.getWriter().append("</tbody>");
+				response.getWriter().append("</table>");
+			}
+		} catch (Throwable t) {
+			logger.error("Failed to write page to client", t);
+		} finally {
+			r.close();
+		}
+	}
+
+	protected boolean isTextType(String contentType) {
+		if (contentType.startsWith("text/")
+				|| "application/json".equals(contentType)
+				|| "application/xml".equals(contentType)) {
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Override all text types to be plain text.
+	 *
+	 * @param response
+	 * @param contentType
+	 */
+	protected void setContentType(HttpServletResponse response, String contentType) {
+		if (isTextType(contentType)) {
+			response.setContentType("text/plain");
+		} else {
+			response.setContentType(contentType);
+		}
+	}
+
+	private void streamFromRepo(HttpServletResponse response, Repository repository,
+			RevCommit commit, String requestedPath) throws IOException {
+
+		response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime());
+		response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");
+
+		RevWalk rw = new RevWalk(repository);
+		TreeWalk tw = new TreeWalk(repository);
+		try {
+			tw.reset();
+			tw.addTree(commit.getTree());
+			PathFilter f = PathFilter.create(requestedPath);
+			tw.setFilter(f);
+			tw.setRecursive(true);
+			MutableObjectId id = new MutableObjectId();
+			ObjectReader reader = tw.getObjectReader();
+			while (tw.next()) {
+				FileMode mode = tw.getFileMode(0);
+				if (mode == FileMode.GITLINK || mode == FileMode.TREE) {
+					continue;
+				}
+				tw.getObjectId(id, 0);
+
+				long len = reader.getObjectSize(id, org.eclipse.jgit.lib.Constants.OBJ_BLOB);
+				response.setIntHeader("Content-Length", (int) len);
+				ObjectLoader ldr = repository.open(id);
+				ldr.copyTo(response.getOutputStream());
+			}
+		} finally {
+			tw.release();
+			rw.dispose();
+		}
+
+		response.flushBuffer();
+	}
+
+	private void sendContent(HttpServletResponse response, Date date, InputStream is) throws ServletException, IOException {
+		response.setDateHeader("Last-Modified", date.getTime());
+		response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");
+		try {
+			byte[] tmp = new byte[8192];
+			int len = 0;
+			while ((len = is.read(tmp)) > -1) {
+				response.getOutputStream().write(tmp, 0, len);
+			}
+		} finally {
+			is.close();
+		}
+		response.flushBuffer();
+	}
+
+	private void error(HttpServletResponse response, String mkd) throws ServletException,
+			IOException, ParseException {
+		String content = MarkdownUtils.transformMarkdown(mkd);
+		response.setContentType("text/html; charset=" + Constants.ENCODING);
+		response.getWriter().write(content);
+	}
+
+	@Override
+	protected void doPost(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		processRequest(request, response);
+	}
+
+	@Override
+	protected void doGet(HttpServletRequest request, HttpServletResponse response)
+			throws ServletException, IOException {
+		processRequest(request, response);
+	}
+}
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
index 9f002d2..dc79af2 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
@@ -64,13 +64,13 @@
 import com.gitblit.wicket.pages.LuceneSearchPage;
 import com.gitblit.wicket.pages.MetricsPage;
 import com.gitblit.wicket.pages.MyDashboardPage;
+import com.gitblit.wicket.pages.MyTicketsPage;
 import com.gitblit.wicket.pages.NewMilestonePage;
 import com.gitblit.wicket.pages.NewTicketPage;
 import com.gitblit.wicket.pages.OverviewPage;
 import com.gitblit.wicket.pages.PatchPage;
 import com.gitblit.wicket.pages.ProjectPage;
 import com.gitblit.wicket.pages.ProjectsPage;
-import com.gitblit.wicket.pages.RawPage;
 import com.gitblit.wicket.pages.ReflogPage;
 import com.gitblit.wicket.pages.RepositoriesPage;
 import com.gitblit.wicket.pages.ReviewProposalPage;
@@ -81,7 +81,6 @@
 import com.gitblit.wicket.pages.TreePage;
 import com.gitblit.wicket.pages.UserPage;
 import com.gitblit.wicket.pages.UsersPage;
-import com.gitblit.wicket.pages.MyTicketsPage;
 
 public class GitBlitWebApp extends WebApplication {
 
@@ -173,7 +172,6 @@
 		mount("/tag", TagPage.class, "r", "h");
 		mount("/tree", TreePage.class, "r", "h", "f");
 		mount("/blob", BlobPage.class, "r", "h", "f");
-		mount("/raw", RawPage.class, "r", "h", "f");
 		mount("/blobdiff", BlobDiffPage.class, "r", "h", "f");
 		mount("/commitdiff", CommitDiffPage.class, "r", "h");
 		mount("/compare", ComparePage.class, "r", "h");
diff --git a/src/main/java/com/gitblit/wicket/MarkupProcessor.java b/src/main/java/com/gitblit/wicket/MarkupProcessor.java
index ff6fbce..e7681f2 100644
--- a/src/main/java/com/gitblit/wicket/MarkupProcessor.java
+++ b/src/main/java/com/gitblit/wicket/MarkupProcessor.java
@@ -56,11 +56,11 @@
 import com.gitblit.IStoredSettings;
 import com.gitblit.Keys;
 import com.gitblit.models.PathModel;
+import com.gitblit.servlet.RawServlet;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.MarkdownUtils;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.pages.DocPage;
-import com.gitblit.wicket.pages.RawPage;
 import com.google.common.base.Joiner;
 
 /**
@@ -260,7 +260,8 @@
 				if (imagePath.indexOf("://") == -1) {
 					// relative image
 					String path = doc.getRelativePath(imagePath);
-					url = getWicketUrl(RawPage.class, repositoryName, commitId, path);
+					String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+					url = RawServlet.asLink(contextUrl, repositoryName, commitId, path);
 				} else {
 					// absolute image
 					url = imagePath;
@@ -312,7 +313,8 @@
 				if (node.url.indexOf("://") == -1) {
 					// repository-relative image link
 					String path = doc.getRelativePath(node.url);
-					String url = getWicketUrl(RawPage.class, repositoryName, commitId, path);
+					String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+					String url = RawServlet.asLink(contextUrl, repositoryName, commitId, path);
 					return new Rendering(url, text);
 				}
 				// absolute image link
@@ -325,7 +327,8 @@
 				if (url.indexOf("://") == -1) {
 					// repository-relative image link
 					String path = doc.getRelativePath(url);
-					String wurl = getWicketUrl(RawPage.class, repositoryName, commitId, path);
+					String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+					String wurl = RawServlet.asLink(contextUrl, repositoryName, commitId, path);
 					rendering = new Rendering(wurl, alt);
 				} else {
 					// absolute image link
diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.java b/src/main/java/com/gitblit/wicket/pages/BasePage.java
index 7d3d3a2..4971039 100644
--- a/src/main/java/com/gitblit/wicket/pages/BasePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BasePage.java
@@ -98,6 +98,10 @@
 		}
 	}
 
+	protected String getContextUrl() {
+		return getRequest().getRelativePathPrefixToContextRoot();
+	}
+
 	protected String getCanonicalUrl() {
 		return getCanonicalUrl(getClass(), getPageParameters());
 	}
diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.java b/src/main/java/com/gitblit/wicket/pages/BlobPage.java
index 299d8db..f3d0bc9 100644
--- a/src/main/java/com/gitblit/wicket/pages/BlobPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.java
@@ -24,10 +24,12 @@
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.image.Image;
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.ExternalLink;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 
 import com.gitblit.Keys;
+import com.gitblit.servlet.RawServlet;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.CacheControl;
@@ -57,8 +59,8 @@
 					WicketUtils.newPathParameter(repositoryName, objectId, blobPath))
 					.setEnabled(false));
 			add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class).setEnabled(false));
-			add(new BookmarkablePageLink<Void>("rawLink", RawPage.class,
-					WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+			String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, objectId, blobPath);
+			add(new ExternalLink("rawLink",  rawUrl));
 			add(new CommitHeaderPanel("commitHeader", objectId));
 			add(new PathBreadcrumbsPanel("breadcrumbs", repositoryName, blobPath, objectId));
 			Component c = new Label("blobText", JGitUtils.getStringContent(r, objectId, encodings));
@@ -87,8 +89,8 @@
 					WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
 			add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
 					WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
-			add(new BookmarkablePageLink<Void>("rawLink", RawPage.class,
-					WicketUtils.newPathParameter(repositoryName, objectId, blobPath)));
+			String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, objectId, blobPath);
+			add(new ExternalLink("rawLink", rawUrl));
 
 			add(new CommitHeaderPanel("commitHeader", repositoryName, commit));
 
@@ -115,7 +117,7 @@
 				case 2:
 					// image blobs
 					add(new Label("blobText").setVisible(false));
-					add(new ExternalImage("blobImage", urlFor(RawPage.class, WicketUtils.newPathParameter(repositoryName, objectId, blobPath)).toString()));
+					add(new ExternalImage("blobImage", rawUrl));
 					break;
 				case 3:
 					// binary blobs
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
index 7f2a8a6..71a5ea6 100644
--- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -34,6 +34,7 @@
 import com.gitblit.models.GitNote;
 import com.gitblit.models.PathModel.PathChangeModel;
 import com.gitblit.models.SubmoduleModel;
+import com.gitblit.servlet.RawServlet;
 import com.gitblit.utils.DiffUtils;
 import com.gitblit.utils.DiffUtils.DiffOutput;
 import com.gitblit.utils.DiffUtils.DiffOutputType;
@@ -170,8 +171,8 @@
 					item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
 							.newPathParameter(repositoryName, entry.commitId, entry.path))
 							.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
-					item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
-							.newPathParameter(repositoryName, entry.commitId, entry.path))
+					String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path);
+					item.add(new ExternalLink("raw", rawUrl)
 							.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
 					item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
 							.newPathParameter(repositoryName, entry.commitId, entry.path))
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitPage.java b/src/main/java/com/gitblit/wicket/pages/CommitPage.java
index 8bc9848..6fadec5 100644
--- a/src/main/java/com/gitblit/wicket/pages/CommitPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/CommitPage.java
@@ -35,6 +35,7 @@
 import com.gitblit.models.GitNote;
 import com.gitblit.models.PathModel.PathChangeModel;
 import com.gitblit.models.SubmoduleModel;
+import com.gitblit.servlet.RawServlet;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.wicket.CacheControl;
 import com.gitblit.wicket.CacheControl.LastModified;
@@ -222,8 +223,8 @@
 					item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
 							.newPathParameter(repositoryName, entry.commitId, entry.path))
 							.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
-					item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
-							.newPathParameter(repositoryName, entry.commitId, entry.path))
+					String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, entry.commitId, entry.path);
+					item.add(new ExternalLink("raw", rawUrl)
 							.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
 					item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
 							.newPathParameter(repositoryName, entry.commitId, entry.path))
diff --git a/src/main/java/com/gitblit/wicket/pages/ComparePage.java b/src/main/java/com/gitblit/wicket/pages/ComparePage.java
index 2024bf1..1ec6613 100644
--- a/src/main/java/com/gitblit/wicket/pages/ComparePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ComparePage.java
@@ -41,6 +41,7 @@
 import com.gitblit.models.RefModel;
 import com.gitblit.models.RepositoryModel;
 import com.gitblit.models.SubmoduleModel;
+import com.gitblit.servlet.RawServlet;
 import com.gitblit.utils.DiffUtils;
 import com.gitblit.utils.DiffUtils.DiffOutput;
 import com.gitblit.utils.DiffUtils.DiffOutputType;
@@ -184,8 +185,8 @@
 						item.add(new BookmarkablePageLink<Void>("view", BlobPage.class, WicketUtils
 								.newPathParameter(repositoryName, endId, entry.path))
 								.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
-						item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
-								.newPathParameter(repositoryName, endId, entry.path))
+						String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, endId, entry.path);
+						item.add(new ExternalLink("raw", rawUrl)
 								.setEnabled(!entry.changeType.equals(ChangeType.DELETE)));
 						item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
 								.newPathParameter(repositoryName, endId, entry.path))
diff --git a/src/main/java/com/gitblit/wicket/pages/DocPage.java b/src/main/java/com/gitblit/wicket/pages/DocPage.java
index bf99978..c06d806 100644
--- a/src/main/java/com/gitblit/wicket/pages/DocPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/DocPage.java
@@ -20,10 +20,12 @@
 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.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 
+import com.gitblit.servlet.RawServlet;
 import com.gitblit.utils.BugtraqProcessor;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.StringUtils;
@@ -87,8 +89,8 @@
 				WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
 		fragment.add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
 				WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
-		fragment.add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(
-				repositoryName, objectId, documentPath)));
+		String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, objectId, documentPath);
+		fragment.add(new ExternalLink("rawLink", rawUrl));
 
 		fragment.add(new Label("content", markupDoc.html).setEscapeModelStrings(false));
 		add(fragment);
diff --git a/src/main/java/com/gitblit/wicket/pages/DocsPage.java b/src/main/java/com/gitblit/wicket/pages/DocsPage.java
index 907dd6e..fc56ee0 100644
--- a/src/main/java/com/gitblit/wicket/pages/DocsPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/DocsPage.java
@@ -31,6 +31,7 @@
 import org.eclipse.jgit.revwalk.RevCommit;
 
 import com.gitblit.models.PathModel;
+import com.gitblit.servlet.RawServlet;
 import com.gitblit.utils.ByteFormat;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.StringUtils;
@@ -103,8 +104,8 @@
 							WicketUtils.newPathParameter(repositoryName, commitId, doc.documentPath)));
 					item.add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
 							WicketUtils.newPathParameter(repositoryName, commitId, doc.documentPath)));
-					item.add(new BookmarkablePageLink<Void>("rawLink", RawPage.class, WicketUtils.newPathParameter(
-							repositoryName, commitId, doc.documentPath)));
+					String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, commitId, doc.documentPath);
+					item.add(new ExternalLink("rawLink", rawUrl));
 
 					// document content
 					String file = StringUtils.getLastPathElement(doc.documentPath);
@@ -145,8 +146,8 @@
 				// links
 				item.add(new BookmarkablePageLink<Void>("view", DocPage.class, WicketUtils
 						.newPathParameter(repositoryName, id, entry.path)));
-				item.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
-						.newPathParameter(repositoryName, id, entry.path)));
+				String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, id, entry.path);
+				item.add(new ExternalLink("raw", rawUrl));
 				item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
 						.newPathParameter(repositoryName, id, entry.path)));
 				item.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils
diff --git a/src/main/java/com/gitblit/wicket/pages/TreePage.java b/src/main/java/com/gitblit/wicket/pages/TreePage.java
index d6bf1fe..722b824 100644
--- a/src/main/java/com/gitblit/wicket/pages/TreePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/TreePage.java
@@ -20,6 +20,7 @@
 import org.apache.wicket.PageParameters;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.ExternalLink;
 import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.markup.repeater.Item;
 import org.apache.wicket.markup.repeater.data.DataView;
@@ -30,6 +31,7 @@
 
 import com.gitblit.models.PathModel;
 import com.gitblit.models.SubmoduleModel;
+import com.gitblit.servlet.RawServlet;
 import com.gitblit.utils.ByteFormat;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.wicket.CacheControl;
@@ -162,8 +164,8 @@
 						links.add(new BookmarkablePageLink<Void>("view", BlobPage.class,
 								WicketUtils.newPathParameter(repositoryName, id,
 										path)));
-						links.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
-								.newPathParameter(repositoryName, id, path)));
+						String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, id, path);
+						links.add(new ExternalLink("raw", rawUrl));
 						links.add(new BookmarkablePageLink<Void>("blame", BlamePage.class,
 								WicketUtils.newPathParameter(repositoryName, id,
 										path)));
diff --git a/src/main/java/com/gitblit/wicket/panels/TagsPanel.java b/src/main/java/com/gitblit/wicket/panels/TagsPanel.java
index 9f3987b..f1f8273 100644
--- a/src/main/java/com/gitblit/wicket/panels/TagsPanel.java
+++ b/src/main/java/com/gitblit/wicket/panels/TagsPanel.java
@@ -17,9 +17,11 @@
 
 import java.util.List;
 
+import org.apache.wicket.RequestCycle;
 import org.apache.wicket.markup.html.WebPage;
 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,13 +31,13 @@
 import org.eclipse.jgit.lib.Repository;
 
 import com.gitblit.models.RefModel;
+import com.gitblit.servlet.RawServlet;
 import com.gitblit.utils.JGitUtils;
 import com.gitblit.utils.StringUtils;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.pages.BlobPage;
 import com.gitblit.wicket.pages.CommitPage;
 import com.gitblit.wicket.pages.LogPage;
-import com.gitblit.wicket.pages.RawPage;
 import com.gitblit.wicket.pages.TagPage;
 import com.gitblit.wicket.pages.TagsPage;
 import com.gitblit.wicket.pages.TreePage;
@@ -113,9 +115,10 @@
 							.newObjectParameter(repositoryName, entry.getReferencedObjectId()
 									.getName())));
 
-					fragment.add(new BookmarkablePageLink<Void>("raw", RawPage.class, WicketUtils
-							.newObjectParameter(repositoryName, entry.getReferencedObjectId()
-									.getName())));
+					String contextUrl = RequestCycle.get().getRequest().getRelativePathPrefixToContextRoot();
+					String rawUrl = RawServlet.asLink(contextUrl, repositoryName, entry.displayName,
+							entry.getReferencedObjectId().getName());
+					fragment.add(new ExternalLink("raw", rawUrl));
 					item.add(fragment);
 				} else {
 					// TODO Tree Tag Object

--
Gitblit v1.9.1