update scalafmt

This commit is contained in:
xuwei-k
2023-11-16 12:12:33 +09:00
committed by kenji yoshida
parent 8cd7068131
commit 3d5ca44d66
96 changed files with 3065 additions and 3130 deletions

View File

@@ -1,12 +1,14 @@
version = "1.5.1" version = "3.7.15"
project.git = true project.git = true
maxColumn = 120 maxColumn = 120
docstrings = JavaDoc docstrings.style = keep
align.tokens = ["%", "%%", {code = "=>", owner = "Case"}] align.tokens = ["%", "%%", {code = "=>", owner = "Case"}]
align.openParenCallSite = false align.openParenCallSite = false
align.openParenDefnSite = false align.openParenDefnSite = false
continuationIndent.callSite = 2 continuationIndent.callSite = 2
continuationIndent.defnSite = 2 continuationIndent.defnSite = 2
danglingParentheses = true danglingParentheses.preset = true
runner.dialect = scala213source3
rewrite.trailingCommas.style = keep

View File

@@ -30,47 +30,47 @@ resolvers ++= Seq(
) )
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.eclipse.jgit" % "org.eclipse.jgit.http.server" % JgitVersion, "org.eclipse.jgit" % "org.eclipse.jgit.http.server" % JgitVersion,
"org.eclipse.jgit" % "org.eclipse.jgit.archive" % JgitVersion, "org.eclipse.jgit" % "org.eclipse.jgit.archive" % JgitVersion,
"org.scalatra" %% "scalatra-javax" % ScalatraVersion, "org.scalatra" %% "scalatra-javax" % ScalatraVersion,
"org.scalatra" %% "scalatra-json-javax" % ScalatraVersion, "org.scalatra" %% "scalatra-json-javax" % ScalatraVersion,
"org.scalatra" %% "scalatra-forms-javax" % ScalatraVersion, "org.scalatra" %% "scalatra-forms-javax" % ScalatraVersion,
"org.json4s" %% "json4s-jackson" % "4.0.6", "org.json4s" %% "json4s-jackson" % "4.0.6",
"commons-io" % "commons-io" % "2.15.0", "commons-io" % "commons-io" % "2.15.0",
"io.github.gitbucket" % "solidbase" % "1.0.5", "io.github.gitbucket" % "solidbase" % "1.0.5",
"io.github.gitbucket" % "markedj" % "1.0.18", "io.github.gitbucket" % "markedj" % "1.0.18",
"org.apache.commons" % "commons-compress" % "1.25.0", "org.apache.commons" % "commons-compress" % "1.25.0",
"org.apache.commons" % "commons-email" % "1.5", "org.apache.commons" % "commons-email" % "1.5",
"commons-net" % "commons-net" % "3.10.0", "commons-net" % "commons-net" % "3.10.0",
"org.apache.httpcomponents" % "httpclient" % "4.5.14", "org.apache.httpcomponents" % "httpclient" % "4.5.14",
"org.apache.sshd" % "apache-sshd" % "2.11.0" exclude ("org.slf4j", "slf4j-jdk14") exclude ("org.apache.sshd", "sshd-mina") exclude ("org.apache.sshd", "sshd-netty"), "org.apache.sshd" % "apache-sshd" % "2.11.0" exclude ("org.slf4j", "slf4j-jdk14") exclude ("org.apache.sshd", "sshd-mina") exclude ("org.apache.sshd", "sshd-netty"),
"org.apache.tika" % "tika-core" % "2.9.1", "org.apache.tika" % "tika-core" % "2.9.1",
"com.github.takezoe" %% "blocking-slick" % "0.0.14", "com.github.takezoe" %% "blocking-slick" % "0.0.14",
"com.novell.ldap" % "jldap" % "2009-10-07", "com.novell.ldap" % "jldap" % "2009-10-07",
"com.h2database" % "h2" % "1.4.199", "com.h2database" % "h2" % "1.4.199",
"org.mariadb.jdbc" % "mariadb-java-client" % "2.7.6", "org.mariadb.jdbc" % "mariadb-java-client" % "2.7.6",
"org.postgresql" % "postgresql" % "42.6.0", "org.postgresql" % "postgresql" % "42.6.0",
"ch.qos.logback" % "logback-classic" % "1.4.11", "ch.qos.logback" % "logback-classic" % "1.4.11",
"com.zaxxer" % "HikariCP" % "4.0.3" exclude ("org.slf4j", "slf4j-api"), "com.zaxxer" % "HikariCP" % "4.0.3" exclude ("org.slf4j", "slf4j-api"),
"com.typesafe" % "config" % "1.4.3", "com.typesafe" % "config" % "1.4.3",
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0", "fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0",
"io.github.java-diff-utils" % "java-diff-utils" % "4.12", "io.github.java-diff-utils" % "java-diff-utils" % "4.12",
"org.cache2k" % "cache2k-all" % "1.6.0.Final", "org.cache2k" % "cache2k-all" % "1.6.0.Final",
"net.coobird" % "thumbnailator" % "0.4.20", "net.coobird" % "thumbnailator" % "0.4.20",
"com.github.zafarkhaja" % "java-semver" % "0.9.0", "com.github.zafarkhaja" % "java-semver" % "0.9.0",
"com.nimbusds" % "oauth2-oidc-sdk" % "11.6", "com.nimbusds" % "oauth2-oidc-sdk" % "11.6",
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided", "org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided", "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
"junit" % "junit" % "4.13.2" % "test", "junit" % "junit" % "4.13.2" % "test",
"org.scalatra" %% "scalatra-scalatest-javax" % ScalatraVersion % "test", "org.scalatra" %% "scalatra-scalatest-javax" % ScalatraVersion % "test",
"org.mockito" % "mockito-core" % "5.7.0" % "test", "org.mockito" % "mockito-core" % "5.7.0" % "test",
"com.dimafeng" %% "testcontainers-scala" % "0.41.0" % "test", "com.dimafeng" %% "testcontainers-scala" % "0.41.0" % "test",
"org.testcontainers" % "mysql" % "1.19.2" % "test", "org.testcontainers" % "mysql" % "1.19.2" % "test",
"org.testcontainers" % "postgresql" % "1.19.2" % "test", "org.testcontainers" % "postgresql" % "1.19.2" % "test",
"net.i2p.crypto" % "eddsa" % "0.3.0", "net.i2p.crypto" % "eddsa" % "0.3.0",
"is.tagomor.woothee" % "woothee-java" % "1.11.0", "is.tagomor.woothee" % "woothee-java" % "1.11.0",
"org.ec4j.core" % "ec4j-core" % "0.3.0", "org.ec4j.core" % "ec4j-core" % "0.3.0",
"org.kohsuke" % "github-api" % "1.317" % "test" "org.kohsuke" % "github-api" % "1.317" % "test"
) )
// Compiler settings // Compiler settings
@@ -115,8 +115,8 @@ assembly / assemblyMergeStrategy := {
// Exclude a war file from published artifacts // Exclude a war file from published artifacts
signedArtifacts := { signedArtifacts := {
signedArtifacts.value.filterNot { signedArtifacts.value.filterNot { case (_, file) =>
case (_, file) => file.getName.endsWith(".war") || file.getName.endsWith(".war.asc") file.getName.endsWith(".war") || file.getName.endsWith(".war.asc")
} }
} }
@@ -205,10 +205,9 @@ executableKey := {
"md5" -> "MD5", "md5" -> "MD5",
"sha1" -> "SHA-1", "sha1" -> "SHA-1",
"sha256" -> "SHA-256" "sha256" -> "SHA-256"
).foreach { ).foreach { case (extension, algorithm) =>
case (extension, algorithm) => val checksumFile = workDir / (warName + "." + extension)
val checksumFile = workDir / (warName + "." + extension) Checksums generate (outputFile, checksumFile, algorithm)
Checksums generate (outputFile, checksumFile, algorithm)
} }
// done // done
@@ -289,5 +288,5 @@ Jetty / javaOptions ++= Seq(
"-Xdebug", "-Xdebug",
"-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000",
"-Dorg.eclipse.jetty.annotations.AnnotationParser.LEVEL=OFF", "-Dorg.eclipse.jetty.annotations.AnnotationParser.LEVEL=OFF",
//"-Ddev-features=keep-session" // "-Ddev-features=keep-session"
) )

View File

@@ -76,21 +76,20 @@ object GitBucketCoreModule
import JDBCUtil._ import JDBCUtil._
val conn = context.get(Solidbase.CONNECTION).asInstanceOf[Connection] val conn = context.get(Solidbase.CONNECTION).asInstanceOf[Connection]
val list = conn.select("SELECT * FROM ACTIVITY ORDER BY ACTIVITY_ID") { val list = conn.select("SELECT * FROM ACTIVITY ORDER BY ACTIVITY_ID") { rs =>
rs => Activity(
Activity( activityId = UUID.randomUUID().toString,
activityId = UUID.randomUUID().toString, userName = rs.getString("USER_NAME"),
userName = rs.getString("USER_NAME"), repositoryName = rs.getString("REPOSITORY_NAME"),
repositoryName = rs.getString("REPOSITORY_NAME"), activityUserName = rs.getString("ACTIVITY_USER_NAME"),
activityUserName = rs.getString("ACTIVITY_USER_NAME"), activityType = rs.getString("ACTIVITY_TYPE"),
activityType = rs.getString("ACTIVITY_TYPE"), message = rs.getString("MESSAGE"),
message = rs.getString("MESSAGE"), additionalInfo = {
additionalInfo = { val additionalInfo = rs.getString("ADDITIONAL_INFO")
val additionalInfo = rs.getString("ADDITIONAL_INFO") if (rs.wasNull()) None else Some(additionalInfo)
if (rs.wasNull()) None else Some(additionalInfo) },
}, activityDate = rs.getTimestamp("ACTIVITY_DATE")
activityDate = rs.getTimestamp("ACTIVITY_DATE") )
)
} }
Using.resource(new FileOutputStream(ActivityLog, true)) { out => Using.resource(new FileOutputStream(ActivityLog, true)) { out =>
list.foreach { activity => list.foreach { activity =>

View File

@@ -66,16 +66,17 @@ object ApiBranchProtection {
} }
} }
implicit val enforcementLevelSerializer: CustomSerializer[EnforcementLevel] = new CustomSerializer[EnforcementLevel]( implicit val enforcementLevelSerializer: CustomSerializer[EnforcementLevel] =
format => new CustomSerializer[EnforcementLevel](format =>
( (
{ {
case JString("off") => Off case JString("off") => Off
case JString("non_admins") => NonAdmins case JString("non_admins") => NonAdmins
case JString("everyone") => Everyone case JString("everyone") => Everyone
}, { },
case x: EnforcementLevel => JString(x.name) { case x: EnforcementLevel =>
JString(x.name)
} }
)
) )
)
} }

View File

@@ -14,7 +14,8 @@ case class ApiComment(id: Int, user: ApiUser, body: String, created_at: Date, up
isPullRequest: Boolean isPullRequest: Boolean
) { ) {
val html_url = ApiPath( val html_url = ApiPath(
s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" } else { "issues" }}/${issueId}#comment-${id}" s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" }
else { "issues" }}/${issueId}#comment-${id}"
) )
} }

View File

@@ -69,7 +69,8 @@ object ApiCommits {
} }
File( File(
filename = if (diff.changeType == ChangeType.DELETE) { diff.oldPath } else { diff.newPath }, filename = if (diff.changeType == ChangeType.DELETE) { diff.oldPath }
else { diff.newPath },
additions = additions, additions = additions,
deletions = deletions, deletions = deletions,
changes = additions + deletions, changes = additions + deletions,
@@ -106,7 +107,9 @@ object ApiCommits {
message = commitInfo.shortMessage, message = commitInfo.shortMessage,
comment_count = commentCount, comment_count = commentCount,
tree = Tree( tree = Tree(
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/tree/${commitInfo.id}"), // TODO This endpoint has not been implemented yet. url = ApiPath(
s"/api/v3/repos/${repositoryName.fullName}/tree/${commitInfo.id}"
), // TODO This endpoint has not been implemented yet.
sha = commitInfo.id sha = commitInfo.id
) )
), ),
@@ -114,7 +117,9 @@ object ApiCommits {
committer = ApiUser(committer), committer = ApiUser(committer),
parents = commitInfo.parents.map { parent => parents = commitInfo.parents.map { parent =>
Tree( Tree(
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/tree/${parent}"), // TODO This endpoint has not been implemented yet. url = ApiPath(
s"/api/v3/repos/${repositoryName.fullName}/tree/${parent}"
), // TODO This endpoint has not been implemented yet.
sha = parent sha = parent
) )
}, },

View File

@@ -22,16 +22,15 @@ object ApiContents {
ApiContents("dir", fileInfo.name, fileInfo.path, fileInfo.commitId, None, None)(repositoryName) ApiContents("dir", fileInfo.name, fileInfo.path, fileInfo.commitId, None, None)(repositoryName)
} else { } else {
content content
.map( .map(arr =>
arr => ApiContents(
ApiContents( "file",
"file", fileInfo.name,
fileInfo.name, fileInfo.path,
fileInfo.path, fileInfo.id.getName,
fileInfo.id.getName, Some(Base64.getEncoder.encodeToString(arr)),
Some(Base64.getEncoder.encodeToString(arr)), Some("base64")
Some("base64") )(repositoryName)
)(repositoryName)
) )
.getOrElse(ApiContents("file", fileInfo.name, fileInfo.path, fileInfo.commitId, None, None)(repositoryName)) .getOrElse(ApiContents("file", fileInfo.name, fileInfo.path, fileInfo.commitId, None, None)(repositoryName))
} }

View File

@@ -23,7 +23,8 @@ case class ApiIssue(
val id = 0 // dummy id val id = 0 // dummy id
val assignee = assignees.headOption val assignee = assignees.headOption
val comments_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/issues/${number}/comments") val comments_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/issues/${number}/comments")
val html_url = ApiPath(s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" } else { "issues" }}/${number}") val html_url = ApiPath(s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" }
else { "issues" }}/${number}")
val pull_request = if (isPullRequest) { val pull_request = if (isPullRequest) {
Some( Some(
Map( Map(
@@ -54,7 +55,8 @@ object ApiIssue {
assignees = assignees, assignees = assignees,
labels = labels, labels = labels,
milestone = milestone, milestone = milestone,
state = if (issue.closed) { "closed" } else { "open" }, state = if (issue.closed) { "closed" }
else { "open" },
body = issue.content.getOrElse(""), body = issue.content.getOrElse(""),
created_at = issue.registeredDate, created_at = issue.registeredDate,
updated_at = issue.updatedDate updated_at = issue.updatedDate

View File

@@ -27,10 +27,10 @@ case class ApiPullRequest(
val id = 0 // dummy id val id = 0 // dummy id
val assignee = assignees.headOption val assignee = assignees.headOption
val html_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}") val html_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}")
//val diff_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}.diff") // val diff_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}.diff")
//val patch_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}.patch") // val patch_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}.patch")
val url = ApiPath(s"${base.repo.url.path}/pulls/${number}") val url = ApiPath(s"${base.repo.url.path}/pulls/${number}")
//val issue_url = ApiPath(s"${base.repo.url.path}/issues/${number}") // val issue_url = ApiPath(s"${base.repo.url.path}/issues/${number}")
val commits_url = ApiPath(s"${base.repo.url.path}/pulls/${number}/commits") val commits_url = ApiPath(s"${base.repo.url.path}/pulls/${number}/commits")
val review_comments_url = ApiPath(s"${base.repo.url.path}/pulls/${number}/comments") val review_comments_url = ApiPath(s"${base.repo.url.path}/pulls/${number}/comments")
val review_comment_url = ApiPath(s"${base.repo.url.path}/pulls/comments/{number}") val review_comment_url = ApiPath(s"${base.repo.url.path}/pulls/comments/{number}")
@@ -69,7 +69,8 @@ object ApiPullRequest {
) )
case class Commit(sha: String, ref: String, repo: ApiRepository)(baseOwner: String) { case class Commit(sha: String, ref: String, repo: ApiRepository)(baseOwner: String) {
val label = if (baseOwner == repo.owner.login) { ref } else { s"${repo.owner.login}:${ref}" } val label = if (baseOwner == repo.owner.login) { ref }
else { s"${repo.owner.login}:${ref}" }
val user = repo.owner val user = repo.owner
} }

View File

@@ -40,12 +40,12 @@ object ApiRef {
ApiRef( ApiRef(
ref = s"refs/tags/${tagInfo.name}", ref = s"refs/tags/${tagInfo.name}",
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/git/refs/tags/${tagInfo.name}"), url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/git/refs/tags/${tagInfo.name}"),
//the GH api distinguishes between "releases" and plain git tags // the GH api distinguishes between "releases" and plain git tags
//for "releases", the api returns a reference to the release object (with type `tag`) // for "releases", the api returns a reference to the release object (with type `tag`)
//this would be something like s"/api/v3/repos/${repositoryName.fullName}/git/tags/<hash-of-tag>" // this would be something like s"/api/v3/repos/${repositoryName.fullName}/git/tags/<hash-of-tag>"
//with a hash for the tag, which I do not fully understand // with a hash for the tag, which I do not fully understand
//since this is not yet implemented in GB, we always return a link to the plain `commit` object, // since this is not yet implemented in GB, we always return a link to the plain `commit` object,
//which GH does for tags that are not annotated // which GH does for tags that are not annotated
`object` = ApiRefCommit( `object` = ApiRefCommit(
sha = tagInfo.objectId, sha = tagInfo.objectId,
url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${tagInfo.objectId}"), url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${tagInfo.objectId}"),

View File

@@ -24,7 +24,8 @@ object ApiUser {
def apply(user: Account): ApiUser = ApiUser( def apply(user: Account): ApiUser = ApiUser(
login = user.userName, login = user.userName,
email = user.mailAddress, email = user.mailAddress,
`type` = if (user.isGroupAccount) { "Organization" } else { "User" }, `type` = if (user.isGroupAccount) { "Organization" }
else { "User" },
site_admin = user.isAdmin, site_admin = user.isAdmin,
created_at = user.registeredDate created_at = user.registeredDate
) )

View File

@@ -15,13 +15,12 @@ object JsonFormat {
val parserISO = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") val parserISO = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
val jsonFormats = Serialization.formats(NoTypeHints) + new CustomSerializer[Date]( val jsonFormats = Serialization.formats(NoTypeHints) + new CustomSerializer[Date](format =>
format => (
( { case JString(s) =>
{ Try(Date.from(Instant.parse(s))).getOrElse(throw new MappingException("Can't convert " + s + " to Date"))
case JString(s) => },
Try(Date.from(Instant.parse(s))).getOrElse(throw new MappingException("Can't convert " + s + " to Date")) { case x: Date => JString(OffsetDateTime.ofInstant(x.toInstant, ZoneId.of("UTC")).format(parserISO)) }
}, { case x: Date => JString(OffsetDateTime.ofInstant(x.toInstant, ZoneId.of("UTC")).format(parserISO)) }
) )
) + FieldSerializer[ApiUser]() + ) + FieldSerializer[ApiUser]() +
FieldSerializer[ApiGroup]() + FieldSerializer[ApiGroup]() +
@@ -48,29 +47,32 @@ object JsonFormat {
ApiBranchProtection.enforcementLevelSerializer ApiBranchProtection.enforcementLevelSerializer
def apiPathSerializer(c: Context) = def apiPathSerializer(c: Context) =
new CustomSerializer[ApiPath]( new CustomSerializer[ApiPath](_ =>
_ => (
({ {
case JString(s) if s.startsWith(c.baseUrl) => ApiPath(s.substring(c.baseUrl.length)) case JString(s) if s.startsWith(c.baseUrl) => ApiPath(s.substring(c.baseUrl.length))
case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath") case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath")
}, { },
case ApiPath(path) => JString(c.baseUrl + path) { case ApiPath(path) =>
}) JString(c.baseUrl + path)
}
)
) )
def sshPathSerializer(c: Context) = def sshPathSerializer(c: Context) =
new CustomSerializer[SshPath]( new CustomSerializer[SshPath](_ =>
_ => (
({ {
case JString(s) if c.sshUrl.exists(sshUrl => s.startsWith(sshUrl)) => case JString(s) if c.sshUrl.exists(sshUrl => s.startsWith(sshUrl)) =>
SshPath(s.substring(c.sshUrl.get.length)) SshPath(s.substring(c.sshUrl.get.length))
case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath") case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath")
}, { },
case SshPath(path) => { case SshPath(path) =>
c.sshUrl.map { sshUrl => c.sshUrl.map { sshUrl =>
JString(sshUrl + path) JString(sshUrl + path)
} getOrElse JNothing } getOrElse JNothing
}) }
)
) )
/** /**

View File

@@ -215,9 +215,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
"events" -> accountWebhookEvents, "events" -> accountWebhookEvents,
"ctype" -> label("ctype", text()), "ctype" -> label("ctype", text()),
"token" -> optional(trim(label("token", text(maxlength(100))))) "token" -> optional(trim(label("token", text(maxlength(100)))))
)( )((url, events, ctype, token) => AccountWebHookForm(url, events, WebHookContentType.valueOf(ctype), token))
(url, events, ctype, token) => AccountWebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
)
/** /**
* Provides duplication check for web hook url. duplicated from RepositorySettingsController.scala * Provides duplication check for web hook url. duplicated from RepositorySettingsController.scala
@@ -340,22 +338,21 @@ trait AccountControllerBase extends AccountManagementControllerBase {
post("/:userName/_edit", editForm)(oneselfOnly { form => post("/:userName/_edit", editForm)(oneselfOnly { form =>
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName).map { getAccountByUserName(userName).map { account =>
account => updateAccount(
updateAccount( account.copy(
account.copy( password = form.password.map(pbkdf2_sha256).getOrElse(account.password),
password = form.password.map(pbkdf2_sha256).getOrElse(account.password), fullName = form.fullName,
fullName = form.fullName, mailAddress = form.mailAddress,
mailAddress = form.mailAddress, description = form.description,
description = form.description, url = form.url
url = form.url
)
) )
)
updateImage(userName, form.fileId, form.clearImage) updateImage(userName, form.fileId, form.clearImage)
updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != "")) updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
flash.update("info", "Account information has been updated.") flash.update("info", "Account information has been updated.")
redirect(s"/${userName}/_edit") redirect(s"/${userName}/_edit")
} getOrElse NotFound() } getOrElse NotFound()
}) })
@@ -363,12 +360,11 @@ trait AccountControllerBase extends AccountManagementControllerBase {
get("/:userName/_delete")(oneselfOnly { get("/:userName/_delete")(oneselfOnly {
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName, true).map { getAccountByUserName(userName, true).map { account =>
account => if (isLastAdministrator(account)) {
if (isLastAdministrator(account)) { flash.update("error", "Account can't be removed because this is last one administrator.")
flash.update("error", "Account can't be removed because this is last one administrator.") redirect(s"/${userName}/_edit")
redirect(s"/${userName}/_edit") } else {
} else {
// // Remove repositories // // Remove repositories
// getRepositoryNamesOfUser(userName).foreach { repositoryName => // getRepositoryNamesOfUser(userName).foreach { repositoryName =>
// deleteRepository(userName, repositoryName) // deleteRepository(userName, repositoryName)
@@ -376,10 +372,10 @@ trait AccountControllerBase extends AccountManagementControllerBase {
// FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName)) // FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName)) // FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
// } // }
suspendAccount(account) suspendAccount(account)
session.invalidate session.invalidate
redirect("/") redirect("/")
} }
} getOrElse NotFound() } getOrElse NotFound()
}) })
@@ -406,7 +402,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
get("/:userName/_gpg")(oneselfOnly { get("/:userName/_gpg")(oneselfOnly {
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName).map { x => getAccountByUserName(userName).map { x =>
//html.ssh(x, getPublicKeys(x.userName)) // html.ssh(x, getPublicKeys(x.userName))
html.gpg(x, getGpgPublicKeys(x.userName)) html.gpg(x, getGpgPublicKeys(x.userName))
} getOrElse NotFound() } getOrElse NotFound()
}) })
@@ -525,9 +521,8 @@ trait AccountControllerBase extends AccountManagementControllerBase {
get("/:userName/_hooks/edit")(managersOnly { get("/:userName/_hooks/edit")(managersOnly {
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName).flatMap { account => getAccountByUserName(userName).flatMap { account =>
getAccountWebHook(userName, params("url")).map { getAccountWebHook(userName, params("url")).map { case (webhook, events) =>
case (webhook, events) => html.edithook(webhook, events, account, false)
html.edithook(webhook, events, account, false)
} }
} getOrElse NotFound() } getOrElse NotFound()
}) })
@@ -584,11 +579,10 @@ trait AccountControllerBase extends AccountManagementControllerBase {
"url" -> url, "url" -> url,
"request" -> Await.result( "request" -> Await.result(
reqFuture reqFuture
.map( .map(req =>
req => Map(
Map( "headers" -> _headers(req.getAllHeaders),
"headers" -> _headers(req.getAllHeaders), "payload" -> json
"payload" -> json
) )
) )
.recover(toErrorMap), .recover(toErrorMap),
@@ -596,12 +590,11 @@ trait AccountControllerBase extends AccountManagementControllerBase {
), ),
"response" -> Await.result( "response" -> Await.result(
resFuture resFuture
.map( .map(res =>
res => Map(
Map( "status" -> res.getStatusLine(),
"status" -> res.getStatusLine(), "body" -> EntityUtils.toString(res.getEntity()),
"body" -> EntityUtils.toString(res.getEntity()), "headers" -> _headers(res.getAllHeaders())
"headers" -> _headers(res.getAllHeaders())
) )
) )
.recover(toErrorMap), .recover(toErrorMap),
@@ -788,83 +781,83 @@ trait AccountControllerBase extends AccountManagementControllerBase {
* Create new repository. * Create new repository.
*/ */
post("/new", newRepositoryForm)(usersOnly { form => post("/new", newRepositoryForm)(usersOnly { form =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (context.settings.basicBehavior.repositoryOperation.create || loginAccount.isAdmin) {
if (context.settings.basicBehavior.repositoryOperation.create || loginAccount.isAdmin) { LockUtil.lock(s"${form.owner}/${form.name}") {
LockUtil.lock(s"${form.owner}/${form.name}") { if (getRepository(form.owner, form.name).isDefined) {
if (getRepository(form.owner, form.name).isDefined) { // redirect to the repository if repository already exists
// redirect to the repository if repository already exists redirect(s"/${form.owner}/${form.name}")
redirect(s"/${form.owner}/${form.name}") } else if (!canCreateRepository(form.owner, loginAccount)) {
} else if (!canCreateRepository(form.owner, loginAccount)) { // Permission error
// Permission error Forbidden()
Forbidden() } else {
} else { // create repository asynchronously
// create repository asynchronously createRepository(
createRepository( loginAccount,
loginAccount, form.owner,
form.owner, form.name,
form.name, form.description,
form.description, form.isPrivate,
form.isPrivate, form.initOption,
form.initOption, form.sourceUrl,
form.sourceUrl, context.settings.defaultBranch
context.settings.defaultBranch )
) // redirect to the repository
// redirect to the repository redirect(s"/${form.owner}/${form.name}")
redirect(s"/${form.owner}/${form.name}")
}
} }
} else Forbidden() }
} else Forbidden()
} }
}) })
get("/:owner/:repository/fork")(readableUsersOnly { repository => get("/:owner/:repository/fork")(readableUsersOnly { repository =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (
if (repository.repository.options.allowFork && (context.settings.basicBehavior.repositoryOperation.fork || loginAccount.isAdmin)) { repository.repository.options.allowFork && (context.settings.basicBehavior.repositoryOperation.fork || loginAccount.isAdmin)
val loginUserName = loginAccount.userName ) {
val groups = getGroupsByUserName(loginUserName) val loginUserName = loginAccount.userName
groups match { val groups = getGroupsByUserName(loginUserName)
case _: List[String] => groups match {
val managerPermissions = groups.map { group => case _: List[String] =>
val members = getGroupMembers(group) val managerPermissions = groups.map { group =>
context.loginAccount.exists( val members = getGroupMembers(group)
x => context.loginAccount.exists(x =>
members.exists { member => members.exists { member =>
member.userName == x.userName && member.isManager member.userName == x.userName && member.isManager
} }
)
}
helper.html.forkrepository(
repository,
(groups zip managerPermissions).sortBy(_._1)
) )
case _ => redirect(s"/${loginUserName}") }
} helper.html.forkrepository(
} else BadRequest() repository,
(groups zip managerPermissions).sortBy(_._1)
)
case _ => redirect(s"/${loginUserName}")
}
} else BadRequest()
} }
}) })
post("/:owner/:repository/fork", accountForm)(readableUsersOnly { (form, repository) => post("/:owner/:repository/fork", accountForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (
if (repository.repository.options.allowFork && (context.settings.basicBehavior.repositoryOperation.fork || loginAccount.isAdmin)) { repository.repository.options.allowFork && (context.settings.basicBehavior.repositoryOperation.fork || loginAccount.isAdmin)
val loginUserName = loginAccount.userName ) {
val accountName = form.accountName val loginUserName = loginAccount.userName
val accountName = form.accountName
if (getRepository(accountName, repository.name).isDefined) { if (getRepository(accountName, repository.name).isDefined) {
// redirect to the repository if repository already exists // redirect to the repository if repository already exists
redirect(s"/${accountName}/${repository.name}") redirect(s"/${accountName}/${repository.name}")
} else if (!canCreateRepository(accountName, loginAccount)) { } else if (!canCreateRepository(accountName, loginAccount)) {
// Permission error // Permission error
Forbidden() Forbidden()
} else { } else {
// fork repository asynchronously // fork repository asynchronously
forkRepository(accountName, repository, loginUserName) forkRepository(accountName, repository, loginUserName)
// redirect to the repository // redirect to the repository
redirect(s"/${accountName}/${repository.name}") redirect(s"/${accountName}/${repository.name}")
} }
} else Forbidden() } else Forbidden()
} }
}) })
@@ -891,9 +884,11 @@ trait AccountControllerBase extends AccountManagementControllerBase {
private def members: Constraint = new Constraint() { private def members: Constraint = new Constraint() {
override def validate(name: String, value: String, messages: Messages): Option[String] = { override def validate(name: String, value: String, messages: Messages): Option[String] = {
if (value.split(",").exists { if (
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean } value.split(",").exists {
}) None _.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
}
) None
else Some("Must select one manager at least.") else Some("Must select one manager at least.")
} }
} }

View File

@@ -423,10 +423,11 @@ trait AccountManagementControllerBase extends ControllerBase {
messages: Messages messages: Messages
): Option[String] = { ): Option[String] = {
val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses")) val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses"))
if (extraMailAddresses.exists { if (
case (k, v) => extraMailAddresses.exists { case (k, v) =>
v.contains(value) v.contains(value)
}) { }
) {
Some("These mail addresses are duplicated.") Some("These mail addresses are duplicated.")
} else { } else {
getAccountByMailAddress(value, true) getAccountByMailAddress(value, true)
@@ -446,10 +447,11 @@ trait AccountManagementControllerBase extends ControllerBase {
messages: Messages messages: Messages
): Option[String] = { ): Option[String] = {
val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses")) val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses"))
if (Some(value) == params.optionValue("mailAddress") || extraMailAddresses.count { if (
case (k, v) => Some(value) == params.optionValue("mailAddress") || extraMailAddresses.count { case (k, v) =>
v.contains(value) v.contains(value)
} > 1) { } > 1
) {
Some("These mail addresses are duplicated.") Some("These mail addresses are duplicated.")
} else { } else {
getAccountByMailAddress(value, true) getAccountByMailAddress(value, true)

View File

@@ -75,59 +75,57 @@ class FileUploadController
post("/wiki/:owner/:repository") { post("/wiki/:owner/:repository") {
setMultipartConfig() setMultipartConfig()
// Don't accept not logged-in users // Don't accept not logged-in users
session.get(Keys.Session.LoginAccount).collect { session.get(Keys.Session.LoginAccount).collect { case loginAccount: Account =>
case loginAccount: Account => val owner = params("owner")
val owner = params("owner") val repository = params("repository")
val repository = params("repository")
// Check whether logged-in user is collaborator // Check whether logged-in user is collaborator
onlyWikiEditable(owner, repository, loginAccount) { onlyWikiEditable(owner, repository, loginAccount) {
execute( execute(
{ (file, fileId) => { (file, fileId) =>
val fileName = file.getName val fileName = file.getName
LockUtil.lock(s"${owner}/${repository}/wiki") { LockUtil.lock(s"${owner}/${repository}/wiki") {
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
git => val builder = DirCache.newInCore.builder()
val builder = DirCache.newInCore.builder() val inserter = git.getRepository.newObjectInserter()
val inserter = git.getRepository.newObjectInserter() val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
if (headId != null) { if (headId != null) {
JGitUtil.processTree(git, headId) { (path, tree) => JGitUtil.processTree(git, headId) { (path, tree) =>
if (path != fileName) { if (path != fileName) {
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId)) builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
}
}
} }
}
val bytes = IOUtils.toByteArray(file.getInputStream)
builder.add(
JGitUtil.createDirCacheEntry(
fileName,
FileMode.REGULAR_FILE,
inserter.insert(Constants.OBJ_BLOB, bytes)
)
)
builder.finish()
val newHeadId = JGitUtil.createNewCommit(
git,
inserter,
headId,
builder.getDirCache.writeTree(inserter),
Constants.HEAD,
loginAccount.fullName,
loginAccount.mailAddress,
s"Uploaded ${fileName}"
)
fileName
} }
val bytes = IOUtils.toByteArray(file.getInputStream)
builder.add(
JGitUtil.createDirCacheEntry(
fileName,
FileMode.REGULAR_FILE,
inserter.insert(Constants.OBJ_BLOB, bytes)
)
)
builder.finish()
val newHeadId = JGitUtil.createNewCommit(
git,
inserter,
headId,
builder.getDirCache.writeTree(inserter),
Constants.HEAD,
loginAccount.fullName,
loginAccount.mailAddress,
s"Uploaded ${fileName}"
)
fileName
} }
}, }
_ => true },
) _ => true
} )
}
} getOrElse BadRequest() } getOrElse BadRequest()
} }
@@ -135,20 +133,19 @@ class FileUploadController
setMultipartConfigForLargeFile() setMultipartConfigForLargeFile()
session session
.get(Keys.Session.LoginAccount) .get(Keys.Session.LoginAccount)
.collect { .collect { case _: Account =>
case _: Account => val owner = params("owner")
val owner = params("owner") val repository = params("repository")
val repository = params("repository") val tag = multiParams("splat").head
val tag = multiParams("splat").head execute(
execute( { (file, fileId) =>
{ (file, fileId) => FileUtils.writeByteArrayToFile(
FileUtils.writeByteArrayToFile( new File(getReleaseFilesDir(owner, repository), FileUtil.checkFilename(tag + "/" + fileId)),
new File(getReleaseFilesDir(owner, repository), FileUtil.checkFilename(tag + "/" + fileId)), file.get()
file.get() )
) },
}, _ => true
_ => true )
)
} }
.getOrElse(BadRequest()) .getOrElse(BadRequest())
} }
@@ -158,9 +155,12 @@ class FileUploadController
setMultipartConfig() setMultipartConfig()
session.get(Keys.Session.LoginAccount).collect { session.get(Keys.Session.LoginAccount).collect {
case loginAccount: Account if loginAccount.isAdmin => case loginAccount: Account if loginAccount.isAdmin =>
execute({ (file, fileId) => execute(
request2Session(request).conn.importAsSQL(file.getInputStream) { (file, fileId) =>
}, _ => true) request2Session(request).conn.importAsSQL(file.getInputStream)
},
_ => true
)
} }
redirect("/admin/data") redirect("/admin/data")
} }

View File

