Implemented optional page caching
diff --git a/releases.moxie b/releases.moxie
index 258435d..49149a8 100644
--- a/releases.moxie
+++ b/releases.moxie
@@ -20,14 +20,18 @@
     changes:
 	- updated Chinese translation
 	- updated Dutch translation
-    additions: ~
+    additions:
+	- Added optional browser-side page caching using Last-Modified and Cache-Control for the dashboard, activity, project, and several repository pages
     dependencyChanges: ~
+	settings:
+	- { name: 'web.pageCacheExpires', defaultValue: 0 }
     contributors:
 	- Rainer Alföldi 
 	- Liyu Wang
 	- Jeroen Baten
 	- James Moger
 	- Stardrad Yin
+	- Chad Horohoe
 }
 
 #
diff --git a/src/main/distrib/data/gitblit.properties b/src/main/distrib/data/gitblit.properties
index f6ed383..1ee6d80 100644
--- a/src/main/distrib/data/gitblit.properties
+++ b/src/main/distrib/data/gitblit.properties
@@ -616,6 +616,13 @@
 # BASEFOLDER

 web.robots.txt = ${baseFolder}/robots.txt

 

+# The number of minutes to cache a page in the browser since the last request.

+# The default value is 0 minutes.  A value <= 0 disables all page caching which

+# is the default behavior for Gitblit <= 1.3.0.

+#

+# SINCE 1.3.1

+web.pageCacheExpires = 0

+

 # If true, the web ui layout will respond and adapt to the browser's dimensions.

 # if false, the web ui will use a 940px fixed-width layout.

 # http://twitter.github.com/bootstrap/scaffolding.html#responsive

diff --git a/src/main/java/WEB-INF/web.xml b/src/main/java/WEB-INF/web.xml
index bf12166..cf71465 100644
--- a/src/main/java/WEB-INF/web.xml
+++ b/src/main/java/WEB-INF/web.xml
@@ -256,7 +256,7 @@
     <filter>

         <filter-name>wicketFilter</filter-name>

         <filter-class>

-            org.apache.wicket.protocol.http.WicketFilter

+            com.gitblit.wicket.GitblitWicketFilter

         </filter-class>

         <init-param>

             <param-name>applicationClassName</param-name>

diff --git a/src/main/java/com/gitblit/GitBlit.java b/src/main/java/com/gitblit/GitBlit.java
index 35ef770..efdaad1 100644
--- a/src/main/java/com/gitblit/GitBlit.java
+++ b/src/main/java/com/gitblit/GitBlit.java
@@ -232,6 +232,33 @@
 		}
 		return gitblit;
 	}
+	
+	/**
+	 * Returns the boot date of the Gitblit server.
+	 * 
+	 * @return the boot date of Gitblit
+	 */
+	public static Date getBootDate() {
+		return self().serverStatus.bootDate;
+	}
+	
+	/**
+	 * Returns the most recent change date of any repository served by Gitblit.
+	 * 
+	 * @return a date
+	 */
+	public static Date getLastActivityDate() {
+		Date date = null;
+		for (String name : self().getRepositoryList()) {
+			Repository r = self().getRepository(name);
+			Date lastChange = JGitUtils.getLastChange(r).when;
+			r.close();
+			if (lastChange != null && (date == null || lastChange.after(date))) {
+				date = lastChange;
+			}
+		}
+		return date;
+	}
 
 	/**
 	 * Determine if this is the GO variant of Gitblit.
diff --git a/src/main/java/com/gitblit/LogoServlet.java b/src/main/java/com/gitblit/LogoServlet.java
index c8820ed..823e464 100644
--- a/src/main/java/com/gitblit/LogoServlet.java
+++ b/src/main/java/com/gitblit/LogoServlet.java
@@ -76,6 +76,7 @@
 				contentType = "image/png";

 			}

 			response.setContentType(contentType);

+			response.setHeader("Cache-Control", "public, max-age=3600, must-revalidate");

 			OutputStream os = response.getOutputStream();

 			byte[] buf = new byte[4096];

 			int bytesRead = is.read(buf);

diff --git a/src/main/java/com/gitblit/PagesServlet.java b/src/main/java/com/gitblit/PagesServlet.java
index 9516b35..fc71bc5 100644
--- a/src/main/java/com/gitblit/PagesServlet.java
+++ b/src/main/java/com/gitblit/PagesServlet.java
@@ -141,7 +141,6 @@
 				r.close();

 				return;

 			}

-			response.setDateHeader("Last-Modified", JGitUtils.getCommitDate(commit).getTime());

 

 			String [] encodings = GitBlit.getEncodings();

 

@@ -226,6 +225,8 @@
 

 			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) {

diff --git a/src/main/java/com/gitblit/wicket/CacheControl.java b/src/main/java/com/gitblit/wicket/CacheControl.java
new file mode 100644
index 0000000..f72fe3a
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/CacheControl.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 2013 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;

+

+import java.lang.annotation.Documented;

+import java.lang.annotation.Retention;

+import java.lang.annotation.RetentionPolicy;

+

+/**

+ * Page attribute to control what date as last-modified for the browser cache.

+ * 

+ * http://betterexplained.com/articles/how-to-optimize-your-site-with-http-caching

+ * https://developers.google.com/speed/docs/best-practices/caching

+ * 

+ * @author James Moger

+ *

+ */

