Merge commit '5317ac5e031a29438657952fb882532af296135b' (tag 1.12) into add-features-to-ldapauth

This commit is contained in:
yjkony
2014-03-31 12:33:58 +09:00
46 changed files with 188 additions and 159 deletions

View File

@@ -35,6 +35,8 @@ Installation
2. Deploy it to the Servlet 3.0 container such as Tomcat 7.x, Jetty 8.x, GlassFish 3.x or higher. 2. Deploy it to the Servlet 3.0 container such as Tomcat 7.x, Jetty 8.x, GlassFish 3.x or higher.
3. Access **http://[hostname]:[port]/gitbucket/** using your web browser. 3. Access **http://[hostname]:[port]/gitbucket/** using your web browser.
If you are using Gitbucket behind a webserver please make sure you have increased the **client_max_body_size** (on nignx)
The default administrator account is **root** and password is **root**. The default administrator account is **root** and password is **root**.
or you can start GitBucket by `java -jar gitbucket.war` without servlet container. In this case, GitBucket URL is **http://[hostname]:8080/**. You can specify following options. or you can start GitBucket by `java -jar gitbucket.war` without servlet container. In this case, GitBucket URL is **http://[hostname]:8080/**. You can specify following options.
@@ -58,6 +60,16 @@ Run the following commands in `Terminal` to
Release Notes Release Notes
-------- --------
### 1.12 - 29 Mar 2014
- SSH repository access is available
- Allow users can create and management their groups
- Git submodule support
- Close issues via commit messages
- Show repository description below the name on repository page
- Fix presentation of the source viewer
- Upgrade to sbt 0.13
- Fix some bugs
### 1.11.1 - 06 Mar 2014 ### 1.11.1 - 06 Mar 2014
- Bug fix - Bug fix

View File

@@ -1 +1 @@
sbt.version=0.12.3 sbt.version=0.13.1

View File

@@ -30,7 +30,7 @@ object MyBuild extends Build {
"org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test", "org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test",
"org.scalatra" %% "scalatra-json" % ScalatraVersion, "org.scalatra" %% "scalatra-json" % ScalatraVersion,
"org.json4s" %% "json4s-jackson" % "3.2.5", "org.json4s" %% "json4s-jackson" % "3.2.5",
"jp.sf.amateras" %% "scalatra-forms" % "0.0.11", "jp.sf.amateras" %% "scalatra-forms" % "0.0.14",
"commons-io" % "commons-io" % "2.4", "commons-io" % "commons-io" % "2.4",
"org.pegdown" % "pegdown" % "1.4.1", "org.pegdown" % "pegdown" % "1.4.1",
"org.apache.commons" % "commons-compress" % "1.5", "org.apache.commons" % "commons-compress" % "1.5",

View File

@@ -1,9 +1,11 @@
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.2.0") addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.4.0")
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.5.1") addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0")
addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.3.0") addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.3.5")
addSbtPlugin("io.spray" % "sbt-twirl" % "0.6.1") resolvers += "spray repo" at "http://repo.spray.io"
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.2") addSbtPlugin("io.spray" % "sbt-twirl" % "0.7.0")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.4")

Binary file not shown.

BIN
sbt-launch-0.13.1.jar Normal file

Binary file not shown.

View File

@@ -1,2 +1,2 @@
set SCRIPT_DIR=%~dp0 set SCRIPT_DIR=%~dp0
java -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar "%SCRIPT_DIR%\sbt-launch-0.12.3.jar" %* java -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar "%SCRIPT_DIR%\sbt-launch-0.13.1.jar" %*

2
sbt.sh
View File

@@ -1 +1 @@
java -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar `dirname $0`/sbt-launch-0.12.3.jar "$@" java -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar `dirname $0`/sbt-launch-0.13.1.jar "$@"

View File