@@ -151,10 +151,9 @@ trait IndexControllerBase extends ControllerBase {
val redirectURI = new URI(s"$baseUrl/signin/oidc") val redirectURI = new URI(s"$baseUrl/signin/oidc")
session.get(Keys.Session.OidcAuthContext) match { session.get(Keys.Session.OidcAuthContext) match {
case Some(context: OidcAuthContext) => case Some(context: OidcAuthContext) =>
authenticate(params.toMap, redirectURI, context.state, context.nonce, oidc).map { authenticate(params.toMap, redirectURI, context.state, context.nonce, oidc).map { case (jwt, account) =>
case (jwt, account) => session.setAttribute(Keys.Session.OidcSessionContext, OidcSessionContext(jwt))
session.setAttribute(Keys.Session.OidcSessionContext, OidcSessionContext(jwt)) signin(account, context.redirectBackURI)
signin(account, context.redirectBackURI)
} orElse { } orElse {
flash.update("error", "Sorry, authentication failed. Please try again.") flash.update("error", "Sorry, authentication failed. Please try again.")
session.invalidate() session.invalidate()
@@ -172,12 +171,11 @@ trait IndexControllerBase extends ControllerBase {
get("/signout") { get("/signout") {
context.settings.oidc.foreach { oidc => context.settings.oidc.foreach { oidc =>
session.get(Keys.Session.OidcSessionContext).foreach { session.get(Keys.Session.OidcSessionContext).foreach { case context: OidcSessionContext =>
case context: OidcSessionContext => val redirectURI = new URI(baseUrl)
val redirectURI = new URI(baseUrl) val authenticationRequest = createOIDLogoutRequest(oidc.issuer, oidc.clientID, redirectURI, context.token)
val authenticationRequest = createOIDLogoutRequest(oidc.issuer, oidc.clientID, redirectURI, context.token) session.invalidate()
session.invalidate() redirect(authenticationRequest.toURI.toString)
redirect(authenticationRequest.toURI.toString)
} }
} }
session.invalidate() session.invalidate()
@@ -244,9 +242,9 @@ trait IndexControllerBase extends ControllerBase {
.map { t => .map { t =>
Map( Map(
"label" -> s"${avatar(t.userName, 16)}<b>@${StringUtil.escapeHtml( "label" -> s"${avatar(t.userName, 16)}<b>@${StringUtil.escapeHtml(
StringUtil.cutTail(t.userName, 25, "...") StringUtil.cutTail(t.userName, 25, "...")
)}</b> ${StringUtil )}</b> ${StringUtil
.escapeHtml(StringUtil.cutTail(t.fullName, 25, "..."))}", .escapeHtml(StringUtil.cutTail(t.fullName, 25, "..."))}",
"value" -> t.userName "value" -> t.userName
) )
} }
@@ -269,12 +267,13 @@ trait IndexControllerBase extends ControllerBase {
get("/:owner/:repository/search")(referrersOnly { repository => get("/:owner/:repository/search")(referrersOnly { repository =>
val query = params.getOrElse("q", "").trim val query = params.getOrElse("q", "").trim
val target = params.getOrElse("type", "code") val target = params.getOrElse("type", "code")
val page = try { val page =
val i = params.getOrElse("page", "1").toInt try {
if (i <= 0) 1 else i val i = params.getOrElse("page", "1").toInt
} catch { if (i <= 0) 1 else i
case _: NumberFormatException => 1 } catch {
} case _: NumberFormatException => 1
}
target.toLowerCase match { target.toLowerCase match {
case "issues" => case "issues" =>

View File

@@ -101,27 +101,26 @@ trait IssuesControllerBase extends ControllerBase {
get("/:owner/:repository/issues/:id")(referrersOnly { repository => get("/:owner/:repository/issues/:id")(referrersOnly { repository =>
val issueId = params("id") val issueId = params("id")
getIssue(repository.owner, repository.name, issueId) map { getIssue(repository.owner, repository.name, issueId) map { issue =>
issue => if (issue.isPullRequest) {
if (issue.isPullRequest) { redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}") } else {
} else { html.issue(
html.issue( issue,
issue, getComments(repository.owner, repository.name, issueId.toInt),
getComments(repository.owner, repository.name, issueId.toInt), getIssueLabels(repository.owner, repository.name, issueId.toInt),
getIssueLabels(repository.owner, repository.name, issueId.toInt), getIssueAssignees(repository.owner, repository.name, issueId.toInt),
getIssueAssignees(repository.owner, repository.name, issueId.toInt), getAssignableUserNames(repository.owner, repository.name),
getAssignableUserNames(repository.owner, repository.name), getMilestonesWithIssueCount(repository.owner, repository.name),
getMilestonesWithIssueCount(repository.owner, repository.name), getPriorities(repository.owner, repository.name),
getPriorities(repository.owner, repository.name), getLabels(repository.owner, repository.name),
getLabels(repository.owner, repository.name), getCustomFieldsWithValue(repository.owner, repository.name, issueId.toInt).filter(_._1.enableForIssues),
getCustomFieldsWithValue(repository.owner, repository.name, issueId.toInt).filter(_._1.enableForIssues), isIssueEditable(repository),
isIssueEditable(repository), isIssueManageable(repository),
isIssueManageable(repository), isIssueCommentManageable(repository),
isIssueCommentManageable(repository), repository
repository )
) }
}
} getOrElse NotFound() } getOrElse NotFound()
}) })
@@ -142,130 +141,120 @@ trait IssuesControllerBase extends ControllerBase {
}) })
post("/:owner/:repository/issues/new", issueCreateForm)(readableUsersOnly { (form, repository) => post("/:owner/:repository/issues/new", issueCreateForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (isIssueEditable(repository)) { // TODO Should this check is provided by authenticator?
if (isIssueEditable(repository)) { // TODO Should this check is provided by authenticator? val issue = createIssue(
val issue = createIssue( repository,
repository, form.title,
form.title, form.content,
form.content, form.assigneeUserNames.toSeq.flatMap(_.split(",")),
form.assigneeUserNames.toSeq.flatMap(_.split(",")), form.milestoneId,
form.milestoneId, form.priorityId,
form.priorityId, form.labelNames.toSeq.flatMap(_.split(",")),
form.labelNames.toSeq.flatMap(_.split(",")), loginAccount
loginAccount )
)
// Insert custom field values // Insert custom field values
params.toMap.foreach { params.toMap.foreach { case (key, value) =>
case (key, value) => if (key.startsWith("custom-field-")) {
if (key.startsWith("custom-field-")) { getCustomField(
getCustomField( repository.owner,
repository.owner, repository.name,
repository.name, key.replaceFirst("^custom-field-", "").toInt
key.replaceFirst("^custom-field-", "").toInt ).foreach { field =>
).foreach { field => CustomFieldBehavior.validate(field, value, messages) match {
CustomFieldBehavior.validate(field, value, messages) match { case None =>
case None => insertOrUpdateCustomFieldValue(field, repository.owner, repository.name, issue.issueId, value)
insertOrUpdateCustomFieldValue(field, repository.owner, repository.name, issue.issueId, value) case Some(_) => halt(400)
case Some(_) => halt(400)
}
}
} }
}
} }
}
redirect(s"/${issue.userName}/${issue.repositoryName}/issues/${issue.issueId}") redirect(s"/${issue.userName}/${issue.repositoryName}/issues/${issue.issueId}")
} else Unauthorized() } else Unauthorized()
} }
}) })
ajaxPost("/:owner/:repository/issues/edit_title/:id", issueTitleEditForm)(readableUsersOnly { (title, repository) => ajaxPost("/:owner/:repository/issues/edit_title/:id", issueTitleEditForm)(readableUsersOnly { (title, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => getIssue(repository.owner, repository.name, params("id")).map { issue =>
getIssue(repository.owner, repository.name, params("id")).map { if (isEditableContent(repository.owner, repository.name, issue.openedUserName, loginAccount)) {
issue => if (issue.title != title) {
if (isEditableContent(repository.owner, repository.name, issue.openedUserName, loginAccount)) { // update issue
if (issue.title != title) { updateIssue(repository.owner, repository.name, issue.issueId, title, issue.content)
// update issue // extract references and create refer comment
updateIssue(repository.owner, repository.name, issue.issueId, title, issue.content) createReferComment(repository.owner, repository.name, issue.copy(title = title), title, loginAccount)
// extract references and create refer comment createComment(
createReferComment(repository.owner, repository.name, issue.copy(title = title), title, loginAccount) repository.owner,
createComment( repository.name,
repository.owner, loginAccount.userName,
repository.name, issue.issueId,
loginAccount.userName, issue.title + "\r\n" + title,
issue.issueId, "change_title"
issue.title + "\r\n" + title, )
"change_title" }
) redirect(s"/${repository.owner}/${repository.name}/issues/_data/${issue.issueId}")
} } else Unauthorized()
redirect(s"/${repository.owner}/${repository.name}/issues/_data/${issue.issueId}") } getOrElse NotFound()
} else Unauthorized()
} getOrElse NotFound()
} }
}) })
ajaxPost("/:owner/:repository/issues/edit/:id", issueEditForm)(readableUsersOnly { (content, repository) => ajaxPost("/:owner/:repository/issues/edit/:id", issueEditForm)(readableUsersOnly { (content, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => getIssue(repository.owner, repository.name, params("id")).map { issue =>
getIssue(repository.owner, repository.name, params("id")).map { issue => if (isEditableContent(repository.owner, repository.name, issue.openedUserName, loginAccount)) {
if (isEditableContent(repository.owner, repository.name, issue.openedUserName, loginAccount)) { // update issue
// update issue updateIssue(repository.owner, repository.name, issue.issueId, issue.title, content)
updateIssue(repository.owner, repository.name, issue.issueId, issue.title, content) // extract references and create refer comment
// extract references and create refer comment createReferComment(repository.owner, repository.name, issue, content.getOrElse(""), loginAccount)
createReferComment(repository.owner, repository.name, issue, content.getOrElse(""), loginAccount)
redirect(s"/${repository.owner}/${repository.name}/issues/_data/${issue.issueId}") redirect(s"/${repository.owner}/${repository.name}/issues/_data/${issue.issueId}")
} else Unauthorized() } else Unauthorized()
} getOrElse NotFound() } getOrElse NotFound()
} }
}) })
post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) => post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue => val actionOpt =
val actionOpt = params
params .get("action")
.get("action") .filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName, loginAccount))
.filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName, loginAccount)) handleComment(issue, Some(form.content), repository, actionOpt) map { case (issue, id) =>
handleComment(issue, Some(form.content), repository, actionOpt) map { redirect(
case (issue, id) => s"/${repository.owner}/${repository.name}/${if (issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}"
redirect( )
s"/${repository.owner}/${repository.name}/${if (issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}" }
) } getOrElse NotFound()
}
} getOrElse NotFound()
} }
}) })
post("/:owner/:repository/issue_comments/state", issueStateForm)(readableUsersOnly { (form, repository) => post("/:owner/:repository/issue_comments/state", issueStateForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue => val actionOpt =
val actionOpt = params
params .get("action")
.get("action") .filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName, loginAccount))
.filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName, loginAccount)) handleComment(issue, form.content, repository, actionOpt) map { case (issue, id) =>
handleComment(issue, form.content, repository, actionOpt) map { redirect(
case (issue, id) => s"/${repository.owner}/${repository.name}/${if (issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}"
redirect( )
s"/${repository.owner}/${repository.name}/${if (issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}" }
) } getOrElse NotFound()
}
} getOrElse NotFound()
} }
}) })
ajaxPost("/:owner/:repository/issue_comments/edit/:id", commentForm)(readableUsersOnly { (form, repository) => ajaxPost("/:owner/:repository/issue_comments/edit/:id", commentForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => getComment(repository.owner, repository.name, params("id")).map { comment =>
getComment(repository.owner, repository.name, params("id")).map { comment => if (isEditableContent(repository.owner, repository.name, comment.commentedUserName, loginAccount)) {
if (isEditableContent(repository.owner, repository.name, comment.commentedUserName, loginAccount)) { updateComment(repository.owner, repository.name, comment.issueId, comment.commentId, form.content)
updateComment(repository.owner, repository.name, comment.issueId, comment.commentId, form.content) redirect(s"/${repository.owner}/${repository.name}/issue_comments/_data/${comment.commentId}")
redirect(s"/${repository.owner}/${repository.name}/issue_comments/_data/${comment.commentId}") } else Unauthorized()
} else Unauthorized() } getOrElse NotFound()
} getOrElse NotFound()
} }
}) })
@@ -280,65 +269,61 @@ trait IssuesControllerBase extends ControllerBase {
}) })
ajaxGet("/:owner/:repository/issues/_data/:id")(readableUsersOnly { repository => ajaxGet("/:owner/:repository/issues/_data/:id")(readableUsersOnly { repository =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => getIssue(repository.owner, repository.name, params("id")) map { x =>
getIssue(repository.owner, repository.name, params("id")) map { if (isEditableContent(x.userName, x.repositoryName, x.openedUserName, loginAccount)) {
x => params.get("dataType") collect {
if (isEditableContent(x.userName, x.repositoryName, x.openedUserName, loginAccount)) { case t if t == "html" => html.editissue(x.content, x.issueId, repository)
params.get("dataType") collect { } getOrElse {
case t if t == "html" => html.editissue(x.content, x.issueId, repository) contentType = formats("json")
} getOrElse { org.json4s.jackson.Serialization.write(
contentType = formats("json") Map(
org.json4s.jackson.Serialization.write( "title" -> x.title,
Map( "content" -> Markdown.toHtml(
"title" -> x.title, markdown = x.content getOrElse "No description given.",
"content" -> Markdown.toHtml( repository = repository,
markdown = x.content getOrElse "No description given.", branch = repository.repository.defaultBranch,
repository = repository, enableWikiLink = false,
branch = repository.repository.defaultBranch, enableRefsLink = true,
enableWikiLink = false, enableAnchor = true,
enableRefsLink = true, enableLineBreaks = true,
enableAnchor = true, enableTaskList = true,
enableLineBreaks = true, hasWritePermission = true
enableTaskList = true,
hasWritePermission = true
)
)
) )
} )
} else Unauthorized() )
} getOrElse NotFound() }
} else Unauthorized()
} getOrElse NotFound()
} }
}) })
ajaxGet("/:owner/:repository/issue_comments/_data/:id")(readableUsersOnly { repository => ajaxGet("/:owner/:repository/issue_comments/_data/:id")(readableUsersOnly { repository =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => getComment(repository.owner, repository.name, params("id")) map { x =>
getComment(repository.owner, repository.name, params("id")) map { if (isEditableContent(x.userName, x.repositoryName, x.commentedUserName, loginAccount)) {
x => params.get("dataType") collect {
if (isEditableContent(x.userName, x.repositoryName, x.commentedUserName, loginAccount)) { case t if t == "html" => html.editcomment(x.content, x.commentId, repository)
params.get("dataType") collect { } getOrElse {
case t if t == "html" => html.editcomment(x.content, x.commentId, repository) contentType = formats("json")
} getOrElse { org.json4s.jackson.Serialization.write(
contentType = formats("json") Map(
org.json4s.jackson.Serialization.write( "content" -> view.Markdown.toHtml(
Map( markdown = x.content,
"content" -> view.Markdown.toHtml( repository = repository,
markdown = x.content, branch = repository.repository.defaultBranch,
repository = repository, enableWikiLink = false,
branch = repository.repository.defaultBranch, enableRefsLink = true,
enableWikiLink = false, enableAnchor = true,
enableRefsLink = true, enableLineBreaks = true,
enableAnchor = true, enableTaskList = true,
enableLineBreaks = true, hasWritePermission = true
enableTaskList = true,
hasWritePermission = true
)
)
) )
} )
} else Unauthorized() )
} getOrElse NotFound() }
} else Unauthorized()
} getOrElse NotFound()
} }
}) })
@@ -377,9 +362,8 @@ trait IssuesControllerBase extends ControllerBase {
milestoneId("milestoneId").map { milestoneId => milestoneId("milestoneId").map { milestoneId =>
getMilestonesWithIssueCount(repository.owner, repository.name) getMilestonesWithIssueCount(repository.owner, repository.name)
.find(_._1.milestoneId == milestoneId) .find(_._1.milestoneId == milestoneId)
.map { .map { case (_, openCount, closeCount) =>
case (_, openCount, closeCount) => gitbucket.core.issues.milestones.html.progress(openCount + closeCount, closeCount)
gitbucket.core.issues.milestones.html.progress(openCount + closeCount, closeCount)
} getOrElse NotFound() } getOrElse NotFound()
} getOrElse Ok() } getOrElse Ok()
}) })
@@ -460,7 +444,7 @@ trait IssuesControllerBase extends ControllerBase {
post("/:owner/:repository/issues/batchedit/assign")(writableUsersOnly { repository => post("/:owner/:repository/issues/batchedit/assign")(writableUsersOnly { repository =>
val value = assignedUserName("value") val value = assignedUserName("value")
executeBatch(repository) { executeBatch(repository) {
//updateAssignedUserName(repository.owner, repository.name, _, value, true) // updateAssignedUserName(repository.owner, repository.name, _, value, true)
value match { value match {
case Some(assignedUserName) => case Some(assignedUserName) =>
registerIssueAssignee(repository.owner, repository.name, _, assignedUserName, true) registerIssueAssignee(repository.owner, repository.name, _, assignedUserName, true)
@@ -510,9 +494,9 @@ trait IssuesControllerBase extends ControllerBase {
.map { t => .map { t =>
Map( Map(
"label" -> s"""${if (t.isPullRequest) "<i class='octicon octicon-git-pull-request'></i>" "label" -> s"""${if (t.isPullRequest) "<i class='octicon octicon-git-pull-request'></i>"
else "<i class='octicon octicon-issue-opened'></i>"}<b> #${StringUtil else "<i class='octicon octicon-issue-opened'></i>"}<b> #${StringUtil
.escapeHtml(t.issueId.toString)} ${StringUtil .escapeHtml(t.issueId.toString)} ${StringUtil
.escapeHtml(StringUtil.cutTail(t.title, 50, "..."))}</b>""", .escapeHtml(StringUtil.cutTail(t.title, 50, "..."))}</b>""",
"value" -> t.issueId.toString "value" -> t.issueId.toString
) )
} }
@@ -565,8 +549,8 @@ trait IssuesControllerBase extends ControllerBase {
/** /**
* Tests whether an issue or a comment is editable by a logged-in user. * Tests whether an issue or a comment is editable by a logged-in user.
*/ */
private def isEditableContent(owner: String, repository: String, author: String, loginAccount: Account)( private def isEditableContent(owner: String, repository: String, author: String, loginAccount: Account)(implicit
implicit context: Context context: Context
): Boolean = { ): Boolean = {
hasDeveloperRole(owner, repository, context.loginAccount) || author == loginAccount.userName hasDeveloperRole(owner, repository, context.loginAccount) || author == loginAccount.userName
} }
@@ -574,8 +558,8 @@ trait IssuesControllerBase extends ControllerBase {
/** /**
* Tests whether an issue comment is deletable by a logged-in user. * Tests whether an issue comment is deletable by a logged-in user.
*/ */
private def isDeletableComment(owner: String, repository: String, author: String, loginAccount: Account)( private def isDeletableComment(owner: String, repository: String, author: String, loginAccount: Account)(implicit
implicit context: Context context: Context
): Boolean = { ): Boolean = {
hasOwnerRole(owner, repository, context.loginAccount) || author == loginAccount.userName hasOwnerRole(owner, repository, context.loginAccount) || author == loginAccount.userName
} }

View File

@@ -30,12 +30,14 @@ trait PreProcessControllerBase extends ControllerBase {
* But if it's not allowed, demands authentication except some paths. * But if it's not allowed, demands authentication except some paths.
*/ */
get(!context.settings.basicBehavior.allowAnonymousAccess, context.loginAccount.isEmpty) { get(!context.settings.basicBehavior.allowAnonymousAccess, context.loginAccount.isEmpty) {
if (!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") && if (
!context.currentPath.startsWith("/register") && !context.currentPath.endsWith("/info/refs") && !context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") &&
!context.currentPath.startsWith("/plugin-assets") && !context.currentPath.startsWith("/register") && !context.currentPath.endsWith("/info/refs") &&
!PluginRegistry().getAnonymousAccessiblePaths().exists { path => !context.currentPath.startsWith("/plugin-assets") &&
context.currentPath.startsWith(path) !PluginRegistry().getAnonymousAccessiblePaths().exists { path =>
}) { context.currentPath.startsWith(path)
}
) {
Unauthorized() Unauthorized()
} else { } else {
pass() pass()

View File

@@ -113,149 +113,141 @@ trait PullRequestsControllerBase extends ControllerBase {
}) })
get("/:owner/:repository/pull/:id")(referrersOnly { repository => get("/:owner/:repository/pull/:id")(referrersOnly { repository =>
params("id").toIntOpt.flatMap { params("id").toIntOpt.flatMap { issueId =>
issueId => getPullRequest(repository.owner, repository.name, issueId) map { case (issue, pullreq) =>
getPullRequest(repository.owner, repository.name, issueId) map { val (commits, diffs) =
case (issue, pullreq) => getRequestCompareInfo(
val (commits, diffs) = repository.owner,
getRequestCompareInfo( repository.name,
repository.owner, pullreq.commitIdFrom,
repository.name, repository.owner,
pullreq.commitIdFrom, repository.name,
repository.owner, pullreq.commitIdTo
repository.name, )
pullreq.commitIdTo
)
html.conversation( html.conversation(
issue, issue,
pullreq, pullreq,
commits.flatten, commits.flatten,
getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten), getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
diffs.size, diffs.size,
getIssueLabels(repository.owner, repository.name, issueId), getIssueLabels(repository.owner, repository.name, issueId),
getIssueAssignees(repository.owner, repository.name, issueId), getIssueAssignees(repository.owner, repository.name, issueId),
getAssignableUserNames(repository.owner, repository.name), getAssignableUserNames(repository.owner, repository.name),
getMilestonesWithIssueCount(repository.owner, repository.name), getMilestonesWithIssueCount(repository.owner, repository.name),
getPriorities(repository.owner, repository.name), getPriorities(repository.owner, repository.name),
getLabels(repository.owner, repository.name), getLabels(repository.owner, repository.name),
getCustomFieldsWithValue(repository.owner, repository.name, issueId).filter(_._1.enableForPullRequests), getCustomFieldsWithValue(repository.owner, repository.name, issueId).filter(_._1.enableForPullRequests),
isEditable(repository), isEditable(repository),
isManageable(repository), isManageable(repository),
hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount), hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount),
repository, repository,
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName), getRepository(pullreq.requestUserName, pullreq.requestRepositoryName),
flash.iterator.map(f => f._1 -> f._2.toString).toMap flash.iterator.map(f => f._1 -> f._2.toString).toMap
) )
} }
} getOrElse NotFound() } getOrElse NotFound()
}) })
get("/:owner/:repository/pull/:id/commits")(referrersOnly { repository => get("/:owner/:repository/pull/:id/commits")(referrersOnly { repository =>
params("id").toIntOpt.flatMap { params("id").toIntOpt.flatMap { issueId =>
issueId => getPullRequest(repository.owner, repository.name, issueId) map { case (issue, pullreq) =>
getPullRequest(repository.owner, repository.name, issueId) map { val (commits, diffs) =
case (issue, pullreq) => getRequestCompareInfo(
val (commits, diffs) = repository.owner,
getRequestCompareInfo( repository.name,
repository.owner, pullreq.commitIdFrom,
repository.name, repository.owner,
pullreq.commitIdFrom, repository.name,
repository.owner, pullreq.commitIdTo
repository.name, )
pullreq.commitIdTo
)
val commitsWithStatus = commits.map { day => val commitsWithStatus = commits.map { day =>
day.map { commit => day.map { commit =>
(commit, getCommitStatusWithSummary(repository.owner, repository.name, commit.id)) (commit, getCommitStatusWithSummary(repository.owner, repository.name, commit.id))
} }
}
html.commits(
issue,
pullreq,
commitsWithStatus,
getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
diffs.size,
isManageable(repository),
repository
)
} }
html.commits(
issue,
pullreq,
commitsWithStatus,
getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
diffs.size,
isManageable(repository),
repository
)
}
} getOrElse NotFound() } getOrElse NotFound()
}) })
get("/:owner/:repository/pull/:id/files")(referrersOnly { repository => get("/:owner/:repository/pull/:id/files")(referrersOnly { repository =>
params("id").toIntOpt.flatMap { params("id").toIntOpt.flatMap { issueId =>
issueId => getPullRequest(repository.owner, repository.name, issueId) map { case (issue, pullreq) =>
getPullRequest(repository.owner, repository.name, issueId) map { val (commits, diffs) =
case (issue, pullreq) => getRequestCompareInfo(
val (commits, diffs) = repository.owner,
getRequestCompareInfo( repository.name,
repository.owner, pullreq.commitIdFrom,
repository.name, repository.owner,
pullreq.commitIdFrom, repository.name,
repository.owner, pullreq.commitIdTo
repository.name, )
pullreq.commitIdTo
)
html.files( html.files(
issue, issue,
pullreq, pullreq,
diffs, diffs,
commits.flatten, commits.flatten,
getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten), getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
isManageable(repository), isManageable(repository),
repository repository
) )
} }
} getOrElse NotFound() } getOrElse NotFound()
}) })
ajaxGet("/:owner/:repository/pull/:id/mergeguide")(referrersOnly { repository => ajaxGet("/:owner/:repository/pull/:id/mergeguide")(referrersOnly { repository =>
params("id").toIntOpt.flatMap { params("id").toIntOpt.flatMap { issueId =>
issueId => getPullRequest(repository.owner, repository.name, issueId) map { case (issue, pullreq) =>
getPullRequest(repository.owner, repository.name, issueId) map { val conflictMessage = LockUtil.lock(s"${repository.owner}/${repository.name}") {
case (issue, pullreq) => checkConflict(repository.owner, repository.name, pullreq.branch, issueId)
val conflictMessage = LockUtil.lock(s"${repository.owner}/${repository.name}") { }
checkConflict(repository.owner, repository.name, pullreq.branch, issueId) val hasMergePermission = hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
} val branchProtection = getProtectedBranchInfo(repository.owner, repository.name, pullreq.branch)
val hasMergePermission = hasDeveloperRole(repository.owner, repository.name, context.loginAccount) val mergeStatus = PullRequestService.MergeStatus(
val branchProtection = getProtectedBranchInfo(repository.owner, repository.name, pullreq.branch) conflictMessage = conflictMessage,
val mergeStatus = PullRequestService.MergeStatus( commitStatuses = getCommitStatuses(repository.owner, repository.name, pullreq.commitIdTo),
conflictMessage = conflictMessage, branchProtection = branchProtection,
commitStatuses = getCommitStatuses(repository.owner, repository.name, pullreq.commitIdTo), branchIsOutOfDate = JGitUtil.getShaByRef(repository.owner, repository.name, pullreq.branch) != Some(
branchProtection = branchProtection, pullreq.commitIdFrom
branchIsOutOfDate = JGitUtil.getShaByRef(repository.owner, repository.name, pullreq.branch) != Some( ),
pullreq.commitIdFrom needStatusCheck = context.loginAccount.forall { u =>
), branchProtection.needStatusCheck(u.userName)
needStatusCheck = context.loginAccount.forall { u => },
branchProtection.needStatusCheck(u.userName) hasUpdatePermission = hasDeveloperRole(
}, pullreq.requestUserName,
hasUpdatePermission = hasDeveloperRole( pullreq.requestRepositoryName,
context.loginAccount
) &&
context.loginAccount.exists { u =>
!getProtectedBranchInfo(
pullreq.requestUserName, pullreq.requestUserName,
pullreq.requestRepositoryName, pullreq.requestRepositoryName,
context.loginAccount pullreq.requestBranch
) && ).needStatusCheck(u.userName)
context.loginAccount.exists { u => },
!getProtectedBranchInfo( hasMergePermission = hasMergePermission,
pullreq.requestUserName, commitIdTo = pullreq.commitIdTo
pullreq.requestRepositoryName, )
pullreq.requestBranch html.mergeguide(
).needStatusCheck(u.userName) mergeStatus,
}, issue,
hasMergePermission = hasMergePermission, pullreq,
commitIdTo = pullreq.commitIdTo repository,
) getRepository(pullreq.requestUserName, pullreq.requestRepositoryName).get
html.mergeguide( )
mergeStatus, }
issue,
pullreq,
repository,
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName).get
)
}
} getOrElse NotFound() } getOrElse NotFound()
}) })
@@ -314,7 +306,9 @@ trait PullRequestsControllerBase extends ControllerBase {
} else { } else {
LockUtil.lock(s"${owner}/${name}") { LockUtil.lock(s"${owner}/${name}") {
val alias = val alias =
if (pullreq.repositoryName == pullreq.requestRepositoryName && pullreq.userName == pullreq.requestUserName) { if (
pullreq.repositoryName == pullreq.requestRepositoryName && pullreq.userName == pullreq.requestUserName
) {
pullreq.branch pullreq.branch
} else { } else {
s"${pullreq.userName}:${pullreq.branch}" s"${pullreq.userName}:${pullreq.branch}"
@@ -383,29 +377,27 @@ trait PullRequestsControllerBase extends ControllerBase {
val headBranch = params.get("head") val headBranch = params.get("head")
(forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match { (forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match {
case (Some(originUserName), Some(originRepositoryName)) => case (Some(originUserName), Some(originRepositoryName)) =>
getRepository(originUserName, originRepositoryName).map { getRepository(originUserName, originRepositoryName).map { originRepository =>
originRepository => Using.resources(
Using.resources( Git.open(getRepositoryDir(originUserName, originRepositoryName)),
Git.open(getRepositoryDir(originUserName, originRepositoryName)), Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name)) ) { (oldGit, newGit) =>
) { (oldGit, newGit) => val newBranch = headBranch.getOrElse(JGitUtil.getDefaultBranch(newGit, forkedRepository).get._2)
val newBranch = headBranch.getOrElse(JGitUtil.getDefaultBranch(newGit, forkedRepository).get._2) val oldBranch = originRepository.branchList
val oldBranch = originRepository.branchList .find(_ == newBranch)
.find(_ == newBranch) .getOrElse(JGitUtil.getDefaultBranch(oldGit, originRepository).get._2)
.getOrElse(JGitUtil.getDefaultBranch(oldGit, originRepository).get._2)
redirect( redirect(
s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${originUserName}:${oldBranch}...${newBranch}" s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${originUserName}:${oldBranch}...${newBranch}"
) )
} }
} getOrElse NotFound() } getOrElse NotFound()
case _ => case _ =>
Using.resource(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))) { git => Using.resource(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))) { git =>
JGitUtil.getDefaultBranch(git, forkedRepository).map { JGitUtil.getDefaultBranch(git, forkedRepository).map { case (_, defaultBranch) =>
case (_, defaultBranch) => redirect(
redirect( s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${defaultBranch}...${headBranch.getOrElse(defaultBranch)}"
s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${defaultBranch}...${headBranch.getOrElse(defaultBranch)}" )
)
} getOrElse { } getOrElse {
redirect(s"/${forkedRepository.owner}/${forkedRepository.name}") redirect(s"/${forkedRepository.owner}/${forkedRepository.name}")
} }
@@ -445,8 +437,10 @@ trait PullRequestsControllerBase extends ControllerBase {
val (originOwner, originId) = parseCompareIdentifier(origin, forkedRepository.owner) val (originOwner, originId) = parseCompareIdentifier(origin, forkedRepository.owner)
val (forkedOwner, forkedId) = parseCompareIdentifier(forked, forkedRepository.owner) val (forkedOwner, forkedId) = parseCompareIdentifier(forked, forkedRepository.owner)
(for (originRepositoryName <- getOriginRepositoryName(originOwner, forkedOwner, forkedRepository); (for (
originRepository <- getRepository(originOwner, originRepositoryName)) yield { originRepositoryName <- getOriginRepositoryName(originOwner, forkedOwner, forkedRepository);
originRepository <- getRepository(originOwner, originRepositoryName)
) yield {
val (oldId, newId) = val (oldId, newId) =
getPullRequestCommitFromTo(originRepository, forkedRepository, originId, forkedId) getPullRequestCommitFromTo(originRepository, forkedRepository, originId, forkedId)
@@ -566,87 +560,88 @@ trait PullRequestsControllerBase extends ControllerBase {
val (originOwner, tmpOriginBranch) = parseCompareIdentifier(origin, forkedRepository.owner) val (originOwner, tmpOriginBranch) = parseCompareIdentifier(origin, forkedRepository.owner)
val (forkedOwner, tmpForkedBranch) = parseCompareIdentifier(forked, forkedRepository.owner) val (forkedOwner, tmpForkedBranch) = parseCompareIdentifier(forked, forkedRepository.owner)
(for (originRepositoryName <- if (originOwner == forkedOwner) { (for (
Some(forkedRepository.name) originRepositoryName <-
} else { if (originOwner == forkedOwner) {
forkedRepository.repository.originRepositoryName.orElse { Some(forkedRepository.name)
getForkedRepositories(forkedRepository.owner, forkedRepository.name) } else {
.find(_.userName == originOwner) forkedRepository.repository.originRepositoryName.orElse {
.map(_.repositoryName) getForkedRepositories(forkedRepository.owner, forkedRepository.name)
} .find(_.userName == originOwner)
}; .map(_.repositoryName)
originRepository <- getRepository(originOwner, originRepositoryName)) yield { }
};
originRepository <- getRepository(originOwner, originRepositoryName)
) yield {
Using.resources( Using.resources(
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)), Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name)) Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
) { ) { case (oldGit, newGit) =>
case (oldGit, newGit) => val originBranch = JGitUtil.getDefaultBranch(oldGit, originRepository, tmpOriginBranch).get._2
val originBranch = JGitUtil.getDefaultBranch(oldGit, originRepository, tmpOriginBranch).get._2 val forkedBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository, tmpForkedBranch).get._2
val forkedBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository, tmpForkedBranch).get._2 val conflict = LockUtil.lock(s"${originRepository.owner}/${originRepository.name}") {
val conflict = LockUtil.lock(s"${originRepository.owner}/${originRepository.name}") { checkConflict(
checkConflict( originRepository.owner,
originRepository.owner, originRepository.name,
originRepository.name, originBranch,
originBranch, forkedRepository.owner,
forkedRepository.owner, forkedRepository.name,
forkedRepository.name, forkedBranch
forkedBranch )
) }
} html.mergecheck(conflict.isDefined)
html.mergecheck(conflict.isDefined)
} }
}) getOrElse NotFound() }) getOrElse NotFound()
}) })
post("/:owner/:repository/pulls/new", pullRequestForm)(readableUsersOnly { (form, repository) => post("/:owner/:repository/pulls/new", pullRequestForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => val manageable = isManageable(repository)
val manageable = isManageable(repository)
val issueId = insertIssue( val issueId = insertIssue(
owner = repository.owner, owner = repository.owner,
repository = repository.name, repository = repository.name,
loginUser = loginAccount.userName, loginUser = loginAccount.userName,
title = form.title, title = form.title,
content = form.content, content = form.content,
milestoneId = if (manageable) form.milestoneId else None, milestoneId = if (manageable) form.milestoneId else None,
priorityId = if (manageable) form.priorityId else None, priorityId = if (manageable) form.priorityId else None,
isPullRequest = true isPullRequest = true
) )
createPullRequest( createPullRequest(
originRepository = repository, originRepository = repository,
issueId = issueId, issueId = issueId,
originBranch = form.targetBranch, originBranch = form.targetBranch,
requestUserName = form.requestUserName, requestUserName = form.requestUserName,
requestRepositoryName = form.requestRepositoryName, requestRepositoryName = form.requestRepositoryName,
requestBranch = form.requestBranch, requestBranch = form.requestBranch,
commitIdFrom = form.commitIdFrom, commitIdFrom = form.commitIdFrom,
commitIdTo = form.commitIdTo, commitIdTo = form.commitIdTo,
isDraft = form.isDraft, isDraft = form.isDraft,
loginAccount = loginAccount, loginAccount = loginAccount,
settings = context.settings settings = context.settings
) )
if (manageable) { if (manageable) {
// insert assignees // insert assignees
form.assigneeUserNames.foreach { value => form.assigneeUserNames.foreach { value =>
value.split(",").foreach { userName => value.split(",").foreach { userName =>
registerIssueAssignee(repository.owner, repository.name, issueId, userName) registerIssueAssignee(repository.owner, repository.name, issueId, userName)
}
} }
// insert labels }
form.labelNames.foreach { value => // insert labels
val labels = getLabels(repository.owner, repository.name) form.labelNames.foreach { value =>
value.split(",").foreach { labelName => val labels = getLabels(repository.owner, repository.name)
labels.find(_.labelName == labelName).map { label => value.split(",").foreach { labelName =>
registerIssueLabel(repository.owner, repository.name, issueId, label.labelId) labels.find(_.labelName == labelName).map { label =>
} registerIssueLabel(repository.owner, repository.name, issueId, label.labelId)
} }
} }
} }
}
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}") redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
} }
}) })
@@ -656,24 +651,23 @@ trait PullRequestsControllerBase extends ControllerBase {
context.loginAccount.map(x => Seq(x.mailAddress) ++ getAccountExtraMailAddresses(x.userName)).getOrElse(Nil) context.loginAccount.map(x => Seq(x.mailAddress) ++ getAccountExtraMailAddresses(x.userName)).getOrElse(Nil)
val branches = val branches =
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
git => JGitUtil
JGitUtil .getBranches(
.getBranches( git = git,
git = git, defaultBranch = repository.repository.defaultBranch,
defaultBranch = repository.repository.defaultBranch, origin = repository.repository.originUserName.isEmpty
origin = repository.repository.originUserName.isEmpty )
) .filter { x =>
.filter { x => x.mergeInfo.map(_.ahead).getOrElse(0) > 0 && x.mergeInfo.map(_.behind).getOrElse(0) == 0 &&
x.mergeInfo.map(_.ahead).getOrElse(0) > 0 && x.mergeInfo.map(_.behind).getOrElse(0) == 0 && x.commitTime.getTime > thresholdTime &&
x.commitTime.getTime > thresholdTime && mailAddresses.contains(x.committerEmailAddress)
mailAddresses.contains(x.committerEmailAddress) }
} .sortBy { br =>
.sortBy { br => (br.mergeInfo.isEmpty, br.commitTime)
(br.mergeInfo.isEmpty, br.commitTime) }
} .map(_.name)
.map(_.name) .reverse
.reverse
} }
val targetRepository = (for { val targetRepository = (for {

View File

@@ -106,33 +106,31 @@ trait ReleaseControllerBase extends ControllerBase {
}) })
post("/:owner/:repository/releases/*/create", releaseForm)(writableUsersOnly { (form, repository) => post("/:owner/:repository/releases/*/create", releaseForm)(writableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => val tagName = multiParams("splat").head
val tagName = multiParams("splat").head
// Insert into RELEASE // Insert into RELEASE
createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount) createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount)
// Insert into RELEASE_ASSET // Insert into RELEASE_ASSET
val files = params.toMap.collect { val files = params.toMap.collect {
case (name, value) if name.startsWith("file:") => case (name, value) if name.startsWith("file:") =>
val Array(_, fileId) = name.split(":") val Array(_, fileId) = name.split(":")
(fileId, value) (fileId, value)
} }
files.foreach { files.foreach { case (fileId, fileName) =>
case (fileId, fileName) => val size =
val size = new File(
new File( getReleaseFilesDir(repository.owner, repository.name),
getReleaseFilesDir(repository.owner, repository.name), FileUtil.checkFilename(tagName + "/" + fileId)
FileUtil.checkFilename(tagName + "/" + fileId) ).length
).length createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount) }
}
val releaseInfo = ReleaseInfo(repository.owner, repository.name, loginAccount.userName, form.name, tagName) val releaseInfo = ReleaseInfo(repository.owner, repository.name, loginAccount.userName, form.name, tagName)
recordActivity(releaseInfo) recordActivity(releaseInfo)
redirect(s"/${repository.owner}/${repository.name}/releases/${tagName}") redirect(s"/${repository.owner}/${repository.name}/releases/${tagName}")
} }
}) })
@@ -171,48 +169,45 @@ trait ReleaseControllerBase extends ControllerBase {
}) })
post("/:owner/:repository/releases/*/edit", releaseForm)(writableUsersOnly { (form, repository) => post("/:owner/:repository/releases/*/edit", releaseForm)(writableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => val tagName = multiParams("splat").head
val tagName = multiParams("splat").head
getRelease(repository.owner, repository.name, tagName) getRelease(repository.owner, repository.name, tagName)
.map { .map { release =>
release => // Update RELEASE
// Update RELEASE updateRelease(repository.owner, repository.name, tagName, form.name, form.content)
updateRelease(repository.owner, repository.name, tagName, form.name, form.content)
// Delete and Insert RELEASE_ASSET // Delete and Insert RELEASE_ASSET
val assets = getReleaseAssets(repository.owner, repository.name, tagName) val assets = getReleaseAssets(repository.owner, repository.name, tagName)
deleteReleaseAssets(repository.owner, repository.name, tagName) deleteReleaseAssets(repository.owner, repository.name, tagName)
val files = params.toMap.collect { val files = params.toMap.collect {
case (name, value) if name.startsWith("file:") => case (name, value) if name.startsWith("file:") =>
val Array(_, fileId) = name.split(":") val Array(_, fileId) = name.split(":")
(fileId, value) (fileId, value)
}
files.foreach {
case (fileId, fileName) =>
val size =
new File(
getReleaseFilesDir(repository.owner, repository.name),
FileUtil.checkFilename(tagName + "/" + fileId)
).length
createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
}
assets.foreach { asset =>
if (!files.exists { case (fileId, _) => fileId == asset.fileName }) {
val file = new File(
getReleaseFilesDir(repository.owner, repository.name),
FileUtil.checkFilename(release.tag + "/" + asset.fileName)
)
FileUtils.forceDelete(file)
}
}
redirect(s"/${release.userName}/${release.repositoryName}/releases/${tagName}")
} }
.getOrElse(NotFound()) files.foreach { case (fileId, fileName) =>
val size =
new File(
getReleaseFilesDir(repository.owner, repository.name),
FileUtil.checkFilename(tagName + "/" + fileId)
).length
createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
}
assets.foreach { asset =>
if (!files.exists { case (fileId, _) => fileId == asset.fileName }) {
val file = new File(
getReleaseFilesDir(repository.owner, repository.name),
FileUtil.checkFilename(release.tag + "/" + asset.fileName)
)
FileUtils.forceDelete(file)
}
}
redirect(s"/${release.userName}/${release.repositoryName}/releases/${tagName}")
}
.getOrElse(NotFound())
} }
}) })
@@ -237,9 +232,12 @@ trait ReleaseControllerBase extends ControllerBase {
val assets = getReleaseAssetsMap(repository.owner, repository.name, releases) val assets = getReleaseAssetsMap(repository.owner, repository.name, releases)
val tagsWithReleases = tagsToDisplay.map { tag => val tagsWithReleases = tagsToDisplay.map { tag =>
(tag, releases.find(_.tag == tag.name).map { release => (
(release, assets(release)) tag,
}) releases.find(_.tag == tag.name).map { release =>
(release, assets(release))
}
)
} }
tagsWithReleases tagsWithReleases
} }