+@Documented

+@Retention(RetentionPolicy.RUNTIME)

+public @interface CacheControl {

+	

+	public static enum LastModified {

+		BOOT, ACTIVITY, PROJECT, REPOSITORY, COMMIT, NONE

+	}

+	

+	LastModified value() default LastModified.NONE;

+}
\ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
index cdae093..74ccac7 100644
--- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
+++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java
@@ -15,7 +15,9 @@
  */

 package com.gitblit.wicket;

 

+import java.util.HashMap;

 import java.util.Locale;

+import java.util.Map;

 

 import org.apache.wicket.Application;

 import org.apache.wicket.Page;

@@ -71,6 +73,8 @@
 

 	public final static Class<? extends BasePage> HOME_PAGE_CLASS = MyDashboardPage.class;

 	

+	private final Map<String, CacheControl> cacheablePages = new HashMap<String, CacheControl>();

+	

 	@Override

 	public void init() {

 		super.init();

@@ -149,6 +153,12 @@
 			parameters = new String[] {};

 		}

 		mount(new GitblitParamUrlCodingStrategy(location, clazz, parameters));

+		

+		// map the mount point to the cache control definition 

+		if (clazz.isAnnotationPresent(CacheControl.class)) {

+			CacheControl cacheControl = clazz.getAnnotation(CacheControl.class);

+			cacheablePages.put(location.substring(1), cacheControl);

+		}

 	}

 

 	@Override

@@ -156,6 +166,14 @@
 		return HOME_PAGE_CLASS;

 	}

 	

+	public boolean isCacheablePage(String mountPoint) {

+		return cacheablePages.containsKey(mountPoint);

+	}

+

+	public CacheControl getCacheControl(String mountPoint) {

+		return cacheablePages.get(mountPoint);

+	}