@@ -132,7 +132,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
get("/:userName/_edit")(oneselfOnly { get("/:userName/_edit")(oneselfOnly {
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName).map { x => getAccountByUserName(userName).map { x =>
account.html.edit(x, loadSystemSettings(), flash.get("info")) account.html.edit(x, flash.get("info"))
} getOrElse NotFound } getOrElse NotFound
}) })
@@ -176,7 +176,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
get("/:userName/_ssh")(oneselfOnly { get("/:userName/_ssh")(oneselfOnly {
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName).map { x => getAccountByUserName(userName).map { x =>
account.html.ssh(x, loadSystemSettings(), getPublicKeys(x.userName)) account.html.ssh(x, getPublicKeys(x.userName))
} getOrElse NotFound } getOrElse NotFound
}) })
@@ -194,7 +194,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
}) })
get("/register"){ get("/register"){
if(loadSystemSettings().allowAccountRegistration){ if(context.settings.allowAccountRegistration){
if(context.loginAccount.isDefined){ if(context.loginAccount.isDefined){
redirect("/") redirect("/")
} else { } else {
@@ -204,7 +204,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
} }
post("/register", newForm){ form => post("/register", newForm){ form =>
if(loadSystemSettings().allowAccountRegistration){ if(context.settings.allowAccountRegistration){
createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, false, form.url) createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, false, form.url)
updateImage(form.userName, form.fileId, false) updateImage(form.userName, form.fileId, false)
redirect("/signin") redirect("/signin")

View File

@@ -36,18 +36,16 @@ abstract class ControllerBase extends ScalatraFilter
if(path.startsWith("/console/")){ if(path.startsWith("/console/")){
val account = httpRequest.getSession.getAttribute(Keys.Session.LoginAccount).asInstanceOf[Account] val account = httpRequest.getSession.getAttribute(Keys.Session.LoginAccount).asInstanceOf[Account]
val baseUrl = this.baseUrl(httpRequest)
if(account == null){ if(account == null){
// Redirect to login form // Redirect to login form
// TODO Should use the configured base url. httpResponse.sendRedirect(baseUrl + "/signin?redirect=" + StringUtil.urlEncode(path))
httpResponse.sendRedirect(context + "/signin?" + StringUtil.urlEncode(path))
} else if(account.isAdmin){ } else if(account.isAdmin){
// H2 Console (administrators only) // H2 Console (administrators only)
// TODO Should use the configured base url.
chain.doFilter(request, response) chain.doFilter(request, response)
} else { } else {
// Redirect to dashboard // Redirect to dashboard
// TODO Should use the configured base url. httpResponse.sendRedirect(baseUrl + "/")
httpResponse.sendRedirect(context + "/")
} }
} else if(path.startsWith("/git/")){ } else if(path.startsWith("/git/")){
// Git repository // Git repository
@@ -68,7 +66,7 @@ abstract class ControllerBase extends ScalatraFilter
implicit def context: Context = { implicit def context: Context = {
contextCache.get match { contextCache.get match {
case null => { case null => {
val context = Context(loadSystemSettings().baseUrl.getOrElse(servletContext.getContextPath), LoginAccount, request) val context = Context(loadSystemSettings(), LoginAccount, request)
contextCache.set(context) contextCache.set(context)
context context
} }
@@ -138,10 +136,10 @@ abstract class ControllerBase extends ScalatraFilter
/** /**
* Context object for the current request. * Context object for the current request.
*
* @param path the context path
*/ */
case class Context(path: String, loginAccount: Option[Account], request: HttpServletRequest){ case class Context(settings: SystemSettingsService.SystemSettings, loginAccount: Option[Account], request: HttpServletRequest){
lazy val path = settings.baseUrl.getOrElse(request.getServletContext.getContextPath)
lazy val currentPath = request.getRequestURI.substring(request.getContextPath.length) lazy val currentPath = request.getRequestURI.substring(request.getContextPath.length)

View File

@@ -22,7 +22,6 @@ trait IndexControllerBase extends ControllerBase {
html.index(getRecentActivities(), html.index(getRecentActivities(),
getVisibleRepositories(loginAccount, baseUrl), getVisibleRepositories(loginAccount, baseUrl),
loadSystemSettings(),
loginAccount.map{ account => getUserRepositories(account.userName, baseUrl) }.getOrElse(Nil) loginAccount.map{ account => getUserRepositories(account.userName, baseUrl) }.getOrElse(Nil)
) )
} }
@@ -32,11 +31,11 @@ trait IndexControllerBase extends ControllerBase {
if(redirect.isDefined && redirect.get.startsWith("/")){ if(redirect.isDefined && redirect.get.startsWith("/")){
flash += Keys.Flash.Redirect -> redirect.get flash += Keys.Flash.Redirect -> redirect.get
} }
html.signin(loadSystemSettings()) html.signin()
} }
post("/signin", form){ form => post("/signin", form){ form =>
authenticate(loadSystemSettings(), form.userName, form.password) match { authenticate(context.settings, form.userName, form.password) match {
case Some(account) => signin(account) case Some(account) => signin(account)
case None => redirect("/signin") case None => redirect("/signin")
} }

View File

@@ -65,7 +65,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
repo.html.commits(if(path.isEmpty) Nil else path.split("/").toList, branchName, repository, repo.html.commits(if(path.isEmpty) Nil else path.split("/").toList, branchName, repository,
logs.splitWith{ (commit1, commit2) => logs.splitWith{ (commit1, commit2) =>
view.helpers.date(commit1.time) == view.helpers.date(commit2.time) view.helpers.date(commit1.time) == view.helpers.date(commit2.time)
}, page, hasNext, loadSystemSettings()) }, page, hasNext)
case Left(_) => NotFound case Left(_) => NotFound
} }
} }
@@ -118,7 +118,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
JGitUtil.ContentInfo(viewer, None) JGitUtil.ContentInfo(viewer, None)
} }
repo.html.blob(id, repository, path.split("/").toList, content, new JGitUtil.CommitInfo(revCommit), loadSystemSettings()) repo.html.blob(id, repository, path.split("/").toList, content, new JGitUtil.CommitInfo(revCommit))
} }
} getOrElse NotFound } getOrElse NotFound
} }
@@ -136,7 +136,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
repo.html.commit(id, new JGitUtil.CommitInfo(revCommit), repo.html.commit(id, new JGitUtil.CommitInfo(revCommit),
JGitUtil.getBranchesOfCommit(git, revCommit.getName), JGitUtil.getBranchesOfCommit(git, revCommit.getName),
JGitUtil.getTagsOfCommit(git, revCommit.getName), JGitUtil.getTagsOfCommit(git, revCommit.getName),
repository, diffs, oldCommitId, loadSystemSettings()) repository, diffs, oldCommitId)
} }
} }
} }
@@ -152,8 +152,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
val revCommit = git.log.add(git.getRepository.resolve(branchName)).setMaxCount(1).call.iterator.next val revCommit = git.log.add(git.getRepository.resolve(branchName)).setMaxCount(1).call.iterator.next
(branchName, revCommit.getCommitterIdent.getWhen) (branchName, revCommit.getCommitterIdent.getWhen)
} }
repo.html.branches(branchInfo, hasWritePermission(repository.owner, repository.name, context.loginAccount), repo.html.branches(branchInfo, hasWritePermission(repository.owner, repository.name, context.loginAccount), repository)
repository, loadSystemSettings())
} }
}) })
@@ -176,14 +175,14 @@ trait RepositoryViewerControllerBase extends ControllerBase {
* Displays tags. * Displays tags.
*/ */
get("/:owner/:repository/tags")(referrersOnly { get("/:owner/:repository/tags")(referrersOnly {
repo.html.tags(_, loadSystemSettings()) repo.html.tags(_)
}) })
/** /**
* Download repository contents as an archive. * Download repository contents as an archive.
*/ */
get("/:owner/:repository/archive/:name")(referrersOnly { repository => get("/:owner/:repository/archive/*")(referrersOnly { repository =>
val name = params("name") val name = multiParams("splat").head
if(name.endsWith(".zip")){ if(name.endsWith(".zip")){
val revision = name.replaceFirst("\\.zip$", "") val revision = name.replaceFirst("\\.zip$", "")
@@ -194,7 +193,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
workDir.mkdirs workDir.mkdirs
val zipFile = new File(workDir, repository.name + "-" + val zipFile = new File(workDir, repository.name + "-" +
(if(revision.length == 40) revision.substring(0, 10) else revision) + ".zip") (if(revision.length == 40) revision.substring(0, 10) else revision).replace('/', '_') + ".zip")
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(revision)) val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(revision))
@@ -209,7 +208,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
while(walk.next){ while(walk.next){
val name = walk.getPathString val name = walk.getPathString
val mode = walk.getFileMode(0) val mode = walk.getFileMode(0)
if(mode == FileMode.REGULAR_FILE){ if(mode == FileMode.REGULAR_FILE || mode == FileMode.EXECUTABLE_FILE){
walk.getObjectId(objectId, 0) walk.getObjectId(objectId, 0)
val entry = new ZipEntry(name) val entry = new ZipEntry(name)
val loader = reader.open(objectId) val loader = reader.open(objectId)
@@ -285,7 +284,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
repo.html.files(revision, repository, repo.html.files(revision, repository,
if(path == ".") Nil else path.split("/").toList, // current path if(path == ".") Nil else path.split("/").toList, // current path
new JGitUtil.CommitInfo(revCommit), // latest commit new JGitUtil.CommitInfo(revCommit), // latest commit
files, readme, loadSystemSettings()) files, readme)
} }
} getOrElse NotFound } getOrElse NotFound
} }

View File

@@ -7,10 +7,10 @@ import jp.sf.amateras.scalatra.forms._
import ssh.SshServer import ssh.SshServer
class SystemSettingsController extends SystemSettingsControllerBase class SystemSettingsController extends SystemSettingsControllerBase
with SystemSettingsService with AccountService with AdminAuthenticator with AccountService with AdminAuthenticator
trait SystemSettingsControllerBase extends ControllerBase { trait SystemSettingsControllerBase extends ControllerBase {
self: SystemSettingsService with AccountService with AdminAuthenticator => self: AccountService with AdminAuthenticator =>
private val form = mapping( private val form = mapping(
"baseUrl" -> trim(label("Base URL", optional(text()))), "baseUrl" -> trim(label("Base URL", optional(text()))),
@@ -50,7 +50,7 @@ trait SystemSettingsControllerBase extends ControllerBase {
get("/admin/system")(adminOnly { get("/admin/system")(adminOnly {
admin.html.system(loadSystemSettings(), flash.get("info")) admin.html.system(flash.get("info"))
}) })
post("/admin/system", form)(adminOnly { form => post("/admin/system", form)(adminOnly { form =>

View File

@@ -36,7 +36,7 @@ trait WikiControllerBase extends ControllerBase {
get("/:owner/:repository/wiki")(referrersOnly { repository => get("/:owner/:repository/wiki")(referrersOnly { repository =>
getWikiPage(repository.owner, repository.name, "Home").map { page => getWikiPage(repository.owner, repository.name, "Home").map { page =>
wiki.html.page("Home", page, repository, hasWritePermission(repository.owner, repository.name, context.loginAccount), loadSystemSettings()) wiki.html.page("Home", page, repository, hasWritePermission(repository.owner, repository.name, context.loginAccount))
} getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/Home/_edit") } getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/Home/_edit")
}) })
@@ -44,7 +44,7 @@ trait WikiControllerBase extends ControllerBase {
val pageName = StringUtil.urlDecode(params("page")) val pageName = StringUtil.urlDecode(params("page"))
getWikiPage(repository.owner, repository.name, pageName).map { page => getWikiPage(repository.owner, repository.name, pageName).map { page =>
wiki.html.page(pageName, page, repository, hasWritePermission(repository.owner, repository.name, context.loginAccount), loadSystemSettings()) wiki.html.page(pageName, page, repository, hasWritePermission(repository.owner, repository.name, context.loginAccount))
} getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_edit") } getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_edit")
}) })
@@ -53,7 +53,7 @@ trait WikiControllerBase extends ControllerBase {
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match { JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match {
case Right((logs, hasNext)) => wiki.html.history(Some(pageName), logs, repository, loadSystemSettings()) case Right((logs, hasNext)) => wiki.html.history(Some(pageName), logs, repository)
case Left(_) => NotFound case Left(_) => NotFound
} }
} }
@@ -65,7 +65,7 @@ trait WikiControllerBase extends ControllerBase {
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
wiki.html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, from, to, true).filter(_.newPath == pageName + ".md"), repository, wiki.html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, from, to, true).filter(_.newPath == pageName + ".md"), repository,
hasWritePermission(repository.owner, repository.name, context.loginAccount), loadSystemSettings(), flash.get("info")) hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info"))
} }
}) })
@@ -74,7 +74,7 @@ trait WikiControllerBase extends ControllerBase {
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
wiki.html.compare(None, from, to, JGitUtil.getDiffs(git, from, to, true), repository, wiki.html.compare(None, from, to, JGitUtil.getDiffs(git, from, to, true), repository,
hasWritePermission(repository.owner, repository.name, context.loginAccount), loadSystemSettings(), flash.get("info")) hasWritePermission(repository.owner, repository.name, context.loginAccount), flash.get("info"))
} }
}) })
@@ -103,7 +103,7 @@ trait WikiControllerBase extends ControllerBase {
get("/:owner/:repository/wiki/:page/_edit")(collaboratorsOnly { repository => get("/:owner/:repository/wiki/:page/_edit")(collaboratorsOnly { repository =>
val pageName = StringUtil.urlDecode(params("page")) val pageName = StringUtil.urlDecode(params("page"))
wiki.html.edit(pageName, getWikiPage(repository.owner, repository.name, pageName), repository, loadSystemSettings()) wiki.html.edit(pageName, getWikiPage(repository.owner, repository.name, pageName), repository)
}) })
post("/:owner/:repository/wiki/_edit", editForm)(collaboratorsOnly { (form, repository) => post("/:owner/:repository/wiki/_edit", editForm)(collaboratorsOnly { (form, repository) =>
@@ -118,7 +118,7 @@ trait WikiControllerBase extends ControllerBase {
}) })
get("/:owner/:repository/wiki/_new")(collaboratorsOnly { get("/:owner/:repository/wiki/_new")(collaboratorsOnly {
wiki.html.edit("", None, _, loadSystemSettings()) wiki.html.edit("", None, _)
}) })
post("/:owner/:repository/wiki/_new", newForm)(collaboratorsOnly { (form, repository) => post("/:owner/:repository/wiki/_new", newForm)(collaboratorsOnly { (form, repository) =>
@@ -146,13 +146,13 @@ trait WikiControllerBase extends ControllerBase {
get("/:owner/:repository/wiki/_pages")(referrersOnly { repository => get("/:owner/:repository/wiki/_pages")(referrersOnly { repository =>
wiki.html.pages(getWikiPageList(repository.owner, repository.name), repository, wiki.html.pages(getWikiPageList(repository.owner, repository.name), repository,
hasWritePermission(repository.owner, repository.name, context.loginAccount), loadSystemSettings()) hasWritePermission(repository.owner, repository.name, context.loginAccount))
}) })
get("/:owner/:repository/wiki/_history")(referrersOnly { repository => get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
JGitUtil.getCommitLog(git, "master") match { JGitUtil.getCommitLog(git, "master") match {
case Right((logs, hasNext)) => wiki.html.history(None, logs, repository, loadSystemSettings()) case Right((logs, hasNext)) => wiki.html.history(None, logs, repository)
case Left(_) => NotFound case Left(_) => NotFound
} }
} }

View File

@@ -63,7 +63,8 @@ RepositorySearchService { self: IssuesService =>
val list = new ListBuffer[(String, String)] val list = new ListBuffer[(String, String)]
while (treeWalk.next()) { while (treeWalk.next()) {
if(treeWalk.getFileMode(0) == FileMode.REGULAR_FILE){ val mode = treeWalk.getFileMode(0)
if(mode == FileMode.REGULAR_FILE || mode == FileMode.EXECUTABLE_FILE){
JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).foreach { bytes => JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).foreach { bytes =>
if(FileUtil.isText(bytes)){ if(FileUtil.isText(bytes)){
val text = StringUtil.convertFromByteArray(bytes) val text = StringUtil.convertFromByteArray(bytes)

View File

@@ -294,7 +294,7 @@ object RepositoryService {
lazy val host = """^https?://(.+?)(:\d+)?/""".r.findFirstMatchIn(httpUrl).get.group(1) lazy val host = """^https?://(.+?)(:\d+)?/""".r.findFirstMatchIn(httpUrl).get.group(1)
def sshUrl(port: Int) = s"ssh://${host}:${port}/${owner}/${name}.git" def sshUrl(port: Int, userName: String) = s"ssh://${userName}@${host}:${port}/${owner}/${name}.git"
/** /**
* Creates instance with issue count and pull request count. * Creates instance with issue count and pull request count.

View File

@@ -1,7 +1,6 @@
package service package service
import model._ import model._
import service.SystemSettingsService.SystemSettings
/** /**
* This service is used for a view helper mainly. * This service is used for a view helper mainly.
@@ -9,28 +8,23 @@ import service.SystemSettingsService.SystemSettings
* It may be called many times in one request, so each method stores * It may be called many times in one request, so each method stores
* its result into the cache which available during a request. * its result into the cache which available during a request.
*/ */
trait RequestCache { trait RequestCache extends SystemSettingsService with AccountService with IssuesService {
def getSystemSettings()(implicit context: app.Context): SystemSettings =
context.cache("system_settings"){
new SystemSettingsService {}.loadSystemSettings()
}
def getIssue(userName: String, repositoryName: String, issueId: String)(implicit context: app.Context): Option[Issue] = { def getIssue(userName: String, repositoryName: String, issueId: String)(implicit context: app.Context): Option[Issue] = {
context.cache(s"issue.${userName}/${repositoryName}#${issueId}"){ context.cache(s"issue.${userName}/${repositoryName}#${issueId}"){
new IssuesService {}.getIssue(userName, repositoryName, issueId) super.getIssue(userName, repositoryName, issueId)
} }
} }
def getAccountByUserName(userName: String)(implicit context: app.Context): Option[Account] = { def getAccountByUserName(userName: String)(implicit context: app.Context): Option[Account] = {
context.cache(s"account.${userName}"){ context.cache(s"account.${userName}"){
new AccountService {}.getAccountByUserName(userName) super.getAccountByUserName(userName)
} }
} }
def getAccountByMailAddress(mailAddress: String)(implicit context: app.Context): Option[Account] = { def getAccountByMailAddress(mailAddress: String)(implicit context: app.Context): Option[Account] = {
context.cache(s"account.${mailAddress}"){ context.cache(s"account.${mailAddress}"){
new AccountService {}.getAccountByMailAddress(mailAddress) super.getAccountByMailAddress(mailAddress)
} }
} }
} }

View File

@@ -43,8 +43,8 @@ object WikiService {
def httpUrl(repository: RepositoryInfo) = repository.httpUrl.replaceFirst("\\.git\\Z", ".wiki.git") def httpUrl(repository: RepositoryInfo) = repository.httpUrl.replaceFirst("\\.git\\Z", ".wiki.git")
def sshUrl(repository: RepositoryInfo, settings: SystemSettingsService.SystemSettings) = def sshUrl(repository: RepositoryInfo, settings: SystemSettingsService.SystemSettings, userName: String) =
repository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort)).replaceFirst("\\.git\\Z", ".wiki.git") repository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), userName).replaceFirst("\\.git\\Z", ".wiki.git")
} }
trait WikiService { trait WikiService {

View File

@@ -98,7 +98,7 @@ object AutoUpdate {
*/ */
def getCurrentVersion(): Version = { def getCurrentVersion(): Version = {
if(versionFile.exists){ if(versionFile.exists){
FileUtils.readFileToString(versionFile, "UTF-8").split("\\.") match { FileUtils.readFileToString(versionFile, "UTF-8").trim.split("\\.") match {
case Array(majorVersion, minorVersion) => { case Array(majorVersion, minorVersion) => {
versions.find { v => versions.find { v =>
v.majorVersion == majorVersion.toInt && v.minorVersion == minorVersion.toInt v.majorVersion == majorVersion.toInt && v.minorVersion == minorVersion.toInt

View File

@@ -99,12 +99,16 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git => using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
commands.asScala.foreach { command => commands.asScala.foreach { command =>
logger.debug(s"commandType: ${command.getType}, refName: ${command.getRefName}") logger.debug(s"commandType: ${command.getType}, refName: ${command.getRefName}")
val commits = command.getType match { val refName = command.getRefName.split("/")
val branchName = refName.drop(2).mkString("/")
val commits = if (refName(1) == "tags") {
Nil
} else {
command.getType match {
case ReceiveCommand.Type.DELETE => Nil case ReceiveCommand.Type.DELETE => Nil
case _ => JGitUtil.getCommitLog(git, command.getOldId.name, command.getNewId.name) case _ => JGitUtil.getCommitLog(git, command.getOldId.name, command.getNewId.name)
} }
val refName = command.getRefName.split("/") }
val branchName = refName.drop(2).mkString("/")
// Extract new commit and apply issue comment // Extract new commit and apply issue comment
val newCommits = if(commits.size > 1000){ val newCommits = if(commits.size > 1000){

View File

@@ -76,11 +76,7 @@ object JGitUtil {
rev.getFullMessage, rev.getFullMessage,
rev.getParents().map(_.name).toList) rev.getParents().map(_.name).toList)
val summary = defining(fullMessage.trim.indexOf("\n")){ i => val summary = getSummaryMessage(fullMessage, shortMessage)
defining(if(i >= 0) fullMessage.trim.substring(0, i).trim else fullMessage){ firstLine =>
if(firstLine.length > shortMessage.length) shortMessage else firstLine
}
}
val description = defining(fullMessage.trim.indexOf("\n")){ i => val description = defining(fullMessage.trim.indexOf("\n")){ i =>
if(i >= 0){ if(i >= 0){
@@ -220,16 +216,18 @@ object JGitUtil {
val commits = getLatestCommitFromPaths(git, list.toList.map(_._3), revision) val commits = getLatestCommitFromPaths(git, list.toList.map(_._3), revision)
list.map { case (objectId, fileMode, path, name, linkUrl) => list.map { case (objectId, fileMode, path, name, linkUrl) =>
defining(commits(path)){ commit =>
FileInfo( FileInfo(
objectId, objectId,
fileMode == FileMode.TREE || fileMode == FileMode.GITLINK, fileMode == FileMode.TREE || fileMode == FileMode.GITLINK,
name, name,
commits(path).getCommitterIdent.getWhen, commit.getCommitterIdent.getWhen,
commits(path).getShortMessage, getSummaryMessage(commit.getFullMessage, commit.getShortMessage),
commits(path).getName, commit.getName,
commits(path).getCommitterIdent.getName, commit.getCommitterIdent.getName,
commits(path).getCommitterIdent.getEmailAddress, commit.getCommitterIdent.getEmailAddress,
linkUrl) linkUrl)
}
}.sortWith { (file1, file2) => }.sortWith { (file1, file2) =>
(file1.isDirectory, file2.isDirectory) match { (file1.isDirectory, file2.isDirectory) match {
case (true , false) => true case (true , false) => true
@@ -239,6 +237,17 @@ object JGitUtil {
}.toList }.toList
} }
/**
* Returns the first line of the commit message.
*/
private def getSummaryMessage(fullMessage: String, shortMessage: String): String = {
defining(fullMessage.trim.indexOf("\n")){ i =>
defining(if(i >= 0) fullMessage.trim.substring(0, i).trim else fullMessage){ firstLine =>
if(firstLine.length > shortMessage.length) shortMessage else firstLine
}
}
}
/** /**
* Returns the commit list of the specified branch. * Returns the commit list of the specified branch.
* *

View File

@@ -16,7 +16,7 @@ trait AvatarImageProvider { self: RequestCache =>
val src = if(mailAddress.isEmpty){ val src = if(mailAddress.isEmpty){
// by user name // by user name
getAccountByUserName(userName).map { account => getAccountByUserName(userName).map { account =>
if(account.image.isEmpty && getSystemSettings().gravatar){ if(account.image.isEmpty && context.settings.gravatar){
s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}""" s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}"""
} else { } else {
s"""${context.path}/${account.userName}/_avatar""" s"""${context.path}/${account.userName}/_avatar"""
@@ -27,13 +27,13 @@ trait AvatarImageProvider { self: RequestCache =>
} else { } else {
// by mail address // by mail address
getAccountByMailAddress(mailAddress).map { account => getAccountByMailAddress(mailAddress).map { account =>
if(account.image.isEmpty && getSystemSettings().gravatar){ if(account.image.isEmpty && context.settings.gravatar){
s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}""" s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}"""
} else { } else {
s"""${context.path}/${account.userName}/_avatar""" s"""${context.path}/${account.userName}/_avatar"""
} }
} getOrElse { } getOrElse {
if(getSystemSettings().gravatar){ if(context.settings.gravatar){
s"""https://www.gravatar.com/avatar/${StringUtil.md5(mailAddress.toLowerCase)}?s=${size}""" s"""https://www.gravatar.com/avatar/${StringUtil.md5(mailAddress.toLowerCase)}?s=${size}"""
} else { } else {
s"""${context.path}/_unknown/_avatar""" s"""${context.path}/_unknown/_avatar"""

View File

@@ -1,4 +1,4 @@
@(account: model.Account, settings: service.SystemSettingsService.SystemSettings, info: Option[Any])(implicit context: app.Context) @(account: model.Account, info: Option[Any])(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@import util.AccountUtil @import util.AccountUtil

View File

@@ -1,4 +1,4 @@
@(account: model.Account, settings: service.SystemSettingsService.SystemSettings, sshKeys: List[model.SshKey])(implicit context: app.Context) @(account: model.Account, sshKeys: List[model.SshKey])(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main("SSH Keys"){ @html.main("SSH Keys"){

View File

@@ -1,4 +1,4 @@
@(settings: service.SystemSettingsService.SystemSettings, info: Option[Any])(implicit context: app.Context) @(info: Option[Any])(implicit context: app.Context)
@import context._ @import context._
@import util.Directory._ @import util.Directory._
@import view.helpers._ @import view.helpers._

View File

@@ -1,6 +1,5 @@
@(activities: List[model.Activity], @(activities: List[model.Activity],
recentRepositories: List[service.RepositoryService.RepositoryInfo], recentRepositories: List[service.RepositoryService.RepositoryInfo],
systemSettings: service.SystemSettingsService.SystemSettings,
userRepositories: List[service.RepositoryService.RepositoryInfo])(implicit context: app.Context) userRepositories: List[service.RepositoryService.RepositoryInfo])(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@@ -12,7 +11,7 @@
</div> </div>
<div class="span4"> <div class="span4">
@if(loginAccount.isEmpty){ @if(loginAccount.isEmpty){
@signinform(systemSettings) @signinform(settings)
} else { } else {
<table class="table table-bordered"> <table class="table table-bordered">
<tr> <tr>

View File

@@ -33,7 +33,7 @@
<input type="hidden" name="milestoneId" value=""/> <input type="hidden" name="milestoneId" value=""/>
@helper.html.dropdown() { @helper.html.dropdown() {
<li><a href="javascript:void(0);" class="milestone" data-id=""><i class="icon-remove-circle"></i> No milestone</a></li> <li><a href="javascript:void(0);" class="milestone" data-id=""><i class="icon-remove-circle"></i> No milestone</a></li>
@milestones.map { milestone => @milestones.filter(_.closedDate.isEmpty).map { milestone =>
<li> <li>
<a href="javascript:void(0);" class="milestone" data-id="@milestone.milestoneId" data-title="@milestone.title"> <a href="javascript:void(0);" class="milestone" data-id="@milestone.milestoneId" data-title="@milestone.title">
<i class="icon-while"></i> @milestone.title <i class="icon-while"></i> @milestone.title

View File

@@ -54,7 +54,7 @@
@if(hasWritePermission){ @if(hasWritePermission){
@helper.html.dropdown() { @helper.html.dropdown() {
<li><a href="javascript:void(0);" class="milestone" data-id=""><i class="icon-remove-circle"></i> No milestone</a></li> <li><a href="javascript:void(0);" class="milestone" data-id=""><i class="icon-remove-circle"></i> No milestone</a></li>
@milestones.map { case (milestone, _, _) => @milestones.filter(_._1.closedDate.isEmpty).map { case (milestone, _, _) =>
<li> <li>
<a href="javascript:void(0);" class="milestone" data-id="@milestone.milestoneId" data-title="@milestone.title"> <a href="javascript:void(0);" class="milestone" data-id="@milestone.milestoneId" data-title="@milestone.title">
@helper.html.checkicon(Some(milestone.milestoneId) == issue.milestoneId) @milestone.title @helper.html.checkicon(Some(milestone.milestoneId) == issue.milestoneId) @milestone.title

View File

@@ -2,13 +2,12 @@
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo,
pathList: List[String], pathList: List[String],
content: util.JGitUtil.ContentInfo, content: util.JGitUtil.ContentInfo,
latestCommit: util.JGitUtil.CommitInfo, latestCommit: util.JGitUtil.CommitInfo)(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) { @html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
@html.header("code", repository) @html.header("code", repository)
@tab(branch, repository, "files", settings) @tab(branch, repository, "files")
<div class="head"> <div class="head">
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> / <a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
@pathList.zipWithIndex.map { case (section, i) => @pathList.zipWithIndex.map { case (section, i) =>

View File

@@ -1,12 +1,11 @@
@(branchInfo: Seq[(String, java.util.Date)], @(branchInfo: Seq[(String, java.util.Date)],
hasWritePermission: Boolean, hasWritePermission: Boolean,
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) { @html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
@html.header("code", repository) @html.header("code", repository)
@tab(repository.repository.defaultBranch, repository, "branches", settings, true) @tab(repository.repository.defaultBranch, repository, "branches", true)
<h1>Branches</h1> <h1>Branches</h1>
<table class="table table-bordered"> <table class="table table-bordered">
<tr> <tr>

View File

@@ -4,14 +4,13 @@
tags: List[String], tags: List[String],
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo,
diffs: Seq[util.JGitUtil.DiffInfo], diffs: Seq[util.JGitUtil.DiffInfo],
oldCommitId: Option[String], oldCommitId: Option[String])(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@import util.Implicits._ @import util.Implicits._
@html.main(commit.shortMessage, Some(repository)){ @html.main(commit.shortMessage, Some(repository)){
@html.header("code", repository) @html.header("code", repository)
@tab(commitId, repository, "commits", settings) @tab(commitId, repository, "commits")
<table class="table table-bordered"> <table class="table table-bordered">
<tr> <tr>
<th> <th>

View File

@@ -3,13 +3,12 @@
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo,
commits: Seq[Seq[util.JGitUtil.CommitInfo]], commits: Seq[Seq[util.JGitUtil.CommitInfo]],
page: Int, page: Int,
hasNext: Boolean, hasNext: Boolean)(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) { @html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
@html.header("code", repository) @html.header("code", repository)
@tab(branch, repository, if(pathList.isEmpty) "commits" else "files", settings) @tab(branch, repository, if(pathList.isEmpty) "commits" else "files")
<div class="head"> <div class="head">
@if(pathList.isEmpty){ @if(pathList.isEmpty){
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> / Commit History <a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> / Commit History

View File

@@ -3,13 +3,12 @@
pathList: List[String], pathList: List[String],
latestCommit: util.JGitUtil.CommitInfo, latestCommit: util.JGitUtil.CommitInfo,
files: List[util.JGitUtil.FileInfo], files: List[util.JGitUtil.FileInfo],
readme: Option[(util.JGitUtil.FileInfo, String)], readme: Option[(util.JGitUtil.FileInfo, String)])(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) { @html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
@html.header("code", repository) @html.header("code", repository)
@tab(branch, repository, "files", settings) @tab(branch, repository, "files")
<div class="head"> <div class="head">
<div class="pull-right"> <div class="pull-right">
@defining(repository.commitCount){ commitCount => @defining(repository.commitCount){ commitCount =>

View File

@@ -1,5 +1,4 @@
@(id: String, repository: service.RepositoryService.RepositoryInfo, active: String, @(id: String, repository: service.RepositoryService.RepositoryInfo, active: String,
settings: service.SystemSettingsService.SystemSettings,
hideBranchPulldown: Boolean = false)(implicit context: app.Context) hideBranchPulldown: Boolean = false)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@@ -24,7 +23,7 @@
<li@if(active=="tags" ){ class="active"}><a href="@url(repository)/tags">Tags@if(repository.tags.length > 0){ <span class="badge">@repository.tags.length</span>}</a></li> <li@if(active=="tags" ){ class="active"}><a href="@url(repository)/tags">Tags@if(repository.tags.length > 0){ <span class="badge">@repository.tags.length</span>}</a></li>
<li class="pull-right"> <li class="pull-right">
@helper.html.copy("repository-url-copy", repository.httpUrl, true){ @helper.html.copy("repository-url-copy", repository.httpUrl, true){
@if(settings.ssh){ @if(settings.ssh && loginAccount.isDefined){
<div class="btn-group add-on" data-toggle="buttons-radio" style="padding: 0px; border-width: 0px;"> <div class="btn-group add-on" data-toggle="buttons-radio" style="padding: 0px; border-width: 0px;">
<button type="button" class="btn active" id="repository-url-http">HTTP</button><button type="button" class="btn" id="repository-url-ssh">SSH</button> <button type="button" class="btn active" id="repository-url-http">HTTP</button><button type="button" class="btn" id="repository-url-ssh">SSH</button>
</div> </div>
@@ -40,7 +39,7 @@
</div> </div>
</li> </li>
</ul> </ul>
@if(settings.ssh){ @if(settings.ssh && loginAccount.isDefined){
<script> <script>
$(function(){ $(function(){
$('#repository-url-http').click(function(){ $('#repository-url-http').click(function(){
@@ -48,7 +47,7 @@ $(function(){
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
}); });
$('#repository-url-ssh').click(function(){ $('#repository-url-ssh').click(function(){
$('#repository-url').val('@repository.sshUrl(settings.sshPort.getOrElse(service.SystemSettingsService.DefaultSshPort))'); $('#repository-url').val('@repository.sshUrl(settings.sshPort.getOrElse(service.SystemSettingsService.DefaultSshPort), loginAccount.get.userName)');
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
}); });
}); });

View File

@@ -1,10 +1,9 @@
@(repository: service.RepositoryService.RepositoryInfo, @(repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) { @html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
@html.header("code", repository) @html.header("code", repository)
@tab(repository.repository.defaultBranch, repository, "tags", settings, true) @tab(repository.repository.defaultBranch, repository, "tags", true)
<h1>Tags</h1> <h1>Tags</h1>
<table class="table table-bordered"> <table class="table table-bordered">
<tr> <tr>

View File

@@ -1,7 +1,7 @@
@(systemSettings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context) @()(implicit context: app.Context)
@import context._ @import context._
@main("Sign in"){ @main("Sign in"){
<div class="signin-form"> <div class="signin-form">
@signinform(systemSettings) @signinform(settings)
</div> </div>
} }

View File

@@ -4,7 +4,6 @@
diffs: Seq[util.JGitUtil.DiffInfo], diffs: Seq[util.JGitUtil.DiffInfo],
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo,
hasWritePermission: Boolean, hasWritePermission: Boolean,
settings: service.SystemSettingsService.SystemSettings,
info: Option[Any])(implicit context: app.Context) info: Option[Any])(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@@ -12,7 +11,7 @@
@html.main(s"Compare Revisions - ${repository.owner}/${repository.name}", Some(repository)){ @html.main(s"Compare Revisions - ${repository.owner}/${repository.name}", Some(repository)){
@helper.html.information(info) @helper.html.information(info)
@html.header("wiki", repository) @html.header("wiki", repository)
@tab("history", repository, settings) @tab("history", repository)
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li> <li>
<h1 class="wiki-title"><span class="muted">Compare Revisions</span></h1> <h1 class="wiki-title"><span class="muted">Compare Revisions</span></h1>

View File

@@ -1,12 +1,11 @@
@(pageName: String, @(pageName: String,
page: Option[service.WikiService.WikiPageInfo], page: Option[service.WikiService.WikiPageInfo],
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"${if(pageName.isEmpty) "New Page" else pageName} - ${repository.owner}/${repository.name}", Some(repository)){ @html.main(s"${if(pageName.isEmpty) "New Page" else pageName} - ${repository.owner}/${repository.name}", Some(repository)){
@html.header("wiki", repository) @html.header("wiki", repository)
@tab("", repository, settings) @tab("", repository)
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li> <li>
<h1 class="wiki-title"><span class="muted">Editing</span> @if(pageName.isEmpty){New Page} else {@pageName}</h1> <h1 class="wiki-title"><span class="muted">Editing</span> @if(pageName.isEmpty){New Page} else {@pageName}</h1>

View File

@@ -1,12 +1,11 @@
@(pageName: Option[String], @(pageName: Option[String],
commits: List[util.JGitUtil.CommitInfo], commits: List[util.JGitUtil.CommitInfo],
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"History - ${repository.owner}/${repository.name}", Some(repository)){ @html.main(s"History - ${repository.owner}/${repository.name}", Some(repository)){
@html.header("wiki", repository) @html.header("wiki", repository)
@tab(if(pageName.isEmpty) "history" else "", repository, settings) @tab(if(pageName.isEmpty) "history" else "", repository)
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li> <li>
<h1 class="wiki-title"> <h1 class="wiki-title">

View File

@@ -1,13 +1,12 @@
@(pageName: String, @(pageName: String,
page: service.WikiService.WikiPageInfo, page: service.WikiService.WikiPageInfo,
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo,
hasWritePermission: Boolean, hasWritePermission: Boolean)(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"${pageName} - ${repository.owner}/${repository.name}", Some(repository)){ @html.main(s"${pageName} - ${repository.owner}/${repository.name}", Some(repository)){
@html.header("wiki", repository) @html.header("wiki", repository)
@tab((if(pageName == "Home") "home" else ""), repository, settings) @tab((if(pageName == "Home") "home" else ""), repository)
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li> <li>
<h1 class="wiki-title">@pageName</h1> <h1 class="wiki-title">@pageName</h1>

View File

@@ -1,12 +1,11 @@
@(pages: List[String], @(pages: List[String],
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo,
hasWritePermission: Boolean, hasWritePermission: Boolean)(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"Pages - ${repository.owner}/${repository.name}", Some(repository)){ @html.main(s"Pages - ${repository.owner}/${repository.name}", Some(repository)){
@html.header("wiki", repository) @html.header("wiki", repository)
@tab("pages", repository, settings) @tab("pages", repository)
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li> <li>
<h1 class="wiki-title"><span class="muted">Pages</span></h1> <h1 class="wiki-title"><span class="muted">Pages</span></h1>

View File

@@ -1,6 +1,5 @@
@(active: String, @(active: String,
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
settings: service.SystemSettingsService.SystemSettings)(implicit context: app.Context)
@import context._ @import context._
@import service.WikiService._ @import service.WikiService._
@import view.helpers._ @import view.helpers._
@@ -10,7 +9,7 @@
<li@if(active == "history"){ class="active"}><a href="@url(repository)/wiki/_history">Wiki History</a></li> <li@if(active == "history"){ class="active"}><a href="@url(repository)/wiki/_history">Wiki History</a></li>
<li class="pull-right"> <li class="pull-right">
@helper.html.copy("repository-url-copy", httpUrl(repository), true){ @helper.html.copy("repository-url-copy", httpUrl(repository), true){
@if(settings.ssh){ @if(settings.ssh && loginAccount.isDefined){
<div class="btn-group add-on" data-toggle="buttons-radio" style="padding: 0px; border-width: 0px;"> <div class="btn-group add-on" data-toggle="buttons-radio" style="padding: 0px; border-width: 0px;">
<button type="button" class="btn active" id="repository-url-http">HTTP</button><button type="button" class="btn" id="repository-url-ssh">SSH</button> <button type="button" class="btn active" id="repository-url-http">HTTP</button><button type="button" class="btn" id="repository-url-ssh">SSH</button>
</div> </div>
@@ -21,7 +20,7 @@
} }
</li> </li>
</ul> </ul>
@if(settings.ssh){ @if(settings.ssh && loginAccount.isDefined){
<script> <script>
$(function(){ $(function(){
$('#repository-url-http').click(function(){ $('#repository-url-http').click(function(){
@@ -29,7 +28,7 @@ $(function(){
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
}); });
$('#repository-url-ssh').click(function(){ $('#repository-url-ssh').click(function(){
$('#repository-url').val('@sshUrl(repository, settings)'); $('#repository-url').val('@sshUrl(repository, settings, loginAccount.get.userName)');
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val()); $('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
}); });
}); });

View File

@@ -458,10 +458,9 @@ ul#commit-file-list li.border {
li.L0, li.L1, li.L2, li.L3, li.L4, li.L5, li.L6, li.L7, li.L8, li.L9 { li.L0, li.L1, li.L2, li.L3, li.L4, li.L5, li.L6, li.L7, li.L8, li.L9 {
list-style-type: decimal; list-style-type: decimal;
background: white; background: white;
} border-left: 1px solid #E5E5E5;
padding-left: 10px;
li.L1, li.L3, li.L5, li.L7, li.L9 { color: rgba(0, 0, 0, 0.3);
background: #f5f5f5;
} }
pre.blob { pre.blob {

View File

@@ -34,3 +34,14 @@ $(function(){
// syntax highlighting by google-code-prettify // syntax highlighting by google-code-prettify
prettyPrint(); prettyPrint();
}); });
function displayErrors(data){
var i = 0;
$.each(data, function(key, value){
$('#error-' + key.split(".").join("_")).text(value);
if(i == 0){
$('#' + key).focus();
}
i++;
});
}

View File

@@ -35,12 +35,22 @@ table.diff {
table.diff tbody { table.diff tbody {
font-family:Courier, monospace font-family:Courier, monospace
} }
table.diff tbody tr:hover {
background-color:#F8EEC7;
}
table.diff tbody tr:hover th {
background-color:#F6E8B5;
}
table.diff tbody tr:hover td {
background-color:#F6E8B5;
}
table.diff tbody th { table.diff tbody th {
font-family:verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif; font-family:verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif;
background:#EED; background-color:#FBFBFB;
font-size:11px; font-size:11px;
font-weight:normal; font-weight:normal;
border:1px solid #BBC; border-top:none; /* for overriding bootstrap */
color:#886; color:#886;
padding:.3em .5em .1em 2em; padding:.3em .5em .1em 2em;
text-align:right; text-align:right;
@@ -58,6 +68,7 @@ table.diff tbody td {
padding:0px .4em; padding:0px .4em;
padding-top:.4em; padding-top:.4em;
vertical-align:top; vertical-align:top;
border-top: none;
} }
table.diff .empty { table.diff .empty {
background-color:#DDD; background-color:#DDD;
@@ -69,9 +80,10 @@ table.diff .delete {
background-color:#FFDDDD; background-color:#FFDDDD;
} }
table.diff .skip { table.diff .skip {
background-color:#EFEFEF; background-color: #F8F8FF;
border:1px solid #AAA; }
border-right:1px solid #BBC; table.diff .skip:before {
content: " ...";
} }
table.diff .insert { table.diff .insert {
background-color:#DDFFDD background-color:#DDFFDD

View File

@@ -10,53 +10,58 @@ import twirl.api.Html
class AvatarImageProviderSpec extends Specification { class AvatarImageProviderSpec extends Specification {
implicit val context = app.Context("", None, null)
"getAvatarImageHtml" should { "getAvatarImageHtml" should {
"show Gravatar image for no image account if gravatar integration is enabled" in { "show Gravatar image for no image account if gravatar integration is enabled" in {
val provider = new AvatarImageProviderImpl(Some(createAccount(None)), createSystemSettings(true)) implicit val context = app.Context(createSystemSettings(true), None, null)
val provider = new AvatarImageProviderImpl(Some(createAccount(None)))
provider.toHtml("user", 20).toString mustEqual provider.toHtml("user", 20).toString mustEqual
"<img src=\"https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s=20\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />" "<img src=\"https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s=20\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />"
} }
"show uploaded image even if gravatar integration is enabled" in { "show uploaded image even if gravatar integration is enabled" in {
val provider = new AvatarImageProviderImpl(Some(createAccount(Some("icon.png"))), createSystemSettings(true)) implicit val context = app.Context(createSystemSettings(true), None, null)
val provider = new AvatarImageProviderImpl(Some(createAccount(Some("icon.png"))))
provider.toHtml("user", 20).toString mustEqual provider.toHtml("user", 20).toString mustEqual
"<img src=\"/user/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />" "<img src=\"/user/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />"
} }
"show local image for no image account if gravatar integration is disabled" in { "show local image for no image account if gravatar integration is disabled" in {
val provider = new AvatarImageProviderImpl(Some(createAccount(None)), createSystemSettings(false)) implicit val context = app.Context(createSystemSettings(false), None, null)
val provider = new AvatarImageProviderImpl(Some(createAccount(None)))
provider.toHtml("user", 20).toString mustEqual provider.toHtml("user", 20).toString mustEqual
"<img src=\"/user/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />" "<img src=\"/user/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />"
} }
"show Gravatar image for specified mail address if gravatar integration is enabled" in { "show Gravatar image for specified mail address if gravatar integration is enabled" in {
val provider = new AvatarImageProviderImpl(None, createSystemSettings(true)) implicit val context = app.Context(createSystemSettings(true), None, null)
val provider = new AvatarImageProviderImpl(None)
provider.toHtml("user", 20, "hoge@hoge.com").toString mustEqual provider.toHtml("user", 20, "hoge@hoge.com").toString mustEqual
"<img src=\"https://www.gravatar.com/avatar/4712f9b0e63f56ad952ad387eaa23b9c?s=20\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />" "<img src=\"https://www.gravatar.com/avatar/4712f9b0e63f56ad952ad387eaa23b9c?s=20\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />"
} }
"show unknown image for unknown user if gravatar integration is enabled" in { "show unknown image for unknown user if gravatar integration is enabled" in {
val provider = new AvatarImageProviderImpl(None, createSystemSettings(true)) implicit val context = app.Context(createSystemSettings(true), None, null)
val provider = new AvatarImageProviderImpl(None)
provider.toHtml("user", 20).toString mustEqual provider.toHtml("user", 20).toString mustEqual
"<img src=\"/_unknown/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />" "<img src=\"/_unknown/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />"
} }
"show unknown image for specified mail address if gravatar integration is disabled" in { "show unknown image for specified mail address if gravatar integration is disabled" in {
val provider = new AvatarImageProviderImpl(None, createSystemSettings(false)) implicit val context = app.Context(createSystemSettings(false), None, null)
val provider = new AvatarImageProviderImpl(None)
provider.toHtml("user", 20, "hoge@hoge.com").toString mustEqual provider.toHtml("user", 20, "hoge@hoge.com").toString mustEqual
"<img src=\"/_unknown/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />" "<img src=\"/_unknown/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" />"
} }
"add tooltip if it's enabled" in { "add tooltip if it's enabled" in {
val provider = new AvatarImageProviderImpl(None, createSystemSettings(false)) implicit val context = app.Context(createSystemSettings(false), None, null)
val provider = new AvatarImageProviderImpl(None)
provider.toHtml("user", 20, "hoge@hoge.com", true).toString mustEqual provider.toHtml("user", 20, "hoge@hoge.com", true).toString mustEqual
"<img src=\"/_unknown/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" data-toggle=\"tooltip\" title=\"user\"/>" "<img src=\"/_unknown/_avatar\" class=\"avatar\" style=\"width: 20px; height: 20px;\" data-toggle=\"tooltip\" title=\"user\"/>"
@@ -80,7 +85,7 @@ class AvatarImageProviderSpec extends Specification {
private def createSystemSettings(useGravatar: Boolean) = private def createSystemSettings(useGravatar: Boolean) =
SystemSettings( SystemSettings(
baseUrl = None, baseUrl = Some(""),
allowAccountRegistration = false, allowAccountRegistration = false,
gravatar = useGravatar, gravatar = useGravatar,
notification = false, notification = false,
@@ -93,15 +98,13 @@ class AvatarImageProviderSpec extends Specification {
/** /**
* Adapter to test AvatarImageProviderImpl. * Adapter to test AvatarImageProviderImpl.
*/ */
class AvatarImageProviderImpl(account: Option[Account], settings: SystemSettings) class AvatarImageProviderImpl(account: Option[Account]) extends AvatarImageProvider with RequestCache {
extends AvatarImageProvider with RequestCache {
def toHtml(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false) def toHtml(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false)
(implicit context: app.Context): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip) (implicit context: app.Context): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip)
override def getAccountByMailAddress(mailAddress: String)(implicit context: app.Context): Option[Account] = account override def getAccountByMailAddress(mailAddress: String)(implicit context: app.Context): Option[Account] = account
override def getAccountByUserName(userName: String)(implicit context: app.Context): Option[Account] = account override def getAccountByUserName(userName: String)(implicit context: app.Context): Option[Account] = account
override def getSystemSettings()(implicit context: app.Context): SystemSettings = settings
} }
} }