View File

@@ -286,93 +286,90 @@ trait RepositorySettingsControllerBase extends ControllerBase {
Array(h.getName, h.getValue) Array(h.getName, h.getValue)
} }
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
git => import scala.concurrent.duration._
import scala.concurrent.duration._ import scala.concurrent._
import scala.concurrent._ import scala.jdk.CollectionConverters._
import scala.jdk.CollectionConverters._ import scala.util.control.NonFatal
import scala.util.control.NonFatal import org.apache.http.util.EntityUtils
import org.apache.http.util.EntityUtils import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.ExecutionContext.Implicits.global
val url = params("url") val url = params("url")
val token = Some(params("token")) val token = Some(params("token"))
val ctype = WebHookContentType.valueOf(params("ctype")) val ctype = WebHookContentType.valueOf(params("ctype"))
val dummyWebHookInfo = RepositoryWebHook( val dummyWebHookInfo = RepositoryWebHook(
userName = repository.owner, userName = repository.owner,
repositoryName = repository.name, repositoryName = repository.name,
url = url, url = url,
ctype = ctype, ctype = ctype,
token = token token = token
)
val dummyPayload = {
val ownerAccount = getAccountByUserName(repository.owner).get
val commits =
if (JGitUtil.isEmpty(git)) List.empty
else
git.log
.add(git.getRepository.resolve(repository.repository.defaultBranch))
.setMaxCount(4)
.call
.iterator
.asScala
.map(new CommitInfo(_))
.toList
val pushedCommit = commits.drop(1)
WebHookPushPayload(
git = git,
sender = ownerAccount,
refName = "refs/heads/" + repository.repository.defaultBranch,
repositoryInfo = repository,
commits = pushedCommit,
repositoryOwner = ownerAccount,
oldId = commits.lastOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId()),
newId = commits.headOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId())
) )
val dummyPayload = { }
val ownerAccount = getAccountByUserName(repository.owner).get
val commits =
if (JGitUtil.isEmpty(git)) List.empty
else
git.log
.add(git.getRepository.resolve(repository.repository.defaultBranch))
.setMaxCount(4)
.call
.iterator
.asScala
.map(new CommitInfo(_))
.toList
val pushedCommit = commits.drop(1)
WebHookPushPayload( val (webHook, json, reqFuture, resFuture) =
git = git, callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload, context.settings).head
sender = ownerAccount,
refName = "refs/heads/" + repository.repository.defaultBranch,
repositoryInfo = repository,
commits = pushedCommit,
repositoryOwner = ownerAccount,
oldId = commits.lastOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId()),
newId = commits.headOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId())
)
}
val (webHook, json, reqFuture, resFuture) = val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload, context.settings).head case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
case e: org.apache.http.client.ClientProtocolException => Map("error" -> ("invalid url"))
case NonFatal(e) => Map("error" -> (s"${e.getClass} ${e.getMessage}"))
}
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = { contentType = formats("json")
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage)) org.json4s.jackson.Serialization.write(
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url")) Map(
case e: org.apache.http.client.ClientProtocolException => Map("error" -> ("invalid url")) "url" -> url,
case NonFatal(e) => Map("error" -> (s"${e.getClass} ${e.getMessage}")) "request" -> Await.result(
} reqFuture
.map(req =>
contentType = formats("json") Map(
org.json4s.jackson.Serialization.write( "headers" -> _headers(req.getAllHeaders),
Map( "payload" -> json
"url" -> url,
"request" -> Await.result(
reqFuture
.map(
req =>
Map(
"headers" -> _headers(req.getAllHeaders),
"payload" -> json
)
) )
.recover(toErrorMap), )
20 seconds .recover(toErrorMap),
), 20 seconds
"response" -> Await.result( ),
resFuture "response" -> Await.result(
.map( resFuture
res => .map(res =>
Map( Map(
"status" -> res.getStatusLine.getStatusCode, "status" -> res.getStatusLine.getStatusCode,
"body" -> EntityUtils.toString(res.getEntity()), "body" -> EntityUtils.toString(res.getEntity()),
"headers" -> _headers(res.getAllHeaders()) "headers" -> _headers(res.getAllHeaders())
)
) )
.recover(toErrorMap), )
20 seconds .recover(toErrorMap),
) 20 seconds
) )
) )
)
} }
}) })
@@ -380,9 +377,8 @@ trait RepositorySettingsControllerBase extends ControllerBase {
* Display the web hook edit page. * Display the web hook edit page.
*/ */
get("/:owner/:repository/settings/hooks/edit")(ownerOnly { repository => get("/:owner/:repository/settings/hooks/edit")(ownerOnly { repository =>
getWebHook(repository.owner, repository.name, params("url")).map { getWebHook(repository.owner, repository.name, params("url")).map { case (webhook, events) =>
case (webhook, events) => html.edithook(webhook, events, repository, false)
html.edithook(webhook, events, repository, false)
} getOrElse NotFound() } getOrElse NotFound()
}) })
@@ -406,23 +402,22 @@ trait RepositorySettingsControllerBase extends ControllerBase {
* Rename repository. * Rename repository.
*/ */
post("/:owner/:repository/settings/rename", renameForm)(ownerOnly { (form, repository) => post("/:owner/:repository/settings/rename", renameForm)(ownerOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (context.settings.basicBehavior.repositoryOperation.rename || loginAccount.isAdmin) {
if (context.settings.basicBehavior.repositoryOperation.rename || loginAccount.isAdmin) { if (repository.name != form.repositoryName) {
if (repository.name != form.repositoryName) { // Update database and move git repository
// Update database and move git repository renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName) // Record activity log
// Record activity log val renameInfo = RenameRepositoryInfo(
val renameInfo = RenameRepositoryInfo( repository.owner,
repository.owner, form.repositoryName,
form.repositoryName, loginAccount.userName,
loginAccount.userName, repository.name
repository.name )
) recordActivity(renameInfo)
recordActivity(renameInfo) }
} redirect(s"/${repository.owner}/${form.repositoryName}")
redirect(s"/${repository.owner}/${form.repositoryName}") } else Forbidden()
} else Forbidden()
} }
}) })
@@ -430,24 +425,23 @@ trait RepositorySettingsControllerBase extends ControllerBase {
* Transfer repository ownership. * Transfer repository ownership.
*/ */
post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) => post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (context.settings.basicBehavior.repositoryOperation.transfer || loginAccount.isAdmin) {
if (context.settings.basicBehavior.repositoryOperation.transfer || loginAccount.isAdmin) { // Change repository owner
// Change repository owner if (repository.owner != form.newOwner) {
if (repository.owner != form.newOwner) { // Update database and move git repository
// Update database and move git repository renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
renameRepository(repository.owner, repository.name, form.newOwner, repository.name) // Record activity log
// Record activity log val renameInfo = RenameRepositoryInfo(
val renameInfo = RenameRepositoryInfo( form.newOwner,
form.newOwner, repository.name,
repository.name, loginAccount.userName,
loginAccount.userName, repository.owner
repository.owner )
) recordActivity(renameInfo)
recordActivity(renameInfo) }
} redirect(s"/${form.newOwner}/${repository.name}")
redirect(s"/${form.newOwner}/${repository.name}") } else Forbidden()
} else Forbidden()
} }
}) })

View File

@@ -55,16 +55,14 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
"bindAddress" -> mapping( "bindAddress" -> mapping(
"host" -> trim(label("Bind SSH host", optional(text()))), "host" -> trim(label("Bind SSH host", optional(text()))),
"port" -> trim(label("Bind SSH port", optional(number()))), "port" -> trim(label("Bind SSH port", optional(number()))),
)( )((hostOption, portOption) =>
(hostOption, portOption) => hostOption.map(h => SshAddress(h, portOption.getOrElse(DefaultSshPort), GenericSshUser))
hostOption.map(h => SshAddress(h, portOption.getOrElse(DefaultSshPort), GenericSshUser))
), ),
"publicAddress" -> mapping( "publicAddress" -> mapping(
"host" -> trim(label("Public SSH host", optional(text()))), "host" -> trim(label("Public SSH host", optional(text()))),
"port" -> trim(label("Public SSH port", optional(number()))), "port" -> trim(label("Public SSH port", optional(number()))),
)( )((hostOption, portOption) =>
(hostOption, portOption) => hostOption.map(h => SshAddress(h, portOption.getOrElse(PublicSshPort), GenericSshUser))
hostOption.map(h => SshAddress(h, portOption.getOrElse(PublicSshPort), GenericSshUser))
), ),
)(Ssh.apply), )(Ssh.apply),
"useSMTP" -> trim(label("SMTP", boolean())), "useSMTP" -> trim(label("SMTP", boolean())),
@@ -251,28 +249,27 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
val conn = request2Session(request).conn val conn = request2Session(request).conn
val meta = conn.getMetaData val meta = conn.getMetaData
val tables = ListBuffer[Table]() val tables = ListBuffer[Table]()
Using.resource(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) { Using.resource(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) { rs =>
rs => while (rs.next()) {
while (rs.next()) { val tableName = rs.getString("TABLE_NAME")
val tableName = rs.getString("TABLE_NAME")
val pkColumns = ListBuffer[String]() val pkColumns = ListBuffer[String]()
Using.resource(meta.getPrimaryKeys(null, null, tableName)) { rs => Using.resource(meta.getPrimaryKeys(null, null, tableName)) { rs =>
while (rs.next()) { while (rs.next()) {
pkColumns += rs.getString("COLUMN_NAME").toUpperCase pkColumns += rs.getString("COLUMN_NAME").toUpperCase
}
} }
val columns = ListBuffer[Column]()
Using.resource(meta.getColumns(null, "%", tableName, "%")) { rs =>
while (rs.next()) {
val columnName = rs.getString("COLUMN_NAME").toUpperCase
columns += Column(columnName, pkColumns.contains(columnName))
}
}
tables += Table(tableName.toUpperCase, columns.toSeq)
} }
val columns = ListBuffer[Column]()
Using.resource(meta.getColumns(null, "%", tableName, "%")) { rs =>
while (rs.next()) {
val columnName = rs.getString("COLUMN_NAME").toUpperCase
columns += Column(columnName, pkColumns.contains(columnName))
}
}
tables += Table(tableName.toUpperCase, columns.toSeq)
}
} }
html.dbviewer(tables.toSeq) html.dbviewer(tables.toSeq)
}) })
@@ -285,28 +282,26 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
if (trimmedQuery.nonEmpty) { if (trimmedQuery.nonEmpty) {
try { try {
val conn = request2Session(request).conn val conn = request2Session(request).conn
Using.resource(conn.prepareStatement(query)) { Using.resource(conn.prepareStatement(query)) { stmt =>
stmt => if (trimmedQuery.toUpperCase.startsWith("SELECT")) {
if (trimmedQuery.toUpperCase.startsWith("SELECT")) { Using.resource(stmt.executeQuery()) { rs =>
Using.resource(stmt.executeQuery()) { val meta = rs.getMetaData
rs => val columns = for (i <- 1 to meta.getColumnCount) yield {
val meta = rs.getMetaData meta.getColumnName(i)
val columns = for (i <- 1 to meta.getColumnCount) yield {
meta.getColumnName(i)
}
val result = ListBuffer[Map[String, String]]()
while (rs.next()) {
val row = columns.map { columnName =>
columnName -> Option(rs.getObject(columnName)).map(_.toString).getOrElse("<NULL>")
}.toMap
result += row
}
Ok(Serialization.write(Map("type" -> "query", "columns" -> columns, "rows" -> result)))
} }
} else { val result = ListBuffer[Map[String, String]]()
val rows = stmt.executeUpdate() while (rs.next()) {
Ok(Serialization.write(Map("type" -> "update", "rows" -> rows))) val row = columns.map { columnName =>
columnName -> Option(rs.getObject(columnName)).map(_.toString).getOrElse("<NULL>")
}.toMap
result += row
}
Ok(Serialization.write(Map("type" -> "query", "columns" -> columns, "rows" -> result)))
} }
} else {
val rows = stmt.executeUpdate()
Ok(Serialization.write(Map("type" -> "update", "rows" -> rows)))
}
} }
} catch { } catch {
case e: Exception => case e: Exception =>
@@ -323,7 +318,9 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
post("/admin/system", form)(adminOnly { form => post("/admin/system", form)(adminOnly { form =>
saveSystemSettings(form) saveSystemSettings(form)
if (form.ssh.bindAddress != context.settings.sshBindAddress || form.ssh.publicAddress != context.settings.sshPublicAddress) { if (
form.ssh.bindAddress != context.settings.sshBindAddress || form.ssh.publicAddress != context.settings.sshPublicAddress
) {
SshServer.stop() SshServer.stop()
for { for {
bindAddress <- form.ssh.bindAddress bindAddress <- form.ssh.bindAddress
@@ -420,44 +417,43 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
post("/admin/users/:name/_edituser", editUserForm)(adminOnly { form => post("/admin/users/:name/_edituser", editUserForm)(adminOnly { form =>
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName, true).map { getAccountByUserName(userName, true).map { account =>
account => if (account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(account)) {
if (account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(account)) { flash.update("error", "Account can't be turned off because this is last one administrator.")
flash.update("error", "Account can't be turned off because this is last one administrator.") redirect(s"/admin/users/${userName}/_edituser")
redirect(s"/admin/users/${userName}/_edituser") } else {
} else { if (form.isRemoved) {
if (form.isRemoved) { // Remove repositories
// Remove repositories // getRepositoryNamesOfUser(userName).foreach { repositoryName =>
// getRepositoryNamesOfUser(userName).foreach { repositoryName => // deleteRepository(userName, repositoryName)
// deleteRepository(userName, repositoryName) // FileUtils.deleteDirectory(getRepositoryDir(userName, repositoryName))
// FileUtils.deleteDirectory(getRepositoryDir(userName, repositoryName)) // FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
// FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName)) // FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName)) // }
// } // Remove from GROUP_MEMBER and COLLABORATOR
// Remove from GROUP_MEMBER and COLLABORATOR removeUserRelatedData(userName)
removeUserRelatedData(userName)
}
updateAccount(
account.copy(
password = form.password.map(pbkdf2_sha256).getOrElse(account.password),
fullName = form.fullName,
mailAddress = form.mailAddress,
isAdmin = form.isAdmin,
description = form.description,
url = form.url,
isRemoved = form.isRemoved
)
)
updateImage(userName, form.fileId, form.clearImage)
updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
// call hooks
if (form.isRemoved) PluginRegistry().getAccountHooks.foreach(_.deleted(userName))
redirect("/admin/users")
} }
updateAccount(
account.copy(
password = form.password.map(pbkdf2_sha256).getOrElse(account.password),
fullName = form.fullName,
mailAddress = form.mailAddress,
isAdmin = form.isAdmin,
description = form.description,
url = form.url,
isRemoved = form.isRemoved
)
)
updateImage(userName, form.fileId, form.clearImage)
updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
// call hooks
if (form.isRemoved) PluginRegistry().getAccountHooks.foreach(_.deleted(userName))
redirect("/admin/users")
}
} getOrElse NotFound() } getOrElse NotFound()
}) })
@@ -498,13 +494,12 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
} }
.toList .toList
getAccountByUserName(groupName, true).map { getAccountByUserName(groupName, true).map { account =>
account => updateGroup(groupName, form.description, form.url, form.isRemoved)
updateGroup(groupName, form.description, form.url, form.isRemoved)
if (form.isRemoved) { if (form.isRemoved) {
// Remove from GROUP_MEMBER // Remove from GROUP_MEMBER
updateGroupMembers(form.groupName, Nil) updateGroupMembers(form.groupName, Nil)
// // Remove repositories // // Remove repositories
// getRepositoryNamesOfUser(form.groupName).foreach { repositoryName => // getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
// deleteRepository(groupName, repositoryName) // deleteRepository(groupName, repositoryName)
@@ -512,9 +507,9 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
// FileUtils.deleteDirectory(getWikiRepositoryDir(groupName, repositoryName)) // FileUtils.deleteDirectory(getWikiRepositoryDir(groupName, repositoryName))
// FileUtils.deleteDirectory(getTemporaryDir(groupName, repositoryName)) // FileUtils.deleteDirectory(getTemporaryDir(groupName, repositoryName))
// } // }
} else { } else {
// Update GROUP_MEMBER // Update GROUP_MEMBER
updateGroupMembers(form.groupName, members) updateGroupMembers(form.groupName, members)
// // Update COLLABORATOR for group repositories // // Update COLLABORATOR for group repositories
// getRepositoryNamesOfUser(form.groupName).foreach { repositoryName => // getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
// removeCollaborators(form.groupName, repositoryName) // removeCollaborators(form.groupName, repositoryName)
@@ -522,10 +517,10 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
// addCollaborator(form.groupName, repositoryName, userName) // addCollaborator(form.groupName, repositoryName, userName)
// } // }
// } // }
} }
updateImage(form.groupName, form.fileId, form.clearImage) updateImage(form.groupName, form.fileId, form.clearImage)
redirect("/admin/users") redirect("/admin/users")
} getOrElse NotFound() } getOrElse NotFound()
}) })
@@ -565,9 +560,11 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
private def members: Constraint = private def members: Constraint =
new Constraint() { new Constraint() {
override def validate(name: String, value: String, messages: Messages): Option[String] = { override def validate(name: String, value: String, messages: Messages): Option[String] = {
if (value.split(",").exists { if (
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean } value.split(",").exists {
}) None _.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
}
) None
else Some("Must select one manager at least.") else Some("Must select one manager at least.")
} }
} }

View File

@@ -84,9 +84,8 @@ trait ValidationSupport extends FormSupport { self: ServletBase with JacksonJson
* Converts errors to JSON. * Converts errors to JSON.
*/ */
private def toJson(errors: Seq[(String, String)]): JObject = private def toJson(errors: Seq[(String, String)]): JObject =
JObject(errors.map { JObject(errors.map { case (key, value) =>
case (key, value) => JField(key, JString(value))
JField(key, JString(value))
}.toList) }.toList)
} }

View File

@@ -146,39 +146,37 @@ trait WikiControllerBase extends ControllerBase {
}) })
get("/:owner/:repository/wiki/:page/_revert/:commitId")(readableUsersOnly { repository => get("/:owner/:repository/wiki/:page/_revert/:commitId")(readableUsersOnly { repository =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (isEditable(repository)) {
if (isEditable(repository)) { val pageName = StringUtil.urlDecode(params("page"))
val pageName = StringUtil.urlDecode(params("page")) val Array(from, to) = params("commitId").split("\\.\\.\\.")
val Array(from, to) = params("commitId").split("\\.\\.\\.") val branch = getWikiBranch(repository.owner, repository.name)
val branch = getWikiBranch(repository.owner, repository.name)
if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, Some(pageName), branch)) { if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, Some(pageName), branch)) {
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}") redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
} else { } else {
flash.update("info", "This patch was not able to be reversed.") flash.update("info", "This patch was not able to be reversed.")
redirect( redirect(
s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}" s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}"
) )
} }
} else Unauthorized() } else Unauthorized()
} }
}) })
get("/:owner/:repository/wiki/_revert/:commitId")(readableUsersOnly { repository => get("/:owner/:repository/wiki/_revert/:commitId")(readableUsersOnly { repository =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (isEditable(repository)) {
if (isEditable(repository)) { val Array(from, to) = params("commitId").split("\\.\\.\\.")
val Array(from, to) = params("commitId").split("\\.\\.\\.") val branch = getWikiBranch(repository.owner, repository.name)
val branch = getWikiBranch(repository.owner, repository.name)
if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, None, branch)) { if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, None, branch)) {
redirect(s"/${repository.owner}/${repository.name}/wiki") redirect(s"/${repository.owner}/${repository.name}/wiki")
} else { } else {
flash.update("info", "This patch was not able to be reversed.") flash.update("info", "This patch was not able to be reversed.")
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}") redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
} }
} else Unauthorized() } else Unauthorized()
} }
}) })
@@ -192,36 +190,34 @@ trait WikiControllerBase extends ControllerBase {
}) })
post("/:owner/:repository/wiki/_edit", editForm)(readableUsersOnly { (form, repository) => post("/:owner/:repository/wiki/_edit", editForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (isEditable(repository)) {
if (isEditable(repository)) { saveWikiPage(
saveWikiPage( repository.owner,
repository.owner, repository.name,
repository.name, form.currentPageName,
form.currentPageName, form.pageName,
form.pageName, appendNewLine(convertLineSeparator(form.content, "LF"), "LF"),
appendNewLine(convertLineSeparator(form.content, "LF"), "LF"), loginAccount,
loginAccount, form.message.getOrElse(""),
form.message.getOrElse(""), Some(form.id)
Some(form.id) ).foreach { commitId =>
).foreach { updateLastActivityDate(repository.owner, repository.name)
commitId => val wikiEditInfo =
updateLastActivityDate(repository.owner, repository.name) EditWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName, commitId)
val wikiEditInfo = recordActivity(wikiEditInfo)
EditWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName, commitId) callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
recordActivity(wikiEditInfo) getAccountByUserName(repository.owner).map { repositoryUser =>
callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) { WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
getAccountByUserName(repository.owner).map { repositoryUser => }
WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
}
}
} }
if (notReservedPageName(form.pageName)) { }
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}") if (notReservedPageName(form.pageName)) {
} else { redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
redirect(s"/${repository.owner}/${repository.name}/wiki") } else {
} redirect(s"/${repository.owner}/${repository.name}/wiki")
} else Unauthorized() }
} else Unauthorized()
} }
}) })
@@ -232,64 +228,61 @@ trait WikiControllerBase extends ControllerBase {
}) })
post("/:owner/:repository/wiki/_new", newForm)(readableUsersOnly { (form, repository) => post("/:owner/:repository/wiki/_new", newForm)(readableUsersOnly { (form, repository) =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (isEditable(repository)) {
if (isEditable(repository)) { saveWikiPage(
saveWikiPage( repository.owner,
repository.owner, repository.name,
repository.name, form.currentPageName,
form.currentPageName, form.pageName,
form.pageName, form.content,
form.content, loginAccount,
loginAccount, form.message.getOrElse(""),
form.message.getOrElse(""), None
None ).foreach { commitId =>
).foreach { updateLastActivityDate(repository.owner, repository.name)
commitId => val createWikiPageInfo =
updateLastActivityDate(repository.owner, repository.name) CreateWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName)
val createWikiPageInfo = recordActivity(createWikiPageInfo)
CreateWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName) callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
recordActivity(createWikiPageInfo) getAccountByUserName(repository.owner).map { repositoryUser =>
callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) { WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
getAccountByUserName(repository.owner).map { repositoryUser => }
WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
}
}
} }
}
if (notReservedPageName(form.pageName)) { if (notReservedPageName(form.pageName)) {
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}") redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
} else { } else {
redirect(s"/${repository.owner}/${repository.name}/wiki") redirect(s"/${repository.owner}/${repository.name}/wiki")
} }
} else Unauthorized() } else Unauthorized()
} }
}) })
get("/:owner/:repository/wiki/:page/_delete")(readableUsersOnly { repository => get("/:owner/:repository/wiki/:page/_delete")(readableUsersOnly { repository =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => if (isEditable(repository)) {
if (isEditable(repository)) { val pageName = StringUtil.urlDecode(params("page"))
val pageName = StringUtil.urlDecode(params("page")) deleteWikiPage(
deleteWikiPage( repository.owner,
repository.owner, repository.name,
repository.name, pageName,
pageName, loginAccount.fullName,
loginAccount.fullName, loginAccount.mailAddress,
loginAccount.mailAddress, s"Destroyed ${pageName}"
s"Destroyed ${pageName}" )
) val deleteWikiInfo = DeleteWikiInfo(
val deleteWikiInfo = DeleteWikiInfo( repository.owner,
repository.owner, repository.name,
repository.name, loginAccount.userName,
loginAccount.userName, pageName
pageName )
) recordActivity(deleteWikiInfo)
recordActivity(deleteWikiInfo) updateLastActivityDate(repository.owner, repository.name)
updateLastActivityDate(repository.owner, repository.name)
redirect(s"/${repository.owner}/${repository.name}/wiki") redirect(s"/${repository.owner}/${repository.name}/wiki")
} else Unauthorized() } else Unauthorized()
} }
}) })

View File

@@ -58,27 +58,25 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
* https://docs.github.com/en/free-pro-team@latest/rest/reference/git#create-a-reference * https://docs.github.com/en/free-pro-team@latest/rest/reference/git#create-a-reference
*/ */
post("/api/v3/repos/:owner/:repository/git/refs")(writableUsersOnly { repository => post("/api/v3/repos/:owner/:repository/git/refs")(writableUsersOnly { repository =>
extractFromJsonBody[CreateARef].map { extractFromJsonBody[CreateARef].map { data =>
data => Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { val ref = git.getRepository.findRef(data.ref)
git => if (ref == null) {
val ref = git.getRepository.findRef(data.ref) val update = git.getRepository.updateRef(data.ref)
if (ref == null) { update.setNewObjectId(ObjectId.fromString(data.sha))
val update = git.getRepository.updateRef(data.ref) val result = update.update()
update.setNewObjectId(ObjectId.fromString(data.sha)) result match {
val result = update.update() case Result.NEW =>
result match { JsonFormat(
case Result.NEW => ApiRef
JsonFormat( .fromRef(RepositoryName(repository.owner, repository.name), git.getRepository.findRef(data.ref))
ApiRef )
.fromRef(RepositoryName(repository.owner, repository.name), git.getRepository.findRef(data.ref)) case _ => UnprocessableEntity(result.name())
) }
case _ => UnprocessableEntity(result.name()) } else {
} UnprocessableEntity("Ref already exists.")
} else {
UnprocessableEntity("Ref already exists.")
}
} }
}
} getOrElse BadRequest() } getOrElse BadRequest()
}) })
@@ -88,24 +86,23 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
*/ */
patch("/api/v3/repos/:owner/:repository/git/refs/*")(writableUsersOnly { repository => patch("/api/v3/repos/:owner/:repository/git/refs/*")(writableUsersOnly { repository =>
val refName = multiParams("splat").mkString("/") val refName = multiParams("splat").mkString("/")
extractFromJsonBody[UpdateARef].map { extractFromJsonBody[UpdateARef].map { data =>
data => Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => val ref = git.getRepository.findRef(refName)
val ref = git.getRepository.findRef(refName) if (ref == null) {
if (ref == null) { UnprocessableEntity("Ref does not exist.")
UnprocessableEntity("Ref does not exist.") } else {
} else { val update = git.getRepository.updateRef(ref.getName)
val update = git.getRepository.updateRef(ref.getName) update.setNewObjectId(ObjectId.fromString(data.sha))
update.setNewObjectId(ObjectId.fromString(data.sha)) update.setForceUpdate(data.force)
update.setForceUpdate(data.force) val result = update.update()
val result = update.update() result match {
result match { case Result.FORCED | Result.FAST_FORWARD | Result.NO_CHANGE =>
case Result.FORCED | Result.FAST_FORWARD | Result.NO_CHANGE => JsonFormat(ApiRef.fromRef(RepositoryName(repository), git.getRepository.findRef(refName)))
JsonFormat(ApiRef.fromRef(RepositoryName(repository), git.getRepository.findRef(refName))) case _ => UnprocessableEntity(result.name())
case _ => UnprocessableEntity(result.name())
}
} }
} }
}
} getOrElse BadRequest() } getOrElse BadRequest()
}) })

View File

@@ -23,9 +23,8 @@ trait ApiIssueCommentControllerBase extends ControllerBase {
issueId <- params("id").toIntOpt issueId <- params("id").toIntOpt
comments = getCommentsForApi(repository.owner, repository.name, issueId) comments = getCommentsForApi(repository.owner, repository.name, issueId)
} yield { } yield {
JsonFormat(comments.map { JsonFormat(comments.map { case (issueComment, user, issue) =>
case (issueComment, user, issue) => ApiComment(issueComment, RepositoryName(repository), issueId, ApiUser(user), issue.isPullRequest)
ApiComment(issueComment, RepositoryName(repository), issueId, ApiUser(user), issue.isPullRequest)
}) })
}) getOrElse NotFound() }) getOrElse NotFound()
}) })

View File

@@ -29,7 +29,7 @@ trait ApiIssueControllerBase extends ControllerBase {
val page = IssueSearchCondition.page(request) val page = IssueSearchCondition.page(request)
// TODO: more api spec condition // TODO: more api spec condition
val condition = IssueSearchCondition(request) val condition = IssueSearchCondition(request)
//val baseOwner = getAccountByUserName(repository.owner).get // val baseOwner = getAccountByUserName(repository.owner).get
val issues: List[(Issue, Account, List[Account])] = val issues: List[(Issue, Account, List[Account])] =
searchIssueByApi( searchIssueByApi(
@@ -39,17 +39,16 @@ trait ApiIssueControllerBase extends ControllerBase {
repos = repository.owner -> repository.name repos = repository.owner -> repository.name
) )
JsonFormat(issues.map { JsonFormat(issues.map { case (issue, issueUser, assigneeUsers) =>
case (issue, issueUser, assigneeUsers) => ApiIssue(
ApiIssue( issue = issue,
issue = issue, repositoryName = RepositoryName(repository),
repositoryName = RepositoryName(repository), user = ApiUser(issueUser),
user = ApiUser(issueUser), assignees = assigneeUsers.map(ApiUser(_)),
assignees = assigneeUsers.map(ApiUser(_)), labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
labels = getIssueLabels(repository.owner, repository.name, issue.issueId) .map(ApiLabel(_, RepositoryName(repository))),
.map(ApiLabel(_, RepositoryName(repository))), issue.milestoneId.flatMap { getApiMilestone(repository, _) }
issue.milestoneId.flatMap { getApiMilestone(repository, _) } )
)
}) })
}) })
@@ -126,7 +125,7 @@ trait ApiIssueControllerBase extends ControllerBase {
*/ */
/* /*
* vii. Unlock an issue * vii. Unlock an issue
* https://developer.github.com/v3/issues/#unlock-an-issue * https://developer.github.com/v3/issues/#unlock-an-issue
*/ */
} }

View File

@@ -69,25 +69,24 @@ trait ApiIssueLabelControllerBase extends ControllerBase {
data <- extractFromJsonBody[CreateALabel] if data.isValid data <- extractFromJsonBody[CreateALabel] if data.isValid
} yield { } yield {
LockUtil.lock(RepositoryName(repository).fullName) { LockUtil.lock(RepositoryName(repository).fullName) {
getLabel(repository.owner, repository.name, params("labelName")).map { getLabel(repository.owner, repository.name, params("labelName")).map { label =>
label => if (getLabel(repository.owner, repository.name, data.name).isEmpty) {
if (getLabel(repository.owner, repository.name, data.name).isEmpty) { updateLabel(repository.owner, repository.name, label.labelId, data.name, data.color)
updateLabel(repository.owner, repository.name, label.labelId, data.name, data.color) JsonFormat(
JsonFormat( ApiLabel(
ApiLabel( getLabel(repository.owner, repository.name, label.labelId).get,
getLabel(repository.owner, repository.name, label.labelId).get, RepositoryName(repository)
RepositoryName(repository)
)
) )
} else { )
// TODO ApiError should support errors field to enhance compatibility of GitHub API } else {
UnprocessableEntity( // TODO ApiError should support errors field to enhance compatibility of GitHub API
ApiError( UnprocessableEntity(
"Validation Failed", ApiError(
Some("https://developer.github.com/v3/issues/labels/#create-a-label") "Validation Failed",
) Some("https://developer.github.com/v3/issues/labels/#create-a-label")
) )
} )
}
} getOrElse NotFound() } getOrElse NotFound()
} }
}) getOrElse NotFound() }) getOrElse NotFound()
@@ -189,7 +188,7 @@ trait ApiIssueLabelControllerBase extends ControllerBase {
}) })
/* /*
* xi Get labels for every issue in a milestone * xi Get labels for every issue in a milestone
* https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone * https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone
*/ */
} }

View File