+

 	@Override

 	public final Session newSession(Request request, Response response) {

 		GitBlitWebSession gitBlitWebSession = new GitBlitWebSession(request);

diff --git a/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java b/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java
new file mode 100644
index 0000000..f46c51e
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/GitblitWicketFilter.java
@@ -0,0 +1,145 @@
+/*

+ * Copyright 2013 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;

+

+import java.util.Date;

+

+import javax.servlet.http.HttpServletRequest;

+

+import org.apache.wicket.protocol.http.WicketFilter;

+import org.apache.wicket.util.string.Strings;

+import org.eclipse.jgit.lib.Repository;

+import org.eclipse.jgit.revwalk.RevCommit;

+

+import com.gitblit.GitBlit;

+import com.gitblit.Keys;

+import com.gitblit.models.ProjectModel;

+import com.gitblit.models.RepositoryModel;

+import com.gitblit.utils.JGitUtils;

+import com.gitblit.utils.StringUtils;

+

+/**

+ * 

+ * Customization of the WicketFilter to allow smart browser-side caching of

+ * some pages.

+ * 

+ * @author James Moger

+ *

+ */

+public class GitblitWicketFilter extends WicketFilter {

+	

+	/**

+	 * Determines the last-modified date of the requested resource.

+	 * 

+	 * @param servletRequest

+	 * @return The last modified time stamp

+	 */

+	protected long getLastModified(final HttpServletRequest servletRequest)	{

+		final String pathInfo = getRelativePath(servletRequest);

+		if (Strings.isEmpty(pathInfo))

+			return -1;

+		long lastModified = super.getLastModified(servletRequest);

+		if (lastModified > -1) {

+			return lastModified;

+		}

+		

+		// try to match request against registered CacheControl pages

+		String [] paths = pathInfo.split("/");

+		

+		String page = paths[0];

+		String repo = "";

+		String commitId = "";

+		if (paths.length >= 2) {

+			repo = paths[1];

+		}

+		if (paths.length >= 3) {

+			commitId = paths[2];

+		}

+		

+		if (!StringUtils.isEmpty(servletRequest.getParameter("r"))) {

+			repo = servletRequest.getParameter("r");

+		}

+		if (!StringUtils.isEmpty(servletRequest.getParameter("h"))) {

+			commitId = servletRequest.getParameter("h");

+		}

+		

+		repo = repo.replace("%2f", "/").replace("%2F", "/").replace(GitBlit.getChar(Keys.web.forwardSlashCharacter, '/'), '/');

+

+		GitBlitWebApp app = (GitBlitWebApp) getWebApplication();

+		int expires = GitBlit.getInteger(Keys.web.pageCacheExpires, 0);

+		if (!StringUtils.isEmpty(page) && app.isCacheablePage(page) && expires > 0) {

+			// page can be cached by the browser

+			CacheControl cacheControl = app.getCacheControl(page);

+			Date bootDate = GitBlit.getBootDate();

+			switch (cacheControl.value()) {

+			case ACTIVITY:

+				// returns the last activity date of the server

+				Date activityDate = GitBlit.getLastActivityDate();

+				if (activityDate != null) {

+					return activityDate.after(bootDate) ? activityDate.getTime() : bootDate.getTime();

+				}

+				return bootDate.getTime();

+			case BOOT:

+				// return the boot date of the server

+				return bootDate.getTime();

+			case PROJECT:

+				// return the latest change date for the project OR the boot date

+				ProjectModel project = GitBlit.self().getProjectModel(StringUtils.getRootPath(repo));

+				if (project != null) {

+					return project.lastChange.after(bootDate) ? project.lastChange.getTime() : bootDate.getTime();

+				}

+				break;

+			case REPOSITORY:

+				// return the lastest change date for the repository OR the boot

+				// date, whichever is latest

+				RepositoryModel repository = GitBlit.self().getRepositoryModel(repo);

+				if (repository != null && repository.lastChange != null) {

+					return repository.lastChange.after(bootDate) ? repository.lastChange.getTime() : bootDate.getTime();

+				}

+				break;

+			case COMMIT:

+				// get the date of the specified commit

+				if (StringUtils.isEmpty(commitId)) {

+					// no commit id, use boot date

+					return bootDate.getTime();

+				} else {

+					// last modified date is the commit date 

+					Repository r = null;

+					try {

+						// return the timestamp of the associated commit

+						r = GitBlit.self().getRepository(repo);

+						if (r != null) {

+							RevCommit commit = JGitUtils.getCommit(r, commitId);

+							if (commit != null) {

+								Date date = JGitUtils.getCommitDate(commit);

+								return date.after(bootDate) ? date.getTime() : bootDate.getTime();

+							}

+						}

+					} finally {

+						if (r != null) {

+							r.close();

+						}

+					}

+				}

+				break;

+			default:

+				break;

+			}

+		}			

+

+		return -1;

+	}

+}

diff --git a/src/main/java/com/gitblit/wicket/pages/ActivityPage.java b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java
index 694fa60..413403b 100644
--- a/src/main/java/com/gitblit/wicket/pages/ActivityPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ActivityPage.java
@@ -35,7 +35,9 @@
 import com.gitblit.models.RepositoryModel;

 import com.gitblit.utils.ActivityUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.PageRegistration;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.PageRegistration.DropDownMenuItem;

 import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;

 import com.gitblit.wicket.WicketUtils;

@@ -52,6 +54,8 @@
  * @author James Moger

  * 

  */

+

+@CacheControl(LastModified.ACTIVITY)

 public class ActivityPage extends RootPage {

 

 	public ActivityPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/BasePage.java b/src/main/java/com/gitblit/wicket/pages/BasePage.java
index 71713cd..c9e11b0 100644
--- a/src/main/java/com/gitblit/wicket/pages/BasePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BasePage.java
@@ -40,7 +40,10 @@
 import org.apache.wicket.markup.html.link.ExternalLink;

 import org.apache.wicket.markup.html.panel.FeedbackPanel;

 import org.apache.wicket.protocol.http.RequestUtils;

+import org.apache.wicket.protocol.http.WebResponse;

 import org.apache.wicket.protocol.http.servlet.ServletWebRequest;

+import org.apache.wicket.util.time.Duration;

+import org.apache.wicket.util.time.Time;

 import org.slf4j.Logger;

 import org.slf4j.LoggerFactory;

 

@@ -56,6 +59,7 @@
 import com.gitblit.models.UserModel;

 import com.gitblit.utils.StringUtils;

 import com.gitblit.utils.TimeUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.GitBlitWebApp;

 import com.gitblit.wicket.GitBlitWebSession;

 import com.gitblit.wicket.WicketUtils;

@@ -121,7 +125,68 @@
 			Application.get().getMarkupSettings().setStripWicketTags(false);

 		}

 		super.onAfterRender();

-	}	

