diff --git a/src/main/scala/gitbucket/core/controller/AccountController.scala b/src/main/scala/gitbucket/core/controller/AccountController.scala index 19c553a..86866e5 100644 --- a/src/main/scala/gitbucket/core/controller/AccountController.scala +++ b/src/main/scala/gitbucket/core/controller/AccountController.scala @@ -15,6 +15,7 @@ import org.apache.commons.io.FileUtils import org.scalatra.i18n.Messages import org.scalatra.BadRequest +import java.util.Date class AccountController extends AccountControllerBase @@ -149,11 +150,17 @@ get("/:userName/_avatar"){ val userName = params("userName") - getAccountByUserName(userName).flatMap(_.image).map { image => - RawData(FileUtil.getMimeType(image), new java.io.File(getUserUploadDir(userName), image)) - } getOrElse { - contentType = "image/png" - Thread.currentThread.getContextClassLoader.getResourceAsStream("noimage.png") + (for { + account <- getAccountByUserName(userName) + image <- account.image + } yield (account, image)) match{ + case Some((account, image)) => + response.setDateHeader("Last-Modified", account.updatedDate.getTime) + RawData(FileUtil.getMimeType(image), new java.io.File(getUserUploadDir(userName), image)) + case None => + contentType = "image/png" + response.setDateHeader("Last-Modified", (new Date(0)).getTime) + Thread.currentThread.getContextClassLoader.getResourceAsStream("noimage.png") } } diff --git a/src/main/scala/gitbucket/core/servlet/PluginAssetsServlet.scala b/src/main/scala/gitbucket/core/servlet/PluginAssetsServlet.scala index 0d43af3..6fe1ed3 100644 --- a/src/main/scala/gitbucket/core/servlet/PluginAssetsServlet.scala +++ b/src/main/scala/gitbucket/core/servlet/PluginAssetsServlet.scala @@ -26,6 +26,7 @@ val bytes = IOUtils.toByteArray(in) resp.setContentLength(bytes.length) resp.setContentType(FileUtil.getContentType(path, bytes)) + resp.setHeader("Cache-Control", "max-age=3600") resp.getOutputStream.write(bytes) } finally { in.close() diff --git a/src/main/scala/gitbucket/core/view/AvatarImageProvider.scala b/src/main/scala/gitbucket/core/view/AvatarImageProvider.scala index 8d316ab..8cd64d0 100644 --- a/src/main/scala/gitbucket/core/view/AvatarImageProvider.scala +++ b/src/main/scala/gitbucket/core/view/AvatarImageProvider.scala @@ -20,7 +20,7 @@ if(account.image.isEmpty && context.settings.gravatar){ s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g""" } else { - s"""${context.path}/${account.userName}/_avatar""" + s"""${context.path}/${account.userName}/_avatar?${helpers.hashDate(account.updatedDate)}""" } } getOrElse { s"""${context.path}/_unknown/_avatar""" @@ -31,7 +31,7 @@ if(account.image.isEmpty && context.settings.gravatar){ s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g""" } else { - s"""${context.path}/${account.userName}/_avatar""" + s"""${context.path}/${account.userName}/_avatar?${helpers.hashDate(account.updatedDate)}""" } } getOrElse { if(context.settings.gravatar){ @@ -49,4 +49,4 @@ } } -} \ No newline at end of file +} diff --git a/src/main/scala/gitbucket/core/view/helpers.scala b/src/main/scala/gitbucket/core/view/helpers.scala index dc4e1c1..f16e81c 100644 --- a/src/main/scala/gitbucket/core/view/helpers.scala +++ b/src/main/scala/gitbucket/core/view/helpers.scala @@ -75,6 +75,21 @@ def date(date: Date): String = new SimpleDateFormat("yyyy-MM-dd").format(date) /** + * Format java.util.Date to "yyyyMMDDHHmmss" (for url hash ex. /some/path.css?19800101010203 + */ + def hashDate(date: Date): String = new SimpleDateFormat("yyyyMMddHHmmss").format(date) + + /** + * java.util.Date of boot timestamp. + */ + val bootDate: Date = new Date() + + /** + * hashDate of bootDate for /assets, /plugin-assets + */ + def hashQuery: String = hashDate(bootDate) + + /** * Returns singular if count is 1, otherwise plural. * If plural is not specified, returns singular + "s" as plural. */ @@ -212,6 +227,11 @@ def assets(implicit context: Context): String = s"${context.path}/assets" /** + * Returns the url to the path of assets. + */ + def assets(path: String)(implicit context: Context): String = s"${context.path}/assets${path}?${hashQuery}" + + /** * Generates the text link to the account page. * If user does not exist or disabled, this method returns user name as text without link. */ diff --git a/src/main/twirl/gitbucket/core/helper/diff.scala.html b/src/main/twirl/gitbucket/core/helper/diff.scala.html index 17fdea2..8217c37 100644 --- a/src/main/twirl/gitbucket/core/helper/diff.scala.html +++ b/src/main/twirl/gitbucket/core/helper/diff.scala.html @@ -130,8 +130,8 @@ } - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + @repository.map { repository => } - +