@@ -16,8 +16,10 @@ trait ApiIssueMilestoneControllerBase extends ControllerBase {
get("/api/v3/repos/:owner/:repository/milestones")(referrersOnly { repository => get("/api/v3/repos/:owner/:repository/milestones")(referrersOnly { repository =>
val state = params.getOrElse("state", "all") val state = params.getOrElse("state", "all")
// TODO "sort", "direction" params should be implemented. // TODO "sort", "direction" params should be implemented.
val apiMilestones = (for (milestoneWithIssue <- getMilestonesWithIssueCount(repository.owner, repository.name) val apiMilestones = (for (
.sortBy(p => p._1.milestoneId)) milestoneWithIssue <- getMilestonesWithIssueCount(repository.owner, repository.name)
.sortBy(p => p._1.milestoneId)
)
yield { yield {
ApiMilestone( ApiMilestone(
repository.repository, repository.repository,

View File

@@ -71,6 +71,6 @@ trait ApiOrganizationControllerBase extends ControllerBase {
*/ */
/* /*
* should implement delete an organization API? * should implement delete an organization API?
*/ */
} }

View File

@@ -48,19 +48,18 @@ trait ApiPullRequestControllerBase extends ControllerBase {
repos = repository.owner -> repository.name repos = repository.owner -> repository.name
) )
JsonFormat(issues.map { JsonFormat(issues.map { case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner, assignees) =>
case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner, assignees) => ApiPullRequest(
ApiPullRequest( issue = issue,
issue = issue, pullRequest = pullRequest,
pullRequest = pullRequest, headRepo = ApiRepository(headRepo, ApiUser(headOwner)),
headRepo = ApiRepository(headRepo, ApiUser(headOwner)), baseRepo = ApiRepository(repository, ApiUser(baseOwner)),
baseRepo = ApiRepository(repository, ApiUser(baseOwner)), user = ApiUser(issueUser),
user = ApiUser(issueUser), labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
labels = getIssueLabels(repository.owner, repository.name, issue.issueId) .map(ApiLabel(_, RepositoryName(repository))),
.map(ApiLabel(_, RepositoryName(repository))), assignees = assignees.map(ApiUser.apply),
assignees = assignees.map(ApiUser.apply), mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId) )
)
}) })
}) })
@@ -89,38 +88,37 @@ trait ApiPullRequestControllerBase extends ControllerBase {
case Left(createPullReq) => case Left(createPullReq) =>
val (reqOwner, reqBranch) = parseCompareIdentifier(createPullReq.head, repository.owner) val (reqOwner, reqBranch) = parseCompareIdentifier(createPullReq.head, repository.owner)
getRepository(reqOwner, repository.name) getRepository(reqOwner, repository.name)
.flatMap { .flatMap { forkedRepository =>
forkedRepository => getPullRequestCommitFromTo(repository, forkedRepository, createPullReq.base, reqBranch) match {
getPullRequestCommitFromTo(repository, forkedRepository, createPullReq.base, reqBranch) match { case (Some(commitIdFrom), Some(commitIdTo)) =>
case (Some(commitIdFrom), Some(commitIdTo)) => val issueId = insertIssue(
val issueId = insertIssue( owner = repository.owner,
owner = repository.owner, repository = repository.name,
repository = repository.name, loginUser = context.loginAccount.get.userName,
loginUser = context.loginAccount.get.userName, title = createPullReq.title,
title = createPullReq.title, content = createPullReq.body,
content = createPullReq.body, milestoneId = None,
milestoneId = None, priorityId = None,
priorityId = None, isPullRequest = true
isPullRequest = true )
)
createPullRequest( createPullRequest(
originRepository = repository, originRepository = repository,
issueId = issueId, issueId = issueId,
originBranch = createPullReq.base, originBranch = createPullReq.base,
requestUserName = reqOwner, requestUserName = reqOwner,
requestRepositoryName = repository.name, requestRepositoryName = repository.name,
requestBranch = reqBranch, requestBranch = reqBranch,
commitIdFrom = commitIdFrom.getName, commitIdFrom = commitIdFrom.getName,
commitIdTo = commitIdTo.getName, commitIdTo = commitIdTo.getName,
isDraft = createPullReq.draft.getOrElse(false), isDraft = createPullReq.draft.getOrElse(false),
loginAccount = context.loginAccount.get, loginAccount = context.loginAccount.get,
settings = context.settings settings = context.settings
) )
getApiPullRequest(repository, issueId).map(JsonFormat(_)) getApiPullRequest(repository, issueId).map(JsonFormat(_))
case _ => case _ =>
None None
} }
} }
.getOrElse { .getOrElse {
NotFound() NotFound()
@@ -128,28 +126,27 @@ trait ApiPullRequestControllerBase extends ControllerBase {
case Right(createPullReqAlt) => case Right(createPullReqAlt) =>
val (reqOwner, reqBranch) = parseCompareIdentifier(createPullReqAlt.head, repository.owner) val (reqOwner, reqBranch) = parseCompareIdentifier(createPullReqAlt.head, repository.owner)
getRepository(reqOwner, repository.name) getRepository(reqOwner, repository.name)
.flatMap { .flatMap { forkedRepository =>
forkedRepository => getPullRequestCommitFromTo(repository, forkedRepository, createPullReqAlt.base, reqBranch) match {
getPullRequestCommitFromTo(repository, forkedRepository, createPullReqAlt.base, reqBranch) match { case (Some(commitIdFrom), Some(commitIdTo)) =>
case (Some(commitIdFrom), Some(commitIdTo)) => changeIssueToPullRequest(repository.owner, repository.name, createPullReqAlt.issue)
changeIssueToPullRequest(repository.owner, repository.name, createPullReqAlt.issue) createPullRequest(
createPullRequest( originRepository = repository,
originRepository = repository, issueId = createPullReqAlt.issue,
issueId = createPullReqAlt.issue, originBranch = createPullReqAlt.base,
originBranch = createPullReqAlt.base, requestUserName = reqOwner,
requestUserName = reqOwner, requestRepositoryName = repository.name,
requestRepositoryName = repository.name, requestBranch = reqBranch,
requestBranch = reqBranch, commitIdFrom = commitIdFrom.getName,
commitIdFrom = commitIdFrom.getName, commitIdTo = commitIdTo.getName,
commitIdTo = commitIdTo.getName, isDraft = false,
isDraft = false, loginAccount = context.loginAccount.get,
loginAccount = context.loginAccount.get, settings = context.settings
settings = context.settings )
) getApiPullRequest(repository, createPullReqAlt.issue).map(JsonFormat(_))
getApiPullRequest(repository, createPullReqAlt.issue).map(JsonFormat(_)) case _ =>
case _ => None
None }
}
} }
.getOrElse { .getOrElse {
NotFound() NotFound()
@@ -190,26 +187,24 @@ trait ApiPullRequestControllerBase extends ControllerBase {
get("/api/v3/repos/:owner/:repository/pulls/:id/commits")(referrersOnly { repository => get("/api/v3/repos/:owner/:repository/pulls/:id/commits")(referrersOnly { repository =>
val owner = repository.owner val owner = repository.owner
val name = repository.name val name = repository.name
params("id").toIntOpt.flatMap { params("id").toIntOpt.flatMap { issueId =>
issueId => getPullRequest(owner, name, issueId) map { case (issue, pullreq) =>
getPullRequest(owner, name, issueId) map { Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
case (issue, pullreq) => val oldId = git.getRepository.resolve(pullreq.commitIdFrom)
Using.resource(Git.open(getRepositoryDir(owner, name))) { git => val newId = git.getRepository.resolve(pullreq.commitIdTo)
val oldId = git.getRepository.resolve(pullreq.commitIdFrom) val repoFullName = RepositoryName(repository)
val newId = git.getRepository.resolve(pullreq.commitIdTo) val commits = git.log
val repoFullName = RepositoryName(repository) .addRange(oldId, newId)
val commits = git.log .call
.addRange(oldId, newId) .iterator
.call .asScala
.iterator .map { c =>
.asScala ApiCommitListItem(new CommitInfo(c), repoFullName)
.map { c =>
ApiCommitListItem(new CommitInfo(c), repoFullName)
}
.toList
JsonFormat(commits)
} }
.toList
JsonFormat(commits)
} }
}
} getOrElse NotFound() } getOrElse NotFound()
}) })
/* /*
@@ -240,8 +235,8 @@ trait ApiPullRequestControllerBase extends ControllerBase {
*/ */
put("/api/v3/repos/:owner/:repository/pulls/:id/merge")(referrersOnly { repository => put("/api/v3/repos/:owner/:repository/pulls/:id/merge")(referrersOnly { repository =>
(for { (for {
//TODO: crash when body is empty // TODO: crash when body is empty
//TODO: Implement sha parameter // TODO: Implement sha parameter
data <- extractFromJsonBody[MergeAPullRequest] data <- extractFromJsonBody[MergeAPullRequest]
issueId <- params("id").toIntOpt issueId <- params("id").toIntOpt
(issue, pullReq) <- getPullRequest(repository.owner, repository.name, issueId) (issue, pullReq) <- getPullRequest(repository.owner, repository.name, issueId)
@@ -273,7 +268,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
repository, repository,
issueId, issueId,
context.loginAccount.get, context.loginAccount.get,
data.commit_message.getOrElse(""), //TODO: Implement commit_title data.commit_message.getOrElse(""), // TODO: Implement commit_title
strategy, strategy,
pullReq.isDraft, pullReq.isDraft,
context.settings context.settings

View File

@@ -119,40 +119,39 @@ trait ApiReleaseControllerBase extends ControllerBase {
* ix. Upload a release asset * ix. Upload a release asset
* https://developer.github.com/v3/repos/releases/#upload-a-release-asset * https://developer.github.com/v3/repos/releases/#upload-a-release-asset
*/ */
post("/api/v3/repos/:owner/:repository/releases/:tag/assets")(writableUsersOnly { post("/api/v3/repos/:owner/:repository/releases/:tag/assets")(writableUsersOnly { repository =>
repository => val name = params("name")
val name = params("name") val tag = params("tag")
val tag = params("tag") getRelease(repository.owner, repository.name, tag)
getRelease(repository.owner, repository.name, tag) .map { release =>
.map { release => val fileId = FileUtil.generateFileId
val fileId = FileUtil.generateFileId val buf = new Array[Byte](request.inputStream.available())
val buf = new Array[Byte](request.inputStream.available()) request.inputStream.read(buf)
request.inputStream.read(buf) FileUtils.writeByteArrayToFile(
FileUtils.writeByteArrayToFile( new File(
new File( getReleaseFilesDir(repository.owner, repository.name),
getReleaseFilesDir(repository.owner, repository.name), FileUtil.checkFilename(tag + "/" + fileId)
FileUtil.checkFilename(tag + "/" + fileId) ),
), buf
buf )
) createReleaseAsset(
createReleaseAsset( repository.owner,
repository.owner, repository.name,
repository.name, tag,
tag, fileId,
fileId, name,
name, request.contentLength.getOrElse(0),
request.contentLength.getOrElse(0), context.loginAccount.get
context.loginAccount.get )
) getReleaseAsset(repository.owner, repository.name, tag, fileId)
getReleaseAsset(repository.owner, repository.name, tag, fileId) .map { asset =>
.map { asset => JsonFormat(ApiReleaseAsset(asset, RepositoryName(repository)))
JsonFormat(ApiReleaseAsset(asset, RepositoryName(repository))) }
} .getOrElse {
.getOrElse { ApiError("Unknown error")
ApiError("Unknown error") }
} }
} .getOrElse(NotFound())
.getOrElse(NotFound())
}) })
/** /**
@@ -176,7 +175,7 @@ trait ApiReleaseControllerBase extends ControllerBase {
*/ */
/* /*
* xii. Delete a release asset * xii. Delete a release asset
* https://developer.github.com/v3/repos/releases/#edit-a-release-asset * https://developer.github.com/v3/repos/releases/#edit-a-release-asset
*/ */
} }

View File

@@ -47,21 +47,20 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
* https://docs.github.com/en/rest/reference/repos#get-a-branch * https://docs.github.com/en/rest/reference/repos#get-a-branch
*/ */
get("/api/v3/repos/:owner/:repository/branches/*")(referrersOnly { repository => get("/api/v3/repos/:owner/:repository/branches/*")(referrersOnly { repository =>
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
git => (for {
(for { branch <- params.get("splat") if repository.branchList.contains(branch)
branch <- params.get("splat") if repository.branchList.contains(branch) br <- getBranches(
br <- getBranches( git,
git, repository.repository.defaultBranch,
repository.repository.defaultBranch, repository.repository.originUserName.isEmpty
repository.repository.originUserName.isEmpty ).find(_.name == branch)
).find(_.name == branch) } yield {
} yield { val protection = getProtectedBranchInfo(repository.owner, repository.name, branch)
val protection = getProtectedBranchInfo(repository.owner, repository.name, branch) JsonFormat(
JsonFormat( ApiBranch(branch, ApiBranchCommit(br.commitId), ApiBranchProtection(protection))(RepositoryName(repository))
ApiBranch(branch, ApiBranchCommit(br.commitId), ApiBranchProtection(protection))(RepositoryName(repository)) )
) }) getOrElse NotFound()
}) getOrElse NotFound()
} }
}) })
@@ -275,30 +274,29 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
*/ */
patch("/api/v3/repos/:owner/:repository/branches/*")(ownerOnly { repository => patch("/api/v3/repos/:owner/:repository/branches/*")(ownerOnly { repository =>
import gitbucket.core.api._ import gitbucket.core.api._
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
git => (for {
(for { branch <- params.get("splat") if repository.branchList.contains(branch)
branch <- params.get("splat") if repository.branchList.contains(branch) protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection) br <- getBranches(
br <- getBranches( git,
git, repository.repository.defaultBranch,
repository.repository.defaultBranch, repository.repository.originUserName.isEmpty
repository.repository.originUserName.isEmpty ).find(_.name == branch)
).find(_.name == branch) } yield {
} yield { if (protection.enabled) {
if (protection.enabled) { enableBranchProtection(
enableBranchProtection( repository.owner,
repository.owner, repository.name,
repository.name, branch,
branch, protection.status.enforcement_level == ApiBranchProtection.Everyone,
protection.status.enforcement_level == ApiBranchProtection.Everyone, protection.status.contexts
protection.status.contexts )
) } else {
} else { disableBranchProtection(repository.owner, repository.name, branch)
disableBranchProtection(repository.owner, repository.name, branch) }
} JsonFormat(ApiBranch(branch, ApiBranchCommit(br.commitId), protection)(RepositoryName(repository)))
JsonFormat(ApiBranch(branch, ApiBranchCommit(br.commitId), protection)(RepositoryName(repository))) }) getOrElse NotFound()
}) getOrElse NotFound()
} }
}) })
} }

View File

@@ -43,56 +43,53 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
val path = params.get("path").filter(_.nonEmpty) val path = params.get("path").filter(_.nonEmpty)
val since = params.get("since").filter(_.nonEmpty) val since = params.get("since").filter(_.nonEmpty)
val until = params.get("until").filter(_.nonEmpty) val until = params.get("until").filter(_.nonEmpty)
Using.resource(Git.open(getRepositoryDir(owner, name))) { Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
git => val repo = git.getRepository
val repo = git.getRepository Using.resource(new RevWalk(repo)) { revWalk =>
Using.resource(new RevWalk(repo)) { val objectId = repo.resolve(sha)
revWalk => revWalk.markStart(revWalk.parseCommit(objectId))
val objectId = repo.resolve(sha) if (path.nonEmpty) {
revWalk.markStart(revWalk.parseCommit(objectId)) revWalk.setTreeFilter(
if (path.nonEmpty) { AndTreeFilter.create(PathFilterGroup.createFromStrings(path.get), TreeFilter.ANY_DIFF)
revWalk.setTreeFilter( )
AndTreeFilter.create(PathFilterGroup.createFromStrings(path.get), TreeFilter.ANY_DIFF)
)
}
val revfilters = new ListBuffer[(RevFilter)]()
if (author.nonEmpty) {
revfilters += AuthorRevFilter.create(author.get)
}
if (since.nonEmpty) {
revfilters += CommitTimeRevFilter.after(
Date.from(LocalDateTime.parse(since.get, ISO_DATE_TIME).toInstant(ZoneOffset.UTC))
)
}
if (until.nonEmpty) {
revfilters += CommitTimeRevFilter.before(
Date.from(LocalDateTime.parse(until.get, ISO_DATE_TIME).toInstant(ZoneOffset.UTC))
)
}
if (page > 1) {
revfilters += SkipRevFilter.create(page * per_page - 2)
}
revfilters += MaxCountRevFilter.create(per_page);
revWalk.setRevFilter(
if (revfilters.size > 1) {
AndRevFilter.create(revfilters.toArray)
} else {
revfilters(0)
}
)
JsonFormat(revWalk.asScala.map {
commit =>
val commitInfo = new CommitInfo(commit)
ApiCommits(
repositoryName = RepositoryName(repository),
commitInfo = commitInfo,
diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
commentCount = getCommitComment(repository.owner, repository.name, commitInfo.id).size
)
})
} }
val revfilters = new ListBuffer[(RevFilter)]()
if (author.nonEmpty) {
revfilters += AuthorRevFilter.create(author.get)
}
if (since.nonEmpty) {
revfilters += CommitTimeRevFilter.after(
Date.from(LocalDateTime.parse(since.get, ISO_DATE_TIME).toInstant(ZoneOffset.UTC))
)
}
if (until.nonEmpty) {
revfilters += CommitTimeRevFilter.before(
Date.from(LocalDateTime.parse(until.get, ISO_DATE_TIME).toInstant(ZoneOffset.UTC))
)
}
if (page > 1) {
revfilters += SkipRevFilter.create(page * per_page - 2)
}
revfilters += MaxCountRevFilter.create(per_page);
revWalk.setRevFilter(
if (revfilters.size > 1) {
AndRevFilter.create(revfilters.toArray)
} else {
revfilters(0)
}
)
JsonFormat(revWalk.asScala.map { commit =>
val commitInfo = new CommitInfo(commit)
ApiCommits(
repositoryName = RepositoryName(repository),
commitInfo = commitInfo,
diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
commentCount = getCommitComment(repository.owner, repository.name, commitInfo.id).size
)
})
}
} }
}) })
@@ -105,24 +102,23 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
val name = repository.name val name = repository.name
val sha = params("sha") val sha = params("sha")
Using.resource(Git.open(getRepositoryDir(owner, name))) { Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
git => val repo = git.getRepository
val repo = git.getRepository val objectId = repo.resolve(sha)
val objectId = repo.resolve(sha) val commitInfo = Using.resource(new RevWalk(repo)) { revWalk =>
val commitInfo = Using.resource(new RevWalk(repo)) { revWalk => new CommitInfo(revWalk.parseCommit(objectId))
new CommitInfo(revWalk.parseCommit(objectId)) }
}
JsonFormat( JsonFormat(
ApiCommits( ApiCommits(
repositoryName = RepositoryName(repository), repositoryName = RepositoryName(repository),
commitInfo = commitInfo, commitInfo = commitInfo,
diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true), diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress), author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress), committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
commentCount = getCommitComment(repository.owner, repository.name, sha).size commentCount = getCommitComment(repository.owner, repository.name, sha).size
)
) )
)
} }
}) })

View File

@@ -19,17 +19,16 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
* https://docs.github.com/en/rest/reference/repos#get-a-repository-readme * https://docs.github.com/en/rest/reference/repos#get-a-repository-readme
*/ */
get("/api/v3/repos/:owner/:repository/readme")(referrersOnly { repository => get("/api/v3/repos/:owner/:repository/readme")(referrersOnly { repository =>
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
git => val refStr = params.getOrElse("ref", repository.repository.defaultBranch)
val refStr = params.getOrElse("ref", repository.repository.defaultBranch) val files = getFileList(git, refStr, ".", maxFiles = context.settings.repositoryViewer.maxFiles)
val files = getFileList(git, refStr, ".", maxFiles = context.settings.repositoryViewer.maxFiles) files // files should be sorted alphabetically.
files // files should be sorted alphabetically. .find { file =>
.find { file => !file.isDirectory && RepositoryService.readmeFiles.contains(file.name.toLowerCase)
!file.isDirectory && RepositoryService.readmeFiles.contains(file.name.toLowerCase) } match {
} match { case Some(x) => getContents(repository = repository, path = x.name, refStr = refStr, ignoreCase = true)
case Some(x) => getContents(repository = repository, path = x.name, refStr = refStr, ignoreCase = true) case _ => NotFound()
case _ => NotFound() }
}
} }
}) })
@@ -134,67 +133,65 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
* requested #2112 * requested #2112
*/ */
put("/api/v3/repos/:owner/:repository/contents/*")(writableUsersOnly { repository => put("/api/v3/repos/:owner/:repository/contents/*")(writableUsersOnly { repository =>
context.withLoginAccount { context.withLoginAccount { loginAccount =>
loginAccount => JsonFormat(for {
JsonFormat(for { data <- extractFromJsonBody[CreateAFile]
data <- extractFromJsonBody[CreateAFile] } yield {
} yield { val branch = data.branch.getOrElse(repository.repository.defaultBranch)
val branch = data.branch.getOrElse(repository.repository.defaultBranch) val commit = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
val commit = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch)) revCommit.name
revCommit.name }
} val paths = multiParams("splat").head.split("/")
val paths = multiParams("splat").head.split("/") val path = paths.take(paths.size - 1).toList.mkString("/")
val path = paths.take(paths.size - 1).toList.mkString("/") Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { val fileInfo = getFileInfo(git, commit, path, false)
git =>
val fileInfo = getFileInfo(git, commit, path, false)
fileInfo match { fileInfo match {
case Some(f) if !data.sha.contains(f.id.getName) => case Some(f) if !data.sha.contains(f.id.getName) =>
ApiError( ApiError(
"The blob SHA is not matched.", "The blob SHA is not matched.",
Some("https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents") Some("https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents")
)
case _ =>
commitFile(
repository,
branch,
path,
Some(paths.last),
data.sha.map(_ => paths.last),
StringUtil.base64Decode(data.content),
data.message,
commit,
loginAccount,
data.committer.map(_.name).getOrElse(loginAccount.fullName),
data.committer.map(_.email).getOrElse(loginAccount.mailAddress),
context.settings
) match {
case Left(error) =>
ApiError(s"Failed to commit a file: ${error}", None)
case Right((_, None)) =>
ApiError("Failed to commit a file.", None)
case Right((commitId, Some(blobId))) =>
Map(
"content" -> ApiContents(
"file",
paths.last,
path,
blobId.name,
Some(data.content),
Some("base64")
)(RepositoryName(repository)),
"commit" -> ApiCommit(
git,
RepositoryName(repository),
new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
)
) )
case _ =>
commitFile(
repository,
branch,
path,
Some(paths.last),
data.sha.map(_ => paths.last),
StringUtil.base64Decode(data.content),
data.message,
commit,
loginAccount,
data.committer.map(_.name).getOrElse(loginAccount.fullName),
data.committer.map(_.email).getOrElse(loginAccount.mailAddress),
context.settings
) match {
case Left(error) =>
ApiError(s"Failed to commit a file: ${error}", None)
case Right((_, None)) =>
ApiError("Failed to commit a file.", None)
case Right((commitId, Some(blobId))) =>
Map(
"content" -> ApiContents(
"file",
paths.last,
path,
blobId.name,
Some(data.content),
Some("base64")
)(RepositoryName(repository)),
"commit" -> ApiCommit(
git,
RepositoryName(repository),
new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
)
)
}
} }
} }
}) }
})
} }
}) })
@@ -205,9 +202,9 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
*/ */
/* /*
* vi. Download a repository archive (tar/zip) * vi. Download a repository archive (tar/zip)
* https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-tar * https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-tar
* https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-zip * https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-zip
*/ */
} }

View File

@@ -47,9 +47,8 @@ trait ApiRepositoryStatusControllerBase extends ControllerBase {
ref <- params.get("ref") ref <- params.get("ref")
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref) sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
} yield { } yield {
JsonFormat(getCommitStatusesWithCreator(repository.owner, repository.name, sha).map { JsonFormat(getCommitStatusesWithCreator(repository.owner, repository.name, sha).map { case (status, creator) =>
case (status, creator) => ApiCommitStatus(status, ApiUser(creator))
ApiCommitStatus(status, ApiUser(creator))
}) })
}) getOrElse NotFound() }) getOrElse NotFound()
}) })

View File

@@ -113,8 +113,8 @@ trait ApiRepositoryWebhookControllerBase extends ControllerBase {
*/ */
/* /*
* vi. Test the push repository webhook * vi. Test the push repository webhook
* https://docs.github.com/en/rest/reference/repos#test-the-push-repository-webhook * https://docs.github.com/en/rest/reference/repos#test-the-push-repository-webhook
*/ */
} }

View File