+	}

+		

+	@Override

+	protected void setHeaders(WebResponse response)	{

+		int expires = GitBlit.getInteger(Keys.web.pageCacheExpires, 0);

+		if (expires > 0) {

+			// pages are personalized for the authenticated user so they must be

+			// marked private to prohibit proxy servers from caching them

+			response.setHeader("Cache-Control", "private, must-revalidate");

+			setLastModified();

+		} else {

+			// use default Wicket caching behavior

+			super.setHeaders(response);

+		}

+	}

+	

+	/**

+	 * Sets the last-modified header date, if appropriate, for this page.  The

+	 * date used is determined by the CacheControl annotation.

+	 * 

+	 */

+	protected void setLastModified() {

+		if (getClass().isAnnotationPresent(CacheControl.class)) {

+			CacheControl cacheControl = getClass().getAnnotation(CacheControl.class);

+			switch (cacheControl.value()) {

+			case ACTIVITY:

+				setLastModified(GitBlit.getLastActivityDate());

+				break;

+			case BOOT:

+				setLastModified(GitBlit.getBootDate());

+				break;

+			case NONE:

+				break;

+			default:

+				logger.warn(getClass().getSimpleName() + ": unhandled LastModified type " + cacheControl.value());

+				break;

+			}

+		}

+	}

+	

+	/**

+	 * Sets the last-modified header field and the expires field.

+	 * 

+	 * @param when

+	 */

+	protected final void setLastModified(Date when) {

+		if (when == null) {

+			return;

+		}

+		

+		if (when.before(GitBlit.getBootDate())) {

+			// last-modified can not be before the Gitblit boot date

+			// this helps ensure that pages are properly refreshed after a

+			// server config change

+			when = GitBlit.getBootDate();

+		}

+		

+		int expires = GitBlit.getInteger(Keys.web.pageCacheExpires, 0);

+		WebResponse response = (WebResponse) getResponse();

+		response.setLastModifiedTime(Time.valueOf(when));

+		response.setDateHeader("Expires", System.currentTimeMillis() + Duration.minutes(expires).getMilliseconds());

+	}

 

 	protected void setupPage(String repositoryName, String pageName) {

 		String siteName = GitBlit.getString(Keys.web.siteName, Constants.NAME);

diff --git a/src/main/java/com/gitblit/wicket/pages/BlamePage.java b/src/main/java/com/gitblit/wicket/pages/BlamePage.java
index 5148915..53bd233 100644
--- a/src/main/java/com/gitblit/wicket/pages/BlamePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BlamePage.java
@@ -37,11 +37,14 @@
 import com.gitblit.utils.DiffUtils;

 import com.gitblit.utils.JGitUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.WicketUtils;

 import com.gitblit.wicket.panels.CommitHeaderPanel;

 import com.gitblit.wicket.panels.LinkPanel;

 import com.gitblit.wicket.panels.PathBreadcrumbsPanel;

 

+@CacheControl(LastModified.BOOT)

 public class BlamePage extends RepositoryPage {

 

 	public BlamePage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java
index 03edaa3..c297bca 100644
--- a/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BlobDiffPage.java
@@ -27,10 +27,13 @@
 import com.gitblit.utils.DiffUtils.DiffOutputType;

 import com.gitblit.utils.JGitUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.WicketUtils;

 import com.gitblit.wicket.panels.CommitHeaderPanel;

 import com.gitblit.wicket.panels.PathBreadcrumbsPanel;

 

+@CacheControl(LastModified.BOOT)

 public class BlobDiffPage extends RepositoryPage {

 

 	public BlobDiffPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/BlobPage.java b/src/main/java/com/gitblit/wicket/pages/BlobPage.java
index f6e9998..b104df2 100644
--- a/src/main/java/com/gitblit/wicket/pages/BlobPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BlobPage.java
@@ -32,11 +32,14 @@
 import com.gitblit.Keys;

 import com.gitblit.utils.JGitUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.ExternalImage;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.panels.CommitHeaderPanel;

 import com.gitblit.wicket.panels.PathBreadcrumbsPanel;

 

+@CacheControl(LastModified.BOOT)

 public class BlobPage extends RepositoryPage {

 

 	protected String fileExtension;

diff --git a/src/main/java/com/gitblit/wicket/pages/BranchesPage.java b/src/main/java/com/gitblit/wicket/pages/BranchesPage.java
index 8684fb3..fe7483e 100644
--- a/src/main/java/com/gitblit/wicket/pages/BranchesPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/BranchesPage.java
@@ -17,8 +17,11 @@
 

 import org.apache.wicket.PageParameters;

 

+import com.gitblit.wicket.CacheControl;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.panels.BranchesPanel;

 

+@CacheControl(LastModified.REPOSITORY)

 public class BranchesPage extends RepositoryPage {

 

 	public BranchesPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
index 6954808..6f1b459 100644
--- a/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/CommitDiffPage.java
@@ -16,7 +16,6 @@
 package com.gitblit.wicket.pages;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import org.apache.wicket.PageParameters;
@@ -30,22 +29,21 @@
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.revwalk.RevCommit;
 
-import com.gitblit.Constants;
 import com.gitblit.GitBlit;
 import com.gitblit.Keys;
 import com.gitblit.models.PathModel.PathChangeModel;
-import com.gitblit.models.GitNote;
 import com.gitblit.models.SubmoduleModel;
 import com.gitblit.utils.DiffUtils;
 import com.gitblit.utils.DiffUtils.DiffOutputType;
 import com.gitblit.utils.JGitUtils;
+import com.gitblit.wicket.CacheControl;
+import com.gitblit.wicket.CacheControl.LastModified;
 import com.gitblit.wicket.WicketUtils;
 import com.gitblit.wicket.panels.CommitHeaderPanel;
 import com.gitblit.wicket.panels.CommitLegendPanel;
-import com.gitblit.wicket.panels.GravatarImage;
 import com.gitblit.wicket.panels.LinkPanel;
-import com.gitblit.wicket.panels.RefsPanel;
 
+@CacheControl(LastModified.BOOT)
 public class CommitDiffPage extends RepositoryPage {
 
 	public CommitDiffPage(PageParameters params) {
diff --git a/src/main/java/com/gitblit/wicket/pages/CommitPage.java b/src/main/java/com/gitblit/wicket/pages/CommitPage.java
index b98dba5..1d11d44 100644
--- a/src/main/java/com/gitblit/wicket/pages/CommitPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/CommitPage.java
@@ -37,6 +37,8 @@
 import com.gitblit.models.PathModel.PathChangeModel;

 import com.gitblit.models.SubmoduleModel;

 import com.gitblit.utils.JGitUtils;

+import com.gitblit.wicket.CacheControl;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.WicketUtils;

 import com.gitblit.wicket.panels.CommitHeaderPanel;

 import com.gitblit.wicket.panels.CommitLegendPanel;

@@ -45,6 +47,7 @@
 import com.gitblit.wicket.panels.LinkPanel;

 import com.gitblit.wicket.panels.RefsPanel;

 

+@CacheControl(LastModified.BOOT)

 public class CommitPage extends RepositoryPage {

 

 	public CommitPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/DocsPage.java b/src/main/java/com/gitblit/wicket/pages/DocsPage.java
index 9ddc98d..9330316 100644
--- a/src/main/java/com/gitblit/wicket/pages/DocsPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/DocsPage.java
@@ -30,9 +30,12 @@
 import com.gitblit.models.PathModel;

 import com.gitblit.utils.ByteFormat;

 import com.gitblit.utils.JGitUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.panels.LinkPanel;

 

+@CacheControl(LastModified.REPOSITORY)

 public class DocsPage extends RepositoryPage {

 

 	public DocsPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/GitSearchPage.java b/src/main/java/com/gitblit/wicket/pages/GitSearchPage.java
index 154bdcc..446531a 100644
--- a/src/main/java/com/gitblit/wicket/pages/GitSearchPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/GitSearchPage.java
@@ -19,9 +19,12 @@
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;

 

 import com.gitblit.Constants;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.panels.SearchPanel;

 

+@CacheControl(LastModified.REPOSITORY)

 public class GitSearchPage extends RepositoryPage {

 

 	public GitSearchPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/HistoryPage.java b/src/main/java/com/gitblit/wicket/pages/HistoryPage.java
index 314bdef..33bc54c 100644
--- a/src/main/java/com/gitblit/wicket/pages/HistoryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/HistoryPage.java
@@ -18,9 +18,12 @@
 import org.apache.wicket.PageParameters;

 import org.apache.wicket.markup.html.link.BookmarkablePageLink;

 

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.panels.HistoryPanel;

 

+@CacheControl(LastModified.REPOSITORY)

 public class HistoryPage extends RepositoryPage {

 

 	public HistoryPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/LogPage.java b/src/main/java/com/gitblit/wicket/pages/LogPage.java
index ee8ddfe..1f4a9bf 100644
--- a/src/main/java/com/gitblit/wicket/pages/LogPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/LogPage.java
@@ -19,9 +19,12 @@
 import org.apache.wicket.markup.html.link.BookmarkablePageLink;

 

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.panels.LogPanel;

 

+@CacheControl(LastModified.REPOSITORY)

 public class LogPage extends RepositoryPage {

 

 	public LogPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java b/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java
index 7f82b64..df078c7 100644
--- a/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/MarkdownPage.java
@@ -29,8 +29,11 @@
 import com.gitblit.utils.JGitUtils;

 import com.gitblit.utils.MarkdownUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 

+@CacheControl(LastModified.BOOT)

 public class MarkdownPage extends RepositoryPage {

 

 	public MarkdownPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/MetricsPage.java b/src/main/java/com/gitblit/wicket/pages/MetricsPage.java
index 3195020..3aa1fcc 100644
--- a/src/main/java/com/gitblit/wicket/pages/MetricsPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/MetricsPage.java
@@ -40,9 +40,12 @@
 import com.gitblit.models.Metric;

 import com.gitblit.utils.MetricUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.charting.SecureChart;

 

+@CacheControl(LastModified.REPOSITORY)

 public class MetricsPage extends RepositoryPage {

 

 	public MetricsPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java b/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java
index d951da3..32c128d 100644
--- a/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/MyDashboardPage.java
@@ -43,11 +43,14 @@
 import com.gitblit.utils.ArrayUtils;

 import com.gitblit.utils.MarkdownUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.GitBlitWebSession;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.panels.FilterableProjectList;

 import com.gitblit.wicket.panels.FilterableRepositoryList;

 

+@CacheControl(LastModified.ACTIVITY)

 public class MyDashboardPage extends DashboardPage {

 

 	public MyDashboardPage() {

diff --git a/src/main/java/com/gitblit/wicket/pages/OverviewPage.java b/src/main/java/com/gitblit/wicket/pages/OverviewPage.java
index ff6326d..e1de9f3 100644
--- a/src/main/java/com/gitblit/wicket/pages/OverviewPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/OverviewPage.java
@@ -34,8 +34,10 @@
 import com.gitblit.models.RepositoryModel;

 import com.gitblit.models.UserModel;

 import com.gitblit.utils.JGitUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.GitBlitWebSession;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.charting.GoogleChart;

 import com.gitblit.wicket.charting.GoogleCharts;

 import com.gitblit.wicket.charting.GoogleLineChart;

@@ -45,6 +47,7 @@
 import com.gitblit.wicket.panels.RepositoryUrlPanel;

 import com.gitblit.wicket.panels.TagsPanel;

 

+@CacheControl(LastModified.REPOSITORY)

 public class OverviewPage extends RepositoryPage {

 

 	public OverviewPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/PatchPage.java b/src/main/java/com/gitblit/wicket/pages/PatchPage.java
index 878cfb4..be959d0 100644
--- a/src/main/java/com/gitblit/wicket/pages/PatchPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/PatchPage.java
@@ -25,9 +25,12 @@
 import com.gitblit.utils.DiffUtils;

 import com.gitblit.utils.JGitUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.GitBlitWebSession;

 import com.gitblit.wicket.WicketUtils;

 

+@CacheControl(LastModified.BOOT)

 public class PatchPage extends WebPage {

 

 	public PatchPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/ProjectPage.java b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java
index 97a31f9..c938891 100644
--- a/src/main/java/com/gitblit/wicket/pages/ProjectPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ProjectPage.java
@@ -33,6 +33,8 @@
 import com.gitblit.models.UserModel;

 import com.gitblit.utils.MarkdownUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.GitBlitWebApp;

 import com.gitblit.wicket.GitBlitWebSession;

 import com.gitblit.wicket.GitblitRedirectException;

@@ -42,6 +44,7 @@
 import com.gitblit.wicket.WicketUtils;

 import com.gitblit.wicket.panels.FilterableRepositoryList;

 

+@CacheControl(LastModified.PROJECT)

 public class ProjectPage extends DashboardPage {

 	

 	List<ProjectModel> projectModels = new ArrayList<ProjectModel>();

@@ -60,6 +63,26 @@
 		return RepositoriesPage.class;

 	}

 

+	@Override

+	protected void setLastModified() {

+		if (getClass().isAnnotationPresent(CacheControl.class)) {

+			CacheControl cacheControl = getClass().getAnnotation(CacheControl.class);

+			switch (cacheControl.value()) {

+			case PROJECT:

+				String projectName = WicketUtils.getProjectName(getPageParameters());

+				if (!StringUtils.isEmpty(projectName)) {

+					ProjectModel project = getProjectModel(projectName);

+					if (project != null) {

+						setLastModified(project.lastChange);

+					}

+				}

+				break;

+			default:

+				super.setLastModified();

+			}

+		}

+	}

+	

 	private void setup(PageParameters params) {

 		setupPage("", "");

 		// check to see if we should display a login message

diff --git a/src/main/java/com/gitblit/wicket/pages/ReflogPage.java b/src/main/java/com/gitblit/wicket/pages/ReflogPage.java
index 884f616..c97b2cc 100644
--- a/src/main/java/com/gitblit/wicket/pages/ReflogPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/ReflogPage.java
@@ -18,9 +18,12 @@
 import org.apache.wicket.PageParameters;

 import org.apache.wicket.markup.html.link.BookmarkablePageLink;

 

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.panels.ReflogPanel;

 

+@CacheControl(LastModified.REPOSITORY)

 public class ReflogPage extends RepositoryPage {

 

 	public ReflogPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java
index 7d4d68c..a4a1ab5 100644
--- a/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoriesPage.java
@@ -33,13 +33,16 @@
 import com.gitblit.models.RepositoryModel;

 import com.gitblit.utils.MarkdownUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.GitBlitWebSession;

 import com.gitblit.wicket.PageRegistration;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.PageRegistration.DropDownMenuItem;

 import com.gitblit.wicket.PageRegistration.DropDownMenuRegistration;

 import com.gitblit.wicket.WicketUtils;

 import com.gitblit.wicket.panels.RepositoriesPanel;

 

+@CacheControl(LastModified.ACTIVITY)

 public class RepositoriesPage extends RootPage {

 

 	public RepositoriesPage() {

diff --git a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
index 372b68d..f5b8c96 100644
--- a/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
@@ -19,6 +19,7 @@
 import java.text.MessageFormat;

 import java.util.ArrayList;

 import java.util.Arrays;

+import java.util.Date;

 import java.util.HashMap;

 import java.util.LinkedHashMap;

 import java.util.LinkedHashSet;

@@ -63,6 +64,7 @@
 import com.gitblit.utils.RefLogUtils;

 import com.gitblit.utils.StringUtils;

 import com.gitblit.utils.TicgitUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.GitBlitWebSession;

 import com.gitblit.wicket.PageRegistration;

 import com.gitblit.wicket.PageRegistration.OtherPageLink;

@@ -572,6 +574,30 @@
 		setupPage(repositoryName, "/ " + getPageName());

 		super.onBeforeRender();

 	}

+	

+	@Override

+	protected void setLastModified() {

+		if (getClass().isAnnotationPresent(CacheControl.class)) {

+			CacheControl cacheControl = getClass().getAnnotation(CacheControl.class);

+			switch (cacheControl.value()) {

+			case REPOSITORY:

+				RepositoryModel repository = getRepositoryModel();

+				if (repository != null) {

+					setLastModified(repository.lastChange);

+				}

+				break;

+			case COMMIT:

+				RevCommit commit = getCommit();

+				if (commit != null) {

+					Date commitDate = JGitUtils.getCommitDate(commit);

+					setLastModified(commitDate);

+				}

+				break;

+			default:

+				super.setLastModified();

+			}

+		}

+	}

 

 	protected PageParameters newRepositoryParameter() {

 		return WicketUtils.newRepositoryParameter(repositoryName);

diff --git a/src/main/java/com/gitblit/wicket/pages/SummaryPage.java b/src/main/java/com/gitblit/wicket/pages/SummaryPage.java
index c231b2b..17c41eb 100644
--- a/src/main/java/com/gitblit/wicket/pages/SummaryPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/SummaryPage.java
@@ -50,8 +50,10 @@
 import com.gitblit.utils.JGitUtils;

 import com.gitblit.utils.MarkdownUtils;

 import com.gitblit.utils.StringUtils;

+import com.gitblit.wicket.CacheControl;

 import com.gitblit.wicket.GitBlitWebSession;

 import com.gitblit.wicket.WicketUtils;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.charting.SecureChart;

 import com.gitblit.wicket.panels.BranchesPanel;

 import com.gitblit.wicket.panels.LinkPanel;

@@ -59,6 +61,7 @@
 import com.gitblit.wicket.panels.RepositoryUrlPanel;

 import com.gitblit.wicket.panels.TagsPanel;

 

+@CacheControl(LastModified.REPOSITORY)

 public class SummaryPage extends RepositoryPage {

 

 	public SummaryPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/TagPage.java b/src/main/java/com/gitblit/wicket/pages/TagPage.java
index 6deef78..13c1011 100644
--- a/src/main/java/com/gitblit/wicket/pages/TagPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/TagPage.java
@@ -28,11 +28,14 @@
 

 import com.gitblit.models.RefModel;

 import com.gitblit.utils.JGitUtils;

+import com.gitblit.wicket.CacheControl;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.WicketUtils;

 import com.gitblit.wicket.panels.GravatarImage;

 import com.gitblit.wicket.panels.LinkPanel;

 import com.gitblit.wicket.panels.RefsPanel;

 

+@CacheControl(LastModified.BOOT)

 public class TagPage extends RepositoryPage {

 

 	public TagPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/TagsPage.java b/src/main/java/com/gitblit/wicket/pages/TagsPage.java
index b052531..2abb410 100644
--- a/src/main/java/com/gitblit/wicket/pages/TagsPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/TagsPage.java
@@ -17,8 +17,11 @@
 

 import org.apache.wicket.PageParameters;

 

+import com.gitblit.wicket.CacheControl;

+import com.gitblit.wicket.CacheControl.LastModified;

 import com.gitblit.wicket.panels.TagsPanel;

 

+@CacheControl(LastModified.REPOSITORY)

 public class TagsPage extends RepositoryPage {

 

 	public TagsPage(PageParameters params) {

diff --git a/src/main/java/com/gitblit/wicket/pages/TreePage.java b/src/main/java/com/gitblit/wicket/pages/TreePage.java
index bc27f0c..25bcd67 100644
--- a/src/main/java/com/gitblit/wicket/pages/TreePage.java
+++ b/src/main/java/com/gitblit/wicket/pages/TreePage.java
@@ -33,12 +33,15 @@
 import com.gitblit.models.SubmoduleModel;

 import com.gitblit.utils.ByteFormat;

 import com.gitblit.utils.JGitUtils;

+import com.gitblit.wicket.CacheControl;

+import com.gitblit.wicket.CacheControl.LastModified;

 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;

 

+@CacheControl(LastModified.BOOT)

 public class TreePage extends RepositoryPage {

 

 	public TreePage(PageParameters params) {