@@ -40,8 +40,8 @@ case class CustomField(
) )
trait CustomFieldBehavior { trait CustomFieldBehavior {
def createHtml(repository: RepositoryInfo, fieldId: Int, fieldName: String, constraints: Option[String])( def createHtml(repository: RepositoryInfo, fieldId: Int, fieldName: String, constraints: Option[String])(implicit
implicit context: Context context: Context
): String ): String
def fieldHtml( def fieldHtml(
repository: RepositoryInfo, repository: RepositoryInfo,
@@ -51,8 +51,8 @@ trait CustomFieldBehavior {
constraints: Option[String], constraints: Option[String],
value: String, value: String,
editable: Boolean editable: Boolean
)( )(implicit
implicit context: Context context: Context
): String ): String
def validate(name: String, constraints: Option[String], value: String, messages: Messages): Option[String] def validate(name: String, constraints: Option[String], value: String, messages: Messages): Option[String]
} }
@@ -151,11 +151,11 @@ object CustomFieldBehavior {
sb.append("""<div>""") sb.append("""<div>""")
if (value == "") { if (value == "") {
sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">No ${StringUtil.escapeHtml( sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">No ${StringUtil.escapeHtml(
fieldName fieldName
)}</span></span>""") )}</span></span>""")
} else { } else {
sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">${StringUtil sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">${StringUtil
.escapeHtml(value)}</span></span>""") .escapeHtml(value)}</span></span>""")
} }
sb.toString() sb.toString()
} else { } else {
@@ -179,21 +179,19 @@ object CustomFieldBehavior {
val options = new StringBuilder() val options = new StringBuilder()
options.append( options.append(
s"""<li><a href="javascript:void(0);" class="custom-field-option-$fieldId" data-value=""><i class="octicon octicon-x"></i> Clear ${StringUtil s"""<li><a href="javascript:void(0);" class="custom-field-option-$fieldId" data-value=""><i class="octicon octicon-x"></i> Clear ${StringUtil
.escapeHtml(fieldName)}</a></li>""" .escapeHtml(fieldName)}</a></li>"""
) )
constraints.foreach { constraints.foreach { x =>
x => x.split(",").map(_.trim).foreach { item =>
x.split(",").map(_.trim).foreach { options.append(s"""<li>
item =>
options.append(s"""<li>
| <a href="javascript:void(0);" class="custom-field-option-$fieldId" data-value="${StringUtil | <a href="javascript:void(0);" class="custom-field-option-$fieldId" data-value="${StringUtil
.escapeHtml(item)}"> .escapeHtml(item)}">
| ${gitbucket.core.helper.html.checkicon(value.contains(item))} | ${gitbucket.core.helper.html.checkicon(value.contains(item))}
| ${StringUtil.escapeHtml(item)} | ${StringUtil.escapeHtml(item)}
| </a> | </a>
|</li> |</li>
|""".stripMargin) |""".stripMargin)
} }
} }
Html(options.toString()) Html(options.toString())
} }
@@ -205,11 +203,11 @@ object CustomFieldBehavior {
value match { value match {
case None => case None =>
sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">No ${StringUtil.escapeHtml( sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">No ${StringUtil.escapeHtml(
fieldName fieldName
)}</span></span>""") )}</span></span>""")
case Some(value) => case Some(value) =>
sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">${StringUtil sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">${StringUtil
.escapeHtml(value)}</span></span>""") .escapeHtml(value)}</span></span>""")
} }
if (value.isEmpty || issueId.isEmpty) { if (value.isEmpty || issueId.isEmpty) {
sb.append(s"""<input type="hidden" id="custom-field-$fieldId" name="custom-field-$fieldId" value=""/>""") sb.append(s"""<input type="hidden" id="custom-field-$fieldId" name="custom-field-$fieldId" value=""/>""")
@@ -220,7 +218,7 @@ object CustomFieldBehavior {
| $$('#custom-field-$fieldId').val(value); | $$('#custom-field-$fieldId').val(value);
| if (value == '') { | if (value == '') {
| $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text('No ${StringUtil | $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text('No ${StringUtil
.escapeHtml(fieldName)}')); .escapeHtml(fieldName)}'));
| } else { | } else {
| $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text(value)); | $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text(value));
| $$('a.custom-field-option-$fieldId[data-value=' + value + '] i').addClass('octicon-check'); | $$('a.custom-field-option-$fieldId[data-value=' + value + '] i').addClass('octicon-check');
@@ -237,7 +235,7 @@ object CustomFieldBehavior {
| $$('a.custom-field-option-$fieldId i.octicon-check').removeClass('octicon-check'); | $$('a.custom-field-option-$fieldId i.octicon-check').removeClass('octicon-check');
| if (value == '') { | if (value == '') {
| $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text('No ${StringUtil | $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text('No ${StringUtil
.escapeHtml(fieldName)}')); .escapeHtml(fieldName)}'));
| } else { | } else {
| $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text(value)); | $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text(value));
| $$('a.custom-field-option-$fieldId[data-value=' + value + '] i').addClass('octicon-check'); | $$('a.custom-field-option-$fieldId[data-value=' + value + '] i').addClass('octicon-check');
@@ -296,14 +294,14 @@ object CustomFieldBehavior {
constraints: Option[String], constraints: Option[String],
value: String, value: String,
editable: Boolean editable: Boolean
)( )(implicit
implicit context: Context context: Context
): String = { ): String = {
val sb = new StringBuilder val sb = new StringBuilder
if (value.nonEmpty) { if (value.nonEmpty) {
sb.append( sb.append(
s"""<span id="custom-field-$fieldId-label" class="custom-field-label">${StringUtil s"""<span id="custom-field-$fieldId-label" class="custom-field-label">${StringUtil
.escapeHtml(value)}</span>""" .escapeHtml(value)}</span>"""
) )
} else { } else {
if (editable) { if (editable) {

View File

@@ -53,24 +53,24 @@ trait RepositoryComponent extends TemplateComponent { self: Profile =>
safeMode safeMode
) )
).shaped.<>( ).shaped.<>(
{ { case (repository, options) =>
case (repository, options) => Repository(
Repository( repository._1,
repository._1, repository._2,
repository._2, repository._3,
repository._3, repository._4,
repository._4, repository._5,
repository._5, repository._6,
repository._6, repository._7,
repository._7, repository._8,
repository._8, repository._9,
repository._9, repository._10,
repository._10, repository._11,
repository._11, repository._12,
repository._12, RepositoryOptions.tupled.apply(options)
RepositoryOptions.tupled.apply(options) )
) },
}, { (r: Repository) => { (r: Repository) =>
Some( Some(
( (
( (

View File

@@ -41,8 +41,8 @@ trait GitRepositoryFilter {
* @param session the database session * @param session the database session
* @return true if allow accessing to repository, otherwise false. * @return true if allow accessing to repository, otherwise false.
*/ */
def filter(path: String, userName: Option[String], settings: SystemSettings, isUpdating: Boolean)( def filter(path: String, userName: Option[String], settings: SystemSettings, isUpdating: Boolean)(implicit
implicit session: Session session: Session
): Boolean ): Boolean
} }

View File

@@ -9,16 +9,16 @@ import profile.api._
trait IssueHook { trait IssueHook {
def created(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = () def created(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
def addedComment(commentId: Int, content: String, issue: Issue, repository: RepositoryInfo)( def addedComment(commentId: Int, content: String, issue: Issue, repository: RepositoryInfo)(implicit
implicit session: Session, session: Session,
context: Context context: Context
): Unit = () ): Unit = ()
def deletedComment(commentId: Int, issue: Issue, repository: RepositoryInfo)( def deletedComment(commentId: Int, issue: Issue, repository: RepositoryInfo)(implicit
implicit session: Session, session: Session,
context: Context context: Context
): Unit = () ): Unit = ()
def updatedComment(commentId: Int, content: String, issue: Issue, repository: RepositoryInfo)( def updatedComment(commentId: Int, content: String, issue: Issue, repository: RepositoryInfo)(implicit
implicit session: Session, session: Session,
context: Context context: Context
): Unit = () ): Unit = ()
def closed(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = () def closed(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
@@ -29,12 +29,12 @@ trait IssueHook {
assigner: Option[String], assigner: Option[String],
assigned: Option[String], assigned: Option[String],
oldAssigned: Option[String] oldAssigned: Option[String]
)( )(implicit
implicit session: Session, session: Session,
context: Context context: Context
): Unit = () ): Unit = ()
def closedByCommitComment(issue: Issue, repository: RepositoryInfo, message: String, pusher: Account)( def closedByCommitComment(issue: Issue, repository: RepositoryInfo, message: String, pusher: Account)(implicit
implicit session: Session session: Session
): Unit = () ): Unit = ()
} }

View File

@@ -340,25 +340,20 @@ abstract class Plugin {
* Register plugin functionality to PluginRegistry. * Register plugin functionality to PluginRegistry.
*/ */
def initialize(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Unit = { def initialize(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Unit = {
(images ++ images(registry, context, settings)).foreach { (images ++ images(registry, context, settings)).foreach { case (id, in) =>
case (id, in) => registry.addImage(id, in)
registry.addImage(id, in)
} }
(controllers ++ controllers(registry, context, settings)).foreach { (controllers ++ controllers(registry, context, settings)).foreach { case (path, controller) =>
case (path, controller) => registry.addController(path, controller)
registry.addController(path, controller)
} }
(anonymousAccessiblePaths ++ anonymousAccessiblePaths(registry, context, settings)).foreach { (anonymousAccessiblePaths ++ anonymousAccessiblePaths(registry, context, settings)).foreach { case (path) =>
case (path) => registry.addAnonymousAccessiblePath(path)
registry.addAnonymousAccessiblePath(path)
} }
(javaScripts ++ javaScripts(registry, context, settings)).foreach { (javaScripts ++ javaScripts(registry, context, settings)).foreach { case (path, script) =>
case (path, script) => registry.addJavaScript(path, script)
registry.addJavaScript(path, script)
} }
(renderers ++ renderers(registry, context, settings)).foreach { (renderers ++ renderers(registry, context, settings)).foreach { case (extension, renderer) =>
case (extension, renderer) => registry.addRenderer(extension, renderer)
registry.addRenderer(extension, renderer)
} }
(repositoryRoutings ++ repositoryRoutings(registry, context, settings)).foreach { routing => (repositoryRoutings ++ repositoryRoutings(registry, context, settings)).foreach { routing =>
registry.addRepositoryRouting(routing) registry.addRepositoryRouting(routing)

View File

@@ -80,7 +80,7 @@ class PluginRegistry {
def getAnonymousAccessiblePaths(): Seq[String] = anonymousAccessiblePaths.asScala.toSeq def getAnonymousAccessiblePaths(): Seq[String] = anonymousAccessiblePaths.asScala.toSeq
def addJavaScript(path: String, script: String): Unit = def addJavaScript(path: String, script: String): Unit =
javaScripts.add((path, script)) //javaScripts += ((path, script)) javaScripts.add((path, script)) // javaScripts += ((path, script))
def getJavaScript(currentPath: String): List[String] = def getJavaScript(currentPath: String): List[String] =
javaScripts.asScala.filter(x => currentPath.matches(x._1)).toList.map(_._2) javaScripts.asScala.filter(x => currentPath.matches(x._1)).toList.map(_._2)

View File

@@ -13,8 +13,8 @@ trait ReceiveHook {
command: ReceiveCommand, command: ReceiveCommand,
pusher: String, pusher: String,
mergePullRequest: Boolean mergePullRequest: Boolean
)( )(implicit
implicit session: Session session: Session
): Option[String] = None ): Option[String] = None
def postReceive( def postReceive(
@@ -24,8 +24,8 @@ trait ReceiveHook {
command: ReceiveCommand, command: ReceiveCommand,
pusher: String, pusher: String,
mergePullRequest: Boolean mergePullRequest: Boolean
)( )(implicit
implicit session: Session session: Session
): Unit = () ): Unit = ()
} }

View File

@@ -37,9 +37,8 @@ trait AccessTokenService {
def getAccountByAccessToken(token: String)(implicit s: Session): Option[Account] = def getAccountByAccessToken(token: String)(implicit s: Session): Option[Account] =
Accounts Accounts
.join(AccessTokens) .join(AccessTokens)
.filter { .filter { case (ac, t) =>
case (ac, t) => (ac.userName === t.userName) && (t.tokenHash === tokenToHash(token).bind) && (ac.removed === false.bind)
(ac.userName === t.userName) && (t.tokenHash === tokenToHash(token).bind) && (ac.removed === false.bind)
} }
.map { case (ac, t) => ac } .map { case (ac, t) => ac }
.firstOption .firstOption

View File

@@ -51,8 +51,8 @@ trait AccountFederationService {
* @param preferredUserName Username * @param preferredUserName Username
* @return Available username * @return Available username
*/ */
def findAvailableUserName(preferredUserName: Option[String], mailAddress: String)( def findAvailableUserName(preferredUserName: Option[String], mailAddress: String)(implicit
implicit s: Session s: Session
): Option[String] = { ): Option[String] = {
preferredUserName preferredUserName
.flatMap(n => extractSafeStringForUserName(n)) .flatMap(n => extractSafeStringForUserName(n))

View File

@@ -19,8 +19,8 @@ trait AccountService {
private val logger = LoggerFactory.getLogger(classOf[AccountService]) private val logger = LoggerFactory.getLogger(classOf[AccountService])
def authenticate(settings: SystemSettings, userName: String, password: String)( def authenticate(settings: SystemSettings, userName: String, password: String)(implicit
implicit s: Session s: Session
): Option[Account] = { ): Option[Account] = {
val account = if (password.isEmpty) { val account = if (password.isEmpty) {
None None
@@ -58,8 +58,8 @@ trait AccountService {
/** /**
* Authenticate by LDAP. * Authenticate by LDAP.
*/ */
private def ldapAuthentication(settings: SystemSettings, userName: String, password: String)( private def ldapAuthentication(settings: SystemSettings, userName: String, password: String)(implicit
implicit s: Session s: Session
): Option[Account] = { ): Option[Account] = {
LDAPUtil.authenticate(settings.ldap.get, userName, password) match { LDAPUtil.authenticate(settings.ldap.get, userName, password) match {
case Right(ldapUserInfo) => { case Right(ldapUserInfo) => {
@@ -112,15 +112,15 @@ trait AccountService {
def getAccountByUserName(userName: String, includeRemoved: Boolean = false)(implicit s: Session): Option[Account] = def getAccountByUserName(userName: String, includeRemoved: Boolean = false)(implicit s: Session): Option[Account] =
Accounts filter (t => (t.userName === userName.bind).&&(t.removed === false.bind, !includeRemoved)) firstOption Accounts filter (t => (t.userName === userName.bind).&&(t.removed === false.bind, !includeRemoved)) firstOption
def getAccountByUserNameIgnoreCase(userName: String, includeRemoved: Boolean = false)( def getAccountByUserNameIgnoreCase(userName: String, includeRemoved: Boolean = false)(implicit
implicit s: Session s: Session
): Option[Account] = ): Option[Account] =
Accounts filter ( Accounts filter (t =>
t => (t.userName.toLowerCase === userName.toLowerCase.bind).&&(t.removed === false.bind, !includeRemoved) (t.userName.toLowerCase === userName.toLowerCase.bind).&&(t.removed === false.bind, !includeRemoved)
) firstOption ) firstOption
def getAccountsByUserNames(userNames: Set[String], knowns: Set[Account], includeRemoved: Boolean = false)( def getAccountsByUserNames(userNames: Set[String], knowns: Set[Account], includeRemoved: Boolean = false)(implicit
implicit s: Session s: Session
): Map[String, Account] = { ): Map[String, Account] = {
val map = knowns.map(a => a.userName -> a).toMap val map = knowns.map(a => a.userName -> a).toMap
val needs = userNames -- map.keySet val needs = userNames -- map.keySet
@@ -135,17 +135,15 @@ trait AccountService {
} }
} }
def getAccountByMailAddress(mailAddress: String, includeRemoved: Boolean = false)( def getAccountByMailAddress(mailAddress: String, includeRemoved: Boolean = false)(implicit
implicit s: Session s: Session
): Option[Account] = ): Option[Account] =
(Accounts joinLeft AccountExtraMailAddresses on { case (a, e) => a.userName === e.userName }) (Accounts joinLeft AccountExtraMailAddresses on { case (a, e) => a.userName === e.userName })
.filter { .filter { case (a, x) =>
case (a, x) => ((a.mailAddress.toLowerCase === mailAddress.toLowerCase.bind) ||
((a.mailAddress.toLowerCase === mailAddress.toLowerCase.bind) || (x.map { e =>
(x.map { e => e.extraMailAddress.toLowerCase === mailAddress.toLowerCase.bind
e.extraMailAddress.toLowerCase === mailAddress.toLowerCase.bind }.getOrElse(false.bind))).&&(a.removed === false.bind, !includeRemoved)
}
.getOrElse(false.bind))).&&(a.removed === false.bind, !includeRemoved)
} }
.map { case (a, e) => a } firstOption .map { case (a, e) => a } firstOption
@@ -267,8 +265,8 @@ trait AccountService {
group group
} }
def updateGroup(groupName: String, description: Option[String], url: Option[String], removed: Boolean)( def updateGroup(groupName: String, description: Option[String], url: Option[String], removed: Boolean)(implicit
implicit s: Session s: Session
): Unit = ): Unit =
Accounts Accounts
.filter(_.userName === groupName.bind) .filter(_.userName === groupName.bind)
@@ -277,9 +275,8 @@ trait AccountService {
def updateGroupMembers(groupName: String, members: List[(String, Boolean)])(implicit s: Session): Unit = { def updateGroupMembers(groupName: String, members: List[(String, Boolean)])(implicit s: Session): Unit = {
GroupMembers.filter(_.groupName === groupName.bind).delete GroupMembers.filter(_.groupName === groupName.bind).delete
members.foreach { members.foreach { case (userName, isManager) =>
case (userName, isManager) => GroupMembers insert GroupMember(groupName, userName, isManager)
GroupMembers insert GroupMember(groupName, userName, isManager)
} }
} }
@@ -318,8 +315,8 @@ trait AccountService {
/* /*
* For account preference * For account preference
*/ */
def getAccountPreference(userName: String)( def getAccountPreference(userName: String)(implicit
implicit s: Session s: Session
): Option[AccountPreference] = { ): Option[AccountPreference] = {
AccountPreferences filter (_.byPrimaryKey(userName)) firstOption AccountPreferences filter (_.byPrimaryKey(userName)) firstOption
} }

View File

@@ -43,9 +43,8 @@ trait ActivityService {
def getRecentActivitiesByRepos(repos: Set[(String, String)])(implicit context: Context): List[Activity] = { def getRecentActivitiesByRepos(repos: Set[(String, String)])(implicit context: Context): List[Activity] = {
getActivities(includePublic = true) { activity => getActivities(includePublic = true) { activity =>
repos.exists { repos.exists { case (userName, repositoryName) =>
case (userName, repositoryName) => activity.userName == userName && activity.repositoryName == repositoryName
activity.userName == userName && activity.repositoryName == repositoryName
} }
} }
} }
@@ -65,10 +64,12 @@ trait ActivityService {
.get() .get()
) { reader => ) { reader =>
var json: String = null var json: String = null
while (list.length < 50 && { while (
json = reader.readLine(); list.length < 50 && {
json json = reader.readLine();
} != null) { json
} != null
) {
val activity = read[Activity](json) val activity = read[Activity](json)
if (filter(activity)) { if (filter(activity)) {
list += activity list += activity

View File

@@ -47,8 +47,8 @@ trait CommitStatusService {
) )
} }
def getCommitStatusWithSummary(userName: String, repositoryName: String, sha: String)( def getCommitStatusWithSummary(userName: String, repositoryName: String, sha: String)(implicit
implicit s: Session s: Session
): Option[(CommitState, List[CommitStatus])] = { ): Option[(CommitState, List[CommitStatus])] = {
val statuses = getCommitStatuses(userName, repositoryName, sha) val statuses = getCommitStatuses(userName, repositoryName, sha)
if (statuses.isEmpty) { if (statuses.isEmpty) {
@@ -62,18 +62,18 @@ trait CommitStatusService {
def getCommitStatus(userName: String, repositoryName: String, id: Int)(implicit s: Session): Option[CommitStatus] = def getCommitStatus(userName: String, repositoryName: String, id: Int)(implicit s: Session): Option[CommitStatus] =
CommitStatuses.filter(t => t.byPrimaryKey(id) && t.byRepository(userName, repositoryName)).firstOption CommitStatuses.filter(t => t.byPrimaryKey(id) && t.byRepository(userName, repositoryName)).firstOption
def getCommitStatus(userName: String, repositoryName: String, sha: String, context: String)( def getCommitStatus(userName: String, repositoryName: String, sha: String, context: String)(implicit
implicit s: Session s: Session
): Option[CommitStatus] = ): Option[CommitStatus] =
CommitStatuses.filter(t => t.byCommit(userName, repositoryName, sha) && t.context === context.bind).firstOption CommitStatuses.filter(t => t.byCommit(userName, repositoryName, sha) && t.context === context.bind).firstOption
def getCommitStatuses(userName: String, repositoryName: String, sha: String)( def getCommitStatuses(userName: String, repositoryName: String, sha: String)(implicit
implicit s: Session s: Session
): List[CommitStatus] = ): List[CommitStatus] =
byCommitStatus(userName, repositoryName, sha).list byCommitStatus(userName, repositoryName, sha).list
def getRecentStatusContexts(userName: String, repositoryName: String, time: java.util.Date)( def getRecentStatusContexts(userName: String, repositoryName: String, time: java.util.Date)(implicit
implicit s: Session s: Session
): List[String] = ): List[String] =
CommitStatuses CommitStatuses
.filter(t => t.byRepository(userName, repositoryName)) .filter(t => t.byRepository(userName, repositoryName))
@@ -82,8 +82,8 @@ trait CommitStatusService {
.map(_._1) .map(_._1)
.list .list
def getCommitStatusesWithCreator(userName: String, repositoryName: String, sha: String)( def getCommitStatusesWithCreator(userName: String, repositoryName: String, sha: String)(implicit
implicit s: Session s: Session
): List[(CommitStatus, Account)] = ): List[(CommitStatus, Account)] =
byCommitStatus(userName, repositoryName, sha) byCommitStatus(userName, repositoryName, sha)
.join(Accounts) .join(Accounts)

View File

@@ -18,8 +18,8 @@ import org.apache.commons.io.FileUtils
trait CommitsService { trait CommitsService {
self: ActivityService with PullRequestService with WebHookPullRequestReviewCommentService => self: ActivityService with PullRequestService with WebHookPullRequestReviewCommentService =>
def getCommitComments(owner: String, repository: String, commitId: String, includePullRequest: Boolean)( def getCommitComments(owner: String, repository: String, commitId: String, includePullRequest: Boolean)(implicit
implicit s: Session s: Session
) = ) =
CommitComments filter { t => CommitComments filter { t =>
t.byCommit(owner, repository, commitId) && (t.issueId.isEmpty || includePullRequest) t.byCommit(owner, repository, commitId) && (t.issueId.isEmpty || includePullRequest)
@@ -79,21 +79,20 @@ trait CommitsService {
val comment = getCommitComment(repository.owner, repository.name, commentId.toString).get val comment = getCommitComment(repository.owner, repository.name, commentId.toString).get
issueId match { issueId match {
case Some(issueId) => case Some(issueId) =>
getPullRequest(repository.owner, repository.name, issueId).foreach { getPullRequest(repository.owner, repository.name, issueId).foreach { case (issue, pullRequest) =>
case (issue, pullRequest) => val pullRequestCommentInfo =
val pullRequestCommentInfo = PullRequestCommentInfo(repository.owner, repository.name, loginAccount.userName, content, issueId)
PullRequestCommentInfo(repository.owner, repository.name, loginAccount.userName, content, issueId) recordActivity(pullRequestCommentInfo)
recordActivity(pullRequestCommentInfo) PluginRegistry().getPullRequestHooks.foreach(_.addedComment(commentId, content, issue, repository))
PluginRegistry().getPullRequestHooks.foreach(_.addedComment(commentId, content, issue, repository)) callPullRequestReviewCommentWebHook(
callPullRequestReviewCommentWebHook( "create",
"create", comment,
comment, repository,
repository, issue,
issue, pullRequest,
pullRequest, loginAccount,
loginAccount, context.settings
context.settings )
)
} }
case None => case None =>
val commitCommentInfo = val commitCommentInfo =
@@ -104,8 +103,8 @@ trait CommitsService {
commentId commentId
} }
def updateCommitCommentPosition(commentId: Int, commitId: String, oldLine: Option[Int], newLine: Option[Int])( def updateCommitCommentPosition(commentId: Int, commitId: String, oldLine: Option[Int], newLine: Option[Int])(implicit
implicit s: Session s: Session
): Unit = ): Unit =
CommitComments CommitComments
.filter(_.byPrimaryKey(commentId)) .filter(_.byPrimaryKey(commentId))

View File

@@ -9,8 +9,8 @@ trait CustomFieldsService {
def getCustomFields(owner: String, repository: String)(implicit s: Session): List[CustomField] = def getCustomFields(owner: String, repository: String)(implicit s: Session): List[CustomField] =
CustomFields.filter(_.byRepository(owner, repository)).sortBy(_.fieldId asc).list CustomFields.filter(_.byRepository(owner, repository)).sortBy(_.fieldId asc).list
def getCustomFieldsWithValue(owner: String, repository: String, issueId: Int)( def getCustomFieldsWithValue(owner: String, repository: String, issueId: Int)(implicit
implicit s: Session s: Session
): List[(CustomField, Option[IssueCustomField])] = { ): List[(CustomField, Option[IssueCustomField])] = {
CustomFields CustomFields
.filter(_.byRepository(owner, repository)) .filter(_.byRepository(owner, repository))
@@ -52,8 +52,8 @@ trait CustomFieldsService {
constraints: Option[String], constraints: Option[String],
enableForIssues: Boolean, enableForIssues: Boolean,
enableForPullRequests: Boolean enableForPullRequests: Boolean
)( )(implicit
implicit s: Session s: Session
): Unit = ): Unit =
CustomFields CustomFields
.filter(_.byPrimaryKey(owner, repository, fieldId)) .filter(_.byPrimaryKey(owner, repository, fieldId))

View File

@@ -17,10 +17,9 @@ trait GpgKeyService {
def addGpgPublicKey(userName: String, title: String, publicKey: String)(implicit s: Session): Unit = { def addGpgPublicKey(userName: String, title: String, publicKey: String)(implicit s: Session): Unit = {
val pubKeyOf = new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(publicKey.getBytes))) val pubKeyOf = new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(publicKey.getBytes)))
pubKeyOf.iterator().asScala.foreach { pubKeyOf.iterator().asScala.foreach { case keyRing: PGPPublicKeyRing =>
case keyRing: PGPPublicKeyRing => val key = keyRing.getPublicKey()
val key = keyRing.getPublicKey() GpgKeys.insert(GpgKey(userName = userName, gpgKeyId = key.getKeyID, title = title, publicKey = publicKey))
GpgKeys.insert(GpgKey(userName = userName, gpgKeyId = key.getKeyID, title = title, publicKey = publicKey))
} }
} }

View File

@@ -134,8 +134,8 @@ trait HandleCommentService {
} }
} }
def deleteCommentByApi(repoInfo: RepositoryInfo, comment: IssueComment, issue: Issue)( def deleteCommentByApi(repoInfo: RepositoryInfo, comment: IssueComment, issue: Issue)(implicit
implicit context: Context, context: Context,
s: Session s: Session
): Option[IssueComment] = context.loginAccount.flatMap { _ => ): Option[IssueComment] = context.loginAccount.flatMap { _ =>
comment.action match { comment.action match {

View File

@@ -41,8 +41,8 @@ trait IssuesService {
IssueComments filter (_.byIssue(owner, repository, issueId)) sortBy (_.commentId asc) list IssueComments filter (_.byIssue(owner, repository, issueId)) sortBy (_.commentId asc) list
/** @return IssueComment and commentedUser and Issue */ /** @return IssueComment and commentedUser and Issue */
def getCommentsForApi(owner: String, repository: String, issueId: Int)( def getCommentsForApi(owner: String, repository: String, issueId: Int)(implicit
implicit s: Session s: Session
): List[(IssueComment, Account, Issue)] = ): List[(IssueComment, Account, Issue)] =
IssueComments IssueComments
.filter(_.byIssue(owner, repository, issueId)) .filter(_.byIssue(owner, repository, issueId))
@@ -54,8 +54,8 @@ trait IssuesService {
.map { case t1 ~ t2 ~ t3 => (t1, t2, t3) } .map { case t1 ~ t2 ~ t3 => (t1, t2, t3) }
.list .list
def getMergedComment(owner: String, repository: String, issueId: Int)( def getMergedComment(owner: String, repository: String, issueId: Int)(implicit
implicit s: Session s: Session
): Option[(IssueComment, Account)] = { ): Option[(IssueComment, Account)] = {
IssueComments IssueComments
.filter(_.byIssue(owner, repository, issueId)) .filter(_.byIssue(owner, repository, issueId))
@@ -74,8 +74,8 @@ trait IssuesService {
else None else None
} }
def getCommentForApi(owner: String, repository: String, commentId: Int)( def getCommentForApi(owner: String, repository: String, commentId: Int)(implicit
implicit s: Session s: Session
): Option[(IssueComment, Account, Issue)] = ): Option[(IssueComment, Account, Issue)] =
IssueComments IssueComments
.filter(_.byRepository(owner, repository)) .filter(_.byRepository(owner, repository))
@@ -91,17 +91,16 @@ trait IssuesService {
def getIssueLabels(owner: String, repository: String, issueId: Int)(implicit s: Session): List[Label] = { def getIssueLabels(owner: String, repository: String, issueId: Int)(implicit s: Session): List[Label] = {
IssueLabels IssueLabels
.join(Labels) .join(Labels)
.on { .on { case t1 ~ t2 =>
case t1 ~ t2 => t1.byLabel(t2.userName, t2.repositoryName, t2.labelId)
t1.byLabel(t2.userName, t2.repositoryName, t2.labelId)
} }
.filter { case t1 ~ t2 => t1.byIssue(owner, repository, issueId) } .filter { case t1 ~ t2 => t1.byIssue(owner, repository, issueId) }
.map { case t1 ~ t2 => t2 } .map { case t1 ~ t2 => t2 }
.list .list
} }
def getIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)( def getIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)(implicit
implicit s: Session s: Session
): Option[IssueLabel] = { ): Option[IssueLabel] = {
IssueLabels filter (_.byPrimaryKey(owner, repository, issueId, labelId)) firstOption IssueLabels filter (_.byPrimaryKey(owner, repository, issueId, labelId)) firstOption
} }
@@ -114,8 +113,8 @@ trait IssuesService {
* @param repos Tuple of the repository owner and the repository name * @param repos Tuple of the repository owner and the repository name
* @return the count of the search result * @return the count of the search result
*/ */
def countIssue(condition: IssueSearchCondition, searchOption: IssueSearchOption, repos: (String, String)*)( def countIssue(condition: IssueSearchCondition, searchOption: IssueSearchOption, repos: (String, String)*)(implicit
implicit s: Session s: Session
): Int = { ): Int = {
Query(searchIssueQuery(repos, condition, searchOption).length).first Query(searchIssueQuery(repos, condition, searchOption).length).first
} }
@@ -136,22 +135,18 @@ trait IssuesService {
searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), IssueSearchOption.Issues) searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), IssueSearchOption.Issues)
.join(IssueLabels) .join(IssueLabels)
.on { .on { case t1 ~ t2 =>
case t1 ~ t2 => t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
} }
.join(Labels) .join(Labels)
.on { .on { case t1 ~ t2 ~ t3 =>
case t1 ~ t2 ~ t3 => t2.byLabel(t3.userName, t3.repositoryName, t3.labelId)
t2.byLabel(t3.userName, t3.repositoryName, t3.labelId)
} }
.groupBy { .groupBy { case t1 ~ t2 ~ t3 =>
case t1 ~ t2 ~ t3 => t3.labelName
t3.labelName
} }
.map { .map { case (labelName, t) =>
case (labelName, t) => labelName -> t.length
labelName -> t.length
} }
.list .list
.toMap .toMap
@@ -173,17 +168,14 @@ trait IssuesService {
searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), IssueSearchOption.Issues) searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), IssueSearchOption.Issues)
.join(Priorities) .join(Priorities)
.on { .on { case t1 ~ t2 =>
case t1 ~ t2 => t1.byPriority(t2.userName, t2.repositoryName, t2.priorityId)
t1.byPriority(t2.userName, t2.repositoryName, t2.priorityId)
} }
.groupBy { .groupBy { case t1 ~ t2 =>
case t1 ~ t2 => t2.priorityName
t2.priorityName
} }
.map { .map { case (priorityName, t) =>
case (priorityName, t) => priorityName -> t.length
priorityName -> t.length
} }
.list .list
.toMap .toMap
@@ -221,19 +213,18 @@ trait IssuesService {
.joinLeft(IssueAssignees) .joinLeft(IssueAssignees)
.on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => t1.byIssue(t8.userName, t8.repositoryName, t8.issueId) } .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => t1.byIssue(t8.userName, t8.repositoryName, t8.issueId) }
.sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => i asc } .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => i asc }
.map { .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 =>
case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => (
( t1,
t1, t2.commentCount,
t2.commentCount, t4.map(_.labelId),
t4.map(_.labelId), t4.map(_.labelName),
t4.map(_.labelName), t4.map(_.color),
t4.map(_.color), t5.map(_.title),
t5.map(_.title), t6.map(_.priorityName),
t6.map(_.priorityName), t7.map(_.commitIdTo),
t7.map(_.commitIdTo), t8.map(_.assigneeUserName)
t8.map(_.assigneeUserName) )
)
} }
.list .list
.splitWith { (c1, c2) => .splitWith { (c1, c2) =>
@@ -264,8 +255,8 @@ trait IssuesService {
/** for api /** for api
* @return (issue, issueUser, Seq(assigneeUsers)) * @return (issue, issueUser, Seq(assigneeUsers))
*/ */
def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)( def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(implicit
implicit s: Session s: Session
): List[(Issue, Account, List[Account])] = { ): List[(Issue, Account, List[Account])] = {
// get issues and comment count and labels // get issues and comment count and labels
searchIssueQueryBase(condition, IssueSearchOption.Issues, offset, limit, repos) searchIssueQueryBase(condition, IssueSearchOption.Issues, offset, limit, repos)
@@ -278,13 +269,11 @@ trait IssuesService {
.sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => i asc } .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => i asc }
.map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => (t1, t3, t5) } .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => (t1, t3, t5) }
.list .list
.groupBy { .groupBy { case (issue, account, _) =>
case (issue, account, _) => (issue, account)
(issue, account)
} }
.map { .map { case (_, values) =>
case (_, values) => (values.head._1, values.head._2, values.flatMap(_._3))
(values.head._1, values.head._2, values.flatMap(_._3))
} }
.toList .toList
} }
@@ -312,21 +301,19 @@ trait IssuesService {
.sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => i asc } .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => i asc }
.map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => (t1, t5, t2.commentCount, t3, t4, t6, t8) } .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => (t1, t5, t2.commentCount, t3, t4, t6, t8) }
.list .list
.groupBy { .groupBy { case (issue, openedUser, commentCount, pullRequest, repository, account, assignedUser) =>
case (issue, openedUser, commentCount, pullRequest, repository, account, assignedUser) => (issue, openedUser, commentCount, pullRequest, repository, account)
(issue, openedUser, commentCount, pullRequest, repository, account)
} }
.map { .map { case (_, values) =>
case (_, values) => (
( values.head._1,
values.head._1, values.head._2,
values.head._2, values.head._3,
values.head._3, values.head._4,
values.head._4, values.head._5,
values.head._5, values.head._6,
values.head._6, values.flatMap(_._7)
values.flatMap(_._7) )
)
} }
.toList .toList
} }
@@ -344,30 +331,29 @@ trait IssuesService {
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
} }
.sortBy { case (t1, t2) => t1.issueId desc } .sortBy { case (t1, t2) => t1.issueId desc }
.sortBy { .sortBy { case (t1, t2) =>
case (t1, t2) => condition.sort match {
condition.sort match { case "created" =>
case "created" => condition.direction match {
condition.direction match { case "asc" => t1.registeredDate asc
case "asc" => t1.registeredDate asc case "desc" => t1.registeredDate desc
case "desc" => t1.registeredDate desc }
} case "comments" =>
case "comments" => condition.direction match {
condition.direction match { case "asc" => t2.commentCount asc
case "asc" => t2.commentCount asc case "desc" => t2.commentCount desc
case "desc" => t2.commentCount desc }
} case "updated" =>
case "updated" => condition.direction match {
condition.direction match { case "asc" => t1.updatedDate asc
case "asc" => t1.updatedDate asc case "desc" => t1.updatedDate desc
case "desc" => t1.updatedDate desc }
} case "priority" =>
case "priority" => condition.direction match {
condition.direction match { case "asc" => t2.priority asc
case "asc" => t2.priority asc case "desc" => t2.priority desc
case "desc" => t2.priority desc }
} }
}
} }
.drop(offset) .drop(offset)
.take(limit) .take(limit)
@@ -380,8 +366,8 @@ trait IssuesService {
repos: Seq[(String, String)], repos: Seq[(String, String)],
condition: IssueSearchCondition, condition: IssueSearchCondition,
searchOption: IssueSearchOption searchOption: IssueSearchOption
)( )(implicit
implicit s: Session s: Session
) = { ) = {
val query = Issues filter { t1 => val query = Issues filter { t1 =>
(if (repos.sizeIs == 1) { (if (repos.sizeIs == 1) {
@@ -395,14 +381,14 @@ trait IssuesService {
case _ => t1.closed === true || t1.closed === false case _ => t1.closed === true || t1.closed === false
}).&&(t1.milestoneId.? isEmpty, condition.milestone.contains(None)) }).&&(t1.milestoneId.? isEmpty, condition.milestone.contains(None))
.&&(t1.priorityId.? isEmpty, condition.priority.contains(None)) .&&(t1.priorityId.? isEmpty, condition.priority.contains(None))
//.&&(t1.assignedUserName.? isEmpty, condition.assigned == Some(None)) // .&&(t1.assignedUserName.? isEmpty, condition.assigned == Some(None))
.&&(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) && .&&(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) &&
(searchOption match { (searchOption match {
case IssueSearchOption.Issues => t1.pullRequest === false case IssueSearchOption.Issues => t1.pullRequest === false
case IssueSearchOption.PullRequests => t1.pullRequest === true case IssueSearchOption.PullRequests => t1.pullRequest === true
case IssueSearchOption.Both => t1.pullRequest === false || t1.pullRequest === true case IssueSearchOption.Both => t1.pullRequest === false || t1.pullRequest === true
}) })
// Milestone filter // Milestone filter
.&&( .&&(
Milestones filter { t2 => Milestones filter { t2 =>
(t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) && (t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) &&
@@ -451,40 +437,46 @@ trait IssuesService {
// Mentioned filter // Mentioned filter
.&&( .&&(
(t1.openedUserName === condition.mentioned.get.bind) || (IssueAssignees filter { t1 => (t1.openedUserName === condition.mentioned.get.bind) || (IssueAssignees filter { t1 =>
t1.byIssue(t1.userName, t1.repositoryName, t1.issueId) && t1.assigneeUserName === condition.mentioned.get.bind t1.byIssue(
t1.userName,
t1.repositoryName,
t1.issueId
) && t1.assigneeUserName === condition.mentioned.get.bind
} exists) || } exists) ||
(IssueComments filter { t2 => (IssueComments filter { t2 =>
(t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) && (t2.commentedUserName === condition.mentioned.get.bind) (t2.byIssue(
t1.userName,
t1.repositoryName,
t1.issueId
)) && (t2.commentedUserName === condition.mentioned.get.bind)
} exists), } exists),
condition.mentioned.isDefined condition.mentioned.isDefined
) )
} }
condition.others.foldLeft(query) { condition.others.foldLeft(query) { case (query, cond) =>
case (query, cond) => def condQuery(f: Rep[String] => Rep[Boolean]): Query[Profile.Issues, Issue, Seq] = {
def condQuery(f: Rep[String] => Rep[Boolean]): Query[Profile.Issues, Issue, Seq] = { query.filter { t1 =>
query.filter { t1 => IssueCustomFields
IssueCustomFields .join(CustomFields)
.join(CustomFields) .on { (t2, t3) =>
.on { (t2, t3) => t2.userName === t3.userName && t2.repositoryName === t3.repositoryName && t2.fieldId === t3.fieldId
t2.userName === t3.userName && t2.repositoryName === t3.repositoryName && t2.fieldId === t3.fieldId }
} .filter { case (t2, t3) =>
.filter { t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) && t3.fieldName === cond.name.bind && f(
case (t2, t3) => t2.value
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) && t3.fieldName === cond.name.bind && f( )
t2.value
)
} exists } exists
}
}
cond.operator match {
case "eq" => condQuery(_ === cond.value.bind)
case "lt" => condQuery(_ < cond.value.bind)
case "gt" => condQuery(_ > cond.value.bind)
case "lte" => condQuery(_ <= cond.value.bind)
case "gte" => condQuery(_ >= cond.value.bind)
case _ => throw new IllegalArgumentException("Unsupported operator")
} }
}
cond.operator match {
case "eq" => condQuery(_ === cond.value.bind)
case "lt" => condQuery(_ < cond.value.bind)
case "gt" => condQuery(_ > cond.value.bind)
case "lte" => condQuery(_ <= cond.value.bind)
case "gte" => condQuery(_ >= cond.value.bind)
case _ => throw new IllegalArgumentException("Unsupported operator")
}
} }
} }
@@ -523,11 +515,12 @@ trait IssuesService {
.filter(_.byPrimaryKey(owner, repository)) .filter(_.byPrimaryKey(owner, repository))
.map(_.issueId) .map(_.issueId)
.update(id) > 0 .update(id) > 0
} get } get
} }
def registerIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int, insertComment: Boolean = false)( def registerIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int, insertComment: Boolean = false)(
implicit context: Context, implicit
context: Context,
s: Session s: Session
): Int = { ): Int = {
if (insertComment) { if (insertComment) {
@@ -546,7 +539,8 @@ trait IssuesService {
} }
def deleteIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int, insertComment: Boolean = false)( def deleteIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int, insertComment: Boolean = false)(
implicit context: Context, implicit
context: Context,
s: Session s: Session
): Int = { ): Int = {
if (insertComment) { if (insertComment) {
@@ -564,8 +558,8 @@ trait IssuesService {
IssueLabels filter (_.byPrimaryKey(owner, repository, issueId, labelId)) delete IssueLabels filter (_.byPrimaryKey(owner, repository, issueId, labelId)) delete
} }
def deleteAllIssueLabels(owner: String, repository: String, issueId: Int, insertComment: Boolean = false)( def deleteAllIssueLabels(owner: String, repository: String, issueId: Int, insertComment: Boolean = false)(implicit
implicit context: Context, context: Context,
s: Session s: Session
): Int = { ): Int = {
if (insertComment) { if (insertComment) {
@@ -604,8 +598,8 @@ trait IssuesService {
) )
} }
def updateIssue(owner: String, repository: String, issueId: Int, title: String, content: Option[String])( def updateIssue(owner: String, repository: String, issueId: Int, title: String, content: Option[String])(implicit
implicit s: Session s: Session
): Int = { ): Int = {
Issues Issues
.filter(_.byPrimaryKey(owner, repository, issueId)) .filter(_.byPrimaryKey(owner, repository, issueId))
@@ -624,8 +618,8 @@ trait IssuesService {
.update(true) .update(true)
} }
def getIssueAssignees(owner: String, repository: String, issueId: Int)( def getIssueAssignees(owner: String, repository: String, issueId: Int)(implicit
implicit s: Session s: Session
): List[IssueAssignee] = { ): List[IssueAssignee] = {
IssueAssignees.filter(_.byIssue(owner, repository, issueId)).sortBy(_.assigneeUserName).list IssueAssignees.filter(_.byIssue(owner, repository, issueId)).sortBy(_.assigneeUserName).list
} }
@@ -636,8 +630,8 @@ trait IssuesService {
issueId: Int, issueId: Int,
assigneeUserName: String, assigneeUserName: String,
insertComment: Boolean = false insertComment: Boolean = false
)( )(implicit
implicit context: Context, context: Context,
s: Session s: Session
): Int = { ): Int = {
val assigner = context.loginAccount.map(_.userName) val assigner = context.loginAccount.map(_.userName)
@@ -665,8 +659,8 @@ trait IssuesService {
issueId: Int, issueId: Int,
assigneeUserName: String, assigneeUserName: String,
insertComment: Boolean = false insertComment: Boolean = false
)( )(implicit
implicit context: Context, context: Context,
s: Session s: Session
): Int = { ): Int = {
val assigner = context.loginAccount.map(_.userName) val assigner = context.loginAccount.map(_.userName)
@@ -688,8 +682,8 @@ trait IssuesService {
IssueAssignees filter (_.byPrimaryKey(owner, repository, issueId, assigneeUserName)) delete IssueAssignees filter (_.byPrimaryKey(owner, repository, issueId, assigneeUserName)) delete
} }
def deleteAllIssueAssignees(owner: String, repository: String, issueId: Int, insertComment: Boolean = false)( def deleteAllIssueAssignees(owner: String, repository: String, issueId: Int, insertComment: Boolean = false)(implicit
implicit context: Context, context: Context,
s: Session s: Session
): Int = { ): Int = {
val assigner = context.loginAccount.map(_.userName) val assigner = context.loginAccount.map(_.userName)
@@ -773,15 +767,15 @@ trait IssuesService {
.update(priorityId, currentDate) .update(priorityId, currentDate)
} }
def updateComment(owner: String, repository: String, issueId: Int, commentId: Int, content: String)( def updateComment(owner: String, repository: String, issueId: Int, commentId: Int, content: String)(implicit
implicit s: Session s: Session
): Int = { ): Int = {
Issues.filter(_.byPrimaryKey(owner, repository, issueId)).map(_.updatedDate).update(currentDate) Issues.filter(_.byPrimaryKey(owner, repository, issueId)).map(_.updatedDate).update(currentDate)
IssueComments.filter(_.byPrimaryKey(commentId)).map(t => (t.content, t.updatedDate)).update(content, currentDate) IssueComments.filter(_.byPrimaryKey(commentId)).map(t => (t.content, t.updatedDate)).update(content, currentDate)
} }
def deleteComment(owner: String, repository: String, issueId: Int, commentId: Int)( def deleteComment(owner: String, repository: String, issueId: Int, commentId: Int)(implicit
implicit context: Context, context: Context,
s: Session s: Session
): Int = { ): Int = {
Issues.filter(_.byPrimaryKey(owner, repository, issueId)).map(_.updatedDate).update(currentDate) Issues.filter(_.byPrimaryKey(owner, repository, issueId)).map(_.updatedDate).update(currentDate)
@@ -820,10 +814,10 @@ trait IssuesService {
* @param query the keywords separated by whitespace. * @param query the keywords separated by whitespace.
* @return issues with comment count and matched content of issue or comment * @return issues with comment count and matched content of issue or comment
*/ */
def searchIssuesByKeyword(owner: String, repository: String, query: String, pullRequest: Boolean)( def searchIssuesByKeyword(owner: String, repository: String, query: String, pullRequest: Boolean)(implicit
implicit s: Session s: Session
): List[(Issue, Int, String)] = { ): List[(Issue, Int, String)] = {
//import slick.driver.JdbcDriver.likeEncode // import slick.driver.JdbcDriver.likeEncode
val keywords = splitWords(query.toLowerCase) val keywords = splitWords(query.toLowerCase)
// Search Issue // Search Issue
@@ -832,61 +826,52 @@ trait IssuesService {
t.byRepository(owner, repository) && t.pullRequest === pullRequest.bind t.byRepository(owner, repository) && t.pullRequest === pullRequest.bind
} }
.join(IssueOutline) .join(IssueOutline)
.on { .on { case (t1, t2) =>
case (t1, t2) => t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
} }
.filter { .filter { case (t1, t2) =>
case (t1, t2) => keywords
keywords .map { keyword =>
.map { keyword => (t1.title.toLowerCase.like(s"%${likeEncode(keyword)}%", '^')) ||
(t1.title.toLowerCase.like(s"%${likeEncode(keyword)}%", '^')) || (t1.content.toLowerCase.like(s"%${likeEncode(keyword)}%", '^'))
(t1.content.toLowerCase.like(s"%${likeEncode(keyword)}%", '^')) }
} .reduceLeft(_ && _)
.reduceLeft(_ && _)
} }
.map { .map { case (t1, t2) =>
case (t1, t2) => (t1, 0, t1.content.?, t2.commentCount)
(t1, 0, t1.content.?, t2.commentCount)
} }
// Search IssueComment // Search IssueComment
val comments = IssueComments val comments = IssueComments
.filter(_.byRepository(owner, repository)) .filter(_.byRepository(owner, repository))
.join(Issues) .join(Issues)
.on { .on { case (t1, t2) =>
case (t1, t2) => t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
} }
.join(IssueOutline) .join(IssueOutline)
.on { .on { case ((t1, t2), t3) =>
case ((t1, t2), t3) => t2.byIssue(t3.userName, t3.repositoryName, t3.issueId)
t2.byIssue(t3.userName, t3.repositoryName, t3.issueId)
} }
.filter { .filter { case ((t1, t2), t3) =>
case ((t1, t2), t3) => t2.pullRequest === pullRequest.bind &&
t2.pullRequest === pullRequest.bind && keywords
keywords .map { query =>
.map { query => t1.content.toLowerCase.like(s"%${likeEncode(query)}%", '^')
t1.content.toLowerCase.like(s"%${likeEncode(query)}%", '^') }
} .reduceLeft(_ && _)
.reduceLeft(_ && _)
} }
.map { .map { case ((t1, t2), t3) =>
case ((t1, t2), t3) => (t2, t1.commentId, t1.content.?, t3.commentCount)
(t2, t1.commentId, t1.content.?, t3.commentCount)
} }
issues issues
.union(comments) .union(comments)
.sortBy { .sortBy { case (issue, commentId, _, _) =>
case (issue, commentId, _, _) => issue.issueId.desc -> commentId
issue.issueId.desc -> commentId
} }
.list .list
.splitWith { .splitWith { case ((issue1, _, _, _), (issue2, _, _, _)) =>
case ((issue1, _, _, _), (issue2, _, _, _)) => issue1.issueId == issue2.issueId
issue1.issueId == issue2.issueId
} }
.map { .map {
_.head match { _.head match {
@@ -896,8 +881,8 @@ trait IssuesService {
.toList .toList
} }
def closeIssuesFromMessage(message: String, userName: String, owner: String, repository: String)( def closeIssuesFromMessage(message: String, userName: String, owner: String, repository: String)(implicit
implicit s: Session s: Session
): Seq[Int] = { ): Seq[Int] = {
extractCloseId(message).flatMap { issueId => extractCloseId(message).flatMap { issueId =>
for (issue <- getIssue(owner, repository, issueId) if !issue.closed) yield { for (issue <- getIssue(owner, repository, issueId) if !issue.closed) yield {
@@ -911,37 +896,37 @@ trait IssuesService {
def createReferComment(owner: String, repository: String, fromIssue: Issue, message: String, loginAccount: Account)( def createReferComment(owner: String, repository: String, fromIssue: Issue, message: String, loginAccount: Account)(
implicit s: Session implicit s: Session
): Unit = { ): Unit = {
extractGlobalIssueId(message).foreach { extractGlobalIssueId(message).foreach { case (_referredOwner, _referredRepository, referredIssueId) =>
case (_referredOwner, _referredRepository, referredIssueId) => val referredOwner = _referredOwner.getOrElse(owner)
val referredOwner = _referredOwner.getOrElse(owner) val referredRepository = _referredRepository.getOrElse(repository)
val referredRepository = _referredRepository.getOrElse(repository) getRepository(referredOwner, referredRepository).foreach { repo =>
getRepository(referredOwner, referredRepository).foreach { repo => if (isReadable(repo.repository, Option(loginAccount))) {
if (isReadable(repo.repository, Option(loginAccount))) { getIssue(referredOwner, referredRepository, referredIssueId.get).foreach { _ =>
getIssue(referredOwner, referredRepository, referredIssueId.get).foreach { _ => val (content, action) = if (owner == referredOwner && repository == referredRepository) {
val (content, action) = if (owner == referredOwner && repository == referredRepository) { (s"${fromIssue.issueId}:${fromIssue.title}", "refer")
(s"${fromIssue.issueId}:${fromIssue.title}", "refer") } else {
} else { (s"${fromIssue.issueId}:${owner}:${repository}:${fromIssue.title}", "refer_global")
(s"${fromIssue.issueId}:${owner}:${repository}:${fromIssue.title}", "refer_global")
}
referredIssueId.foreach(
x =>
// Not add if refer comment already exist.
if (!getComments(referredOwner, referredRepository, x.toInt).exists { x =>
(x.action == "refer" || x.action == "refer_global") && x.content == content
}) {
createComment(
referredOwner,
referredRepository,
loginAccount.userName,
x.toInt,
content,
action
)
}
)
} }
referredIssueId.foreach(x =>
// Not add if refer comment already exist.
if (
!getComments(referredOwner, referredRepository, x.toInt).exists { x =>
(x.action == "refer" || x.action == "refer_global") && x.content == content
}
) {
createComment(
referredOwner,
referredRepository,
loginAccount.userName,
x.toInt,
content,
action
)
}
)
} }
} }
}
} }
} }
@@ -958,13 +943,13 @@ trait IssuesService {
def getAssignableUserNames(owner: String, repository: String)(implicit s: Session): List[String] = { def getAssignableUserNames(owner: String, repository: String)(implicit s: Session): List[String] = {
(getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER)) ::: (getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER)) :::
(getAccountByUserName(owner) match { (getAccountByUserName(owner) match {
case Some(x) if x.isGroupAccount => case Some(x) if x.isGroupAccount =>
getGroupMembers(owner).map(_.userName) getGroupMembers(owner).map(_.userName)
case Some(_) => case Some(_) =>
List(owner) List(owner)
case None => case None =>
Nil Nil
})).distinct.sorted })).distinct.sorted
} }
} }
@@ -1090,9 +1075,8 @@ object IssuesService {
dim(0) -> dim(1) dim(0) -> dim(1)
} }
.groupBy(_._1) .groupBy(_._1)
.map { .map { case (key, values) =>
case (key, values) => key -> values.map(_._2).toSeq
key -> values.map(_._2).toSeq
} }
val (sort, direction) = conditions.get("sort").flatMap(_.headOption).getOrElse("created-desc") match { val (sort, direction) = conditions.get("sort").flatMap(_.headOption).getOrElse("created-desc") match {
@@ -1123,9 +1107,8 @@ object IssuesService {
val dim = x.split(">") val dim = x.split(">")
dim(0) -> ("gt", dim(1)) dim(0) -> ("gt", dim(1))
} }
.map { .map { case (key, (operator, value)) =>
case (key, (operator, value)) => CustomFieldCondition(key.stripPrefix("custom."), value, operator)
CustomFieldCondition(key.stripPrefix("custom."), value, operator)
} }
.toSeq .toSeq
@@ -1134,7 +1117,7 @@ object IssuesService {
conditions.get("milestone").flatMap(_.headOption) match { conditions.get("milestone").flatMap(_.headOption) match {
case None => None case None => None
case Some("none") => Some(None) case Some("none") => Some(None)
case Some(x) => Some(Some(x)) //milestones.get(x).map(x => Some(x)) case Some(x) => Some(Some(x)) // milestones.get(x).map(x => Some(x))
}, },
conditions.get("priority").map(_.headOption), // TODO conditions.get("priority").map(_.headOption), // TODO
conditions.get("author").flatMap(_.headOption), conditions.get("author").flatMap(_.headOption),

View File

@@ -30,8 +30,8 @@ trait LabelsService {
createLabel(owner, repository, labelName, color) createLabel(owner, repository, labelName, color)
} }
def updateLabel(owner: String, repository: String, labelId: Int, labelName: String, color: String)( def updateLabel(owner: String, repository: String, labelId: Int, labelName: String, color: String)(implicit
implicit s: Session s: Session
): Unit = ): Unit =
Labels Labels
.filter(_.byPrimaryKey(owner, repository, labelId)) .filter(_.byPrimaryKey(owner, repository, labelId))

View File

@@ -121,8 +121,8 @@ trait MergeService {
afterCommitId: ObjectId, afterCommitId: ObjectId,
loginAccount: Account, loginAccount: Account,
settings: SystemSettings settings: SystemSettings
)( )(implicit
implicit s: Session, s: Session,
c: JsonFormat.Context c: JsonFormat.Context
): Unit = { ): Unit = {
callWebHookOf(repository.owner, repository.name, WebHook.Push, settings) { callWebHookOf(repository.owner, repository.name, WebHook.Push, settings) {
@@ -220,7 +220,14 @@ trait MergeService {
requestRepositoryName: String, requestRepositoryName: String,
requestBranch: String requestBranch: String
): Option[String] = ): Option[String] =
tryMergeRemote(userName, repositoryName, branch, requestUserName, requestRepositoryName, requestBranch).left.toOption tryMergeRemote(
userName,
repositoryName,
branch,
requestUserName,
requestRepositoryName,
requestBranch
).left.toOption
def pullRemote( def pullRemote(
localRepository: RepositoryInfo, localRepository: RepositoryInfo,
@@ -236,84 +243,90 @@ trait MergeService {
val localRepositoryName = localRepository.name val localRepositoryName = localRepository.name
val remoteUserName = remoteRepository.owner val remoteUserName = remoteRepository.owner
val remoteRepositoryName = remoteRepository.name val remoteRepositoryName = remoteRepository.name
tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map { tryMergeRemote(
case (newTreeId, oldBaseId, oldHeadId) => localUserName,
Using.resource(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git => localRepositoryName,
val existIds = JGitUtil.getAllCommitIds(git).toSet localBranch,
remoteUserName,
remoteRepositoryName,
remoteBranch
).map { case (newTreeId, oldBaseId, oldHeadId) =>
Using.resource(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
val existIds = JGitUtil.getAllCommitIds(git).toSet
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress) val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
val newCommit = val newCommit =
Util.createMergeCommit(git.getRepository, newTreeId, committer, message, Seq(oldBaseId, oldHeadId)) Util.createMergeCommit(git.getRepository, newTreeId, committer, message, Seq(oldBaseId, oldHeadId))
Util.updateRefs(git.getRepository, s"refs/heads/${localBranch}", newCommit, false, committer, Some("merge")) Util.updateRefs(git.getRepository, s"refs/heads/${localBranch}", newCommit, false, committer, Some("merge"))
val commits = git.log val commits = git.log
.addRange(oldBaseId, newCommit) .addRange(oldBaseId, newCommit)
.call .call
.iterator .iterator
.asScala .asScala
.map(c => new JGitUtil.CommitInfo(c)) .map(c => new JGitUtil.CommitInfo(c))
.toList .toList
commits.foreach { commit =>
if (!existIds.contains(commit.id)) {
createIssueComment(localUserName, localRepositoryName, commit)
}
}
// record activity
val pushInfo = PushInfo(
localUserName,
localRepositoryName,
loginAccount.userName,
localBranch,
commits
)
recordActivity(pushInfo)
// close issue by commit message
if (localBranch == localRepository.repository.defaultBranch) {
commits.foreach { commit => commits.foreach { commit =>
if (!existIds.contains(commit.id)) { closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
createIssueComment(localUserName, localRepositoryName, commit) .foreach { issueId =>
} getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
} callIssuesWebHook("closed", localRepository, issue, loginAccount, settings)
val closeIssueInfo = CloseIssueInfo(
// record activity localRepository.owner,
val pushInfo = PushInfo( localRepository.name,
localUserName, localUserName,
localRepositoryName, issue.issueId,
loginAccount.userName, issue.title
localBranch, )
commits recordActivity(closeIssueInfo)
) PluginRegistry().getIssueHooks
recordActivity(pushInfo) .foreach(
_.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
// close issue by commit message
if (localBranch == localRepository.repository.defaultBranch) {
commits.foreach { commit =>
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
.foreach { issueId =>
getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
callIssuesWebHook("closed", localRepository, issue, loginAccount, settings)
val closeIssueInfo = CloseIssueInfo(
localRepository.owner,
localRepository.name,
localUserName,
issue.issueId,
issue.title
) )
recordActivity(closeIssueInfo)
PluginRegistry().getIssueHooks
.foreach(
_.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
)
}
} }
}
}
pullRequest.foreach { pullRequest =>
callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push, settings) {
for {
ownerAccount <- getAccountByUserName(localRepository.owner)
} yield {
WebHookService.WebHookPushPayload(
git,
loginAccount,
pullRequest.requestBranch,
localRepository,
commits,
ownerAccount,
oldId = oldBaseId,
newId = newCommit
)
} }
}
}
pullRequest.foreach { pullRequest =>
callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push, settings) {
for {
ownerAccount <- getAccountByUserName(localRepository.owner)
} yield {
WebHookService.WebHookPushPayload(
git,
loginAccount,
pullRequest.requestBranch,
localRepository,
commits,
ownerAccount,
oldId = oldBaseId,
newId = newCommit
)
} }
} }
} }
oldBaseId }
oldBaseId
}.toOption }.toOption
} }
@@ -621,11 +634,12 @@ object MergeService {
def checkConflictForce(): Option[String] = { def checkConflictForce(): Option[String] = {
val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true) val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true)
val conflicted = try { val conflicted =
!merger.merge(mergeBaseTip, mergeTip) try {
} catch { !merger.merge(mergeBaseTip, mergeTip)
case e: NoMergeBaseException => true } catch {
} case e: NoMergeBaseException => true
}
val mergeTipCommit = Using.resource(new RevWalk(git.getRepository))(_.parseCommit(mergeTip)) val mergeTipCommit = Using.resource(new RevWalk(git.getRepository))(_.parseCommit(mergeTip))
val committer = mergeTipCommit.getCommitterIdent val committer = mergeTipCommit.getCommitterIdent

View File

@@ -46,8 +46,8 @@ trait MilestonesService {
def getMilestone(owner: String, repository: String, milestoneId: Int)(implicit s: Session): Option[Milestone] = def getMilestone(owner: String, repository: String, milestoneId: Int)(implicit s: Session): Option[Milestone] =
Milestones.filter(_.byPrimaryKey(owner, repository, milestoneId)).firstOption Milestones.filter(_.byPrimaryKey(owner, repository, milestoneId)).firstOption
def getMilestonesWithIssueCount(owner: String, repository: String)( def getMilestonesWithIssueCount(owner: String, repository: String)(implicit
implicit s: Session s: Session
): List[(Milestone, Int, Int)] = { ): List[(Milestone, Int, Int)] = {
val counts = Issues val counts = Issues
.filter { t => .filter { t =>
@@ -78,13 +78,12 @@ trait MilestonesService {
def getApiMilestone(repository: RepositoryInfo, milestoneId: Int)(implicit s: Session): Option[ApiMilestone] = { def getApiMilestone(repository: RepositoryInfo, milestoneId: Int)(implicit s: Session): Option[ApiMilestone] = {
getMilestonesWithIssueCount(repository.owner, repository.name) getMilestonesWithIssueCount(repository.owner, repository.name)
.find(p => p._1.milestoneId == milestoneId) .find(p => p._1.milestoneId == milestoneId)
.map( .map(milestoneWithIssue =>
milestoneWithIssue => ApiMilestone(
ApiMilestone( repository.repository,
repository.repository, milestoneWithIssue._1,
milestoneWithIssue._1, milestoneWithIssue._2,
milestoneWithIssue._2, milestoneWithIssue._3
milestoneWithIssue._3
) )
) )
} }

View File

@@ -59,12 +59,11 @@ trait PrioritiesService {
Priorities Priorities
.filter(_.byRepository(owner, repository)) .filter(_.byRepository(owner, repository))
.list .list
.foreach( .foreach(p =>
p => Priorities
Priorities .filter(_.byPrimaryKey(owner, repository, p.priorityId))
.filter(_.byPrimaryKey(owner, repository, p.priorityId)) .map(_.ordering)
.map(_.ordering) .update(order.get(p.priorityId).get)
.update(order.get(p.priorityId).get)
) )
} }
@@ -93,12 +92,11 @@ trait PrioritiesService {
.map(_.isDefault) .map(_.isDefault)
.update(false) .update(false)
priorityId.foreach( priorityId.foreach(id =>
id => Priorities
Priorities .filter(_.byPrimaryKey(owner, repository, id))
.filter(_.byPrimaryKey(owner, repository, id)) .map(_.isDefault)
.map(_.isDefault) .update(true)
.update(true)
) )
} }
} }

View File

@@ -8,8 +8,8 @@ import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
trait ProtectedBranchService { trait ProtectedBranchService {
import ProtectedBranchService._ import ProtectedBranchService._
private def getProtectedBranchInfoOpt(owner: String, repository: String, branch: String)( private def getProtectedBranchInfoOpt(owner: String, repository: String, branch: String)(implicit
implicit session: Session session: Session
): Option[ProtectedBranchInfo] = ): Option[ProtectedBranchInfo] =
ProtectedBranches ProtectedBranches
.joinLeft(ProtectedBranchContexts) .joinLeft(ProtectedBranchContexts)
@@ -22,13 +22,12 @@ trait ProtectedBranchService {
.map { p => .map { p =>
p._1 -> p._2.flatMap(_._2) p._1 -> p._2.flatMap(_._2)
} }
.map { .map { case (t1, contexts) =>
case (t1, contexts) => new ProtectedBranchInfo(t1.userName, t1.repositoryName, t1.branch, true, contexts, t1.statusCheckAdmin)
new ProtectedBranchInfo(t1.userName, t1.repositoryName, t1.branch, true, contexts, t1.statusCheckAdmin)
} }
def getProtectedBranchInfo(owner: String, repository: String, branch: String)( def getProtectedBranchInfo(owner: String, repository: String, branch: String)(implicit
implicit session: Session session: Session
): ProtectedBranchInfo = ): ProtectedBranchInfo =
getProtectedBranchInfoOpt(owner, repository, branch).getOrElse( getProtectedBranchInfoOpt(owner, repository, branch).getOrElse(
ProtectedBranchInfo.disabled(owner, repository, branch) ProtectedBranchInfo.disabled(owner, repository, branch)
@@ -88,9 +87,11 @@ object ProtectedBranchService {
val branch = command.getRefName.stripPrefix("refs/heads/") val branch = command.getRefName.stripPrefix("refs/heads/")
if (branch != command.getRefName) { if (branch != command.getRefName) {
val repositoryInfo = getRepository(owner, repository) val repositoryInfo = getRepository(owner, repository)
if (command.getType == ReceiveCommand.Type.DELETE && repositoryInfo.exists( if (
_.repository.defaultBranch == branch command.getType == ReceiveCommand.Type.DELETE && repositoryInfo.exists(
)) { _.repository.defaultBranch == branch
)
) {
Some(s"refusing to delete the branch: ${command.getRefName}.") Some(s"refusing to delete the branch: ${command.getRefName}.")
} else { } else {
getProtectedBranchInfo(owner, repository, branch).getStopReason( getProtectedBranchInfo(owner, repository, branch).getStopReason(
@@ -128,15 +129,14 @@ object ProtectedBranchService {
def isAdministrator(pusher: String)(implicit session: Session): Boolean = def isAdministrator(pusher: String)(implicit session: Session): Boolean =
pusher == owner || getGroupMembers(owner).exists(gm => gm.userName == pusher && gm.isManager) || pusher == owner || getGroupMembers(owner).exists(gm => gm.userName == pusher && gm.isManager) ||
getCollaborators(owner, repository).exists { getCollaborators(owner, repository).exists { case (collaborator, isGroup) =>
case (collaborator, isGroup) => if (collaborator.role == Role.ADMIN.name) {
if (collaborator.role == Role.ADMIN.name) { if (isGroup) {
if (isGroup) { getGroupMembers(collaborator.collaboratorName).exists(gm => gm.userName == pusher)
getGroupMembers(collaborator.collaboratorName).exists(gm => gm.userName == pusher) } else {
} else { collaborator.collaboratorName == pusher
collaborator.collaboratorName == pusher }
} } else false
} else false
} }
/** /**
@@ -144,8 +144,8 @@ object ProtectedBranchService {
* Can't be deleted * Can't be deleted
* Can't have changes merged into them until required status checks pass * Can't have changes merged into them until required status checks pass
*/ */
def getStopReason(isAllowNonFastForwards: Boolean, command: ReceiveCommand, pusher: String)( def getStopReason(isAllowNonFastForwards: Boolean, command: ReceiveCommand, pusher: String)(implicit
implicit session: Session session: Session
): Option[String] = { ): Option[String] = {
if (enabled) { if (enabled) {
command.getType() match { command.getType() match {

View File

@@ -34,8 +34,8 @@ trait PullRequestService {
with ActivityService => with ActivityService =>
import PullRequestService._ import PullRequestService._
def getPullRequest(owner: String, repository: String, issueId: Int)( def getPullRequest(owner: String, repository: String, issueId: Int)(implicit
implicit s: Session s: Session
): Option[(Issue, PullRequest)] = ): Option[(Issue, PullRequest)] =
getIssue(owner, repository, issueId.toString).flatMap { issue => getIssue(owner, repository, issueId.toString).flatMap { issue =>
PullRequests.filter(_.byPrimaryKey(owner, repository, issueId)).firstOption.map { pullreq => PullRequests.filter(_.byPrimaryKey(owner, repository, issueId)).firstOption.map { pullreq =>
@@ -43,24 +43,24 @@ trait PullRequestService {
} }
} }
def updateCommitId(owner: String, repository: String, issueId: Int, commitIdTo: String, commitIdFrom: String)( def updateCommitId(owner: String, repository: String, issueId: Int, commitIdTo: String, commitIdFrom: String)(implicit
implicit s: Session s: Session
): Unit = ): Unit =
PullRequests PullRequests
.filter(_.byPrimaryKey(owner, repository, issueId)) .filter(_.byPrimaryKey(owner, repository, issueId))
.map(pr => pr.commitIdTo -> pr.commitIdFrom) .map(pr => pr.commitIdTo -> pr.commitIdFrom)
.update((commitIdTo, commitIdFrom)) .update((commitIdTo, commitIdFrom))
def updateDraftToPullRequest(owner: String, repository: String, issueId: Int)( def updateDraftToPullRequest(owner: String, repository: String, issueId: Int)(implicit
implicit s: Session s: Session
): Unit = ): Unit =
PullRequests PullRequests
.filter(_.byPrimaryKey(owner, repository, issueId)) .filter(_.byPrimaryKey(owner, repository, issueId))
.map(pr => pr.isDraft) .map(pr => pr.isDraft)
.update(false) .update(false)
def updateBaseBranch(owner: String, repository: String, issueId: Int, baseBranch: String, commitIdTo: String)( def updateBaseBranch(owner: String, repository: String, issueId: Int, baseBranch: String, commitIdTo: String)(implicit
implicit s: Session s: Session
): Unit = { ): Unit = {
PullRequests PullRequests
.filter(_.byPrimaryKey(owner, repository, issueId)) .filter(_.byPrimaryKey(owner, repository, issueId))
@@ -68,19 +68,18 @@ trait PullRequestService {
.update((baseBranch, commitIdTo)) .update((baseBranch, commitIdTo))
} }
def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])( def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])(implicit
implicit s: Session s: Session
): List[PullRequestCount] = ): List[PullRequestCount] =
PullRequests PullRequests
.join(Issues) .join(Issues)
.on { (t1, t2) => .on { (t1, t2) =>
t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId) t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
} }
.filter { .filter { case (t1, t2) =>
case (t1, t2) => (t2.closed === closed.bind)
(t2.closed === closed.bind) .&&(t1.userName === owner.get.bind, owner.isDefined)
.&&(t1.userName === owner.get.bind, owner.isDefined) .&&(t1.repositoryName === repository.get.bind, repository.isDefined)
.&&(t1.repositoryName === repository.get.bind, repository.isDefined)
} }
.groupBy { case (t1, t2) => t2.openedUserName } .groupBy { case (t1, t2) => t2.openedUserName }
.map { case (userName, t) => userName -> t.length } .map { case (userName, t) => userName -> t.length }
@@ -182,12 +181,11 @@ trait PullRequestService {
.on { (t1, t2) => .on { (t1, t2) =>
t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId) t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
} }
.filter { .filter { case (t1, t2) =>
case (t1, t2) => (t1.requestUserName === userName.bind)
(t1.requestUserName === userName.bind) .&&(t1.requestRepositoryName === repositoryName.bind)
.&&(t1.requestRepositoryName === repositoryName.bind) .&&(t1.requestBranch === branch.bind)
.&&(t1.requestBranch === branch.bind) .&&(t2.closed === closed.get.bind, closed.isDefined)
.&&(t2.closed === closed.get.bind, closed.isDefined)
} }
.map { case (t1, t2) => t1 } .map { case (t1, t2) => t1 }
.list .list
@@ -200,12 +198,11 @@ trait PullRequestService {
.on { (t1, t2) => .on { (t1, t2) =>
t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId) t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
} }
.filter { .filter { case (t1, t2) =>
case (t1, t2) => (t1.requestUserName === userName.bind)
(t1.requestUserName === userName.bind) .&&(t1.requestRepositoryName === repositoryName.bind)
.&&(t1.requestRepositoryName === repositoryName.bind) .&&(t1.branch === branch.bind)
.&&(t1.branch === branch.bind) .&&(t2.closed === closed.get.bind, closed.isDefined)
.&&(t2.closed === closed.get.bind, closed.isDefined)
} }
.map { case (t1, t2) => t1 } .map { case (t1, t2) => t1 }
.list .list
@@ -217,22 +214,21 @@ trait PullRequestService {
* 2. return if exists pull request to other branch * 2. return if exists pull request to other branch
* 2. return None * 2. return None
*/ */
def getPullRequestFromBranch(userName: String, repositoryName: String, branch: String, defaultBranch: String)( def getPullRequestFromBranch(userName: String, repositoryName: String, branch: String, defaultBranch: String)(implicit
implicit s: Session s: Session
): Option[(PullRequest, Issue)] = ): Option[(PullRequest, Issue)] =
PullRequests PullRequests
.join(Issues) .join(Issues)
.on { (t1, t2) => .on { (t1, t2) =>
t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId) t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
} }
.filter { .filter { case (t1, t2) =>
case (t1, t2) => (t1.requestUserName === userName.bind) &&
(t1.requestUserName === userName.bind) && (t1.requestRepositoryName === repositoryName.bind) &&
(t1.requestRepositoryName === repositoryName.bind) && (t1.requestBranch === branch.bind) &&
(t1.requestBranch === branch.bind) && (t1.userName === userName.bind) &&
(t1.userName === userName.bind) && (t1.repositoryName === repositoryName.bind) &&
(t1.repositoryName === repositoryName.bind) && (t2.closed === false.bind)
(t2.closed === false.bind)
} }
.sortBy { case (t1, t2) => t1.branch =!= defaultBranch.bind } .sortBy { case (t1, t2) => t1.branch =!= defaultBranch.bind }
.firstOption .firstOption
@@ -247,8 +243,8 @@ trait PullRequestService {
pusherAccount: Account, pusherAccount: Account,
action: String, action: String,
settings: SystemSettings settings: SystemSettings
)( )(implicit
implicit s: Session, s: Session,
c: JsonFormat.Context c: JsonFormat.Context
): Unit = { ): Unit = {
getPullRequestsByRequest(owner, repository, branch, Some(false)).foreach { pullreq => getPullRequestsByRequest(owner, repository, branch, Some(false)).foreach { pullreq =>
@@ -273,10 +269,9 @@ trait PullRequestService {
(file, commentId, Left(oldLine)) (file, commentId, Left(oldLine))
} }
.groupBy { case (file, _, _) => file } .groupBy { case (file, _, _) => file }
.map { .map { case (file, comments) =>
case (file, comments) => file ->
file -> comments.map { case (_, commentId, lineNumber) => (commentId, lineNumber) }
comments.map { case (_, commentId, lineNumber) => (commentId, lineNumber) }
} }
// Update comments position // Update comments position
@@ -312,64 +307,63 @@ trait PullRequestService {
body: Option[String], body: Option[String],
state: Option[String], state: Option[String],
base: Option[String] base: Option[String]
)( )(implicit
implicit s: Session, s: Session,
c: JsonFormat.Context c: JsonFormat.Context
): Unit = { ): Unit = {
getPullRequest(repository.owner, repository.name, issueId).foreach { getPullRequest(repository.owner, repository.name, issueId).foreach { case (issue, pr) =>
case (issue, pr) => if (Repositories.filter(_.byRepository(pr.userName, pr.repositoryName)).exists.run) {
if (Repositories.filter(_.byRepository(pr.userName, pr.repositoryName)).exists.run) { // Update base branch
// Update base branch base.foreach { _base =>
base.foreach { _base => if (pr.branch != _base) {
if (pr.branch != _base) { Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git => getBranches(git, repository.repository.defaultBranch, origin = true)
getBranches(git, repository.repository.defaultBranch, origin = true) .find(_.name == _base)
.find(_.name == _base) .foreach(br => updateBaseBranch(repository.owner, repository.name, issueId, br.name, br.commitId))
.foreach(br => updateBaseBranch(repository.owner, repository.name, issueId, br.name, br.commitId))
}
createComment(
repository.owner,
repository.name,
loginAccount.userName,
issue.issueId,
pr.branch + "\r\n" + _base,
"change_base_branch"
)
} }
createComment(
repository.owner,
repository.name,
loginAccount.userName,
issue.issueId,
pr.branch + "\r\n" + _base,
"change_base_branch"
)
} }
// Update title and content
title.foreach { _title =>
updateIssue(repository.owner, repository.name, issueId, _title, body)
if (issue.title != _title) {
createComment(
repository.owner,
repository.name,
loginAccount.userName,
issue.issueId,
issue.title + "\r\n" + _title,
"change_title"
)
}
}
// Update state
val action = (state, issue.closed) match {
case (Some("open"), true) =>
updateClosed(repository.owner, repository.name, issueId, closed = false)
"reopened"
case (Some("closed"), false) =>
updateClosed(repository.owner, repository.name, issueId, closed = true)
"closed"
case _ => "edited"
}
// Call web hook
callPullRequestWebHookByRequestBranch(
action,
getRepository(repository.owner, repository.name).get,
pr.requestBranch,
loginAccount,
settings
)
} }
// Update title and content
title.foreach { _title =>
updateIssue(repository.owner, repository.name, issueId, _title, body)
if (issue.title != _title) {
createComment(
repository.owner,
repository.name,
loginAccount.userName,
issue.issueId,
issue.title + "\r\n" + _title,
"change_title"
)
}
}
// Update state
val action = (state, issue.closed) match {
case (Some("open"), true) =>
updateClosed(repository.owner, repository.name, issueId, closed = false)
"reopened"
case (Some("closed"), false) =>
updateClosed(repository.owner, repository.name, issueId, closed = true)
"closed"
case _ => "edited"
}
// Call web hook
callPullRequestWebHookByRequestBranch(
action,
getRepository(repository.owner, repository.name).get,
pr.requestBranch,
loginAccount,
settings
)
}
} }
} }
@@ -388,15 +382,14 @@ trait PullRequestService {
.on { (t1, t2) => .on { (t1, t2) =>
t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId) t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
} }
.filter { .filter { case (t1, t2) =>
case (t1, t2) => (t1.userName === userName.bind) &&
(t1.userName === userName.bind) && (t1.repositoryName === repositoryName.bind) &&
(t1.repositoryName === repositoryName.bind) && (t1.branch === toBranch.bind) &&
(t1.branch === toBranch.bind) && (t1.requestUserName === userName.bind) &&
(t1.requestUserName === userName.bind) && (t1.requestRepositoryName === repositoryName.bind) &&
(t1.requestRepositoryName === repositoryName.bind) && (t1.requestBranch === fromBranch.bind) &&
(t1.requestBranch === fromBranch.bind) && (t1.commitIdTo === commitId.bind)
(t1.commitIdTo === commitId.bind)
} }
.firstOption .firstOption
} }
@@ -412,63 +405,61 @@ trait PullRequestService {
val (_, diffs) = getRequestCompareInfo(userName, repositoryName, oldCommitId, userName, repositoryName, newCommitId) val (_, diffs) = getRequestCompareInfo(userName, repositoryName, oldCommitId, userName, repositoryName, newCommitId)
val patchs = positions.map { val patchs = positions.map { case (file, _) =>
case (file, _) => diffs
diffs .find(x => x.oldPath == file)
.find(x => x.oldPath == file) .map { diff =>
.map { diff => (diff.oldContent, diff.newContent) match {
(diff.oldContent, diff.newContent) match { case (Some(oldContent), Some(newContent)) => {
case (Some(oldContent), Some(newContent)) => { val oldLines = convertLineSeparator(oldContent, "LF").split("\n")
val oldLines = convertLineSeparator(oldContent, "LF").split("\n") val newLines = convertLineSeparator(newContent, "LF").split("\n")
val newLines = convertLineSeparator(newContent, "LF").split("\n") file -> Option(DiffUtils.diff(oldLines.toList.asJava, newLines.toList.asJava))
file -> Option(DiffUtils.diff(oldLines.toList.asJava, newLines.toList.asJava))
}
case _ =>
file -> None
} }
case _ =>
file -> None
} }
.getOrElse { }
file -> None .getOrElse {
} file -> None
}
} }
positions.foreach { positions.foreach { case (file, comments) =>
case (file, comments) => patchs(file) match {
patchs(file) match { case Some(patch) =>
case Some(patch) => file -> comments.foreach { case (commentId, lineNumber) =>
file -> comments.foreach { lineNumber match {
case (commentId, lineNumber) => case Left(oldLine) => updateCommitCommentPosition(commentId, newCommitId, Some(oldLine), None)
lineNumber match { case Right(newLine) =>
case Left(oldLine) => updateCommitCommentPosition(commentId, newCommitId, Some(oldLine), None) var counter = newLine
case Right(newLine) => patch.getDeltas.asScala.filter(_.getSource.getPosition < newLine).foreach { delta =>
var counter = newLine delta.getType match {
patch.getDeltas.asScala.filter(_.getSource.getPosition < newLine).foreach { delta => case DeltaType.CHANGE =>
delta.getType match { if (
case DeltaType.CHANGE => delta.getSource.getPosition <= newLine - 1 && newLine <= delta.getSource.getPosition + delta.getTarget.getLines.size
if (delta.getSource.getPosition <= newLine - 1 && newLine <= delta.getSource.getPosition + delta.getTarget.getLines.size) { ) {
counter = -1 counter = -1
} else { } else {
counter = counter + (delta.getTarget.getLines.size - delta.getSource.getLines.size) counter = counter + (delta.getTarget.getLines.size - delta.getSource.getLines.size)
}
case DeltaType.INSERT => counter = counter + delta.getTarget.getLines.size
case DeltaType.DELETE => counter = counter - delta.getSource.getLines.size
case DeltaType.EQUAL => // Do nothing
} }
} case DeltaType.INSERT => counter = counter + delta.getTarget.getLines.size
if (counter >= 0) { case DeltaType.DELETE => counter = counter - delta.getSource.getLines.size
updateCommitCommentPosition(commentId, newCommitId, None, Some(counter)) case DeltaType.EQUAL => // Do nothing
} }
}
if (counter >= 0) {
updateCommitCommentPosition(commentId, newCommitId, None, Some(counter))
} }
} }
case _ => }
comments.foreach { case _ =>
case (commentId, lineNumber) => comments.foreach { case (commentId, lineNumber) =>
lineNumber match { lineNumber match {
case Right(oldLine) => updateCommitCommentPosition(commentId, newCommitId, Some(oldLine), None) case Right(oldLine) => updateCommitCommentPosition(commentId, newCommitId, Some(oldLine), None)
case Left(newLine) => updateCommitCommentPosition(commentId, newCommitId, None, Some(newLine)) case Left(newLine) => updateCommitCommentPosition(commentId, newCommitId, None, Some(newLine))
}
} }
} }
}
} }
} }
@@ -540,20 +531,19 @@ trait PullRequestService {
(commits, diffs) (commits, diffs)
} }
def getPullRequestComments(userName: String, repositoryName: String, issueId: Int, commits: Seq[CommitInfo])( def getPullRequestComments(userName: String, repositoryName: String, issueId: Int, commits: Seq[CommitInfo])(implicit
implicit s: Session s: Session
): Seq[Comment] = { ): Seq[Comment] = {
(commits.flatMap(commit => getCommitComments(userName, repositoryName, commit.id, true)) ++ getComments( (commits.flatMap(commit => getCommitComments(userName, repositoryName, commit.id, true)) ++ getComments(
userName, userName,
repositoryName, repositoryName,
issueId issueId
)).groupBy { )).groupBy {
case x: IssueComment => (Some(x.commentId), None, None, None) case x: IssueComment => (Some(x.commentId), None, None, None)
case x: CommitComment if x.fileName.isEmpty => (Some(x.commentId), None, None, None) case x: CommitComment if x.fileName.isEmpty => (Some(x.commentId), None, None, None)
case x: CommitComment => (None, x.fileName, x.originalOldLine, x.originalNewLine) case x: CommitComment => (None, x.fileName, x.originalOldLine, x.originalNewLine)
case x => throw new MatchError(x) case x => throw new MatchError(x)
} }.toSeq
.toSeq
.map { .map {
// Normal comment // Normal comment
case ((Some(_), _, _, _), comments) => case ((Some(_), _, _, _), comments) =>
@@ -578,8 +568,8 @@ trait PullRequestService {
.sortWith(_.registeredDate before _.registeredDate) .sortWith(_.registeredDate before _.registeredDate)
} }
def markMergeAndClosePullRequest(userName: String, owner: String, repository: String, pull: PullRequest)( def markMergeAndClosePullRequest(userName: String, owner: String, repository: String, pull: PullRequest)(implicit
implicit s: Session s: Session
): Unit = { ): Unit = {
createComment(owner, repository, userName, pull.issueId, "Merged by user", "merge") createComment(owner, repository, userName, pull.issueId, "Merged by user", "merge")
createComment(owner, repository, userName, pull.issueId, "Close", "close") createComment(owner, repository, userName, pull.issueId, "Close", "close")
@@ -609,33 +599,32 @@ trait PullRequestService {
Using.resources( Using.resources(
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)), Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name)) Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
) { ) { case (oldGit, newGit) =>
case (oldGit, newGit) => if (originRepository.branchList.contains(originId)) {
if (originRepository.branchList.contains(originId)) { val forkedId2 =
val forkedId2 = forkedRepository.tags.collectFirst { case x if x.name == forkedId => x.commitId }.getOrElse(forkedId)
forkedRepository.tags.collectFirst { case x if x.name == forkedId => x.commitId }.getOrElse(forkedId)
val originId2 = JGitUtil.getForkedCommitId( val originId2 = JGitUtil.getForkedCommitId(
oldGit, oldGit,
newGit, newGit,
originRepository.owner, originRepository.owner,
originRepository.name, originRepository.name,
originId, originId,
forkedRepository.owner, forkedRepository.owner,
forkedRepository.name, forkedRepository.name,
forkedId2 forkedId2
) )
(Option(oldGit.getRepository.resolve(originId2)), Option(newGit.getRepository.resolve(forkedId2))) (Option(oldGit.getRepository.resolve(originId2)), Option(newGit.getRepository.resolve(forkedId2)))
} else { } else {
val originId2 = val originId2 =
originRepository.tags.collectFirst { case x if x.name == originId => x.commitId }.getOrElse(originId) originRepository.tags.collectFirst { case x if x.name == originId => x.commitId }.getOrElse(originId)
val forkedId2 = val forkedId2 =
forkedRepository.tags.collectFirst { case x if x.name == forkedId => x.commitId }.getOrElse(forkedId) forkedRepository.tags.collectFirst { case x if x.name == forkedId => x.commitId }.getOrElse(forkedId)
(Option(oldGit.getRepository.resolve(originId2)), Option(newGit.getRepository.resolve(forkedId2))) (Option(oldGit.getRepository.resolve(originId2)), Option(newGit.getRepository.resolve(forkedId2)))
} }
} }
} }
} }
@@ -661,8 +650,8 @@ object PullRequestService {
val statuses: List[CommitStatus] = val statuses: List[CommitStatus] =
commitStatuses ++ (branchProtection.contexts.toSet -- commitStatuses.map(_.context).toSet) commitStatuses ++ (branchProtection.contexts.toSet -- commitStatuses.map(_.context).toSet)
.map(CommitStatus.pending(branchProtection.owner, branchProtection.repository, _)) .map(CommitStatus.pending(branchProtection.owner, branchProtection.repository, _))
val hasRequiredStatusProblem = needStatusCheck && branchProtection.contexts.exists( val hasRequiredStatusProblem = needStatusCheck && branchProtection.contexts.exists(context =>
context => statuses.find(_.context == context).map(_.state) != Some(CommitState.SUCCESS) statuses.find(_.context == context).map(_.state) != Some(CommitState.SUCCESS)
) )
val hasProblem = hasRequiredStatusProblem || hasConflict || (statuses.nonEmpty && CommitState.combine( val hasProblem = hasRequiredStatusProblem || hasConflict || (statuses.nonEmpty && CommitState.combine(
statuses.map(_.state).toSet statuses.map(_.state).toSet

View File

@@ -36,14 +36,14 @@ trait ReleaseService {
ReleaseAssets.filter(x => x.byTag(owner, repository, tag)).list ReleaseAssets.filter(x => x.byTag(owner, repository, tag)).list
} }
def getReleaseAssetsMap(owner: String, repository: String, releases: Seq[ReleaseTag])( def getReleaseAssetsMap(owner: String, repository: String, releases: Seq[ReleaseTag])(implicit
implicit s: Session s: Session
): Map[ReleaseTag, Seq[ReleaseAsset]] = { ): Map[ReleaseTag, Seq[ReleaseAsset]] = {
releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.tag))).toMap releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.tag))).toMap
} }
def getReleaseAsset(owner: String, repository: String, tag: String, fileId: String)( def getReleaseAsset(owner: String, repository: String, tag: String, fileId: String)(implicit
implicit s: Session s: Session
): Option[ReleaseAsset] = { ): Option[ReleaseAsset] = {
ReleaseAssets.filter(x => x.byPrimaryKey(owner, repository, tag, fileId)) firstOption ReleaseAssets.filter(x => x.byPrimaryKey(owner, repository, tag, fileId)) firstOption
} }
@@ -76,8 +76,8 @@ trait ReleaseService {
ReleaseTags.filter(x => x.byRepository(owner, repository)).sortBy(x => x.updatedDate).list ReleaseTags.filter(x => x.byRepository(owner, repository)).sortBy(x => x.updatedDate).list
} }
def getReleases(owner: String, repository: String, tags: Seq[JGitUtil.TagInfo])( def getReleases(owner: String, repository: String, tags: Seq[JGitUtil.TagInfo])(implicit
implicit s: Session s: Session
): Seq[ReleaseTag] = { ): Seq[ReleaseTag] = {
ReleaseTags ReleaseTags
.filter(x => x.byRepository(owner, repository)) .filter(x => x.byRepository(owner, repository))
@@ -89,8 +89,8 @@ trait ReleaseService {
ReleaseTags.filter(_.byTag(owner, repository, tag)).firstOption ReleaseTags.filter(_.byTag(owner, repository, tag)).firstOption
} }
def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])( def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])(implicit
implicit s: Session s: Session
): Int = { ): Int = {
ReleaseTags ReleaseTags
.filter(_.byPrimaryKey(owner, repository, tag)) .filter(_.byPrimaryKey(owner, repository, tag))

View File

@@ -65,7 +65,8 @@ trait RepositoryCommitFileService {
path, path,
newFileName, newFileName,
oldFileName, oldFileName,
if (content.nonEmpty) { content.getBytes(charset) } else { Array.emptyByteArray }, if (content.nonEmpty) { content.getBytes(charset) }
else { Array.emptyByteArray },
message, message,
commit, commit,
loginAccount, loginAccount,

View File

@@ -92,7 +92,7 @@ trait RepositoryCreationService {
RepositoryCreationService.startCreation(owner, name) RepositoryCreationService.startCreation(owner, name)
try { try {
Database() withTransaction { implicit session => Database() withTransaction { implicit session =>
//val ownerAccount = getAccountByUserName(owner).get // val ownerAccount = getAccountByUserName(owner).get
val loginUserName = loginAccount.userName val loginUserName = loginAccount.userName
val copyRepositoryDir = if (initOption == "COPY") { val copyRepositoryDir = if (initOption == "COPY") {
@@ -214,9 +214,8 @@ trait RepositoryCreationService {
// Set default collaborators for the private fork // Set default collaborators for the private fork
if (repository.repository.isPrivate) { if (repository.repository.isPrivate) {
// Copy collaborators from the source repository // Copy collaborators from the source repository
getCollaborators(repository.owner, repository.name).foreach { getCollaborators(repository.owner, repository.name).foreach { case (collaborator, _) =>
case (collaborator, _) => addCollaborator(accountName, repository.name, collaborator.collaboratorName, collaborator.role)
addCollaborator(accountName, repository.name, collaborator.collaboratorName, collaborator.role)
} }
// Register an owner of the source repository as a collaborator // Register an owner of the source repository as a collaborator
addCollaborator(accountName, repository.name, repository.owner, Role.ADMIN.name) addCollaborator(accountName, repository.name, repository.owner, Role.ADMIN.name)

View File

@@ -14,26 +14,25 @@ import scala.util.Using
trait RepositorySearchService { self: IssuesService => trait RepositorySearchService { self: IssuesService =>
import RepositorySearchService._ import RepositorySearchService._
def countIssues(owner: String, repository: String, query: String, pullRequest: Boolean)( def countIssues(owner: String, repository: String, query: String, pullRequest: Boolean)(implicit
implicit session: Session session: Session
): Int = ): Int =
searchIssuesByKeyword(owner, repository, query, pullRequest).length searchIssuesByKeyword(owner, repository, query, pullRequest).length
def searchIssues(owner: String, repository: String, query: String, pullRequest: Boolean)( def searchIssues(owner: String, repository: String, query: String, pullRequest: Boolean)(implicit
implicit session: Session session: Session
): List[IssueSearchResult] = ): List[IssueSearchResult] =
searchIssuesByKeyword(owner, repository, query, pullRequest).map { searchIssuesByKeyword(owner, repository, query, pullRequest).map { case (issue, commentCount, content) =>
case (issue, commentCount, content) => IssueSearchResult(
IssueSearchResult( issue.issueId,
issue.issueId, issue.isPullRequest,
issue.isPullRequest, issue.title,
issue.title, issue.closed,
issue.closed, issue.openedUserName,
issue.openedUserName, issue.registeredDate,
issue.registeredDate, commentCount,
commentCount, getHighlightText(content, query)._1
getHighlightText(content, query)._1 )
)
} }
def countFiles(owner: String, repository: String, query: String): Int = def countFiles(owner: String, repository: String, query: String): Int =
@@ -48,10 +47,9 @@ trait RepositorySearchService { self: IssuesService =>
} else { } else {
val files = searchRepositoryFiles(git, query) val files = searchRepositoryFiles(git, query)
val commits = JGitUtil.getLatestCommitFromPaths(git, files.map(_._1), "HEAD") val commits = JGitUtil.getLatestCommitFromPaths(git, files.map(_._1), "HEAD")
files.map { files.map { case (path, text) =>
case (path, text) => val (highlightText, lineNumber) = getHighlightText(text, query)
val (highlightText, lineNumber) = getHighlightText(text, query) FileSearchResult(path, commits(path).getCommitterIdent.getWhen, highlightText, lineNumber)
FileSearchResult(path, commits(path).getCommitterIdent.getWhen, highlightText, lineNumber)
} }
} }
} }
@@ -68,15 +66,14 @@ trait RepositorySearchService { self: IssuesService =>
} else { } else {
val files = searchRepositoryFiles(git, query) val files = searchRepositoryFiles(git, query)
val commits = JGitUtil.getLatestCommitFromPaths(git, files.map(_._1), "HEAD") val commits = JGitUtil.getLatestCommitFromPaths(git, files.map(_._1), "HEAD")
files.map { files.map { case (path, text) =>
case (path, text) => val (highlightText, lineNumber) = getHighlightText(text, query)
val (highlightText, lineNumber) = getHighlightText(text, query) FileSearchResult(
FileSearchResult( path.stripSuffix(".md"),
path.stripSuffix(".md"), commits(path).getCommitterIdent.getWhen,
commits(path).getCommitterIdent.getWhen, highlightText,
highlightText, lineNumber
lineNumber )
)
} }
} }
} }

View File

@@ -187,12 +187,11 @@ trait RepositoryService {
val newLabelMap = val newLabelMap =
Labels.filter(_.byRepository(newUserName, newRepositoryName)).map(x => (x.labelName, x.labelId)).list.toMap Labels.filter(_.byRepository(newUserName, newRepositoryName)).map(x => (x.labelName, x.labelId)).list.toMap
IssueLabels.insertAll( IssueLabels.insertAll(
issueLabels.map( issueLabels.map(x =>
x => x.copy(
x.copy( labelId = newLabelMap(oldLabelMap(x.labelId)),
labelId = newLabelMap(oldLabelMap(x.labelId)), userName = newUserName,
userName = newUserName, repositoryName = newRepositoryName
repositoryName = newRepositoryName
) )
): _* ): _*
) )
@@ -275,12 +274,11 @@ trait RepositoryService {
(x.userName, x.repositoryName) (x.userName, x.repositoryName)
} }
.list .list
.foreach { .foreach { case (userName, repositoryName) =>
case (userName, repositoryName) => Repositories
Repositories .filter(_.byRepository(userName, repositoryName))
.filter(_.byRepository(userName, repositoryName)) .map(x => (x.originUserName ?, x.originRepositoryName ?))
.map(x => (x.originUserName ?, x.originRepositoryName ?)) .update(None, None)
.update(None, None)
} }
// Update PARENT_USER_NAME and PARENT_REPOSITORY_NAME // Update PARENT_USER_NAME and PARENT_REPOSITORY_NAME
@@ -292,12 +290,11 @@ trait RepositoryService {
(x.userName, x.repositoryName) (x.userName, x.repositoryName)
} }
.list .list
.foreach { .foreach { case (userName, repositoryName) =>
case (userName, repositoryName) => Repositories
Repositories .filter(_.byRepository(userName, repositoryName))
.filter(_.byRepository(userName, repositoryName)) .map(x => (x.parentUserName ?, x.parentRepositoryName ?))
.map(x => (x.parentUserName ?, x.parentRepositoryName ?)) .update(None, None)
.update(None, None)
} }
} }
@@ -321,31 +318,29 @@ trait RepositoryService {
(Repositories (Repositories
.join(Accounts) .join(Accounts)
.on(_.userName === _.userName) .on(_.userName === _.userName)
.filter { .filter { case (t1, t2) =>
case (t1, t2) => t1.byRepository(userName, repositoryName) && t2.removed === false.bind
t1.byRepository(userName, repositoryName) && t2.removed === false.bind } firstOption) map { case (repository, account) =>
} firstOption) map { // for getting issue count and pull request count
case (repository, account) => val issues = Issues
// for getting issue count and pull request count .filter { t =>
val issues = Issues t.byRepository(repository.userName, repository.repositoryName) && (t.closed === false.bind)
.filter { t => }
t.byRepository(repository.userName, repository.repositoryName) && (t.closed === false.bind) .map(_.pullRequest)
} .list
.map(_.pullRequest)
.list
new RepositoryInfo( new RepositoryInfo(
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName), JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName),
repository, repository,
issues.count(_ == false), issues.count(_ == false),
issues.count(_ == true), issues.count(_ == true),
getForkedCount( getForkedCount(
repository.originUserName.getOrElse(repository.userName), repository.originUserName.getOrElse(repository.userName),
repository.originRepositoryName.getOrElse(repository.repositoryName) repository.originRepositoryName.getOrElse(repository.repositoryName)
), ),
getOpenMilestones(repository.userName, repository.repositoryName), getOpenMilestones(repository.userName, repository.repositoryName),
getRepositoryManagers(repository.userName, repository.repositoryName) getRepositoryManagers(repository.userName, repository.repositoryName)
) )
} }
} }
@@ -396,8 +391,8 @@ trait RepositoryService {
* Returns the list of repositories which are owned by the specified user. * Returns the list of repositories which are owned by the specified user.
* This list includes group repositories if the specified user is a member of the group. * This list includes group repositories if the specified user is a member of the group.
*/ */
def getUserRepositories(userName: String, withoutPhysicalInfo: Boolean = false)( def getUserRepositories(userName: String, withoutPhysicalInfo: Boolean = false)(implicit
implicit s: Session s: Session
): List[RepositoryInfo] = { ): List[RepositoryInfo] = {
Repositories Repositories
.filter { t1 => .filter { t1 =>
@@ -464,15 +459,14 @@ trait RepositoryService {
Repositories Repositories
.join(Accounts) .join(Accounts)
.on(_.userName === _.userName) .on(_.userName === _.userName)
.filter { .filter { case (t1, t2) =>
case (t1, t2) => (t2.removed === false.bind) && ((t1.isPrivate === false.bind && !limit.bind) || (t1.userName === x.userName) ||
(t2.removed === false.bind) && ((t1.isPrivate === false.bind && !limit.bind) || (t1.userName === x.userName) || (t1.userName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)) ||
(t1.userName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)) || (Collaborators.filter { t3 =>
(Collaborators.filter { t3 => t3.byRepository(t1.userName, t1.repositoryName) &&
t3.byRepository(t1.userName, t1.repositoryName) && ((t3.collaboratorName === x.userName.bind) ||
((t3.collaboratorName === x.userName.bind) ||
(t3.collaboratorName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName))) (t3.collaboratorName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)))
} exists)) } exists))
} }
.map { case (t1, t2) => t1 } .map { case (t1, t2) => t1 }
// for Guests // for Guests
@@ -483,17 +477,16 @@ trait RepositoryService {
.filter { case (t1, t2) => t1.isPrivate === false.bind && t2.removed === false.bind } .filter { case (t1, t2) => t1.isPrivate === false.bind && t2.removed === false.bind }
.map { case (t1, t2) => t1 } .map { case (t1, t2) => t1 }
}).filter { t => }).filter { t =>
repositoryUserName.map { userName => repositoryUserName.map { userName =>
t.userName === userName.bind t.userName === userName.bind
} getOrElse LiteralColumn(true) } getOrElse LiteralColumn(true)
} }.sortBy(_.lastActivityDate desc)
.sortBy(_.lastActivityDate desc)
.list .list
.map(createRepositoryInfo(_, withoutPhysicalInfo)) .map(createRepositoryInfo(_, withoutPhysicalInfo))
} }
private def createRepositoryInfo(repository: Repository, withoutPhysicalInfo: Boolean = false)( private def createRepositoryInfo(repository: Repository, withoutPhysicalInfo: Boolean = false)(implicit
implicit s: Session s: Session
): RepositoryInfo = { ): RepositoryInfo = {
new RepositoryInfo( new RepositoryInfo(
if (withoutPhysicalInfo) { if (withoutPhysicalInfo) {
@@ -586,8 +579,8 @@ trait RepositoryService {
) )
} }
def saveRepositoryDefaultBranch(userName: String, repositoryName: String, defaultBranch: String)( def saveRepositoryDefaultBranch(userName: String, repositoryName: String, defaultBranch: String)(implicit
implicit s: Session s: Session
): Unit = ): Unit =
Repositories Repositories
.filter(_.byRepository(userName, repositoryName)) .filter(_.byRepository(userName, repositoryName))
@@ -599,16 +592,16 @@ trait RepositoryService {
/** /**
* Add collaborator (user or group) to the repository. * Add collaborator (user or group) to the repository.
*/ */
def addCollaborator(userName: String, repositoryName: String, collaboratorName: String, role: String)( def addCollaborator(userName: String, repositoryName: String, collaboratorName: String, role: String)(implicit
implicit s: Session s: Session
): Unit = ): Unit =
Collaborators insert Collaborator(userName, repositoryName, collaboratorName, role) Collaborators insert Collaborator(userName, repositoryName, collaboratorName, role)
/** /**
* Remove specified collaborator from the repository. * Remove specified collaborator from the repository.
*/ */
def removeCollaborator(userName: String, repositoryName: String, collaboratorName: String)( def removeCollaborator(userName: String, repositoryName: String, collaboratorName: String)(implicit
implicit s: Session s: Session
): Unit = ): Unit =
Collaborators.filter(_.byPrimaryKey(userName, repositoryName, collaboratorName)).delete Collaborators.filter(_.byPrimaryKey(userName, repositoryName, collaboratorName)).delete
@@ -634,8 +627,8 @@ trait RepositoryService {
* Returns the list of all collaborator name and permission which is sorted with ascending order. * Returns the list of all collaborator name and permission which is sorted with ascending order.
* If a group is added as a collaborator, this method returns users who are belong to that group. * If a group is added as a collaborator, this method returns users who are belong to that group.
*/ */
def getCollaboratorUserNames(userName: String, repositoryName: String, filter: Seq[Role] = Nil)( def getCollaboratorUserNames(userName: String, repositoryName: String, filter: Seq[Role] = Nil)(implicit
implicit s: Session s: Session
): List[String] = { ): List[String] = {
val q1 = Collaborators val q1 = Collaborators
.join(Accounts) .join(Accounts)
@@ -669,8 +662,8 @@ trait RepositoryService {
} }
} }
def hasDeveloperRole(owner: String, repository: String, loginAccount: Option[Account])( def hasDeveloperRole(owner: String, repository: String, loginAccount: Option[Account])(implicit
implicit s: Session s: Session
): Boolean = { ): Boolean = {
loginAccount match { loginAccount match {
case Some(a) if (a.isAdmin) => true case Some(a) if (a.isAdmin) => true
@@ -731,7 +724,7 @@ trait RepositoryService {
(t.originUserName === userName.bind) && (t.originRepositoryName === repositoryName.bind) (t.originUserName === userName.bind) && (t.originRepositoryName === repositoryName.bind)
} }
.sortBy(_.userName asc) .sortBy(_.userName asc)
.list //.map(t => t.userName -> t.repositoryName).list .list // .map(t => t.userName -> t.repositoryName).list
private val templateExtensions = Seq("md", "markdown") private val templateExtensions = Seq("md", "markdown")

View File

@@ -25,8 +25,8 @@ trait RequestCache
private implicit def context2Session(implicit context: Context): Session = private implicit def context2Session(implicit context: Context): Session =
request2Session(context.request) request2Session(context.request)
def getIssueFromCache(userName: String, repositoryName: String, issueId: String)( def getIssueFromCache(userName: String, repositoryName: String, issueId: String)(implicit
implicit context: Context context: Context
): Option[Issue] = { ): Option[Issue] = {
context.cache(s"issue.${userName}/${repositoryName}#${issueId}") { context.cache(s"issue.${userName}/${repositoryName}#${issueId}") {
super.getIssue(userName, repositoryName, issueId) super.getIssue(userName, repositoryName, issueId)
@@ -45,19 +45,18 @@ trait RequestCache
} }
} }
def getRepositoryInfoFromCache(userName: String, repositoryName: String)( def getRepositoryInfoFromCache(userName: String, repositoryName: String)(implicit
implicit context: Context context: Context
): Option[Repository] = { ): Option[Repository] = {
context.cache(s"repository.${userName}/${repositoryName}") { context.cache(s"repository.${userName}/${repositoryName}") {
Repositories Repositories
.join(Accounts) .join(Accounts)
.on(_.userName === _.userName) .on(_.userName === _.userName)
.filter { .filter { case (t1, t2) =>
case (t1, t2) => t1.byRepository(userName, repositoryName) && t2.removed === false.bind
t1.byRepository(userName, repositoryName) && t2.removed === false.bind
} }
.map { .map { case (t1, t2) =>
case (t1, t2) => t1 t1
} }
.firstOption .firstOption
} }

View File

@@ -45,8 +45,8 @@ trait WebHookService {
private val logger = LoggerFactory.getLogger(classOf[WebHookService]) private val logger = LoggerFactory.getLogger(classOf[WebHookService])
/** get All WebHook informations of repository */ /** get All WebHook informations of repository */
def getWebHooks(owner: String, repository: String)( def getWebHooks(owner: String, repository: String)(implicit
implicit s: Session s: Session
): List[(RepositoryWebHook, Set[WebHook.Event])] = ): List[(RepositoryWebHook, Set[WebHook.Event])] =
RepositoryWebHooks RepositoryWebHooks
.filter(_.byRepository(owner, repository)) .filter(_.byRepository(owner, repository))
@@ -63,8 +63,8 @@ trait WebHookService {
.sortBy(_._1.url) .sortBy(_._1.url)
/** get All WebHook informations of repository event */ /** get All WebHook informations of repository event */
def getWebHooksByEvent(owner: String, repository: String, event: WebHook.Event)( def getWebHooksByEvent(owner: String, repository: String, event: WebHook.Event)(implicit
implicit s: Session s: Session
): List[RepositoryWebHook] = ): List[RepositoryWebHook] =
RepositoryWebHooks RepositoryWebHooks
.filter(_.byRepository(owner, repository)) .filter(_.byRepository(owner, repository))
@@ -78,8 +78,8 @@ trait WebHookService {
.distinct .distinct
/** get All WebHook information from repository to url */ /** get All WebHook information from repository to url */
def getWebHook(owner: String, repository: String, url: String)( def getWebHook(owner: String, repository: String, url: String)(implicit
implicit s: Session s: Session
): Option[(RepositoryWebHook, Set[WebHook.Event])] = ): Option[(RepositoryWebHook, Set[WebHook.Event])] =
RepositoryWebHooks RepositoryWebHooks
.filter(_.byRepositoryUrl(owner, repository, url)) .filter(_.byRepositoryUrl(owner, repository, url))
@@ -95,8 +95,8 @@ trait WebHookService {
.headOption .headOption
/** get All WebHook informations of repository */ /** get All WebHook informations of repository */
def getWebHookById(id: Int)( def getWebHookById(id: Int)(implicit
implicit s: Session s: Session
): Option[(RepositoryWebHook, Set[WebHook.Event])] = ): Option[(RepositoryWebHook, Set[WebHook.Event])] =
RepositoryWebHooks RepositoryWebHooks
.filter(_.byId(id)) .filter(_.byId(id))
@@ -451,8 +451,8 @@ trait WebHookPullRequestService extends WebHookService {
} }
/** @return Map[(issue, issueUser, pullRequest, baseOwner, headOwner), webHooks] */ /** @return Map[(issue, issueUser, pullRequest, baseOwner, headOwner), webHooks] */
def getPullRequestsByRequestForWebhook(userName: String, repositoryName: String, branch: String)( def getPullRequestsByRequestForWebhook(userName: String, repositoryName: String, branch: String)(implicit
implicit s: Session s: Session
): Map[(Issue, Account, PullRequest, Account, Account), List[RepositoryWebHook]] = ): Map[(Issue, Account, PullRequest, Account, Account), List[RepositoryWebHook]] =
(for { (for {
is <- Issues if is.closed === false.bind is <- Issues if is.closed === false.bind
@@ -652,8 +652,8 @@ object WebHookService {
) extends FieldSerializable ) extends FieldSerializable
with WebHookPayload { with WebHookPayload {
val compare = commits.size match { val compare = commits.size match {
case 0 => ApiPath(s"/${repository.full_name}") // maybe test hook on un-initialized repository case 0 => ApiPath(s"/${repository.full_name}") // maybe test hook on un-initialized repository
case 1 => ApiPath(s"/${repository.full_name}/commit/${after}") case 1 => ApiPath(s"/${repository.full_name}/commit/${after}")
case _ if before.forall(_ == '0') => ApiPath(s"/${repository.full_name}/compare/${commits.head.id}^...${after}") case _ if before.forall(_ == '0') => ApiPath(s"/${repository.full_name}/compare/${commits.head.id}^...${after}")
case _ => ApiPath(s"/${repository.full_name}/compare/${before}...${after}") case _ => ApiPath(s"/${repository.full_name}/compare/${before}...${after}")
} }
@@ -878,15 +878,14 @@ object WebHookService {
sender: Account sender: Account
): WebHookGollumPayload = { ): WebHookGollumPayload = {
WebHookGollumPayload( WebHookGollumPayload(
pages = pages.map { pages = pages.map { case (action, pageName, sha) =>
case (action, pageName, sha) => WebHookGollumPagePayload(
WebHookGollumPagePayload( action = action,
action = action, page_name = pageName,
page_name = pageName, title = pageName,
title = pageName, sha = sha,
sha = sha, html_url = ApiPath(s"/${RepositoryName(repository).fullName}/wiki/${StringUtil.urlDecode(pageName)}")
html_url = ApiPath(s"/${RepositoryName(repository).fullName}/wiki/${StringUtil.urlDecode(pageName)}") )
)
}, },
repository = ApiRepository(repository, repositoryUser), repository = ApiRepository(repository, repositoryUser),
sender = ApiUser(sender) sender = ApiUser(sender)

View File

@@ -20,8 +20,10 @@ abstract class ControllerFilter extends Filter {
requestPath + "/" requestPath + "/"
} }
if (!checkPath.startsWith("/upload/") && !checkPath.startsWith("/git/") && !checkPath.startsWith("/git-lfs/") && if (
!checkPath.startsWith("/assets/") && !checkPath.startsWith("/plugin-assets/")) { !checkPath.startsWith("/upload/") && !checkPath.startsWith("/git/") && !checkPath.startsWith("/git-lfs/") &&
!checkPath.startsWith("/assets/") && !checkPath.startsWith("/plugin-assets/")
) {
val continue = process(request, response, checkPath) val continue = process(request, response, checkPath)
if (!continue) { if (!continue) {
return () return ()
@@ -41,33 +43,29 @@ class CompositeScalatraFilter extends ControllerFilter {
} }
override def init(filterConfig: FilterConfig): Unit = { override def init(filterConfig: FilterConfig): Unit = {
filters.foreach { filters.foreach { case (filter, _) =>
case (filter, _) => filter.init(filterConfig)
filter.init(filterConfig)
} }
} }
override def destroy(): Unit = { override def destroy(): Unit = {
filters.foreach { filters.foreach { case (filter, _) =>
case (filter, _) => filter.destroy()
filter.destroy()
} }
} }
override def process(request: ServletRequest, response: ServletResponse, checkPath: String): Boolean = { override def process(request: ServletRequest, response: ServletResponse, checkPath: String): Boolean = {
filters filters
.filter { .filter { case (_, path) =>
case (_, path) => val start = path.replaceFirst("/\\*$", "/")
val start = path.replaceFirst("/\\*$", "/") checkPath.startsWith(start)
checkPath.startsWith(start)
} }
.foreach { .foreach { case (filter, _) =>
case (filter, _) => val mockChain = new MockFilterChain()
val mockChain = new MockFilterChain() filter.doFilter(request, response, mockChain)
filter.doFilter(request, response, mockChain) if (mockChain.continue == false) {
if (mockChain.continue == false) { return false
return false }
}
} }
true true

View File

@@ -43,10 +43,9 @@ class GitAuthenticationFilter extends Filter with RepositoryService with Account
try { try {
PluginRegistry() PluginRegistry()
.getRepositoryRouting(request.gitRepositoryPath) .getRepositoryRouting(request.gitRepositoryPath)
.map { .map { case GitRepositoryRouting(_, _, filter) =>
case GitRepositoryRouting(_, _, filter) => // served by plug-ins
// served by plug-ins pluginRepository(request, wrappedResponse, chain, settings, isUpdating, filter)
pluginRepository(request, wrappedResponse, chain, settings, isUpdating, filter)
} }
.getOrElse { .getOrElse {
@@ -130,19 +129,17 @@ class GitAuthenticationFilter extends Filter with RepositoryService with Account
} }
} }
case None => case None =>
() => () => {
{ logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.")
logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.") response.sendError(HttpServletResponse.SC_NOT_FOUND)
response.sendError(HttpServletResponse.SC_NOT_FOUND) }
}
} }
} }
case _ => case _ =>
() => () => {
{ logger.debug(s"Not enough path arguments: ${request.paths.mkString(", ")}")
logger.debug(s"Not enough path arguments: ${request.paths.mkString(", ")}") response.sendError(HttpServletResponse.SC_NOT_FOUND)
response.sendError(HttpServletResponse.SC_NOT_FOUND) }
}
} }
action() action()
@@ -159,8 +156,8 @@ class GitAuthenticationFilter extends Filter with RepositoryService with Account
* @param s database session * @param s database session
* @return an account or none * @return an account or none
*/ */
private def authenticateByHeader(authorizationHeader: String, settings: SystemSettings)( private def authenticateByHeader(authorizationHeader: String, settings: SystemSettings)(implicit
implicit s: Session s: Session
): Option[Account] = { ): Option[Account] = {
val Array(username, password) = AuthUtil.decodeAuthHeader(authorizationHeader).split(":", 2) val Array(username, password) = AuthUtil.decodeAuthHeader(authorizationHeader).split(":", 2)
authenticate(settings, username, password).orElse { authenticate(settings, username, password).orElse {

View File

@@ -171,10 +171,9 @@ class GitBucketRepositoryResolver extends RepositoryResolver[HttpServletRequest]
// Rewrite repository path if routing is marched // Rewrite repository path if routing is marched
PluginRegistry() PluginRegistry()
.getRepositoryRouting("/" + name) .getRepositoryRouting("/" + name)
.map { .map { case GitRepositoryRouting(urlPattern, localPath, _) =>
case GitRepositoryRouting(urlPattern, localPath, _) => val path = urlPattern.r.replaceFirstIn(name, localPath)
val path = urlPattern.r.replaceFirstIn(name, localPath) new FileRepository(new File(Directory.GitBucketHome, path))
new FileRepository(new File(Directory.GitBucketHome, path))
} }
.getOrElse { .getOrElse {
new FileRepository(new File(Directory.RepositoryHome, name)) new FileRepository(new File(Directory.RepositoryHome, name))
@@ -341,7 +340,11 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
pushedIds.add(commit.id) pushedIds.add(commit.id)
createIssueComment(owner, repository, commit) createIssueComment(owner, repository, commit)
// close issues // close issues
if (refName(1) == "heads" && branchName == defaultBranch && command.getType == ReceiveCommand.Type.UPDATE) { if (
refName(
1
) == "heads" && branchName == defaultBranch && command.getType == ReceiveCommand.Type.UPDATE
) {
getAccountByUserName(pusher).foreach { pusherAccount => getAccountByUserName(pusher).foreach { pusherAccount =>
closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository).foreach { issueId => closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository).foreach { issueId =>
getIssue(owner, repository, issueId.toString).foreach { issue => getIssue(owner, repository, issueId.toString).foreach { issue =>
@@ -362,9 +365,11 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
// set PR as merged // set PR as merged
val pulls = getPullRequestsByBranch(owner, repository, branchName, Some(false)) val pulls = getPullRequestsByBranch(owner, repository, branchName, Some(false))
pulls.foreach { pull => pulls.foreach { pull =>
if (commits.exists { c => if (
c.id == pull.commitIdTo commits.exists { c =>
}) { c.id == pull.commitIdTo
}
) {
markMergeAndClosePullRequest(pusher, owner, repository, pull) markMergeAndClosePullRequest(pusher, owner, repository, pull)
getAccountByUserName(pusher).foreach { pusherAccount => getAccountByUserName(pusher).foreach { pusherAccount =>
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount, settings) callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount, settings)
@@ -490,40 +495,38 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
} }
} }
commitIds.foreach { commitIds.foreach { case (oldCommitId, newCommitId) =>
case (oldCommitId, newCommitId) => val commits = Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
val commits = Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git => JGitUtil.getCommitLog(git, oldCommitId, newCommitId).flatMap { commit =>
JGitUtil.getCommitLog(git, oldCommitId, newCommitId).flatMap { commit => val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false) diffs.collect {
diffs.collect { case diff if diff.newPath.toLowerCase.endsWith(".md") =>
case diff if diff.newPath.toLowerCase.endsWith(".md") => val action = mapToAction(diff.changeType)
val action = mapToAction(diff.changeType) val fileName = diff.newPath
val fileName = diff.newPath updateLastActivityDate(owner, repository)
updateLastActivityDate(owner, repository) buildWikiRecord(action, owner, repository, commit, fileName).foreach(recordActivity)
buildWikiRecord(action, owner, repository, commit, fileName).foreach(recordActivity) (action, fileName, commit.id)
(action, fileName, commit.id)
}
} }
} }
}
val pages = commits val pages = commits
.groupBy { case (_, fileName, _) => fileName } .groupBy { case (_, fileName, _) => fileName }
.map { .map { case (fileName, commits) =>
case (fileName, commits) => val (commitHeadAction, _, _) = commits.head
val (commitHeadAction, _, _) = commits.head val (_, _, commitLastId) = commits.last
val (_, _, commitLastId) = commits.last (commitHeadAction, fileName, commitLastId)
(commitHeadAction, fileName, commitLastId)
}
callWebHookOf(owner, repository, WebHook.Gollum, settings) {
for {
pusherAccount <- getAccountByUserName(pusher)
repositoryUser <- getAccountByUserName(owner)
repositoryInfo <- getRepository(owner, repository)
} yield {
WebHookGollumPayload(pages.toSeq, repositoryInfo, repositoryUser, pusherAccount)
}
} }
callWebHookOf(owner, repository, WebHook.Gollum, settings) {
for {
pusherAccount <- getAccountByUserName(pusher)
repositoryUser <- getAccountByUserName(owner)
repositoryInfo <- getRepository(owner, repository)
} yield {
WebHookGollumPayload(pages.toSeq, repositoryInfo, repositoryUser, pusherAccount)
}
}
} }
} }
} catch { } catch {

View File

@@ -153,8 +153,8 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
logger.info(s"Extract to ${file.getAbsolutePath}") logger.info(s"Extract to ${file.getAbsolutePath}")
FileUtils.forceMkdirParent(file) FileUtils.forceMkdirParent(file)
Using.resources(in, new FileOutputStream(file)) { Using.resources(in, new FileOutputStream(file)) { case (in, out) =>
case (in, out) => IOUtils.copy(in, out) IOUtils.copy(in, out)
} }
} }
case _ => () case _ => ()

View File

@@ -16,32 +16,31 @@ class PluginAssetsServlet extends HttpServlet {
assetsMappings assetsMappings
.find { case (prefix, _, _) => path.startsWith("/plugin-assets" + prefix) } .find { case (prefix, _, _) => path.startsWith("/plugin-assets" + prefix) }
.foreach { .foreach { case (prefix, resourcePath, classLoader) =>
case (prefix, resourcePath, classLoader) => val ifNoneMatch = req.getHeader("If-None-Match")
val ifNoneMatch = req.getHeader("If-None-Match") PluginRegistry.getPluginInfoFromClassLoader(classLoader).map { info =>
PluginRegistry.getPluginInfoFromClassLoader(classLoader).map { info => val etag = s""""${info.pluginJar.lastModified}"""" // ETag must wrapped with double quote
val etag = s""""${info.pluginJar.lastModified}"""" // ETag must wrapped with double quote if (ifNoneMatch == etag) {
if (ifNoneMatch == etag) { resp.setStatus(304)
resp.setStatus(304) } else {
} else { val resourceName = path.substring(("/plugin-assets" + prefix).length)
val resourceName = path.substring(("/plugin-assets" + prefix).length) Option(classLoader.getResourceAsStream(resourcePath.stripPrefix("/") + resourceName))
Option(classLoader.getResourceAsStream(resourcePath.stripPrefix("/") + resourceName)) .map { in =>
.map { in => try {
try { val bytes = IOUtils.toByteArray(in)
val bytes = IOUtils.toByteArray(in) resp.setContentLength(bytes.length)
resp.setContentLength(bytes.length) resp.setContentType(FileUtil.getMimeType(path, bytes))
resp.setContentType(FileUtil.getMimeType(path, bytes)) resp.setHeader("ETag", etag)
resp.setHeader("ETag", etag) resp.getOutputStream.write(bytes)
resp.getOutputStream.write(bytes) } finally {
} finally { in.close()
in.close()
}
} }
.getOrElse { }
resp.setStatus(404) .getOrElse {
} resp.setStatus(404)
} }
} }
}
} }
} }

View File

@@ -14,32 +14,29 @@ class PluginControllerFilter extends ControllerFilter {
} }
override def destroy(): Unit = { override def destroy(): Unit = {
PluginRegistry().getControllers().foreach { PluginRegistry().getControllers().foreach { case (controller, _) =>
case (controller, _) => controller.destroy()
controller.destroy()
} }
} }
override def process(request: ServletRequest, response: ServletResponse, checkPath: String): Boolean = { override def process(request: ServletRequest, response: ServletResponse, checkPath: String): Boolean = {
PluginRegistry() PluginRegistry()
.getControllers() .getControllers()
.filter { .filter { case (_, path) =>
case (_, path) => val start = path.replaceFirst("/\\*$", "/")
val start = path.replaceFirst("/\\*$", "/") checkPath.startsWith(start)
checkPath.startsWith(start)
} }
.foreach { .foreach { case (controller, _) =>
case (controller, _) => controller match {
controller match { case x: ControllerBase if (x.config == null) => x.init(filterConfig)
case x: ControllerBase if (x.config == null) => x.init(filterConfig) case _ => ()
case _ => () }
} val mockChain = new MockFilterChain()
val mockChain = new MockFilterChain() controller.doFilter(request, response, mockChain)
controller.doFilter(request, response, mockChain)
if (mockChain.continue == false) { if (mockChain.continue == false) {
return false return false
} }
} }
true true

View File

@@ -101,8 +101,8 @@ abstract class DefaultGitCommand(val owner: String, val repoName: String) extend
} }
} }
protected def isReadableUser(authType: AuthType, repositoryInfo: RepositoryService.RepositoryInfo)( protected def isReadableUser(authType: AuthType, repositoryInfo: RepositoryService.RepositoryInfo)(implicit
implicit session: Session session: Session
): Boolean = { ): Boolean = {
authType match { authType match {
case AuthType.UserAuthType(username) => { case AuthType.UserAuthType(username) => {
@@ -120,8 +120,8 @@ abstract class DefaultGitCommand(val owner: String, val repoName: String) extend
} }
} }
protected def isWritableUser(authType: AuthType, repositoryInfo: RepositoryService.RepositoryInfo)( protected def isWritableUser(authType: AuthType, repositoryInfo: RepositoryService.RepositoryInfo)(implicit
implicit session: Session session: Session
): Boolean = { ): Boolean = {
authType match { authType match {
case AuthType.UserAuthType(username) => { case AuthType.UserAuthType(username) => {

View File

@@ -56,8 +56,8 @@ class PublicKeyAuthenticator(genericUser: String)
} }
} }
private def authenticateLoginUser(userName: String, key: PublicKey, session: ServerSession)( private def authenticateLoginUser(userName: String, key: PublicKey, session: ServerSession)(implicit
implicit s: Session s: Session
): Boolean = { ): Boolean = {
val authenticated = getPublicKeys(userName).map(_.publicKey).flatMap(SshUtil.str2PublicKey).contains(key) val authenticated = getPublicKeys(userName).map(_.publicKey).flatMap(SshUtil.str2PublicKey).contains(key)

View File

@@ -36,7 +36,7 @@ object DatabaseConfig {
ConfigFactory.parseFile(file) ConfigFactory.parseFile(file)
} }
private lazy val dbUrl = getValue("db.url", config.getString) //config.getString("db.url") private lazy val dbUrl = getValue("db.url", config.getString) // config.getString("db.url")
def url(directory: Option[String]): String = { def url(directory: Option[String]): String = {
val sb = new StringBuilder() val sb = new StringBuilder()

View File

@@ -18,7 +18,7 @@ object Directory {
case None => { case None => {
val oldHome = new File(System.getProperty("user.home"), "gitbucket") val oldHome = new File(System.getProperty("user.home"), "gitbucket")
if (oldHome.exists && oldHome.isDirectory && new File(oldHome, "version").exists) { if (oldHome.exists && oldHome.isDirectory && new File(oldHome, "version").exists) {
//FileUtils.moveDirectory(oldHome, newHome) // FileUtils.moveDirectory(oldHome, newHome)
oldHome oldHome
} else { } else {
new File(System.getProperty("user.home"), ".gitbucket") new File(System.getProperty("user.home"), ".gitbucket")

View File

@@ -12,9 +12,8 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider
object GpgUtil { object GpgUtil {
def str2GpgKeyId(keyStr: String): Option[Long] = { def str2GpgKeyId(keyStr: String): Option[Long] = {
val pubKeyOf = new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(keyStr.getBytes))) val pubKeyOf = new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(keyStr.getBytes)))
pubKeyOf.iterator().asScala.collectFirst { pubKeyOf.iterator().asScala.collectFirst { case keyRing: PGPPublicKeyRing =>
case keyRing: PGPPublicKeyRing => keyRing.getPublicKey().getKeyID
keyRing.getPublicKey().getKeyID
} }
} }
@@ -37,23 +36,22 @@ object GpgUtil {
new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(signInfo.signArmored))) new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(signInfo.signArmored)))
.iterator() .iterator()
.asScala .asScala
.flatMap { .flatMap { case signList: PGPSignatureList =>
case signList: PGPSignatureList => signList
signList .iterator()
.iterator() .asScala
.asScala .flatMap { sign =>
.flatMap { sign => getGpgKey(sign.getKeyID)
getGpgKey(sign.getKeyID) .map { pubKey =>
.map { pubKey => sign.init(new BcPGPContentVerifierBuilderProvider, pubKey)
sign.init(new BcPGPContentVerifierBuilderProvider, pubKey) sign.update(signInfo.target)
sign.update(signInfo.target) (sign, pubKey)
(sign, pubKey) }
} .collect {
.collect { case (sign, pubKey) if sign.verify() =>
case (sign, pubKey) if sign.verify() => JGitUtil.GpgVerifyInfo(pubKey.getUserIDs.next, pubKey.getKeyID.toHexString.toUpperCase)
JGitUtil.GpgVerifyInfo(pubKey.getUserIDs.next, pubKey.getKeyID.toHexString.toUpperCase) }
} }
}
} }
.toList .toList
.headOption .headOption

View File

@@ -54,12 +54,11 @@ object JDBCUtil {
private def execute[T](sql: String, params: Any*)(f: (PreparedStatement) => T): T = { private def execute[T](sql: String, params: Any*)(f: (PreparedStatement) => T): T = {
Using.resource(conn.prepareStatement(sql)) { stmt => Using.resource(conn.prepareStatement(sql)) { stmt =>
params.zipWithIndex.foreach { params.zipWithIndex.foreach { case (p, i) =>
case (p, i) => p match {
p match { case x: Int => stmt.setInt(i + 1, x)
case x: Int => stmt.setInt(i + 1, x) case x: String => stmt.setString(i + 1, x)
case x: String => stmt.setString(i + 1, x) }
}
} }
f(stmt) f(stmt)
} }
@@ -136,19 +135,18 @@ object JDBCUtil {
sb.append(columns.map(_._1).mkString(", ")) sb.append(columns.map(_._1).mkString(", "))
sb.append(") VALUES (") sb.append(") VALUES (")
val values = columns.map { val values = columns.map { case (columnName, columnType) =>
case (columnName, columnType) => if (rs.getObject(columnName) == null) {
if (rs.getObject(columnName) == null) { null
null } else {
} else { columnType match {
columnType match { case Types.BOOLEAN | Types.BIT => rs.getBoolean(columnName)
case Types.BOOLEAN | Types.BIT => rs.getBoolean(columnName) case Types.VARCHAR | Types.CLOB | Types.CHAR | Types.LONGVARCHAR => rs.getString(columnName)
case Types.VARCHAR | Types.CLOB | Types.CHAR | Types.LONGVARCHAR => rs.getString(columnName) case Types.INTEGER => rs.getInt(columnName)
case Types.INTEGER => rs.getInt(columnName) case Types.BIGINT => rs.getLong(columnName)
case Types.BIGINT => rs.getLong(columnName) case Types.TIMESTAMP => rs.getTimestamp(columnName)
case Types.TIMESTAMP => rs.getTimestamp(columnName)
}
} }
}
} }
val columnValues = values.map { val columnValues = values.map {

View File

@@ -454,27 +454,26 @@ object JGitUtil {
def appendLastCommits( def appendLastCommits(
fileList: List[(ObjectId, FileMode, String, String, Option[String])] fileList: List[(ObjectId, FileMode, String, String, Option[String])]
): List[(ObjectId, FileMode, String, String, Option[String], Option[RevCommit])] = { ): List[(ObjectId, FileMode, String, String, Option[String], Option[RevCommit])] = {
fileList.map { fileList.map { case (id, mode, name, path, opt) =>
case (id, mode, name, path, opt) => if (maxFiles > 0 && fileList.size >= maxFiles) {
if (maxFiles > 0 && fileList.size >= maxFiles) { // Don't attempt to get the last commit if the number of files is very large.
// Don't attempt to get the last commit if the number of files is very large. (id, mode, name, path, opt, None)
(id, mode, name, path, opt, None) } else if (commitCount < 10000) {
} else if (commitCount < 10000) { (id, mode, name, path, opt, Some(getCommit(path)))
(id, mode, name, path, opt, Some(getCommit(path))) } else if (isCacheEnabled()) {
} else if (isCacheEnabled()) { // Use in-memory cache if the commit count is too big.
// Use in-memory cache if the commit count is too big. val cached = objectCommitCache.getEntry(id)
val cached = objectCommitCache.getEntry(id) if (cached == null) {
if (cached == null) {
val commit = getCommit(path)
objectCommitCache.put(id, commit)
(id, mode, name, path, opt, Some(commit))
} else {
(id, mode, name, path, opt, Some(cached.getValue))
}
} else {
val commit = getCommit(path) val commit = getCommit(path)
objectCommitCache.put(id, commit)
(id, mode, name, path, opt, Some(commit)) (id, mode, name, path, opt, Some(commit))
} else {
(id, mode, name, path, opt, Some(cached.getValue))
} }
} else {
val commit = getCommit(path)
(id, mode, name, path, opt, Some(commit))
}
} }
} }
@@ -503,23 +502,22 @@ object JGitUtil {
appendLastCommits(fileList) appendLastCommits(fileList)
.map(simplifyPath) .map(simplifyPath)
.map { .map { case (objectId, fileMode, name, path, linkUrl, commit) =>
case (objectId, fileMode, name, path, linkUrl, commit) => FileInfo(
FileInfo( objectId,
objectId, fileMode == FileMode.TREE || fileMode == FileMode.GITLINK,
fileMode == FileMode.TREE || fileMode == FileMode.GITLINK, name,
name, path,
path, getSummaryMessage(
getSummaryMessage( commit.map(_.getFullMessage).getOrElse(""),
commit.map(_.getFullMessage).getOrElse(""), commit.map(_.getShortMessage).getOrElse("")
commit.map(_.getShortMessage).getOrElse("") ),
), commit.map(_.getName).getOrElse(""),
commit.map(_.getName).getOrElse(""), commit.map(_.getAuthorIdent.getWhen).orNull,
commit.map(_.getAuthorIdent.getWhen).orNull, commit.map(_.getAuthorIdent.getName).getOrElse(""),
commit.map(_.getAuthorIdent.getName).getOrElse(""), commit.map(_.getAuthorIdent.getEmailAddress).getOrElse(""),
commit.map(_.getAuthorIdent.getEmailAddress).getOrElse(""), linkUrl
linkUrl )
)
} }
.sortWith { (file1, file2) => .sortWith { (file1, file2) =>
(file1.isDirectory, file2.isDirectory) match { (file1.isDirectory, file2.isDirectory) match {
@@ -714,10 +712,9 @@ object JGitUtil {
toCommit.getParentCount match { toCommit.getParentCount match {
case 0 => case 0 =>
df.scan( df.scan(
new EmptyTreeIterator(), new EmptyTreeIterator(),
new CanonicalTreeParser(null, git.getRepository.newObjectReader(), toCommit.getTree) new CanonicalTreeParser(null, git.getRepository.newObjectReader(), toCommit.getTree)
) ).asScala
.asScala
case _ => df.scan(toCommit.getParent(0), toCommit.getTree).asScala case _ => df.scan(toCommit.getParent(0), toCommit.getTree).asScala
} }
case Some(from) => case Some(from) =>
@@ -788,8 +785,10 @@ object JGitUtil {
} else { } else {
val oldIsImage = FileUtil.isImage(diff.getOldPath) val oldIsImage = FileUtil.isImage(diff.getOldPath)
val newIsImage = FileUtil.isImage(diff.getNewPath) val newIsImage = FileUtil.isImage(diff.getNewPath)
val patch = if (oldIsImage || newIsImage) None else Some(makePatchFromDiffEntry(git, diff)) // TODO use DiffFormatter val patch =
val tooLarge = patch.exists(_.count(_ == '\n') > 1000) // Don't show diff if the file has more than 1000 lines diff if (oldIsImage || newIsImage) None else Some(makePatchFromDiffEntry(git, diff)) // TODO use DiffFormatter
val tooLarge =
patch.exists(_.count(_ == '\n') > 1000) // Don't show diff if the file has more than 1000 lines diff
val includeContent = tooLarge || !fetchContent || oldIsImage || newIsImage val includeContent = tooLarge || !fetchContent || oldIsImage || newIsImage
DiffInfo( DiffInfo(
changeType = diff.getChangeType, changeType = diff.getChangeType,
@@ -921,10 +920,9 @@ object JGitUtil {
Some(if (revstr.isEmpty) repository.repository.defaultBranch else revstr), Some(if (revstr.isEmpty) repository.repository.defaultBranch else revstr),
repository.branchList.headOption repository.branchList.headOption
).flatMap { ).flatMap {
case Some(rev) => Some((git.getRepository.resolve(rev), rev)) case Some(rev) => Some((git.getRepository.resolve(rev), rev))
case None => None case None => None
} }.find(_._1 != null)
.find(_._1 != null)
} }
def createTag(git: Git, name: String, message: Option[String], commitId: String) = { def createTag(git: Git, name: String, message: Option[String], commitId: String) = {
@@ -1301,7 +1299,8 @@ object JGitUtil {
c.getAuthorIdent.getWhen, c.getAuthorIdent.getWhen,
Option(git.log.add(c).addPath(blame.getSourcePath(i)).setSkip(1).setMaxCount(2).call.iterator.next) Option(git.log.add(c).addPath(blame.getSourcePath(i)).setSkip(1).setMaxCount(2).call.iterator.next)
.map(_.name), .map(_.name),
if (blame.getSourcePath(i) == path) { None } else { Some(blame.getSourcePath(i)) }, if (blame.getSourcePath(i) == path) { None }
else { Some(blame.getSourcePath(i)) },
c.getCommitterIdent.getWhen, c.getCommitterIdent.getWhen,
c.getShortMessage, c.getShortMessage,
Set.empty Set.empty

View File

@@ -128,13 +128,14 @@ object LDAPUtil {
val cachedInstance = provider.get() val cachedInstance = provider.get()
if (cachedInstance == null) { if (cachedInstance == null) {
val cls = try { val cls =
Class.forName("com.sun.net.ssl.internal.ssl.Provider") try {
} catch { Class.forName("com.sun.net.ssl.internal.ssl.Provider")
case e: ClassNotFoundException => } catch {
Class.forName("com.ibm.jsse.IBMJSSEProvider") case e: ClassNotFoundException =>
case e: Throwable => throw e Class.forName("com.ibm.jsse.IBMJSSEProvider")
} case e: Throwable => throw e
}
val newInstance = cls val newInstance = cls
.getDeclaredConstructor() .getDeclaredConstructor()
.newInstance() .newInstance()
@@ -234,8 +235,8 @@ object LDAPUtil {
case x => "(&(" + x + ")(" + userNameAttribute + "=" + userName + "))" case x => "(&(" + x + ")(" + userNameAttribute + "=" + userName + "))"
} }
getEntries(conn.search(baseDN, LDAPConnection.SCOPE_SUB, filterCond, null, false)).collectFirst { getEntries(conn.search(baseDN, LDAPConnection.SCOPE_SUB, filterCond, null, false)).collectFirst { case x =>
case x => x.getDN x.getDN
} }
} }

View File

@@ -62,9 +62,8 @@ class Mailer(settings: SystemSettings) {
smtp.fromAddress smtp.fromAddress
.map(_ -> smtp.fromName.getOrElse(loginAccount.map(_.userName).getOrElse("GitBucket"))) .map(_ -> smtp.fromName.getOrElse(loginAccount.map(_.userName).getOrElse("GitBucket")))
.orElse(Some("notifications@gitbucket.com" -> loginAccount.map(_.userName).getOrElse("GitBucket"))) .orElse(Some("notifications@gitbucket.com" -> loginAccount.map(_.userName).getOrElse("GitBucket")))
.foreach { .foreach { case (address, name) =>
case (address, name) => email.setFrom(address, name)
email.setFrom(address, name)
} }
email.setCharset("UTF-8") email.setCharset("UTF-8")
email.setSubject(subject) email.setSubject(subject)

View File

@@ -9,7 +9,7 @@ object RepositoryName {
def apply(fullName: String): RepositoryName = { def apply(fullName: String): RepositoryName = {
fullName.split("/").toList match { fullName.split("/").toList match {
case owner :: name :: Nil => RepositoryName(owner, name) case owner :: name :: Nil => RepositoryName(owner, name)
case _ => throw new IllegalArgumentException(s"${fullName} is not repositoryName (only 'owner/name')") case _ => throw new IllegalArgumentException(s"${fullName} is not repositoryName (only 'owner/name')")
} }
} }
def apply(repository: gitbucket.core.model.Repository): RepositoryName = def apply(repository: gitbucket.core.model.Repository): RepositoryName =

View File

@@ -19,7 +19,9 @@ trait AvatarImageProvider { self: RequestCache =>
// by user name // by user name
getAccountByUserNameFromCache(userName).map { account => getAccountByUserNameFromCache(userName).map { account =>
if (account.image.isEmpty && context.settings.basicBehavior.gravatar) { if (account.image.isEmpty && context.settings.basicBehavior.gravatar) {
s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g""" s"""https://www.gravatar.com/avatar/${StringUtil.md5(
account.mailAddress.toLowerCase
)}?s=${size}&d=retro&r=g"""
} else { } else {
s"""${context.path}/${account.userName}/_avatar?${helpers.hashDate(account.updatedDate)}""" s"""${context.path}/${account.userName}/_avatar?${helpers.hashDate(account.updatedDate)}"""
} }
@@ -30,7 +32,9 @@ trait AvatarImageProvider { self: RequestCache =>
// by mail address // by mail address
getAccountByMailAddressFromCache(mailAddress).map { account => getAccountByMailAddressFromCache(mailAddress).map { account =>
if (account.image.isEmpty && context.settings.basicBehavior.gravatar) { if (account.image.isEmpty && context.settings.basicBehavior.gravatar) {
s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g""" s"""https://www.gravatar.com/avatar/${StringUtil.md5(
account.mailAddress.toLowerCase
)}?s=${size}&d=retro&r=g"""
} else { } else {
s"""${context.path}/${account.userName}/_avatar?${helpers.hashDate(account.updatedDate)}""" s"""${context.path}/${account.userName}/_avatar?${helpers.hashDate(account.updatedDate)}"""
} }
@@ -45,13 +49,15 @@ trait AvatarImageProvider { self: RequestCache =>
if (tooltip) { if (tooltip) {
Html( Html(
s"""<img src="${src}" class="${if (size > 20) { "avatar" } else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;" s"""<img src="${src}" class="${if (size > 20) { "avatar" }
else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
| alt="@${StringUtil.escapeHtml(userName)}" | alt="@${StringUtil.escapeHtml(userName)}"
| data-toggle="tooltip" title="${StringUtil.escapeHtml(userName)}" />""".stripMargin | data-toggle="tooltip" title="${StringUtil.escapeHtml(userName)}" />""".stripMargin
) )
} else { } else {
Html( Html(
s"""<img src="${src}" class="${if (size > 20) { "avatar" } else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;" s"""<img src="${src}" class="${if (size > 20) { "avatar" }
else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
| alt="@${StringUtil.escapeHtml(userName)}" />""".stripMargin | alt="@${StringUtil.escapeHtml(userName)}" />""".stripMargin
) )
} }

View File

@@ -10,13 +10,14 @@ trait LinkConverter { self: RequestCache =>
/** /**
* Creates a link to the issue or the pull request from the issue id. * Creates a link to the issue or the pull request from the issue id.
*/ */
protected def createIssueLink(owner: String, repository: String, issueId: Int, title: String)( protected def createIssueLink(owner: String, repository: String, issueId: Int, title: String)(implicit
implicit context: Context context: Context
): String = { ): String = {
getIssueFromCache(owner, repository, issueId.toString) match { getIssueFromCache(owner, repository, issueId.toString) match {
case Some(issue) => case Some(issue) =>
s"""<a href="${context.path}/${owner}/${repository}/${if (issue.isPullRequest) "pull" else "issues"}/${issueId}"><strong>${StringUtil s"""<a href="${context.path}/${owner}/${repository}/${if (issue.isPullRequest) "pull"
.escapeHtml(title)}</strong> #${issueId}</a>""" else "issues"}/${issueId}"><strong>${StringUtil
.escapeHtml(title)}</strong> #${issueId}</a>"""
case None => case None =>
s"Unknown #${issueId}" s"Unknown #${issueId}"
} }
@@ -25,13 +26,14 @@ trait LinkConverter { self: RequestCache =>
/** /**
* Creates a global link to the issue or the pull request from the issue id. * Creates a global link to the issue or the pull request from the issue id.
*/ */
protected def createGlobalIssueLink(owner: String, repository: String, issueId: Int, title: String)( protected def createGlobalIssueLink(owner: String, repository: String, issueId: Int, title: String)(implicit
implicit context: Context context: Context
): String = { ): String = {
getIssueFromCache(owner, repository, issueId.toString) match { getIssueFromCache(owner, repository, issueId.toString) match {
case Some(issue) => case Some(issue) =>
s"""<a href="${context.path}/${owner}/${repository}/${if (issue.isPullRequest) "pull" else "issues"}/${issueId}"><strong>${StringUtil s"""<a href="${context.path}/${owner}/${repository}/${if (issue.isPullRequest) "pull"
.escapeHtml(title)}</strong> ${owner}/${repository}#${issueId}</a>""" else "issues"}/${issueId}"><strong>${StringUtil
.escapeHtml(title)}</strong> ${owner}/${repository}#${issueId}</a>"""
case None => case None =>
s"Unknown ${owner}/${repository}#${issueId}" s"Unknown ${owner}/${repository}#${issueId}"
} }
@@ -53,12 +55,12 @@ trait LinkConverter { self: RequestCache =>
else text else text
escaped escaped
// convert username/project@SHA to link // convert username/project@SHA to link
.replaceBy("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)/([a-zA-Z0-9\\-_\\.]+)@([a-f0-9]{40})(?=(\\W|$))".r) { m => .replaceBy("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)/([a-zA-Z0-9\\-_\\.]+)@([a-f0-9]{40})(?=(\\W|$))".r) { m =>
getAccountByUserNameFromCache(m.group(2)).map { _ => getAccountByUserNameFromCache(m.group(2)).map { _ =>
s"""<code><a href="${context.path}/${m.group(2)}/${m.group(3)}/commit/${m.group(4)}">${m.group(2)}/${m.group( s"""<code><a href="${context.path}/${m.group(2)}/${m.group(3)}/commit/${m.group(4)}">${m.group(2)}/${m.group(
3 3
)}@${m.group(4).substring(0, 7)}</a></code>""" )}@${m.group(4).substring(0, 7)}</a></code>"""
} }
} }
@@ -68,13 +70,13 @@ trait LinkConverter { self: RequestCache =>
getIssueFromCache(m.group(2), m.group(3), m.group(4)) match { getIssueFromCache(m.group(2), m.group(3), m.group(4)) match {
case Some(pull) if (pull.isPullRequest) => case Some(pull) if (pull.isPullRequest) =>
Some(s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/pull/${m Some(s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/pull/${m
.group(4)}" title="${pull.title}">${m.group(2)}/${m.group( .group(4)}" title="${pull.title}">${m.group(2)}/${m.group(
3 3
)}#${m.group(4)}</a>""") )}#${m.group(4)}</a>""")
case Some(issue) => case Some(issue) =>
Some(s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/issues/${m Some(s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/issues/${m
.group(4)}" title="${issue.title}">${m.group(2)}/${m .group(4)}" title="${issue.title}">${m.group(2)}/${m
.group(3)}#${m.group(4)}</a>""") .group(3)}#${m.group(4)}</a>""")
case None => case None =>
Some(s"""${m.group(2)}/${m.group(3)}#${m.group(4)}""") Some(s"""${m.group(2)}/${m.group(3)}#${m.group(4)}""")
} }
@@ -84,8 +86,8 @@ trait LinkConverter { self: RequestCache =>
.replaceBy(("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)@([a-f0-9]{40})(?=(\\W|$))").r) { m => .replaceBy(("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)@([a-f0-9]{40})(?=(\\W|$))").r) { m =>
getAccountByUserNameFromCache(m.group(2)).map { _ => getAccountByUserNameFromCache(m.group(2)).map { _ =>
s"""<code><a href="${context.path}/${m.group(2)}/${repository.name}/commit/${m.group(3)}">${m.group(2)}@${m s"""<code><a href="${context.path}/${m.group(2)}/${repository.name}/commit/${m.group(3)}">${m.group(2)}@${m
.group(3) .group(3)
.substring(0, 7)}</a></code>""" .substring(0, 7)}</a></code>"""
} }
} }
@@ -94,10 +96,10 @@ trait LinkConverter { self: RequestCache =>
getIssueFromCache(m.group(2), repository.name, m.group(3)) match { getIssueFromCache(m.group(2), repository.name, m.group(3)) match {
case Some(issue) if (issue.isPullRequest) => case Some(issue) if (issue.isPullRequest) =>
Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/pull/${m.group(3)}">${m.group(2)}#${m Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/pull/${m.group(3)}">${m.group(2)}#${m
.group(3)}</a>""") .group(3)}</a>""")
case Some(_) => case Some(_) =>
Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/issues/${m.group(3)}">${m.group(2)}#${m Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/issues/${m.group(3)}">${m.group(2)}#${m
.group(3)}</a>""") .group(3)}</a>""")
case None => case None =>
Some(s"""${m.group(2)}#${m.group(3)}""") Some(s"""${m.group(2)}#${m.group(3)}""")
} }
@@ -109,12 +111,12 @@ trait LinkConverter { self: RequestCache =>
getIssueFromCache(repository.owner, repository.name, m.group(3)) match { getIssueFromCache(repository.owner, repository.name, m.group(3)) match {
case Some(pull) if (pull.isPullRequest) => case Some(pull) if (pull.isPullRequest) =>
Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/pull/${m Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/pull/${m
.group(3)}" title="${pull.title}">${prefix}${m .group(3)}" title="${pull.title}">${prefix}${m
.group(3)}</a>""") .group(3)}</a>""")
case Some(issue) => case Some(issue) =>
Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/issues/${m Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/issues/${m
.group(3)}" title="${issue.title}">${prefix}${m .group(3)}" title="${issue.title}">${prefix}${m
.group(3)}</a>""") .group(3)}</a>""")
case None => case None =>
Some(s"""${m.group(2)}${m.group(3)}""") Some(s"""${m.group(2)}${m.group(3)}""")
} }
@@ -130,7 +132,7 @@ trait LinkConverter { self: RequestCache =>
// convert commit id to link // convert commit id to link
.replaceBy("(?<=(^|[^\\w/@]))([a-f0-9]{40})(?=(\\W|$))".r) { m => .replaceBy("(?<=(^|[^\\w/@]))([a-f0-9]{40})(?=(\\W|$))".r) { m =>
Some(s"""<code><a href="${context.path}/${repository.owner}/${repository.name}/commit/${m Some(s"""<code><a href="${context.path}/${repository.owner}/${repository.name}/commit/${m
.group(2)}">${m.group(2).substring(0, 7)}</a></code>""") .group(2)}">${m.group(2).substring(0, 7)}</a></code>""")
} }
} }
} }

View File

@@ -160,8 +160,8 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
/** /**
* Creates a link to the issue or the pull request from the issue id. * Creates a link to the issue or the pull request from the issue id.
*/ */
def issueLink(owner: String, repository: String, issueId: Int, title: String)( def issueLink(owner: String, repository: String, issueId: Int, title: String)(implicit
implicit context: Context context: Context
): Html = { ): Html = {
Html(createIssueLink(owner, repository, issueId, title)) Html(createIssueLink(owner, repository, issueId, title))
} }
@@ -169,8 +169,8 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
/** /**
* Creates a global link to the issue or the pull request from the issue id. * Creates a global link to the issue or the pull request from the issue id.
*/ */
def issueGlobalLink(owner: String, repository: String, issueId: Int, title: String)( def issueGlobalLink(owner: String, repository: String, issueId: Int, title: String)(implicit
implicit context: Context context: Context
): Html = { ): Html = {
Html(createGlobalIssueLink(owner, repository, issueId, title)) Html(createGlobalIssueLink(owner, repository, issueId, title))
} }
@@ -179,8 +179,8 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
* Returns &lt;img&gt; which displays the avatar icon for the given user name. * Returns &lt;img&gt; which displays the avatar icon for the given user name.
* This method looks up Gravatar if avatar icon has not been configured in user settings. * This method looks up Gravatar if avatar icon has not been configured in user settings.
*/ */
def avatar(userName: String, size: Int, tooltip: Boolean = false, mailAddress: String = "")( def avatar(userName: String, size: Int, tooltip: Boolean = false, mailAddress: String = "")(implicit
implicit context: Context context: Context
): Html = ): Html =
getAvatarImageHtml(userName, size, mailAddress, tooltip) getAvatarImageHtml(userName, size, mailAddress, tooltip)

View File

@@ -257,7 +257,7 @@ class ApiIntegrationTest extends AnyFunSuite {
assert(label1.getUrl == "http://localhost:19999/api/v3/repos/root/issue_label_test/labels/bug") assert(label1.getUrl == "http://localhost:19999/api/v3/repos/root/issue_label_test/labels/bug")
} }
// Replace labels (Cannot test because GHLabel.setLabels() doesn't use the replace endpoint) // Replace labels (Cannot test because GHLabel.setLabels() doesn't use the replace endpoint)
// { // {
// issue.setLabels("enhancement", "invalid", "question") // issue.setLabels("enhancement", "invalid", "question")
// //

View File

@@ -297,7 +297,7 @@ object ApiSpecModels {
committerEmailAddress = account.mailAddress, committerEmailAddress = account.mailAddress,
None, None,
None None
) )
val apiCommitListItem = ApiCommitListItem( val apiCommitListItem = ApiCommitListItem(
commit = commitInfo(sha1), commit = commitInfo(sha1),
@@ -443,7 +443,7 @@ object ApiSpecModels {
val apiPusher = ApiPusher(account) val apiPusher = ApiPusher(account)
//have both urls as https, as the expected samples are using https // have both urls as https, as the expected samples are using https
val gitHubContext = JsonFormat.Context("https://api.github.com", Some("https://api.github.com")) val gitHubContext = JsonFormat.Context("https://api.github.com", Some("https://api.github.com"))
val apiRefHeadsMaster = ApiRef( val apiRefHeadsMaster = ApiRef(
@@ -797,7 +797,7 @@ object ApiSpecModels {
val jsonPusher = """{"name":"octocat","email":"octocat@example.com"}""" val jsonPusher = """{"name":"octocat","email":"octocat@example.com"}"""
//I checked all refs in gitbucket repo, and there appears to be only type "commit" and type "tag" // I checked all refs in gitbucket repo, and there appears to be only type "commit" and type "tag"
val jsonRef = """{"ref":"refs/heads/featureA","object":{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}}""" val jsonRef = """{"ref":"refs/heads/featureA","object":{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}}"""
val jsonRefHeadsMain = val jsonRefHeadsMain =

View File

@@ -28,9 +28,19 @@ import MergeServiceSpec._
import org.json4s.JsonAST.{JArray, JString} import org.json4s.JsonAST.{JArray, JString}
class MergeServiceSpec extends AnyFunSpec with ServiceSpecBase { class MergeServiceSpec extends AnyFunSpec with ServiceSpecBase {
val service = new MergeService with AccountService with ActivityService with IssuesService with LabelsService val service = new MergeService
with MilestonesService with RepositoryService with PrioritiesService with PullRequestService with CommitsService with AccountService
with WebHookPullRequestService with WebHookPullRequestReviewCommentService with RequestCache { with ActivityService
with IssuesService
with LabelsService
with MilestonesService
with RepositoryService
with PrioritiesService
with PullRequestService
with CommitsService
with WebHookPullRequestService
with WebHookPullRequestReviewCommentService
with RequestCache {
override protected def getReceiveHooks(): Seq[ReceiveHook] = Nil override protected def getReceiveHooks(): Seq[ReceiveHook] = Nil
} }
val branch = "master" val branch = "master"

View File

@@ -33,7 +33,11 @@ class PullRequestServiceSpec
generateNewUserWithDBRepository("user1", "repo2") generateNewUserWithDBRepository("user1", "repo2")
generateNewUserWithDBRepository("user2", "repo1") generateNewUserWithDBRepository("user2", "repo1")
generateNewPullRequest("user1/repo1/master", "user1/repo1/head2", loginUser = "root") // not target branch generateNewPullRequest("user1/repo1/master", "user1/repo1/head2", loginUser = "root") // not target branch
generateNewPullRequest("user1/repo1/head1", "user1/repo1/master", loginUser = "root") // not target branch ( swap from, to ) generateNewPullRequest(
"user1/repo1/head1",
"user1/repo1/master",
loginUser = "root"
) // not target branch ( swap from, to )
generateNewPullRequest("user1/repo1/master", "user2/repo1/head1", loginUser = "root") // other user generateNewPullRequest("user1/repo1/master", "user2/repo1/head1", loginUser = "root") // other user
generateNewPullRequest("user1/repo1/master", "user1/repo2/head1", loginUser = "root") // other repository generateNewPullRequest("user1/repo1/master", "user1/repo2/head1", loginUser = "root") // other repository
val r1 = swap(generateNewPullRequest("user1/repo1/master2", "user1/repo1/head1", loginUser = "root")) val r1 = swap(generateNewPullRequest("user1/repo1/master2", "user1/repo1/head1", loginUser = "root"))

View File

@@ -108,10 +108,21 @@ trait ServiceSpecBase {
def user(name: String)(implicit s: Session): Account = AccountService.getAccountByUserName(name).get def user(name: String)(implicit s: Session): Account = AccountService.getAccountByUserName(name).get
lazy val dummyService = new RepositoryService with AccountService with ActivityService with IssuesService lazy val dummyService = new RepositoryService
with MergeService with PullRequestService with CommitsService with CommitStatusService with LabelsService with AccountService
with MilestonesService with PrioritiesService with WebHookService with WebHookPullRequestService with ActivityService
with WebHookPullRequestReviewCommentService with RequestCache { with IssuesService
with MergeService
with PullRequestService
with CommitsService
with CommitStatusService
with LabelsService
with MilestonesService
with PrioritiesService
with WebHookService
with WebHookPullRequestService
with WebHookPullRequestReviewCommentService
with RequestCache {
override def fetchAsPullRequest( override def fetchAsPullRequest(
userName: String, userName: String,
repositoryName: String, repositoryName: String,
@@ -133,8 +144,7 @@ trait ServiceSpecBase {
ac ac
} }
def generateNewIssue(userName: String, repositoryName: String, loginUser: String = "root")( def generateNewIssue(userName: String, repositoryName: String, loginUser: String = "root")(implicit
implicit
s: Session s: Session
): Int = { ): Int = {
dummyService.insertIssue( dummyService.insertIssue(
@@ -149,8 +159,7 @@ trait ServiceSpecBase {
) )
} }
def generateNewPullRequest(base: String, request: String, loginUser: String)( def generateNewPullRequest(base: String, request: String, loginUser: String)(implicit
implicit
s: Session s: Session
): (Issue, PullRequest) = { ): (Issue, PullRequest) = {
implicit val context = Context(createSystemSettings(), None, this.request) implicit val context = Context(createSystemSettings(), None, this.request)

View File

@@ -5,9 +5,19 @@ import org.scalatest.funsuite.AnyFunSuite
import gitbucket.core.model.WebHookContentType import gitbucket.core.model.WebHookContentType
class WebHookServiceSpec extends AnyFunSuite with ServiceSpecBase { class WebHookServiceSpec extends AnyFunSuite with ServiceSpecBase {
lazy val service = new WebHookPullRequestService with AccountService with ActivityService with RepositoryService lazy val service = new WebHookPullRequestService
with MergeService with PullRequestService with IssuesService with CommitsService with LabelsService with AccountService
with MilestonesService with PrioritiesService with WebHookPullRequestReviewCommentService with RequestCache with ActivityService
with RepositoryService
with MergeService
with PullRequestService
with IssuesService
with CommitsService
with LabelsService
with MilestonesService
with PrioritiesService
with WebHookPullRequestReviewCommentService
with RequestCache
test("WebHookPullRequestService.getPullRequestsByRequestForWebhook") { test("WebHookPullRequestService.getPullRequestsByRequestForWebhook") {
withTestDB { implicit session => withTestDB { implicit session =>

View File

@@ -101,11 +101,12 @@ object GitSpecUtil {
val merger = MergeStrategy.RECURSIVE.newMerger(repository, true) val merger = MergeStrategy.RECURSIVE.newMerger(repository, true)
val mergeBaseTip = repository.resolve(into) val mergeBaseTip = repository.resolve(into)
val mergeTip = repository.resolve(branch) val mergeTip = repository.resolve(branch)
val conflicted = try { val conflicted =
!merger.merge(mergeBaseTip, mergeTip) try {
} catch { !merger.merge(mergeBaseTip, mergeTip)
case e: NoMergeBaseException => true } catch {
} case e: NoMergeBaseException => true
}
if (conflicted) { if (conflicted) {
throw new RuntimeException("conflict!") throw new RuntimeException("conflict!")
} }

View File

@@ -88,41 +88,65 @@ class StringUtilSpec extends AnyFunSpec {
it("should convert GitBucket repository url") { it("should convert GitBucket repository url") {
assert( assert(
StringUtil StringUtil
.getRepositoryViewerUrl("http://localhost:8080/git/root/gitbucket.git", baseUrl) == "http://localhost:8080/root/gitbucket" .getRepositoryViewerUrl(
"http://localhost:8080/git/root/gitbucket.git",
baseUrl
) == "http://localhost:8080/root/gitbucket"
) )
assert( assert(
StringUtil StringUtil
.getRepositoryViewerUrl("http://root@localhost:8080/git/root/gitbucket.git", baseUrl) == "http://localhost:8080/root/gitbucket" .getRepositoryViewerUrl(
"http://root@localhost:8080/git/root/gitbucket.git",
baseUrl
) == "http://localhost:8080/root/gitbucket"
) )
} }
it("should convert GitHub repository url") { it("should convert GitHub repository url") {
assert( assert(
StringUtil StringUtil
.getRepositoryViewerUrl("https://github.com/root/gitbucket.git", baseUrl) == "https://github.com/root/gitbucket" .getRepositoryViewerUrl(
"https://github.com/root/gitbucket.git",
baseUrl
) == "https://github.com/root/gitbucket"
) )
assert( assert(
StringUtil StringUtil
.getRepositoryViewerUrl("https://root@github.com/root/gitbucket.git", baseUrl) == "https://github.com/root/gitbucket" .getRepositoryViewerUrl(
"https://root@github.com/root/gitbucket.git",
baseUrl
) == "https://github.com/root/gitbucket"
) )
} }
it("should convert BitBucket repository url") { it("should convert BitBucket repository url") {
assert( assert(
StringUtil StringUtil
.getRepositoryViewerUrl("https://bitbucket.org/root/gitbucket.git", baseUrl) == "https://bitbucket.org/root/gitbucket" .getRepositoryViewerUrl(
"https://bitbucket.org/root/gitbucket.git",
baseUrl
) == "https://bitbucket.org/root/gitbucket"
) )
assert( assert(
StringUtil StringUtil
.getRepositoryViewerUrl("https://root@bitbucket.org/root/gitbucket.git", baseUrl) == "https://bitbucket.org/root/gitbucket" .getRepositoryViewerUrl(
"https://root@bitbucket.org/root/gitbucket.git",
baseUrl
) == "https://bitbucket.org/root/gitbucket"
) )
} }
it("should convert GitLab repository url") { it("should convert GitLab repository url") {
assert( assert(
StringUtil StringUtil
.getRepositoryViewerUrl("https://gitlab.com/root/gitbucket.git", baseUrl) == "https://gitlab.com/root/gitbucket" .getRepositoryViewerUrl(
"https://gitlab.com/root/gitbucket.git",
baseUrl
) == "https://gitlab.com/root/gitbucket"
) )
assert( assert(
StringUtil StringUtil
.getRepositoryViewerUrl("https://root@gitlab.com/root/gitbucket.git", baseUrl) == "https://gitlab.com/root/gitbucket" .getRepositoryViewerUrl(
"https://root@gitlab.com/root/gitbucket.git",
baseUrl
) == "https://gitlab.com/root/gitbucket"
) )
} }
} }

View File

@@ -202,8 +202,7 @@ class AvatarImageProviderSpec extends AnyFunSpec {
*/ */
class AvatarImageProviderImpl(account: Option[Account]) extends AvatarImageProvider with RequestCache { class AvatarImageProviderImpl(account: Option[Account]) 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
implicit
context: Context context: Context
): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip) ): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip)