mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-05-06 17:06:28 +02:00
Apply scalafmt
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
|
||||
import java.util.EnumSet
|
||||
import javax.servlet._
|
||||
|
||||
@@ -9,28 +8,35 @@ import gitbucket.core.servlet._
|
||||
import gitbucket.core.util.Directory
|
||||
import org.scalatra._
|
||||
|
||||
|
||||
class ScalatraBootstrap extends LifeCycle with SystemSettingsService {
|
||||
override def init(context: ServletContext) {
|
||||
|
||||
val settings = loadSystemSettings()
|
||||
if(settings.baseUrl.exists(_.startsWith("https://"))) {
|
||||
if (settings.baseUrl.exists(_.startsWith("https://"))) {
|
||||
context.getSessionCookieConfig.setSecure(true)
|
||||
}
|
||||
|
||||
// Register TransactionFilter and BasicAuthenticationFilter at first
|
||||
context.addFilter("transactionFilter", new TransactionFilter)
|
||||
context.getFilterRegistration("transactionFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*")
|
||||
context
|
||||
.getFilterRegistration("transactionFilter")
|
||||
.addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*")
|
||||
context.addFilter("gitAuthenticationFilter", new GitAuthenticationFilter)
|
||||
context.getFilterRegistration("gitAuthenticationFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/git/*")
|
||||
context
|
||||
.getFilterRegistration("gitAuthenticationFilter")
|
||||
.addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/git/*")
|
||||
context.addFilter("apiAuthenticationFilter", new ApiAuthenticationFilter)
|
||||
context.getFilterRegistration("apiAuthenticationFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/api/v3/*")
|
||||
context
|
||||
.getFilterRegistration("apiAuthenticationFilter")
|
||||
.addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/api/v3/*")
|
||||
|
||||
// Register controllers
|
||||
context.mount(new PreProcessController, "/*")
|
||||
|
||||
context.addFilter("pluginControllerFilter", new PluginControllerFilter)
|
||||
context.getFilterRegistration("pluginControllerFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*")
|
||||
context
|
||||
.getFilterRegistration("pluginControllerFilter")
|
||||
.addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*")
|
||||
|
||||
context.mount(new FileUploadController, "/upload")
|
||||
|
||||
@@ -51,11 +57,13 @@ class ScalatraBootstrap extends LifeCycle with SystemSettingsService {
|
||||
filter.mount(new RepositorySettingsController, "/*")
|
||||
|
||||
context.addFilter("compositeScalatraFilter", filter)
|
||||
context.getFilterRegistration("compositeScalatraFilter").addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*")
|
||||
context
|
||||
.getFilterRegistration("compositeScalatraFilter")
|
||||
.addMappingForUrlPatterns(EnumSet.allOf(classOf[DispatcherType]), true, "/*")
|
||||
|
||||
// Create GITBUCKET_HOME directory if it does not exist
|
||||
val dir = new java.io.File(Directory.GitBucketHome)
|
||||
if(!dir.exists){
|
||||
if (!dir.exists) {
|
||||
dir.mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,61 +3,52 @@ package gitbucket.core
|
||||
import io.github.gitbucket.solidbase.migration.{SqlMigration, LiquibaseMigration}
|
||||
import io.github.gitbucket.solidbase.model.{Version, Module}
|
||||
|
||||
object GitBucketCoreModule extends Module("gitbucket-core",
|
||||
new Version("4.0.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.0.xml"),
|
||||
new SqlMigration("update/gitbucket-core_4.0.sql")
|
||||
),
|
||||
new Version("4.1.0"),
|
||||
new Version("4.2.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.2.xml")
|
||||
),
|
||||
new Version("4.2.1"),
|
||||
new Version("4.3.0"),
|
||||
new Version("4.4.0"),
|
||||
new Version("4.5.0"),
|
||||
new Version("4.6.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.6.xml")
|
||||
),
|
||||
new Version("4.7.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.7.xml"),
|
||||
new SqlMigration("update/gitbucket-core_4.7.sql")
|
||||
),
|
||||
new Version("4.7.1"),
|
||||
new Version("4.8"),
|
||||
new Version("4.9.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.9.xml")
|
||||
),
|
||||
new Version("4.10.0"),
|
||||
new Version("4.11.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.11.xml")
|
||||
),
|
||||
new Version("4.12.0"),
|
||||
new Version("4.12.1"),
|
||||
new Version("4.13.0"),
|
||||
new Version("4.14.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.14.xml"),
|
||||
new SqlMigration("update/gitbucket-core_4.14.sql")
|
||||
),
|
||||
new Version("4.14.1"),
|
||||
new Version("4.15.0"),
|
||||
new Version("4.16.0"),
|
||||
new Version("4.17.0"),
|
||||
new Version("4.18.0"),
|
||||
new Version("4.19.0"),
|
||||
new Version("4.19.1"),
|
||||
new Version("4.19.2"),
|
||||
new Version("4.19.3"),
|
||||
new Version("4.20.0"),
|
||||
new Version("4.21.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.21.xml")
|
||||
),
|
||||
new Version("4.21.1"),
|
||||
new Version("4.21.2"),
|
||||
new Version("4.22.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.22.xml")
|
||||
),
|
||||
new Version("4.23.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.23.xml")
|
||||
)
|
||||
)
|
||||
object GitBucketCoreModule
|
||||
extends Module(
|
||||
"gitbucket-core",
|
||||
new Version(
|
||||
"4.0.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.0.xml"),
|
||||
new SqlMigration("update/gitbucket-core_4.0.sql")
|
||||
),
|
||||
new Version("4.1.0"),
|
||||
new Version("4.2.0", new LiquibaseMigration("update/gitbucket-core_4.2.xml")),
|
||||
new Version("4.2.1"),
|
||||
new Version("4.3.0"),
|
||||
new Version("4.4.0"),
|
||||
new Version("4.5.0"),
|
||||
new Version("4.6.0", new LiquibaseMigration("update/gitbucket-core_4.6.xml")),
|
||||
new Version(
|
||||
"4.7.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.7.xml"),
|
||||
new SqlMigration("update/gitbucket-core_4.7.sql")
|
||||
),
|
||||
new Version("4.7.1"),
|
||||
new Version("4.8"),
|
||||
new Version("4.9.0", new LiquibaseMigration("update/gitbucket-core_4.9.xml")),
|
||||
new Version("4.10.0"),
|
||||
new Version("4.11.0", new LiquibaseMigration("update/gitbucket-core_4.11.xml")),
|
||||
new Version("4.12.0"),
|
||||
new Version("4.12.1"),
|
||||
new Version("4.13.0"),
|
||||
new Version(
|
||||
"4.14.0",
|
||||
new LiquibaseMigration("update/gitbucket-core_4.14.xml"),
|
||||
new SqlMigration("update/gitbucket-core_4.14.sql")
|
||||
),
|
||||
new Version("4.14.1"),
|
||||
new Version("4.15.0"),
|
||||
new Version("4.16.0"),
|
||||
new Version("4.17.0"),
|
||||
new Version("4.18.0"),
|
||||
new Version("4.19.0"),
|
||||
new Version("4.19.1"),
|
||||
new Version("4.19.2"),
|
||||
new Version("4.19.3"),
|
||||
new Version("4.20.0"),
|
||||
new Version("4.21.0", new LiquibaseMigration("update/gitbucket-core_4.21.xml")),
|
||||
new Version("4.21.1"),
|
||||
new Version("4.21.2"),
|
||||
new Version("4.22.0", new LiquibaseMigration("update/gitbucket-core_4.22.xml")),
|
||||
new Version("4.23.0", new LiquibaseMigration("update/gitbucket-core_4.23.xml"))
|
||||
)
|
||||
|
||||
@@ -6,13 +6,14 @@ import gitbucket.core.util.RepositoryName
|
||||
* https://developer.github.com/v3/repos/#get-branch
|
||||
* https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection
|
||||
*/
|
||||
case class ApiBranch(
|
||||
name: String,
|
||||
commit: ApiBranchCommit,
|
||||
protection: ApiBranchProtection)(repositoryName:RepositoryName) extends FieldSerializable {
|
||||
def _links = Map(
|
||||
"self" -> ApiPath(s"/api/v3/repos/${repositoryName.fullName}/branches/${name}"),
|
||||
"html" -> ApiPath(s"/${repositoryName.fullName}/tree/${name}"))
|
||||
case class ApiBranch(name: String, commit: ApiBranchCommit, protection: ApiBranchProtection)(
|
||||
repositoryName: RepositoryName
|
||||
) extends FieldSerializable {
|
||||
def _links =
|
||||
Map(
|
||||
"self" -> ApiPath(s"/api/v3/repos/${repositoryName.fullName}/branches/${name}"),
|
||||
"html" -> ApiPath(s"/${repositoryName.fullName}/tree/${name}")
|
||||
)
|
||||
}
|
||||
|
||||
case class ApiBranchCommit(sha: String)
|
||||
|
||||
@@ -4,17 +4,22 @@ import gitbucket.core.service.ProtectedBranchService
|
||||
import org.json4s._
|
||||
|
||||
/** https://developer.github.com/v3/repos/#enabling-and-disabling-branch-protection */
|
||||
case class ApiBranchProtection(enabled: Boolean, required_status_checks: Option[ApiBranchProtection.Status]){
|
||||
case class ApiBranchProtection(enabled: Boolean, required_status_checks: Option[ApiBranchProtection.Status]) {
|
||||
def status: ApiBranchProtection.Status = required_status_checks.getOrElse(ApiBranchProtection.statusNone)
|
||||
}
|
||||
|
||||
object ApiBranchProtection{
|
||||
object ApiBranchProtection {
|
||||
|
||||
/** form for enabling-and-disabling-branch-protection */
|
||||
case class EnablingAndDisabling(protection: ApiBranchProtection)
|
||||
|
||||
def apply(info: ProtectedBranchService.ProtectedBranchInfo): ApiBranchProtection = ApiBranchProtection(
|
||||
enabled = info.enabled,
|
||||
required_status_checks = Some(Status(EnforcementLevel(info.enabled && info.contexts.nonEmpty, info.includeAdministrators), info.contexts)))
|
||||
def apply(info: ProtectedBranchService.ProtectedBranchInfo): ApiBranchProtection =
|
||||
ApiBranchProtection(
|
||||
enabled = info.enabled,
|
||||
required_status_checks = Some(
|
||||
Status(EnforcementLevel(info.enabled && info.contexts.nonEmpty, info.includeAdministrators), info.contexts)
|
||||
)
|
||||
)
|
||||
val statusNone = Status(Off, Seq.empty)
|
||||
case class Status(enforcement_level: EnforcementLevel, contexts: Seq[String])
|
||||
sealed class EnforcementLevel(val name: String)
|
||||
@@ -22,25 +27,28 @@ object ApiBranchProtection{
|
||||
case object NonAdmins extends EnforcementLevel("non_admins")
|
||||
case object Everyone extends EnforcementLevel("everyone")
|
||||
object EnforcementLevel {
|
||||
def apply(enabled: Boolean, includeAdministrators: Boolean): EnforcementLevel = if(enabled){
|
||||
if(includeAdministrators){
|
||||
Everyone
|
||||
}else{
|
||||
NonAdmins
|
||||
def apply(enabled: Boolean, includeAdministrators: Boolean): EnforcementLevel =
|
||||
if (enabled) {
|
||||
if (includeAdministrators) {
|
||||
Everyone
|
||||
} else {
|
||||
NonAdmins
|
||||
}
|
||||
} else {
|
||||
Off
|
||||
}
|
||||
}else{
|
||||
Off
|
||||
}
|
||||
}
|
||||
|
||||
implicit val enforcementLevelSerializer = new CustomSerializer[EnforcementLevel](format => (
|
||||
{
|
||||
case JString("off") => Off
|
||||
case JString("non_admins") => NonAdmins
|
||||
case JString("everyone") => Everyone
|
||||
},
|
||||
{
|
||||
case x: EnforcementLevel => JString(x.name)
|
||||
}
|
||||
))
|
||||
implicit val enforcementLevelSerializer = new CustomSerializer[EnforcementLevel](
|
||||
format =>
|
||||
(
|
||||
{
|
||||
case JString("off") => Off
|
||||
case JString("non_admins") => NonAdmins
|
||||
case JString("everyone") => Everyone
|
||||
}, {
|
||||
case x: EnforcementLevel => JString(x.name)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package gitbucket.core.api
|
||||
|
||||
import gitbucket.core.model.{Account, CommitState, CommitStatus}
|
||||
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
|
||||
*/
|
||||
@@ -11,15 +10,22 @@ case class ApiCombinedCommitStatus(
|
||||
sha: String,
|
||||
total_count: Int,
|
||||
statuses: Iterable[ApiCommitStatus],
|
||||
repository: ApiRepository){
|
||||
repository: ApiRepository
|
||||
) {
|
||||
// val commit_url = ApiPath(s"/api/v3/repos/${repository.full_name}/${sha}")
|
||||
val url = ApiPath(s"/api/v3/repos/${repository.full_name}/commits/${sha}/status")
|
||||
}
|
||||
object ApiCombinedCommitStatus {
|
||||
def apply(sha:String, statuses: Iterable[(CommitStatus, Account)], repository:ApiRepository): ApiCombinedCommitStatus = ApiCombinedCommitStatus(
|
||||
state = CommitState.combine(statuses.map(_._1.state).toSet).name,
|
||||
sha = sha,
|
||||
total_count= statuses.size,
|
||||
statuses = statuses.map{ case (s, a)=> ApiCommitStatus(s, ApiUser(a)) },
|
||||
repository = repository)
|
||||
def apply(
|
||||
sha: String,
|
||||
statuses: Iterable[(CommitStatus, Account)],
|
||||
repository: ApiRepository
|
||||
): ApiCombinedCommitStatus =
|
||||
ApiCombinedCommitStatus(
|
||||
state = CommitState.combine(statuses.map(_._1.state).toSet).name,
|
||||
sha = sha,
|
||||
total_count = statuses.size,
|
||||
statuses = statuses.map { case (s, a) => ApiCommitStatus(s, ApiUser(a)) },
|
||||
repository = repository
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,25 +5,32 @@ import gitbucket.core.util.RepositoryName
|
||||
|
||||
import java.util.Date
|
||||
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/issues/comments/
|
||||
*/
|
||||
case class ApiComment(
|
||||
id: Int,
|
||||
user: ApiUser,
|
||||
body: String,
|
||||
created_at: Date,
|
||||
updated_at: Date)(repositoryName: RepositoryName, issueId: Int, isPullRequest: Boolean){
|
||||
val html_url = ApiPath(s"/${repositoryName.fullName}/${if(isPullRequest){ "pull" }else{ "issues" }}/${issueId}#comment-${id}")
|
||||
case class ApiComment(id: Int, user: ApiUser, body: String, created_at: Date, updated_at: Date)(
|
||||
repositoryName: RepositoryName,
|
||||
issueId: Int,
|
||||
isPullRequest: Boolean
|
||||
) {
|
||||
val html_url = ApiPath(
|
||||
s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" } else { "issues" }}/${issueId}#comment-${id}"
|
||||
)
|
||||
}
|
||||
|
||||
object ApiComment{
|
||||
def apply(comment: IssueComment, repositoryName: RepositoryName, issueId: Int, user: ApiUser, isPullRequest: Boolean): ApiComment =
|
||||
object ApiComment {
|
||||
def apply(
|
||||
comment: IssueComment,
|
||||
repositoryName: RepositoryName,
|
||||
issueId: Int,
|
||||
user: ApiUser,
|
||||
isPullRequest: Boolean
|
||||
): ApiComment =
|
||||
ApiComment(
|
||||
id = comment.commentId,
|
||||
user = user,
|
||||
body = comment.content,
|
||||
created_at = comment.registeredDate,
|
||||
updated_at = comment.updatedDate)(repositoryName, issueId, isPullRequest)
|
||||
updated_at = comment.updatedDate
|
||||
)(repositoryName, issueId, isPullRequest)
|
||||
}
|
||||
|
||||
@@ -20,38 +20,41 @@ case class ApiCommit(
|
||||
removed: List[String],
|
||||
modified: List[String],
|
||||
author: ApiPersonIdent,
|
||||
committer: ApiPersonIdent)(repositoryName:RepositoryName, urlIsHtmlUrl: Boolean) extends FieldSerializable{
|
||||
val url = if(urlIsHtmlUrl){
|
||||
committer: ApiPersonIdent
|
||||
)(repositoryName: RepositoryName, urlIsHtmlUrl: Boolean)
|
||||
extends FieldSerializable {
|
||||
val url = if (urlIsHtmlUrl) {
|
||||
ApiPath(s"/${repositoryName.fullName}/commit/${id}")
|
||||
}else{
|
||||
} else {
|
||||
ApiPath(s"/api/v3/${repositoryName.fullName}/commits/${id}")
|
||||
}
|
||||
val html_url = if(urlIsHtmlUrl){
|
||||
val html_url = if (urlIsHtmlUrl) {
|
||||
None
|
||||
}else{
|
||||
} else {
|
||||
Some(ApiPath(s"/${repositoryName.fullName}/commit/${id}"))
|
||||
}
|
||||
}
|
||||
|
||||
object ApiCommit{
|
||||
object ApiCommit {
|
||||
def apply(git: Git, repositoryName: RepositoryName, commit: CommitInfo, urlIsHtmlUrl: Boolean = false): ApiCommit = {
|
||||
val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
|
||||
ApiCommit(
|
||||
id = commit.id,
|
||||
message = commit.fullMessage,
|
||||
id = commit.id,
|
||||
message = commit.fullMessage,
|
||||
timestamp = commit.commitTime,
|
||||
added = diffs.collect {
|
||||
case x if x.changeType == DiffEntry.ChangeType.ADD => x.newPath
|
||||
added = diffs.collect {
|
||||
case x if x.changeType == DiffEntry.ChangeType.ADD => x.newPath
|
||||
},
|
||||
removed = diffs.collect {
|
||||
removed = diffs.collect {
|
||||
case x if x.changeType == DiffEntry.ChangeType.DELETE => x.oldPath
|
||||
},
|
||||
modified = diffs.collect {
|
||||
modified = diffs.collect {
|
||||
case x if x.changeType != DiffEntry.ChangeType.ADD && x.changeType != DiffEntry.ChangeType.DELETE => x.newPath
|
||||
},
|
||||
author = ApiPersonIdent.author(commit),
|
||||
author = ApiPersonIdent.author(commit),
|
||||
committer = ApiPersonIdent.committer(commit)
|
||||
)(repositoryName, urlIsHtmlUrl)
|
||||
}
|
||||
def forWebhookPayload(git: Git, repositoryName: RepositoryName, commit: CommitInfo): ApiCommit = apply(git, repositoryName, commit, true)
|
||||
def forWebhookPayload(git: Git, repositoryName: RepositoryName, commit: CommitInfo): ApiCommit =
|
||||
apply(git, repositoryName, commit, true)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import gitbucket.core.api.ApiCommitListItem._
|
||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||
import gitbucket.core.util.RepositoryName
|
||||
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/repos/commits/
|
||||
*/
|
||||
@@ -13,30 +12,33 @@ case class ApiCommitListItem(
|
||||
commit: Commit,
|
||||
author: Option[ApiUser],
|
||||
committer: Option[ApiUser],
|
||||
parents: Seq[Parent])(repositoryName: RepositoryName) {
|
||||
parents: Seq[Parent]
|
||||
)(repositoryName: RepositoryName) {
|
||||
val url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${sha}")
|
||||
}
|
||||
|
||||
object ApiCommitListItem {
|
||||
def apply(commit: CommitInfo, repositoryName: RepositoryName): ApiCommitListItem = ApiCommitListItem(
|
||||
sha = commit.id,
|
||||
commit = Commit(
|
||||
message = commit.fullMessage,
|
||||
author = ApiPersonIdent.author(commit),
|
||||
committer = ApiPersonIdent.committer(commit)
|
||||
def apply(commit: CommitInfo, repositoryName: RepositoryName): ApiCommitListItem =
|
||||
ApiCommitListItem(
|
||||
sha = commit.id,
|
||||
commit = Commit(
|
||||
message = commit.fullMessage,
|
||||
author = ApiPersonIdent.author(commit),
|
||||
committer = ApiPersonIdent.committer(commit)
|
||||
)(commit.id, repositoryName),
|
||||
author = None,
|
||||
committer = None,
|
||||
parents = commit.parents.map(Parent(_)(repositoryName)))(repositoryName)
|
||||
author = None,
|
||||
committer = None,
|
||||
parents = commit.parents.map(Parent(_)(repositoryName))
|
||||
)(repositoryName)
|
||||
|
||||
case class Parent(sha: String)(repositoryName: RepositoryName){
|
||||
case class Parent(sha: String)(repositoryName: RepositoryName) {
|
||||
val url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${sha}")
|
||||
}
|
||||
|
||||
case class Commit(
|
||||
message: String,
|
||||
author: ApiPersonIdent,
|
||||
committer: ApiPersonIdent)(sha:String, repositoryName: RepositoryName) {
|
||||
case class Commit(message: String, author: ApiPersonIdent, committer: ApiPersonIdent)(
|
||||
sha: String,
|
||||
repositoryName: RepositoryName
|
||||
) {
|
||||
val url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/git/commits/${sha}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import gitbucket.core.util.RepositoryName
|
||||
|
||||
import java.util.Date
|
||||
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/repos/statuses/#create-a-status
|
||||
* https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref
|
||||
@@ -23,16 +22,16 @@ case class ApiCommitStatus(
|
||||
val url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${sha}/statuses")
|
||||
}
|
||||
|
||||
|
||||
object ApiCommitStatus {
|
||||
def apply(status: CommitStatus, creator:ApiUser): ApiCommitStatus = ApiCommitStatus(
|
||||
created_at = status.registeredDate,
|
||||
updated_at = status.updatedDate,
|
||||
state = status.state.name,
|
||||
target_url = status.targetUrl,
|
||||
description= status.description,
|
||||
id = status.commitStatusId,
|
||||
context = status.context,
|
||||
creator = creator
|
||||
)(status.commitId, RepositoryName(status))
|
||||
def apply(status: CommitStatus, creator: ApiUser): ApiCommitStatus =
|
||||
ApiCommitStatus(
|
||||
created_at = status.registeredDate,
|
||||
updated_at = status.updatedDate,
|
||||
state = status.state.name,
|
||||
target_url = status.targetUrl,
|
||||
description = status.description,
|
||||
id = status.commitStatusId,
|
||||
context = status.context,
|
||||
creator = creator
|
||||
)(status.commitId, RepositoryName(status))
|
||||
}
|
||||
|
||||
@@ -51,20 +51,25 @@ object ApiCommits {
|
||||
patch: String
|
||||
)
|
||||
|
||||
|
||||
def apply(repositoryName: RepositoryName, commitInfo: CommitInfo, diffs: Seq[DiffInfo], author: Account, committer: Account,
|
||||
commentCount: Int): ApiCommits = {
|
||||
def apply(
|
||||
repositoryName: RepositoryName,
|
||||
commitInfo: CommitInfo,
|
||||
diffs: Seq[DiffInfo],
|
||||
author: Account,
|
||||
committer: Account,
|
||||
commentCount: Int
|
||||
): ApiCommits = {
|
||||
val files = diffs.map { diff =>
|
||||
var additions = 0
|
||||
var deletions = 0
|
||||
|
||||
diff.patch.getOrElse("").split("\n").foreach { line =>
|
||||
if(line.startsWith("+")) additions = additions + 1
|
||||
if(line.startsWith("-")) deletions = deletions + 1
|
||||
if (line.startsWith("+")) additions = additions + 1
|
||||
if (line.startsWith("-")) deletions = deletions + 1
|
||||
}
|
||||
|
||||
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,
|
||||
deletions = deletions,
|
||||
changes = additions + deletions,
|
||||
@@ -75,12 +80,12 @@ object ApiCommits {
|
||||
case ChangeType.RENAME => "renamed"
|
||||
case ChangeType.COPY => "copied"
|
||||
},
|
||||
raw_url = if(diff.changeType == ChangeType.DELETE){
|
||||
raw_url = if (diff.changeType == ChangeType.DELETE) {
|
||||
ApiPath(s"/${repositoryName.fullName}/raw/${commitInfo.parents.head}/${diff.oldPath}")
|
||||
} else {
|
||||
ApiPath(s"/${repositoryName.fullName}/raw/${commitInfo.id}/${diff.newPath}")
|
||||
},
|
||||
blob_url = if(diff.changeType == ChangeType.DELETE){
|
||||
blob_url = if (diff.changeType == ChangeType.DELETE) {
|
||||
ApiPath(s"/${repositoryName.fullName}/blob/${commitInfo.parents.head}/${diff.oldPath}")
|
||||
} else {
|
||||
ApiPath(s"/${repositoryName.fullName}/blob/${commitInfo.id}/${diff.newPath}")
|
||||
|
||||
@@ -11,18 +11,29 @@ case class ApiContents(
|
||||
path: String,
|
||||
sha: String,
|
||||
content: Option[String],
|
||||
encoding: Option[String])(repositoryName: RepositoryName){
|
||||
encoding: Option[String]
|
||||
)(repositoryName: RepositoryName) {
|
||||
val download_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/raw/${sha}/${path}")
|
||||
}
|
||||
|
||||
object ApiContents{
|
||||
object ApiContents {
|
||||
def apply(fileInfo: FileInfo, repositoryName: RepositoryName, content: Option[Array[Byte]]): ApiContents = {
|
||||
if(fileInfo.isDirectory) {
|
||||
if (fileInfo.isDirectory) {
|
||||
ApiContents("dir", fileInfo.name, fileInfo.path, fileInfo.commitId, None, None)(repositoryName)
|
||||
} else {
|
||||
content.map(arr =>
|
||||
ApiContents("file", fileInfo.name, fileInfo.path, fileInfo.commitId, Some(Base64.getEncoder.encodeToString(arr)), Some("base64"))(repositoryName)
|
||||
).getOrElse(ApiContents("file", fileInfo.name, fileInfo.path, fileInfo.commitId, None, None)(repositoryName))
|
||||
content
|
||||
.map(
|
||||
arr =>
|
||||
ApiContents(
|
||||
"file",
|
||||
fileInfo.name,
|
||||
fileInfo.path,
|
||||
fileInfo.commitId,
|
||||
Some(Base64.getEncoder.encodeToString(arr)),
|
||||
Some("base64")
|
||||
)(repositoryName)
|
||||
)
|
||||
.getOrElse(ApiContents("file", fileInfo.name, fileInfo.path, fileInfo.commitId, None, None)(repositoryName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package gitbucket.core.api
|
||||
|
||||
case class ApiEndPoint(rate_limit_url: ApiPath = ApiPath("/api/v3/rate_limit"))
|
||||
case class ApiEndPoint(rate_limit_url: ApiPath = ApiPath("/api/v3/rate_limit"))
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
package gitbucket.core.api
|
||||
|
||||
case class ApiError(
|
||||
message: String,
|
||||
documentation_url: Option[String] = None)
|
||||
case class ApiError(message: String, documentation_url: Option[String] = None)
|
||||
|
||||
@@ -5,7 +5,6 @@ import gitbucket.core.util.RepositoryName
|
||||
|
||||
import java.util.Date
|
||||
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/issues/
|
||||
*/
|
||||
@@ -17,30 +16,34 @@ case class ApiIssue(
|
||||
state: String,
|
||||
created_at: Date,
|
||||
updated_at: Date,
|
||||
body: String)(repositoryName: RepositoryName, isPullRequest: Boolean){
|
||||
body: String
|
||||
)(repositoryName: RepositoryName, isPullRequest: Boolean) {
|
||||
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) {
|
||||
Some(Map(
|
||||
"url" -> ApiPath(s"/api/v3/repos/${repositoryName.fullName}/pulls/${number}"),
|
||||
"html_url" -> ApiPath(s"/${repositoryName.fullName}/pull/${number}")
|
||||
// "diff_url" -> ApiPath(s"/${repositoryName.fullName}/pull/${number}.diff"),
|
||||
// "patch_url" -> ApiPath(s"/${repositoryName.fullName}/pull/${number}.patch")
|
||||
))
|
||||
Some(
|
||||
Map(
|
||||
"url" -> ApiPath(s"/api/v3/repos/${repositoryName.fullName}/pulls/${number}"),
|
||||
"html_url" -> ApiPath(s"/${repositoryName.fullName}/pull/${number}")
|
||||
// "diff_url" -> ApiPath(s"/${repositoryName.fullName}/pull/${number}.diff"),
|
||||
// "patch_url" -> ApiPath(s"/${repositoryName.fullName}/pull/${number}.patch")
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
object ApiIssue{
|
||||
object ApiIssue {
|
||||
def apply(issue: Issue, repositoryName: RepositoryName, user: ApiUser, labels: List[ApiLabel]): ApiIssue =
|
||||
ApiIssue(
|
||||
number = issue.issueId,
|
||||
title = issue.title,
|
||||
user = user,
|
||||
title = issue.title,
|
||||
user = user,
|
||||
labels = labels,
|
||||
state = if(issue.closed){ "closed" }else{ "open" },
|
||||
body = issue.content.getOrElse(""),
|
||||
state = if (issue.closed) { "closed" } else { "open" },
|
||||
body = issue.content.getOrElse(""),
|
||||
created_at = issue.registeredDate,
|
||||
updated_at = issue.updatedDate)(repositoryName, issue.isPullRequest)
|
||||
updated_at = issue.updatedDate
|
||||
)(repositoryName, issue.isPullRequest)
|
||||
}
|
||||
|
||||
@@ -4,18 +4,16 @@ import gitbucket.core.model.Label
|
||||
import gitbucket.core.util.RepositoryName
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/issues/labels/
|
||||
*/
|
||||
case class ApiLabel(
|
||||
name: String,
|
||||
color: String)(repositoryName: RepositoryName){
|
||||
* https://developer.github.com/v3/issues/labels/
|
||||
*/
|
||||
case class ApiLabel(name: String, color: String)(repositoryName: RepositoryName) {
|
||||
var url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/labels/${name}")
|
||||
}
|
||||
|
||||
object ApiLabel{
|
||||
def apply(label:Label, repositoryName: RepositoryName): ApiLabel =
|
||||
object ApiLabel {
|
||||
def apply(label: Label, repositoryName: RepositoryName): ApiLabel =
|
||||
ApiLabel(
|
||||
name = label.labelName,
|
||||
color = label.color
|
||||
)(repositoryName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,22 +4,11 @@ import gitbucket.core.util.JGitUtil.CommitInfo
|
||||
|
||||
import java.util.Date
|
||||
|
||||
|
||||
case class ApiPersonIdent(
|
||||
name: String,
|
||||
email: String,
|
||||
date: Date)
|
||||
|
||||
case class ApiPersonIdent(name: String, email: String, date: Date)
|
||||
|
||||
object ApiPersonIdent {
|
||||
def author(commit: CommitInfo): ApiPersonIdent =
|
||||
ApiPersonIdent(
|
||||
name = commit.authorName,
|
||||
email = commit.authorEmailAddress,
|
||||
date = commit.authorTime)
|
||||
ApiPersonIdent(name = commit.authorName, email = commit.authorEmailAddress, date = commit.authorTime)
|
||||
def committer(commit: CommitInfo): ApiPersonIdent =
|
||||
ApiPersonIdent(
|
||||
name = commit.committerName,
|
||||
email = commit.committerEmailAddress,
|
||||
date = commit.commitTime)
|
||||
ApiPersonIdent(name = commit.committerName, email = commit.committerEmailAddress, date = commit.commitTime)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ case class ApiPlugin(
|
||||
jarFileName: String
|
||||
)
|
||||
|
||||
object ApiPlugin{
|
||||
object ApiPlugin {
|
||||
def apply(plugin: PluginInfo): ApiPlugin = {
|
||||
ApiPlugin(plugin.pluginId, plugin.pluginName, plugin.pluginVersion, plugin.description, plugin.pluginJar.getName)
|
||||
}
|
||||
|
||||
@@ -21,20 +21,21 @@ case class ApiPullRequest(
|
||||
body: String,
|
||||
user: ApiUser,
|
||||
labels: List[ApiLabel],
|
||||
assignee: Option[ApiUser]){
|
||||
val html_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}")
|
||||
assignee: Option[ApiUser]
|
||||
) {
|
||||
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 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 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_comment_url = ApiPath(s"${base.repo.url.path}/pulls/comments/{number}")
|
||||
val comments_url = ApiPath(s"${base.repo.url.path}/issues/${number}/comments")
|
||||
val statuses_url = ApiPath(s"${base.repo.url.path}/statuses/${head.sha}")
|
||||
val review_comment_url = ApiPath(s"${base.repo.url.path}/pulls/comments/{number}")
|
||||
val comments_url = ApiPath(s"${base.repo.url.path}/issues/${number}/comments")
|
||||
val statuses_url = ApiPath(s"${base.repo.url.path}/statuses/${head.sha}")
|
||||
}
|
||||
|
||||
object ApiPullRequest{
|
||||
object ApiPullRequest {
|
||||
def apply(
|
||||
issue: Issue,
|
||||
pullRequest: PullRequest,
|
||||
@@ -46,34 +47,25 @@ object ApiPullRequest{
|
||||
mergedComment: Option[(IssueComment, Account)]
|
||||
): ApiPullRequest =
|
||||
ApiPullRequest(
|
||||
number = issue.issueId,
|
||||
state = if (issue.closed) "closed" else "open",
|
||||
number = issue.issueId,
|
||||
state = if (issue.closed) "closed" else "open",
|
||||
updated_at = issue.updatedDate,
|
||||
created_at = issue.registeredDate,
|
||||
head = Commit(
|
||||
sha = pullRequest.commitIdTo,
|
||||
ref = pullRequest.requestBranch,
|
||||
repo = headRepo)(issue.userName),
|
||||
base = Commit(
|
||||
sha = pullRequest.commitIdFrom,
|
||||
ref = pullRequest.branch,
|
||||
repo = baseRepo)(issue.userName),
|
||||
mergeable = None, // TODO: need check mergeable.
|
||||
merged = mergedComment.isDefined,
|
||||
merged_at = mergedComment.map { case (comment, _) => comment.registeredDate },
|
||||
merged_by = mergedComment.map { case (_, account) => ApiUser(account) },
|
||||
title = issue.title,
|
||||
body = issue.content.getOrElse(""),
|
||||
user = user,
|
||||
labels = labels,
|
||||
assignee = assignee
|
||||
head = Commit(sha = pullRequest.commitIdTo, ref = pullRequest.requestBranch, repo = headRepo)(issue.userName),
|
||||
base = Commit(sha = pullRequest.commitIdFrom, ref = pullRequest.branch, repo = baseRepo)(issue.userName),
|
||||
mergeable = None, // TODO: need check mergeable.
|
||||
merged = mergedComment.isDefined,
|
||||
merged_at = mergedComment.map { case (comment, _) => comment.registeredDate },
|
||||
merged_by = mergedComment.map { case (_, account) => ApiUser(account) },
|
||||
title = issue.title,
|
||||
body = issue.content.getOrElse(""),
|
||||
user = user,
|
||||
labels = labels,
|
||||
assignee = assignee
|
||||
)
|
||||
|
||||
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}" }
|
||||
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 user = repo.owner
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,10 @@ case class ApiPullRequestReviewComment(
|
||||
// "original_commit_id": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c",
|
||||
user: ApiUser,
|
||||
body: String, // "Maybe you should use more emojji on this line.",
|
||||
created_at: Date, // "2015-05-05T23:40:27Z",
|
||||
created_at: Date, // "2015-05-05T23:40:27Z",
|
||||
updated_at: Date // "2015-05-05T23:40:27Z",
|
||||
)(repositoryName:RepositoryName, issueId: Int) extends FieldSerializable {
|
||||
)(repositoryName: RepositoryName, issueId: Int)
|
||||
extends FieldSerializable {
|
||||
// "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments/29724692",
|
||||
val url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/pulls/comments/${id}")
|
||||
// "html_url": "https://github.com/baxterthehacker/public-repo/pull/1#discussion_r29724692",
|
||||
@@ -40,22 +41,28 @@ case class ApiPullRequestReviewComment(
|
||||
"href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1"
|
||||
}
|
||||
}
|
||||
*/
|
||||
*/
|
||||
val _links = Map(
|
||||
"self" -> Map("href" -> url),
|
||||
"html" -> Map("href" -> html_url),
|
||||
"pull_request" -> Map("href" -> pull_request_url))
|
||||
"pull_request" -> Map("href" -> pull_request_url)
|
||||
)
|
||||
}
|
||||
|
||||
object ApiPullRequestReviewComment{
|
||||
def apply(comment: CommitComment, commentedUser: ApiUser, repositoryName: RepositoryName, issueId: Int): ApiPullRequestReviewComment =
|
||||
object ApiPullRequestReviewComment {
|
||||
def apply(
|
||||
comment: CommitComment,
|
||||
commentedUser: ApiUser,
|
||||
repositoryName: RepositoryName,
|
||||
issueId: Int
|
||||
): ApiPullRequestReviewComment =
|
||||
new ApiPullRequestReviewComment(
|
||||
id = comment.commentId,
|
||||
path = comment.fileName.getOrElse(""),
|
||||
commit_id = comment.commitId,
|
||||
user = commentedUser,
|
||||
body = comment.content,
|
||||
created_at = comment.registeredDate,
|
||||
updated_at = comment.updatedDate
|
||||
)(repositoryName, issueId)
|
||||
id = comment.commentId,
|
||||
path = comment.fileName.getOrElse(""),
|
||||
commit_id = comment.commitId,
|
||||
user = commentedUser,
|
||||
body = comment.content,
|
||||
created_at = comment.registeredDate,
|
||||
updated_at = comment.updatedDate
|
||||
)(repositoryName, issueId)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,5 @@ import gitbucket.core.model.Account
|
||||
case class ApiPusher(name: String, email: String)
|
||||
|
||||
object ApiPusher {
|
||||
def apply(user: Account): ApiPusher = ApiPusher(
|
||||
name = user.userName,
|
||||
email = user.mailAddress)
|
||||
}
|
||||
def apply(user: Account): ApiPusher = ApiPusher(name = user.userName, email = user.mailAddress)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package gitbucket.core.api
|
||||
import gitbucket.core.model.{Account, Repository}
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
|
||||
|
||||
// https://developer.github.com/v3/repos/
|
||||
case class ApiRepository(
|
||||
name: String,
|
||||
@@ -13,56 +12,58 @@ case class ApiRepository(
|
||||
forks: Int,
|
||||
`private`: Boolean,
|
||||
default_branch: String,
|
||||
owner: ApiUser)(urlIsHtmlUrl: Boolean) {
|
||||
owner: ApiUser
|
||||
)(urlIsHtmlUrl: Boolean) {
|
||||
val forks_count = forks
|
||||
val watchers_count = watchers
|
||||
val url = if(urlIsHtmlUrl){
|
||||
val url = if (urlIsHtmlUrl) {
|
||||
ApiPath(s"/${full_name}")
|
||||
} else {
|
||||
ApiPath(s"/api/v3/repos/${full_name}")
|
||||
}
|
||||
val http_url = ApiPath(s"/git/${full_name}.git")
|
||||
val http_url = ApiPath(s"/git/${full_name}.git")
|
||||
val clone_url = ApiPath(s"/git/${full_name}.git")
|
||||
val html_url = ApiPath(s"/${full_name}")
|
||||
val ssh_url = Some(SshPath(s":${full_name}.git"))
|
||||
val html_url = ApiPath(s"/${full_name}")
|
||||
val ssh_url = Some(SshPath(s":${full_name}.git"))
|
||||
}
|
||||
|
||||
object ApiRepository{
|
||||
object ApiRepository {
|
||||
def apply(
|
||||
repository: Repository,
|
||||
owner: ApiUser,
|
||||
forkedCount: Int =0,
|
||||
watchers: Int = 0,
|
||||
urlIsHtmlUrl: Boolean = false): ApiRepository =
|
||||
repository: Repository,
|
||||
owner: ApiUser,
|
||||
forkedCount: Int = 0,
|
||||
watchers: Int = 0,
|
||||
urlIsHtmlUrl: Boolean = false
|
||||
): ApiRepository =
|
||||
ApiRepository(
|
||||
name = repository.repositoryName,
|
||||
full_name = s"${repository.userName}/${repository.repositoryName}",
|
||||
description = repository.description.getOrElse(""),
|
||||
watchers = watchers,
|
||||
forks = forkedCount,
|
||||
`private` = repository.isPrivate,
|
||||
name = repository.repositoryName,
|
||||
full_name = s"${repository.userName}/${repository.repositoryName}",
|
||||
description = repository.description.getOrElse(""),
|
||||
watchers = watchers,
|
||||
forks = forkedCount,
|
||||
`private` = repository.isPrivate,
|
||||
default_branch = repository.defaultBranch,
|
||||
owner = owner
|
||||
owner = owner
|
||||
)(urlIsHtmlUrl)
|
||||
|
||||
def apply(repositoryInfo: RepositoryInfo, owner: ApiUser): ApiRepository =
|
||||
ApiRepository(repositoryInfo.repository, owner, forkedCount=repositoryInfo.forkedCount)
|
||||
ApiRepository(repositoryInfo.repository, owner, forkedCount = repositoryInfo.forkedCount)
|
||||
|
||||
def apply(repositoryInfo: RepositoryInfo, owner: Account): ApiRepository =
|
||||
this(repositoryInfo.repository, ApiUser(owner))
|
||||
|
||||
def forWebhookPayload(repositoryInfo: RepositoryInfo, owner: ApiUser): ApiRepository =
|
||||
ApiRepository(repositoryInfo.repository, owner, forkedCount=repositoryInfo.forkedCount, urlIsHtmlUrl=true)
|
||||
ApiRepository(repositoryInfo.repository, owner, forkedCount = repositoryInfo.forkedCount, urlIsHtmlUrl = true)
|
||||
|
||||
def forDummyPayload(owner: ApiUser): ApiRepository =
|
||||
ApiRepository(
|
||||
name = "dummy",
|
||||
full_name = s"${owner.login}/dummy",
|
||||
description = "",
|
||||
watchers = 0,
|
||||
forks = 0,
|
||||
`private` = false,
|
||||
name = "dummy",
|
||||
full_name = s"${owner.login}/dummy",
|
||||
description = "",
|
||||
watchers = 0,
|
||||
forks = 0,
|
||||
`private` = false,
|
||||
default_branch = "master",
|
||||
owner = owner
|
||||
owner = owner
|
||||
)(true)
|
||||
}
|
||||
|
||||
@@ -4,16 +4,10 @@ import gitbucket.core.model.Account
|
||||
|
||||
import java.util.Date
|
||||
|
||||
|
||||
case class ApiUser(
|
||||
login: String,
|
||||
email: String,
|
||||
`type`: String,
|
||||
site_admin: Boolean,
|
||||
created_at: Date) {
|
||||
val url = ApiPath(s"/api/v3/users/${login}")
|
||||
val html_url = ApiPath(s"/${login}")
|
||||
val avatar_url = ApiPath(s"/${login}/_avatar")
|
||||
case class ApiUser(login: String, email: String, `type`: String, site_admin: Boolean, created_at: Date) {
|
||||
val url = ApiPath(s"/api/v3/users/${login}")
|
||||
val html_url = ApiPath(s"/${login}")
|
||||
val avatar_url = ApiPath(s"/${login}/_avatar")
|
||||
// val followers_url = ApiPath(s"/api/v3/users/${login}/followers")
|
||||
// val following_url = ApiPath(s"/api/v3/users/${login}/following{/other_user}")
|
||||
// val gists_url = ApiPath(s"/api/v3/users/${login}/gists{/gist_id}")
|
||||
@@ -25,12 +19,11 @@ case class ApiUser(
|
||||
// val received_events_url = ApiPath(s"/api/v3/users/${login}/received_events")
|
||||
}
|
||||
|
||||
|
||||
object ApiUser{
|
||||
object ApiUser {
|
||||
def apply(user: Account): ApiUser = ApiUser(
|
||||
login = user.userName,
|
||||
email = user.mailAddress,
|
||||
`type` = if(user.isGroupAccount){ "Organization" } else { "User" },
|
||||
login = user.userName,
|
||||
email = user.mailAddress,
|
||||
`type` = if (user.isGroupAccount) { "Organization" } else { "User" },
|
||||
site_admin = user.isAdmin,
|
||||
created_at = user.registeredDate
|
||||
)
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package gitbucket.core.api
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/issues/labels/#create-a-label
|
||||
* api form
|
||||
*/
|
||||
* https://developer.github.com/v3/issues/labels/#create-a-label
|
||||
* api form
|
||||
*/
|
||||
case class CreateALabel(
|
||||
name: String,
|
||||
color: String
|
||||
name: String,
|
||||
color: String
|
||||
) {
|
||||
def isValid: Boolean = {
|
||||
name.length<=100 &&
|
||||
!name.startsWith("_") &&
|
||||
!name.startsWith("-") &&
|
||||
color.length==6 &&
|
||||
color.matches("[a-fA-F0-9+_.]+")
|
||||
name.length <= 100 &&
|
||||
!name.startsWith("_") &&
|
||||
!name.startsWith("-") &&
|
||||
color.length == 6 &&
|
||||
color.matches("[a-fA-F0-9+_.]+")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,15 @@ package gitbucket.core.api
|
||||
* api form
|
||||
*/
|
||||
case class CreateARepository(
|
||||
name: String,
|
||||
description: Option[String],
|
||||
`private`: Boolean = false,
|
||||
auto_init: Boolean = false
|
||||
name: String,
|
||||
description: Option[String],
|
||||
`private`: Boolean = false,
|
||||
auto_init: Boolean = false
|
||||
) {
|
||||
def isValid: Boolean = {
|
||||
name.length <= 100 &&
|
||||
name.matches("[a-zA-Z0-9\\-\\+_.]+") &&
|
||||
!name.startsWith("_") &&
|
||||
!name.startsWith("-")
|
||||
name.matches("[a-zA-Z0-9\\-\\+_.]+") &&
|
||||
!name.startsWith("_") &&
|
||||
!name.startsWith("-")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ case class CreateAStatus(
|
||||
) {
|
||||
def isValid: Boolean = {
|
||||
CommitState.valueOf(state).isDefined &&
|
||||
// only http
|
||||
target_url.forall(f => "\\Ahttps?://".r.findPrefixOf(f).isDefined && f.length < 255) &&
|
||||
context.forall(f => f.length < 255) &&
|
||||
description.forall(f => f.length < 1000)
|
||||
// only http
|
||||
target_url.forall(f => "\\Ahttps?://".r.findPrefixOf(f).isDefined && f.length < 255) &&
|
||||
context.forall(f => f.length < 255) &&
|
||||
description.forall(f => f.length < 1000)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package gitbucket.core.api
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/issues/#create-an-issue
|
||||
*/
|
||||
* https://developer.github.com/v3/issues/#create-an-issue
|
||||
*/
|
||||
case class CreateAnIssue(
|
||||
title: String,
|
||||
body: Option[String],
|
||||
assignees: List[String],
|
||||
milestone: Option[Int],
|
||||
labels: List[String])
|
||||
title: String,
|
||||
body: Option[String],
|
||||
assignees: List[String],
|
||||
milestone: Option[Int],
|
||||
labels: List[String]
|
||||
)
|
||||
|
||||
@@ -15,10 +15,13 @@ object JsonFormat {
|
||||
|
||||
val parserISO = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
|
||||
val jsonFormats = Serialization.formats(NoTypeHints) + new CustomSerializer[Date](format =>
|
||||
(
|
||||
{ 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)) }
|
||||
val jsonFormats = Serialization.formats(NoTypeHints) + new CustomSerializer[Date](
|
||||
format =>
|
||||
(
|
||||
{
|
||||
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)) }
|
||||
)
|
||||
) + FieldSerializer[ApiUser]() +
|
||||
FieldSerializer[ApiPullRequest]() +
|
||||
@@ -41,19 +44,31 @@ object JsonFormat {
|
||||
FieldSerializer[ApiCommits.File]() +
|
||||
ApiBranchProtection.enforcementLevelSerializer
|
||||
|
||||
def apiPathSerializer(c: Context) = new CustomSerializer[ApiPath](_ => ({
|
||||
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 ApiPath(path) => JString(c.baseUrl + path)
|
||||
}))
|
||||
def apiPathSerializer(c: Context) =
|
||||
new CustomSerializer[ApiPath](
|
||||
_ =>
|
||||
({
|
||||
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 ApiPath(path) => JString(c.baseUrl + path)
|
||||
})
|
||||
)
|
||||
|
||||
def sshPathSerializer(c: Context) = new CustomSerializer[SshPath](_ => ({
|
||||
case JString(s) if c.sshUrl.exists(sshUrl => s.startsWith(sshUrl)) => SshPath(s.substring(c.sshUrl.get.length))
|
||||
case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath")
|
||||
}, {
|
||||
case SshPath(path) => c.sshUrl.map { sshUrl => JString(sshUrl + path) } getOrElse JNothing
|
||||
}))
|
||||
def sshPathSerializer(c: Context) =
|
||||
new CustomSerializer[SshPath](
|
||||
_ =>
|
||||
({
|
||||
case JString(s) if c.sshUrl.exists(sshUrl => s.startsWith(sshUrl)) =>
|
||||
SshPath(s.substring(c.sshUrl.get.length))
|
||||
case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath")
|
||||
}, {
|
||||
case SshPath(path) =>
|
||||
c.sshUrl.map { sshUrl =>
|
||||
JString(sshUrl + path)
|
||||
} getOrElse JNothing
|
||||
})
|
||||
)
|
||||
|
||||
/**
|
||||
* convert object to json string
|
||||
|
||||
@@ -16,77 +16,133 @@ import org.scalatra.i18n.Messages
|
||||
import org.scalatra.BadRequest
|
||||
import org.scalatra.forms._
|
||||
|
||||
class AccountController extends AccountControllerBase
|
||||
with AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
|
||||
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
|
||||
with AccessTokenService with WebHookService with PrioritiesService with RepositoryCreationService
|
||||
|
||||
class AccountController
|
||||
extends AccountControllerBase
|
||||
with AccountService
|
||||
with RepositoryService
|
||||
with ActivityService
|
||||
with WikiService
|
||||
with LabelsService
|
||||
with SshKeyService
|
||||
with OneselfAuthenticator
|
||||
with UsersAuthenticator
|
||||
with GroupManagerAuthenticator
|
||||
with ReadableUsersAuthenticator
|
||||
with AccessTokenService
|
||||
with WebHookService
|
||||
with PrioritiesService
|
||||
with RepositoryCreationService
|
||||
|
||||
trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
self: AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
|
||||
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
|
||||
with AccessTokenService with WebHookService with PrioritiesService with RepositoryCreationService =>
|
||||
self: AccountService
|
||||
with RepositoryService
|
||||
with ActivityService
|
||||
with WikiService
|
||||
with LabelsService
|
||||
with SshKeyService
|
||||
with OneselfAuthenticator
|
||||
with UsersAuthenticator
|
||||
with GroupManagerAuthenticator
|
||||
with ReadableUsersAuthenticator
|
||||
with AccessTokenService
|
||||
with WebHookService
|
||||
with PrioritiesService
|
||||
with RepositoryCreationService =>
|
||||
|
||||
case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String,
|
||||
description: Option[String], url: Option[String], fileId: Option[String])
|
||||
case class AccountNewForm(
|
||||
userName: String,
|
||||
password: String,
|
||||
fullName: String,
|
||||
mailAddress: String,
|
||||
description: Option[String],
|
||||
url: Option[String],
|
||||
fileId: Option[String]
|
||||
)
|
||||
|
||||
case class AccountEditForm(password: Option[String], fullName: String, mailAddress: String,
|
||||
description: Option[String], url: Option[String], fileId: Option[String], clearImage: Boolean)
|
||||
case class AccountEditForm(
|
||||
password: Option[String],
|
||||
fullName: String,
|
||||
mailAddress: String,
|
||||
description: Option[String],
|
||||
url: Option[String],
|
||||
fileId: Option[String],
|
||||
clearImage: Boolean
|
||||
)
|
||||
|
||||
case class SshKeyForm(title: String, publicKey: String)
|
||||
|
||||
case class PersonalTokenForm(note: String)
|
||||
|
||||
val newForm = mapping(
|
||||
"userName" -> trim(label("User name" , text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"password" -> trim(label("Password" , text(required, maxlength(20), password))),
|
||||
"fullName" -> trim(label("Full Name" , text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress()))),
|
||||
"description" -> trim(label("bio" , optional(text()))),
|
||||
"url" -> trim(label("URL" , optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" , optional(text())))
|
||||
"userName" -> trim(label("User name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"password" -> trim(label("Password", text(required, maxlength(20), password))),
|
||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
||||
"description" -> trim(label("bio", optional(text()))),
|
||||
"url" -> trim(label("URL", optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID", optional(text())))
|
||||
)(AccountNewForm.apply)
|
||||
|
||||
val editForm = mapping(
|
||||
"password" -> trim(label("Password" , optional(text(maxlength(20), password)))),
|
||||
"fullName" -> trim(label("Full Name" , text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address" , text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||
"description" -> trim(label("bio" , optional(text()))),
|
||||
"url" -> trim(label("URL" , optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" , optional(text()))),
|
||||
"clearImage" -> trim(label("Clear image" , boolean()))
|
||||
"password" -> trim(label("Password", optional(text(maxlength(20), password)))),
|
||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||
"description" -> trim(label("bio", optional(text()))),
|
||||
"url" -> trim(label("URL", optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID", optional(text()))),
|
||||
"clearImage" -> trim(label("Clear image", boolean()))
|
||||
)(AccountEditForm.apply)
|
||||
|
||||
val sshKeyForm = mapping(
|
||||
"title" -> trim(label("Title", text(required, maxlength(100)))),
|
||||
"publicKey" -> trim2(label("Key" , text(required, validPublicKey)))
|
||||
"title" -> trim(label("Title", text(required, maxlength(100)))),
|
||||
"publicKey" -> trim2(label("Key", text(required, validPublicKey)))
|
||||
)(SshKeyForm.apply)
|
||||
|
||||
val personalTokenForm = mapping(
|
||||
"note" -> trim(label("Token", text(required, maxlength(100))))
|
||||
)(PersonalTokenForm.apply)
|
||||
|
||||
case class NewGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String)
|
||||
case class EditGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String, clearImage: Boolean)
|
||||
case class NewGroupForm(
|
||||
groupName: String,
|
||||
description: Option[String],
|
||||
url: Option[String],
|
||||
fileId: Option[String],
|
||||
members: String
|
||||
)
|
||||
case class EditGroupForm(
|
||||
groupName: String,
|
||||
description: Option[String],
|
||||
url: Option[String],
|
||||
fileId: Option[String],
|
||||
members: String,
|
||||
clearImage: Boolean
|
||||
)
|
||||
|
||||
val newGroupForm = mapping(
|
||||
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"groupName" -> trim(label("Group name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"description" -> trim(label("Group description", optional(text()))),
|
||||
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" ,optional(text()))),
|
||||
"members" -> trim(label("Members" ,text(required, members)))
|
||||
"url" -> trim(label("URL", optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID", optional(text()))),
|
||||
"members" -> trim(label("Members", text(required, members)))
|
||||
)(NewGroupForm.apply)
|
||||
|
||||
val editGroupForm = mapping(
|
||||
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier))),
|
||||
"groupName" -> trim(label("Group name", text(required, maxlength(100), identifier))),
|
||||
"description" -> trim(label("Group description", optional(text()))),
|
||||
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" ,optional(text()))),
|
||||
"members" -> trim(label("Members" ,text(required, members))),
|
||||
"clearImage" -> trim(label("Clear image" ,boolean()))
|
||||
"url" -> trim(label("URL", optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID", optional(text()))),
|
||||
"members" -> trim(label("Members", text(required, members))),
|
||||
"clearImage" -> trim(label("Clear image", boolean()))
|
||||
)(EditGroupForm.apply)
|
||||
|
||||
case class RepositoryCreationForm(owner: String, name: String, description: Option[String], isPrivate: Boolean, initOption: String, sourceUrl: Option[String])
|
||||
case class RepositoryCreationForm(
|
||||
owner: String,
|
||||
name: String,
|
||||
description: Option[String],
|
||||
isPrivate: Boolean,
|
||||
initOption: String,
|
||||
sourceUrl: Option[String]
|
||||
)
|
||||
case class ForkRepositoryForm(owner: String, name: String)
|
||||
|
||||
val newRepositoryForm = mapping(
|
||||
@@ -100,7 +156,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
val forkRepositoryForm = mapping(
|
||||
"owner" -> trim(label("Repository owner", text(required))),
|
||||
"name" -> trim(label("Repository name", text(required)))
|
||||
"name" -> trim(label("Repository name", text(required)))
|
||||
)(ForkRepositoryForm.apply)
|
||||
|
||||
case class AccountForm(accountName: String)
|
||||
@@ -110,23 +166,30 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
)(AccountForm.apply)
|
||||
|
||||
// for account web hook url addition.
|
||||
case class AccountWebHookForm(url: String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])
|
||||
|
||||
def accountWebHookForm(update:Boolean) = mapping(
|
||||
"url" -> trim(label("url", text(required, accountWebHook(update)))),
|
||||
"events" -> accountWebhookEvents,
|
||||
"ctype" -> label("ctype", text()),
|
||||
"token" -> optional(trim(label("token", text(maxlength(100)))))
|
||||
)(
|
||||
(url, events, ctype, token) => AccountWebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
|
||||
case class AccountWebHookForm(
|
||||
url: String,
|
||||
events: Set[WebHook.Event],
|
||||
ctype: WebHookContentType,
|
||||
token: Option[String]
|
||||
)
|
||||
|
||||
def accountWebHookForm(update: Boolean) =
|
||||
mapping(
|
||||
"url" -> trim(label("url", text(required, accountWebHook(update)))),
|
||||
"events" -> accountWebhookEvents,
|
||||
"ctype" -> label("ctype", text()),
|
||||
"token" -> optional(trim(label("token", text(maxlength(100)))))
|
||||
)(
|
||||
(url, events, ctype, token) => AccountWebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
|
||||
)
|
||||
|
||||
/**
|
||||
* Provides duplication check for web hook url. duplicated from RepositorySettingsController.scala
|
||||
*/
|
||||
private def accountWebHook(needExists: Boolean): Constraint = new Constraint(){
|
||||
* Provides duplication check for web hook url. duplicated from RepositorySettingsController.scala
|
||||
*/
|
||||
private def accountWebHook(needExists: Boolean): Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if(getAccountWebHook(params("userName"), value).isDefined != needExists){
|
||||
Some(if(needExists){
|
||||
if (getAccountWebHook(params("userName"), value).isDefined != needExists) {
|
||||
Some(if (needExists) {
|
||||
"URL had not been registered yet."
|
||||
} else {
|
||||
"URL had been registered already."
|
||||
@@ -136,21 +199,20 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
private def accountWebhookEvents = new ValueType[Set[WebHook.Event]]{
|
||||
private def accountWebhookEvents = new ValueType[Set[WebHook.Event]] {
|
||||
def convert(name: String, params: Map[String, Seq[String]], messages: Messages): Set[WebHook.Event] = {
|
||||
WebHook.Event.values.flatMap { t =>
|
||||
params.optionValue(name + "." + t.name).map(_ => t)
|
||||
}.toSet
|
||||
}
|
||||
def validate(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[(String, String)] =
|
||||
if(convert(name, params, messages).isEmpty){
|
||||
if (convert(name, params, messages).isEmpty) {
|
||||
Seq(name -> messages("error.required").format(name))
|
||||
} else {
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Displays user information.
|
||||
*/
|
||||
@@ -160,24 +222,41 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
params.getOrElse("tab", "repositories") match {
|
||||
// Public Activity
|
||||
case "activity" =>
|
||||
gitbucket.core.account.html.activity(account,
|
||||
if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||
getActivitiesByUser(userName, true))
|
||||
gitbucket.core.account.html.activity(
|
||||
account,
|
||||
if (account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||
getActivitiesByUser(userName, true)
|
||||
)
|
||||
|
||||
// Members
|
||||
case "members" if(account.isGroupAccount) => {
|
||||
case "members" if (account.isGroupAccount) => {
|
||||
val members = getGroupMembers(account.userName)
|
||||
gitbucket.core.account.html.members(account, members,
|
||||
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager }))
|
||||
gitbucket.core.account.html.members(
|
||||
account,
|
||||
members,
|
||||
context.loginAccount.exists(
|
||||
x =>
|
||||
members.exists { member =>
|
||||
member.userName == x.userName && member.isManager
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Repositories
|
||||
case _ => {
|
||||
val members = getGroupMembers(account.userName)
|
||||
gitbucket.core.account.html.repositories(account,
|
||||
if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||
gitbucket.core.account.html.repositories(
|
||||
account,
|
||||
if (account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||
getVisibleRepositories(context.loginAccount, Some(userName)),
|
||||
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager }))
|
||||
context.loginAccount.exists(
|
||||
x =>
|
||||
members.exists { member =>
|
||||
member.userName == x.userName && member.isManager
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
@@ -189,24 +268,28 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
helper.xml.feed(getActivitiesByUser(userName, true))
|
||||
}
|
||||
|
||||
get("/:userName/_avatar"){
|
||||
get("/:userName/_avatar") {
|
||||
val userName = params("userName")
|
||||
contentType = "image/png"
|
||||
getAccountByUserName(userName).flatMap{ account =>
|
||||
response.setDateHeader("Last-Modified", account.updatedDate.getTime)
|
||||
account.image.map{ image =>
|
||||
Some(RawData(FileUtil.getMimeType(image), new java.io.File(getUserUploadDir(userName), image)))
|
||||
}.getOrElse{
|
||||
if (account.isGroupAccount) {
|
||||
TextAvatarUtil.textGroupAvatar(account.fullName)
|
||||
} else {
|
||||
TextAvatarUtil.textAvatar(account.fullName)
|
||||
}
|
||||
getAccountByUserName(userName)
|
||||
.flatMap { account =>
|
||||
response.setDateHeader("Last-Modified", account.updatedDate.getTime)
|
||||
account.image
|
||||
.map { image =>
|
||||
Some(RawData(FileUtil.getMimeType(image), new java.io.File(getUserUploadDir(userName), image)))
|
||||
}
|
||||
.getOrElse {
|
||||
if (account.isGroupAccount) {
|
||||
TextAvatarUtil.textGroupAvatar(account.fullName)
|
||||
} else {
|
||||
TextAvatarUtil.textAvatar(account.fullName)
|
||||
}
|
||||
}
|
||||
}
|
||||
.getOrElse {
|
||||
response.setHeader("Cache-Control", "max-age=3600")
|
||||
Thread.currentThread.getContextClassLoader.getResourceAsStream("noimage.png")
|
||||
}
|
||||
}.getOrElse{
|
||||
response.setHeader("Cache-Control", "max-age=3600")
|
||||
Thread.currentThread.getContextClassLoader.getResourceAsStream("noimage.png")
|
||||
}
|
||||
}
|
||||
|
||||
get("/:userName/_edit")(oneselfOnly {
|
||||
@@ -219,12 +302,15 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
post("/:userName/_edit", editForm)(oneselfOnly { form =>
|
||||
val userName = params("userName")
|
||||
getAccountByUserName(userName).map { account =>
|
||||
updateAccount(account.copy(
|
||||
password = form.password.map(sha1).getOrElse(account.password),
|
||||
fullName = form.fullName,
|
||||
mailAddress = form.mailAddress,
|
||||
description = form.description,
|
||||
url = form.url))
|
||||
updateAccount(
|
||||
account.copy(
|
||||
password = form.password.map(sha1).getOrElse(account.password),
|
||||
fullName = form.fullName,
|
||||
mailAddress = form.mailAddress,
|
||||
description = form.description,
|
||||
url = form.url
|
||||
)
|
||||
)
|
||||
|
||||
updateImage(userName, form.fileId, form.clearImage)
|
||||
flash += "info" -> "Account information has been updated."
|
||||
@@ -236,11 +322,12 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
get("/:userName/_delete")(oneselfOnly {
|
||||
val userName = params("userName")
|
||||
|
||||
getAccountByUserName(userName, true).map { account =>
|
||||
if(isLastAdministrator(account)){
|
||||
flash += "error" -> "Account can't be removed because this is last one administrator."
|
||||
redirect(s"/${userName}/_edit")
|
||||
} else {
|
||||
getAccountByUserName(userName, true).map {
|
||||
account =>
|
||||
if (isLastAdministrator(account)) {
|
||||
flash += "error" -> "Account can't be removed because this is last one administrator."
|
||||
redirect(s"/${userName}/_edit")
|
||||
} else {
|
||||
// // Remove repositories
|
||||
// getRepositoryNamesOfUser(userName).foreach { repositoryName =>
|
||||
// deleteRepository(userName, repositoryName)
|
||||
@@ -248,16 +335,16 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
// FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
|
||||
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
|
||||
// }
|
||||
// Remove from GROUP_MEMBER and COLLABORATOR
|
||||
removeUserRelatedData(userName)
|
||||
updateAccount(account.copy(isRemoved = true))
|
||||
// Remove from GROUP_MEMBER and COLLABORATOR
|
||||
removeUserRelatedData(userName)
|
||||
updateAccount(account.copy(isRemoved = true))
|
||||
|
||||
// call hooks
|
||||
PluginRegistry().getAccountHooks.foreach(_.deleted(userName))
|
||||
// call hooks
|
||||
PluginRegistry().getAccountHooks.foreach(_.deleted(userName))
|
||||
|
||||
session.invalidate
|
||||
redirect("/")
|
||||
}
|
||||
session.invalidate
|
||||
redirect("/")
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -286,9 +373,9 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
getAccountByUserName(userName).map { x =>
|
||||
var tokens = getAccessTokens(x.userName)
|
||||
val generatedToken = flash.get("generatedToken") match {
|
||||
case Some((tokenId:Int, token:String)) => {
|
||||
case Some((tokenId: Int, token: String)) => {
|
||||
val gt = tokens.find(_.accessTokenId == tokenId)
|
||||
gt.map{ t =>
|
||||
gt.map { t =>
|
||||
tokens = tokens.filterNot(_ == t)
|
||||
(t, token)
|
||||
}
|
||||
@@ -359,8 +446,9 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
get("/:userName/_hooks/edit")(oneselfOnly {
|
||||
val userName = params("userName")
|
||||
getAccountByUserName(userName).flatMap { account =>
|
||||
getAccountWebHook(userName, params("url")).map { case (webhook, events) =>
|
||||
html.edithook(webhook, events, account, false)
|
||||
getAccountWebHook(userName, params("url")).map {
|
||||
case (webhook, events) =>
|
||||
html.edithook(webhook, events, account, false)
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
@@ -386,7 +474,9 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
import org.apache.http.util.EntityUtils
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
def _headers(h: Array[org.apache.http.Header]): Array[Array[String]] = h.map { h => Array(h.getName, h.getValue) }
|
||||
def _headers(h: Array[org.apache.http.Header]): Array[Array[String]] = h.map { h =>
|
||||
Array(h.getName, h.getValue)
|
||||
}
|
||||
|
||||
val userName = params("userName")
|
||||
val url = params("url")
|
||||
@@ -400,31 +490,49 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
val (webHook, json, reqFuture, resFuture) = callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload).head
|
||||
|
||||
val toErrorMap: PartialFunction[Throwable, Map[String,String]] = {
|
||||
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"-> (e.getClass + " "+ e.getMessage))
|
||||
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
||||
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" -> (e.getClass + " " + e.getMessage))
|
||||
}
|
||||
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(Map(
|
||||
"url" -> url,
|
||||
"request" -> Await.result(reqFuture.map(req => Map(
|
||||
"headers" -> _headers(req.getAllHeaders),
|
||||
"payload" -> json
|
||||
)).recover(toErrorMap), 20 seconds),
|
||||
"response" -> Await.result(resFuture.map(res => Map(
|
||||
"status" -> res.getStatusLine(),
|
||||
"body" -> EntityUtils.toString(res.getEntity()),
|
||||
"headers" -> _headers(res.getAllHeaders())
|
||||
)).recover(toErrorMap), 20 seconds)
|
||||
))
|
||||
org.json4s.jackson.Serialization.write(
|
||||
Map(
|
||||
"url" -> url,
|
||||
"request" -> Await.result(
|
||||
reqFuture
|
||||
.map(
|
||||
req =>
|
||||
Map(
|
||||
"headers" -> _headers(req.getAllHeaders),
|
||||
"payload" -> json
|
||||
)
|
||||
)
|
||||
.recover(toErrorMap),
|
||||
20 seconds
|
||||
),
|
||||
"response" -> Await.result(
|
||||
resFuture
|
||||
.map(
|
||||
res =>
|
||||
Map(
|
||||
"status" -> res.getStatusLine(),
|
||||
"body" -> EntityUtils.toString(res.getEntity()),
|
||||
"headers" -> _headers(res.getAllHeaders())
|
||||
)
|
||||
)
|
||||
.recover(toErrorMap),
|
||||
20 seconds
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
get("/register"){
|
||||
if(context.settings.allowAccountRegistration){
|
||||
if(context.loginAccount.isDefined){
|
||||
get("/register") {
|
||||
if (context.settings.allowAccountRegistration) {
|
||||
if (context.loginAccount.isDefined) {
|
||||
redirect("/")
|
||||
} else {
|
||||
html.register()
|
||||
@@ -432,9 +540,17 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
} else NotFound()
|
||||
}
|
||||
|
||||
post("/register", newForm){ form =>
|
||||
if(context.settings.allowAccountRegistration){
|
||||
createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, false, form.description, form.url)
|
||||
post("/register", newForm) { form =>
|
||||
if (context.settings.allowAccountRegistration) {
|
||||
createAccount(
|
||||
form.userName,
|
||||
sha1(form.password),
|
||||
form.fullName,
|
||||
form.mailAddress,
|
||||
false,
|
||||
form.description,
|
||||
form.url
|
||||
)
|
||||
updateImage(form.userName, form.fileId, false)
|
||||
redirect("/signin")
|
||||
} else NotFound()
|
||||
@@ -446,17 +562,23 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
post("/groups/new", newGroupForm)(usersOnly { form =>
|
||||
createGroup(form.groupName, form.description, form.url)
|
||||
updateGroupMembers(form.groupName, form.members.split(",").map {
|
||||
_.split(":") match {
|
||||
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||
}
|
||||
}.toList)
|
||||
updateGroupMembers(
|
||||
form.groupName,
|
||||
form.members
|
||||
.split(",")
|
||||
.map {
|
||||
_.split(":") match {
|
||||
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||
}
|
||||
}
|
||||
.toList
|
||||
)
|
||||
updateImage(form.groupName, form.fileId, false)
|
||||
redirect(s"/${form.groupName}")
|
||||
})
|
||||
|
||||
get("/:groupName/_editgroup")(managersOnly {
|
||||
defining(params("groupName")){ groupName =>
|
||||
defining(params("groupName")) { groupName =>
|
||||
getAccountByUserName(groupName, true).map { account =>
|
||||
html.editgroup(account, getGroupMembers(groupName), flash.get("info"))
|
||||
} getOrElse NotFound()
|
||||
@@ -464,13 +586,14 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
})
|
||||
|
||||
get("/:groupName/_deletegroup")(managersOnly {
|
||||
defining(params("groupName")){ groupName =>
|
||||
// Remove from GROUP_MEMBER
|
||||
updateGroupMembers(groupName, Nil)
|
||||
// Disable group
|
||||
getAccountByUserName(groupName, false).foreach { account =>
|
||||
updateGroup(groupName, account.description, account.url, true)
|
||||
}
|
||||
defining(params("groupName")) {
|
||||
groupName =>
|
||||
// Remove from GROUP_MEMBER
|
||||
updateGroupMembers(groupName, Nil)
|
||||
// Disable group
|
||||
getAccountByUserName(groupName, false).foreach { account =>
|
||||
updateGroup(groupName, account.description, account.url, true)
|
||||
}
|
||||
// // Remove repositories
|
||||
// getRepositoryNamesOfUser(groupName).foreach { repositoryName =>
|
||||
// deleteRepository(groupName, repositoryName)
|
||||
@@ -483,16 +606,23 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
})
|
||||
|
||||
post("/:groupName/_editgroup", editGroupForm)(managersOnly { form =>
|
||||
defining(params("groupName"), form.members.split(",").map {
|
||||
_.split(":") match {
|
||||
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||
}
|
||||
}.toList){ case (groupName, members) =>
|
||||
getAccountByUserName(groupName, true).map { account =>
|
||||
updateGroup(groupName, form.description, form.url, false)
|
||||
defining(
|
||||
params("groupName"),
|
||||
form.members
|
||||
.split(",")
|
||||
.map {
|
||||
_.split(":") match {
|
||||
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||
}
|
||||
}
|
||||
.toList
|
||||
) {
|
||||
case (groupName, members) =>
|
||||
getAccountByUserName(groupName, true).map { account =>
|
||||
updateGroup(groupName, form.description, form.url, false)
|
||||
|
||||
// Update GROUP_MEMBER
|
||||
updateGroupMembers(form.groupName, members)
|
||||
// Update GROUP_MEMBER
|
||||
updateGroupMembers(form.groupName, members)
|
||||
// // Update COLLABORATOR for group repositories
|
||||
// getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
||||
// removeCollaborators(form.groupName, repositoryName)
|
||||
@@ -501,12 +631,12 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
// }
|
||||
// }
|
||||
|
||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||
|
||||
flash += "info" -> "Account information has been updated."
|
||||
redirect(s"/${groupName}/_editgroup")
|
||||
flash += "info" -> "Account information has been updated."
|
||||
redirect(s"/${groupName}/_editgroup")
|
||||
|
||||
} getOrElse NotFound()
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -521,9 +651,17 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
* Create new repository.
|
||||
*/
|
||||
post("/new", newRepositoryForm)(usersOnly { form =>
|
||||
LockUtil.lock(s"${form.owner}/${form.name}"){
|
||||
if(getRepository(form.owner, form.name).isEmpty){
|
||||
createRepository(context.loginAccount.get, form.owner, form.name, form.description, form.isPrivate, form.initOption, form.sourceUrl)
|
||||
LockUtil.lock(s"${form.owner}/${form.name}") {
|
||||
if (getRepository(form.owner, form.name).isEmpty) {
|
||||
createRepository(
|
||||
context.loginAccount.get,
|
||||
form.owner,
|
||||
form.name,
|
||||
form.description,
|
||||
form.isPrivate,
|
||||
form.initOption,
|
||||
form.sourceUrl
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,15 +670,20 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
})
|
||||
|
||||
get("/:owner/:repository/fork")(readableUsersOnly { repository =>
|
||||
if(repository.repository.options.allowFork){
|
||||
val loginAccount = context.loginAccount.get
|
||||
val loginUserName = loginAccount.userName
|
||||
val groups = getGroupsByUserName(loginUserName)
|
||||
if (repository.repository.options.allowFork) {
|
||||
val loginAccount = context.loginAccount.get
|
||||
val loginUserName = loginAccount.userName
|
||||
val groups = getGroupsByUserName(loginUserName)
|
||||
groups match {
|
||||
case _: List[String] =>
|
||||
val managerPermissions = groups.map { group =>
|
||||
val members = getGroupMembers(group)
|
||||
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager })
|
||||
context.loginAccount.exists(
|
||||
x =>
|
||||
members.exists { member =>
|
||||
member.userName == x.userName && member.isManager
|
||||
}
|
||||
)
|
||||
}
|
||||
helper.html.forkrepository(
|
||||
repository,
|
||||
@@ -552,13 +695,13 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
})
|
||||
|
||||
post("/:owner/:repository/fork", accountForm)(readableUsersOnly { (form, repository) =>
|
||||
if(repository.repository.options.allowFork){
|
||||
val loginAccount = context.loginAccount.get
|
||||
if (repository.repository.options.allowFork) {
|
||||
val loginAccount = context.loginAccount.get
|
||||
val loginUserName = loginAccount.userName
|
||||
val accountName = form.accountName
|
||||
val accountName = form.accountName
|
||||
|
||||
if (getRepository(accountName, repository.name).isDefined ||
|
||||
(accountName != loginUserName && !getGroupsByUserName(loginUserName).contains(accountName))) {
|
||||
(accountName != loginUserName && !getGroupsByUserName(loginUserName).contains(accountName))) {
|
||||
// redirect to the repository if repository already exists
|
||||
redirect(s"/${accountName}/${repository.name}")
|
||||
} else {
|
||||
@@ -570,42 +713,49 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
} else BadRequest()
|
||||
})
|
||||
|
||||
private def existsAccount: Constraint = new Constraint(){
|
||||
private def existsAccount: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if(getAccountByUserName(value).isEmpty) Some("User or group does not exist.") else None
|
||||
if (getAccountByUserName(value).isEmpty) Some("User or group does not exist.") else None
|
||||
}
|
||||
|
||||
private def uniqueRepository: Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, params: Map[String, Seq[String]], messages: Messages): Option[String] = {
|
||||
private def uniqueRepository: Constraint = new Constraint() {
|
||||
override def validate(
|
||||
name: String,
|
||||
value: String,
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
for {
|
||||
userName <- params.optionValue("owner")
|
||||
_ <- getRepositoryNamesOfUser(userName).find(_ == value)
|
||||
_ <- getRepositoryNamesOfUser(userName).find(_ == value)
|
||||
} yield {
|
||||
"Repository already exists."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def members: Constraint = new Constraint(){
|
||||
private def members: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
if(value.split(",").exists {
|
||||
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
||||
}) None else Some("Must select one manager at least.")
|
||||
if (value.split(",").exists {
|
||||
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
||||
}) None
|
||||
else Some("Must select one manager at least.")
|
||||
}
|
||||
}
|
||||
|
||||
private def validPublicKey: Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = SshUtil.str2PublicKey(value) match {
|
||||
case Some(_) if !getAllKeys().exists(_.publicKey == value) => None
|
||||
case _ => Some("Key is invalid.")
|
||||
}
|
||||
private def validPublicKey: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
SshUtil.str2PublicKey(value) match {
|
||||
case Some(_) if !getAllKeys().exists(_.publicKey == value) => None
|
||||
case _ => Some("Key is invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
private def validAccountName: Constraint = new Constraint(){
|
||||
private def validAccountName: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
getAccountByUserName(value) match {
|
||||
case Some(_) => None
|
||||
case None => Some("Invalid Group/User Account.")
|
||||
case None => Some("Invalid Group/User Account.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,31 +20,32 @@ import scala.collection.JavaConverters._
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration.Duration
|
||||
|
||||
class ApiController extends ApiControllerBase
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ProtectedBranchService
|
||||
with IssuesService
|
||||
with LabelsService
|
||||
with MilestonesService
|
||||
with PullRequestService
|
||||
with CommitsService
|
||||
with CommitStatusService
|
||||
with RepositoryCreationService
|
||||
with IssueCreationService
|
||||
with HandleCommentService
|
||||
with WebHookService
|
||||
with WebHookPullRequestService
|
||||
with WebHookIssueCommentService
|
||||
with WikiService
|
||||
with ActivityService
|
||||
with PrioritiesService
|
||||
with OwnerAuthenticator
|
||||
with UsersAuthenticator
|
||||
with GroupManagerAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
with ReadableUsersAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
class ApiController
|
||||
extends ApiControllerBase
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ProtectedBranchService
|
||||
with IssuesService
|
||||
with LabelsService
|
||||
with MilestonesService
|
||||
with PullRequestService
|
||||
with CommitsService
|
||||
with CommitStatusService
|
||||
with RepositoryCreationService
|
||||
with IssueCreationService
|
||||
with HandleCommentService
|
||||
with WebHookService
|
||||
with WebHookPullRequestService
|
||||
with WebHookIssueCommentService
|
||||
with WikiService
|
||||
with ActivityService
|
||||
with PrioritiesService
|
||||
with OwnerAuthenticator
|
||||
with UsersAuthenticator
|
||||
with GroupManagerAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
with ReadableUsersAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
|
||||
trait ApiControllerBase extends ControllerBase {
|
||||
self: RepositoryService
|
||||
@@ -68,22 +69,22 @@ trait ApiControllerBase extends ControllerBase {
|
||||
with WritableUsersAuthenticator =>
|
||||
|
||||
/**
|
||||
* 404 for non-implemented api
|
||||
*/
|
||||
* 404 for non-implemented api
|
||||
*/
|
||||
get("/api/v3/*") {
|
||||
NotFound()
|
||||
}
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/#root-endpoint
|
||||
*/
|
||||
* https://developer.github.com/v3/#root-endpoint
|
||||
*/
|
||||
get("/api/v3") {
|
||||
JsonFormat(ApiEndPoint())
|
||||
}
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/orgs/#get-an-organization
|
||||
*/
|
||||
* https://developer.github.com/v3/orgs/#get-an-organization
|
||||
*/
|
||||
get("/api/v3/orgs/:groupName") {
|
||||
getAccountByUserName(params("groupName")).filter(account => account.isGroupAccount).map { account =>
|
||||
JsonFormat(ApiUser(account))
|
||||
@@ -101,43 +102,59 @@ trait ApiControllerBase extends ControllerBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/repos/#list-organization-repositories
|
||||
*/
|
||||
* https://developer.github.com/v3/repos/#list-organization-repositories
|
||||
*/
|
||||
get("/api/v3/orgs/:orgName/repos") {
|
||||
JsonFormat(getVisibleRepositories(context.loginAccount, Some(params("orgName"))).map{ r => ApiRepository(r, getAccountByUserName(r.owner).get)})
|
||||
JsonFormat(getVisibleRepositories(context.loginAccount, Some(params("orgName"))).map { r =>
|
||||
ApiRepository(r, getAccountByUserName(r.owner).get)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/repos/#list-user-repositories
|
||||
*/
|
||||
get("/api/v3/users/:userName/repos") {
|
||||
JsonFormat(getVisibleRepositories(context.loginAccount, Some(params("userName"))).map{ r => ApiRepository(r, getAccountByUserName(r.owner).get)})
|
||||
JsonFormat(getVisibleRepositories(context.loginAccount, Some(params("userName"))).map { r =>
|
||||
ApiRepository(r, getAccountByUserName(r.owner).get)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* https://developer.github.com/v3/repos/branches/#list-branches
|
||||
*/
|
||||
get ("/api/v3/repos/:owner/:repo/branches")(referrersOnly { repository =>
|
||||
JsonFormat(JGitUtil.getBranches(
|
||||
owner = repository.owner,
|
||||
name = repository.name,
|
||||
defaultBranch = repository.repository.defaultBranch,
|
||||
origin = repository.repository.originUserName.isEmpty
|
||||
).map { br =>
|
||||
ApiBranchForList(br.name, ApiBranchCommit(br.commitId))
|
||||
})
|
||||
get("/api/v3/repos/:owner/:repo/branches")(referrersOnly { repository =>
|
||||
JsonFormat(
|
||||
JGitUtil
|
||||
.getBranches(
|
||||
owner = repository.owner,
|
||||
name = repository.name,
|
||||
defaultBranch = repository.repository.defaultBranch,
|
||||
origin = repository.repository.originUserName.isEmpty
|
||||
)
|
||||
.map { br =>
|
||||
ApiBranchForList(br.name, ApiBranchCommit(br.commitId))
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/repos/branches/#get-branch
|
||||
*/
|
||||
get ("/api/v3/repos/:owner/:repo/branches/*")(referrersOnly { repository =>
|
||||
* https://developer.github.com/v3/repos/branches/#get-branch
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repo/branches/*")(referrersOnly { repository =>
|
||||
//import gitbucket.core.api._
|
||||
(for{
|
||||
(for {
|
||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||
br <- getBranches(repository.owner, repository.name, repository.repository.defaultBranch, repository.repository.originUserName.isEmpty).find(_.name == branch)
|
||||
br <- getBranches(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
repository.repository.defaultBranch,
|
||||
repository.repository.originUserName.isEmpty
|
||||
).find(_.name == branch)
|
||||
} yield {
|
||||
val protection = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||
JsonFormat(ApiBranch(branch, ApiBranchCommit(br.commitId), ApiBranchProtection(protection))(RepositoryName(repository)))
|
||||
JsonFormat(
|
||||
ApiBranch(branch, ApiBranchCommit(br.commitId), ApiBranchProtection(protection))(RepositoryName(repository))
|
||||
)
|
||||
}) getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -157,65 +174,85 @@ trait ApiControllerBase extends ControllerBase {
|
||||
|
||||
val path = multiParams("splat").head match {
|
||||
case s if s.isEmpty => "."
|
||||
case s => s
|
||||
case s => s
|
||||
}
|
||||
val refStr = params.getOrElse("ref", repository.repository.defaultBranch)
|
||||
|
||||
using(Git.open(getRepositoryDir(params("owner"), params("repo")))){ git =>
|
||||
val fileList = getFileList(git, refStr, path)
|
||||
if (fileList.isEmpty) { // file or NotFound
|
||||
getFileInfo(git, refStr, path).flatMap(f => {
|
||||
val largeFile = params.get("large_file").exists(s => s.equals("true"))
|
||||
val content = getContentFromId(git, f.id, largeFile)
|
||||
request.getHeader("Accept") match {
|
||||
case "application/vnd.github.v3.raw" => {
|
||||
contentType = "application/vnd.github.v3.raw"
|
||||
content
|
||||
}
|
||||
case "application/vnd.github.v3.html" if isRenderable(f.name) => {
|
||||
contentType = "application/vnd.github.v3.html"
|
||||
content.map(c =>
|
||||
List(
|
||||
"<div data-path=\"", path, "\" id=\"file\">", "<article>",
|
||||
renderMarkup(path.split("/").toList, new String(c), refStr, repository, false, false, true).body,
|
||||
"</article>", "</div>"
|
||||
).mkString
|
||||
)
|
||||
}
|
||||
case "application/vnd.github.v3.html" => {
|
||||
contentType = "application/vnd.github.v3.html"
|
||||
content.map(c =>
|
||||
List(
|
||||
"<div data-path=\"", path, "\" id=\"file\">", "<div class=\"plain\">", "<pre>",
|
||||
play.twirl.api.HtmlFormat.escape(new String(c)).body,
|
||||
"</pre>", "</div>", "</div>"
|
||||
).mkString
|
||||
)
|
||||
}
|
||||
case _ =>
|
||||
Some(JsonFormat(ApiContents(f, RepositoryName(repository), content)))
|
||||
}
|
||||
}).getOrElse(NotFound())
|
||||
} else { // directory
|
||||
JsonFormat(fileList.map{f => ApiContents(f, RepositoryName(repository), None)})
|
||||
}
|
||||
using(Git.open(getRepositoryDir(params("owner"), params("repo")))) {
|
||||
git =>
|
||||
val fileList = getFileList(git, refStr, path)
|
||||
if (fileList.isEmpty) { // file or NotFound
|
||||
getFileInfo(git, refStr, path)
|
||||
.flatMap(f => {
|
||||
val largeFile = params.get("large_file").exists(s => s.equals("true"))
|
||||
val content = getContentFromId(git, f.id, largeFile)
|
||||
request.getHeader("Accept") match {
|
||||
case "application/vnd.github.v3.raw" => {
|
||||
contentType = "application/vnd.github.v3.raw"
|
||||
content
|
||||
}
|
||||
case "application/vnd.github.v3.html" if isRenderable(f.name) => {
|
||||
contentType = "application/vnd.github.v3.html"
|
||||
content.map(
|
||||
c =>
|
||||
List(
|
||||
"<div data-path=\"",
|
||||
path,
|
||||
"\" id=\"file\">",
|
||||
"<article>",
|
||||
renderMarkup(path.split("/").toList, new String(c), refStr, repository, false, false, true).body,
|
||||
"</article>",
|
||||
"</div>"
|
||||
).mkString
|
||||
)
|
||||
}
|
||||
case "application/vnd.github.v3.html" => {
|
||||
contentType = "application/vnd.github.v3.html"
|
||||
content.map(
|
||||
c =>
|
||||
List(
|
||||
"<div data-path=\"",
|
||||
path,
|
||||
"\" id=\"file\">",
|
||||
"<div class=\"plain\">",
|
||||
"<pre>",
|
||||
play.twirl.api.HtmlFormat.escape(new String(c)).body,
|
||||
"</pre>",
|
||||
"</div>",
|
||||
"</div>"
|
||||
).mkString
|
||||
)
|
||||
}
|
||||
case _ =>
|
||||
Some(JsonFormat(ApiContents(f, RepositoryName(repository), content)))
|
||||
}
|
||||
})
|
||||
.getOrElse(NotFound())
|
||||
} else { // directory
|
||||
JsonFormat(fileList.map { f =>
|
||||
ApiContents(f, RepositoryName(repository), None)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
* https://developer.github.com/v3/git/refs/#get-a-reference
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repo/git/refs/*") (referrersOnly { repository =>
|
||||
get("/api/v3/repos/:owner/:repo/git/refs/*")(referrersOnly { repository =>
|
||||
val revstr = multiParams("splat").head
|
||||
using(Git.open(getRepositoryDir(params("owner"), params("repo")))) { git =>
|
||||
val ref = git.getRepository().findRef(revstr)
|
||||
|
||||
if(ref != null){
|
||||
if (ref != null) {
|
||||
val sha = ref.getObjectId().name()
|
||||
JsonFormat(ApiRef(revstr, ApiObject(sha)))
|
||||
|
||||
} else {
|
||||
val refs = git.getRepository().getAllRefs().asScala
|
||||
val refs = git
|
||||
.getRepository()
|
||||
.getAllRefs()
|
||||
.asScala
|
||||
.collect { case (str, ref) if str.startsWith("refs/" + revstr) => ref }
|
||||
|
||||
JsonFormat(refs.map { ref =>
|
||||
@@ -229,7 +266,7 @@ trait ApiControllerBase extends ControllerBase {
|
||||
/**
|
||||
* https://developer.github.com/v3/repos/collaborators/#list-collaborators
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repo/collaborators") (referrersOnly { repository =>
|
||||
get("/api/v3/repos/:owner/:repo/collaborators")(referrersOnly { repository =>
|
||||
// TODO Should ApiUser take permission? getCollaboratorUserNames does not return owner group members.
|
||||
JsonFormat(getCollaboratorUserNames(params("owner"), params("repo")).map(u => ApiUser(getAccountByUserName(u).get)))
|
||||
})
|
||||
@@ -247,9 +284,9 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* List user's own repository
|
||||
* https://developer.github.com/v3/repos/#list-your-repositories
|
||||
*/
|
||||
get("/api/v3/user/repos")(usersOnly{
|
||||
JsonFormat(getVisibleRepositories(context.loginAccount, Option(context.loginAccount.get.userName)).map{
|
||||
r => ApiRepository(r, getAccountByUserName(r.owner).get)
|
||||
get("/api/v3/user/repos")(usersOnly {
|
||||
JsonFormat(getVisibleRepositories(context.loginAccount, Option(context.loginAccount.get.userName)).map { r =>
|
||||
ApiRepository(r, getAccountByUserName(r.owner).get)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -263,8 +300,15 @@ trait ApiControllerBase extends ControllerBase {
|
||||
data <- extractFromJsonBody[CreateARepository] if data.isValid
|
||||
} yield {
|
||||
LockUtil.lock(s"${owner}/${data.name}") {
|
||||
if(getRepository(owner, data.name).isEmpty){
|
||||
val f = createRepository(context.loginAccount.get, owner, data.name, data.description, data.`private`, data.auto_init)
|
||||
if (getRepository(owner, data.name).isEmpty) {
|
||||
val f = createRepository(
|
||||
context.loginAccount.get,
|
||||
owner,
|
||||
data.name,
|
||||
data.description,
|
||||
data.`private`,
|
||||
data.auto_init
|
||||
)
|
||||
Await.result(f, Duration.Inf)
|
||||
val repository = getRepository(owner, data.name).get
|
||||
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(owner).get)))
|
||||
@@ -288,8 +332,15 @@ trait ApiControllerBase extends ControllerBase {
|
||||
data <- extractFromJsonBody[CreateARepository] if data.isValid
|
||||
} yield {
|
||||
LockUtil.lock(s"${groupName}/${data.name}") {
|
||||
if(getRepository(groupName, data.name).isEmpty){
|
||||
val f = createRepository(context.loginAccount.get, groupName, data.name, data.description, data.`private`, data.auto_init)
|
||||
if (getRepository(groupName, data.name).isEmpty) {
|
||||
val f = createRepository(
|
||||
context.loginAccount.get,
|
||||
groupName,
|
||||
data.name,
|
||||
data.description,
|
||||
data.`private`,
|
||||
data.auto_init
|
||||
)
|
||||
Await.result(f, Duration.Inf)
|
||||
val repository = getRepository(groupName, data.name).get
|
||||
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(groupName).get)))
|
||||
@@ -308,13 +359,24 @@ trait ApiControllerBase extends ControllerBase {
|
||||
*/
|
||||
patch("/api/v3/repos/:owner/:repo/branches/*")(ownerOnly { repository =>
|
||||
import gitbucket.core.api._
|
||||
(for{
|
||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||
(for {
|
||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||
protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
|
||||
br <- getBranches(repository.owner, repository.name, repository.repository.defaultBranch, repository.repository.originUserName.isEmpty).find(_.name == branch)
|
||||
br <- getBranches(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
repository.repository.defaultBranch,
|
||||
repository.repository.originUserName.isEmpty
|
||||
).find(_.name == branch)
|
||||
} yield {
|
||||
if(protection.enabled){
|
||||
enableBranchProtection(repository.owner, repository.name, branch, protection.status.enforcement_level == ApiBranchProtection.Everyone, protection.status.contexts)
|
||||
if (protection.enabled) {
|
||||
enableBranchProtection(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
branch,
|
||||
protection.status.enforcement_level == ApiBranchProtection.Everyone,
|
||||
protection.status.contexts
|
||||
)
|
||||
} else {
|
||||
disableBranchProtection(repository.owner, repository.name, branch)
|
||||
}
|
||||
@@ -326,15 +388,15 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* @see https://developer.github.com/v3/rate_limit/#get-your-current-rate-limit-status
|
||||
* but not enabled.
|
||||
*/
|
||||
get("/api/v3/rate_limit"){
|
||||
get("/api/v3/rate_limit") {
|
||||
contentType = formats("json")
|
||||
// this message is same as github enterprise...
|
||||
org.scalatra.NotFound(ApiError("Rate limiting is not enabled."))
|
||||
}
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/issues/#list-issues-for-a-repository
|
||||
*/
|
||||
* https://developer.github.com/v3/issues/#list-issues-for-a-repository
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/issues")(referrersOnly { repository =>
|
||||
val page = IssueSearchCondition.page(request)
|
||||
// TODO: more api spec condition
|
||||
@@ -344,18 +406,20 @@ trait ApiControllerBase extends ControllerBase {
|
||||
val issues: List[(Issue, Account)] =
|
||||
searchIssueByApi(
|
||||
condition = condition,
|
||||
offset = (page - 1) * PullRequestLimit,
|
||||
limit = PullRequestLimit,
|
||||
repos = repository.owner -> repository.name
|
||||
offset = (page - 1) * PullRequestLimit,
|
||||
limit = PullRequestLimit,
|
||||
repos = repository.owner -> repository.name
|
||||
)
|
||||
|
||||
JsonFormat(issues.map { case (issue, issueUser) =>
|
||||
ApiIssue(
|
||||
issue = issue,
|
||||
repositoryName = RepositoryName(repository),
|
||||
user = ApiUser(issueUser),
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))
|
||||
)
|
||||
JsonFormat(issues.map {
|
||||
case (issue, issueUser) =>
|
||||
ApiIssue(
|
||||
issue = issue,
|
||||
repositoryName = RepositoryName(repository),
|
||||
user = ApiUser(issueUser),
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(repository)))
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -363,22 +427,28 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* https://developer.github.com/v3/issues/#get-a-single-issue
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/issues/:id")(referrersOnly { repository =>
|
||||
(for{
|
||||
issueId <- params("id").toIntOpt
|
||||
(for {
|
||||
issueId <- params("id").toIntOpt
|
||||
issue <- getIssue(repository.owner, repository.name, issueId.toString)
|
||||
openedUser <- getAccountByUserName(issue.openedUserName)
|
||||
} yield {
|
||||
JsonFormat(ApiIssue(issue, RepositoryName(repository), ApiUser(openedUser),
|
||||
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))))
|
||||
JsonFormat(
|
||||
ApiIssue(
|
||||
issue,
|
||||
RepositoryName(repository),
|
||||
ApiUser(openedUser),
|
||||
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))
|
||||
)
|
||||
)
|
||||
}) getOrElse NotFound()
|
||||
})
|
||||
|
||||
/**
|
||||
* https://developer.github.com/v3/issues/#create-an-issue
|
||||
*/
|
||||
* https://developer.github.com/v3/issues/#create-an-issue
|
||||
*/
|
||||
post("/api/v3/repos/:owner/:repository/issues")(readableUsersOnly { repository =>
|
||||
if(isIssueEditable(repository)){ // TODO Should this check is provided by authenticator?
|
||||
(for{
|
||||
if (isIssueEditable(repository)) { // TODO Should this check is provided by authenticator?
|
||||
(for {
|
||||
data <- extractFromJsonBody[CreateAnIssue]
|
||||
loginAccount <- context.loginAccount
|
||||
} yield {
|
||||
@@ -391,9 +461,17 @@ trait ApiControllerBase extends ControllerBase {
|
||||
milestone.map(_.milestoneId),
|
||||
None,
|
||||
data.labels,
|
||||
loginAccount)
|
||||
JsonFormat(ApiIssue(issue, RepositoryName(repository), ApiUser(loginAccount),
|
||||
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))))
|
||||
loginAccount
|
||||
)
|
||||
JsonFormat(
|
||||
ApiIssue(
|
||||
issue,
|
||||
RepositoryName(repository),
|
||||
ApiUser(loginAccount),
|
||||
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(repository)))
|
||||
)
|
||||
)
|
||||
}) getOrElse NotFound()
|
||||
} else Unauthorized()
|
||||
})
|
||||
@@ -402,11 +480,14 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/issues/:id/comments")(referrersOnly { repository =>
|
||||
(for{
|
||||
issueId <- params("id").toIntOpt
|
||||
comments = getCommentsForApi(repository.owner, repository.name, issueId)
|
||||
(for {
|
||||
issueId <- params("id").toIntOpt
|
||||
comments = getCommentsForApi(repository.owner, repository.name, issueId)
|
||||
} yield {
|
||||
JsonFormat(comments.map{ case (issueComment, user, issue) => ApiComment(issueComment, RepositoryName(repository), issueId, ApiUser(user), issue.isPullRequest) })
|
||||
JsonFormat(comments.map {
|
||||
case (issueComment, user, issue) =>
|
||||
ApiComment(issueComment, RepositoryName(repository), issueId, ApiUser(user), issue.isPullRequest)
|
||||
})
|
||||
}) getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -414,15 +495,23 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* https://developer.github.com/v3/issues/comments/#create-a-comment
|
||||
*/
|
||||
post("/api/v3/repos/:owner/:repository/issues/:id/comments")(readableUsersOnly { repository =>
|
||||
(for{
|
||||
issueId <- params("id").toIntOpt
|
||||
issue <- getIssue(repository.owner, repository.name, issueId.toString)
|
||||
body <- extractFromJsonBody[CreateAComment].map(_.body) if ! body.isEmpty
|
||||
action = params.get("action").filter(_ => isEditable(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||
(issue, id) <- handleComment(issue, Some(body), repository, action)
|
||||
(for {
|
||||
issueId <- params("id").toIntOpt
|
||||
issue <- getIssue(repository.owner, repository.name, issueId.toString)
|
||||
body <- extractFromJsonBody[CreateAComment].map(_.body) if !body.isEmpty
|
||||
action = params.get("action").filter(_ => isEditable(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||
(issue, id) <- handleComment(issue, Some(body), repository, action)
|
||||
issueComment <- getComment(repository.owner, repository.name, id.toString())
|
||||
} yield {
|
||||
JsonFormat(ApiComment(issueComment, RepositoryName(repository), issueId, ApiUser(context.loginAccount.get), issue.isPullRequest))
|
||||
JsonFormat(
|
||||
ApiComment(
|
||||
issueComment,
|
||||
RepositoryName(repository),
|
||||
issueId,
|
||||
ApiUser(context.loginAccount.get),
|
||||
issue.isPullRequest
|
||||
)
|
||||
)
|
||||
}) getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -451,7 +540,7 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* https://developer.github.com/v3/issues/labels/#create-a-label
|
||||
*/
|
||||
post("/api/v3/repos/:owner/:repository/labels")(writableUsersOnly { repository =>
|
||||
(for{
|
||||
(for {
|
||||
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
||||
} yield {
|
||||
LockUtil.lock(RepositoryName(repository).fullName) {
|
||||
@@ -462,10 +551,12 @@ trait ApiControllerBase extends ControllerBase {
|
||||
} getOrElse NotFound()
|
||||
} else {
|
||||
// TODO ApiError should support errors field to enhance compatibility of GitHub API
|
||||
UnprocessableEntity(ApiError(
|
||||
"Validation Failed",
|
||||
Some("https://developer.github.com/v3/issues/labels/#create-a-label")
|
||||
))
|
||||
UnprocessableEntity(
|
||||
ApiError(
|
||||
"Validation Failed",
|
||||
Some("https://developer.github.com/v3/issues/labels/#create-a-label")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}) getOrElse NotFound()
|
||||
@@ -476,24 +567,29 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* https://developer.github.com/v3/issues/labels/#update-a-label
|
||||
*/
|
||||
patch("/api/v3/repos/:owner/:repository/labels/:labelName")(writableUsersOnly { repository =>
|
||||
(for{
|
||||
(for {
|
||||
data <- extractFromJsonBody[CreateALabel] if data.isValid
|
||||
} yield {
|
||||
LockUtil.lock(RepositoryName(repository).fullName) {
|
||||
getLabel(repository.owner, repository.name, params("labelName")).map { label =>
|
||||
if (getLabel(repository.owner, repository.name, data.name).isEmpty) {
|
||||
updateLabel(repository.owner, repository.name, label.labelId, data.name, data.color)
|
||||
JsonFormat(ApiLabel(
|
||||
getLabel(repository.owner, repository.name, label.labelId).get,
|
||||
RepositoryName(repository)
|
||||
))
|
||||
} else {
|
||||
// TODO ApiError should support errors field to enhance compatibility of GitHub API
|
||||
UnprocessableEntity(ApiError(
|
||||
"Validation Failed",
|
||||
Some("https://developer.github.com/v3/issues/labels/#create-a-label")
|
||||
))
|
||||
}
|
||||
getLabel(repository.owner, repository.name, params("labelName")).map {
|
||||
label =>
|
||||
if (getLabel(repository.owner, repository.name, data.name).isEmpty) {
|
||||
updateLabel(repository.owner, repository.name, label.labelId, data.name, data.color)
|
||||
JsonFormat(
|
||||
ApiLabel(
|
||||
getLabel(repository.owner, repository.name, label.labelId).get,
|
||||
RepositoryName(repository)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// TODO ApiError should support errors field to enhance compatibility of GitHub API
|
||||
UnprocessableEntity(
|
||||
ApiError(
|
||||
"Validation Failed",
|
||||
Some("https://developer.github.com/v3/issues/labels/#create-a-label")
|
||||
)
|
||||
)
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
}) getOrElse NotFound()
|
||||
@@ -524,22 +620,24 @@ trait ApiControllerBase extends ControllerBase {
|
||||
val issues: List[(Issue, Account, Int, PullRequest, Repository, Account, Option[Account])] =
|
||||
searchPullRequestByApi(
|
||||
condition = condition,
|
||||
offset = (page - 1) * PullRequestLimit,
|
||||
limit = PullRequestLimit,
|
||||
repos = repository.owner -> repository.name
|
||||
offset = (page - 1) * PullRequestLimit,
|
||||
limit = PullRequestLimit,
|
||||
repos = repository.owner -> repository.name
|
||||
)
|
||||
|
||||
JsonFormat(issues.map { case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner, assignee) =>
|
||||
ApiPullRequest(
|
||||
issue = issue,
|
||||
pullRequest = pullRequest,
|
||||
headRepo = ApiRepository(headRepo, ApiUser(headOwner)),
|
||||
baseRepo = ApiRepository(repository, ApiUser(baseOwner)),
|
||||
user = ApiUser(issueUser),
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository))),
|
||||
assignee = assignee.map(ApiUser.apply),
|
||||
mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
|
||||
)
|
||||
JsonFormat(issues.map {
|
||||
case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner, assignee) =>
|
||||
ApiPullRequest(
|
||||
issue = issue,
|
||||
pullRequest = pullRequest,
|
||||
headRepo = ApiRepository(headRepo, ApiUser(headOwner)),
|
||||
baseRepo = ApiRepository(repository, ApiUser(baseOwner)),
|
||||
user = ApiUser(issueUser),
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(repository))),
|
||||
assignee = assignee.map(ApiUser.apply),
|
||||
mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -547,26 +645,34 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* https://developer.github.com/v3/pulls/#get-a-single-pull-request
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/pulls/:id")(referrersOnly { repository =>
|
||||
(for{
|
||||
issueId <- params("id").toIntOpt
|
||||
(for {
|
||||
issueId <- params("id").toIntOpt
|
||||
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
|
||||
users = getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set.empty)
|
||||
users = getAccountsByUserNames(
|
||||
Set(repository.owner, pullRequest.requestUserName, issue.openedUserName),
|
||||
Set.empty
|
||||
)
|
||||
baseOwner <- users.get(repository.owner)
|
||||
headOwner <- users.get(pullRequest.requestUserName)
|
||||
issueUser <- users.get(issue.openedUserName)
|
||||
assignee = issue.assignedUserName.flatMap { userName => getAccountByUserName(userName, false) }
|
||||
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
|
||||
assignee = issue.assignedUserName.flatMap { userName =>
|
||||
getAccountByUserName(userName, false)
|
||||
}
|
||||
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
|
||||
} yield {
|
||||
JsonFormat(ApiPullRequest(
|
||||
issue = issue,
|
||||
pullRequest = pullRequest,
|
||||
headRepo = ApiRepository(headRepo, ApiUser(headOwner)),
|
||||
baseRepo = ApiRepository(repository, ApiUser(baseOwner)),
|
||||
user = ApiUser(issueUser),
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository))),
|
||||
assignee = assignee.map(ApiUser.apply),
|
||||
mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
|
||||
))
|
||||
JsonFormat(
|
||||
ApiPullRequest(
|
||||
issue = issue,
|
||||
pullRequest = pullRequest,
|
||||
headRepo = ApiRepository(headRepo, ApiUser(headOwner)),
|
||||
baseRepo = ApiRepository(repository, ApiUser(baseOwner)),
|
||||
user = ApiUser(issueUser),
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(repository))),
|
||||
assignee = assignee.map(ApiUser.apply),
|
||||
mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
|
||||
)
|
||||
)
|
||||
}) getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -576,16 +682,26 @@ trait ApiControllerBase extends ControllerBase {
|
||||
get("/api/v3/repos/:owner/:repository/pulls/:id/commits")(referrersOnly { repository =>
|
||||
val owner = repository.owner
|
||||
val name = repository.name
|
||||
params("id").toIntOpt.flatMap{ issueId =>
|
||||
getPullRequest(owner, name, issueId) map { case(issue, pullreq) =>
|
||||
using(Git.open(getRepositoryDir(owner, name))){ git =>
|
||||
val oldId = git.getRepository.resolve(pullreq.commitIdFrom)
|
||||
val newId = git.getRepository.resolve(pullreq.commitIdTo)
|
||||
val repoFullName = RepositoryName(repository)
|
||||
val commits = git.log.addRange(oldId, newId).call.iterator.asScala.map { c => ApiCommitListItem(new CommitInfo(c), repoFullName) }.toList
|
||||
JsonFormat(commits)
|
||||
params("id").toIntOpt.flatMap {
|
||||
issueId =>
|
||||
getPullRequest(owner, name, issueId) map {
|
||||
case (issue, pullreq) =>
|
||||
using(Git.open(getRepositoryDir(owner, name))) { git =>
|
||||
val oldId = git.getRepository.resolve(pullreq.commitIdFrom)
|
||||
val newId = git.getRepository.resolve(pullreq.commitIdTo)
|
||||
val repoFullName = RepositoryName(repository)
|
||||
val commits = git.log
|
||||
.addRange(oldId, newId)
|
||||
.call
|
||||
.iterator
|
||||
.asScala
|
||||
.map { c =>
|
||||
ApiCommitListItem(new CommitInfo(c), repoFullName)
|
||||
}
|
||||
.toList
|
||||
JsonFormat(commits)
|
||||
}
|
||||
}
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -600,15 +716,24 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* https://developer.github.com/v3/repos/statuses/#create-a-status
|
||||
*/
|
||||
post("/api/v3/repos/:owner/:repo/statuses/:sha")(writableUsersOnly { repository =>
|
||||
(for{
|
||||
ref <- params.get("sha")
|
||||
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
||||
data <- extractFromJsonBody[CreateAStatus] if data.isValid
|
||||
creator <- context.loginAccount
|
||||
state <- CommitState.valueOf(data.state)
|
||||
statusId = createCommitStatus(repository.owner, repository.name, sha, data.context.getOrElse("default"),
|
||||
state, data.target_url, data.description, new java.util.Date(), creator)
|
||||
status <- getCommitStatus(repository.owner, repository.name, statusId)
|
||||
(for {
|
||||
ref <- params.get("sha")
|
||||
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
||||
data <- extractFromJsonBody[CreateAStatus] if data.isValid
|
||||
creator <- context.loginAccount
|
||||
state <- CommitState.valueOf(data.state)
|
||||
statusId = createCommitStatus(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
sha,
|
||||
data.context.getOrElse("default"),
|
||||
state,
|
||||
data.target_url,
|
||||
data.description,
|
||||
new java.util.Date(),
|
||||
creator
|
||||
)
|
||||
status <- getCommitStatus(repository.owner, repository.name, statusId)
|
||||
} yield {
|
||||
JsonFormat(ApiCommitStatus(status, ApiUser(creator)))
|
||||
}) getOrElse NotFound()
|
||||
@@ -620,12 +745,13 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* ref is Ref to list the statuses from. It can be a SHA, a branch name, or a tag name.
|
||||
*/
|
||||
val listStatusesRoute = get("/api/v3/repos/:owner/:repo/commits/:ref/statuses")(referrersOnly { repository =>
|
||||
(for{
|
||||
(for {
|
||||
ref <- params.get("ref")
|
||||
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
||||
} yield {
|
||||
JsonFormat(getCommitStatuesWithCreator(repository.owner, repository.name, sha).map{ case(status, creator) =>
|
||||
ApiCommitStatus(status, ApiUser(creator))
|
||||
JsonFormat(getCommitStatuesWithCreator(repository.owner, repository.name, sha).map {
|
||||
case (status, creator) =>
|
||||
ApiCommitStatus(status, ApiUser(creator))
|
||||
})
|
||||
}) getOrElse NotFound()
|
||||
})
|
||||
@@ -635,7 +761,7 @@ trait ApiControllerBase extends ControllerBase {
|
||||
*
|
||||
* legacy route
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repo/statuses/:ref"){
|
||||
get("/api/v3/repos/:owner/:repo/statuses/:ref") {
|
||||
listStatusesRoute.action()
|
||||
}
|
||||
|
||||
@@ -645,10 +771,10 @@ trait ApiControllerBase extends ControllerBase {
|
||||
* ref is Ref to list the statuses from. It can be a SHA, a branch name, or a tag name.
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repo/commits/:ref/status")(referrersOnly { repository =>
|
||||
(for{
|
||||
ref <- params.get("ref")
|
||||
(for {
|
||||
ref <- params.get("ref")
|
||||
owner <- getAccountByUserName(repository.owner)
|
||||
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
||||
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
|
||||
} yield {
|
||||
val statuses = getCommitStatuesWithCreator(repository.owner, repository.name, sha)
|
||||
JsonFormat(ApiCombinedCommitStatus(sha, statuses, ApiRepository(repository, owner)))
|
||||
@@ -660,24 +786,27 @@ trait ApiControllerBase extends ControllerBase {
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repo/commits/:sha")(referrersOnly { repository =>
|
||||
val owner = repository.owner
|
||||
val name = repository.name
|
||||
val sha = params("sha")
|
||||
val name = repository.name
|
||||
val sha = params("sha")
|
||||
|
||||
using(Git.open(getRepositoryDir(owner, name))){ git =>
|
||||
val repo = git.getRepository
|
||||
val objectId = repo.resolve(sha)
|
||||
val commitInfo = using(new RevWalk(repo)){ revWalk =>
|
||||
new CommitInfo(revWalk.parseCommit(objectId))
|
||||
}
|
||||
using(Git.open(getRepositoryDir(owner, name))) {
|
||||
git =>
|
||||
val repo = git.getRepository
|
||||
val objectId = repo.resolve(sha)
|
||||
val commitInfo = using(new RevWalk(repo)) { revWalk =>
|
||||
new CommitInfo(revWalk.parseCommit(objectId))
|
||||
}
|
||||
|
||||
JsonFormat(ApiCommits(
|
||||
repositoryName = RepositoryName(repository),
|
||||
commitInfo = commitInfo,
|
||||
diffs = JGitUtil.getDiffs(git, Some(commitInfo.parents.head), commitInfo.id, false, true),
|
||||
author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
|
||||
committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
|
||||
commentCount = getCommitComment(repository.owner, repository.name, sha).size
|
||||
))
|
||||
JsonFormat(
|
||||
ApiCommits(
|
||||
repositoryName = RepositoryName(repository),
|
||||
commitInfo = commitInfo,
|
||||
diffs = JGitUtil.getDiffs(git, Some(commitInfo.parents.head), commitInfo.id, false, true),
|
||||
author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
|
||||
committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
|
||||
commentCount = getCommitComment(repository.owner, repository.name, sha).size
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -705,11 +834,11 @@ trait ApiControllerBase extends ControllerBase {
|
||||
hasDeveloperRole(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
|
||||
|
||||
/**
|
||||
* non-GitHub compatible API for Jenkins-Plugin
|
||||
*/
|
||||
* non-GitHub compatible API for Jenkins-Plugin
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repo/raw/*")(referrersOnly { repository =>
|
||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||
|
||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||
@@ -719,10 +848,9 @@ trait ApiControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
/**
|
||||
* non-GitHub compatible API for listing plugins
|
||||
*/
|
||||
get("/api/v3/gitbucket/plugins"){
|
||||
PluginRegistry().getPlugins().map{ApiPlugin(_)}
|
||||
* non-GitHub compatible API for listing plugins
|
||||
*/
|
||||
get("/api/v3/gitbucket/plugins") {
|
||||
PluginRegistry().getPlugins().map { ApiPlugin(_) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,9 +31,14 @@ import org.slf4j.LoggerFactory
|
||||
/**
|
||||
* Provides generic features for controller implementations.
|
||||
*/
|
||||
abstract class ControllerBase extends ScalatraFilter
|
||||
with ValidationSupport with JacksonJsonSupport with I18nSupport with FlashMapSupport with Validations
|
||||
with SystemSettingsService {
|
||||
abstract class ControllerBase
|
||||
extends ScalatraFilter
|
||||
with ValidationSupport
|
||||
with JacksonJsonSupport
|
||||
with I18nSupport
|
||||
with FlashMapSupport
|
||||
with Validations
|
||||
with SystemSettingsService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(getClass)
|
||||
|
||||
@@ -45,31 +50,32 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
|
||||
override def requestPath(uri: String, idx: Int): String = {
|
||||
val path = super.requestPath(uri, idx)
|
||||
if(path != "/" && path.endsWith("/")){
|
||||
if (path != "/" && path.endsWith("/")) {
|
||||
path.substring(0, path.length - 1)
|
||||
} else {
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = try {
|
||||
val httpRequest = request.asInstanceOf[HttpServletRequest]
|
||||
val context = request.getServletContext.getContextPath
|
||||
val path = httpRequest.getRequestURI.substring(context.length)
|
||||
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit =
|
||||
try {
|
||||
val httpRequest = request.asInstanceOf[HttpServletRequest]
|
||||
val context = request.getServletContext.getContextPath
|
||||
val path = httpRequest.getRequestURI.substring(context.length)
|
||||
|
||||
if(path.startsWith("/git/") || path.startsWith("/git-lfs/")){
|
||||
// Git repository
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
if(path.startsWith("/api/v3/")){
|
||||
httpRequest.setAttribute(Keys.Request.APIv3, true)
|
||||
if (path.startsWith("/git/") || path.startsWith("/git-lfs/")) {
|
||||
// Git repository
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
if (path.startsWith("/api/v3/")) {
|
||||
httpRequest.setAttribute(Keys.Request.APIv3, true)
|
||||
}
|
||||
// Scalatra actions
|
||||
super.doFilter(request, response, chain)
|
||||
}
|
||||
// Scalatra actions
|
||||
super.doFilter(request, response, chain)
|
||||
} finally {
|
||||
contextCache.remove();
|
||||
}
|
||||
} finally {
|
||||
contextCache.remove();
|
||||
}
|
||||
|
||||
private val contextCache = new java.lang.ThreadLocal[Context]()
|
||||
|
||||
@@ -87,36 +93,37 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
}
|
||||
}
|
||||
|
||||
private def LoginAccount: Option[Account] = request.getAs[Account](Keys.Session.LoginAccount).orElse(session.getAs[Account](Keys.Session.LoginAccount))
|
||||
private def LoginAccount: Option[Account] =
|
||||
request.getAs[Account](Keys.Session.LoginAccount).orElse(session.getAs[Account](Keys.Session.LoginAccount))
|
||||
|
||||
def ajaxGet(path : String)(action : => Any) : Route =
|
||||
super.get(path){
|
||||
def ajaxGet(path: String)(action: => Any): Route =
|
||||
super.get(path) {
|
||||
request.setAttribute(Keys.Request.Ajax, "true")
|
||||
action
|
||||
}
|
||||
|
||||
override def ajaxGet[T](path : String, form : ValueType[T])(action : T => Any) : Route =
|
||||
super.ajaxGet(path, form){ form =>
|
||||
override def ajaxGet[T](path: String, form: ValueType[T])(action: T => Any): Route =
|
||||
super.ajaxGet(path, form) { form =>
|
||||
request.setAttribute(Keys.Request.Ajax, "true")
|
||||
action(form)
|
||||
}
|
||||
|
||||
def ajaxPost(path : String)(action : => Any) : Route =
|
||||
super.post(path){
|
||||
def ajaxPost(path: String)(action: => Any): Route =
|
||||
super.post(path) {
|
||||
request.setAttribute(Keys.Request.Ajax, "true")
|
||||
action
|
||||
}
|
||||
|
||||
override def ajaxPost[T](path : String, form : ValueType[T])(action : T => Any) : Route =
|
||||
super.ajaxPost(path, form){ form =>
|
||||
override def ajaxPost[T](path: String, form: ValueType[T])(action: T => Any): Route =
|
||||
super.ajaxPost(path, form) { form =>
|
||||
request.setAttribute(Keys.Request.Ajax, "true")
|
||||
action(form)
|
||||
}
|
||||
|
||||
protected def NotFound() =
|
||||
if(request.hasAttribute(Keys.Request.Ajax)){
|
||||
if (request.hasAttribute(Keys.Request.Ajax)) {
|
||||
org.scalatra.NotFound()
|
||||
} else if(request.hasAttribute(Keys.Request.APIv3)){
|
||||
} else if (request.hasAttribute(Keys.Request.APIv3)) {
|
||||
contentType = formats("json")
|
||||
org.scalatra.NotFound(ApiError("Not Found"))
|
||||
} else {
|
||||
@@ -124,7 +131,7 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
}
|
||||
|
||||
private def isBrowser(userAgent: String): Boolean = {
|
||||
if(userAgent == null || userAgent.isEmpty){
|
||||
if (userAgent == null || userAgent.isEmpty) {
|
||||
false
|
||||
} else {
|
||||
val data = Classifier.parse(userAgent)
|
||||
@@ -134,35 +141,41 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
}
|
||||
|
||||
protected def Unauthorized()(implicit context: Context) =
|
||||
if(request.hasAttribute(Keys.Request.Ajax)){
|
||||
if (request.hasAttribute(Keys.Request.Ajax)) {
|
||||
org.scalatra.Unauthorized()
|
||||
} else if(request.hasAttribute(Keys.Request.APIv3)){
|
||||
} else if (request.hasAttribute(Keys.Request.APIv3)) {
|
||||
contentType = formats("json")
|
||||
org.scalatra.Unauthorized(ApiError("Requires authentication"))
|
||||
} else if(!isBrowser(request.getHeader("USER-AGENT"))){
|
||||
} else if (!isBrowser(request.getHeader("USER-AGENT"))) {
|
||||
org.scalatra.Unauthorized()
|
||||
} else {
|
||||
if(context.loginAccount.isDefined){
|
||||
if (context.loginAccount.isDefined) {
|
||||
org.scalatra.Unauthorized(redirect("/"))
|
||||
} else {
|
||||
if(request.getMethod.toUpperCase == "POST"){
|
||||
if (request.getMethod.toUpperCase == "POST") {
|
||||
org.scalatra.Unauthorized(redirect("/signin"))
|
||||
} else {
|
||||
org.scalatra.Unauthorized(redirect("/signin?redirect=" + StringUtil.urlEncode(
|
||||
defining(request.getQueryString){ queryString =>
|
||||
request.getRequestURI.substring(request.getContextPath.length) + (if(queryString != null) "?" + queryString else "")
|
||||
}
|
||||
)))
|
||||
org.scalatra.Unauthorized(
|
||||
redirect(
|
||||
"/signin?redirect=" + StringUtil.urlEncode(
|
||||
defining(request.getQueryString) { queryString =>
|
||||
request.getRequestURI.substring(request.getContextPath.length) + (if (queryString != null)
|
||||
"?" + queryString
|
||||
else "")
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error{
|
||||
error {
|
||||
case e => {
|
||||
logger.error(s"Catch unhandled error in request: ${request}", e)
|
||||
if(request.hasAttribute(Keys.Request.Ajax)){
|
||||
if (request.hasAttribute(Keys.Request.Ajax)) {
|
||||
org.scalatra.InternalServerError()
|
||||
} else if(request.hasAttribute(Keys.Request.APIv3)){
|
||||
} else if (request.hasAttribute(Keys.Request.APIv3)) {
|
||||
contentType = formats("json")
|
||||
org.scalatra.InternalServerError(ApiError("Internal Server Error"))
|
||||
} else {
|
||||
@@ -171,30 +184,39 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
}
|
||||
}
|
||||
|
||||
override def url(path: String, params: Iterable[(String, Any)] = Iterable.empty,
|
||||
includeContextPath: Boolean = true, includeServletPath: Boolean = true,
|
||||
absolutize: Boolean = true, withSessionId: Boolean = true)
|
||||
(implicit request: HttpServletRequest, response: HttpServletResponse): String =
|
||||
override def url(
|
||||
path: String,
|
||||
params: Iterable[(String, Any)] = Iterable.empty,
|
||||
includeContextPath: Boolean = true,
|
||||
includeServletPath: Boolean = true,
|
||||
absolutize: Boolean = true,
|
||||
withSessionId: Boolean = true
|
||||
)(implicit request: HttpServletRequest, response: HttpServletResponse): String =
|
||||
if (path.startsWith("http")) path
|
||||
else baseUrl + super.url(path, params, false, false, false)
|
||||
|
||||
/**
|
||||
* Extends scalatra-form's trim rule to eliminate CR and LF.
|
||||
*/
|
||||
protected def trim2[T](valueType: SingleValueType[T]): SingleValueType[T] = new SingleValueType[T](){
|
||||
protected def trim2[T](valueType: SingleValueType[T]): SingleValueType[T] = new SingleValueType[T]() {
|
||||
def convert(value: String, messages: Messages): T = valueType.convert(trim(value), messages)
|
||||
|
||||
override def validate(name: String, value: String, params: Map[String, Seq[String]], messages: Messages): Seq[(String, String)] =
|
||||
override def validate(
|
||||
name: String,
|
||||
value: String,
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Seq[(String, String)] =
|
||||
valueType.validate(name, trim(value), params, messages)
|
||||
|
||||
private def trim(value: String): String = if(value == null) null else value.replace("\r\n", "").trim
|
||||
private def trim(value: String): String = if (value == null) null else value.replace("\r\n", "").trim
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to response the raw data against XSS.
|
||||
*/
|
||||
protected def RawData[T](contentType: String, rawData: T): T = {
|
||||
if(contentType.split(";").head.trim.toLowerCase.startsWith("text/html")){
|
||||
if (contentType.split(";").head.trim.toLowerCase.startsWith("text/html")) {
|
||||
this.contentType = "text/plain"
|
||||
} else {
|
||||
this.contentType = contentType
|
||||
@@ -204,35 +226,39 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
}
|
||||
|
||||
// jenkins send message as 'application/x-www-form-urlencoded' but scalatra already parsed as multi-part-request.
|
||||
def extractFromJsonBody[A](implicit request:HttpServletRequest, mf:Manifest[A]): Option[A] = {
|
||||
(request.contentType.map(_.split(";").head.toLowerCase) match{
|
||||
def extractFromJsonBody[A](implicit request: HttpServletRequest, mf: Manifest[A]): Option[A] = {
|
||||
(request.contentType.map(_.split(";").head.toLowerCase) match {
|
||||
case Some("application/x-www-form-urlencoded") => multiParams.keys.headOption.map(parse(_))
|
||||
case Some("application/json") => Some(parsedBody)
|
||||
case _ => Some(parse(request.body))
|
||||
case Some("application/json") => Some(parsedBody)
|
||||
case _ => Some(parse(request.body))
|
||||
}).filterNot(_ == JNothing).flatMap(j => Try(j.extract[A]).toOption)
|
||||
}
|
||||
|
||||
protected def getPathObjectId(git: Git, path: String, revCommit: RevCommit): Option[ObjectId] = {
|
||||
@scala.annotation.tailrec
|
||||
def _getPathObjectId(path: String, walk: TreeWalk): Option[ObjectId] = walk.next match {
|
||||
case true if(walk.getPathString == path) => Some(walk.getObjectId(0))
|
||||
case true => _getPathObjectId(path, walk)
|
||||
case false => None
|
||||
case true if (walk.getPathString == path) => Some(walk.getObjectId(0))
|
||||
case true => _getPathObjectId(path, walk)
|
||||
case false => None
|
||||
}
|
||||
|
||||
using(new TreeWalk(git.getRepository)){ treeWalk =>
|
||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
treeWalk.addTree(revCommit.getTree)
|
||||
treeWalk.setRecursive(true)
|
||||
_getPathObjectId(path, treeWalk)
|
||||
}
|
||||
}
|
||||
|
||||
protected def responseRawFile(git: Git, objectId: ObjectId, path: String,
|
||||
repository: RepositoryService.RepositoryInfo): Unit = {
|
||||
JGitUtil.getObjectLoaderFromId(git, objectId){ loader =>
|
||||
protected def responseRawFile(
|
||||
git: Git,
|
||||
objectId: ObjectId,
|
||||
path: String,
|
||||
repository: RepositoryService.RepositoryInfo
|
||||
): Unit = {
|
||||
JGitUtil.getObjectLoaderFromId(git, objectId) { loader =>
|
||||
contentType = FileUtil.getMimeType(path)
|
||||
|
||||
if(loader.isLarge){
|
||||
if (loader.isLarge) {
|
||||
response.setContentLength(loader.getSize.toInt)
|
||||
loader.copyTo(response.outputStream)
|
||||
} else {
|
||||
@@ -240,11 +266,11 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
val text = new String(bytes, "UTF-8")
|
||||
|
||||
val attrs = JGitUtil.getLfsObjects(text)
|
||||
if(attrs.nonEmpty) {
|
||||
if (attrs.nonEmpty) {
|
||||
response.setContentLength(attrs("size").toInt)
|
||||
val oid = attrs("oid").split(":")(1)
|
||||
|
||||
using(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))){ in =>
|
||||
using(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))) { in =>
|
||||
IOUtils.copy(in, response.getOutputStream)
|
||||
}
|
||||
} else {
|
||||
@@ -259,17 +285,21 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
/**
|
||||
* Context object for the current request.
|
||||
*/
|
||||
case class Context(settings: SystemSettingsService.SystemSettings, loginAccount: Option[Account], request: HttpServletRequest){
|
||||
case class Context(
|
||||
settings: SystemSettingsService.SystemSettings,
|
||||
loginAccount: Option[Account],
|
||||
request: HttpServletRequest
|
||||
) {
|
||||
val path = settings.baseUrl.getOrElse(request.getContextPath)
|
||||
val currentPath = request.getRequestURI.substring(request.getContextPath.length)
|
||||
val baseUrl = settings.baseUrl(request)
|
||||
val host = new java.net.URL(baseUrl).getHost
|
||||
val platform = request.getHeader("User-Agent") match {
|
||||
case null => null
|
||||
case agent if agent.contains("Mac") => "mac"
|
||||
case null => null
|
||||
case agent if agent.contains("Mac") => "mac"
|
||||
case agent if agent.contains("Linux") => "linux"
|
||||
case agent if agent.contains("Win") => "windows"
|
||||
case _ => null
|
||||
case agent if agent.contains("Win") => "windows"
|
||||
case _ => null
|
||||
}
|
||||
val sidebarCollapse = request.getSession.getAttribute("sidebar-collapse") != null
|
||||
|
||||
@@ -280,7 +310,7 @@ case class Context(settings: SystemSettingsService.SystemSettings, loginAccount:
|
||||
* Cached object are available during a request.
|
||||
*/
|
||||
def cache[A](key: String)(action: => A): A =
|
||||
defining(Keys.Request.Cache(key)){ cacheKey =>
|
||||
defining(Keys.Request.Cache(key)) { cacheKey =>
|
||||
Option(request.getAttribute(cacheKey).asInstanceOf[A]).getOrElse {
|
||||
val newObject = action
|
||||
request.setAttribute(cacheKey, newObject)
|
||||
@@ -294,10 +324,10 @@ case class Context(settings: SystemSettingsService.SystemSettings, loginAccount:
|
||||
* Base trait for controllers which manages account information.
|
||||
*/
|
||||
trait AccountManagementControllerBase extends ControllerBase {
|
||||
self: AccountService =>
|
||||
self: AccountService =>
|
||||
|
||||
protected def updateImage(userName: String, fileId: Option[String], clearImage: Boolean): Unit =
|
||||
if(clearImage){
|
||||
if (clearImage) {
|
||||
getAccountByUserName(userName).flatMap(_.image).map { image =>
|
||||
new java.io.File(getUserUploadDir(userName), image).delete()
|
||||
updateAvatarImage(userName, None)
|
||||
@@ -306,36 +336,63 @@ trait AccountManagementControllerBase extends ControllerBase {
|
||||
fileId.map { fileId =>
|
||||
val filename = "avatar." + FileUtil.getExtension(session.getAndRemove(Keys.Session.Upload(fileId)).get)
|
||||
val uploadDir = getUserUploadDir(userName)
|
||||
if(!uploadDir.exists){
|
||||
if (!uploadDir.exists) {
|
||||
uploadDir.mkdirs()
|
||||
}
|
||||
Thumbnails.of(new java.io.File(getTemporaryDir(session.getId), fileId))
|
||||
Thumbnails
|
||||
.of(new java.io.File(getTemporaryDir(session.getId), fileId))
|
||||
.size(324, 324)
|
||||
.toFile(new java.io.File(uploadDir, filename))
|
||||
updateAvatarImage(userName, Some(filename))
|
||||
}
|
||||
}
|
||||
|
||||
protected def uniqueUserName: Constraint = new Constraint(){
|
||||
protected def uniqueUserName: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
getAccountByUserName(value, true).map { _ => "User already exists." }
|
||||
getAccountByUserName(value, true).map { _ =>
|
||||
"User already exists."
|
||||
}
|
||||
}
|
||||
|
||||
protected def uniqueMailAddress(paramName: String = ""): Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, params: Map[String, Seq[String]], messages: Messages): Option[String] = {
|
||||
protected def uniqueMailAddress(paramName: String = ""): Constraint = new Constraint() {
|
||||
override def validate(
|
||||
name: String,
|
||||
value: String,
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
getAccountByMailAddress(value, true)
|
||||
.filter { x => if(paramName.isEmpty) true else Some(x.userName) != params.optionValue(paramName) }
|
||||
.map { _ => "Mail address is already registered." }
|
||||
.filter { x =>
|
||||
if (paramName.isEmpty) true else Some(x.userName) != params.optionValue(paramName)
|
||||
}
|
||||
.map { _ =>
|
||||
"Mail address is already registered."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val allReservedNames = Set("git", "admin", "upload", "api", "assets", "plugin-assets", "signin", "signout", "register", "activities.atom", "sidebar-collapse", "groups", "new")
|
||||
protected def reservedNames(): Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = if(allReservedNames.contains(value)){
|
||||
Some(s"${value} is reserved")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
val allReservedNames = Set(
|
||||
"git",
|
||||
"admin",
|
||||
"upload",
|
||||
"api",
|
||||
"assets",
|
||||
"plugin-assets",
|
||||
"signin",
|
||||
"signout",
|
||||
"register",
|
||||
"activities.atom",
|
||||
"sidebar-collapse",
|
||||
"groups",
|
||||
"new"
|
||||
)
|
||||
protected def reservedNames(): Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if (allReservedNames.contains(value)) {
|
||||
Some(s"${value} is reserved")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,13 +6,20 @@ import gitbucket.core.util.{Keys, UsersAuthenticator}
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.service.IssuesService._
|
||||
|
||||
class DashboardController extends DashboardControllerBase
|
||||
with IssuesService with PullRequestService with RepositoryService with AccountService with CommitsService
|
||||
with LabelsService with PrioritiesService with MilestonesService with UsersAuthenticator
|
||||
class DashboardController
|
||||
extends DashboardControllerBase
|
||||
with IssuesService
|
||||
with PullRequestService
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with CommitsService
|
||||
with LabelsService
|
||||
with PrioritiesService
|
||||
with MilestonesService
|
||||
with UsersAuthenticator
|
||||
|
||||
trait DashboardControllerBase extends ControllerBase {
|
||||
self: IssuesService with PullRequestService with RepositoryService with AccountService
|
||||
with UsersAuthenticator =>
|
||||
self: IssuesService with PullRequestService with RepositoryService with AccountService with UsersAuthenticator =>
|
||||
|
||||
get("/dashboard/issues")(usersOnly {
|
||||
searchIssues("created_by")
|
||||
@@ -59,51 +66,52 @@ trait DashboardControllerBase extends ControllerBase {
|
||||
private def searchIssues(filter: String) = {
|
||||
import IssuesService._
|
||||
|
||||
val userName = context.loginAccount.get.userName
|
||||
val userName = context.loginAccount.get.userName
|
||||
val condition = getOrCreateCondition(Keys.Session.DashboardIssues, filter, userName)
|
||||
val userRepos = getUserRepositories(userName, true).map(repo => repo.owner -> repo.name)
|
||||
val page = IssueSearchCondition.page(request)
|
||||
val page = IssueSearchCondition.page(request)
|
||||
|
||||
html.issues(
|
||||
searchIssue(condition, false, (page - 1) * IssueLimit, IssueLimit, userRepos: _*),
|
||||
page,
|
||||
countIssue(condition.copy(state = "open" ), false, userRepos: _*),
|
||||
countIssue(condition.copy(state = "open"), false, userRepos: _*),
|
||||
countIssue(condition.copy(state = "closed"), false, userRepos: _*),
|
||||
filter match {
|
||||
case "assigned" => condition.copy(assigned = Some(Some(userName)))
|
||||
case "assigned" => condition.copy(assigned = Some(Some(userName)))
|
||||
case "mentioned" => condition.copy(mentioned = Some(userName))
|
||||
case _ => condition.copy(author = Some(userName))
|
||||
case _ => condition.copy(author = Some(userName))
|
||||
},
|
||||
filter,
|
||||
getGroupNames(userName),
|
||||
Nil,
|
||||
getUserRepositories(userName, withoutPhysicalInfo = true))
|
||||
getUserRepositories(userName, withoutPhysicalInfo = true)
|
||||
)
|
||||
}
|
||||
|
||||
private def searchPullRequests(filter: String) = {
|
||||
import IssuesService._
|
||||
import PullRequestService._
|
||||
|
||||
val userName = context.loginAccount.get.userName
|
||||
val userName = context.loginAccount.get.userName
|
||||
val condition = getOrCreateCondition(Keys.Session.DashboardPulls, filter, userName)
|
||||
val allRepos = getAllRepositories(userName)
|
||||
val page = IssueSearchCondition.page(request)
|
||||
val allRepos = getAllRepositories(userName)
|
||||
val page = IssueSearchCondition.page(request)
|
||||
|
||||
html.pulls(
|
||||
searchIssue(condition, true, (page - 1) * PullRequestLimit, PullRequestLimit, allRepos: _*),
|
||||
page,
|
||||
countIssue(condition.copy(state = "open" ), true, allRepos: _*),
|
||||
countIssue(condition.copy(state = "open"), true, allRepos: _*),
|
||||
countIssue(condition.copy(state = "closed"), true, allRepos: _*),
|
||||
filter match {
|
||||
case "assigned" => condition.copy(assigned = Some(Some(userName)))
|
||||
case "assigned" => condition.copy(assigned = Some(Some(userName)))
|
||||
case "mentioned" => condition.copy(mentioned = Some(userName))
|
||||
case _ => condition.copy(author = Some(userName))
|
||||
case _ => condition.copy(author = Some(userName))
|
||||
},
|
||||
filter,
|
||||
getGroupNames(userName),
|
||||
Nil,
|
||||
getUserRepositories(userName, withoutPhysicalInfo = true))
|
||||
getUserRepositories(userName, withoutPhysicalInfo = true)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -19,95 +19,133 @@ import org.apache.commons.io.{FileUtils, IOUtils}
|
||||
*
|
||||
* This servlet saves uploaded file.
|
||||
*/
|
||||
class FileUploadController extends ScalatraServlet
|
||||
with FileUploadSupport
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ReleaseService{
|
||||
class FileUploadController
|
||||
extends ScalatraServlet
|
||||
with FileUploadSupport
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ReleaseService {
|
||||
|
||||
configureMultipartHandling(MultipartConfig(maxFileSize = Some(FileUtil.MaxFileSize)))
|
||||
|
||||
post("/image"){
|
||||
execute({ (file, fileId) =>
|
||||
FileUtils.writeByteArrayToFile(new java.io.File(getTemporaryDir(session.getId), fileId), file.get)
|
||||
session += Keys.Session.Upload(fileId) -> file.name
|
||||
}, FileUtil.isImage)
|
||||
post("/image") {
|
||||
execute(
|
||||
{ (file, fileId) =>
|
||||
FileUtils.writeByteArrayToFile(new java.io.File(getTemporaryDir(session.getId), fileId), file.get)
|
||||
session += Keys.Session.Upload(fileId) -> file.name
|
||||
},
|
||||
FileUtil.isImage
|
||||
)
|
||||
}
|
||||
|
||||
post("/tmp"){
|
||||
execute({ (file, fileId) =>
|
||||
FileUtils.writeByteArrayToFile(new java.io.File(getTemporaryDir(session.getId), fileId), file.get)
|
||||
session += Keys.Session.Upload(fileId) -> file.name
|
||||
}, _ => true)
|
||||
post("/tmp") {
|
||||
execute(
|
||||
{ (file, fileId) =>
|
||||
FileUtils.writeByteArrayToFile(new java.io.File(getTemporaryDir(session.getId), fileId), file.get)
|
||||
session += Keys.Session.Upload(fileId) -> file.name
|
||||
},
|
||||
_ => true
|
||||
)
|
||||
}
|
||||
|
||||
post("/file/:owner/:repository"){
|
||||
execute({ (file, fileId) =>
|
||||
FileUtils.writeByteArrayToFile(new java.io.File(
|
||||
getAttachedDir(params("owner"), params("repository")),
|
||||
fileId + "." + FileUtil.getExtension(file.getName)), file.get)
|
||||
}, _ => true)
|
||||
post("/file/:owner/:repository") {
|
||||
execute(
|
||||
{ (file, fileId) =>
|
||||
FileUtils.writeByteArrayToFile(
|
||||
new java.io.File(
|
||||
getAttachedDir(params("owner"), params("repository")),
|
||||
fileId + "." + FileUtil.getExtension(file.getName)
|
||||
),
|
||||
file.get
|
||||
)
|
||||
},
|
||||
_ => true
|
||||
)
|
||||
}
|
||||
|
||||
post("/wiki/:owner/:repository"){
|
||||
post("/wiki/:owner/:repository") {
|
||||
// Don't accept not logged-in users
|
||||
session.get(Keys.Session.LoginAccount).collect { case loginAccount: Account =>
|
||||
val owner = params("owner")
|
||||
val repository = params("repository")
|
||||
session.get(Keys.Session.LoginAccount).collect {
|
||||
case loginAccount: Account =>
|
||||
val owner = params("owner")
|
||||
val repository = params("repository")
|
||||
|
||||
// Check whether logged-in user is collaborator
|
||||
onlyWikiEditable(owner, repository, loginAccount){
|
||||
execute({ (file, fileId) =>
|
||||
val fileName = file.getName
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||
// Check whether logged-in user is collaborator
|
||||
onlyWikiEditable(owner, repository, loginAccount) {
|
||||
execute(
|
||||
{ (file, fileId) =>
|
||||
val fileName = file.getName
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) {
|
||||
git =>
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||
|
||||
if(headId != null){
|
||||
JGitUtil.processTree(git, headId){ (path, tree) =>
|
||||
if(path != fileName){
|
||||
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||
}
|
||||
if (headId != null) {
|
||||
JGitUtil.processTree(git, headId) { (path, tree) =>
|
||||
if (path != fileName) {
|
||||
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()
|
||||
}
|
||||
|
||||
post("/release/:owner/:repository/:tag"){
|
||||
session.get(Keys.Session.LoginAccount).collect { case _: Account =>
|
||||
val owner = params("owner")
|
||||
val repository = params("repository")
|
||||
val tag = params("tag")
|
||||
execute({ (file, fileId) =>
|
||||
FileUtils.writeByteArrayToFile(
|
||||
new java.io.File(getReleaseFilesDir(owner, repository), tag + "/" + fileId),
|
||||
file.get
|
||||
)
|
||||
}, _ => true)
|
||||
}.getOrElse(BadRequest())
|
||||
post("/release/:owner/:repository/:tag") {
|
||||
session
|
||||
.get(Keys.Session.LoginAccount)
|
||||
.collect {
|
||||
case _: Account =>
|
||||
val owner = params("owner")
|
||||
val repository = params("repository")
|
||||
val tag = params("tag")
|
||||
execute({ (file, fileId) =>
|
||||
FileUtils.writeByteArrayToFile(
|
||||
new java.io.File(getReleaseFilesDir(owner, repository), tag + "/" + fileId),
|
||||
file.get
|
||||
)
|
||||
}, _ => true)
|
||||
}
|
||||
.getOrElse(BadRequest())
|
||||
}
|
||||
|
||||
post("/import") {
|
||||
import JDBCUtil._
|
||||
session.get(Keys.Session.LoginAccount).collect { case loginAccount: Account if loginAccount.isAdmin =>
|
||||
execute({ (file, fileId) =>
|
||||
request2Session(request).conn.importAsSQL(file.getInputStream)
|
||||
}, _ => true)
|
||||
session.get(Keys.Session.LoginAccount).collect {
|
||||
case loginAccount: Account if loginAccount.isAdmin =>
|
||||
execute({ (file, fileId) =>
|
||||
request2Session(request).conn.importAsSQL(file.getInputStream)
|
||||
}, _ => true)
|
||||
}
|
||||
redirect("/admin/data")
|
||||
}
|
||||
@@ -115,24 +153,26 @@ class FileUploadController extends ScalatraServlet
|
||||
private def onlyWikiEditable(owner: String, repository: String, loginAccount: Account)(action: => Any): Any = {
|
||||
implicit val session = Database.getSession(request)
|
||||
getRepository(owner, repository) match {
|
||||
case Some(x) => x.repository.options.wikiOption match {
|
||||
case "ALL" if !x.repository.isPrivate => action
|
||||
case "PUBLIC" if hasGuestRole(owner, repository, Some(loginAccount)) => action
|
||||
case "PRIVATE" if hasDeveloperRole(owner, repository, Some(loginAccount)) => action
|
||||
case _ => BadRequest()
|
||||
}
|
||||
case Some(x) =>
|
||||
x.repository.options.wikiOption match {
|
||||
case "ALL" if !x.repository.isPrivate => action
|
||||
case "PUBLIC" if hasGuestRole(owner, repository, Some(loginAccount)) => action
|
||||
case "PRIVATE" if hasDeveloperRole(owner, repository, Some(loginAccount)) => action
|
||||
case _ => BadRequest()
|
||||
}
|
||||
case None => BadRequest()
|
||||
}
|
||||
}
|
||||
|
||||
private def execute(f: (FileItem, String) => Unit , mimeTypeChcker: (String) => Boolean) = fileParams.get("file") match {
|
||||
case Some(file) if(mimeTypeChcker(file.name)) =>
|
||||
defining(FileUtil.generateFileId){ fileId =>
|
||||
f(file, fileId)
|
||||
contentType = "text/plain"
|
||||
Ok(fileId)
|
||||
}
|
||||
case _ => BadRequest()
|
||||
}
|
||||
private def execute(f: (FileItem, String) => Unit, mimeTypeChcker: (String) => Boolean) =
|
||||
fileParams.get("file") match {
|
||||
case Some(file) if (mimeTypeChcker(file.name)) =>
|
||||
defining(FileUtil.generateFileId) { fileId =>
|
||||
f(file, fileId)
|
||||
contentType = "text/plain"
|
||||
Ok(fileId)
|
||||
}
|
||||
case _ => BadRequest()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,22 +13,21 @@ import gitbucket.core.util.{Keys, LDAPUtil, ReferrerAuthenticator, UsersAuthenti
|
||||
import org.scalatra.Ok
|
||||
import org.scalatra.forms._
|
||||
|
||||
|
||||
class IndexController extends IndexControllerBase
|
||||
with RepositoryService
|
||||
with ActivityService
|
||||
with AccountService
|
||||
with RepositorySearchService
|
||||
with IssuesService
|
||||
with LabelsService
|
||||
with MilestonesService
|
||||
with PrioritiesService
|
||||
with UsersAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
with AccessTokenService
|
||||
with AccountFederationService
|
||||
with OpenIDConnectService
|
||||
|
||||
class IndexController
|
||||
extends IndexControllerBase
|
||||
with RepositoryService
|
||||
with ActivityService
|
||||
with AccountService
|
||||
with RepositorySearchService
|
||||
with IssuesService
|
||||
with LabelsService
|
||||
with MilestonesService
|
||||
with PrioritiesService
|
||||
with UsersAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
with AccessTokenService
|
||||
with AccountFederationService
|
||||
with OpenIDConnectService
|
||||
|
||||
trait IndexControllerBase extends ControllerBase {
|
||||
self: RepositoryService
|
||||
@@ -59,37 +58,43 @@ trait IndexControllerBase extends ControllerBase {
|
||||
|
||||
case class OidcContext(state: State, nonce: Nonce, redirectBackURI: String)
|
||||
|
||||
get("/"){
|
||||
context.loginAccount.map { account =>
|
||||
val visibleOwnerSet: Set[String] = Set(account.userName) ++ getGroupsByUserName(account.userName)
|
||||
gitbucket.core.html.index(
|
||||
getRecentActivitiesByOwners(visibleOwnerSet),
|
||||
Nil,
|
||||
getUserRepositories(account.userName, withoutPhysicalInfo = true),
|
||||
showBannerToCreatePersonalAccessToken = hasAccountFederation(account.userName) && !hasAccessToken(account.userName))
|
||||
}.getOrElse {
|
||||
gitbucket.core.html.index(
|
||||
getRecentActivities(),
|
||||
getVisibleRepositories(None, withoutPhysicalInfo = true),
|
||||
Nil,
|
||||
showBannerToCreatePersonalAccessToken = false)
|
||||
}
|
||||
get("/") {
|
||||
context.loginAccount
|
||||
.map { account =>
|
||||
val visibleOwnerSet: Set[String] = Set(account.userName) ++ getGroupsByUserName(account.userName)
|
||||
gitbucket.core.html.index(
|
||||
getRecentActivitiesByOwners(visibleOwnerSet),
|
||||
Nil,
|
||||
getUserRepositories(account.userName, withoutPhysicalInfo = true),
|
||||
showBannerToCreatePersonalAccessToken = hasAccountFederation(account.userName) && !hasAccessToken(
|
||||
account.userName
|
||||
)
|
||||
)
|
||||
}
|
||||
.getOrElse {
|
||||
gitbucket.core.html.index(
|
||||
getRecentActivities(),
|
||||
getVisibleRepositories(None, withoutPhysicalInfo = true),
|
||||
Nil,
|
||||
showBannerToCreatePersonalAccessToken = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
get("/signin"){
|
||||
get("/signin") {
|
||||
val redirect = params.get("redirect")
|
||||
if(redirect.isDefined && redirect.get.startsWith("/")){
|
||||
if (redirect.isDefined && redirect.get.startsWith("/")) {
|
||||
flash += Keys.Flash.Redirect -> redirect.get
|
||||
}
|
||||
gitbucket.core.html.signin(flash.get("userName"), flash.get("password"), flash.get("error"))
|
||||
}
|
||||
|
||||
post("/signin", signinForm){ form =>
|
||||
post("/signin", signinForm) { form =>
|
||||
authenticate(context.settings, form.userName, form.password) match {
|
||||
case Some(account) =>
|
||||
flash.get(Keys.Flash.Redirect) match {
|
||||
case Some(redirectUrl: String) => signin(account, redirectUrl + form.hash.getOrElse(""))
|
||||
case _ => signin(account)
|
||||
case _ => signin(account)
|
||||
}
|
||||
case None =>
|
||||
flash += "userName" -> form.userName
|
||||
@@ -100,17 +105,20 @@ trait IndexControllerBase extends ControllerBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an OpenID Connect authentication request.
|
||||
*/
|
||||
* Initiate an OpenID Connect authentication request.
|
||||
*/
|
||||
post("/signin/oidc") {
|
||||
context.settings.oidc.map { oidc =>
|
||||
val redirectURI = new URI(s"$baseUrl/signin/oidc")
|
||||
val authenticationRequest = createOIDCAuthenticationRequest(oidc.issuer, oidc.clientID, redirectURI)
|
||||
val redirectBackURI = flash.get(Keys.Flash.Redirect) match {
|
||||
case Some(redirectBackURI: String) => redirectBackURI + params.getOrElse("hash", "")
|
||||
case _ => "/"
|
||||
case _ => "/"
|
||||
}
|
||||
session.setAttribute(Keys.Session.OidcContext, OidcContext(authenticationRequest.getState, authenticationRequest.getNonce, redirectBackURI))
|
||||
session.setAttribute(
|
||||
Keys.Session.OidcContext,
|
||||
OidcContext(authenticationRequest.getState, authenticationRequest.getNonce, redirectBackURI)
|
||||
)
|
||||
redirect(authenticationRequest.toURI.toString)
|
||||
} getOrElse {
|
||||
NotFound()
|
||||
@@ -118,8 +126,8 @@ trait IndexControllerBase extends ControllerBase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an OpenID Connect authentication response.
|
||||
*/
|
||||
* Handle an OpenID Connect authentication response.
|
||||
*/
|
||||
get("/signin/oidc") {
|
||||
context.settings.oidc.map { oidc =>
|
||||
val redirectURI = new URI(s"$baseUrl/signin/oidc")
|
||||
@@ -142,33 +150,33 @@ trait IndexControllerBase extends ControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
get("/signout"){
|
||||
get("/signout") {
|
||||
session.invalidate
|
||||
redirect("/")
|
||||
}
|
||||
|
||||
get("/activities.atom"){
|
||||
get("/activities.atom") {
|
||||
contentType = "application/atom+xml; type=feed"
|
||||
xml.feed(getRecentActivities())
|
||||
}
|
||||
|
||||
post("/sidebar-collapse"){
|
||||
if(params("collapse") == "true"){
|
||||
post("/sidebar-collapse") {
|
||||
if (params("collapse") == "true") {
|
||||
session.setAttribute("sidebar-collapse", "true")
|
||||
} else {
|
||||
} else {
|
||||
session.setAttribute("sidebar-collapse", null)
|
||||
}
|
||||
Ok()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set account information into HttpSession and redirect.
|
||||
*/
|
||||
* Set account information into HttpSession and redirect.
|
||||
*/
|
||||
private def signin(account: Account, redirectUrl: String = "/") = {
|
||||
session.setAttribute(Keys.Session.LoginAccount, account)
|
||||
updateLastLoginDate(account.userName)
|
||||
|
||||
if(LDAPUtil.isDummyMailAddress(account)) {
|
||||
if (LDAPUtil.isDummyMailAddress(account)) {
|
||||
redirect("/" + account.userName + "/_edit")
|
||||
}
|
||||
|
||||
@@ -184,23 +192,28 @@ trait IndexControllerBase extends ControllerBase {
|
||||
*/
|
||||
get("/_user/proposals")(usersOnly {
|
||||
contentType = formats("json")
|
||||
val user = params("user").toBoolean
|
||||
val user = params("user").toBoolean
|
||||
val group = params("group").toBoolean
|
||||
org.json4s.jackson.Serialization.write(
|
||||
Map("options" -> (
|
||||
getAllUsers(false)
|
||||
.withFilter { t => (user, group) match {
|
||||
case (true, true) => true
|
||||
case (true, false) => !t.isGroupAccount
|
||||
case (false, true) => t.isGroupAccount
|
||||
case (false, false) => false
|
||||
}}.map { t =>
|
||||
Map(
|
||||
"label" -> s"<b>@${t.userName}</b> ${t.fullName}",
|
||||
"value" -> t.userName
|
||||
)
|
||||
}
|
||||
))
|
||||
Map(
|
||||
"options" -> (
|
||||
getAllUsers(false)
|
||||
.withFilter { t =>
|
||||
(user, group) match {
|
||||
case (true, true) => true
|
||||
case (true, false) => !t.isGroupAccount
|
||||
case (false, true) => t.isGroupAccount
|
||||
case (false, false) => false
|
||||
}
|
||||
}
|
||||
.map { t =>
|
||||
Map(
|
||||
"label" -> s"<b>@${t.userName}</b> ${t.fullName}",
|
||||
"value" -> t.userName
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -210,47 +223,68 @@ trait IndexControllerBase extends ControllerBase {
|
||||
*/
|
||||
post("/_user/existence")(usersOnly {
|
||||
getAccountByUserName(params("userName")).map { account =>
|
||||
if(account.isGroupAccount) "group" else "user"
|
||||
if (account.isGroupAccount) "group" else "user"
|
||||
} getOrElse ""
|
||||
})
|
||||
|
||||
// TODO Move to RepositoryViwerController?
|
||||
get("/:owner/:repository/search")(referrersOnly { repository =>
|
||||
defining(params.getOrElse("q", "").trim, params.getOrElse("type", "code")){ case (query, target) =>
|
||||
val page = try {
|
||||
val i = params.getOrElse("page", "1").toInt
|
||||
if(i <= 0) 1 else i
|
||||
} catch {
|
||||
case e: NumberFormatException => 1
|
||||
}
|
||||
defining(params.getOrElse("q", "").trim, params.getOrElse("type", "code")) {
|
||||
case (query, target) =>
|
||||
val page = try {
|
||||
val i = params.getOrElse("page", "1").toInt
|
||||
if (i <= 0) 1 else i
|
||||
} catch {
|
||||
case e: NumberFormatException => 1
|
||||
}
|
||||
|
||||
target.toLowerCase match {
|
||||
case "issue" => gitbucket.core.search.html.issues(
|
||||
if(query.nonEmpty) searchIssues(repository.owner, repository.name, query) else Nil,
|
||||
query, page, repository)
|
||||
target.toLowerCase match {
|
||||
case "issue" =>
|
||||
gitbucket.core.search.html.issues(
|
||||
if (query.nonEmpty) searchIssues(repository.owner, repository.name, query) else Nil,
|
||||
query,
|
||||
page,
|
||||
repository
|
||||
)
|
||||
|
||||
case "wiki" => gitbucket.core.search.html.wiki(
|
||||
if(query.nonEmpty) searchWikiPages(repository.owner, repository.name, query) else Nil,
|
||||
query, page, repository)
|
||||
case "wiki" =>
|
||||
gitbucket.core.search.html.wiki(
|
||||
if (query.nonEmpty) searchWikiPages(repository.owner, repository.name, query) else Nil,
|
||||
query,
|
||||
page,
|
||||
repository
|
||||
)
|
||||
|
||||
case _ => gitbucket.core.search.html.code(
|
||||
if(query.nonEmpty) searchFiles(repository.owner, repository.name, query) else Nil,
|
||||
query, page, repository)
|
||||
}
|
||||
case _ =>
|
||||
gitbucket.core.search.html.code(
|
||||
if (query.nonEmpty) searchFiles(repository.owner, repository.name, query) else Nil,
|
||||
query,
|
||||
page,
|
||||
repository
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
get("/search"){
|
||||
get("/search") {
|
||||
val query = params.getOrElse("query", "").trim.toLowerCase
|
||||
val visibleRepositories = getVisibleRepositories(context.loginAccount, repositoryUserName = None, withoutPhysicalInfo = true)
|
||||
val visibleRepositories =
|
||||
getVisibleRepositories(context.loginAccount, repositoryUserName = None, withoutPhysicalInfo = true)
|
||||
val repositories = visibleRepositories.filter { repository =>
|
||||
repository.name.toLowerCase.indexOf(query) >= 0 || repository.owner.toLowerCase.indexOf(query) >= 0
|
||||
}
|
||||
context.loginAccount.map { account =>
|
||||
gitbucket.core.search.html.repositories(query, repositories, Nil, getUserRepositories(account.userName, withoutPhysicalInfo = true))
|
||||
}.getOrElse {
|
||||
gitbucket.core.search.html.repositories(query, repositories, visibleRepositories, Nil)
|
||||
}
|
||||
context.loginAccount
|
||||
.map { account =>
|
||||
gitbucket.core.search.html.repositories(
|
||||
query,
|
||||
repositories,
|
||||
Nil,
|
||||
getUserRepositories(account.userName, withoutPhysicalInfo = true)
|
||||
)
|
||||
}
|
||||
.getOrElse {
|
||||
gitbucket.core.search.html.repositories(query, repositories, visibleRepositories, Nil)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,23 +11,23 @@ import gitbucket.core.view.Markdown
|
||||
import org.scalatra.forms._
|
||||
import org.scalatra.{BadRequest, Ok}
|
||||
|
||||
|
||||
class IssuesController extends IssuesControllerBase
|
||||
with IssuesService
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with LabelsService
|
||||
with MilestonesService
|
||||
with ActivityService
|
||||
with HandleCommentService
|
||||
with IssueCreationService
|
||||
with ReadableUsersAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
with PullRequestService
|
||||
with WebHookIssueCommentService
|
||||
with CommitsService
|
||||
with PrioritiesService
|
||||
class IssuesController
|
||||
extends IssuesControllerBase
|
||||
with IssuesService
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with LabelsService
|
||||
with MilestonesService
|
||||
with ActivityService
|
||||
with HandleCommentService
|
||||
with IssueCreationService
|
||||
with ReadableUsersAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
with PullRequestService
|
||||
with WebHookIssueCommentService
|
||||
with CommitsService
|
||||
with PrioritiesService
|
||||
|
||||
trait IssuesControllerBase extends ControllerBase {
|
||||
self: IssuesService
|
||||
@@ -45,40 +45,46 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
with WebHookIssueCommentService
|
||||
with PrioritiesService =>
|
||||
|
||||
case class IssueCreateForm(title: String, content: Option[String],
|
||||
assignedUserName: Option[String], milestoneId: Option[Int], priorityId: Option[Int], labelNames: Option[String])
|
||||
case class IssueCreateForm(
|
||||
title: String,
|
||||
content: Option[String],
|
||||
assignedUserName: Option[String],
|
||||
milestoneId: Option[Int],
|
||||
priorityId: Option[Int],
|
||||
labelNames: Option[String]
|
||||
)
|
||||
case class CommentForm(issueId: Int, content: String)
|
||||
case class IssueStateForm(issueId: Int, content: Option[String])
|
||||
|
||||
val issueCreateForm = mapping(
|
||||
"title" -> trim(label("Title", text(required))),
|
||||
"content" -> trim(optional(text())),
|
||||
"assignedUserName" -> trim(optional(text())),
|
||||
"milestoneId" -> trim(optional(number())),
|
||||
"priorityId" -> trim(optional(number())),
|
||||
"labelNames" -> trim(optional(text()))
|
||||
)(IssueCreateForm.apply)
|
||||
"title" -> trim(label("Title", text(required))),
|
||||
"content" -> trim(optional(text())),
|
||||
"assignedUserName" -> trim(optional(text())),
|
||||
"milestoneId" -> trim(optional(number())),
|
||||
"priorityId" -> trim(optional(number())),
|
||||
"labelNames" -> trim(optional(text()))
|
||||
)(IssueCreateForm.apply)
|
||||
|
||||
val issueTitleEditForm = mapping(
|
||||
"title" -> trim(label("Title", text(required)))
|
||||
)(x => x)
|
||||
)(x => x)
|
||||
val issueEditForm = mapping(
|
||||
"content" -> trim(optional(text()))
|
||||
)(x => x)
|
||||
)(x => x)
|
||||
|
||||
val commentForm = mapping(
|
||||
"issueId" -> label("Issue Id", number()),
|
||||
"content" -> trim(label("Comment", text(required)))
|
||||
)(CommentForm.apply)
|
||||
"issueId" -> label("Issue Id", number()),
|
||||
"content" -> trim(label("Comment", text(required)))
|
||||
)(CommentForm.apply)
|
||||
|
||||
val issueStateForm = mapping(
|
||||
"issueId" -> label("Issue Id", number()),
|
||||
"content" -> trim(optional(text()))
|
||||
)(IssueStateForm.apply)
|
||||
"issueId" -> label("Issue Id", number()),
|
||||
"content" -> trim(optional(text()))
|
||||
)(IssueStateForm.apply)
|
||||
|
||||
get("/:owner/:repository/issues")(referrersOnly { repository =>
|
||||
val q = request.getParameter("q")
|
||||
if(Option(q).exists(_.contains("is:pr"))){
|
||||
if (Option(q).exists(_.contains("is:pr"))) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/pulls?q=${StringUtil.urlEncode(q)}")
|
||||
} else {
|
||||
searchIssues(repository)
|
||||
@@ -86,45 +92,50 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
get("/:owner/:repository/issues/:id")(referrersOnly { repository =>
|
||||
defining(repository.owner, repository.name, params("id")){ case (owner, name, issueId) =>
|
||||
getIssue(owner, name, issueId) map { issue =>
|
||||
if(issue.isPullRequest){
|
||||
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
|
||||
} else {
|
||||
html.issue(
|
||||
issue,
|
||||
getComments(owner, name, issueId.toInt),
|
||||
getIssueLabels(owner, name, issueId.toInt),
|
||||
getAssignableUserNames(owner, name),
|
||||
getMilestonesWithIssueCount(owner, name),
|
||||
getPriorities(owner, name),
|
||||
getLabels(owner, name),
|
||||
isIssueEditable(repository),
|
||||
isIssueManageable(repository),
|
||||
repository)
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
defining(repository.owner, repository.name, params("id")) {
|
||||
case (owner, name, issueId) =>
|
||||
getIssue(owner, name, issueId) map {
|
||||
issue =>
|
||||
if (issue.isPullRequest) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
|
||||
} else {
|
||||
html.issue(
|
||||
issue,
|
||||
getComments(owner, name, issueId.toInt),
|
||||
getIssueLabels(owner, name, issueId.toInt),
|
||||
getAssignableUserNames(owner, name),
|
||||
getMilestonesWithIssueCount(owner, name),
|
||||
getPriorities(owner, name),
|
||||
getLabels(owner, name),
|
||||
isIssueEditable(repository),
|
||||
isIssueManageable(repository),
|
||||
repository
|
||||
)
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
get("/:owner/:repository/issues/new")(readableUsersOnly { repository =>
|
||||
if(isIssueEditable(repository)){ // TODO Should this check is provided by authenticator?
|
||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||
html.create(
|
||||
getAssignableUserNames(owner, name),
|
||||
getMilestones(owner, name),
|
||||
getPriorities(owner, name),
|
||||
getDefaultPriority(owner, name),
|
||||
getLabels(owner, name),
|
||||
isIssueManageable(repository),
|
||||
getContentTemplate(repository, "ISSUE_TEMPLATE"),
|
||||
repository)
|
||||
if (isIssueEditable(repository)) { // TODO Should this check is provided by authenticator?
|
||||
defining(repository.owner, repository.name) {
|
||||
case (owner, name) =>
|
||||
html.create(
|
||||
getAssignableUserNames(owner, name),
|
||||
getMilestones(owner, name),
|
||||
getPriorities(owner, name),
|
||||
getDefaultPriority(owner, name),
|
||||
getLabels(owner, name),
|
||||
isIssueManageable(repository),
|
||||
getContentTemplate(repository, "ISSUE_TEMPLATE"),
|
||||
repository
|
||||
)
|
||||
}
|
||||
} else Unauthorized()
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/new", issueCreateForm)(readableUsersOnly { (form, repository) =>
|
||||
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(
|
||||
repository,
|
||||
form.title,
|
||||
@@ -133,133 +144,146 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
form.milestoneId,
|
||||
form.priorityId,
|
||||
form.labelNames.toArray.flatMap(_.split(",")),
|
||||
context.loginAccount.get)
|
||||
context.loginAccount.get
|
||||
)
|
||||
|
||||
redirect(s"/${issue.userName}/${issue.repositoryName}/issues/${issue.issueId}")
|
||||
} else Unauthorized()
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/edit_title/:id", issueTitleEditForm)(readableUsersOnly { (title, repository) =>
|
||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||
getIssue(owner, name, params("id")).map { issue =>
|
||||
if(isEditableContent(owner, name, issue.openedUserName)){
|
||||
// update issue
|
||||
updateIssue(owner, name, issue.issueId, title, issue.content)
|
||||
// extract references and create refer comment
|
||||
createReferComment(owner, name, issue.copy(title = title), title, context.loginAccount.get)
|
||||
defining(repository.owner, repository.name) {
|
||||
case (owner, name) =>
|
||||
getIssue(owner, name, params("id")).map { issue =>
|
||||
if (isEditableContent(owner, name, issue.openedUserName)) {
|
||||
// update issue
|
||||
updateIssue(owner, name, issue.issueId, title, issue.content)
|
||||
// extract references and create refer comment
|
||||
createReferComment(owner, name, issue.copy(title = title), title, context.loginAccount.get)
|
||||
|
||||
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/edit/:id", issueEditForm)(readableUsersOnly { (content, repository) =>
|
||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||
getIssue(owner, name, params("id")).map { issue =>
|
||||
if(isEditableContent(owner, name, issue.openedUserName)){
|
||||
// update issue
|
||||
updateIssue(owner, name, issue.issueId, issue.title, content)
|
||||
// extract references and create refer comment
|
||||
createReferComment(owner, name, issue, content.getOrElse(""), context.loginAccount.get)
|
||||
defining(repository.owner, repository.name) {
|
||||
case (owner, name) =>
|
||||
getIssue(owner, name, params("id")).map { issue =>
|
||||
if (isEditableContent(owner, name, issue.openedUserName)) {
|
||||
// update issue
|
||||
updateIssue(owner, name, issue.issueId, issue.title, content)
|
||||
// extract references and create refer comment
|
||||
createReferComment(owner, name, issue, content.getOrElse(""), context.loginAccount.get)
|
||||
|
||||
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
redirect(s"/${owner}/${name}/issues/_data/${issue.issueId}")
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) =>
|
||||
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
|
||||
val actionOpt = params.get("action").filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||
handleComment(issue, Some(form.content), repository, actionOpt) map { case (issue, id) =>
|
||||
redirect(s"/${repository.owner}/${repository.name}/${
|
||||
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
||||
val actionOpt =
|
||||
params.get("action").filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||
handleComment(issue, Some(form.content), repository, actionOpt) map {
|
||||
case (issue, id) =>
|
||||
redirect(
|
||||
s"/${repository.owner}/${repository.name}/${if (issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}"
|
||||
)
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issue_comments/state", issueStateForm)(readableUsersOnly { (form, repository) =>
|
||||
getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
|
||||
val actionOpt = params.get("action").filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||
handleComment(issue, form.content, repository, actionOpt) map { case (issue, id) =>
|
||||
redirect(s"/${repository.owner}/${repository.name}/${
|
||||
if(issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}")
|
||||
val actionOpt =
|
||||
params.get("action").filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName))
|
||||
handleComment(issue, form.content, repository, actionOpt) map {
|
||||
case (issue, id) =>
|
||||
redirect(
|
||||
s"/${repository.owner}/${repository.name}/${if (issue.isPullRequest) "pull" else "issues"}/${form.issueId}#comment-${id}"
|
||||
)
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issue_comments/edit/:id", commentForm)(readableUsersOnly { (form, repository) =>
|
||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||
getComment(owner, name, params("id")).map { comment =>
|
||||
if(isEditableContent(owner, name, comment.commentedUserName)){
|
||||
updateComment(comment.issueId, comment.commentId, form.content)
|
||||
redirect(s"/${owner}/${name}/issue_comments/_data/${comment.commentId}")
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
defining(repository.owner, repository.name) {
|
||||
case (owner, name) =>
|
||||
getComment(owner, name, params("id")).map { comment =>
|
||||
if (isEditableContent(owner, name, comment.commentedUserName)) {
|
||||
updateComment(comment.issueId, comment.commentId, form.content)
|
||||
redirect(s"/${owner}/${name}/issue_comments/_data/${comment.commentId}")
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issue_comments/delete/:id")(readableUsersOnly { repository =>
|
||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||
getComment(owner, name, params("id")).map { comment =>
|
||||
if(isEditableContent(owner, name, comment.commentedUserName)){
|
||||
Ok(deleteComment(comment.issueId, comment.commentId))
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
defining(repository.owner, repository.name) {
|
||||
case (owner, name) =>
|
||||
getComment(owner, name, params("id")).map { comment =>
|
||||
if (isEditableContent(owner, name, comment.commentedUserName)) {
|
||||
Ok(deleteComment(comment.issueId, comment.commentId))
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
ajaxGet("/:owner/:repository/issues/_data/:id")(readableUsersOnly { repository =>
|
||||
getIssue(repository.owner, repository.name, params("id")) map { x =>
|
||||
if(isEditableContent(x.userName, x.repositoryName, x.openedUserName)){
|
||||
params.get("dataType") collect {
|
||||
case t if t == "html" => html.editissue(x.content, x.issueId, repository)
|
||||
} getOrElse {
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(
|
||||
Map(
|
||||
"title" -> x.title,
|
||||
"content" -> Markdown.toHtml(
|
||||
markdown = x.content getOrElse "No description given.",
|
||||
repository = repository,
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableAnchor = true,
|
||||
enableLineBreaks = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = true
|
||||
getIssue(repository.owner, repository.name, params("id")) map {
|
||||
x =>
|
||||
if (isEditableContent(x.userName, x.repositoryName, x.openedUserName)) {
|
||||
params.get("dataType") collect {
|
||||
case t if t == "html" => html.editissue(x.content, x.issueId, repository)
|
||||
} getOrElse {
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(
|
||||
Map(
|
||||
"title" -> x.title,
|
||||
"content" -> Markdown.toHtml(
|
||||
markdown = x.content getOrElse "No description given.",
|
||||
repository = repository,
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableAnchor = true,
|
||||
enableLineBreaks = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
} else Unauthorized()
|
||||
}
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
ajaxGet("/:owner/:repository/issue_comments/_data/:id")(readableUsersOnly { repository =>
|
||||
getComment(repository.owner, repository.name, params("id")) map { x =>
|
||||
if(isEditableContent(x.userName, x.repositoryName, x.commentedUserName)){
|
||||
params.get("dataType") collect {
|
||||
case t if t == "html" => html.editcomment(x.content, x.commentId, repository)
|
||||
} getOrElse {
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(
|
||||
Map(
|
||||
"content" -> view.Markdown.toHtml(
|
||||
markdown = x.content,
|
||||
repository = repository,
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableAnchor = true,
|
||||
enableLineBreaks = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = true
|
||||
getComment(repository.owner, repository.name, params("id")) map {
|
||||
x =>
|
||||
if (isEditableContent(x.userName, x.repositoryName, x.commentedUserName)) {
|
||||
params.get("dataType") collect {
|
||||
case t if t == "html" => html.editcomment(x.content, x.commentId, repository)
|
||||
} getOrElse {
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(
|
||||
Map(
|
||||
"content" -> view.Markdown.toHtml(
|
||||
markdown = x.content,
|
||||
repository = repository,
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableAnchor = true,
|
||||
enableLineBreaks = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
} else Unauthorized()
|
||||
}
|
||||
} else Unauthorized()
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -270,21 +294,27 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/:id/label/new")(writableUsersOnly { repository =>
|
||||
defining(params("id").toInt){ issueId =>
|
||||
defining(params("id").toInt) { issueId =>
|
||||
registerIssueLabel(repository.owner, repository.name, issueId, params("labelId").toInt, true)
|
||||
html.labellist(getIssueLabels(repository.owner, repository.name, issueId))
|
||||
}
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/:id/label/delete")(writableUsersOnly { repository =>
|
||||
defining(params("id").toInt){ issueId =>
|
||||
defining(params("id").toInt) { issueId =>
|
||||
deleteIssueLabel(repository.owner, repository.name, issueId, params("labelId").toInt, true)
|
||||
html.labellist(getIssueLabels(repository.owner, repository.name, issueId))
|
||||
}
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/:id/assign")(writableUsersOnly { repository =>
|
||||
updateAssignedUserName(repository.owner, repository.name, params("id").toInt, assignedUserName("assignedUserName"), true)
|
||||
updateAssignedUserName(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
params("id").toInt,
|
||||
assignedUserName("assignedUserName"),
|
||||
true
|
||||
)
|
||||
Ok("updated")
|
||||
})
|
||||
|
||||
@@ -292,9 +322,11 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
updateMilestoneId(repository.owner, repository.name, params("id").toInt, milestoneId("milestoneId"), true)
|
||||
milestoneId("milestoneId").map { milestoneId =>
|
||||
getMilestonesWithIssueCount(repository.owner, repository.name)
|
||||
.find(_._1.milestoneId == milestoneId).map { case (_, openCount, closeCount) =>
|
||||
gitbucket.core.issues.milestones.html.progress(openCount + closeCount, closeCount)
|
||||
} getOrElse NotFound()
|
||||
.find(_._1.milestoneId == milestoneId)
|
||||
.map {
|
||||
case (_, openCount, closeCount) =>
|
||||
gitbucket.core.issues.milestones.html.progress(openCount + closeCount, closeCount)
|
||||
} getOrElse NotFound()
|
||||
} getOrElse Ok()
|
||||
})
|
||||
|
||||
@@ -305,25 +337,28 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/batchedit/state")(writableUsersOnly { repository =>
|
||||
defining(params.get("value")){ action =>
|
||||
action match {
|
||||
case Some("open") => executeBatch(repository) { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
handleComment(issue, None, repository, Some("reopen"))
|
||||
}
|
||||
defining(params.get("value")) {
|
||||
action =>
|
||||
action match {
|
||||
case Some("open") =>
|
||||
executeBatch(repository) { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
handleComment(issue, None, repository, Some("reopen"))
|
||||
}
|
||||
}
|
||||
case Some("close") =>
|
||||
executeBatch(repository) { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
handleComment(issue, None, repository, Some("close"))
|
||||
}
|
||||
}
|
||||
case _ => BadRequest()
|
||||
}
|
||||
case Some("close") => executeBatch(repository) { issueId =>
|
||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||
handleComment(issue, None, repository, Some("close"))
|
||||
}
|
||||
}
|
||||
case _ => BadRequest()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/batchedit/label")(writableUsersOnly { repository =>
|
||||
params("value").toIntOpt.map{ labelId =>
|
||||
params("value").toIntOpt.map { labelId =>
|
||||
executeBatch(repository) { issueId =>
|
||||
getIssueLabel(repository.owner, repository.name, issueId, labelId) getOrElse {
|
||||
registerIssueLabel(repository.owner, repository.name, issueId, labelId, true)
|
||||
@@ -333,7 +368,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/batchedit/assign")(writableUsersOnly { repository =>
|
||||
defining(assignedUserName("value")){ value =>
|
||||
defining(assignedUserName("value")) { value =>
|
||||
executeBatch(repository) {
|
||||
updateAssignedUserName(repository.owner, repository.name, _, value, true)
|
||||
}
|
||||
@@ -341,7 +376,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/batchedit/milestone")(writableUsersOnly { repository =>
|
||||
defining(milestoneId("value")){ value =>
|
||||
defining(milestoneId("value")) { value =>
|
||||
executeBatch(repository) {
|
||||
updateMilestoneId(repository.owner, repository.name, _, value, true)
|
||||
}
|
||||
@@ -349,7 +384,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/batchedit/priority")(writableUsersOnly { repository =>
|
||||
defining(priorityId("value")){ value =>
|
||||
defining(priorityId("value")) { value =>
|
||||
executeBatch(repository) {
|
||||
updatePriorityId(repository.owner, repository.name, _, value, true)
|
||||
}
|
||||
@@ -358,7 +393,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
|
||||
get("/:owner/:repository/_attached/:file")(referrersOnly { repository =>
|
||||
(Directory.getAttachedDir(repository.owner, repository.name) match {
|
||||
case dir if(dir.exists && dir.isDirectory) =>
|
||||
case dir if (dir.exists && dir.isDirectory) =>
|
||||
dir.listFiles.find(_.getName.startsWith(params("file") + ".")).map { file =>
|
||||
response.setHeader("Content-Disposition", f"""inline; filename=${file.getName}""")
|
||||
RawData(FileUtil.getMimeType(file.getName), file)
|
||||
@@ -372,7 +407,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
val priorityId: String => Option[Int] = (key: String) => params.get(key).flatMap(_.toIntOpt)
|
||||
|
||||
private def executeBatch(repository: RepositoryService.RepositoryInfo)(execute: Int => Unit) = {
|
||||
params("checked").split(',') map(_.toInt) foreach execute
|
||||
params("checked").split(',') map (_.toInt) foreach execute
|
||||
params("from") match {
|
||||
case "issues" => redirect(s"/${repository.owner}/${repository.name}/issues")
|
||||
case "pulls" => redirect(s"/${repository.owner}/${repository.name}/pulls")
|
||||
@@ -380,13 +415,14 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
}
|
||||
|
||||
private def searchIssues(repository: RepositoryService.RepositoryInfo) = {
|
||||
defining(repository.owner, repository.name){ case (owner, repoName) =>
|
||||
val page = IssueSearchCondition.page(request)
|
||||
defining(repository.owner, repository.name) {
|
||||
case (owner, repoName) =>
|
||||
val page = IssueSearchCondition.page(request)
|
||||
|
||||
// retrieve search condition
|
||||
val condition = IssueSearchCondition(request)
|
||||
// retrieve search condition
|
||||
val condition = IssueSearchCondition(request)
|
||||
|
||||
html.list(
|
||||
html.list(
|
||||
"issues",
|
||||
searchIssue(condition, false, (page - 1) * IssueLimit, IssueLimit, owner -> repoName),
|
||||
page,
|
||||
@@ -394,19 +430,22 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
getMilestones(owner, repoName),
|
||||
getPriorities(owner, repoName),
|
||||
getLabels(owner, repoName),
|
||||
countIssue(condition.copy(state = "open" ), false, owner -> repoName),
|
||||
countIssue(condition.copy(state = "open"), false, owner -> repoName),
|
||||
countIssue(condition.copy(state = "closed"), false, owner -> repoName),
|
||||
condition,
|
||||
repository,
|
||||
isIssueEditable(repository),
|
||||
isIssueManageable(repository))
|
||||
isIssueManageable(repository)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether an issue or a comment is editable by a logged-in user.
|
||||
*/
|
||||
private def isEditableContent(owner: String, repository: String, author: String)(implicit context: Context): Boolean = {
|
||||
private def isEditableContent(owner: String, repository: String, author: String)(
|
||||
implicit context: Context
|
||||
): Boolean = {
|
||||
hasDeveloperRole(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import gitbucket.core.issues.labels.html
|
||||
import gitbucket.core.service.{RepositoryService, AccountService, IssuesService, LabelsService, MilestonesService, PrioritiesService}
|
||||
import gitbucket.core.service.{
|
||||
RepositoryService,
|
||||
AccountService,
|
||||
IssuesService,
|
||||
LabelsService,
|
||||
MilestonesService,
|
||||
PrioritiesService
|
||||
}
|
||||
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
@@ -9,29 +16,38 @@ import org.scalatra.forms._
|
||||
import org.scalatra.i18n.Messages
|
||||
import org.scalatra.Ok
|
||||
|
||||
class LabelsController extends LabelsControllerBase
|
||||
with IssuesService with RepositoryService with AccountService
|
||||
with LabelsService with PrioritiesService with MilestonesService
|
||||
with ReferrerAuthenticator with WritableUsersAuthenticator
|
||||
class LabelsController
|
||||
extends LabelsControllerBase
|
||||
with IssuesService
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with LabelsService
|
||||
with PrioritiesService
|
||||
with MilestonesService
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
|
||||
trait LabelsControllerBase extends ControllerBase {
|
||||
self: LabelsService with IssuesService with RepositoryService
|
||||
with ReferrerAuthenticator with WritableUsersAuthenticator =>
|
||||
self: LabelsService
|
||||
with IssuesService
|
||||
with RepositoryService
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator =>
|
||||
|
||||
case class LabelForm(labelName: String, color: String)
|
||||
|
||||
val labelForm = mapping(
|
||||
"labelName" -> trim(label("Label name", text(required, labelName, uniqueLabelName, maxlength(100)))),
|
||||
"labelColor" -> trim(label("Color", text(required, color)))
|
||||
"labelName" -> trim(label("Label name", text(required, labelName, uniqueLabelName, maxlength(100)))),
|
||||
"labelColor" -> trim(label("Color", text(required, color)))
|
||||
)(LabelForm.apply)
|
||||
|
||||
|
||||
get("/:owner/:repository/issues/labels")(referrersOnly { repository =>
|
||||
html.list(
|
||||
getLabels(repository.owner, repository.name),
|
||||
countIssueGroupByLabels(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
|
||||
repository,
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
||||
)
|
||||
})
|
||||
|
||||
ajaxGet("/:owner/:repository/issues/labels/new")(writableUsersOnly { repository =>
|
||||
@@ -45,7 +61,8 @@ trait LabelsControllerBase extends ControllerBase {
|
||||
// TODO futility
|
||||
countIssueGroupByLabels(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
|
||||
repository,
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
||||
)
|
||||
})
|
||||
|
||||
ajaxGet("/:owner/:repository/issues/labels/:labelId/edit")(writableUsersOnly { repository =>
|
||||
@@ -61,7 +78,8 @@ trait LabelsControllerBase extends ControllerBase {
|
||||
// TODO futility
|
||||
countIssueGroupByLabels(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
|
||||
repository,
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
||||
)
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/labels/:labelId/delete")(writableUsersOnly { repository =>
|
||||
@@ -72,26 +90,34 @@ trait LabelsControllerBase extends ControllerBase {
|
||||
/**
|
||||
* Constraint for the identifier such as user name, repository name or page name.
|
||||
*/
|
||||
private def labelName: Constraint = new Constraint(){
|
||||
private def labelName: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if(value.contains(',')){
|
||||
if (value.contains(',')) {
|
||||
Some(s"${name} contains invalid character.")
|
||||
} else if(value.startsWith("_") || value.startsWith("-")){
|
||||
} else if (value.startsWith("_") || value.startsWith("-")) {
|
||||
Some(s"${name} starts with invalid character.")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private def uniqueLabelName: Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, params: Map[String, Seq[String]], messages: Messages): Option[String] = {
|
||||
val owner = params.value("owner")
|
||||
private def uniqueLabelName: Constraint = new Constraint() {
|
||||
override def validate(
|
||||
name: String,
|
||||
value: String,
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
val owner = params.value("owner")
|
||||
val repository = params.value("repository")
|
||||
params.optionValue("labelId").map { labelId =>
|
||||
getLabel(owner, repository, value).filter(_.labelId != labelId.toInt).map(_ => "Name has already been taken.")
|
||||
}.getOrElse {
|
||||
getLabel(owner, repository, value).map(_ => "Name has already been taken.")
|
||||
}
|
||||
params
|
||||
.optionValue("labelId")
|
||||
.map { labelId =>
|
||||
getLabel(owner, repository, value).filter(_.labelId != labelId.toInt).map(_ => "Name has already been taken.")
|
||||
}
|
||||
.getOrElse {
|
||||
getLabel(owner, repository, value).map(_ => "Name has already been taken.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,20 +6,23 @@ import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
||||
import gitbucket.core.util.Implicits._
|
||||
import org.scalatra.forms._
|
||||
|
||||
class MilestonesController extends MilestonesControllerBase
|
||||
with MilestonesService with RepositoryService with AccountService
|
||||
with ReferrerAuthenticator with WritableUsersAuthenticator
|
||||
class MilestonesController
|
||||
extends MilestonesControllerBase
|
||||
with MilestonesService
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
|
||||
trait MilestonesControllerBase extends ControllerBase {
|
||||
self: MilestonesService with RepositoryService
|
||||
with ReferrerAuthenticator with WritableUsersAuthenticator =>
|
||||
self: MilestonesService with RepositoryService with ReferrerAuthenticator with WritableUsersAuthenticator =>
|
||||
|
||||
case class MilestoneForm(title: String, description: Option[String], dueDate: Option[java.util.Date])
|
||||
|
||||
val milestoneForm = mapping(
|
||||
"title" -> trim(label("Title", text(required, maxlength(100)))),
|
||||
"title" -> trim(label("Title", text(required, maxlength(100)))),
|
||||
"description" -> trim(label("Description", optional(text()))),
|
||||
"dueDate" -> trim(label("Due Date", optional(date())))
|
||||
"dueDate" -> trim(label("Due Date", optional(date())))
|
||||
)(MilestoneForm.apply)
|
||||
|
||||
get("/:owner/:repository/issues/milestones")(referrersOnly { repository =>
|
||||
@@ -27,7 +30,8 @@ trait MilestonesControllerBase extends ControllerBase {
|
||||
params.getOrElse("state", "open"),
|
||||
getMilestonesWithIssueCount(repository.owner, repository.name),
|
||||
repository,
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
||||
)
|
||||
})
|
||||
|
||||
get("/:owner/:repository/issues/milestones/new")(writableUsersOnly {
|
||||
@@ -40,22 +44,23 @@ trait MilestonesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
get("/:owner/:repository/issues/milestones/:milestoneId/edit")(writableUsersOnly { repository =>
|
||||
params("milestoneId").toIntOpt.map{ milestoneId =>
|
||||
params("milestoneId").toIntOpt.map { milestoneId =>
|
||||
html.edit(getMilestone(repository.owner, repository.name, milestoneId), repository)
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
post("/:owner/:repository/issues/milestones/:milestoneId/edit", milestoneForm)(writableUsersOnly { (form, repository) =>
|
||||
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
||||
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
||||
updateMilestone(milestone.copy(title = form.title, description = form.description, dueDate = form.dueDate))
|
||||
redirect(s"/${repository.owner}/${repository.name}/issues/milestones")
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
post("/:owner/:repository/issues/milestones/:milestoneId/edit", milestoneForm)(writableUsersOnly {
|
||||
(form, repository) =>
|
||||
params("milestoneId").toIntOpt.flatMap { milestoneId =>
|
||||
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
||||
updateMilestone(milestone.copy(title = form.title, description = form.description, dueDate = form.dueDate))
|
||||
redirect(s"/${repository.owner}/${repository.name}/issues/milestones")
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
get("/:owner/:repository/issues/milestones/:milestoneId/close")(writableUsersOnly { repository =>
|
||||
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
||||
params("milestoneId").toIntOpt.flatMap { milestoneId =>
|
||||
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
||||
closeMilestone(milestone)
|
||||
redirect(s"/${repository.owner}/${repository.name}/issues/milestones")
|
||||
@@ -64,7 +69,7 @@ trait MilestonesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
get("/:owner/:repository/issues/milestones/:milestoneId/open")(writableUsersOnly { repository =>
|
||||
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
||||
params("milestoneId").toIntOpt.flatMap { milestoneId =>
|
||||
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
||||
openMilestone(milestone)
|
||||
redirect(s"/${repository.owner}/${repository.name}/issues/milestones")
|
||||
@@ -73,7 +78,7 @@ trait MilestonesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
get("/:owner/:repository/issues/milestones/:milestoneId/delete")(writableUsersOnly { repository =>
|
||||
params("milestoneId").toIntOpt.flatMap{ milestoneId =>
|
||||
params("milestoneId").toIntOpt.flatMap { milestoneId =>
|
||||
getMilestone(repository.owner, repository.name, milestoneId).map { milestone =>
|
||||
deleteMilestone(repository.owner, repository.name, milestone.milestoneId)
|
||||
redirect(s"/${repository.owner}/${repository.name}/issues/milestones")
|
||||
|
||||
@@ -28,13 +28,12 @@ trait PreProcessControllerBase extends ControllerBase {
|
||||
* But if it's not allowed, demands authentication except some paths.
|
||||
*/
|
||||
get(!context.settings.allowAnonymousAccess, context.loginAccount.isEmpty) {
|
||||
if(!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") &&
|
||||
!context.currentPath.startsWith("/register") && !context.currentPath.endsWith("/info/refs")) {
|
||||
if (!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") &&
|
||||
!context.currentPath.startsWith("/register") && !context.currentPath.endsWith("/info/refs")) {
|
||||
Unauthorized()
|
||||
} else {
|
||||
pass()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import gitbucket.core.issues.priorities.html
|
||||
import gitbucket.core.service.{RepositoryService, AccountService, IssuesService, LabelsService, MilestonesService, PrioritiesService}
|
||||
import gitbucket.core.service.{
|
||||
RepositoryService,
|
||||
AccountService,
|
||||
IssuesService,
|
||||
LabelsService,
|
||||
MilestonesService,
|
||||
PrioritiesService
|
||||
}
|
||||
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.SyntaxSugars._
|
||||
@@ -9,30 +16,39 @@ import org.scalatra.forms._
|
||||
import org.scalatra.i18n.Messages
|
||||
import org.scalatra.Ok
|
||||
|
||||
class PrioritiesController extends PrioritiesControllerBase
|
||||
with IssuesService with RepositoryService with AccountService
|
||||
with LabelsService with PrioritiesService with MilestonesService
|
||||
with ReferrerAuthenticator with WritableUsersAuthenticator
|
||||
class PrioritiesController
|
||||
extends PrioritiesControllerBase
|
||||
with IssuesService
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with LabelsService
|
||||
with PrioritiesService
|
||||
with MilestonesService
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
|
||||
trait PrioritiesControllerBase extends ControllerBase {
|
||||
self: PrioritiesService with IssuesService with RepositoryService
|
||||
with ReferrerAuthenticator with WritableUsersAuthenticator =>
|
||||
self: PrioritiesService
|
||||
with IssuesService
|
||||
with RepositoryService
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator =>
|
||||
|
||||
case class PriorityForm(priorityName: String, description: Option[String], color: String)
|
||||
|
||||
val priorityForm = mapping(
|
||||
"priorityName" -> trim(label("Priority name", text(required, priorityName, uniquePriorityName, maxlength(100)))),
|
||||
"description" -> trim(label("Description", optional(text(maxlength(255))))),
|
||||
"priorityName" -> trim(label("Priority name", text(required, priorityName, uniquePriorityName, maxlength(100)))),
|
||||
"description" -> trim(label("Description", optional(text(maxlength(255))))),
|
||||
"priorityColor" -> trim(label("Color", text(required, color)))
|
||||
)(PriorityForm.apply)
|
||||
|
||||
|
||||
get("/:owner/:repository/issues/priorities")(referrersOnly { repository =>
|
||||
html.list(
|
||||
getPriorities(repository.owner, repository.name),
|
||||
countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
|
||||
repository,
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
||||
)
|
||||
})
|
||||
|
||||
ajaxGet("/:owner/:repository/issues/priorities/new")(writableUsersOnly { repository =>
|
||||
@@ -40,12 +56,14 @@ trait PrioritiesControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/priorities/new", priorityForm)(writableUsersOnly { (form, repository) =>
|
||||
val priorityId = createPriority(repository.owner, repository.name, form.priorityName, form.description, form.color.substring(1))
|
||||
val priorityId =
|
||||
createPriority(repository.owner, repository.name, form.priorityName, form.description, form.color.substring(1))
|
||||
html.priority(
|
||||
getPriority(repository.owner, repository.name, priorityId).get,
|
||||
countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
|
||||
repository,
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
||||
)
|
||||
})
|
||||
|
||||
ajaxGet("/:owner/:repository/issues/priorities/:priorityId/edit")(writableUsersOnly { repository =>
|
||||
@@ -54,21 +72,34 @@ trait PrioritiesControllerBase extends ControllerBase {
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/priorities/:priorityId/edit", priorityForm)(writableUsersOnly { (form, repository) =>
|
||||
updatePriority(repository.owner, repository.name, params("priorityId").toInt, form.priorityName, form.description, form.color.substring(1))
|
||||
html.priority(
|
||||
getPriority(repository.owner, repository.name, params("priorityId").toInt).get,
|
||||
countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
|
||||
repository,
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||
ajaxPost("/:owner/:repository/issues/priorities/:priorityId/edit", priorityForm)(writableUsersOnly {
|
||||
(form, repository) =>
|
||||
updatePriority(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
params("priorityId").toInt,
|
||||
form.priorityName,
|
||||
form.description,
|
||||
form.color.substring(1)
|
||||
)
|
||||
html.priority(
|
||||
getPriority(repository.owner, repository.name, params("priorityId").toInt).get,
|
||||
countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
|
||||
repository,
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
||||
)
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/priorities/reorder")(writableUsersOnly { (repository) =>
|
||||
reorderPriorities(repository.owner, repository.name, params("order")
|
||||
.split(",")
|
||||
.map(id => id.toInt)
|
||||
.zipWithIndex
|
||||
.toMap)
|
||||
reorderPriorities(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
params("order")
|
||||
.split(",")
|
||||
.map(id => id.toInt)
|
||||
.zipWithIndex
|
||||
.toMap
|
||||
)
|
||||
|
||||
Ok()
|
||||
})
|
||||
@@ -88,26 +119,36 @@ trait PrioritiesControllerBase extends ControllerBase {
|
||||
/**
|
||||
* Constraint for the identifier such as user name, repository name or page name.
|
||||
*/
|
||||
private def priorityName: Constraint = new Constraint(){
|
||||
private def priorityName: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if(value.contains(',')){
|
||||
if (value.contains(',')) {
|
||||
Some(s"${name} contains invalid character.")
|
||||
} else if(value.startsWith("_") || value.startsWith("-")){
|
||||
} else if (value.startsWith("_") || value.startsWith("-")) {
|
||||
Some(s"${name} starts with invalid character.")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private def uniquePriorityName: Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, params: Map[String, Seq[String]], messages: Messages): Option[String] = {
|
||||
val owner = params.value("owner")
|
||||
private def uniquePriorityName: Constraint = new Constraint() {
|
||||
override def validate(
|
||||
name: String,
|
||||
value: String,
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
val owner = params.value("owner")
|
||||
val repository = params.value("repository")
|
||||
params.optionValue("priorityId").map { priorityId =>
|
||||
getPriority(owner, repository, value).filter(_.priorityId != priorityId.toInt).map(_ => "Name has already been taken.")
|
||||
}.getOrElse {
|
||||
getPriority(owner, repository, value).map(_ => "Name has already been taken.")
|
||||
}
|
||||
params
|
||||
.optionValue("priorityId")
|
||||
.map { priorityId =>
|
||||
getPriority(owner, repository, value)
|
||||
.filter(_.priorityId != priorityId.toInt)
|
||||
.map(_ => "Name has already been taken.")
|
||||
}
|
||||
.getOrElse {
|
||||
getPriority(owner, repository, value).map(_ => "Name has already been taken.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,14 +11,15 @@ import gitbucket.core.releases.html
|
||||
import org.apache.commons.io.FileUtils
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
class ReleaseController extends ReleaseControllerBase
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ReleaseService
|
||||
with ActivityService
|
||||
with ReadableUsersAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
class ReleaseController
|
||||
extends ReleaseControllerBase
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ReleaseService
|
||||
with ActivityService
|
||||
with ReadableUsersAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
with WritableUsersAuthenticator
|
||||
|
||||
trait ReleaseControllerBase extends ControllerBase {
|
||||
self: RepositoryService
|
||||
@@ -35,36 +36,45 @@ trait ReleaseControllerBase extends ControllerBase {
|
||||
)
|
||||
|
||||
val releaseForm = mapping(
|
||||
"name" -> trim(text(required)),
|
||||
"name" -> trim(text(required)),
|
||||
"content" -> trim(optional(text()))
|
||||
)(ReleaseForm.apply)
|
||||
|
||||
get("/:owner/:repository/releases")(referrersOnly {repository =>
|
||||
get("/:owner/:repository/releases")(referrersOnly { repository =>
|
||||
val releases = getReleases(repository.owner, repository.name)
|
||||
val assets = getReleaseAssetsMap(repository.owner, repository.name)
|
||||
|
||||
html.list(
|
||||
repository,
|
||||
repository.tags.reverse.map { tag =>
|
||||
(tag, releases.find(_.tag == tag.name).map { release => (release, assets(release)) })
|
||||
(tag, releases.find(_.tag == tag.name).map { release =>
|
||||
(release, assets(release))
|
||||
})
|
||||
},
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
||||
)
|
||||
})
|
||||
|
||||
get("/:owner/:repository/releases/:tag")(referrersOnly { repository =>
|
||||
val tagName = params("tag")
|
||||
getRelease(repository.owner, repository.name, tagName).map { release =>
|
||||
html.release(release, getReleaseAssets(repository.owner, repository.name, tagName),
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount), repository)
|
||||
}.getOrElse(NotFound())
|
||||
getRelease(repository.owner, repository.name, tagName)
|
||||
.map { release =>
|
||||
html.release(
|
||||
release,
|
||||
getReleaseAssets(repository.owner, repository.name, tagName),
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
||||
repository
|
||||
)
|
||||
}
|
||||
.getOrElse(NotFound())
|
||||
})
|
||||
|
||||
get("/:owner/:repository/releases/:tag/assets/:fileId")(referrersOnly {repository =>
|
||||
get("/:owner/:repository/releases/:tag/assets/:fileId")(referrersOnly { repository =>
|
||||
val tagName = params("tag")
|
||||
val fileId = params("fileId")
|
||||
(for {
|
||||
_ <- repository.tags.find(_.name == tagName)
|
||||
_ <- getRelease(repository.owner, repository.name, tagName)
|
||||
_ <- repository.tags.find(_.name == tagName)
|
||||
_ <- getRelease(repository.owner, repository.name, tagName)
|
||||
asset <- getReleaseAsset(repository.owner, repository.name, tagName, fileId)
|
||||
} yield {
|
||||
response.setHeader("Content-Disposition", s"attachment; filename=${asset.label}")
|
||||
@@ -75,11 +85,14 @@ trait ReleaseControllerBase extends ControllerBase {
|
||||
}).getOrElse(NotFound())
|
||||
})
|
||||
|
||||
get("/:owner/:repository/releases/:tag/create")(writableUsersOnly {repository =>
|
||||
get("/:owner/:repository/releases/:tag/create")(writableUsersOnly { repository =>
|
||||
val tagName = params("tag")
|
||||
repository.tags.find(_.name == tagName).map { tag =>
|
||||
html.form(repository, tag, None)
|
||||
}.getOrElse(NotFound())
|
||||
repository.tags
|
||||
.find(_.name == tagName)
|
||||
.map { tag =>
|
||||
html.form(repository, tag, None)
|
||||
}
|
||||
.getOrElse(NotFound())
|
||||
})
|
||||
|
||||
post("/:owner/:repository/releases/:tag/create", releaseForm)(writableUsersOnly { (form, repository) =>
|
||||
@@ -90,13 +103,16 @@ trait ReleaseControllerBase extends ControllerBase {
|
||||
createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount)
|
||||
|
||||
// Insert into RELEASE_ASSET
|
||||
val files = params.collect { case (name, value) if name.startsWith("file:") =>
|
||||
val Array(_, fileId) = name.split(":")
|
||||
(fileId, value)
|
||||
val files = params.collect {
|
||||
case (name, value) if name.startsWith("file:") =>
|
||||
val Array(_, fileId) = name.split(":")
|
||||
(fileId, value)
|
||||
}
|
||||
files.foreach { case (fileId, fileName) =>
|
||||
val size = new java.io.File(getReleaseFilesDir(repository.owner, repository.name), tagName + "/" + fileId).length
|
||||
createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
|
||||
files.foreach {
|
||||
case (fileId, fileName) =>
|
||||
val size =
|
||||
new java.io.File(getReleaseFilesDir(repository.owner, repository.name), tagName + "/" + fileId).length
|
||||
createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
|
||||
}
|
||||
|
||||
recordReleaseActivity(repository.owner, repository.name, loginAccount.userName, form.name)
|
||||
@@ -104,47 +120,56 @@ trait ReleaseControllerBase extends ControllerBase {
|
||||
redirect(s"/${repository.owner}/${repository.name}/releases/${tagName}")
|
||||
})
|
||||
|
||||
get("/:owner/:repository/releases/:tag/edit")(writableUsersOnly {repository =>
|
||||
get("/:owner/:repository/releases/:tag/edit")(writableUsersOnly { repository =>
|
||||
val tagName = params("tag")
|
||||
|
||||
(for {
|
||||
release <- getRelease(repository.owner, repository.name, tagName)
|
||||
tag <- repository.tags.find(_.name == tagName)
|
||||
tag <- repository.tags.find(_.name == tagName)
|
||||
} yield {
|
||||
html.form(repository, tag, Some(release, getReleaseAssets(repository.owner, repository.name, tagName)))
|
||||
}).getOrElse(NotFound())
|
||||
})
|
||||
|
||||
post("/:owner/:repository/releases/:tag/edit", releaseForm)(writableUsersOnly { (form, repository) =>
|
||||
val tagName = params("tag")
|
||||
val loginAccount = context.loginAccount.get
|
||||
post("/:owner/:repository/releases/:tag/edit", releaseForm)(writableUsersOnly {
|
||||
(form, repository) =>
|
||||
val tagName = params("tag")
|
||||
val loginAccount = context.loginAccount.get
|
||||
|
||||
getRelease(repository.owner, repository.name, tagName).map { release =>
|
||||
// Update RELEASE
|
||||
updateRelease(repository.owner, repository.name, tagName, form.name, form.content)
|
||||
getRelease(repository.owner, repository.name, tagName)
|
||||
.map { release =>
|
||||
// Update RELEASE
|
||||
updateRelease(repository.owner, repository.name, tagName, form.name, form.content)
|
||||
|
||||
// Delete and Insert RELEASE_ASSET
|
||||
val assets = getReleaseAssets(repository.owner, repository.name, tagName)
|
||||
deleteReleaseAssets(repository.owner, repository.name, tagName)
|
||||
// Delete and Insert RELEASE_ASSET
|
||||
val assets = getReleaseAssets(repository.owner, repository.name, tagName)
|
||||
deleteReleaseAssets(repository.owner, repository.name, tagName)
|
||||
|
||||
val files = params.collect { case (name, value) if name.startsWith("file:") =>
|
||||
val Array(_, fileId) = name.split(":")
|
||||
(fileId, value)
|
||||
}
|
||||
files.foreach { case (fileId, fileName) =>
|
||||
val size = new java.io.File(getReleaseFilesDir(repository.owner, repository.name), tagName + "/" + fileId).length
|
||||
createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
|
||||
}
|
||||
val files = params.collect {
|
||||
case (name, value) if name.startsWith("file:") =>
|
||||
val Array(_, fileId) = name.split(":")
|
||||
(fileId, value)
|
||||
}
|
||||
files.foreach {
|
||||
case (fileId, fileName) =>
|
||||
val size =
|
||||
new java.io.File(getReleaseFilesDir(repository.owner, repository.name), 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 java.io.File(getReleaseFilesDir(repository.owner, repository.name), release.tag + "/" + asset.fileName)
|
||||
FileUtils.forceDelete(file)
|
||||
assets.foreach { asset =>
|
||||
if (!files.exists { case (fileId, _) => fileId == asset.fileName }) {
|
||||
val file = new java.io.File(
|
||||
getReleaseFilesDir(repository.owner, repository.name),
|
||||
release.tag + "/" + asset.fileName
|
||||
)
|
||||
FileUtils.forceDelete(file)
|
||||
}
|
||||
}
|
||||
|
||||
redirect(s"/${release.userName}/${release.repositoryName}/releases/${tagName}")
|
||||
}
|
||||
}
|
||||
|
||||
redirect(s"/${release.userName}/${release.repositoryName}/releases/${tagName}")
|
||||
}.getOrElse(NotFound())
|
||||
.getOrElse(NotFound())
|
||||
})
|
||||
|
||||
post("/:owner/:repository/releases/:tag/delete")(writableUsersOnly { repository =>
|
||||
|
||||
@@ -21,14 +21,26 @@ import org.eclipse.jgit.lib.ObjectId
|
||||
import gitbucket.core.model.WebHookContentType
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
|
||||
|
||||
class RepositorySettingsController extends RepositorySettingsControllerBase
|
||||
with RepositoryService with AccountService with WebHookService with ProtectedBranchService with CommitStatusService with DeployKeyService
|
||||
with OwnerAuthenticator with UsersAuthenticator
|
||||
class RepositorySettingsController
|
||||
extends RepositorySettingsControllerBase
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with WebHookService
|
||||
with ProtectedBranchService
|
||||
with CommitStatusService
|
||||
with DeployKeyService
|
||||
with OwnerAuthenticator
|
||||
with UsersAuthenticator
|
||||
|
||||
trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
self: RepositoryService with AccountService with WebHookService with ProtectedBranchService with CommitStatusService with DeployKeyService
|
||||
with OwnerAuthenticator with UsersAuthenticator =>
|
||||
self: RepositoryService
|
||||
with AccountService
|
||||
with WebHookService
|
||||
with ProtectedBranchService
|
||||
with CommitStatusService
|
||||
with DeployKeyService
|
||||
with OwnerAuthenticator
|
||||
with UsersAuthenticator =>
|
||||
|
||||
// for repository options
|
||||
case class OptionsForm(
|
||||
@@ -45,18 +57,20 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
)
|
||||
|
||||
val optionsForm = mapping(
|
||||
"repositoryName" -> trim(label("Repository Name" , text(required, maxlength(100), repository, renameRepositoryName))),
|
||||
"description" -> trim(label("Description" , optional(text()))),
|
||||
"isPrivate" -> trim(label("Repository Type" , boolean())),
|
||||
"issuesOption" -> trim(label("Issues Option" , text(required, featureOption))),
|
||||
"externalIssuesUrl" -> trim(label("External Issues URL", optional(text(maxlength(200))))),
|
||||
"wikiOption" -> trim(label("Wiki Option" , text(required, featureOption))),
|
||||
"externalWikiUrl" -> trim(label("External Wiki URL" , optional(text(maxlength(200))))),
|
||||
"allowFork" -> trim(label("Allow Forking" , boolean())),
|
||||
"mergeOptions" -> mergeOptions,
|
||||
"repositoryName" -> trim(
|
||||
label("Repository Name", text(required, maxlength(100), repository, renameRepositoryName))
|
||||
),
|
||||
"description" -> trim(label("Description", optional(text()))),
|
||||
"isPrivate" -> trim(label("Repository Type", boolean())),
|
||||
"issuesOption" -> trim(label("Issues Option", text(required, featureOption))),
|
||||
"externalIssuesUrl" -> trim(label("External Issues URL", optional(text(maxlength(200))))),
|
||||
"wikiOption" -> trim(label("Wiki Option", text(required, featureOption))),
|
||||
"externalWikiUrl" -> trim(label("External Wiki URL", optional(text(maxlength(200))))),
|
||||
"allowFork" -> trim(label("Allow Forking", boolean())),
|
||||
"mergeOptions" -> mergeOptions,
|
||||
"defaultMergeOption" -> trim(label("Default merge strategy", text(required)))
|
||||
)(OptionsForm.apply).verifying { form =>
|
||||
if(!form.mergeOptions.contains(form.defaultMergeOption)){
|
||||
if (!form.mergeOptions.contains(form.defaultMergeOption)) {
|
||||
Seq("defaultMergeOption" -> s"This merge strategy isn't enabled.")
|
||||
} else Nil
|
||||
}
|
||||
@@ -65,30 +79,30 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
case class DefaultBranchForm(defaultBranch: String)
|
||||
|
||||
val defaultBranchForm = mapping(
|
||||
"defaultBranch" -> trim(label("Default Branch" , text(required, maxlength(100))))
|
||||
"defaultBranch" -> trim(label("Default Branch", text(required, maxlength(100))))
|
||||
)(DefaultBranchForm.apply)
|
||||
|
||||
|
||||
// for deploy key
|
||||
case class DeployKeyForm(title: String, publicKey: String, allowWrite: Boolean)
|
||||
|
||||
val deployKeyForm = mapping(
|
||||
"title" -> trim(label("Title", text(required, maxlength(100)))),
|
||||
"publicKey" -> trim2(label("Key" , text(required))), // TODO duplication check in the repository?
|
||||
"allowWrite" -> trim(label("Key" , boolean()))
|
||||
"title" -> trim(label("Title", text(required, maxlength(100)))),
|
||||
"publicKey" -> trim2(label("Key", text(required))), // TODO duplication check in the repository?
|
||||
"allowWrite" -> trim(label("Key", boolean()))
|
||||
)(DeployKeyForm.apply)
|
||||
|
||||
// for web hook url addition
|
||||
case class WebHookForm(url: String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])
|
||||
|
||||
def webHookForm(update:Boolean) = mapping(
|
||||
"url" -> trim(label("url", text(required, webHook(update)))),
|
||||
"events" -> webhookEvents,
|
||||
"ctype" -> label("ctype", text()),
|
||||
"token" -> optional(trim(label("token", text(maxlength(100)))))
|
||||
)(
|
||||
(url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
|
||||
)
|
||||
def webHookForm(update: Boolean) =
|
||||
mapping(
|
||||
"url" -> trim(label("url", text(required, webHook(update)))),
|
||||
"events" -> webhookEvents,
|
||||
"ctype" -> label("ctype", text()),
|
||||
"token" -> optional(trim(label("token", text(maxlength(100)))))
|
||||
)(
|
||||
(url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
|
||||
)
|
||||
|
||||
// for transfer ownership
|
||||
case class TransferOwnerShipForm(newOwner: String)
|
||||
@@ -131,24 +145,24 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
form.defaultMergeOption
|
||||
)
|
||||
// Change repository name
|
||||
if(repository.name != form.repositoryName){
|
||||
if (repository.name != form.repositoryName) {
|
||||
// Update database
|
||||
renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
|
||||
// Move git repository
|
||||
defining(getRepositoryDir(repository.owner, repository.name)){ dir =>
|
||||
if(dir.isDirectory){
|
||||
defining(getRepositoryDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryDir(repository.owner, form.repositoryName))
|
||||
}
|
||||
}
|
||||
// Move wiki repository
|
||||
defining(getWikiRepositoryDir(repository.owner, repository.name)){ dir =>
|
||||
if(dir.isDirectory) {
|
||||
defining(getWikiRepositoryDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getWikiRepositoryDir(repository.owner, form.repositoryName))
|
||||
}
|
||||
}
|
||||
// Move files directory
|
||||
defining(getRepositoryFilesDir(repository.owner, repository.name)){ dir =>
|
||||
if(dir.isDirectory) {
|
||||
defining(getRepositoryFilesDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryFilesDir(repository.owner, form.repositoryName))
|
||||
}
|
||||
}
|
||||
@@ -170,7 +184,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
|
||||
/** Update default branch */
|
||||
post("/:owner/:repository/settings/update_default_branch", defaultBranchForm)(ownerOnly { (form, repository) =>
|
||||
if(!repository.branchList.contains(form.defaultBranch)){
|
||||
if (!repository.branchList.contains(form.defaultBranch)) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/options")
|
||||
} else {
|
||||
saveRepositoryDefaultBranch(repository.owner, repository.name, form.defaultBranch)
|
||||
@@ -187,12 +201,15 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
get("/:owner/:repository/settings/branches/:branch")(ownerOnly { repository =>
|
||||
import gitbucket.core.api._
|
||||
val branch = params("branch")
|
||||
if(!repository.branchList.contains(branch)){
|
||||
if (!repository.branchList.contains(branch)) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/branches")
|
||||
} else {
|
||||
val protection = ApiBranchProtection(getProtectedBranchInfo(repository.owner, repository.name, branch))
|
||||
val lastWeeks = getRecentStatuesContexts(repository.owner, repository.name,
|
||||
Date.from(LocalDateTime.now.minusWeeks(1).toInstant(ZoneOffset.UTC))).toSet
|
||||
val lastWeeks = getRecentStatuesContexts(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
Date.from(LocalDateTime.now.minusWeeks(1).toInstant(ZoneOffset.UTC))
|
||||
).toSet
|
||||
val knownContexts = (lastWeeks ++ protection.status.contexts).toSeq.sortBy(identity)
|
||||
html.branchprotection(repository, branch, protection, knownContexts, flash.get("info"))
|
||||
}
|
||||
@@ -205,7 +222,8 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
html.collaborators(
|
||||
getCollaborators(repository.owner, repository.name),
|
||||
getAccountByUserName(repository.owner).get.isGroupAccount,
|
||||
repository)
|
||||
repository
|
||||
)
|
||||
})
|
||||
|
||||
post("/:owner/:repository/settings/collaborators")(ownerOnly { repository =>
|
||||
@@ -255,62 +273,90 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Send the test request to registered web hook URLs.
|
||||
*/
|
||||
ajaxPost("/:owner/:repository/settings/hooks/test")(ownerOnly { repository =>
|
||||
def _headers(h: Array[org.apache.http.Header]): Array[Array[String]] = h.map { h => Array(h.getName, h.getValue) }
|
||||
def _headers(h: Array[org.apache.http.Header]): Array[Array[String]] = h.map { h =>
|
||||
Array(h.getName, h.getValue)
|
||||
}
|
||||
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent._
|
||||
import scala.util.control.NonFatal
|
||||
import org.apache.http.util.EntityUtils
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||
git =>
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent._
|
||||
import scala.util.control.NonFatal
|
||||
import org.apache.http.util.EntityUtils
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
|
||||
val url = params("url")
|
||||
val token = Some(params("token"))
|
||||
val ctype = WebHookContentType.valueOf(params("ctype"))
|
||||
val dummyWebHookInfo = RepositoryWebHook(repository.owner, repository.name, url, ctype, 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)
|
||||
val url = params("url")
|
||||
val token = Some(params("token"))
|
||||
val ctype = WebHookContentType.valueOf(params("ctype"))
|
||||
val dummyWebHookInfo = RepositoryWebHook(repository.owner, repository.name, url, ctype, 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())
|
||||
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 (webHook, json, reqFuture, resFuture) = callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload).head
|
||||
|
||||
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
||||
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" -> (e.getClass + " " + e.getMessage))
|
||||
}
|
||||
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(
|
||||
Map(
|
||||
"url" -> url,
|
||||
"request" -> Await.result(
|
||||
reqFuture
|
||||
.map(
|
||||
req =>
|
||||
Map(
|
||||
"headers" -> _headers(req.getAllHeaders),
|
||||
"payload" -> json
|
||||
)
|
||||
)
|
||||
.recover(toErrorMap),
|
||||
20 seconds
|
||||
),
|
||||
"response" -> Await.result(
|
||||
resFuture
|
||||
.map(
|
||||
res =>
|
||||
Map(
|
||||
"status" -> res.getStatusLine(),
|
||||
"body" -> EntityUtils.toString(res.getEntity()),
|
||||
"headers" -> _headers(res.getAllHeaders())
|
||||
)
|
||||
)
|
||||
.recover(toErrorMap),
|
||||
20 seconds
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val (webHook, json, reqFuture, resFuture) = callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload).head
|
||||
|
||||
val toErrorMap: PartialFunction[Throwable, Map[String,String]] = {
|
||||
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"-> (e.getClass + " "+ e.getMessage))
|
||||
}
|
||||
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(Map(
|
||||
"url" -> url,
|
||||
"request" -> Await.result(reqFuture.map(req => Map(
|
||||
"headers" -> _headers(req.getAllHeaders),
|
||||
"payload" -> json
|
||||
)).recover(toErrorMap), 20 seconds),
|
||||
"response" -> Await.result(resFuture.map(res => Map(
|
||||
"status" -> res.getStatusLine(),
|
||||
"body" -> EntityUtils.toString(res.getEntity()),
|
||||
"headers" -> _headers(res.getAllHeaders())
|
||||
)).recover(toErrorMap), 20 seconds)
|
||||
))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -318,8 +364,9 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Display the web hook edit page.
|
||||
*/
|
||||
get("/:owner/:repository/settings/hooks/edit")(ownerOnly { repository =>
|
||||
getWebHook(repository.owner, repository.name, params("url")).map{ case (webhook, events) =>
|
||||
html.edithook(webhook, events, repository, false)
|
||||
getWebHook(repository.owner, repository.name, params("url")).map {
|
||||
case (webhook, events) =>
|
||||
html.edithook(webhook, events, repository, false)
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -344,25 +391,25 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
*/
|
||||
post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
|
||||
// Change repository owner
|
||||
if(repository.owner != form.newOwner){
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}"){
|
||||
if (repository.owner != form.newOwner) {
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
// Update database
|
||||
renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
|
||||
// Move git repository
|
||||
defining(getRepositoryDir(repository.owner, repository.name)){ dir =>
|
||||
if(dir.isDirectory){
|
||||
defining(getRepositoryDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryDir(form.newOwner, repository.name))
|
||||
}
|
||||
}
|
||||
// Move wiki repository
|
||||
defining(getWikiRepositoryDir(repository.owner, repository.name)){ dir =>
|
||||
if(dir.isDirectory) {
|
||||
defining(getWikiRepositoryDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getWikiRepositoryDir(form.newOwner, repository.name))
|
||||
}
|
||||
}
|
||||
// Move files directory
|
||||
defining(getRepositoryFilesDir(repository.owner, repository.name)){ dir =>
|
||||
if(dir.isDirectory) {
|
||||
defining(getRepositoryFilesDir(repository.owner, repository.name)) { dir =>
|
||||
if (dir.isDirectory) {
|
||||
FileUtils.moveDirectory(dir, getRepositoryFilesDir(form.newOwner, repository.name))
|
||||
}
|
||||
}
|
||||
@@ -378,7 +425,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Delete the repository.
|
||||
*/
|
||||
post("/:owner/:repository/settings/delete")(ownerOnly { repository =>
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}"){
|
||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||
// Delete the repository and related files
|
||||
deleteRepository(repository.owner, repository.name)
|
||||
|
||||
@@ -428,10 +475,10 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
/**
|
||||
* Provides duplication check for web hook url.
|
||||
*/
|
||||
private def webHook(needExists: Boolean): Constraint = new Constraint(){
|
||||
private def webHook(needExists: Boolean): Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if(getWebHook(params("owner"), params("repository"), value).isDefined != needExists){
|
||||
Some(if(needExists){
|
||||
if (getWebHook(params("owner"), params("repository"), value).isDefined != needExists) {
|
||||
Some(if (needExists) {
|
||||
"URL had not been registered yet."
|
||||
} else {
|
||||
"URL had been registered already."
|
||||
@@ -441,17 +488,18 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
private def webhookEvents = new ValueType[Set[WebHook.Event]]{
|
||||
private def webhookEvents = new ValueType[Set[WebHook.Event]] {
|
||||
def convert(name: String, params: Map[String, Seq[String]], messages: Messages): Set[WebHook.Event] = {
|
||||
WebHook.Event.values.flatMap { t =>
|
||||
params.get(name + "." + t.name).map(_ => t)
|
||||
}.toSet
|
||||
}
|
||||
def validate(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[(String, String)] = if(convert(name,params,messages).isEmpty){
|
||||
Seq(name -> messages("error.required").format(name))
|
||||
} else {
|
||||
Nil
|
||||
}
|
||||
def validate(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[(String, String)] =
|
||||
if (convert(name, params, messages).isEmpty) {
|
||||
Seq(name -> messages("error.required").format(name))
|
||||
} else {
|
||||
Nil
|
||||
}
|
||||
}
|
||||
|
||||
// /**
|
||||
@@ -472,12 +520,17 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
/**
|
||||
* Duplicate check for the rename repository name.
|
||||
*/
|
||||
private def renameRepositoryName: Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, params: Map[String, Seq[String]], messages: Messages): Option[String] = {
|
||||
private def renameRepositoryName: Constraint = new Constraint() {
|
||||
override def validate(
|
||||
name: String,
|
||||
value: String,
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
for {
|
||||
repoName <- params.optionValue("repository") if repoName != value
|
||||
userName <- params.optionValue("owner")
|
||||
_ <- getRepositoryNamesOfUser(userName).find(_ == value)
|
||||
_ <- getRepositoryNamesOfUser(userName).find(_ == value)
|
||||
} yield {
|
||||
"Repository already exists."
|
||||
}
|
||||
@@ -487,38 +540,45 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private def featureOption: Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, params: Map[String, Seq[String]], messages: Messages): Option[String] =
|
||||
if(Seq("DISABLE", "PRIVATE", "PUBLIC", "ALL").contains(value)) None else Some("Option is invalid.")
|
||||
private def featureOption: Constraint = new Constraint() {
|
||||
override def validate(
|
||||
name: String,
|
||||
value: String,
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] =
|
||||
if (Seq("DISABLE", "PRIVATE", "PUBLIC", "ALL").contains(value)) None else Some("Option is invalid.")
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides Constraint to validate the repository transfer user.
|
||||
*/
|
||||
private def transferUser: Constraint = new Constraint(){
|
||||
private def transferUser: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
getAccountByUserName(value) match {
|
||||
case None => Some("User does not exist.")
|
||||
case Some(x) => if(x.userName == params("owner")){
|
||||
Some("This is current repository owner.")
|
||||
} else {
|
||||
params.get("repository").flatMap { repositoryName =>
|
||||
getRepositoryNamesOfUser(x.userName).find(_ == repositoryName).map{ _ => "User already has same repository." }
|
||||
case None => Some("User does not exist.")
|
||||
case Some(x) =>
|
||||
if (x.userName == params("owner")) {
|
||||
Some("This is current repository owner.")
|
||||
} else {
|
||||
params.get("repository").flatMap { repositoryName =>
|
||||
getRepositoryNamesOfUser(x.userName).find(_ == repositoryName).map { _ =>
|
||||
"User already has same repository."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def mergeOptions = new ValueType[Seq[String]]{
|
||||
private def mergeOptions = new ValueType[Seq[String]] {
|
||||
override def convert(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[String] = {
|
||||
params.get("mergeOptions").getOrElse(Nil)
|
||||
}
|
||||
override def validate(name: String, params: Map[String, Seq[String]], messages: Messages): Seq[(String, String)] = {
|
||||
val mergeOptions = params.get("mergeOptions").getOrElse(Nil)
|
||||
if(mergeOptions.isEmpty){
|
||||
if (mergeOptions.isEmpty) {
|
||||
Seq("mergeOptions" -> "At least one option must be enabled.")
|
||||
} else if(!mergeOptions.forall(x => Seq("merge-commit", "squash", "rebase").contains(x))){
|
||||
} else if (!mergeOptions.forall(x => Seq("merge-commit", "squash", "rebase").contains(x))) {
|
||||
Seq("mergeOptions" -> "mergeOptions are invalid.")
|
||||
} else {
|
||||
Nil
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,8 +23,11 @@ import org.scalatra.i18n.Messages
|
||||
import scala.collection.JavaConverters._
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
class SystemSettingsController extends SystemSettingsControllerBase
|
||||
with AccountService with RepositoryService with AdminAuthenticator
|
||||
class SystemSettingsController
|
||||
extends SystemSettingsControllerBase
|
||||
with AccountService
|
||||
with RepositoryService
|
||||
with AdminAuthenticator
|
||||
|
||||
case class Table(name: String, columns: Seq[Column])
|
||||
case class Column(name: String, primaryKey: Boolean)
|
||||
@@ -33,72 +36,81 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
self: AccountService with RepositoryService with AdminAuthenticator =>
|
||||
|
||||
private val form = mapping(
|
||||
"baseUrl" -> trim(label("Base URL", optional(text()))),
|
||||
"information" -> trim(label("Information", optional(text()))),
|
||||
"baseUrl" -> trim(label("Base URL", optional(text()))),
|
||||
"information" -> trim(label("Information", optional(text()))),
|
||||
"allowAccountRegistration" -> trim(label("Account registration", boolean())),
|
||||
"allowAnonymousAccess" -> trim(label("Anonymous access", boolean())),
|
||||
"allowAnonymousAccess" -> trim(label("Anonymous access", boolean())),
|
||||
"isCreateRepoOptionPublic" -> trim(label("Default option to create a new repository", boolean())),
|
||||
"gravatar" -> trim(label("Gravatar", boolean())),
|
||||
"notification" -> trim(label("Notification", boolean())),
|
||||
"activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))),
|
||||
"ssh" -> trim(label("SSH access", boolean())),
|
||||
"sshHost" -> trim(label("SSH host", optional(text()))),
|
||||
"sshPort" -> trim(label("SSH port", optional(number()))),
|
||||
"useSMTP" -> trim(label("SMTP", boolean())),
|
||||
"smtp" -> optionalIfNotChecked("useSMTP", mapping(
|
||||
"host" -> trim(label("SMTP Host", text(required))),
|
||||
"port" -> trim(label("SMTP Port", optional(number()))),
|
||||
"user" -> trim(label("SMTP User", optional(text()))),
|
||||
"password" -> trim(label("SMTP Password", optional(text()))),
|
||||
"ssl" -> trim(label("Enable SSL", optional(boolean()))),
|
||||
"starttls" -> trim(label("Enable STARTTLS", optional(boolean()))),
|
||||
"fromAddress" -> trim(label("FROM Address", optional(text()))),
|
||||
"fromName" -> trim(label("FROM Name", optional(text())))
|
||||
)(Smtp.apply)),
|
||||
"ldapAuthentication" -> trim(label("LDAP", boolean())),
|
||||
"ldap" -> optionalIfNotChecked("ldapAuthentication", mapping(
|
||||
"host" -> trim(label("LDAP host", text(required))),
|
||||
"port" -> trim(label("LDAP port", optional(number()))),
|
||||
"bindDN" -> trim(label("Bind DN", optional(text()))),
|
||||
"bindPassword" -> trim(label("Bind Password", optional(text()))),
|
||||
"baseDN" -> trim(label("Base DN", text(required))),
|
||||
"userNameAttribute" -> trim(label("User name attribute", text(required))),
|
||||
"additionalFilterCondition"-> trim(label("Additional filter condition", optional(text()))),
|
||||
"fullNameAttribute" -> trim(label("Full name attribute", optional(text()))),
|
||||
"mailAttribute" -> trim(label("Mail address attribute", optional(text()))),
|
||||
"tls" -> trim(label("Enable TLS", optional(boolean()))),
|
||||
"ssl" -> trim(label("Enable SSL", optional(boolean()))),
|
||||
"keystore" -> trim(label("Keystore", optional(text())))
|
||||
)(Ldap.apply)),
|
||||
"oidcAuthentication" -> trim(label("OIDC", boolean())),
|
||||
"oidc" -> optionalIfNotChecked("oidcAuthentication", mapping(
|
||||
"issuer" -> trim(label("Issuer", text(required))),
|
||||
"clientID" -> trim(label("Client ID", text(required))),
|
||||
"clientSecret" -> trim(label("Client secret", text(required))),
|
||||
"jwsAlgorithm" -> trim(label("Signature algorithm", optional(text())))
|
||||
)(OIDC.apply)),
|
||||
"gravatar" -> trim(label("Gravatar", boolean())),
|
||||
"notification" -> trim(label("Notification", boolean())),
|
||||
"activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))),
|
||||
"ssh" -> trim(label("SSH access", boolean())),
|
||||
"sshHost" -> trim(label("SSH host", optional(text()))),
|
||||
"sshPort" -> trim(label("SSH port", optional(number()))),
|
||||
"useSMTP" -> trim(label("SMTP", boolean())),
|
||||
"smtp" -> optionalIfNotChecked(
|
||||
"useSMTP",
|
||||
mapping(
|
||||
"host" -> trim(label("SMTP Host", text(required))),
|
||||
"port" -> trim(label("SMTP Port", optional(number()))),
|
||||
"user" -> trim(label("SMTP User", optional(text()))),
|
||||
"password" -> trim(label("SMTP Password", optional(text()))),
|
||||
"ssl" -> trim(label("Enable SSL", optional(boolean()))),
|
||||
"starttls" -> trim(label("Enable STARTTLS", optional(boolean()))),
|
||||
"fromAddress" -> trim(label("FROM Address", optional(text()))),
|
||||
"fromName" -> trim(label("FROM Name", optional(text())))
|
||||
)(Smtp.apply)
|
||||
),
|
||||
"ldapAuthentication" -> trim(label("LDAP", boolean())),
|
||||
"ldap" -> optionalIfNotChecked(
|
||||
"ldapAuthentication",
|
||||
mapping(
|
||||
"host" -> trim(label("LDAP host", text(required))),
|
||||
"port" -> trim(label("LDAP port", optional(number()))),
|
||||
"bindDN" -> trim(label("Bind DN", optional(text()))),
|
||||
"bindPassword" -> trim(label("Bind Password", optional(text()))),
|
||||
"baseDN" -> trim(label("Base DN", text(required))),
|
||||
"userNameAttribute" -> trim(label("User name attribute", text(required))),
|
||||
"additionalFilterCondition" -> trim(label("Additional filter condition", optional(text()))),
|
||||
"fullNameAttribute" -> trim(label("Full name attribute", optional(text()))),
|
||||
"mailAttribute" -> trim(label("Mail address attribute", optional(text()))),
|
||||
"tls" -> trim(label("Enable TLS", optional(boolean()))),
|
||||
"ssl" -> trim(label("Enable SSL", optional(boolean()))),
|
||||
"keystore" -> trim(label("Keystore", optional(text())))
|
||||
)(Ldap.apply)
|
||||
),
|
||||
"oidcAuthentication" -> trim(label("OIDC", boolean())),
|
||||
"oidc" -> optionalIfNotChecked(
|
||||
"oidcAuthentication",
|
||||
mapping(
|
||||
"issuer" -> trim(label("Issuer", text(required))),
|
||||
"clientID" -> trim(label("Client ID", text(required))),
|
||||
"clientSecret" -> trim(label("Client secret", text(required))),
|
||||
"jwsAlgorithm" -> trim(label("Signature algorithm", optional(text())))
|
||||
)(OIDC.apply)
|
||||
),
|
||||
"skinName" -> trim(label("AdminLTE skin name", text(required)))
|
||||
)(SystemSettings.apply).verifying { settings =>
|
||||
Vector(
|
||||
if(settings.ssh && settings.baseUrl.isEmpty){
|
||||
if (settings.ssh && settings.baseUrl.isEmpty) {
|
||||
Some("baseUrl" -> "Base URL is required if SSH access is enabled.")
|
||||
} else None,
|
||||
if(settings.ssh && settings.sshHost.isEmpty){
|
||||
if (settings.ssh && settings.sshHost.isEmpty) {
|
||||
Some("sshHost" -> "SSH host is required if SSH access is enabled.")
|
||||
} else None
|
||||
).flatten
|
||||
}
|
||||
|
||||
private val sendMailForm = mapping(
|
||||
"smtp" -> mapping(
|
||||
"host" -> trim(label("SMTP Host", text(required))),
|
||||
"port" -> trim(label("SMTP Port", optional(number()))),
|
||||
"user" -> trim(label("SMTP User", optional(text()))),
|
||||
"password" -> trim(label("SMTP Password", optional(text()))),
|
||||
"ssl" -> trim(label("Enable SSL", optional(boolean()))),
|
||||
"starttls" -> trim(label("Enable STARTTLS", optional(boolean()))),
|
||||
"smtp" -> mapping(
|
||||
"host" -> trim(label("SMTP Host", text(required))),
|
||||
"port" -> trim(label("SMTP Port", optional(number()))),
|
||||
"user" -> trim(label("SMTP User", optional(text()))),
|
||||
"password" -> trim(label("SMTP Password", optional(text()))),
|
||||
"ssl" -> trim(label("Enable SSL", optional(boolean()))),
|
||||
"starttls" -> trim(label("Enable STARTTLS", optional(boolean()))),
|
||||
"fromAddress" -> trim(label("FROM Address", optional(text()))),
|
||||
"fromName" -> trim(label("FROM Name", optional(text())))
|
||||
"fromName" -> trim(label("FROM Name", optional(text())))
|
||||
)(Smtp.apply),
|
||||
"testAddress" -> trim(label("", text(required)))
|
||||
)(SendMailForm.apply)
|
||||
@@ -107,126 +119,156 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
case class DataExportForm(tableNames: List[String])
|
||||
|
||||
case class NewUserForm(userName: String, password: String, fullName: String,
|
||||
mailAddress: String, isAdmin: Boolean,
|
||||
description: Option[String], url: Option[String], fileId: Option[String])
|
||||
case class NewUserForm(
|
||||
userName: String,
|
||||
password: String,
|
||||
fullName: String,
|
||||
mailAddress: String,
|
||||
isAdmin: Boolean,
|
||||
description: Option[String],
|
||||
url: Option[String],
|
||||
fileId: Option[String]
|
||||
)
|
||||
|
||||
case class EditUserForm(userName: String, password: Option[String], fullName: String,
|
||||
mailAddress: String, isAdmin: Boolean, description: Option[String], url: Option[String],
|
||||
fileId: Option[String], clearImage: Boolean, isRemoved: Boolean)
|
||||
case class EditUserForm(
|
||||
userName: String,
|
||||
password: Option[String],
|
||||
fullName: String,
|
||||
mailAddress: String,
|
||||
isAdmin: Boolean,
|
||||
description: Option[String],
|
||||
url: Option[String],
|
||||
fileId: Option[String],
|
||||
clearImage: Boolean,
|
||||
isRemoved: Boolean
|
||||
)
|
||||
|
||||
case class NewGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String],
|
||||
members: String)
|
||||
|
||||
case class EditGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String],
|
||||
members: String, clearImage: Boolean, isRemoved: Boolean)
|
||||
case class NewGroupForm(
|
||||
groupName: String,
|
||||
description: Option[String],
|
||||
url: Option[String],
|
||||
fileId: Option[String],
|
||||
members: String
|
||||
)
|
||||
|
||||
case class EditGroupForm(
|
||||
groupName: String,
|
||||
description: Option[String],
|
||||
url: Option[String],
|
||||
fileId: Option[String],
|
||||
members: String,
|
||||
clearImage: Boolean,
|
||||
isRemoved: Boolean
|
||||
)
|
||||
|
||||
val newUserForm = mapping(
|
||||
"userName" -> trim(label("Username" ,text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"password" -> trim(label("Password" ,text(required, maxlength(20), password))),
|
||||
"fullName" -> trim(label("Full Name" ,text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address" ,text(required, maxlength(100), uniqueMailAddress()))),
|
||||
"isAdmin" -> trim(label("User Type" ,boolean())),
|
||||
"description" -> trim(label("bio" ,optional(text()))),
|
||||
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" ,optional(text())))
|
||||
"userName" -> trim(label("Username", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"password" -> trim(label("Password", text(required, maxlength(20), password))),
|
||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
||||
"isAdmin" -> trim(label("User Type", boolean())),
|
||||
"description" -> trim(label("bio", optional(text()))),
|
||||
"url" -> trim(label("URL", optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID", optional(text())))
|
||||
)(NewUserForm.apply)
|
||||
|
||||
val editUserForm = mapping(
|
||||
"userName" -> trim(label("Username" ,text(required, maxlength(100), identifier))),
|
||||
"password" -> trim(label("Password" ,optional(text(maxlength(20), password)))),
|
||||
"fullName" -> trim(label("Full Name" ,text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address" ,text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||
"isAdmin" -> trim(label("User Type" ,boolean())),
|
||||
"description" -> trim(label("bio" ,optional(text()))),
|
||||
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" ,optional(text()))),
|
||||
"clearImage" -> trim(label("Clear image" ,boolean())),
|
||||
"removed" -> trim(label("Disable" ,boolean(disableByNotYourself("userName"))))
|
||||
"userName" -> trim(label("Username", text(required, maxlength(100), identifier))),
|
||||
"password" -> trim(label("Password", optional(text(maxlength(20), password)))),
|
||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||
"isAdmin" -> trim(label("User Type", boolean())),
|
||||
"description" -> trim(label("bio", optional(text()))),
|
||||
"url" -> trim(label("URL", optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID", optional(text()))),
|
||||
"clearImage" -> trim(label("Clear image", boolean())),
|
||||
"removed" -> trim(label("Disable", boolean(disableByNotYourself("userName"))))
|
||||
)(EditUserForm.apply)
|
||||
|
||||
val newGroupForm = mapping(
|
||||
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"groupName" -> trim(label("Group name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"description" -> trim(label("Group description", optional(text()))),
|
||||
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" ,optional(text()))),
|
||||
"members" -> trim(label("Members" ,text(required, members)))
|
||||
"url" -> trim(label("URL", optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID", optional(text()))),
|
||||
"members" -> trim(label("Members", text(required, members)))
|
||||
)(NewGroupForm.apply)
|
||||
|
||||
val editGroupForm = mapping(
|
||||
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier))),
|
||||
"groupName" -> trim(label("Group name", text(required, maxlength(100), identifier))),
|
||||
"description" -> trim(label("Group description", optional(text()))),
|
||||
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID" ,optional(text()))),
|
||||
"members" -> trim(label("Members" ,text(required, members))),
|
||||
"clearImage" -> trim(label("Clear image" ,boolean())),
|
||||
"removed" -> trim(label("Disable" ,boolean()))
|
||||
"url" -> trim(label("URL", optional(text(maxlength(200))))),
|
||||
"fileId" -> trim(label("File ID", optional(text()))),
|
||||
"members" -> trim(label("Members", text(required, members))),
|
||||
"clearImage" -> trim(label("Clear image", boolean())),
|
||||
"removed" -> trim(label("Disable", boolean()))
|
||||
)(EditGroupForm.apply)
|
||||
|
||||
|
||||
get("/admin/dbviewer")(adminOnly {
|
||||
val conn = request2Session(request).conn
|
||||
val meta = conn.getMetaData
|
||||
val tables = ListBuffer[Table]()
|
||||
using(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))){ rs =>
|
||||
while(rs.next()){
|
||||
val tableName = rs.getString("TABLE_NAME")
|
||||
using(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) {
|
||||
rs =>
|
||||
while (rs.next()) {
|
||||
val tableName = rs.getString("TABLE_NAME")
|
||||
|
||||
val pkColumns = ListBuffer[String]()
|
||||
using(meta.getPrimaryKeys(null, null, tableName)){ rs =>
|
||||
while(rs.next()){
|
||||
pkColumns += rs.getString("COLUMN_NAME").toUpperCase
|
||||
val pkColumns = ListBuffer[String]()
|
||||
using(meta.getPrimaryKeys(null, null, tableName)) { rs =>
|
||||
while (rs.next()) {
|
||||
pkColumns += rs.getString("COLUMN_NAME").toUpperCase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val columns = ListBuffer[Column]()
|
||||
using(meta.getColumns(null, "%", tableName, "%")){ rs =>
|
||||
while(rs.next()){
|
||||
val columnName = rs.getString("COLUMN_NAME").toUpperCase
|
||||
columns += Column(columnName, pkColumns.contains(columnName))
|
||||
val columns = ListBuffer[Column]()
|
||||
using(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)
|
||||
}
|
||||
tables += Table(tableName.toUpperCase, columns)
|
||||
}
|
||||
}
|
||||
html.dbviewer(tables)
|
||||
})
|
||||
|
||||
post("/admin/dbviewer/_query")(adminOnly {
|
||||
contentType = formats("json")
|
||||
params.get("query").collectFirst { case query if query.trim.nonEmpty =>
|
||||
val trimmedQuery = query.trim
|
||||
if(trimmedQuery.nonEmpty){
|
||||
try {
|
||||
val conn = request2Session(request).conn
|
||||
using(conn.prepareStatement(query)){ stmt =>
|
||||
if(trimmedQuery.toUpperCase.startsWith("SELECT")){
|
||||
using(stmt.executeQuery()){ rs =>
|
||||
val meta = rs.getMetaData
|
||||
val columns = for(i <- 1 to meta.getColumnCount) yield {
|
||||
meta.getColumnName(i)
|
||||
params.get("query").collectFirst {
|
||||
case query if query.trim.nonEmpty =>
|
||||
val trimmedQuery = query.trim
|
||||
if (trimmedQuery.nonEmpty) {
|
||||
try {
|
||||
val conn = request2Session(request).conn
|
||||
using(conn.prepareStatement(query)) {
|
||||
stmt =>
|
||||
if (trimmedQuery.toUpperCase.startsWith("SELECT")) {
|
||||
using(stmt.executeQuery()) {
|
||||
rs =>
|
||||
val meta = rs.getMetaData
|
||||
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 rows = stmt.executeUpdate()
|
||||
Ok(Serialization.write(Map("type" -> "update", "rows" -> rows)))
|
||||
}
|
||||
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 rows = stmt.executeUpdate()
|
||||
Ok(Serialization.write(Map("type" -> "update", "rows" -> rows)))
|
||||
}
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
Ok(Serialization.write(Map("type" -> "error", "message" -> e.toString)))
|
||||
}
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
Ok(Serialization.write(Map("type" -> "error", "message" -> e.toString)))
|
||||
}
|
||||
}
|
||||
} getOrElse Ok(Serialization.write(Map("type" -> "error", "message" -> "query is empty")))
|
||||
})
|
||||
|
||||
@@ -239,11 +281,10 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
if (form.sshAddress != context.settings.sshAddress) {
|
||||
SshServer.stop()
|
||||
for {
|
||||
sshAddress <- form.sshAddress
|
||||
baseUrl <- form.baseUrl
|
||||
}
|
||||
SshServer.start(sshAddress, baseUrl)
|
||||
for {
|
||||
sshAddress <- form.sshAddress
|
||||
baseUrl <- form.baseUrl
|
||||
} SshServer.start(sshAddress, baseUrl)
|
||||
}
|
||||
|
||||
flash += "info" -> "System settings has been updated."
|
||||
@@ -253,10 +294,10 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
post("/admin/system/sendmail", sendMailForm)(adminOnly { form =>
|
||||
try {
|
||||
new Mailer(context.settings.copy(smtp = Some(form.smtp), notification = true)).send(
|
||||
to = form.testAddress,
|
||||
subject = "Test message from GitBucket",
|
||||
textMsg = "This is a test message from GitBucket.",
|
||||
htmlMsg = None,
|
||||
to = form.testAddress,
|
||||
subject = "Test message from GitBucket",
|
||||
textMsg = "This is a test message from GitBucket.",
|
||||
htmlMsg = None,
|
||||
loginAccount = context.loginAccount
|
||||
)
|
||||
|
||||
@@ -274,20 +315,27 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
val gitbucketVersion = Semver.valueOf(GitBucketCoreModule.getVersions.asScala.last.getVersion)
|
||||
|
||||
// Plugins in the local repository
|
||||
val repositoryPlugins = PluginRepository.getPlugins()
|
||||
val repositoryPlugins = PluginRepository
|
||||
.getPlugins()
|
||||
.filterNot { meta =>
|
||||
enabledPlugins.exists { plugin => plugin.pluginId == meta.id &&
|
||||
enabledPlugins.exists { plugin =>
|
||||
plugin.pluginId == meta.id &&
|
||||
Semver.valueOf(plugin.pluginVersion).greaterThanOrEqualTo(Semver.valueOf(meta.latestVersion.version))
|
||||
}
|
||||
}.map { meta =>
|
||||
(meta, meta.versions.reverse.find { version => gitbucketVersion.satisfies(version.range) })
|
||||
}.collect { case (meta, Some(version)) =>
|
||||
new PluginInfoBase(
|
||||
pluginId = meta.id,
|
||||
pluginName = meta.name,
|
||||
pluginVersion = version.version,
|
||||
description = meta.description
|
||||
)
|
||||
}
|
||||
.map { meta =>
|
||||
(meta, meta.versions.reverse.find { version =>
|
||||
gitbucketVersion.satisfies(version.range)
|
||||
})
|
||||
}
|
||||
.collect {
|
||||
case (meta, Some(version)) =>
|
||||
new PluginInfoBase(
|
||||
pluginId = meta.id,
|
||||
pluginName = meta.name,
|
||||
pluginVersion = version.version,
|
||||
description = meta.description
|
||||
)
|
||||
}
|
||||
|
||||
// Merge
|
||||
@@ -304,11 +352,13 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
post("/admin/plugins/:pluginId/:version/_uninstall")(adminOnly {
|
||||
val pluginId = params("pluginId")
|
||||
val version = params("version")
|
||||
PluginRegistry().getPlugins()
|
||||
val version = params("version")
|
||||
PluginRegistry()
|
||||
.getPlugins()
|
||||
.collect { case plugin if (plugin.pluginId == pluginId && plugin.pluginVersion == version) => plugin }
|
||||
.foreach { _ =>
|
||||
PluginRegistry.uninstall(pluginId, request.getServletContext, loadSystemSettings(), request2Session(request).conn)
|
||||
PluginRegistry
|
||||
.uninstall(pluginId, request.getServletContext, loadSystemSettings(), request2Session(request).conn)
|
||||
flash += "info" -> s"${pluginId} was uninstalled."
|
||||
}
|
||||
redirect("/admin/plugins")
|
||||
@@ -316,32 +366,34 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
post("/admin/plugins/:pluginId/:version/_install")(adminOnly {
|
||||
val pluginId = params("pluginId")
|
||||
val version = params("version")
|
||||
val version = params("version")
|
||||
/// TODO!!!!
|
||||
PluginRepository.getPlugins()
|
||||
.collect { case meta if meta.id == pluginId => (meta, meta.versions.find(_.version == version) )}
|
||||
.foreach { case (meta, version) =>
|
||||
version.foreach { version =>
|
||||
// TODO Install version!
|
||||
PluginRegistry.install(
|
||||
new java.io.File(PluginHome, s".repository/${version.file}"),
|
||||
request.getServletContext,
|
||||
loadSystemSettings(),
|
||||
request2Session(request).conn
|
||||
)
|
||||
flash += "info" -> s"${pluginId} was installed."
|
||||
}
|
||||
PluginRepository
|
||||
.getPlugins()
|
||||
.collect { case meta if meta.id == pluginId => (meta, meta.versions.find(_.version == version)) }
|
||||
.foreach {
|
||||
case (meta, version) =>
|
||||
version.foreach { version =>
|
||||
// TODO Install version!
|
||||
PluginRegistry.install(
|
||||
new java.io.File(PluginHome, s".repository/${version.file}"),
|
||||
request.getServletContext,
|
||||
loadSystemSettings(),
|
||||
request2Session(request).conn
|
||||
)
|
||||
flash += "info" -> s"${pluginId} was installed."
|
||||
}
|
||||
}
|
||||
redirect("/admin/plugins")
|
||||
})
|
||||
|
||||
|
||||
get("/admin/users")(adminOnly {
|
||||
val includeRemoved = params.get("includeRemoved").map(_.toBoolean).getOrElse(false)
|
||||
val includeGroups = params.get("includeGroups").map(_.toBoolean).getOrElse(false)
|
||||
val users = getAllUsers(includeRemoved, includeGroups)
|
||||
val members = users.collect { case account if(account.isGroupAccount) =>
|
||||
account.userName -> getGroupMembers(account.userName).map(_.userName)
|
||||
val users = getAllUsers(includeRemoved, includeGroups)
|
||||
val members = users.collect {
|
||||
case account if (account.isGroupAccount) =>
|
||||
account.userName -> getGroupMembers(account.userName).map(_.userName)
|
||||
}.toMap
|
||||
|
||||
html.userlist(users, members, includeRemoved, includeGroups)
|
||||
@@ -352,7 +404,15 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
})
|
||||
|
||||
post("/admin/users/_newuser", newUserForm)(adminOnly { form =>
|
||||
createAccount(form.userName, sha1(form.password), form.fullName, form.mailAddress, form.isAdmin, form.description, form.url)
|
||||
createAccount(
|
||||
form.userName,
|
||||
sha1(form.password),
|
||||
form.fullName,
|
||||
form.mailAddress,
|
||||
form.isAdmin,
|
||||
form.description,
|
||||
form.url
|
||||
)
|
||||
updateImage(form.userName, form.fileId, false)
|
||||
redirect("/admin/users")
|
||||
})
|
||||
@@ -364,39 +424,43 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
post("/admin/users/:name/_edituser", editUserForm)(adminOnly { form =>
|
||||
val userName = params("userName")
|
||||
getAccountByUserName(userName, true).map { account =>
|
||||
if(account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(account)){
|
||||
flash += "error" -> "Account can't be turned off because this is last one administrator."
|
||||
redirect(s"/admin/users/${userName}/_edituser")
|
||||
} else {
|
||||
if(form.isRemoved){
|
||||
// Remove repositories
|
||||
// getRepositoryNamesOfUser(userName).foreach { repositoryName =>
|
||||
// deleteRepository(userName, repositoryName)
|
||||
// FileUtils.deleteDirectory(getRepositoryDir(userName, repositoryName))
|
||||
// FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
|
||||
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
|
||||
// }
|
||||
// Remove from GROUP_MEMBER and COLLABORATOR
|
||||
removeUserRelatedData(userName)
|
||||
getAccountByUserName(userName, true).map {
|
||||
account =>
|
||||
if (account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(account)) {
|
||||
flash += "error" -> "Account can't be turned off because this is last one administrator."
|
||||
redirect(s"/admin/users/${userName}/_edituser")
|
||||
} else {
|
||||
if (form.isRemoved) {
|
||||
// Remove repositories
|
||||
// getRepositoryNamesOfUser(userName).foreach { repositoryName =>
|
||||
// deleteRepository(userName, repositoryName)
|
||||
// FileUtils.deleteDirectory(getRepositoryDir(userName, repositoryName))
|
||||
// FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
|
||||
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
|
||||
// }
|
||||
// Remove from GROUP_MEMBER and COLLABORATOR
|
||||
removeUserRelatedData(userName)
|
||||
}
|
||||
|
||||
updateAccount(
|
||||
account.copy(
|
||||
password = form.password.map(sha1).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)
|
||||
|
||||
// call hooks
|
||||
if (form.isRemoved) PluginRegistry().getAccountHooks.foreach(_.deleted(userName))
|
||||
|
||||
redirect("/admin/users")
|
||||
}
|
||||
|
||||
updateAccount(account.copy(
|
||||
password = form.password.map(sha1).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)
|
||||
|
||||
// call hooks
|
||||
if(form.isRemoved) PluginRegistry().getAccountHooks.foreach(_.deleted(userName))
|
||||
|
||||
redirect("/admin/users")
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
})
|
||||
|
||||
@@ -406,33 +470,47 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
post("/admin/users/_newgroup", newGroupForm)(adminOnly { form =>
|
||||
createGroup(form.groupName, form.description, form.url)
|
||||
updateGroupMembers(form.groupName, form.members.split(",").map {
|
||||
_.split(":") match {
|
||||
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||
}
|
||||
}.toList)
|
||||
updateGroupMembers(
|
||||
form.groupName,
|
||||
form.members
|
||||
.split(",")
|
||||
.map {
|
||||
_.split(":") match {
|
||||
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||
}
|
||||
}
|
||||
.toList
|
||||
)
|
||||
updateImage(form.groupName, form.fileId, false)
|
||||
redirect("/admin/users")
|
||||
})
|
||||
|
||||
get("/admin/users/:groupName/_editgroup")(adminOnly {
|
||||
defining(params("groupName")){ groupName =>
|
||||
defining(params("groupName")) { groupName =>
|
||||
html.usergroup(getAccountByUserName(groupName, true), getGroupMembers(groupName))
|
||||
}
|
||||
})
|
||||
|
||||
post("/admin/users/:groupName/_editgroup", editGroupForm)(adminOnly { form =>
|
||||
defining(params("groupName"), form.members.split(",").map {
|
||||
_.split(":") match {
|
||||
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||
}
|
||||
}.toList){ case (groupName, members) =>
|
||||
getAccountByUserName(groupName, true).map { account =>
|
||||
updateGroup(groupName, form.description, form.url, form.isRemoved)
|
||||
defining(
|
||||
params("groupName"),
|
||||
form.members
|
||||
.split(",")
|
||||
.map {
|
||||
_.split(":") match {
|
||||
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||
}
|
||||
}
|
||||
.toList
|
||||
) {
|
||||
case (groupName, members) =>
|
||||
getAccountByUserName(groupName, true).map {
|
||||
account =>
|
||||
updateGroup(groupName, form.description, form.url, form.isRemoved)
|
||||
|
||||
if(form.isRemoved){
|
||||
// Remove from GROUP_MEMBER
|
||||
updateGroupMembers(form.groupName, Nil)
|
||||
if (form.isRemoved) {
|
||||
// Remove from GROUP_MEMBER
|
||||
updateGroupMembers(form.groupName, Nil)
|
||||
// // Remove repositories
|
||||
// getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
||||
// deleteRepository(groupName, repositoryName)
|
||||
@@ -440,9 +518,9 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
// FileUtils.deleteDirectory(getWikiRepositoryDir(groupName, repositoryName))
|
||||
// FileUtils.deleteDirectory(getTemporaryDir(groupName, repositoryName))
|
||||
// }
|
||||
} else {
|
||||
// Update GROUP_MEMBER
|
||||
updateGroupMembers(form.groupName, members)
|
||||
} else {
|
||||
// Update GROUP_MEMBER
|
||||
updateGroupMembers(form.groupName, members)
|
||||
// // Update COLLABORATOR for group repositories
|
||||
// getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
||||
// removeCollaborators(form.groupName, repositoryName)
|
||||
@@ -450,12 +528,12 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
// addCollaborator(form.groupName, repositoryName, userName)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||
redirect("/admin/users")
|
||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||
redirect("/admin/users")
|
||||
|
||||
} getOrElse NotFound()
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -473,25 +551,26 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName)
|
||||
response.setContentLength(file.length.toInt)
|
||||
|
||||
using(new FileInputStream(file)){ in =>
|
||||
using(new FileInputStream(file)) { in =>
|
||||
IOUtils.copy(in, response.outputStream)
|
||||
}
|
||||
|
||||
()
|
||||
})
|
||||
|
||||
private def members: Constraint = new Constraint(){
|
||||
private def members: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
if(value.split(",").exists {
|
||||
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
||||
}) None else Some("Must select one manager at least.")
|
||||
if (value.split(",").exists {
|
||||
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
||||
}) None
|
||||
else Some("Must select one manager at least.")
|
||||
}
|
||||
}
|
||||
|
||||
protected def disableByNotYourself(paramName: String): Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
params.get(paramName).flatMap { userName =>
|
||||
if(userName == context.loginAccount.get.userName && params.get("removed") == Some("true"))
|
||||
if (userName == context.loginAccount.get.userName && params.get("removed") == Some("true"))
|
||||
Some("You can't disable your account yourself")
|
||||
else
|
||||
None
|
||||
|
||||
@@ -14,58 +14,58 @@ trait ValidationSupport extends FormSupport { self: ServletBase with JacksonJson
|
||||
|
||||
def get[T](path: String, form: ValueType[T])(action: T => Any): Route = {
|
||||
registerValidate(path, form)
|
||||
get(path){
|
||||
get(path) {
|
||||
validate(form)(errors => BadRequest(), form => action(form))
|
||||
}
|
||||
}
|
||||
|
||||
def post[T](path: String, form: ValueType[T])(action: T => Any): Route = {
|
||||
registerValidate(path, form)
|
||||
post(path){
|
||||
post(path) {
|
||||
validate(form)(errors => BadRequest(), form => action(form))
|
||||
}
|
||||
}
|
||||
|
||||
def put[T](path: String, form: ValueType[T])(action: T => Any): Route = {
|
||||
registerValidate(path, form)
|
||||
put(path){
|
||||
put(path) {
|
||||
validate(form)(errors => BadRequest(), form => action(form))
|
||||
}
|
||||
}
|
||||
|
||||
def delete[T](path: String, form: ValueType[T])(action: T => Any): Route = {
|
||||
registerValidate(path, form)
|
||||
delete(path){
|
||||
delete(path) {
|
||||
validate(form)(errors => BadRequest(), form => action(form))
|
||||
}
|
||||
}
|
||||
|
||||
def ajaxGet[T](path: String, form: ValueType[T])(action: T => Any): Route = {
|
||||
get(path){
|
||||
get(path) {
|
||||
validate(form)(errors => ajaxError(errors), form => action(form))
|
||||
}
|
||||
}
|
||||
|
||||
def ajaxPost[T](path: String, form: ValueType[T])(action: T => Any): Route = {
|
||||
post(path){
|
||||
post(path) {
|
||||
validate(form)(errors => ajaxError(errors), form => action(form))
|
||||
}
|
||||
}
|
||||
|
||||
def ajaxDelete[T](path: String, form: ValueType[T])(action: T => Any): Route = {
|
||||
delete(path){
|
||||
delete(path) {
|
||||
validate(form)(errors => ajaxError(errors), form => action(form))
|
||||
}
|
||||
}
|
||||
|
||||
def ajaxPut[T](path: String, form: ValueType[T])(action: T => Any): Route = {
|
||||
put(path){
|
||||
put(path) {
|
||||
validate(form)(errors => ajaxError(errors), form => action(form))
|
||||
}
|
||||
}
|
||||
|
||||
private def registerValidate[T](path: String, form: ValueType[T]) = {
|
||||
post(path.replaceFirst("/$", "") + "/validate"){
|
||||
post(path.replaceFirst("/$", "") + "/validate") {
|
||||
contentType = "application/json"
|
||||
toJson(form.validate("", multiParams, messages))
|
||||
}
|
||||
@@ -84,8 +84,9 @@ trait ValidationSupport extends FormSupport { self: ServletBase with JacksonJson
|
||||
* Converts errors to JSON.
|
||||
*/
|
||||
private def toJson(errors: Seq[(String, String)]): JObject =
|
||||
JObject(errors.map { case (key, value) =>
|
||||
JField(key, JString(value))
|
||||
JObject(errors.map {
|
||||
case (key, value) =>
|
||||
JField(key, JString(value))
|
||||
}.toList)
|
||||
|
||||
}
|
||||
|
||||
@@ -14,38 +14,60 @@ import org.scalatra.forms._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.scalatra.i18n.Messages
|
||||
|
||||
class WikiController extends WikiControllerBase
|
||||
with WikiService with RepositoryService with AccountService with ActivityService with WebHookService
|
||||
with ReadableUsersAuthenticator with ReferrerAuthenticator
|
||||
class WikiController
|
||||
extends WikiControllerBase
|
||||
with WikiService
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ActivityService
|
||||
with WebHookService
|
||||
with ReadableUsersAuthenticator
|
||||
with ReferrerAuthenticator
|
||||
|
||||
trait WikiControllerBase extends ControllerBase {
|
||||
self: WikiService with RepositoryService with AccountService with ActivityService with WebHookService
|
||||
with ReadableUsersAuthenticator with ReferrerAuthenticator =>
|
||||
self: WikiService
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ActivityService
|
||||
with WebHookService
|
||||
with ReadableUsersAuthenticator
|
||||
with ReferrerAuthenticator =>
|
||||
|
||||
case class WikiPageEditForm(pageName: String, content: String, message: Option[String], currentPageName: String, id: String)
|
||||
case class WikiPageEditForm(
|
||||
pageName: String,
|
||||
content: String,
|
||||
message: Option[String],
|
||||
currentPageName: String,
|
||||
id: String
|
||||
)
|
||||
|
||||
val newForm = mapping(
|
||||
"pageName" -> trim(label("Page name" , text(required, maxlength(40), pagename, unique))),
|
||||
"content" -> trim(label("Content" , text(required, conflictForNew))),
|
||||
"message" -> trim(label("Message" , optional(text()))),
|
||||
"currentPageName" -> trim(label("Current page name" , text())),
|
||||
"id" -> trim(label("Latest commit id" , text()))
|
||||
"pageName" -> trim(label("Page name", text(required, maxlength(40), pagename, unique))),
|
||||
"content" -> trim(label("Content", text(required, conflictForNew))),
|
||||
"message" -> trim(label("Message", optional(text()))),
|
||||
"currentPageName" -> trim(label("Current page name", text())),
|
||||
"id" -> trim(label("Latest commit id", text()))
|
||||
)(WikiPageEditForm.apply)
|
||||
|
||||
val editForm = mapping(
|
||||
"pageName" -> trim(label("Page name" , text(required, maxlength(40), pagename))),
|
||||
"content" -> trim(label("Content" , text(required, conflictForEdit))),
|
||||
"message" -> trim(label("Message" , optional(text()))),
|
||||
"currentPageName" -> trim(label("Current page name" , text(required))),
|
||||
"id" -> trim(label("Latest commit id" , text(required)))
|
||||
"pageName" -> trim(label("Page name", text(required, maxlength(40), pagename))),
|
||||
"content" -> trim(label("Content", text(required, conflictForEdit))),
|
||||
"message" -> trim(label("Message", optional(text()))),
|
||||
"currentPageName" -> trim(label("Current page name", text(required))),
|
||||
"id" -> trim(label("Latest commit id", text(required)))
|
||||
)(WikiPageEditForm.apply)
|
||||
|
||||
get("/:owner/:repository/wiki")(referrersOnly { repository =>
|
||||
getWikiPage(repository.owner, repository.name, "Home").map { page =>
|
||||
html.page("Home", page, getWikiPageList(repository.owner, repository.name),
|
||||
repository, isEditable(repository),
|
||||
html.page(
|
||||
"Home",
|
||||
page,
|
||||
getWikiPageList(repository.owner, repository.name),
|
||||
repository,
|
||||
isEditable(repository),
|
||||
getWikiPage(repository.owner, repository.name, "_Sidebar"),
|
||||
getWikiPage(repository.owner, repository.name, "_Footer"))
|
||||
getWikiPage(repository.owner, repository.name, "_Footer")
|
||||
)
|
||||
} getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/Home/_edit")
|
||||
})
|
||||
|
||||
@@ -53,20 +75,25 @@ trait WikiControllerBase extends ControllerBase {
|
||||
val pageName = StringUtil.urlDecode(params("page"))
|
||||
|
||||
getWikiPage(repository.owner, repository.name, pageName).map { page =>
|
||||
html.page(pageName, page, getWikiPageList(repository.owner, repository.name),
|
||||
repository, isEditable(repository),
|
||||
html.page(
|
||||
pageName,
|
||||
page,
|
||||
getWikiPageList(repository.owner, repository.name),
|
||||
repository,
|
||||
isEditable(repository),
|
||||
getWikiPage(repository.owner, repository.name, "_Sidebar"),
|
||||
getWikiPage(repository.owner, repository.name, "_Footer"))
|
||||
getWikiPage(repository.owner, repository.name, "_Footer")
|
||||
)
|
||||
} getOrElse redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_edit")
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/:page/_history")(referrersOnly { repository =>
|
||||
val pageName = StringUtil.urlDecode(params("page"))
|
||||
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match {
|
||||
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository, isEditable(repository))
|
||||
case Left(_) => NotFound()
|
||||
case Left(_) => NotFound()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -75,40 +102,56 @@ trait WikiControllerBase extends ControllerBase {
|
||||
val pageName = StringUtil.urlDecode(params("page"))
|
||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
html.compare(Some(pageName), from, to, JGitUtil.getDiffs(git, Some(from), to, true, false).filter(_.newPath == pageName + ".md"), repository,
|
||||
isEditable(repository), flash.get("info"))
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
html.compare(
|
||||
Some(pageName),
|
||||
from,
|
||||
to,
|
||||
JGitUtil.getDiffs(git, Some(from), to, true, false).filter(_.newPath == pageName + ".md"),
|
||||
repository,
|
||||
isEditable(repository),
|
||||
flash.get("info")
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/_compare/:commitId")(referrersOnly { repository =>
|
||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
html.compare(None, from, to, JGitUtil.getDiffs(git, Some(from), to, true, false), repository,
|
||||
isEditable(repository), flash.get("info"))
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
html.compare(
|
||||
None,
|
||||
from,
|
||||
to,
|
||||
JGitUtil.getDiffs(git, Some(from), to, true, false),
|
||||
repository,
|
||||
isEditable(repository),
|
||||
flash.get("info")
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/:page/_revert/:commitId")(readableUsersOnly { repository =>
|
||||
if(isEditable(repository)){
|
||||
if (isEditable(repository)) {
|
||||
val pageName = StringUtil.urlDecode(params("page"))
|
||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||
|
||||
if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))){
|
||||
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
|
||||
} else {
|
||||
flash += "info" -> "This patch was not able to be reversed."
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}")
|
||||
redirect(
|
||||
s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}"
|
||||
)
|
||||
}
|
||||
} else Unauthorized()
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/_revert/:commitId")(readableUsersOnly { repository =>
|
||||
if(isEditable(repository)){
|
||||
if (isEditable(repository)) {
|
||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||
|
||||
if(revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)){
|
||||
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||
} else {
|
||||
flash += "info" -> "This patch was not able to be reversed."
|
||||
@@ -118,85 +161,102 @@ trait WikiControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/:page/_edit")(readableUsersOnly { repository =>
|
||||
if(isEditable(repository)){
|
||||
if (isEditable(repository)) {
|
||||
val pageName = StringUtil.urlDecode(params("page"))
|
||||
html.edit(pageName, getWikiPage(repository.owner, repository.name, pageName), repository)
|
||||
} else Unauthorized()
|
||||
})
|
||||
|
||||
post("/:owner/:repository/wiki/_edit", editForm)(readableUsersOnly { (form, repository) =>
|
||||
if(isEditable(repository)){
|
||||
defining(context.loginAccount.get){ loginAccount =>
|
||||
saveWikiPage(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
form.currentPageName,
|
||||
form.pageName,
|
||||
appendNewLine(convertLineSeparator(form.content, "LF"), "LF"),
|
||||
loginAccount,
|
||||
form.message.getOrElse(""),
|
||||
Some(form.id)
|
||||
).map { commitId =>
|
||||
updateLastActivityDate(repository.owner, repository.name)
|
||||
recordEditWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName, commitId)
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum){
|
||||
getAccountByUserName(repository.owner).map { repositoryUser =>
|
||||
WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
||||
}
|
||||
if (isEditable(repository)) {
|
||||
defining(context.loginAccount.get) {
|
||||
loginAccount =>
|
||||
saveWikiPage(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
form.currentPageName,
|
||||
form.pageName,
|
||||
appendNewLine(convertLineSeparator(form.content, "LF"), "LF"),
|
||||
loginAccount,
|
||||
form.message.getOrElse(""),
|
||||
Some(form.id)
|
||||
).map {
|
||||
commitId =>
|
||||
updateLastActivityDate(repository.owner, repository.name)
|
||||
recordEditWikiPageActivity(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
loginAccount.userName,
|
||||
form.pageName,
|
||||
commitId
|
||||
)
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum) {
|
||||
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)}")
|
||||
} else {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||
}
|
||||
}
|
||||
if(notReservedPageName(form.pageName)) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
|
||||
} else {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||
}
|
||||
}
|
||||
} else Unauthorized()
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/_new")(readableUsersOnly { repository =>
|
||||
if(isEditable(repository)){
|
||||
if (isEditable(repository)) {
|
||||
html.edit("", None, repository)
|
||||
} else Unauthorized()
|
||||
})
|
||||
|
||||
post("/:owner/:repository/wiki/_new", newForm)(readableUsersOnly { (form, repository) =>
|
||||
if(isEditable(repository)){
|
||||
defining(context.loginAccount.get){ loginAccount =>
|
||||
saveWikiPage(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
form.currentPageName,
|
||||
form.pageName,
|
||||
form.content,
|
||||
loginAccount,
|
||||
form.message.getOrElse(""),
|
||||
None
|
||||
).map { commitId =>
|
||||
updateLastActivityDate(repository.owner, repository.name)
|
||||
recordCreateWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName)
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum){
|
||||
getAccountByUserName(repository.owner).map { repositoryUser =>
|
||||
WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
||||
}
|
||||
if (isEditable(repository)) {
|
||||
defining(context.loginAccount.get) {
|
||||
loginAccount =>
|
||||
saveWikiPage(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
form.currentPageName,
|
||||
form.pageName,
|
||||
form.content,
|
||||
loginAccount,
|
||||
form.message.getOrElse(""),
|
||||
None
|
||||
).map {
|
||||
commitId =>
|
||||
updateLastActivityDate(repository.owner, repository.name)
|
||||
recordCreateWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName)
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum) {
|
||||
getAccountByUserName(repository.owner).map { repositoryUser =>
|
||||
WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(notReservedPageName(form.pageName)) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
|
||||
} else {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||
}
|
||||
if (notReservedPageName(form.pageName)) {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(form.pageName)}")
|
||||
} else {
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||
}
|
||||
}
|
||||
} else Unauthorized()
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/:page/_delete")(readableUsersOnly { repository =>
|
||||
if(isEditable(repository)){
|
||||
if (isEditable(repository)) {
|
||||
val pageName = StringUtil.urlDecode(params("page"))
|
||||
|
||||
defining(context.loginAccount.get){ loginAccount =>
|
||||
deleteWikiPage(repository.owner, repository.name, pageName, loginAccount.fullName, loginAccount.mailAddress, s"Destroyed ${pageName}")
|
||||
defining(context.loginAccount.get) { loginAccount =>
|
||||
deleteWikiPage(
|
||||
repository.owner,
|
||||
repository.name,
|
||||
pageName,
|
||||
loginAccount.fullName,
|
||||
loginAccount.mailAddress,
|
||||
s"Destroyed ${pageName}"
|
||||
)
|
||||
updateLastActivityDate(repository.owner, repository.name)
|
||||
|
||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||
@@ -209,17 +269,17 @@ trait WikiControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
JGitUtil.getCommitLog(git, "master") match {
|
||||
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
|
||||
case Left(_) => NotFound()
|
||||
case Left(_) => NotFound()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository =>
|
||||
val path = multiParams("splat").head
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve("master"))
|
||||
|
||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||
@@ -228,25 +288,32 @@ trait WikiControllerBase extends ControllerBase {
|
||||
}
|
||||
})
|
||||
|
||||
private def unique: Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, params: Map[String, Seq[String]], messages: Messages): Option[String] =
|
||||
getWikiPageList(params.value("owner"), params.value("repository")).find(_ == value).map(_ => "Page already exists.")
|
||||
private def unique: Constraint = new Constraint() {
|
||||
override def validate(
|
||||
name: String,
|
||||
value: String,
|
||||
params: Map[String, Seq[String]],
|
||||
messages: Messages
|
||||
): Option[String] =
|
||||
getWikiPageList(params.value("owner"), params.value("repository"))
|
||||
.find(_ == value)
|
||||
.map(_ => "Page already exists.")
|
||||
}
|
||||
|
||||
private def pagename: Constraint = new Constraint(){
|
||||
private def pagename: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if(value.exists("\\/:*?\"<>|".contains(_))){
|
||||
if (value.exists("\\/:*?\"<>|".contains(_))) {
|
||||
Some(s"${name} contains invalid character.")
|
||||
} else if(notReservedPageName(value) && (value.startsWith("_") || value.startsWith("-"))){
|
||||
} else if (notReservedPageName(value) && (value.startsWith("_") || value.startsWith("-"))) {
|
||||
Some(s"${name} starts with invalid character.")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
private def notReservedPageName(value: String) = ! (Array[String]("_Sidebar","_Footer") contains value)
|
||||
private def notReservedPageName(value: String) = !(Array[String]("_Sidebar", "_Footer") contains value)
|
||||
|
||||
private def conflictForNew: Constraint = new Constraint(){
|
||||
private def conflictForNew: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
targetWikiPage.map { _ =>
|
||||
"Someone has created the wiki since you started. Please reload this page and re-apply your changes."
|
||||
@@ -254,9 +321,9 @@ trait WikiControllerBase extends ControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
private def conflictForEdit: Constraint = new Constraint(){
|
||||
private def conflictForEdit: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
targetWikiPage.filter(_.id != params("id")).map{ _ =>
|
||||
targetWikiPage.filter(_.id != params("id")).map { _ =>
|
||||
"Someone has edited the wiki since you started. Please reload this page and re-apply your changes."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package gitbucket.core.model
|
||||
|
||||
|
||||
trait AccessTokenComponent { self: Profile =>
|
||||
import profile.api._
|
||||
|
||||
|
||||
@@ -20,7 +20,22 @@ trait AccountComponent { self: Profile =>
|
||||
val groupAccount = column[Boolean]("GROUP_ACCOUNT")
|
||||
val removed = column[Boolean]("REMOVED")
|
||||
val description = column[String]("DESCRIPTION")
|
||||
def * = (userName, fullName, mailAddress, password, isAdmin, url.?, registeredDate, updatedDate, lastLoginDate.?, image.?, groupAccount, removed, description.?) <> (Account.tupled, Account.unapply)
|
||||
def * =
|
||||
(
|
||||
userName,
|
||||
fullName,
|
||||
mailAddress,
|
||||
password,
|
||||
isAdmin,
|
||||
url.?,
|
||||
registeredDate,
|
||||
updatedDate,
|
||||
lastLoginDate.?,
|
||||
image.?,
|
||||
groupAccount,
|
||||
removed,
|
||||
description.?
|
||||
) <> (Account.tupled, Account.unapply)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ package gitbucket.core.model
|
||||
trait AccountWebHookComponent extends TemplateComponent { self: Profile =>
|
||||
import profile.api._
|
||||
|
||||
private implicit val whContentTypeColumnType = MappedColumnType.base[WebHookContentType, String](whct => whct.code , code => WebHookContentType.valueOf(code))
|
||||
private implicit val whContentTypeColumnType =
|
||||
MappedColumnType.base[WebHookContentType, String](whct => whct.code, code => WebHookContentType.valueOf(code))
|
||||
|
||||
lazy val AccountWebHooks = TableQuery[AccountWebHooks]
|
||||
|
||||
|
||||
@@ -8,11 +8,13 @@ trait AccountWebHookEventComponent extends TemplateComponent {
|
||||
|
||||
lazy val AccountWebHookEvents = TableQuery[AccountWebHookEvents]
|
||||
|
||||
class AccountWebHookEvents(tag: Tag) extends Table[AccountWebHookEvent](tag, "ACCOUNT_WEB_HOOK_EVENT") with BasicTemplate {
|
||||
class AccountWebHookEvents(tag: Tag)
|
||||
extends Table[AccountWebHookEvent](tag, "ACCOUNT_WEB_HOOK_EVENT")
|
||||
with BasicTemplate {
|
||||
val url = column[String]("URL")
|
||||
val event = column[WebHook.Event]("EVENT")
|
||||
|
||||
def * = (userName, url, event) <> ((AccountWebHookEvent.apply _).tupled, AccountWebHookEvent.unapply)
|
||||
def * = (userName, url, event) <> ((AccountWebHookEvent.apply _).tupled, AccountWebHookEvent.unapply)
|
||||
|
||||
def byAccountWebHook(userName: String, url: String) = (this.userName === userName.bind) && (this.url === url.bind)
|
||||
|
||||
@@ -28,7 +30,7 @@ trait AccountWebHookEventComponent extends TemplateComponent {
|
||||
}
|
||||
|
||||
case class AccountWebHookEvent(
|
||||
userName: String,
|
||||
url: String,
|
||||
event: WebHook.Event
|
||||
)
|
||||
userName: String,
|
||||
url: String,
|
||||
event: WebHook.Event
|
||||
)
|
||||
|
||||
@@ -13,7 +13,8 @@ trait ActivityComponent extends TemplateComponent { self: Profile =>
|
||||
val message = column[String]("MESSAGE")
|
||||
val additionalInfo = column[String]("ADDITIONAL_INFO")
|
||||
val activityDate = column[java.util.Date]("ACTIVITY_DATE")
|
||||
def * = (userName, repositoryName, activityUserName, activityType, message, additionalInfo.?, activityDate, activityId) <> (Activity.tupled, Activity.unapply)
|
||||
def * =
|
||||
(userName, repositoryName, activityUserName, activityType, message, additionalInfo.?, activityDate, activityId) <> (Activity.tupled, Activity.unapply)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,9 +76,11 @@ protected[model] trait TemplateComponent { self: Profile =>
|
||||
byRepository(userName, repositoryName) && (this.commitId === commitId)
|
||||
}
|
||||
|
||||
trait BranchTemplate extends BasicTemplate{ self: Table[_] =>
|
||||
trait BranchTemplate extends BasicTemplate { self: Table[_] =>
|
||||
val branch = column[String]("BRANCH")
|
||||
def byBranch(owner: String, repository: String, branchName: String) = byRepository(owner, repository) && (branch === branchName.bind)
|
||||
def byBranch(owner: Rep[String], repository: Rep[String], branchName: Rep[String]) = byRepository(owner, repository) && (this.branch === branchName)
|
||||
def byBranch(owner: String, repository: String, branchName: String) =
|
||||
byRepository(owner, repository) && (branch === branchName.bind)
|
||||
def byBranch(owner: Rep[String], repository: Rep[String], branchName: Rep[String]) =
|
||||
byRepository(owner, repository) && (this.branch === branchName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,13 +18,14 @@ trait IssueCommentComponent extends TemplateComponent { self: Profile =>
|
||||
val content = column[String]("CONTENT")
|
||||
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||
def * = (userName, repositoryName, issueId, commentId, action, commentedUserName, content, registeredDate, updatedDate) <> (IssueComment.tupled, IssueComment.unapply)
|
||||
def * =
|
||||
(userName, repositoryName, issueId, commentId, action, commentedUserName, content, registeredDate, updatedDate) <> (IssueComment.tupled, IssueComment.unapply)
|
||||
|
||||
def byPrimaryKey(commentId: Int) = this.commentId === commentId.bind
|
||||
}
|
||||
}
|
||||
|
||||
case class IssueComment (
|
||||
case class IssueComment(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
issueId: Int,
|
||||
@@ -52,7 +53,21 @@ trait CommitCommentComponent extends TemplateComponent { self: Profile =>
|
||||
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||
val issueId = column[Option[Int]]("ISSUE_ID")
|
||||
def * = (userName, repositoryName, commitId, commentId, commentedUserName, content, fileName, oldLine, newLine, registeredDate, updatedDate, issueId) <> (CommitComment.tupled, CommitComment.unapply)
|
||||
def * =
|
||||
(
|
||||
userName,
|
||||
repositoryName,
|
||||
commitId,
|
||||
commentId,
|
||||
commentedUserName,
|
||||
content,
|
||||
fileName,
|
||||
oldLine,
|
||||
newLine,
|
||||
registeredDate,
|
||||
updatedDate,
|
||||
issueId
|
||||
) <> (CommitComment.tupled, CommitComment.unapply)
|
||||
|
||||
def byPrimaryKey(commentId: Int) = this.commentId === commentId.bind
|
||||
}
|
||||
@@ -71,4 +86,4 @@ case class CommitComment(
|
||||
registeredDate: java.util.Date,
|
||||
updatedDate: java.util.Date,
|
||||
issueId: Option[Int]
|
||||
) extends Comment
|
||||
) extends Comment
|
||||
|
||||
@@ -4,7 +4,7 @@ trait CommitStatusComponent extends TemplateComponent { self: Profile =>
|
||||
import profile.api._
|
||||
import self._
|
||||
|
||||
implicit val commitStateColumnType = MappedColumnType.base[CommitState, String](b => b.name , i => CommitState(i))
|
||||
implicit val commitStateColumnType = MappedColumnType.base[CommitState, String](b => b.name, i => CommitState(i))
|
||||
|
||||
lazy val CommitStatuses = TableQuery[CommitStatuses]
|
||||
class CommitStatuses(tag: Tag) extends Table[CommitStatus](tag, "COMMIT_STATUS") with CommitTemplate {
|
||||
@@ -16,12 +16,24 @@ trait CommitStatusComponent extends TemplateComponent { self: Profile =>
|
||||
val creator = column[String]("CREATOR")
|
||||
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||
def * = (commitStatusId, userName, repositoryName, commitId, context, state, targetUrl, description, creator, registeredDate, updatedDate) <> ((CommitStatus.apply _).tupled, CommitStatus.unapply)
|
||||
def * =
|
||||
(
|
||||
commitStatusId,
|
||||
userName,
|
||||
repositoryName,
|
||||
commitId,
|
||||
context,
|
||||
state,
|
||||
targetUrl,
|
||||
description,
|
||||
creator,
|
||||
registeredDate,
|
||||
updatedDate
|
||||
) <> ((CommitStatus.apply _).tupled, CommitStatus.unapply)
|
||||
def byPrimaryKey(id: Int) = commitStatusId === id.bind
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case class CommitStatus(
|
||||
commitStatusId: Int = 0,
|
||||
userName: String,
|
||||
@@ -36,23 +48,24 @@ case class CommitStatus(
|
||||
updatedDate: java.util.Date
|
||||
)
|
||||
object CommitStatus {
|
||||
def pending(owner: String, repository: String, context: String) = CommitStatus(
|
||||
commitStatusId = 0,
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
commitId = "",
|
||||
context = context,
|
||||
state = CommitState.PENDING,
|
||||
targetUrl = None,
|
||||
description = Some("Waiting for status to be reported"),
|
||||
creator = "",
|
||||
registeredDate = new java.util.Date(),
|
||||
updatedDate = new java.util.Date())
|
||||
def pending(owner: String, repository: String, context: String) =
|
||||
CommitStatus(
|
||||
commitStatusId = 0,
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
commitId = "",
|
||||
context = context,
|
||||
state = CommitState.PENDING,
|
||||
targetUrl = None,
|
||||
description = Some("Waiting for status to be reported"),
|
||||
creator = "",
|
||||
registeredDate = new java.util.Date(),
|
||||
updatedDate = new java.util.Date()
|
||||
)
|
||||
}
|
||||
|
||||
sealed abstract class CommitState(val name: String)
|
||||
|
||||
|
||||
object CommitState {
|
||||
object ERROR extends CommitState("error")
|
||||
|
||||
@@ -76,11 +89,11 @@ object CommitState {
|
||||
* success if the latest status for all contexts is success
|
||||
*/
|
||||
def combine(statuses: Set[CommitState]): CommitState = {
|
||||
if(statuses.isEmpty){
|
||||
if (statuses.isEmpty) {
|
||||
PENDING
|
||||
} else if(statuses.contains(CommitState.ERROR) || statuses.contains(CommitState.FAILURE)) {
|
||||
} else if (statuses.contains(CommitState.ERROR) || statuses.contains(CommitState.FAILURE)) {
|
||||
FAILURE
|
||||
} else if(statuses.contains(CommitState.PENDING)) {
|
||||
} else if (statuses.contains(CommitState.PENDING)) {
|
||||
PENDING
|
||||
} else {
|
||||
SUCCESS
|
||||
@@ -88,4 +101,3 @@ object CommitState {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ trait DeployKeyComponent extends TemplateComponent { self: Profile =>
|
||||
val title = column[String]("TITLE")
|
||||
val publicKey = column[String]("PUBLIC_KEY")
|
||||
val allowWrite = column[Boolean]("ALLOW_WRITE")
|
||||
def * = (userName, repositoryName, deployKeyId, title, publicKey, allowWrite) <> (DeployKey.tupled, DeployKey.unapply)
|
||||
def * =
|
||||
(userName, repositoryName, deployKeyId, title, publicKey, allowWrite) <> (DeployKey.tupled, DeployKey.unapply)
|
||||
|
||||
def byPrimaryKey(userName: String, repositoryName: String, deployKeyId: Int) =
|
||||
(this.userName === userName.bind) && (this.repositoryName === repositoryName.bind) && (this.deployKeyId === deployKeyId.bind)
|
||||
|
||||
@@ -13,13 +13,19 @@ trait IssueComponent extends TemplateComponent { self: Profile =>
|
||||
def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
|
||||
}
|
||||
|
||||
class IssueOutline(tag: Tag) extends Table[(String, String, Int, Int, Int)](tag, "ISSUE_OUTLINE_VIEW") with IssueTemplate {
|
||||
class IssueOutline(tag: Tag)
|
||||
extends Table[(String, String, Int, Int, Int)](tag, "ISSUE_OUTLINE_VIEW")
|
||||
with IssueTemplate {
|
||||
val commentCount = column[Int]("COMMENT_COUNT")
|
||||
val priority = column[Int]("PRIORITY")
|
||||
def * = (userName, repositoryName, issueId, commentCount, priority)
|
||||
}
|
||||
|
||||
class Issues(tag: Tag) extends Table[Issue](tag, "ISSUE") with IssueTemplate with MilestoneTemplate with PriorityTemplate {
|
||||
class Issues(tag: Tag)
|
||||
extends Table[Issue](tag, "ISSUE")
|
||||
with IssueTemplate
|
||||
with MilestoneTemplate
|
||||
with PriorityTemplate {
|
||||
val openedUserName = column[String]("OPENED_USER_NAME")
|
||||
val assignedUserName = column[String]("ASSIGNED_USER_NAME")
|
||||
val title = column[String]("TITLE")
|
||||
@@ -28,7 +34,22 @@ trait IssueComponent extends TemplateComponent { self: Profile =>
|
||||
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||
val pullRequest = column[Boolean]("PULL_REQUEST")
|
||||
def * = (userName, repositoryName, issueId, openedUserName, milestoneId.?, priorityId.?, assignedUserName.?, title, content.?, closed, registeredDate, updatedDate, pullRequest) <> (Issue.tupled, Issue.unapply)
|
||||
def * =
|
||||
(
|
||||
userName,
|
||||
repositoryName,
|
||||
issueId,
|
||||
openedUserName,
|
||||
milestoneId.?,
|
||||
priorityId.?,
|
||||
assignedUserName.?,
|
||||
title,
|
||||
content.?,
|
||||
closed,
|
||||
registeredDate,
|
||||
updatedDate,
|
||||
pullRequest
|
||||
) <> (Issue.tupled, Issue.unapply)
|
||||
|
||||
def byPrimaryKey(owner: String, repository: String, issueId: Int) = byIssue(owner, repository, issueId)
|
||||
}
|
||||
|
||||
@@ -12,23 +12,19 @@ trait LabelComponent extends TemplateComponent { self: Profile =>
|
||||
def * = (userName, repositoryName, labelId, labelName, color) <> (Label.tupled, Label.unapply)
|
||||
|
||||
def byPrimaryKey(owner: String, repository: String, labelId: Int) = byLabel(owner, repository, labelId)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], labelId: Rep[Int]) = byLabel(userName, repositoryName, labelId)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], labelId: Rep[Int]) =
|
||||
byLabel(userName, repositoryName, labelId)
|
||||
}
|
||||
}
|
||||
|
||||
case class Label(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
labelId: Int = 0,
|
||||
labelName: String,
|
||||
color: String){
|
||||
case class Label(userName: String, repositoryName: String, labelId: Int = 0, labelName: String, color: String) {
|
||||
|
||||
val fontColor = {
|
||||
val r = color.substring(0, 2)
|
||||
val g = color.substring(2, 4)
|
||||
val b = color.substring(4, 6)
|
||||
|
||||
if(Integer.parseInt(r, 16) + Integer.parseInt(g, 16) + Integer.parseInt(b, 16) > 408){
|
||||
if (Integer.parseInt(r, 16) + Integer.parseInt(g, 16) + Integer.parseInt(b, 16) > 408) {
|
||||
"000000"
|
||||
} else {
|
||||
"ffffff"
|
||||
|
||||
@@ -12,10 +12,12 @@ trait MilestoneComponent extends TemplateComponent { self: Profile =>
|
||||
val description = column[Option[String]]("DESCRIPTION")
|
||||
val dueDate = column[Option[java.util.Date]]("DUE_DATE")
|
||||
val closedDate = column[Option[java.util.Date]]("CLOSED_DATE")
|
||||
def * = (userName, repositoryName, milestoneId, title, description, dueDate, closedDate) <> (Milestone.tupled, Milestone.unapply)
|
||||
def * =
|
||||
(userName, repositoryName, milestoneId, title, description, dueDate, closedDate) <> (Milestone.tupled, Milestone.unapply)
|
||||
|
||||
def byPrimaryKey(owner: String, repository: String, milestoneId: Int) = byMilestone(owner, repository, milestoneId)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], milestoneId: Rep[Int]) = byMilestone(userName, repositoryName, milestoneId)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], milestoneId: Rep[Int]) =
|
||||
byMilestone(userName, repositoryName, milestoneId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,14 +12,16 @@ trait PriorityComponent extends TemplateComponent { self: Profile =>
|
||||
val ordering = column[Int]("ORDERING")
|
||||
val isDefault = column[Boolean]("IS_DEFAULT")
|
||||
val color = column[String]("COLOR")
|
||||
def * = (userName, repositoryName, priorityId, priorityName, description.?, isDefault, ordering, color) <> (Priority.tupled, Priority.unapply)
|
||||
def * =
|
||||
(userName, repositoryName, priorityId, priorityName, description.?, isDefault, ordering, color) <> (Priority.tupled, Priority.unapply)
|
||||
|
||||
def byPrimaryKey(owner: String, repository: String, priorityId: Int) = byPriority(owner, repository, priorityId)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], priorityId: Rep[Int]) = byPriority(userName, repositoryName, priorityId)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], priorityId: Rep[Int]) =
|
||||
byPriority(userName, repositoryName, priorityId)
|
||||
}
|
||||
}
|
||||
|
||||
case class Priority (
|
||||
case class Priority(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
priorityId: Int = 0,
|
||||
@@ -27,14 +29,15 @@ case class Priority (
|
||||
description: Option[String],
|
||||
isDefault: Boolean,
|
||||
ordering: Int = 0,
|
||||
color: String){
|
||||
color: String
|
||||
) {
|
||||
|
||||
val fontColor = {
|
||||
val r = color.substring(0, 2)
|
||||
val g = color.substring(2, 4)
|
||||
val b = color.substring(4, 6)
|
||||
|
||||
if(Integer.parseInt(r, 16) + Integer.parseInt(g, 16) + Integer.parseInt(b, 16) > 408){
|
||||
if (Integer.parseInt(r, 16) + Integer.parseInt(g, 16) + Integer.parseInt(b, 16) > 408) {
|
||||
"000000"
|
||||
} else {
|
||||
"ffffff"
|
||||
|
||||
@@ -16,15 +16,15 @@ trait Profile {
|
||||
)
|
||||
|
||||
/**
|
||||
* WebHookBase.Event Column Types
|
||||
*/
|
||||
* WebHookBase.Event Column Types
|
||||
*/
|
||||
implicit val eventColumnType = MappedColumnType.base[WebHook.Event, String](_.name, WebHook.Event.valueOf(_))
|
||||
|
||||
/**
|
||||
* Extends Column to add conditional condition
|
||||
*/
|
||||
implicit class RichColumn(c1: Rep[Boolean]){
|
||||
def &&(c2: => Rep[Boolean], guard: => Boolean): Rep[Boolean] = if(guard) c1 && c2 else c1
|
||||
implicit class RichColumn(c1: Rep[Boolean]) {
|
||||
def &&(c2: => Rep[Boolean], guard: => Boolean): Rep[Boolean] = if (guard) c1 && c2 else c1
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,31 +40,33 @@ trait ProfileProvider { self: Profile =>
|
||||
|
||||
}
|
||||
|
||||
trait CoreProfile extends ProfileProvider with Profile
|
||||
with AccessTokenComponent
|
||||
with AccountComponent
|
||||
with ActivityComponent
|
||||
with CollaboratorComponent
|
||||
with CommitCommentComponent
|
||||
with CommitStatusComponent
|
||||
with GroupMemberComponent
|
||||
with IssueComponent
|
||||
with IssueCommentComponent
|
||||
with IssueLabelComponent
|
||||
with LabelComponent
|
||||
with PriorityComponent
|
||||
with MilestoneComponent
|
||||
with PullRequestComponent
|
||||
with RepositoryComponent
|
||||
with SshKeyComponent
|
||||
with RepositoryWebHookComponent
|
||||
with RepositoryWebHookEventComponent
|
||||
with AccountWebHookComponent
|
||||
with AccountWebHookEventComponent
|
||||
with AccountFederationComponent
|
||||
with ProtectedBranchComponent
|
||||
with DeployKeyComponent
|
||||
with ReleaseTagComponent
|
||||
with ReleaseAssetComponent
|
||||
trait CoreProfile
|
||||
extends ProfileProvider
|
||||
with Profile
|
||||
with AccessTokenComponent
|
||||
with AccountComponent
|
||||
with ActivityComponent
|
||||
with CollaboratorComponent
|
||||
with CommitCommentComponent
|
||||
with CommitStatusComponent
|
||||
with GroupMemberComponent
|
||||
with IssueComponent
|
||||
with IssueCommentComponent
|
||||
with IssueLabelComponent
|
||||
with LabelComponent
|
||||
with PriorityComponent
|
||||
with MilestoneComponent
|
||||
with PullRequestComponent
|
||||
with RepositoryComponent
|
||||
with SshKeyComponent
|
||||
with RepositoryWebHookComponent
|
||||
with RepositoryWebHookEventComponent
|
||||
with AccountWebHookComponent
|
||||
with AccountWebHookEventComponent
|
||||
with AccountFederationComponent
|
||||
with ProtectedBranchComponent
|
||||
with DeployKeyComponent
|
||||
with ReleaseTagComponent
|
||||
with ReleaseAssetComponent
|
||||
|
||||
object Profile extends CoreProfile
|
||||
|
||||
@@ -8,27 +8,22 @@ trait ProtectedBranchComponent extends TemplateComponent { self: Profile =>
|
||||
class ProtectedBranches(tag: Tag) extends Table[ProtectedBranch](tag, "PROTECTED_BRANCH") with BranchTemplate {
|
||||
val statusCheckAdmin = column[Boolean]("STATUS_CHECK_ADMIN")
|
||||
def * = (userName, repositoryName, branch, statusCheckAdmin) <> (ProtectedBranch.tupled, ProtectedBranch.unapply)
|
||||
def byPrimaryKey(userName: String, repositoryName: String, branch: String) = byBranch(userName, repositoryName, branch)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], branch: Rep[String]) = byBranch(userName, repositoryName, branch)
|
||||
def byPrimaryKey(userName: String, repositoryName: String, branch: String) =
|
||||
byBranch(userName, repositoryName, branch)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], branch: Rep[String]) =
|
||||
byBranch(userName, repositoryName, branch)
|
||||
}
|
||||
|
||||
lazy val ProtectedBranchContexts = TableQuery[ProtectedBranchContexts]
|
||||
class ProtectedBranchContexts(tag: Tag) extends Table[ProtectedBranchContext](tag, "PROTECTED_BRANCH_REQUIRE_CONTEXT") with BranchTemplate {
|
||||
class ProtectedBranchContexts(tag: Tag)
|
||||
extends Table[ProtectedBranchContext](tag, "PROTECTED_BRANCH_REQUIRE_CONTEXT")
|
||||
with BranchTemplate {
|
||||
val context = column[String]("CONTEXT")
|
||||
def * = (userName, repositoryName, branch, context) <> (ProtectedBranchContext.tupled, ProtectedBranchContext.unapply)
|
||||
def * =
|
||||
(userName, repositoryName, branch, context) <> (ProtectedBranchContext.tupled, ProtectedBranchContext.unapply)
|
||||
}
|
||||
}
|
||||
|
||||
case class ProtectedBranch(userName: String, repositoryName: String, branch: String, statusCheckAdmin: Boolean)
|
||||
|
||||
case class ProtectedBranch(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
branch: String,
|
||||
statusCheckAdmin: Boolean)
|
||||
|
||||
|
||||
case class ProtectedBranchContext(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
branch: String,
|
||||
context: String)
|
||||
case class ProtectedBranchContext(userName: String, repositoryName: String, branch: String, context: String)
|
||||
|
||||
@@ -12,10 +12,23 @@ trait PullRequestComponent extends TemplateComponent { self: Profile =>
|
||||
val requestBranch = column[String]("REQUEST_BRANCH")
|
||||
val commitIdFrom = column[String]("COMMIT_ID_FROM")
|
||||
val commitIdTo = column[String]("COMMIT_ID_TO")
|
||||
def * = (userName, repositoryName, issueId, branch, requestUserName, requestRepositoryName, requestBranch, commitIdFrom, commitIdTo) <> (PullRequest.tupled, PullRequest.unapply)
|
||||
def * =
|
||||
(
|
||||
userName,
|
||||
repositoryName,
|
||||
issueId,
|
||||
branch,
|
||||
requestUserName,
|
||||
requestRepositoryName,
|
||||
requestBranch,
|
||||
commitIdFrom,
|
||||
commitIdTo
|
||||
) <> (PullRequest.tupled, PullRequest.unapply)
|
||||
|
||||
def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) = byIssue(userName, repositoryName, issueId)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], issueId: Rep[Int]) = byIssue(userName, repositoryName, issueId)
|
||||
def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) =
|
||||
byIssue(userName, repositoryName, issueId)
|
||||
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], issueId: Rep[Int]) =
|
||||
byIssue(userName, repositoryName, issueId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,12 @@ trait ReleaseAssetComponent extends TemplateComponent {
|
||||
val registeredDate = column[Date]("REGISTERED_DATE")
|
||||
val updatedDate = column[Date]("UPDATED_DATE")
|
||||
|
||||
def * = (userName, repositoryName, tag, releaseAssetId, fileName, label, size, uploader, registeredDate, updatedDate) <> (ReleaseAsset.tupled, ReleaseAsset.unapply)
|
||||
def byPrimaryKey(owner: String, repository: String, tag: String, fileName: String) = byTag(owner, repository, tag) && (this.fileName === fileName.bind)
|
||||
def byTag(owner: String, repository: String, tag: String) = byRepository(owner, repository) && (this.tag === tag.bind)
|
||||
def * =
|
||||
(userName, repositoryName, tag, releaseAssetId, fileName, label, size, uploader, registeredDate, updatedDate) <> (ReleaseAsset.tupled, ReleaseAsset.unapply)
|
||||
def byPrimaryKey(owner: String, repository: String, tag: String, fileName: String) =
|
||||
byTag(owner, repository, tag) && (this.fileName === fileName.bind)
|
||||
def byTag(owner: String, repository: String, tag: String) =
|
||||
byRepository(owner, repository) && (this.tag === tag.bind)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,11 @@ trait ReleaseTagComponent extends TemplateComponent {
|
||||
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||
|
||||
def * = (userName, repositoryName, name, tag, author, content, registeredDate, updatedDate) <> (ReleaseTag.tupled, ReleaseTag.unapply)
|
||||
def * =
|
||||
(userName, repositoryName, name, tag, author, content, registeredDate, updatedDate) <> (ReleaseTag.tupled, ReleaseTag.unapply)
|
||||
def byPrimaryKey(owner: String, repository: String, tag: String) = byTag(owner, repository, tag)
|
||||
def byTag(owner: String, repository: String, tag: String) = byRepository(owner, repository) && (this.tag === tag.bind)
|
||||
def byTag(owner: String, repository: String, tag: String) =
|
||||
byRepository(owner, repository) && (this.tag === tag.bind)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,62 +7,80 @@ trait RepositoryComponent extends TemplateComponent { self: Profile =>
|
||||
lazy val Repositories = TableQuery[Repositories]
|
||||
|
||||
class Repositories(tag: Tag) extends Table[Repository](tag, "REPOSITORY") with BasicTemplate {
|
||||
val isPrivate = column[Boolean]("PRIVATE")
|
||||
val description = column[String]("DESCRIPTION")
|
||||
val defaultBranch = column[String]("DEFAULT_BRANCH")
|
||||
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||
val lastActivityDate = column[java.util.Date]("LAST_ACTIVITY_DATE")
|
||||
val originUserName = column[String]("ORIGIN_USER_NAME")
|
||||
val isPrivate = column[Boolean]("PRIVATE")
|
||||
val description = column[String]("DESCRIPTION")
|
||||
val defaultBranch = column[String]("DEFAULT_BRANCH")
|
||||
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||
val lastActivityDate = column[java.util.Date]("LAST_ACTIVITY_DATE")
|
||||
val originUserName = column[String]("ORIGIN_USER_NAME")
|
||||
val originRepositoryName = column[String]("ORIGIN_REPOSITORY_NAME")
|
||||
val parentUserName = column[String]("PARENT_USER_NAME")
|
||||
val parentUserName = column[String]("PARENT_USER_NAME")
|
||||
val parentRepositoryName = column[String]("PARENT_REPOSITORY_NAME")
|
||||
val issuesOption = column[String]("ISSUES_OPTION")
|
||||
val externalIssuesUrl = column[String]("EXTERNAL_ISSUES_URL")
|
||||
val wikiOption = column[String]("WIKI_OPTION")
|
||||
val externalWikiUrl = column[String]("EXTERNAL_WIKI_URL")
|
||||
val allowFork = column[Boolean]("ALLOW_FORK")
|
||||
val mergeOptions = column[String]("MERGE_OPTIONS")
|
||||
val defaultMergeOption = column[String]("DEFAULT_MERGE_OPTION")
|
||||
val issuesOption = column[String]("ISSUES_OPTION")
|
||||
val externalIssuesUrl = column[String]("EXTERNAL_ISSUES_URL")
|
||||
val wikiOption = column[String]("WIKI_OPTION")
|
||||
val externalWikiUrl = column[String]("EXTERNAL_WIKI_URL")
|
||||
val allowFork = column[Boolean]("ALLOW_FORK")
|
||||
val mergeOptions = column[String]("MERGE_OPTIONS")
|
||||
val defaultMergeOption = column[String]("DEFAULT_MERGE_OPTION")
|
||||
|
||||
def * = (
|
||||
(userName, repositoryName, isPrivate, description.?, defaultBranch,
|
||||
registeredDate, updatedDate, lastActivityDate, originUserName.?, originRepositoryName.?, parentUserName.?, parentRepositoryName.?),
|
||||
(issuesOption, externalIssuesUrl.?, wikiOption, externalWikiUrl.?, allowFork, mergeOptions, defaultMergeOption)
|
||||
).shaped <> (
|
||||
{ case (repository, options) =>
|
||||
Repository(
|
||||
repository._1,
|
||||
repository._2,
|
||||
repository._3,
|
||||
repository._4,
|
||||
repository._5,
|
||||
repository._6,
|
||||
repository._7,
|
||||
repository._8,
|
||||
repository._9,
|
||||
repository._10,
|
||||
repository._11,
|
||||
repository._12,
|
||||
RepositoryOptions.tupled.apply(options)
|
||||
)
|
||||
def * =
|
||||
(
|
||||
(
|
||||
userName,
|
||||
repositoryName,
|
||||
isPrivate,
|
||||
description.?,
|
||||
defaultBranch,
|
||||
registeredDate,
|
||||
updatedDate,
|
||||
lastActivityDate,
|
||||
originUserName.?,
|
||||
originRepositoryName.?,
|
||||
parentUserName.?,
|
||||
parentRepositoryName.?
|
||||
),
|
||||
(issuesOption, externalIssuesUrl.?, wikiOption, externalWikiUrl.?, allowFork, mergeOptions, defaultMergeOption)
|
||||
).shaped <> ({
|
||||
case (repository, options) =>
|
||||
Repository(
|
||||
repository._1,
|
||||
repository._2,
|
||||
repository._3,
|
||||
repository._4,
|
||||
repository._5,
|
||||
repository._6,
|
||||
repository._7,
|
||||
repository._8,
|
||||
repository._9,
|
||||
repository._10,
|
||||
repository._11,
|
||||
repository._12,
|
||||
RepositoryOptions.tupled.apply(options)
|
||||
)
|
||||
}, { (r: Repository) =>
|
||||
Some(((
|
||||
r.userName,
|
||||
r.repositoryName,
|
||||
r.isPrivate,
|
||||
r.description,
|
||||
r.defaultBranch,
|
||||
r.registeredDate,
|
||||
r.updatedDate,
|
||||
r.lastActivityDate,
|
||||
r.originUserName,
|
||||
r.originRepositoryName,
|
||||
r.parentUserName,
|
||||
r.parentRepositoryName
|
||||
),(
|
||||
RepositoryOptions.unapply(r.options).get
|
||||
)))
|
||||
Some(
|
||||
(
|
||||
(
|
||||
r.userName,
|
||||
r.repositoryName,
|
||||
r.isPrivate,
|
||||
r.description,
|
||||
r.defaultBranch,
|
||||
r.registeredDate,
|
||||
r.updatedDate,
|
||||
r.lastActivityDate,
|
||||
r.originUserName,
|
||||
r.originRepositoryName,
|
||||
r.parentUserName,
|
||||
r.parentRepositoryName
|
||||
),
|
||||
(
|
||||
RepositoryOptions.unapply(r.options).get
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
|
||||
def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
|
||||
|
||||
@@ -3,7 +3,8 @@ package gitbucket.core.model
|
||||
trait RepositoryWebHookComponent extends TemplateComponent { self: Profile =>
|
||||
import profile.api._
|
||||
|
||||
implicit val whContentTypeColumnType = MappedColumnType.base[WebHookContentType, String](whct => whct.code , code => WebHookContentType.valueOf(code))
|
||||
implicit val whContentTypeColumnType =
|
||||
MappedColumnType.base[WebHookContentType, String](whct => whct.code, code => WebHookContentType.valueOf(code))
|
||||
|
||||
lazy val RepositoryWebHooks = TableQuery[RepositoryWebHooks]
|
||||
|
||||
@@ -11,13 +12,14 @@ trait RepositoryWebHookComponent extends TemplateComponent { self: Profile =>
|
||||
val url = column[String]("URL")
|
||||
val token = column[Option[String]]("TOKEN")
|
||||
val ctype = column[WebHookContentType]("CTYPE")
|
||||
def * = (userName, repositoryName, url, ctype, token) <> ((RepositoryWebHook.apply _).tupled, RepositoryWebHook.unapply)
|
||||
def * =
|
||||
(userName, repositoryName, url, ctype, token) <> ((RepositoryWebHook.apply _).tupled, RepositoryWebHook.unapply)
|
||||
|
||||
def byPrimaryKey(owner: String, repository: String, url: String) = byRepository(owner, repository) && (this.url === url.bind)
|
||||
def byPrimaryKey(owner: String, repository: String, url: String) =
|
||||
byRepository(owner, repository) && (this.url === url.bind)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case class RepositoryWebHook(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
|
||||
@@ -6,17 +6,22 @@ trait RepositoryWebHookEventComponent extends TemplateComponent { self: Profile
|
||||
|
||||
lazy val RepositoryWebHookEvents = TableQuery[RepositoryWebHookEvents]
|
||||
|
||||
class RepositoryWebHookEvents(tag: Tag) extends Table[RepositoryWebHookEvent](tag, "WEB_HOOK_EVENT") with BasicTemplate {
|
||||
class RepositoryWebHookEvents(tag: Tag)
|
||||
extends Table[RepositoryWebHookEvent](tag, "WEB_HOOK_EVENT")
|
||||
with BasicTemplate {
|
||||
val url = column[String]("URL")
|
||||
val event = column[WebHook.Event]("EVENT")
|
||||
def * = (userName, repositoryName, url, event) <> ((RepositoryWebHookEvent.apply _).tupled, RepositoryWebHookEvent.unapply)
|
||||
def * =
|
||||
(userName, repositoryName, url, event) <> ((RepositoryWebHookEvent.apply _).tupled, RepositoryWebHookEvent.unapply)
|
||||
|
||||
def byRepositoryWebHook(owner: String, repository: String, url: String) = byRepository(owner, repository) && (this.url === url.bind)
|
||||
def byRepositoryWebHook(owner: String, repository: String, url: String) =
|
||||
byRepository(owner, repository) && (this.url === url.bind)
|
||||
def byRepositoryWebHook(owner: Rep[String], repository: Rep[String], url: Rep[String]) =
|
||||
byRepository(userName, repositoryName) && (this.url === url)
|
||||
def byRepositoryWebHook(webhook: RepositoryWebHooks) =
|
||||
byRepository(webhook.userName, webhook.repositoryName) && (this.url === webhook.url)
|
||||
def byPrimaryKey(owner: String, repository: String, url: String, event: WebHook.Event) = byRepositoryWebHook(owner, repository, url) && (this.event === event.bind)
|
||||
def byPrimaryKey(owner: String, repository: String, url: String, event: WebHook.Event) =
|
||||
byRepositoryWebHook(owner, repository, url) && (this.event === event.bind)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ trait SshKeyComponent { self: Profile =>
|
||||
val publicKey = column[String]("PUBLIC_KEY")
|
||||
def * = (userName, sshKeyId, title, publicKey) <> (SshKey.tupled, SshKey.unapply)
|
||||
|
||||
def byPrimaryKey(userName: String, sshKeyId: Int) = (this.userName === userName.bind) && (this.sshKeyId === sshKeyId.bind)
|
||||
def byPrimaryKey(userName: String, sshKeyId: Int) =
|
||||
(this.userName === userName.bind) && (this.sshKeyId === sshKeyId.bind)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ object WebHookContentType {
|
||||
def valueOpt(code: String): Option[WebHookContentType] = map.get(code)
|
||||
}
|
||||
|
||||
trait WebHook{
|
||||
trait WebHook {
|
||||
val url: String
|
||||
val ctype: WebHookContentType
|
||||
val token: Option[String]
|
||||
@@ -45,7 +45,7 @@ object WebHook {
|
||||
case object TeamAdd extends Event("team_add")
|
||||
case object Watch extends Event("watch")
|
||||
|
||||
object Event{
|
||||
object Event {
|
||||
val values = List(
|
||||
CommitComment,
|
||||
Create,
|
||||
@@ -68,7 +68,7 @@ object WebHook {
|
||||
Watch
|
||||
)
|
||||
|
||||
private val map: Map[String,Event] = values.map(e => e.name -> e).toMap
|
||||
private val map: Map[String, Event] = values.map(e => e.name -> e).toMap
|
||||
def valueOf(name: String): Event = map(name)
|
||||
def valueOpt(name: String): Option[Event] = map.get(name)
|
||||
}
|
||||
|
||||
@@ -10,13 +10,18 @@ import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
* @param localPath the string to assemble local file path of repository (e.g. "gist/$1/$2")
|
||||
* @param filter the filter for request to the Git repository which is defined by this routing
|
||||
*/
|
||||
case class GitRepositoryRouting(urlPattern: String, localPath: String, filter: GitRepositoryFilter){
|
||||
case class GitRepositoryRouting(urlPattern: String, localPath: String, filter: GitRepositoryFilter) {
|
||||
|
||||
def this(urlPattern: String, localPath: String) = {
|
||||
this(urlPattern, localPath, new GitRepositoryFilter(){
|
||||
def filter(repositoryName: String, userName: Option[String], settings: SystemSettings, isUpdating: Boolean)
|
||||
(implicit session: Session): Boolean = true
|
||||
})
|
||||
this(
|
||||
urlPattern,
|
||||
localPath,
|
||||
new GitRepositoryFilter() {
|
||||
def filter(repositoryName: String, userName: Option[String], settings: SystemSettings, isUpdating: Boolean)(
|
||||
implicit session: Session
|
||||
): Boolean = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,7 +41,8 @@ trait GitRepositoryFilter {
|
||||
* @param session the database session
|
||||
* @return true if allow accessing to repository, otherwise false.
|
||||
*/
|
||||
def filter(path: String, userName: Option[String], settings: SystemSettings, isUpdating: Boolean)
|
||||
(implicit session: Session): Boolean
|
||||
def filter(path: String, userName: Option[String], settings: SystemSettings, isUpdating: Boolean)(
|
||||
implicit session: Session
|
||||
): Boolean
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ import profile.api._
|
||||
trait IssueHook {
|
||||
|
||||
def created(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
|
||||
def addedComment(commentId: Int, content: String, issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
|
||||
def addedComment(commentId: Int, content: String, issue: Issue, repository: RepositoryInfo)(
|
||||
implicit session: Session,
|
||||
context: Context
|
||||
): Unit = ()
|
||||
def closed(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
|
||||
def reopened(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
|
||||
|
||||
|
||||
@@ -30,7 +30,8 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to declare this plug-in provides images.
|
||||
*/
|
||||
def images(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, Array[Byte])] = Nil
|
||||
def images(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, Array[Byte])] =
|
||||
Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides controllers.
|
||||
@@ -40,7 +41,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to declare this plug-in provides controllers.
|
||||
*/
|
||||
def controllers(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, ControllerBase)] = Nil
|
||||
def controllers(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(String, ControllerBase)] = Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides JavaScript.
|
||||
@@ -50,7 +55,8 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to declare this plug-in provides JavaScript.
|
||||
*/
|
||||
def javaScripts(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, String)] = Nil
|
||||
def javaScripts(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, String)] =
|
||||
Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides renderers.
|
||||
@@ -60,7 +66,8 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to declare this plug-in provides renderers.
|
||||
*/
|
||||
def renderers(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, Renderer)] = Nil
|
||||
def renderers(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, Renderer)] =
|
||||
Nil
|
||||
|
||||
/**
|
||||
* Override to add git repository routings.
|
||||
@@ -70,7 +77,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add git repository routings.
|
||||
*/
|
||||
def repositoryRoutings(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[GitRepositoryRouting] = Nil
|
||||
def repositoryRoutings(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[GitRepositoryRouting] = Nil
|
||||
|
||||
/**
|
||||
* Override to add account hooks.
|
||||
@@ -100,7 +111,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add repository hooks.
|
||||
*/
|
||||
def repositoryHooks(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[RepositoryHook] = Nil
|
||||
def repositoryHooks(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[RepositoryHook] = Nil
|
||||
|
||||
/**
|
||||
* Override to add issue hooks.
|
||||
@@ -120,7 +135,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add pull request hooks.
|
||||
*/
|
||||
def pullRequestHooks(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[PullRequestHook] = Nil
|
||||
def pullRequestHooks(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[PullRequestHook] = Nil
|
||||
|
||||
/**
|
||||
* Override to add repository headers.
|
||||
@@ -130,7 +149,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add repository headers.
|
||||
*/
|
||||
def repositoryHeaders(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(RepositoryInfo, Context) => Option[Html]] = Nil
|
||||
def repositoryHeaders(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(RepositoryInfo, Context) => Option[Html]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add global menus.
|
||||
@@ -140,7 +163,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add global menus.
|
||||
*/
|
||||
def globalMenus(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Context) => Option[Link]] = Nil
|
||||
def globalMenus(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add repository menus.
|
||||
@@ -150,7 +177,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add repository menus.
|
||||
*/
|
||||
def repositoryMenus(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(RepositoryInfo, Context) => Option[Link]] = Nil
|
||||
def repositoryMenus(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(RepositoryInfo, Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add repository setting tabs.
|
||||
@@ -160,7 +191,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add repository setting tabs.
|
||||
*/
|
||||
def repositorySettingTabs(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(RepositoryInfo, Context) => Option[Link]] = Nil
|
||||
def repositorySettingTabs(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(RepositoryInfo, Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add profile tabs.
|
||||
@@ -170,7 +205,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add profile tabs.
|
||||
*/
|
||||
def profileTabs(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Account, Context) => Option[Link]] = Nil
|
||||
def profileTabs(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(Account, Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add system setting menus.
|
||||
@@ -180,7 +219,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add system setting menus.
|
||||
*/
|
||||
def systemSettingMenus(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Context) => Option[Link]] = Nil
|
||||
def systemSettingMenus(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add account setting menus.
|
||||
@@ -190,7 +233,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add account setting menus.
|
||||
*/
|
||||
def accountSettingMenus(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Context) => Option[Link]] = Nil
|
||||
def accountSettingMenus(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add dashboard tabs.
|
||||
@@ -200,7 +247,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add dashboard tabs.
|
||||
*/
|
||||
def dashboardTabs(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Context) => Option[Link]] = Nil
|
||||
def dashboardTabs(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add issue sidebars.
|
||||
@@ -210,7 +261,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add issue sidebars.
|
||||
*/
|
||||
def issueSidebars(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Issue, RepositoryInfo, Context) => Option[Html]] = Nil
|
||||
def issueSidebars(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(Issue, RepositoryInfo, Context) => Option[Html]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add assets mappings.
|
||||
@@ -220,7 +275,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add assets mappings.
|
||||
*/
|
||||
def assetsMappings(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, String)] = Nil
|
||||
def assetsMappings(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[(String, String)] = Nil
|
||||
|
||||
/**
|
||||
* Override to add text decorators.
|
||||
@@ -230,7 +289,8 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add text decorators.
|
||||
*/
|
||||
def textDecorators(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[TextDecorator] = Nil
|
||||
def textDecorators(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[TextDecorator] =
|
||||
Nil
|
||||
|
||||
/**
|
||||
* Override to add suggestion provider.
|
||||
@@ -240,7 +300,11 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add suggestion provider.
|
||||
*/
|
||||
def suggestionProviders(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[SuggestionProvider] = Nil
|
||||
def suggestionProviders(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[SuggestionProvider] = Nil
|
||||
|
||||
/**
|
||||
* Override to add ssh command providers.
|
||||
@@ -250,25 +314,32 @@ abstract class Plugin {
|
||||
/**
|
||||
* Override to add ssh command providers.
|
||||
*/
|
||||
def sshCommandProviders(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[PartialFunction[String, Command]] = Nil
|
||||
|
||||
def sshCommandProviders(
|
||||
registry: PluginRegistry,
|
||||
context: ServletContext,
|
||||
settings: SystemSettings
|
||||
): Seq[PartialFunction[String, Command]] = Nil
|
||||
|
||||
/**
|
||||
* This method is invoked in initialization of plugin system.
|
||||
* Register plugin functionality to PluginRegistry.
|
||||
*/
|
||||
def initialize(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Unit = {
|
||||
(images ++ images(registry, context, settings)).foreach { case (id, in) =>
|
||||
registry.addImage(id, in)
|
||||
(images ++ images(registry, context, settings)).foreach {
|
||||
case (id, in) =>
|
||||
registry.addImage(id, in)
|
||||
}
|
||||
(controllers ++ controllers(registry, context, settings)).foreach { case (path, controller) =>
|
||||
registry.addController(path, controller)
|
||||
(controllers ++ controllers(registry, context, settings)).foreach {
|
||||
case (path, controller) =>
|
||||
registry.addController(path, controller)
|
||||
}
|
||||
(javaScripts ++ javaScripts(registry, context, settings)).foreach { case (path, script) =>
|
||||
registry.addJavaScript(path, script)
|
||||
(javaScripts ++ javaScripts(registry, context, settings)).foreach {
|
||||
case (path, script) =>
|
||||
registry.addJavaScript(path, script)
|
||||
}
|
||||
(renderers ++ renderers(registry, context, settings)).foreach { case (extension, renderer) =>
|
||||
registry.addRenderer(extension, renderer)
|
||||
(renderers ++ renderers(registry, context, settings)).foreach {
|
||||
case (extension, renderer) =>
|
||||
registry.addRenderer(extension, renderer)
|
||||
}
|
||||
(repositoryRoutings ++ repositoryRoutings(registry, context, settings)).foreach { routing =>
|
||||
registry.addRepositoryRouting(routing)
|
||||
@@ -345,7 +416,7 @@ abstract class Plugin {
|
||||
* Helper method to get a resource from classpath.
|
||||
*/
|
||||
protected def fromClassPath(path: String): Array[Byte] =
|
||||
using(getClass.getClassLoader.getResourceAsStream(path)){ in =>
|
||||
using(getClass.getClassLoader.getResourceAsStream(path)) { in =>
|
||||
val bytes = new Array[Byte](in.available)
|
||||
in.read(bytes)
|
||||
bytes
|
||||
|
||||
@@ -70,7 +70,7 @@ class PluginRegistry {
|
||||
|
||||
@deprecated("Use addImage(id: String, bytes: Array[Byte]) instead", "3.4.0")
|
||||
def addImage(id: String, in: InputStream): Unit = {
|
||||
val bytes = using(in){ in =>
|
||||
val bytes = using(in) { in =>
|
||||
val bytes = new Array[Byte](in.available)
|
||||
in.read(bytes)
|
||||
bytes
|
||||
@@ -87,9 +87,11 @@ class PluginRegistry {
|
||||
|
||||
def getControllers(): Seq[(ControllerBase, String)] = controllers.asScala.toSeq
|
||||
|
||||
def addJavaScript(path: String, script: String): Unit = javaScripts.add((path, script)) //javaScripts += ((path, script))
|
||||
def addJavaScript(path: String, script: String): Unit =
|
||||
javaScripts.add((path, script)) //javaScripts += ((path, script))
|
||||
|
||||
def getJavaScript(currentPath: String): List[String] = javaScripts.asScala.filter(x => currentPath.matches(x._1)).toList.map(_._2)
|
||||
def getJavaScript(currentPath: String): List[String] =
|
||||
javaScripts.asScala.filter(x => currentPath.matches(x._1)).toList.map(_._2)
|
||||
|
||||
def addRenderer(extension: String, renderer: Renderer): Unit = renderers.put(extension, renderer)
|
||||
|
||||
@@ -129,7 +131,8 @@ class PluginRegistry {
|
||||
|
||||
def getPullRequestHooks: Seq[PullRequestHook] = pullRequestHooks.asScala.toSeq
|
||||
|
||||
def addRepositoryHeader(repositoryHeader: (RepositoryInfo, Context) => Option[Html]): Unit = repositoryHeaders.add(repositoryHeader)
|
||||
def addRepositoryHeader(repositoryHeader: (RepositoryInfo, Context) => Option[Html]): Unit =
|
||||
repositoryHeaders.add(repositoryHeader)
|
||||
|
||||
def getRepositoryHeaders: Seq[(RepositoryInfo, Context) => Option[Html]] = repositoryHeaders.asScala.toSeq
|
||||
|
||||
@@ -137,11 +140,13 @@ class PluginRegistry {
|
||||
|
||||
def getGlobalMenus: Seq[(Context) => Option[Link]] = globalMenus.asScala.toSeq
|
||||
|
||||
def addRepositoryMenu(repositoryMenu: (RepositoryInfo, Context) => Option[Link]): Unit = repositoryMenus.add(repositoryMenu)
|
||||
def addRepositoryMenu(repositoryMenu: (RepositoryInfo, Context) => Option[Link]): Unit =
|
||||
repositoryMenus.add(repositoryMenu)
|
||||
|
||||
def getRepositoryMenus: Seq[(RepositoryInfo, Context) => Option[Link]] = repositoryMenus.asScala.toSeq
|
||||
|
||||
def addRepositorySettingTab(repositorySettingTab: (RepositoryInfo, Context) => Option[Link]): Unit = repositorySettingTabs.add(repositorySettingTab)
|
||||
def addRepositorySettingTab(repositorySettingTab: (RepositoryInfo, Context) => Option[Link]): Unit =
|
||||
repositorySettingTabs.add(repositorySettingTab)
|
||||
|
||||
def getRepositorySettingTabs: Seq[(RepositoryInfo, Context) => Option[Link]] = repositorySettingTabs.asScala.toSeq
|
||||
|
||||
@@ -149,11 +154,13 @@ class PluginRegistry {
|
||||
|
||||
def getProfileTabs: Seq[(Account, Context) => Option[Link]] = profileTabs.asScala.toSeq
|
||||
|
||||
def addSystemSettingMenu(systemSettingMenu: (Context) => Option[Link]): Unit = systemSettingMenus.add(systemSettingMenu)
|
||||
def addSystemSettingMenu(systemSettingMenu: (Context) => Option[Link]): Unit =
|
||||
systemSettingMenus.add(systemSettingMenu)
|
||||
|
||||
def getSystemSettingMenus: Seq[(Context) => Option[Link]] = systemSettingMenus.asScala.toSeq
|
||||
|
||||
def addAccountSettingMenu(accountSettingMenu: (Context) => Option[Link]): Unit = accountSettingMenus.add(accountSettingMenu)
|
||||
def addAccountSettingMenu(accountSettingMenu: (Context) => Option[Link]): Unit =
|
||||
accountSettingMenus.add(accountSettingMenu)
|
||||
|
||||
def getAccountSettingMenus: Seq[(Context) => Option[Link]] = accountSettingMenus.asScala.toSeq
|
||||
|
||||
@@ -161,7 +168,8 @@ class PluginRegistry {
|
||||
|
||||
def getDashboardTabs: Seq[(Context) => Option[Link]] = dashboardTabs.asScala.toSeq
|
||||
|
||||
def addIssueSidebar(issueSidebar: (Issue, RepositoryInfo, Context) => Option[Html]): Unit = issueSidebars.add(issueSidebar)
|
||||
def addIssueSidebar(issueSidebar: (Issue, RepositoryInfo, Context) => Option[Html]): Unit =
|
||||
issueSidebars.add(issueSidebar)
|
||||
|
||||
def getIssueSidebars: Seq[(Issue, RepositoryInfo, Context) => Option[Html]] = issueSidebars.asScala.toSeq
|
||||
|
||||
@@ -177,7 +185,8 @@ class PluginRegistry {
|
||||
|
||||
def getSuggestionProviders: Seq[SuggestionProvider] = suggestionProviders.asScala.toSeq
|
||||
|
||||
def addSshCommandProvider(sshCommandProvider: PartialFunction[String, Command]): Unit = sshCommandProviders.add(sshCommandProvider)
|
||||
def addSshCommandProvider(sshCommandProvider: PartialFunction[String, Command]): Unit =
|
||||
sshCommandProviders.add(sshCommandProvider)
|
||||
|
||||
def getSshCommandProviders: Seq[PartialFunction[String, Command]] = sshCommandProviders.asScala.toSeq
|
||||
}
|
||||
@@ -212,37 +221,44 @@ object PluginRegistry {
|
||||
/**
|
||||
* Uninstall a specified plugin.
|
||||
*/
|
||||
def uninstall(pluginId: String, context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit = synchronized {
|
||||
instance.getPlugins()
|
||||
.collect { case plugin if plugin.pluginId == pluginId => plugin }
|
||||
.foreach { plugin =>
|
||||
def uninstall(pluginId: String, context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit =
|
||||
synchronized {
|
||||
instance
|
||||
.getPlugins()
|
||||
.collect { case plugin if plugin.pluginId == pluginId => plugin }
|
||||
.foreach { plugin =>
|
||||
// try {
|
||||
// plugin.pluginClass.uninstall(instance, context, settings)
|
||||
// } catch {
|
||||
// case e: Exception =>
|
||||
// logger.error(s"Error during uninstalling plugin: ${plugin.pluginJar.getName}", e)
|
||||
// }
|
||||
shutdown(context, settings)
|
||||
plugin.pluginJar.delete()
|
||||
instance = new PluginRegistry()
|
||||
initialize(context, settings, conn)
|
||||
}
|
||||
}
|
||||
shutdown(context, settings)
|
||||
plugin.pluginJar.delete()
|
||||
instance = new PluginRegistry()
|
||||
initialize(context, settings, conn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a plugin from a specified jar file.
|
||||
*/
|
||||
def install(file: File, context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit = synchronized {
|
||||
shutdown(context, settings)
|
||||
FileUtils.copyFile(file, new File(PluginHome, file.getName))
|
||||
instance = new PluginRegistry()
|
||||
initialize(context, settings, conn)
|
||||
}
|
||||
def install(file: File, context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit =
|
||||
synchronized {
|
||||
shutdown(context, settings)
|
||||
FileUtils.copyFile(file, new File(PluginHome, file.getName))
|
||||
instance = new PluginRegistry()
|
||||
initialize(context, settings, conn)
|
||||
}
|
||||
|
||||
private def listPluginJars(dir: File): Seq[File] = {
|
||||
dir.listFiles(new FilenameFilter {
|
||||
override def accept(dir: File, name: String): Boolean = name.endsWith(".jar")
|
||||
}).toSeq.sortBy(_.getName).reverse
|
||||
dir
|
||||
.listFiles(new FilenameFilter {
|
||||
override def accept(dir: File, name: String): Boolean = name.endsWith(".jar")
|
||||
})
|
||||
.toSeq
|
||||
.sortBy(_.getName)
|
||||
.reverse
|
||||
}
|
||||
|
||||
lazy val extraPluginDir: Option[String] = Option(System.getProperty("gitbucket.pluginDir"))
|
||||
@@ -256,13 +272,17 @@ object PluginRegistry {
|
||||
|
||||
// Clean installed directory
|
||||
val installedDir = new File(PluginHome, ".installed")
|
||||
if(installedDir.exists){
|
||||
if (installedDir.exists) {
|
||||
FileUtils.deleteDirectory(installedDir)
|
||||
}
|
||||
installedDir.mkdirs()
|
||||
|
||||
val pluginJars = listPluginJars(pluginDir)
|
||||
val extraJars = extraPluginDir.map { extraDir => listPluginJars(new File(extraDir)) }.getOrElse(Nil)
|
||||
val extraJars = extraPluginDir
|
||||
.map { extraDir =>
|
||||
listPluginJars(new File(extraDir))
|
||||
}
|
||||
.getOrElse(Nil)
|
||||
|
||||
(extraJars ++ pluginJars).foreach { pluginJar =>
|
||||
val installedJar = new File(installedDir, pluginJar.getName)
|
||||
@@ -283,27 +303,32 @@ object PluginRegistry {
|
||||
case None => {
|
||||
// Migration
|
||||
val solidbase = new Solidbase()
|
||||
solidbase.migrate(conn, classLoader, DatabaseConfig.liquiDriver, new Module(plugin.pluginId, plugin.versions: _*))
|
||||
solidbase
|
||||
.migrate(conn, classLoader, DatabaseConfig.liquiDriver, new Module(plugin.pluginId, plugin.versions: _*))
|
||||
conn.commit()
|
||||
|
||||
// Check database version
|
||||
val databaseVersion = manager.getCurrentVersion(plugin.pluginId)
|
||||
val pluginVersion = plugin.versions.last.getVersion
|
||||
if (databaseVersion != pluginVersion) {
|
||||
throw new IllegalStateException(s"Plugin version is ${pluginVersion}, but database version is ${databaseVersion}")
|
||||
throw new IllegalStateException(
|
||||
s"Plugin version is ${pluginVersion}, but database version is ${databaseVersion}"
|
||||
)
|
||||
}
|
||||
|
||||
// Initialize
|
||||
plugin.initialize(instance, context, settings)
|
||||
instance.addPlugin(PluginInfo(
|
||||
pluginId = plugin.pluginId,
|
||||
pluginName = plugin.pluginName,
|
||||
pluginVersion = plugin.versions.last.getVersion,
|
||||
description = plugin.description,
|
||||
pluginClass = plugin,
|
||||
pluginJar = pluginJar,
|
||||
classLoader = classLoader
|
||||
))
|
||||
instance.addPlugin(
|
||||
PluginInfo(
|
||||
pluginId = plugin.pluginId,
|
||||
pluginName = plugin.pluginName,
|
||||
pluginVersion = plugin.versions.last.getVersion,
|
||||
description = plugin.description,
|
||||
pluginClass = plugin,
|
||||
pluginJar = pluginJar,
|
||||
classLoader = classLoader
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
@@ -311,13 +336,13 @@ object PluginRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
if(watcher == null){
|
||||
if (watcher == null) {
|
||||
watcher = new PluginWatchThread(context, PluginHome)
|
||||
watcher.start()
|
||||
}
|
||||
|
||||
extraPluginDir.foreach { extraDir =>
|
||||
if(extraWatcher == null){
|
||||
if (extraWatcher == null) {
|
||||
extraWatcher = new PluginWatchThread(context, extraDir)
|
||||
extraWatcher.start()
|
||||
}
|
||||
@@ -328,11 +353,11 @@ object PluginRegistry {
|
||||
instance.getPlugins().foreach { plugin =>
|
||||
try {
|
||||
plugin.pluginClass.shutdown(instance, context, settings)
|
||||
if(watcher != null){
|
||||
if (watcher != null) {
|
||||
watcher.interrupt()
|
||||
watcher = null
|
||||
}
|
||||
if(extraWatcher != null){
|
||||
if (extraWatcher != null) {
|
||||
extraWatcher.interrupt()
|
||||
extraWatcher = null
|
||||
}
|
||||
@@ -380,17 +405,19 @@ class PluginWatchThread(context: ServletContext, dir: String) extends Thread wit
|
||||
|
||||
override def run(): Unit = {
|
||||
val path = Paths.get(dir)
|
||||
if(!Files.exists(path)){
|
||||
if (!Files.exists(path)) {
|
||||
Files.createDirectories(path)
|
||||
}
|
||||
val fs = path.getFileSystem
|
||||
val watcher = fs.newWatchService
|
||||
|
||||
val watchKey = path.register(watcher,
|
||||
val watchKey = path.register(
|
||||
watcher,
|
||||
StandardWatchEventKinds.ENTRY_CREATE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY,
|
||||
StandardWatchEventKinds.ENTRY_DELETE,
|
||||
StandardWatchEventKinds.OVERFLOW)
|
||||
StandardWatchEventKinds.OVERFLOW
|
||||
)
|
||||
|
||||
logger.info("Start PluginWatchThread: " + path)
|
||||
|
||||
@@ -400,13 +427,13 @@ class PluginWatchThread(context: ServletContext, dir: String) extends Thread wit
|
||||
val events = detectedWatchKey.pollEvents.asScala.filter { e =>
|
||||
e.context.toString != ".installed" && !e.context.toString.endsWith(".bak")
|
||||
}
|
||||
if(events.nonEmpty){
|
||||
if (events.nonEmpty) {
|
||||
events.foreach { event =>
|
||||
logger.info(event.kind + ": " + event.context)
|
||||
}
|
||||
new Thread {
|
||||
override def run(): Unit = {
|
||||
gitbucket.core.servlet.Database() withTransaction { session =>
|
||||
gitbucket.core.servlet.Database() withTransaction { session =>
|
||||
logger.info("Reloading plugins...")
|
||||
PluginRegistry.reload(context, loadSystemSettings(), session.conn)
|
||||
logger.info("Reloading finished.")
|
||||
|
||||
@@ -15,7 +15,7 @@ object PluginRepository {
|
||||
lazy val LocalRepositoryIndexFile = new java.io.File(LocalRepositoryDir, "plugins.json")
|
||||
|
||||
def getPlugins(): Seq[PluginMetadata] = {
|
||||
if(LocalRepositoryIndexFile.exists){
|
||||
if (LocalRepositoryIndexFile.exists) {
|
||||
parsePluginJson(FileUtils.readFileToString(LocalRepositoryIndexFile, "UTF-8"))
|
||||
} else Nil
|
||||
}
|
||||
@@ -29,7 +29,7 @@ case class PluginMetadata(
|
||||
description: String,
|
||||
versions: Seq[VersionDef],
|
||||
default: Boolean = false
|
||||
){
|
||||
) {
|
||||
lazy val latestVersion: VersionDef = versions.last
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ case class VersionDef(
|
||||
version: String,
|
||||
url: String,
|
||||
range: String
|
||||
){
|
||||
) {
|
||||
lazy val file = url.substring(url.lastIndexOf("/") + 1)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@ import profile.api._
|
||||
|
||||
trait ReceiveHook {
|
||||
|
||||
def preReceive(owner: String, repository: String, receivePack: ReceivePack, command: ReceiveCommand, pusher: String)
|
||||
(implicit session: Session): Option[String] = None
|
||||
def preReceive(owner: String, repository: String, receivePack: ReceivePack, command: ReceiveCommand, pusher: String)(
|
||||
implicit session: Session
|
||||
): Option[String] = None
|
||||
|
||||
def postReceive(owner: String, repository: String, receivePack: ReceivePack, command: ReceiveCommand, pusher: String)
|
||||
(implicit session: Session): Unit = ()
|
||||
def postReceive(owner: String, repository: String, receivePack: ReceivePack, command: ReceiveCommand, pusher: String)(
|
||||
implicit session: Session
|
||||
): Unit = ()
|
||||
|
||||
}
|
||||
|
||||
@@ -21,14 +21,16 @@ trait Renderer {
|
||||
object MarkdownRenderer extends Renderer {
|
||||
override def render(request: RenderRequest): Html = {
|
||||
import request._
|
||||
Html(Markdown.toHtml(
|
||||
markdown = fileContent,
|
||||
repository = repository,
|
||||
enableWikiLink = enableWikiLink,
|
||||
enableRefsLink = enableRefsLink,
|
||||
enableAnchor = enableAnchor,
|
||||
enableLineBreaks = false
|
||||
)(context))
|
||||
Html(
|
||||
Markdown.toHtml(
|
||||
markdown = fileContent,
|
||||
repository = repository,
|
||||
enableWikiLink = enableWikiLink,
|
||||
enableRefsLink = enableRefsLink,
|
||||
enableAnchor = enableAnchor,
|
||||
enableLineBreaks = false
|
||||
)(context)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,9 @@ trait SuggestionProvider {
|
||||
*
|
||||
* Each element can be accessed as `option` in `template()` or `replace()` method.
|
||||
*/
|
||||
def options(repository: RepositoryInfo): Seq[(String, String)] = values(repository).map { value => (value, value) }
|
||||
def options(repository: RepositoryInfo): Seq[(String, String)] = values(repository).map { value =>
|
||||
(value, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* JavaScript fragment to generate a label of completion proposal. The default is: `option.label`.
|
||||
|
||||
@@ -7,7 +7,6 @@ import gitbucket.core.util.StringUtil
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
|
||||
trait AccessTokenService {
|
||||
|
||||
def makeAccessTokenString: String = {
|
||||
@@ -27,13 +26,10 @@ trait AccessTokenService {
|
||||
|
||||
do {
|
||||
token = makeAccessTokenString
|
||||
hash = tokenToHash(token)
|
||||
hash = tokenToHash(token)
|
||||
} while (AccessTokens.filter(_.tokenHash === hash.bind).exists.run)
|
||||
|
||||
val newToken = AccessToken(
|
||||
userName = userName,
|
||||
note = note,
|
||||
tokenHash = hash)
|
||||
val newToken = AccessToken(userName = userName, note = note, tokenHash = hash)
|
||||
val tokenId = (AccessTokens returning AccessTokens.map(_.accessTokenId)) insert newToken
|
||||
(tokenId, token)
|
||||
}
|
||||
@@ -41,8 +37,11 @@ trait AccessTokenService {
|
||||
def getAccountByAccessToken(token: String)(implicit s: Session): Option[Account] =
|
||||
Accounts
|
||||
.join(AccessTokens)
|
||||
.filter { case (ac, t) => (ac.userName === t.userName) && (t.tokenHash === tokenToHash(token).bind) && (ac.removed === false.bind) }
|
||||
.map { case (ac, t) => ac }
|
||||
.filter {
|
||||
case (ac, t) =>
|
||||
(ac.userName === t.userName) && (t.tokenHash === tokenToHash(token).bind) && (ac.removed === false.bind)
|
||||
}
|
||||
.map { case (ac, t) => ac }
|
||||
.firstOption
|
||||
|
||||
def getAccessTokens(userName: String)(implicit s: Session): List[AccessToken] =
|
||||
|
||||
@@ -12,20 +12,22 @@ trait AccountFederationService {
|
||||
private val logger = LoggerFactory.getLogger(classOf[AccountFederationService])
|
||||
|
||||
/**
|
||||
* Get or create a user account federated with OIDC or SAML IdP.
|
||||
*
|
||||
* @param issuer Issuer
|
||||
* @param subject Subject
|
||||
* @param mailAddress Mail address
|
||||
* @param preferredUserName Username (if this is none, username will be generated from the mail address)
|
||||
* @param fullName Fullname (defaults to username)
|
||||
* @return Account
|
||||
*/
|
||||
def getOrCreateFederatedUser(issuer: String,
|
||||
subject: String,
|
||||
mailAddress: String,
|
||||
preferredUserName: Option[String],
|
||||
fullName: Option[String])(implicit s: Session): Option[Account] =
|
||||
* Get or create a user account federated with OIDC or SAML IdP.
|
||||
*
|
||||
* @param issuer Issuer
|
||||
* @param subject Subject
|
||||
* @param mailAddress Mail address
|
||||
* @param preferredUserName Username (if this is none, username will be generated from the mail address)
|
||||
* @param fullName Fullname (defaults to username)
|
||||
* @return Account
|
||||
*/
|
||||
def getOrCreateFederatedUser(
|
||||
issuer: String,
|
||||
subject: String,
|
||||
mailAddress: String,
|
||||
preferredUserName: Option[String],
|
||||
fullName: Option[String]
|
||||
)(implicit s: Session): Option[Account] =
|
||||
getAccountByFederation(issuer, subject) match {
|
||||
case Some(account) if !account.isRemoved =>
|
||||
Some(account)
|
||||
@@ -43,19 +45,25 @@ trait AccountFederationService {
|
||||
private def extractSafeStringForUserName(s: String) = """^[a-zA-Z0-9][a-zA-Z0-9\-_.]*""".r.findPrefixOf(s)
|
||||
|
||||
/**
|
||||
* Find an available username from the preferred username or mail address.
|
||||
*
|
||||
* @param mailAddress Mail address
|
||||
* @param preferredUserName Username
|
||||
* @return Available username
|
||||
*/
|
||||
def findAvailableUserName(preferredUserName: Option[String], mailAddress: String)(implicit s: Session): Option[String] = {
|
||||
preferredUserName.flatMap(n => extractSafeStringForUserName(n)).orElse(extractSafeStringForUserName(mailAddress)) match {
|
||||
* Find an available username from the preferred username or mail address.
|
||||
*
|
||||
* @param mailAddress Mail address
|
||||
* @param preferredUserName Username
|
||||
* @return Available username
|
||||
*/
|
||||
def findAvailableUserName(preferredUserName: Option[String], mailAddress: String)(
|
||||
implicit s: Session
|
||||
): Option[String] = {
|
||||
preferredUserName
|
||||
.flatMap(n => extractSafeStringForUserName(n))
|
||||
.orElse(extractSafeStringForUserName(mailAddress)) match {
|
||||
case Some(safeUserName) =>
|
||||
getAccountByUserName(safeUserName, includeRemoved = true) match {
|
||||
case None => Some(safeUserName)
|
||||
case Some(_) =>
|
||||
logger.info(s"User ($safeUserName) already exists. preferredUserName=$preferredUserName, mailAddress=$mailAddress")
|
||||
logger.info(
|
||||
s"User ($safeUserName) already exists. preferredUserName=$preferredUserName, mailAddress=$mailAddress"
|
||||
)
|
||||
None
|
||||
}
|
||||
case None =>
|
||||
@@ -65,8 +73,10 @@ trait AccountFederationService {
|
||||
}
|
||||
|
||||
def getAccountByFederation(issuer: String, subject: String)(implicit s: Session): Option[Account] =
|
||||
AccountFederations.filter(_.byPrimaryKey(issuer, subject))
|
||||
.join(Accounts).on { case af ~ ac => af.userName === ac.userName }
|
||||
AccountFederations
|
||||
.filter(_.byPrimaryKey(issuer, subject))
|
||||
.join(Accounts)
|
||||
.on { case af ~ ac => af.userName === ac.userName }
|
||||
.map { case _ ~ ac => ac }
|
||||
.firstOption
|
||||
|
||||
|
||||
@@ -13,14 +13,16 @@ trait AccountService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[AccountService])
|
||||
|
||||
def authenticate(settings: SystemSettings, userName: String, password: String)(implicit s: Session): Option[Account] = {
|
||||
def authenticate(settings: SystemSettings, userName: String, password: String)(
|
||||
implicit s: Session
|
||||
): Option[Account] = {
|
||||
val account = if (settings.ldapAuthentication) {
|
||||
ldapAuthentication(settings, userName, password)
|
||||
} else {
|
||||
defaultAuthentication(userName, password)
|
||||
}
|
||||
|
||||
if(account.isEmpty){
|
||||
if (account.isEmpty) {
|
||||
logger.info(s"Failed to authenticate: $userName")
|
||||
}
|
||||
|
||||
@@ -32,45 +34,55 @@ trait AccountService {
|
||||
*/
|
||||
private def defaultAuthentication(userName: String, password: String)(implicit s: Session) = {
|
||||
getAccountByUserName(userName).collect {
|
||||
case account if(!account.isGroupAccount && account.password == sha1(password)) => Some(account)
|
||||
case account if (!account.isGroupAccount && account.password == sha1(password)) => Some(account)
|
||||
} getOrElse None
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate by LDAP.
|
||||
*/
|
||||
private def ldapAuthentication(settings: SystemSettings, userName: String, password: String)
|
||||
(implicit s: Session): Option[Account] = {
|
||||
private def ldapAuthentication(settings: SystemSettings, userName: String, password: String)(
|
||||
implicit s: Session
|
||||
): Option[Account] = {
|
||||
LDAPUtil.authenticate(settings.ldap.get, userName, password) match {
|
||||
case Right(ldapUserInfo) => {
|
||||
// Create or update account by LDAP information
|
||||
getAccountByUserName(ldapUserInfo.userName, true) match {
|
||||
case Some(x) if(!x.isRemoved) => {
|
||||
if(settings.ldap.get.mailAttribute.getOrElse("").isEmpty) {
|
||||
case Some(x) if (!x.isRemoved) => {
|
||||
if (settings.ldap.get.mailAttribute.getOrElse("").isEmpty) {
|
||||
updateAccount(x.copy(fullName = ldapUserInfo.fullName))
|
||||
} else {
|
||||
updateAccount(x.copy(mailAddress = ldapUserInfo.mailAddress, fullName = ldapUserInfo.fullName))
|
||||
}
|
||||
getAccountByUserName(ldapUserInfo.userName)
|
||||
}
|
||||
case Some(x) if(x.isRemoved) => {
|
||||
case Some(x) if (x.isRemoved) => {
|
||||
logger.info("LDAP Authentication Failed: Account is already registered but disabled.")
|
||||
defaultAuthentication(userName, password)
|
||||
}
|
||||
case None => getAccountByMailAddress(ldapUserInfo.mailAddress, true) match {
|
||||
case Some(x) if(!x.isRemoved) => {
|
||||
updateAccount(x.copy(fullName = ldapUserInfo.fullName))
|
||||
getAccountByUserName(ldapUserInfo.userName)
|
||||
case None =>
|
||||
getAccountByMailAddress(ldapUserInfo.mailAddress, true) match {
|
||||
case Some(x) if (!x.isRemoved) => {
|
||||
updateAccount(x.copy(fullName = ldapUserInfo.fullName))
|
||||
getAccountByUserName(ldapUserInfo.userName)
|
||||
}
|
||||
case Some(x) if (x.isRemoved) => {
|
||||
logger.info("LDAP Authentication Failed: Account is already registered but disabled.")
|
||||
defaultAuthentication(userName, password)
|
||||
}
|
||||
case None => {
|
||||
createAccount(
|
||||
ldapUserInfo.userName,
|
||||
"",
|
||||
ldapUserInfo.fullName,
|
||||
ldapUserInfo.mailAddress,
|
||||
false,
|
||||
None,
|
||||
None
|
||||
)
|
||||
getAccountByUserName(ldapUserInfo.userName)
|
||||
}
|
||||
}
|
||||
case Some(x) if(x.isRemoved) => {
|
||||
logger.info("LDAP Authentication Failed: Account is already registered but disabled.")
|
||||
defaultAuthentication(userName, password)
|
||||
}
|
||||
case None => {
|
||||
createAccount(ldapUserInfo.userName, "", ldapUserInfo.fullName, ldapUserInfo.mailAddress, false, None, None)
|
||||
getAccountByUserName(ldapUserInfo.userName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case Left(errorMessage) => {
|
||||
@@ -81,58 +93,90 @@ trait AccountService {
|
||||
}
|
||||
|
||||
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 getAccountsByUserNames(userNames: Set[String], knowns:Set[Account], includeRemoved: Boolean = false)(implicit s: Session): Map[String, Account] = {
|
||||
def getAccountsByUserNames(userNames: Set[String], knowns: Set[Account], includeRemoved: Boolean = false)(
|
||||
implicit s: Session
|
||||
): Map[String, Account] = {
|
||||
val map = knowns.map(a => a.userName -> a).toMap
|
||||
val needs = userNames -- map.keySet
|
||||
if(needs.isEmpty){
|
||||
if (needs.isEmpty) {
|
||||
map
|
||||
}else{
|
||||
map ++ Accounts.filter(t => (t.userName inSetBind needs) && (t.removed === false.bind, !includeRemoved)).list.map(a => a.userName -> a).toMap
|
||||
} else {
|
||||
map ++ Accounts
|
||||
.filter(t => (t.userName inSetBind needs) && (t.removed === false.bind, !includeRemoved))
|
||||
.list
|
||||
.map(a => a.userName -> a)
|
||||
.toMap
|
||||
}
|
||||
}
|
||||
|
||||
def getAccountByMailAddress(mailAddress: String, includeRemoved: Boolean = false)(implicit s: Session): Option[Account] =
|
||||
Accounts filter(t => (t.mailAddress.toLowerCase === mailAddress.toLowerCase.bind) && (t.removed === false.bind, !includeRemoved)) firstOption
|
||||
def getAccountByMailAddress(mailAddress: String, includeRemoved: Boolean = false)(
|
||||
implicit s: Session
|
||||
): Option[Account] =
|
||||
Accounts filter (
|
||||
t => (t.mailAddress.toLowerCase === mailAddress.toLowerCase.bind) && (t.removed === false.bind, !includeRemoved)
|
||||
) firstOption
|
||||
|
||||
def getAllUsers(includeRemoved: Boolean = true, includeGroups: Boolean = true)(implicit s: Session): List[Account] =
|
||||
{
|
||||
def getAllUsers(includeRemoved: Boolean = true, includeGroups: Boolean = true)(implicit s: Session): List[Account] = {
|
||||
Accounts filter { t =>
|
||||
(1.bind === 1.bind) &&
|
||||
(t.groupAccount === false.bind, !includeGroups) &&
|
||||
(t.removed === false.bind, !includeRemoved)
|
||||
} sortBy(_.userName) list
|
||||
(t.groupAccount === false.bind, !includeGroups) &&
|
||||
(t.removed === false.bind, !includeRemoved)
|
||||
} sortBy (_.userName) list
|
||||
}
|
||||
|
||||
def isLastAdministrator(account: Account)(implicit s: Session): Boolean = {
|
||||
if(account.isAdmin){
|
||||
if (account.isAdmin) {
|
||||
(Accounts filter (_.removed === false.bind) filter (_.isAdmin === true.bind) map (_.userName.length)).first == 1
|
||||
} else false
|
||||
}
|
||||
|
||||
def createAccount(userName: String, password: String, fullName: String, mailAddress: String, isAdmin: Boolean, description: Option[String], url: Option[String])
|
||||
(implicit s: Session): Unit =
|
||||
def createAccount(
|
||||
userName: String,
|
||||
password: String,
|
||||
fullName: String,
|
||||
mailAddress: String,
|
||||
isAdmin: Boolean,
|
||||
description: Option[String],
|
||||
url: Option[String]
|
||||
)(implicit s: Session): Unit =
|
||||
Accounts insert Account(
|
||||
userName = userName,
|
||||
password = password,
|
||||
fullName = fullName,
|
||||
mailAddress = mailAddress,
|
||||
isAdmin = isAdmin,
|
||||
url = url,
|
||||
userName = userName,
|
||||
password = password,
|
||||
fullName = fullName,
|
||||
mailAddress = mailAddress,
|
||||
isAdmin = isAdmin,
|
||||
url = url,
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate,
|
||||
lastLoginDate = None,
|
||||
image = None,
|
||||
updatedDate = currentDate,
|
||||
lastLoginDate = None,
|
||||
image = None,
|
||||
isGroupAccount = false,
|
||||
isRemoved = false,
|
||||
description = description)
|
||||
isRemoved = false,
|
||||
description = description
|
||||
)
|
||||
|
||||
def updateAccount(account: Account)(implicit s: Session): Unit =
|
||||
Accounts
|
||||
.filter { a => a.userName === account.userName.bind }
|
||||
.map { a => (a.password, a.fullName, a.mailAddress, a.isAdmin, a.url.?, a.registeredDate, a.updatedDate, a.lastLoginDate.?, a.removed, a.description.?) }
|
||||
.update (
|
||||
.filter { a =>
|
||||
a.userName === account.userName.bind
|
||||
}
|
||||
.map { a =>
|
||||
(
|
||||
a.password,
|
||||
a.fullName,
|
||||
a.mailAddress,
|
||||
a.isAdmin,
|
||||
a.url.?,
|
||||
a.registeredDate,
|
||||
a.updatedDate,
|
||||
a.lastLoginDate.?,
|
||||
a.removed,
|
||||
a.description.?
|
||||
)
|
||||
}
|
||||
.update(
|
||||
account.password,
|
||||
account.fullName,
|
||||
account.mailAddress,
|
||||
@@ -142,7 +186,8 @@ trait AccountService {
|
||||
currentDate,
|
||||
account.lastLoginDate,
|
||||
account.isRemoved,
|
||||
account.description)
|
||||
account.description
|
||||
)
|
||||
|
||||
def updateAvatarImage(userName: String, image: Option[String])(implicit s: Session): Unit =
|
||||
Accounts.filter(_.userName === userName.bind).map(_.image.?).update(image)
|
||||
@@ -152,29 +197,34 @@ trait AccountService {
|
||||
|
||||
def createGroup(groupName: String, description: Option[String], url: Option[String])(implicit s: Session): Unit =
|
||||
Accounts insert Account(
|
||||
userName = groupName,
|
||||
password = "",
|
||||
fullName = groupName,
|
||||
mailAddress = groupName + "@devnull",
|
||||
isAdmin = false,
|
||||
url = url,
|
||||
userName = groupName,
|
||||
password = "",
|
||||
fullName = groupName,
|
||||
mailAddress = groupName + "@devnull",
|
||||
isAdmin = false,
|
||||
url = url,
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate,
|
||||
lastLoginDate = None,
|
||||
image = None,
|
||||
updatedDate = currentDate,
|
||||
lastLoginDate = None,
|
||||
image = None,
|
||||
isGroupAccount = true,
|
||||
isRemoved = false,
|
||||
description = description)
|
||||
isRemoved = false,
|
||||
description = description
|
||||
)
|
||||
|
||||
def updateGroup(groupName: String, description: Option[String], url: Option[String], removed: Boolean)(implicit s: Session): Unit =
|
||||
Accounts.filter(_.userName === groupName.bind)
|
||||
def updateGroup(groupName: String, description: Option[String], url: Option[String], removed: Boolean)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
Accounts
|
||||
.filter(_.userName === groupName.bind)
|
||||
.map(t => (t.url.?, t.description.?, t.updatedDate, t.removed))
|
||||
.update(url, description, currentDate, removed)
|
||||
|
||||
def updateGroupMembers(groupName: String, members: List[(String, Boolean)])(implicit s: Session): Unit = {
|
||||
GroupMembers.filter(_.groupName === groupName.bind).delete
|
||||
members.foreach { case (userName, isManager) =>
|
||||
GroupMembers insert GroupMember (groupName, userName, isManager)
|
||||
members.foreach {
|
||||
case (userName, isManager) =>
|
||||
GroupMembers insert GroupMember(groupName, userName, isManager)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,188 +15,356 @@ trait ActivityService {
|
||||
|
||||
def getActivitiesByUser(activityUserName: String, isPublic: Boolean)(implicit s: Session): List[Activity] =
|
||||
Activities
|
||||
.join(Repositories).on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||
.filter { case (t1, t2) =>
|
||||
if(isPublic){
|
||||
(t1.activityUserName === activityUserName.bind) && (t2.isPrivate === false.bind)
|
||||
} else {
|
||||
(t1.activityUserName === activityUserName.bind)
|
||||
}
|
||||
.join(Repositories)
|
||||
.on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||
.filter {
|
||||
case (t1, t2) =>
|
||||
if (isPublic) {
|
||||
(t1.activityUserName === activityUserName.bind) && (t2.isPrivate === false.bind)
|
||||
} else {
|
||||
(t1.activityUserName === activityUserName.bind)
|
||||
}
|
||||
}
|
||||
.sortBy { case (t1, t2) => t1.activityId desc }
|
||||
.map { case (t1, t2) => t1 }
|
||||
.map { case (t1, t2) => t1 }
|
||||
.take(30)
|
||||
.list
|
||||
|
||||
def getRecentActivities()(implicit s: Session): List[Activity] =
|
||||
Activities
|
||||
.join(Repositories).on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||
.filter { case (t1, t2) => t2.isPrivate === false.bind }
|
||||
.join(Repositories)
|
||||
.on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||
.filter { case (t1, t2) => t2.isPrivate === false.bind }
|
||||
.sortBy { case (t1, t2) => t1.activityId desc }
|
||||
.map { case (t1, t2) => t1 }
|
||||
.map { case (t1, t2) => t1 }
|
||||
.take(30)
|
||||
.list
|
||||
|
||||
def getRecentActivitiesByOwners(owners : Set[String])(implicit s: Session): List[Activity] =
|
||||
def getRecentActivitiesByOwners(owners: Set[String])(implicit s: Session): List[Activity] =
|
||||
Activities
|
||||
.join(Repositories).on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||
.join(Repositories)
|
||||
.on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||
.filter { case (t1, t2) => (t2.isPrivate === false.bind) || (t2.userName inSetBind owners) }
|
||||
.sortBy { case (t1, t2) => t1.activityId desc }
|
||||
.map { case (t1, t2) => t1 }
|
||||
.map { case (t1, t2) => t1 }
|
||||
.take(30)
|
||||
.list
|
||||
|
||||
def recordCreateRepositoryActivity(userName: String, repositoryName: String, activityUserName: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordCreateRepositoryActivity(userName: String, repositoryName: String, activityUserName: String)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"create_repository",
|
||||
s"[user:${activityUserName}] created [repo:${userName}/${repositoryName}]",
|
||||
None,
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCreateIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordCreateIssueActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
issueId: Int,
|
||||
title: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"open_issue",
|
||||
s"[user:${activityUserName}] opened issue [issue:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCloseIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordCloseIssueActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
issueId: Int,
|
||||
title: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"close_issue",
|
||||
s"[user:${activityUserName}] closed issue [issue:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordClosePullRequestActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordClosePullRequestActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
issueId: Int,
|
||||
title: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"close_issue",
|
||||
s"[user:${activityUserName}] closed pull request [pullreq:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordReopenIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordReopenIssueActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
issueId: Int,
|
||||
title: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"reopen_issue",
|
||||
s"[user:${activityUserName}] reopened issue [issue:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCommentIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, comment: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordCommentIssueActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
issueId: Int,
|
||||
comment: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"comment_issue",
|
||||
s"[user:${activityUserName}] commented on issue [issue:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(cut(comment, 200)),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCommentPullRequestActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, comment: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordCommentPullRequestActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
issueId: Int,
|
||||
comment: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"comment_issue",
|
||||
s"[user:${activityUserName}] commented on pull request [pullreq:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(cut(comment, 200)),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCommentCommitActivity(userName: String, repositoryName: String, activityUserName: String, commitId: String, comment: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordCommentCommitActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
commitId: String,
|
||||
comment: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"comment_commit",
|
||||
s"[user:${activityUserName}] commented on commit [commit:${userName}/${repositoryName}@${commitId}]",
|
||||
Some(cut(comment, 200)),
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCreateWikiPageActivity(userName: String, repositoryName: String, activityUserName: String, pageName: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordCreateWikiPageActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
pageName: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"create_wiki",
|
||||
s"[user:${activityUserName}] created the [repo:${userName}/${repositoryName}] wiki",
|
||||
Some(pageName),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordEditWikiPageActivity(userName: String, repositoryName: String, activityUserName: String, pageName: String, commitId: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordEditWikiPageActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
pageName: String,
|
||||
commitId: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"edit_wiki",
|
||||
s"[user:${activityUserName}] edited the [repo:${userName}/${repositoryName}] wiki",
|
||||
Some(pageName + ":" + commitId),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordPushActivity(userName: String, repositoryName: String, activityUserName: String,
|
||||
branchName: String, commits: List[JGitUtil.CommitInfo])(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordPushActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
branchName: String,
|
||||
commits: List[JGitUtil.CommitInfo]
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"push",
|
||||
s"[user:${activityUserName}] pushed to [branch:${userName}/${repositoryName}#${branchName}] at [repo:${userName}/${repositoryName}]",
|
||||
Some(commits.take(5).map { commit => commit.id + ":" + commit.shortMessage }.mkString("\n")),
|
||||
currentDate)
|
||||
Some(
|
||||
commits
|
||||
.take(5)
|
||||
.map { commit =>
|
||||
commit.id + ":" + commit.shortMessage
|
||||
}
|
||||
.mkString("\n")
|
||||
),
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCreateTagActivity(userName: String, repositoryName: String, activityUserName: String,
|
||||
tagName: String, commits: List[JGitUtil.CommitInfo])(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordCreateTagActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
tagName: String,
|
||||
commits: List[JGitUtil.CommitInfo]
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"create_tag",
|
||||
s"[user:${activityUserName}] created tag [tag:${userName}/${repositoryName}#${tagName}] at [repo:${userName}/${repositoryName}]",
|
||||
None,
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordDeleteTagActivity(userName: String, repositoryName: String, activityUserName: String,
|
||||
tagName: String, commits: List[JGitUtil.CommitInfo])(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordDeleteTagActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
tagName: String,
|
||||
commits: List[JGitUtil.CommitInfo]
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"delete_tag",
|
||||
s"[user:${activityUserName}] deleted tag ${tagName} at [repo:${userName}/${repositoryName}]",
|
||||
None,
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCreateBranchActivity(userName: String, repositoryName: String, activityUserName: String, branchName: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordCreateBranchActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
branchName: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"create_branch",
|
||||
s"[user:${activityUserName}] created branch [branch:${userName}/${repositoryName}#${branchName}] at [repo:${userName}/${repositoryName}]",
|
||||
None,
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordDeleteBranchActivity(userName: String, repositoryName: String, activityUserName: String, branchName: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordDeleteBranchActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
branchName: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"delete_branch",
|
||||
s"[user:${activityUserName}] deleted branch ${branchName} at [repo:${userName}/${repositoryName}]",
|
||||
None,
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordForkActivity(userName: String, repositoryName: String, activityUserName: String, forkedUserName: String)(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordForkActivity(userName: String, repositoryName: String, activityUserName: String, forkedUserName: String)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"fork",
|
||||
s"[user:${activityUserName}] forked [repo:${userName}/${repositoryName}] to [repo:${forkedUserName}/${repositoryName}]",
|
||||
None,
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordPullRequestActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordPullRequestActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
issueId: Int,
|
||||
title: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"open_pullreq",
|
||||
s"[user:${activityUserName}] opened pull request [pullreq:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordMergeActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, message: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordMergeActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
issueId: Int,
|
||||
message: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"merge_pullreq",
|
||||
s"[user:${activityUserName}] merged pull request [pullreq:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(message),
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordReleaseActivity(userName: String, repositoryName: String, activityUserName: String, name: String)(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
def recordReleaseActivity(userName: String, repositoryName: String, activityUserName: String, name: String)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"release",
|
||||
s"[user:${activityUserName}] released ${name} at [repo:${userName}/${repositoryName}]",
|
||||
None,
|
||||
currentDate)
|
||||
currentDate
|
||||
)
|
||||
|
||||
private def cut(value: String, length: Int): String =
|
||||
if(value.length > length) value.substring(0, length) + "..." else value
|
||||
if (value.length > length) value.substring(0, length) + "..." else value
|
||||
}
|
||||
|
||||
@@ -6,47 +6,77 @@ import gitbucket.core.model.Profile.dateColumnType
|
||||
import gitbucket.core.model.{CommitState, CommitStatus, Account}
|
||||
|
||||
trait CommitStatusService {
|
||||
/** insert or update */
|
||||
def createCommitStatus(userName: String, repositoryName: String, sha: String, context: String, state: CommitState,
|
||||
targetUrl: Option[String], description: Option[String], now: java.util.Date, creator: Account)(implicit s: Session): Int =
|
||||
CommitStatuses
|
||||
.filter(t => t.byCommit(userName, repositoryName, sha) && t.context === context.bind )
|
||||
.map(_.commitStatusId).firstOption match {
|
||||
case Some(id: Int) => {
|
||||
CommitStatuses.filter(_.byPrimaryKey(id)).map { t =>
|
||||
(t.state , t.targetUrl , t.updatedDate , t.creator, t.description)
|
||||
}.update((state, targetUrl, now, creator.userName, description))
|
||||
id
|
||||
}
|
||||
case None => (CommitStatuses returning CommitStatuses.map(_.commitStatusId)) insert CommitStatus(
|
||||
userName = userName,
|
||||
repositoryName = repositoryName,
|
||||
commitId = sha,
|
||||
context = context,
|
||||
state = state,
|
||||
targetUrl = targetUrl,
|
||||
description = description,
|
||||
creator = creator.userName,
|
||||
registeredDate = now,
|
||||
updatedDate = now)
|
||||
}
|
||||
|
||||
def getCommitStatus(userName: String, repositoryName: String, id: Int)(implicit s: Session) :Option[CommitStatus] =
|
||||
/** insert or update */
|
||||
def createCommitStatus(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
sha: String,
|
||||
context: String,
|
||||
state: CommitState,
|
||||
targetUrl: Option[String],
|
||||
description: Option[String],
|
||||
now: java.util.Date,
|
||||
creator: Account
|
||||
)(implicit s: Session): Int =
|
||||
CommitStatuses
|
||||
.filter(t => t.byCommit(userName, repositoryName, sha) && t.context === context.bind)
|
||||
.map(_.commitStatusId)
|
||||
.firstOption match {
|
||||
case Some(id: Int) => {
|
||||
CommitStatuses
|
||||
.filter(_.byPrimaryKey(id))
|
||||
.map { t =>
|
||||
(t.state, t.targetUrl, t.updatedDate, t.creator, t.description)
|
||||
}
|
||||
.update((state, targetUrl, now, creator.userName, description))
|
||||
id
|
||||
}
|
||||
case None =>
|
||||
(CommitStatuses returning CommitStatuses.map(_.commitStatusId)) insert CommitStatus(
|
||||
userName = userName,
|
||||
repositoryName = repositoryName,
|
||||
commitId = sha,
|
||||
context = context,
|
||||
state = state,
|
||||
targetUrl = targetUrl,
|
||||
description = description,
|
||||
creator = creator.userName,
|
||||
registeredDate = now,
|
||||
updatedDate = now
|
||||
)
|
||||
}
|
||||
|
||||
def getCommitStatus(userName: String, repositoryName: String, id: Int)(implicit s: Session): Option[CommitStatus] =
|
||||
CommitStatuses.filter(t => t.byPrimaryKey(id) && t.byRepository(userName, repositoryName)).firstOption
|
||||
|
||||
def getCommitStatus(userName: String, repositoryName: String, sha: String, context: String)(implicit s: Session) :Option[CommitStatus] =
|
||||
def getCommitStatus(userName: String, repositoryName: String, sha: String, context: String)(
|
||||
implicit s: Session
|
||||
): Option[CommitStatus] =
|
||||
CommitStatuses.filter(t => t.byCommit(userName, repositoryName, sha) && t.context === context.bind).firstOption
|
||||
|
||||
def getCommitStatues(userName: String, repositoryName: String, sha: String)(implicit s: Session) :List[CommitStatus] =
|
||||
def getCommitStatues(userName: String, repositoryName: String, sha: String)(implicit s: Session): List[CommitStatus] =
|
||||
byCommitStatues(userName, repositoryName, sha).list
|
||||
|
||||
def getRecentStatuesContexts(userName: String, repositoryName: String, time: java.util.Date)(implicit s: Session) :List[String] =
|
||||
CommitStatuses.filter(t => t.byRepository(userName, repositoryName)).filter(t => t.updatedDate > time.bind).groupBy(_.context).map(_._1).list
|
||||
def getRecentStatuesContexts(userName: String, repositoryName: String, time: java.util.Date)(
|
||||
implicit s: Session
|
||||
): List[String] =
|
||||
CommitStatuses
|
||||
.filter(t => t.byRepository(userName, repositoryName))
|
||||
.filter(t => t.updatedDate > time.bind)
|
||||
.groupBy(_.context)
|
||||
.map(_._1)
|
||||
.list
|
||||
|
||||
def getCommitStatuesWithCreator(userName: String, repositoryName: String, sha: String)(implicit s: Session) :List[(CommitStatus, Account)] =
|
||||
byCommitStatues(userName, repositoryName, sha).join(Accounts).filter { case (t, a) => t.creator === a.userName }.list
|
||||
def getCommitStatuesWithCreator(userName: String, repositoryName: String, sha: String)(
|
||||
implicit s: Session
|
||||
): List[(CommitStatus, Account)] =
|
||||
byCommitStatues(userName, repositoryName, sha)
|
||||
.join(Accounts)
|
||||
.filter { case (t, a) => t.creator === a.userName }
|
||||
.list
|
||||
|
||||
protected def byCommitStatues(userName: String, repositoryName: String, sha: String)(implicit s: Session) =
|
||||
protected def byCommitStatues(userName: String, repositoryName: String, sha: String)(implicit s: Session) =
|
||||
CommitStatuses.filter(t => t.byCommit(userName, repositoryName, sha)).sortBy(_.updatedDate desc)
|
||||
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ import gitbucket.core.model.Profile.dateColumnType
|
||||
|
||||
trait CommitsService {
|
||||
|
||||
def getCommitComments(owner: String, repository: String, commitId: String, includePullRequest: Boolean)(implicit s: Session) =
|
||||
CommitComments filter {
|
||||
t => t.byCommit(owner, repository, commitId) && (t.issueId.isEmpty || includePullRequest)
|
||||
def getCommitComments(owner: String, repository: String, commitId: String, includePullRequest: Boolean)(
|
||||
implicit s: Session
|
||||
) =
|
||||
CommitComments filter { t =>
|
||||
t.byCommit(owner, repository, commitId) && (t.issueId.isEmpty || includePullRequest)
|
||||
} list
|
||||
|
||||
def getCommitComment(owner: String, repository: String, commentId: String)(implicit s: Session) =
|
||||
@@ -20,33 +22,48 @@ trait CommitsService {
|
||||
else
|
||||
None
|
||||
|
||||
def createCommitComment(owner: String, repository: String, commitId: String, loginUser: String,
|
||||
content: String, fileName: Option[String], oldLine: Option[Int], newLine: Option[Int],
|
||||
issueId: Option[Int])(implicit s: Session): Int =
|
||||
def createCommitComment(
|
||||
owner: String,
|
||||
repository: String,
|
||||
commitId: String,
|
||||
loginUser: String,
|
||||
content: String,
|
||||
fileName: Option[String],
|
||||
oldLine: Option[Int],
|
||||
newLine: Option[Int],
|
||||
issueId: Option[Int]
|
||||
)(implicit s: Session): Int =
|
||||
CommitComments returning CommitComments.map(_.commentId) insert CommitComment(
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
commitId = commitId,
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
commitId = commitId,
|
||||
commentedUserName = loginUser,
|
||||
content = content,
|
||||
fileName = fileName,
|
||||
oldLine = oldLine,
|
||||
newLine = newLine,
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate,
|
||||
issueId = issueId)
|
||||
content = content,
|
||||
fileName = fileName,
|
||||
oldLine = oldLine,
|
||||
newLine = newLine,
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate,
|
||||
issueId = issueId
|
||||
)
|
||||
|
||||
def updateCommitCommentPosition(commentId: Int, commitId: String, oldLine: Option[Int], newLine: Option[Int])(implicit s: Session): Unit =
|
||||
CommitComments.filter(_.byPrimaryKey(commentId))
|
||||
def updateCommitCommentPosition(commentId: Int, commitId: String, oldLine: Option[Int], newLine: Option[Int])(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
CommitComments
|
||||
.filter(_.byPrimaryKey(commentId))
|
||||
.map { t =>
|
||||
(t.commitId, t.oldLine, t.newLine)
|
||||
}.update(commitId, oldLine, newLine)
|
||||
}
|
||||
.update(commitId, oldLine, newLine)
|
||||
|
||||
def updateCommitComment(commentId: Int, content: String)(implicit s: Session) = {
|
||||
CommitComments
|
||||
.filter (_.byPrimaryKey(commentId))
|
||||
.map { t => (t.content, t.updatedDate) }
|
||||
.update (content, currentDate)
|
||||
.filter(_.byPrimaryKey(commentId))
|
||||
.map { t =>
|
||||
(t.content, t.updatedDate)
|
||||
}
|
||||
.update(content, currentDate)
|
||||
}
|
||||
|
||||
def deleteCommitComment(commentId: Int)(implicit s: Session) =
|
||||
|
||||
@@ -6,20 +6,24 @@ import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
|
||||
trait DeployKeyService {
|
||||
|
||||
def addDeployKey(userName: String, repositoryName: String, title: String, publicKey: String, allowWrite: Boolean)
|
||||
(implicit s: Session): Unit =
|
||||
DeployKeys.insert(DeployKey(
|
||||
userName = userName,
|
||||
repositoryName = repositoryName,
|
||||
title = title,
|
||||
publicKey = publicKey,
|
||||
allowWrite = allowWrite
|
||||
))
|
||||
def addDeployKey(userName: String, repositoryName: String, title: String, publicKey: String, allowWrite: Boolean)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
DeployKeys.insert(
|
||||
DeployKey(
|
||||
userName = userName,
|
||||
repositoryName = repositoryName,
|
||||
title = title,
|
||||
publicKey = publicKey,
|
||||
allowWrite = allowWrite
|
||||
)
|
||||
)
|
||||
|
||||
def getDeployKeys(userName: String, repositoryName: String)(implicit s: Session): List[DeployKey] =
|
||||
DeployKeys
|
||||
.filter(x => (x.userName === userName.bind) && (x.repositoryName === repositoryName.bind))
|
||||
.sortBy(_.deployKeyId).list
|
||||
.sortBy(_.deployKeyId)
|
||||
.list
|
||||
|
||||
def getAllDeployKeys()(implicit s: Session): List[DeployKey] =
|
||||
DeployKeys.filter(_.publicKey.trim =!= "").list
|
||||
@@ -27,5 +31,4 @@ trait DeployKeyService {
|
||||
def deleteDeployKey(userName: String, repositoryName: String, deployKeyId: Int)(implicit s: Session): Unit =
|
||||
DeployKeys.filter(_.byPrimaryKey(userName, repositoryName, deployKeyId)).delete
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,85 +8,111 @@ import gitbucket.core.util.SyntaxSugars._
|
||||
import gitbucket.core.util.Implicits._
|
||||
|
||||
trait HandleCommentService {
|
||||
self: RepositoryService with IssuesService with ActivityService
|
||||
with WebHookService with WebHookIssueCommentService with WebHookPullRequestService =>
|
||||
self: RepositoryService
|
||||
with IssuesService
|
||||
with ActivityService
|
||||
with WebHookService
|
||||
with WebHookIssueCommentService
|
||||
with WebHookPullRequestService =>
|
||||
|
||||
/**
|
||||
* @see [[https://github.com/gitbucket/gitbucket/wiki/CommentAction]]
|
||||
*/
|
||||
def handleComment(issue: Issue, content: Option[String], repository: RepositoryService.RepositoryInfo, actionOpt: Option[String])
|
||||
(implicit context: Context, s: Session) = {
|
||||
def handleComment(
|
||||
issue: Issue,
|
||||
content: Option[String],
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
actionOpt: Option[String]
|
||||
)(implicit context: Context, s: Session) = {
|
||||
context.loginAccount.flatMap { loginAccount =>
|
||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||
val userName = loginAccount.userName
|
||||
defining(repository.owner, repository.name) {
|
||||
case (owner, name) =>
|
||||
val userName = loginAccount.userName
|
||||
|
||||
val (action, actionActivity) = actionOpt
|
||||
.collect {
|
||||
case "close" if(!issue.closed) => true ->
|
||||
(Some("close") -> Some(if(issue.isPullRequest) recordClosePullRequestActivity _ else recordCloseIssueActivity _))
|
||||
case "reopen" if(issue.closed) => false ->
|
||||
(Some("reopen") -> Some(recordReopenIssueActivity _))
|
||||
}
|
||||
.map { case (closed, t) =>
|
||||
updateClosed(owner, name, issue.issueId, closed)
|
||||
t
|
||||
}
|
||||
.getOrElse(None -> None)
|
||||
|
||||
val commentId = (content, action) match {
|
||||
case (None, None) => None
|
||||
case (None, Some(action)) =>
|
||||
Some(createComment(owner, name, userName, issue.issueId, action.capitalize, action))
|
||||
case (Some(content), _) =>
|
||||
val id = Some(createComment(owner, name, userName, issue.issueId, content, action.map(_+ "_comment").getOrElse("comment")))
|
||||
|
||||
// record comment activity
|
||||
if(issue.isPullRequest) recordCommentPullRequestActivity(owner, name, userName, issue.issueId, content)
|
||||
else recordCommentIssueActivity(owner, name, userName, issue.issueId, content)
|
||||
|
||||
// extract references and create refer comment
|
||||
createReferComment(owner, name, issue, content, loginAccount)
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
actionActivity.foreach { f => f(owner, name, userName, issue.issueId, issue.title) }
|
||||
|
||||
// call web hooks
|
||||
action match {
|
||||
case None => commentId foreach (callIssueCommentWebHook(repository, issue, _, loginAccount))
|
||||
case Some(act) =>
|
||||
val webHookAction = act match {
|
||||
case "close" => "closed"
|
||||
case "reopen" => "reopened"
|
||||
val (action, actionActivity) = actionOpt
|
||||
.collect {
|
||||
case "close" if (!issue.closed) =>
|
||||
true ->
|
||||
(Some("close") -> Some(
|
||||
if (issue.isPullRequest) recordClosePullRequestActivity _
|
||||
else recordCloseIssueActivity _
|
||||
))
|
||||
case "reopen" if (issue.closed) =>
|
||||
false ->
|
||||
(Some("reopen") -> Some(recordReopenIssueActivity _))
|
||||
}
|
||||
if(issue.isPullRequest)
|
||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, context.baseUrl, loginAccount)
|
||||
else
|
||||
callIssuesWebHook(webHookAction, repository, issue, context.baseUrl, loginAccount)
|
||||
}
|
||||
.map {
|
||||
case (closed, t) =>
|
||||
updateClosed(owner, name, issue.issueId, closed)
|
||||
t
|
||||
}
|
||||
.getOrElse(None -> None)
|
||||
|
||||
// call hooks
|
||||
content foreach { x =>
|
||||
if(issue.isPullRequest)
|
||||
PluginRegistry().getPullRequestHooks.foreach(_.addedComment(commentId.get, x, issue, repository))
|
||||
else
|
||||
PluginRegistry().getIssueHooks.foreach(_.addedComment(commentId.get, x, issue, repository))
|
||||
}
|
||||
action foreach {
|
||||
case "close" =>
|
||||
if(issue.isPullRequest)
|
||||
PluginRegistry().getPullRequestHooks.foreach(_.closed(issue, repository))
|
||||
else
|
||||
PluginRegistry().getIssueHooks.foreach(_.closed(issue, repository))
|
||||
case "reopen" =>
|
||||
if(issue.isPullRequest)
|
||||
PluginRegistry().getPullRequestHooks.foreach(_.reopened(issue, repository))
|
||||
else
|
||||
PluginRegistry().getIssueHooks.foreach(_.reopened(issue, repository))
|
||||
}
|
||||
val commentId = (content, action) match {
|
||||
case (None, None) => None
|
||||
case (None, Some(action)) =>
|
||||
Some(createComment(owner, name, userName, issue.issueId, action.capitalize, action))
|
||||
case (Some(content), _) =>
|
||||
val id = Some(
|
||||
createComment(
|
||||
owner,
|
||||
name,
|
||||
userName,
|
||||
issue.issueId,
|
||||
content,
|
||||
action.map(_ + "_comment").getOrElse("comment")
|
||||
)
|
||||
)
|
||||
|
||||
commentId.map( issue -> _ )
|
||||
// record comment activity
|
||||
if (issue.isPullRequest) recordCommentPullRequestActivity(owner, name, userName, issue.issueId, content)
|
||||
else recordCommentIssueActivity(owner, name, userName, issue.issueId, content)
|
||||
|
||||
// extract references and create refer comment
|
||||
createReferComment(owner, name, issue, content, loginAccount)
|
||||
|
||||
id
|
||||
}
|
||||
|
||||
actionActivity.foreach { f =>
|
||||
f(owner, name, userName, issue.issueId, issue.title)
|
||||
}
|
||||
|
||||
// call web hooks
|
||||
action match {
|
||||
case None => commentId foreach (callIssueCommentWebHook(repository, issue, _, loginAccount))
|
||||
case Some(act) =>
|
||||
val webHookAction = act match {
|
||||
case "close" => "closed"
|
||||
case "reopen" => "reopened"
|
||||
}
|
||||
if (issue.isPullRequest)
|
||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, context.baseUrl, loginAccount)
|
||||
else
|
||||
callIssuesWebHook(webHookAction, repository, issue, context.baseUrl, loginAccount)
|
||||
}
|
||||
|
||||
// call hooks
|
||||
content foreach { x =>
|
||||
if (issue.isPullRequest)
|
||||
PluginRegistry().getPullRequestHooks.foreach(_.addedComment(commentId.get, x, issue, repository))
|
||||
else
|
||||
PluginRegistry().getIssueHooks.foreach(_.addedComment(commentId.get, x, issue, repository))
|
||||
}
|
||||
action foreach {
|
||||
case "close" =>
|
||||
if (issue.isPullRequest)
|
||||
PluginRegistry().getPullRequestHooks.foreach(_.closed(issue, repository))
|
||||
else
|
||||
PluginRegistry().getIssueHooks.foreach(_.closed(issue, repository))
|
||||
case "reopen" =>
|
||||
if (issue.isPullRequest)
|
||||
PluginRegistry().getPullRequestHooks.foreach(_.reopened(issue, repository))
|
||||
else
|
||||
PluginRegistry().getIssueHooks.foreach(_.reopened(issue, repository))
|
||||
}
|
||||
|
||||
commentId.map(issue -> _)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,20 +11,33 @@ trait IssueCreationService {
|
||||
|
||||
self: RepositoryService with WebHookIssueCommentService with LabelsService with IssuesService with ActivityService =>
|
||||
|
||||
def createIssue(repository: RepositoryInfo, title:String, body:Option[String],
|
||||
assignee: Option[String], milestoneId: Option[Int], priorityId: Option[Int], labelNames: Seq[String],
|
||||
loginAccount: Account)(implicit context: Context, s: Session) : Issue = {
|
||||
def createIssue(
|
||||
repository: RepositoryInfo,
|
||||
title: String,
|
||||
body: Option[String],
|
||||
assignee: Option[String],
|
||||
milestoneId: Option[Int],
|
||||
priorityId: Option[Int],
|
||||
labelNames: Seq[String],
|
||||
loginAccount: Account
|
||||
)(implicit context: Context, s: Session): Issue = {
|
||||
|
||||
val owner = repository.owner
|
||||
val name = repository.name
|
||||
val userName = loginAccount.userName
|
||||
val name = repository.name
|
||||
val userName = loginAccount.userName
|
||||
val manageable = isIssueManageable(repository)
|
||||
|
||||
// insert issue
|
||||
val issueId = insertIssue(owner, name, userName, title, body,
|
||||
val issueId = insertIssue(
|
||||
owner,
|
||||
name,
|
||||
userName,
|
||||
title,
|
||||
body,
|
||||
if (manageable) assignee else None,
|
||||
if (manageable) milestoneId else None,
|
||||
if (manageable) priorityId else None)
|
||||
if (manageable) priorityId else None
|
||||
)
|
||||
val issue: Issue = getIssue(owner, name, issueId.toString).get
|
||||
|
||||
// insert labels
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,18 +17,20 @@ trait LabelsService {
|
||||
|
||||
def createLabel(owner: String, repository: String, labelName: String, color: String)(implicit s: Session): Int = {
|
||||
Labels returning Labels.map(_.labelId) insert Label(
|
||||
userName = owner,
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
labelName = labelName,
|
||||
color = color
|
||||
labelName = labelName,
|
||||
color = color
|
||||
)
|
||||
}
|
||||
|
||||
def updateLabel(owner: String, repository: String, labelId: Int, labelName: String, color: String)
|
||||
(implicit s: Session): Unit =
|
||||
Labels.filter(_.byPrimaryKey(owner, repository, labelId))
|
||||
.map(t => t.labelName -> t.color)
|
||||
.update(labelName, color)
|
||||
def updateLabel(owner: String, repository: String, labelId: Int, labelName: String, color: String)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
Labels
|
||||
.filter(_.byPrimaryKey(owner, repository, labelId))
|
||||
.map(t => t.labelName -> t.color)
|
||||
.update(labelName, color)
|
||||
|
||||
def deleteLabel(owner: String, repository: String, labelId: Int)(implicit s: Session): Unit = {
|
||||
IssueLabels.filter(_.byLabel(owner, repository, labelId)).delete
|
||||
|
||||
@@ -31,7 +31,12 @@ trait MergeService {
|
||||
* Returns Some(true) if conflict will be caused.
|
||||
* Returns None if cache has not created yet.
|
||||
*/
|
||||
def checkConflictCache(userName: String, repositoryName: String, branch: String, issueId: Int): Option[Option[String]] = {
|
||||
def checkConflictCache(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
branch: String,
|
||||
issueId: Int
|
||||
): Option[Option[String]] = {
|
||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||
new MergeCacheInfo(git, branch, issueId).checkConflictCache()
|
||||
}
|
||||
@@ -43,7 +48,13 @@ trait MergeService {
|
||||
}
|
||||
|
||||
/** rebase to the head of the pull request branch */
|
||||
def rebasePullRequest(git: Git, branch: String, issueId: Int, commits: Seq[RevCommit], committer: PersonIdent): Unit = {
|
||||
def rebasePullRequest(
|
||||
git: Git,
|
||||
branch: String,
|
||||
issueId: Int,
|
||||
commits: Seq[RevCommit],
|
||||
committer: PersonIdent
|
||||
): Unit = {
|
||||
new MergeCacheInfo(git, branch, issueId).rebase(committer, commits)
|
||||
}
|
||||
|
||||
@@ -53,8 +64,15 @@ trait MergeService {
|
||||
}
|
||||
|
||||
/** fetch remote branch to my repository refs/pull/{issueId}/head */
|
||||
def fetchAsPullRequest(userName: String, repositoryName: String, requestUserName: String, requestRepositoryName: String, requestBranch:String, issueId:Int){
|
||||
using(Git.open(getRepositoryDir(userName, repositoryName))){ git =>
|
||||
def fetchAsPullRequest(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
requestUserName: String,
|
||||
requestRepositoryName: String,
|
||||
requestBranch: String,
|
||||
issueId: Int
|
||||
) {
|
||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||
git.fetch
|
||||
.setRemote(getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString)
|
||||
.setRefSpecs(new RefSpec(s"refs/heads/${requestBranch}:refs/pull/${issueId}/head"))
|
||||
@@ -65,8 +83,14 @@ trait MergeService {
|
||||
/**
|
||||
* Checks whether conflict will be caused in merging. Returns true if conflict will be caused.
|
||||
*/
|
||||
def tryMergeRemote(localUserName: String, localRepositoryName: String, localBranch: String,
|
||||
remoteUserName: String, remoteRepositoryName: String, remoteBranch: String): Either[String, (ObjectId, ObjectId, ObjectId)] = {
|
||||
def tryMergeRemote(
|
||||
localUserName: String,
|
||||
localRepositoryName: String,
|
||||
localBranch: String,
|
||||
remoteUserName: String,
|
||||
remoteRepositoryName: String,
|
||||
remoteBranch: String
|
||||
): Either[String, (ObjectId, ObjectId, ObjectId)] = {
|
||||
using(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||
val remoteRefName = s"refs/heads/${remoteBranch}"
|
||||
val tmpRefName = s"refs/remote-temp/${remoteUserName}/${remoteRepositoryName}/${remoteBranch}"
|
||||
@@ -74,15 +98,15 @@ trait MergeService {
|
||||
try {
|
||||
// fetch objects from origin repository branch
|
||||
git.fetch
|
||||
.setRemote(getRepositoryDir(remoteUserName, remoteRepositoryName).toURI.toString)
|
||||
.setRefSpecs(refSpec)
|
||||
.call
|
||||
.setRemote(getRepositoryDir(remoteUserName, remoteRepositoryName).toURI.toString)
|
||||
.setRefSpecs(refSpec)
|
||||
.call
|
||||
// merge conflict check
|
||||
val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true)
|
||||
val mergeBaseTip = git.getRepository.resolve(s"refs/heads/${localBranch}")
|
||||
val mergeTip = git.getRepository.resolve(tmpRefName)
|
||||
try {
|
||||
if(merger.merge(mergeBaseTip, mergeTip)){
|
||||
if (merger.merge(mergeBaseTip, mergeTip)) {
|
||||
Right((merger.getResultTreeId, mergeBaseTip, mergeTip))
|
||||
} else {
|
||||
Left(createConflictMessage(mergeTip, mergeBaseTip, merger))
|
||||
@@ -101,45 +125,73 @@ trait MergeService {
|
||||
/**
|
||||
* Checks whether conflict will be caused in merging. Returns `Some(errorMessage)` if conflict will be caused.
|
||||
*/
|
||||
def checkConflict(userName: String, repositoryName: String, branch: String,
|
||||
requestUserName: String, requestRepositoryName: String, requestBranch: String): Option[String] =
|
||||
def checkConflict(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
branch: String,
|
||||
requestUserName: String,
|
||||
requestRepositoryName: String,
|
||||
requestBranch: String
|
||||
): Option[String] =
|
||||
tryMergeRemote(userName, repositoryName, branch, requestUserName, requestRepositoryName, requestBranch).left.toOption
|
||||
|
||||
def pullRemote(localUserName: String, localRepositoryName: String, localBranch: String,
|
||||
remoteUserName: String, remoteRepositoryName: String, remoteBranch: String,
|
||||
loginAccount: Account, message: String): Option[ObjectId] = {
|
||||
tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map { case (newTreeId, oldBaseId, oldHeadId) =>
|
||||
using(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
val newCommit = Util.createMergeCommit(git.getRepository, newTreeId, committer, message, Seq(oldBaseId, oldHeadId))
|
||||
Util.updateRefs(git.getRepository, s"refs/heads/${localBranch}", newCommit, false, committer, Some("merge"))
|
||||
}
|
||||
oldBaseId
|
||||
def pullRemote(
|
||||
localUserName: String,
|
||||
localRepositoryName: String,
|
||||
localBranch: String,
|
||||
remoteUserName: String,
|
||||
remoteRepositoryName: String,
|
||||
remoteBranch: String,
|
||||
loginAccount: Account,
|
||||
message: String
|
||||
): Option[ObjectId] = {
|
||||
tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map {
|
||||
case (newTreeId, oldBaseId, oldHeadId) =>
|
||||
using(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||
val newCommit =
|
||||
Util.createMergeCommit(git.getRepository, newTreeId, committer, message, Seq(oldBaseId, oldHeadId))
|
||||
Util.updateRefs(git.getRepository, s"refs/heads/${localBranch}", newCommit, false, committer, Some("merge"))
|
||||
}
|
||||
oldBaseId
|
||||
}.toOption
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object MergeService{
|
||||
object MergeService {
|
||||
|
||||
object Util{
|
||||
object Util {
|
||||
// return merge commit id
|
||||
def createMergeCommit(repository: Repository, treeId: ObjectId, committer: PersonIdent, message: String, parents: Seq[ObjectId]): ObjectId = {
|
||||
def createMergeCommit(
|
||||
repository: Repository,
|
||||
treeId: ObjectId,
|
||||
committer: PersonIdent,
|
||||
message: String,
|
||||
parents: Seq[ObjectId]
|
||||
): ObjectId = {
|
||||
val mergeCommit = new CommitBuilder()
|
||||
mergeCommit.setTreeId(treeId)
|
||||
mergeCommit.setParentIds(parents:_*)
|
||||
mergeCommit.setParentIds(parents: _*)
|
||||
mergeCommit.setAuthor(committer)
|
||||
mergeCommit.setCommitter(committer)
|
||||
mergeCommit.setMessage(message)
|
||||
// insertObject and got mergeCommit Object Id
|
||||
using(repository.newObjectInserter){ inserter =>
|
||||
using(repository.newObjectInserter) { inserter =>
|
||||
val mergeCommitId = inserter.insert(mergeCommit)
|
||||
inserter.flush()
|
||||
mergeCommitId
|
||||
}
|
||||
}
|
||||
|
||||
def updateRefs(repository: Repository, ref: String, newObjectId: ObjectId, force: Boolean, committer: PersonIdent, refLogMessage: Option[String] = None): Unit = {
|
||||
def updateRefs(
|
||||
repository: Repository,
|
||||
ref: String,
|
||||
newObjectId: ObjectId,
|
||||
force: Boolean,
|
||||
committer: PersonIdent,
|
||||
refLogMessage: Option[String] = None
|
||||
): Unit = {
|
||||
val refUpdate = repository.updateRef(ref)
|
||||
refUpdate.setNewObjectId(newObjectId)
|
||||
refUpdate.setForceUpdate(force)
|
||||
@@ -149,56 +201,58 @@ object MergeService{
|
||||
}
|
||||
}
|
||||
|
||||
class MergeCacheInfo(git: Git, branch: String, issueId: Int){
|
||||
class MergeCacheInfo(git: Git, branch: String, issueId: Int) {
|
||||
|
||||
private val repository = git.getRepository
|
||||
|
||||
private val mergedBranchName = s"refs/pull/${issueId}/merge"
|
||||
private val mergedBranchName = s"refs/pull/${issueId}/merge"
|
||||
private val conflictedBranchName = s"refs/pull/${issueId}/conflict"
|
||||
|
||||
lazy val mergeBaseTip = repository.resolve(s"refs/heads/${branch}")
|
||||
lazy val mergeTip = repository.resolve(s"refs/pull/${issueId}/head")
|
||||
lazy val mergeTip = repository.resolve(s"refs/pull/${issueId}/head")
|
||||
|
||||
def checkConflictCache(): Option[Option[String]] = {
|
||||
Option(repository.resolve(mergedBranchName)).flatMap { merged =>
|
||||
if(parseCommit(merged).getParents().toSet == Set( mergeBaseTip, mergeTip )){
|
||||
Option(repository.resolve(mergedBranchName))
|
||||
.flatMap { merged =>
|
||||
if (parseCommit(merged).getParents().toSet == Set(mergeBaseTip, mergeTip)) {
|
||||
// merged branch exists
|
||||
Some(None)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}.orElse(Option(repository.resolve(conflictedBranchName)).flatMap{ conflicted =>
|
||||
val commit = parseCommit(conflicted)
|
||||
if(commit.getParents().toSet == Set( mergeBaseTip, mergeTip )){
|
||||
// conflict branch exists
|
||||
Some(Some(commit.getFullMessage))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.orElse(Option(repository.resolve(conflictedBranchName)).flatMap { conflicted =>
|
||||
val commit = parseCommit(conflicted)
|
||||
if (commit.getParents().toSet == Set(mergeBaseTip, mergeTip)) {
|
||||
// conflict branch exists
|
||||
Some(Some(commit.getFullMessage))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
def checkConflict(): Option[String] ={
|
||||
def checkConflict(): Option[String] = {
|
||||
checkConflictCache.getOrElse(checkConflictForce)
|
||||
}
|
||||
|
||||
def checkConflictForce(): Option[String] ={
|
||||
def checkConflictForce(): Option[String] = {
|
||||
val merger = MergeStrategy.RECURSIVE.newMerger(repository, true)
|
||||
val conflicted = try {
|
||||
!merger.merge(mergeBaseTip, mergeTip)
|
||||
} catch {
|
||||
case e: NoMergeBaseException => true
|
||||
}
|
||||
val mergeTipCommit = using(new RevWalk( repository ))(_.parseCommit( mergeTip ))
|
||||
val mergeTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeTip))
|
||||
val committer = mergeTipCommit.getCommitterIdent
|
||||
|
||||
def _updateBranch(treeId: ObjectId, message: String, branchName: String){
|
||||
def _updateBranch(treeId: ObjectId, message: String, branchName: String) {
|
||||
// creates merge commit
|
||||
val mergeCommitId = createMergeCommit(treeId, committer, message)
|
||||
Util.updateRefs(repository, branchName, mergeCommitId, true, committer)
|
||||
}
|
||||
|
||||
if(!conflicted){
|
||||
if (!conflicted) {
|
||||
_updateBranch(merger.getResultTreeId, s"Merge ${mergeTip.name} into ${mergeBaseTip.name}", mergedBranchName)
|
||||
git.branchDelete().setForce(true).setBranchNames(conflictedBranchName).call()
|
||||
None
|
||||
@@ -212,7 +266,7 @@ object MergeService{
|
||||
|
||||
// update branch from cache
|
||||
def merge(message: String, committer: PersonIdent) = {
|
||||
if(checkConflict().isDefined){
|
||||
if (checkConflict().isDefined) {
|
||||
throw new RuntimeException("This pull request can't merge automatically.")
|
||||
}
|
||||
val mergeResultCommit = parseCommit(Option(repository.resolve(mergedBranchName)).getOrElse {
|
||||
@@ -225,7 +279,7 @@ object MergeService{
|
||||
}
|
||||
|
||||
def rebase(committer: PersonIdent, commits: Seq[RevCommit]): Unit = {
|
||||
if(checkConflict().isDefined){
|
||||
if (checkConflict().isDefined) {
|
||||
throw new RuntimeException("This pull request can't merge automatically.")
|
||||
}
|
||||
|
||||
@@ -242,10 +296,10 @@ object MergeService{
|
||||
newCommit
|
||||
}
|
||||
|
||||
val mergeBaseTipCommit = using(new RevWalk( repository ))(_.parseCommit( mergeBaseTip ))
|
||||
val mergeBaseTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||
var previousId = mergeBaseTipCommit.getId
|
||||
|
||||
using(repository.newObjectInserter){ inserter =>
|
||||
using(repository.newObjectInserter) { inserter =>
|
||||
commits.foreach { commit =>
|
||||
val nextCommit = _cloneCommit(commit, previousId, mergeBaseTipCommit.getId)
|
||||
previousId = inserter.insert(nextCommit)
|
||||
@@ -257,12 +311,12 @@ object MergeService{
|
||||
}
|
||||
|
||||
def squash(message: String, committer: PersonIdent): Unit = {
|
||||
if(checkConflict().isDefined){
|
||||
if (checkConflict().isDefined) {
|
||||
throw new RuntimeException("This pull request can't merge automatically.")
|
||||
}
|
||||
|
||||
val mergeBaseTipCommit = using(new RevWalk( repository ))(_.parseCommit(mergeBaseTip))
|
||||
val mergeBranchHeadCommit = using(new RevWalk( repository ))(_.parseCommit(repository.resolve(mergedBranchName)))
|
||||
val mergeBaseTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||
val mergeBranchHeadCommit = using(new RevWalk(repository))(_.parseCommit(repository.resolve(mergedBranchName)))
|
||||
|
||||
// Create squash commit
|
||||
val mergeCommit = new CommitBuilder()
|
||||
@@ -273,7 +327,7 @@ object MergeService{
|
||||
mergeCommit.setMessage(message)
|
||||
|
||||
// insertObject and got squash commit Object Id
|
||||
val newCommitId = using(repository.newObjectInserter){ inserter =>
|
||||
val newCommitId = using(repository.newObjectInserter) { inserter =>
|
||||
val newCommitId = inserter.insert(mergeCommit)
|
||||
inserter.flush()
|
||||
newCommitId
|
||||
@@ -282,14 +336,21 @@ object MergeService{
|
||||
Util.updateRefs(repository, mergedBranchName, newCommitId, true, committer)
|
||||
|
||||
// rebase to squash commit
|
||||
Util.updateRefs(repository, s"refs/heads/${branch}", repository.resolve(mergedBranchName), false, committer, Some("squashed"))
|
||||
Util.updateRefs(
|
||||
repository,
|
||||
s"refs/heads/${branch}",
|
||||
repository.resolve(mergedBranchName),
|
||||
false,
|
||||
committer,
|
||||
Some("squashed")
|
||||
)
|
||||
}
|
||||
|
||||
// return treeId
|
||||
private def createMergeCommit(treeId: ObjectId, committer: PersonIdent, message: String) =
|
||||
Util.createMergeCommit(repository, treeId, committer, message, Seq[ObjectId](mergeBaseTip, mergeTip))
|
||||
|
||||
private def parseCommit(id: ObjectId) = using(new RevWalk( repository ))(_.parseCommit(id))
|
||||
private def parseCommit(id: ObjectId) = using(new RevWalk(repository))(_.parseCommit(id))
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,22 +7,27 @@ import gitbucket.core.model.Profile.dateColumnType
|
||||
|
||||
trait MilestonesService {
|
||||
|
||||
def createMilestone(owner: String, repository: String, title: String, description: Option[String],
|
||||
dueDate: Option[java.util.Date])(implicit s: Session): Unit =
|
||||
def createMilestone(
|
||||
owner: String,
|
||||
repository: String,
|
||||
title: String,
|
||||
description: Option[String],
|
||||
dueDate: Option[java.util.Date]
|
||||
)(implicit s: Session): Unit =
|
||||
Milestones insert Milestone(
|
||||
userName = owner,
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
title = title,
|
||||
description = description,
|
||||
dueDate = dueDate,
|
||||
closedDate = None
|
||||
title = title,
|
||||
description = description,
|
||||
dueDate = dueDate,
|
||||
closedDate = None
|
||||
)
|
||||
|
||||
def updateMilestone(milestone: Milestone)(implicit s: Session): Unit =
|
||||
Milestones
|
||||
.filter (t => t.byPrimaryKey(milestone.userName, milestone.repositoryName, milestone.milestoneId))
|
||||
.map (t => (t.title, t.description, t.dueDate, t.closedDate))
|
||||
.update (milestone.title, milestone.description, milestone.dueDate, milestone.closedDate)
|
||||
.filter(t => t.byPrimaryKey(milestone.userName, milestone.repositoryName, milestone.milestoneId))
|
||||
.map(t => (t.title, t.description, t.dueDate, t.closedDate))
|
||||
.update(milestone.title, milestone.description, milestone.dueDate, milestone.closedDate)
|
||||
|
||||
def openMilestone(milestone: Milestone)(implicit s: Session): Unit =
|
||||
updateMilestone(milestone.copy(closedDate = None))
|
||||
@@ -38,20 +43,33 @@ trait MilestonesService {
|
||||
def getMilestone(owner: String, repository: String, milestoneId: Int)(implicit s: Session): Option[Milestone] =
|
||||
Milestones.filter(_.byPrimaryKey(owner, repository, milestoneId)).firstOption
|
||||
|
||||
def getMilestonesWithIssueCount(owner: String, repository: String)(implicit s: Session): List[(Milestone, Int, Int)] = {
|
||||
def getMilestonesWithIssueCount(owner: String, repository: String)(
|
||||
implicit s: Session
|
||||
): List[(Milestone, Int, Int)] = {
|
||||
val counts = Issues
|
||||
.filter { t => t.byRepository(owner, repository) && (t.milestoneId.? isDefined) }
|
||||
.groupBy { t => t.milestoneId -> t.closed }
|
||||
.map { case (t1, t2) => t1._1 -> t1._2 -> t2.length }
|
||||
.filter { t =>
|
||||
t.byRepository(owner, repository) && (t.milestoneId.? isDefined)
|
||||
}
|
||||
.groupBy { t =>
|
||||
t.milestoneId -> t.closed
|
||||
}
|
||||
.map { case (t1, t2) => t1._1 -> t1._2 -> t2.length }
|
||||
.list
|
||||
.toMap
|
||||
|
||||
getMilestones(owner, repository).map { milestone =>
|
||||
(milestone, counts.getOrElse((milestone.milestoneId, false), 0), counts.getOrElse((milestone.milestoneId, true), 0))
|
||||
(
|
||||
milestone,
|
||||
counts.getOrElse((milestone.milestoneId, false), 0),
|
||||
counts.getOrElse((milestone.milestoneId, true), 0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def getMilestones(owner: String, repository: String)(implicit s: Session): List[Milestone] =
|
||||
Milestones.filter(_.byRepository(owner, repository)).sortBy(t => (t.dueDate.asc, t.closedDate.desc, t.milestoneId.desc)).list
|
||||
Milestones
|
||||
.filter(_.byRepository(owner, repository))
|
||||
.sortBy(t => (t.dueDate.asc, t.closedDate.desc, t.milestoneId.desc))
|
||||
.list
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ import org.slf4j.LoggerFactory
|
||||
import scala.collection.JavaConverters.{asScalaSet, mapAsJavaMap}
|
||||
|
||||
/**
|
||||
* Service class for the OpenID Connect authentication.
|
||||
*/
|
||||
* Service class for the OpenID Connect authentication.
|
||||
*/
|
||||
trait OpenIDConnectService {
|
||||
self: AccountFederationService =>
|
||||
|
||||
@@ -29,22 +29,17 @@ trait OpenIDConnectService {
|
||||
|
||||
private val JWK_REQUEST_TIMEOUT = 5000
|
||||
|
||||
private val OIDC_SCOPE = new Scope(
|
||||
OIDCScopeValue.OPENID,
|
||||
OIDCScopeValue.EMAIL,
|
||||
OIDCScopeValue.PROFILE)
|
||||
private val OIDC_SCOPE = new Scope(OIDCScopeValue.OPENID, OIDCScopeValue.EMAIL, OIDCScopeValue.PROFILE)
|
||||
|
||||
/**
|
||||
* Obtain the OIDC metadata from discovery and create an authentication request.
|
||||
*
|
||||
* @param issuer Issuer, used to construct the discovery endpoint URL, e.g. https://accounts.google.com
|
||||
* @param clientID Client ID (given by the issuer)
|
||||
* @param redirectURI Redirect URI
|
||||
* @return Authentication request
|
||||
*/
|
||||
def createOIDCAuthenticationRequest(issuer: Issuer,
|
||||
clientID: ClientID,
|
||||
redirectURI: URI): AuthenticationRequest = {
|
||||
* Obtain the OIDC metadata from discovery and create an authentication request.
|
||||
*
|
||||
* @param issuer Issuer, used to construct the discovery endpoint URL, e.g. https://accounts.google.com
|
||||
* @param clientID Client ID (given by the issuer)
|
||||
* @param redirectURI Redirect URI
|
||||
* @return Authentication request
|
||||
*/
|
||||
def createOIDCAuthenticationRequest(issuer: Issuer, clientID: ClientID, redirectURI: URI): AuthenticationRequest = {
|
||||
val metadata = OIDCProviderMetadata.resolve(issuer)
|
||||
new AuthenticationRequest(
|
||||
metadata.getAuthorizationEndpointURI,
|
||||
@@ -53,29 +48,38 @@ trait OpenIDConnectService {
|
||||
clientID,
|
||||
redirectURI,
|
||||
new State(),
|
||||
new Nonce())
|
||||
new Nonce()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Proceed the OpenID Connect authentication.
|
||||
*
|
||||
* @param params Query parameters of the authentication response
|
||||
* @param redirectURI Redirect URI
|
||||
* @param state State saved in the session
|
||||
* @param nonce Nonce saved in the session
|
||||
* @param oidc OIDC settings
|
||||
* @return ID token
|
||||
*/
|
||||
def authenticate(params: Map[String, String],
|
||||
redirectURI: URI,
|
||||
state: State,
|
||||
nonce: Nonce,
|
||||
oidc: SystemSettingsService.OIDC)(implicit s: Session): Option[Account] =
|
||||
* Proceed the OpenID Connect authentication.
|
||||
*
|
||||
* @param params Query parameters of the authentication response
|
||||
* @param redirectURI Redirect URI
|
||||
* @param state State saved in the session
|
||||
* @param nonce Nonce saved in the session
|
||||
* @param oidc OIDC settings
|
||||
* @return ID token
|
||||
*/
|
||||
def authenticate(
|
||||
params: Map[String, String],
|
||||
redirectURI: URI,
|
||||
state: State,
|
||||
nonce: Nonce,
|
||||
oidc: SystemSettingsService.OIDC
|
||||
)(implicit s: Session): Option[Account] =
|
||||
validateOIDCAuthenticationResponse(params, state, redirectURI) flatMap { authenticationResponse =>
|
||||
obtainOIDCToken(authenticationResponse.getAuthorizationCode, nonce, redirectURI, oidc) flatMap { claims =>
|
||||
Seq("email", "preferred_username", "name").map(k => Option(claims.getStringClaim(k))) match {
|
||||
case Seq(Some(email), preferredUsername, name) =>
|
||||
getOrCreateFederatedUser(claims.getIssuer.getValue, claims.getSubject.getValue, email, preferredUsername, name)
|
||||
getOrCreateFederatedUser(
|
||||
claims.getIssuer.getValue,
|
||||
claims.getSubject.getValue,
|
||||
email,
|
||||
preferredUsername,
|
||||
name
|
||||
)
|
||||
case _ =>
|
||||
logger.info(s"OIDC ID token must have an email claim: claims=${claims.toJSONObject}")
|
||||
None
|
||||
@@ -84,14 +88,18 @@ trait OpenIDConnectService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the authentication response.
|
||||
*
|
||||
* @param params Query parameters of the authentication response
|
||||
* @param state State saved in the session
|
||||
* @param redirectURI Redirect URI
|
||||
* @return Authentication response
|
||||
*/
|
||||
def validateOIDCAuthenticationResponse(params: Map[String, String], state: State, redirectURI: URI): Option[AuthenticationSuccessResponse] =
|
||||
* Validate the authentication response.
|
||||
*
|
||||
* @param params Query parameters of the authentication response
|
||||
* @param state State saved in the session
|
||||
* @param redirectURI Redirect URI
|
||||
* @return Authentication response
|
||||
*/
|
||||
def validateOIDCAuthenticationResponse(
|
||||
params: Map[String, String],
|
||||
state: State,
|
||||
redirectURI: URI
|
||||
): Option[AuthenticationSuccessResponse] =
|
||||
try {
|
||||
AuthenticationResponseParser.parse(redirectURI, mapAsJavaMap(params)) match {
|
||||
case response: AuthenticationSuccessResponse =>
|
||||
@@ -112,23 +120,27 @@ trait OpenIDConnectService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the ID token from the OpenID Provider.
|
||||
*
|
||||
* @param authorizationCode Authorization code in the query string
|
||||
* @param nonce Nonce
|
||||
* @param redirectURI Redirect URI
|
||||
* @param oidc OIDC settings
|
||||
* @return Token response
|
||||
*/
|
||||
def obtainOIDCToken(authorizationCode: AuthorizationCode,
|
||||
nonce: Nonce,
|
||||
redirectURI: URI,
|
||||
oidc: SystemSettingsService.OIDC): Option[IDTokenClaimsSet] = {
|
||||
* Obtain the ID token from the OpenID Provider.
|
||||
*
|
||||
* @param authorizationCode Authorization code in the query string
|
||||
* @param nonce Nonce
|
||||
* @param redirectURI Redirect URI
|
||||
* @param oidc OIDC settings
|
||||
* @return Token response
|
||||
*/
|
||||
def obtainOIDCToken(
|
||||
authorizationCode: AuthorizationCode,
|
||||
nonce: Nonce,
|
||||
redirectURI: URI,
|
||||
oidc: SystemSettingsService.OIDC
|
||||
): Option[IDTokenClaimsSet] = {
|
||||
val metadata = OIDCProviderMetadata.resolve(oidc.issuer)
|
||||
val tokenRequest = new TokenRequest(metadata.getTokenEndpointURI,
|
||||
val tokenRequest = new TokenRequest(
|
||||
metadata.getTokenEndpointURI,
|
||||
new ClientSecretBasic(oidc.clientID, oidc.clientSecret),
|
||||
new AuthorizationCodeGrant(authorizationCode, redirectURI),
|
||||
OIDC_SCOPE)
|
||||
OIDC_SCOPE
|
||||
)
|
||||
val httpResponse = tokenRequest.toHTTPRequest.send()
|
||||
try {
|
||||
OIDCTokenResponseParser.parse(httpResponse) match {
|
||||
@@ -146,29 +158,36 @@ trait OpenIDConnectService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the token response.
|
||||
*
|
||||
* @param response Token response
|
||||
* @param metadata OpenID Provider metadata
|
||||
* @param nonce Nonce
|
||||
* @return Claims
|
||||
*/
|
||||
def validateOIDCTokenResponse(response: OIDCTokenResponse,
|
||||
metadata: OIDCProviderMetadata,
|
||||
nonce: Nonce,
|
||||
oidc: SystemSettingsService.OIDC): Option[IDTokenClaimsSet] =
|
||||
* Validate the token response.
|
||||
*
|
||||
* @param response Token response
|
||||
* @param metadata OpenID Provider metadata
|
||||
* @param nonce Nonce
|
||||
* @return Claims
|
||||
*/
|
||||
def validateOIDCTokenResponse(
|
||||
response: OIDCTokenResponse,
|
||||
metadata: OIDCProviderMetadata,
|
||||
nonce: Nonce,
|
||||
oidc: SystemSettingsService.OIDC
|
||||
): Option[IDTokenClaimsSet] =
|
||||
Option(response.getOIDCTokens.getIDToken) match {
|
||||
case Some(jwt) =>
|
||||
val validator = oidc.jwsAlgorithm map { jwsAlgorithm =>
|
||||
new IDTokenValidator(metadata.getIssuer, oidc.clientID, jwsAlgorithm, metadata.getJWKSetURI.toURL,
|
||||
new DefaultResourceRetriever(JWK_REQUEST_TIMEOUT, JWK_REQUEST_TIMEOUT))
|
||||
new IDTokenValidator(
|
||||
metadata.getIssuer,
|
||||
oidc.clientID,
|
||||
jwsAlgorithm,
|
||||
metadata.getJWKSetURI.toURL,
|
||||
new DefaultResourceRetriever(JWK_REQUEST_TIMEOUT, JWK_REQUEST_TIMEOUT)
|
||||
)
|
||||
} getOrElse {
|
||||
new IDTokenValidator(metadata.getIssuer, oidc.clientID)
|
||||
}
|
||||
try {
|
||||
Some(validator.validate(jwt, nonce))
|
||||
} catch {
|
||||
case e@(_: BadJOSEException | _: JOSEException) =>
|
||||
case e @ (_: BadJOSEException | _: JOSEException) =>
|
||||
logger.info(s"OIDC ID token has error: $e")
|
||||
None
|
||||
}
|
||||
@@ -179,9 +198,10 @@ trait OpenIDConnectService {
|
||||
}
|
||||
|
||||
object OpenIDConnectService {
|
||||
|
||||
/**
|
||||
* All signature algorithms.
|
||||
*/
|
||||
* All signature algorithms.
|
||||
*/
|
||||
val JWS_ALGORITHMS: Map[String, Set[JWSAlgorithm]] = Seq(
|
||||
"HMAC" -> Family.HMAC_SHA,
|
||||
"RSA" -> Family.RSA,
|
||||
|
||||
@@ -16,8 +16,15 @@ trait PrioritiesService {
|
||||
def getPriority(owner: String, repository: String, priorityName: String)(implicit s: Session): Option[Priority] =
|
||||
Priorities.filter(_.byPriority(owner, repository, priorityName)).firstOption
|
||||
|
||||
def createPriority(owner: String, repository: String, priorityName: String, description: Option[String], color: String)(implicit s: Session): Int = {
|
||||
val ordering = Priorities.filter(_.byRepository(owner, repository))
|
||||
def createPriority(
|
||||
owner: String,
|
||||
repository: String,
|
||||
priorityName: String,
|
||||
description: Option[String],
|
||||
color: String
|
||||
)(implicit s: Session): Int = {
|
||||
val ordering = Priorities
|
||||
.filter(_.byRepository(owner, repository))
|
||||
.list
|
||||
.map(p => p.ordering)
|
||||
.reduceOption(_ max _)
|
||||
@@ -25,37 +32,48 @@ trait PrioritiesService {
|
||||
.getOrElse(0)
|
||||
|
||||
Priorities returning Priorities.map(_.priorityId) insert Priority(
|
||||
userName = owner,
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
priorityName = priorityName,
|
||||
description = description,
|
||||
isDefault = false,
|
||||
ordering = ordering,
|
||||
color = color
|
||||
priorityName = priorityName,
|
||||
description = description,
|
||||
isDefault = false,
|
||||
ordering = ordering,
|
||||
color = color
|
||||
)
|
||||
}
|
||||
|
||||
def updatePriority(owner: String, repository: String, priorityId: Int, priorityName: String, description: Option[String], color: String)
|
||||
(implicit s: Session): Unit =
|
||||
Priorities.filter(_.byPrimaryKey(owner, repository, priorityId))
|
||||
.map(t => (t.priorityName, t.description.?, t.color))
|
||||
.update(priorityName, description, color)
|
||||
def updatePriority(
|
||||
owner: String,
|
||||
repository: String,
|
||||
priorityId: Int,
|
||||
priorityName: String,
|
||||
description: Option[String],
|
||||
color: String
|
||||
)(implicit s: Session): Unit =
|
||||
Priorities
|
||||
.filter(_.byPrimaryKey(owner, repository, priorityId))
|
||||
.map(t => (t.priorityName, t.description.?, t.color))
|
||||
.update(priorityName, description, color)
|
||||
|
||||
def reorderPriorities(owner: String, repository: String, order: Map[Int, Int])
|
||||
(implicit s: Session): Unit = {
|
||||
def reorderPriorities(owner: String, repository: String, order: Map[Int, Int])(implicit s: Session): Unit = {
|
||||
|
||||
Priorities.filter(_.byRepository(owner, repository))
|
||||
Priorities
|
||||
.filter(_.byRepository(owner, repository))
|
||||
.list
|
||||
.foreach(p => Priorities
|
||||
.filter(_.byPrimaryKey(owner, repository, p.priorityId))
|
||||
.map(_.ordering)
|
||||
.update(order.get(p.priorityId).get))
|
||||
.foreach(
|
||||
p =>
|
||||
Priorities
|
||||
.filter(_.byPrimaryKey(owner, repository, p.priorityId))
|
||||
.map(_.ordering)
|
||||
.update(order.get(p.priorityId).get)
|
||||
)
|
||||
}
|
||||
|
||||
def deletePriority(owner: String, repository: String, priorityId: Int)(implicit s: Session): Unit = {
|
||||
Issues.filter(_.byRepository(owner, repository))
|
||||
Issues
|
||||
.filter(_.byRepository(owner, repository))
|
||||
.filter(_.priorityId === priorityId)
|
||||
.map(_.priorityId?)
|
||||
.map(_.priorityId ?)
|
||||
.update(None)
|
||||
|
||||
Priorities.filter(_.byPrimaryKey(owner, repository, priorityId)).delete
|
||||
@@ -76,9 +94,12 @@ trait PrioritiesService {
|
||||
.map(_.isDefault)
|
||||
.update(false)
|
||||
|
||||
priorityId.foreach(id => Priorities
|
||||
.filter(_.byPrimaryKey(owner, repository, id))
|
||||
.map(_.isDefault)
|
||||
.update(true))
|
||||
priorityId.foreach(
|
||||
id =>
|
||||
Priorities
|
||||
.filter(_.byPrimaryKey(owner, repository, id))
|
||||
.map(_.isDefault)
|
||||
.update(true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,55 +6,81 @@ import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||
|
||||
|
||||
trait ProtectedBranchService {
|
||||
import ProtectedBranchService._
|
||||
private def getProtectedBranchInfoOpt(owner: String, repository: String, branch: String)(implicit session: Session): Option[ProtectedBranchInfo] =
|
||||
private def getProtectedBranchInfoOpt(owner: String, repository: String, branch: String)(
|
||||
implicit session: Session
|
||||
): Option[ProtectedBranchInfo] =
|
||||
ProtectedBranches
|
||||
.joinLeft(ProtectedBranchContexts)
|
||||
.on { case (pb, c) => pb.byBranch(c.userName, c.repositoryName, c.branch) }
|
||||
.on { case (pb, c) => pb.byBranch(c.userName, c.repositoryName, c.branch) }
|
||||
.map { case (pb, c) => pb -> c.map(_.context) }
|
||||
.filter(_._1.byPrimaryKey(owner, repository, branch))
|
||||
.list
|
||||
.groupBy(_._1)
|
||||
.headOption
|
||||
.map { p => p._1 -> p._2.flatMap(_._2) }
|
||||
.map { case (t1, contexts) =>
|
||||
new ProtectedBranchInfo(t1.userName, t1.repositoryName, true, contexts, t1.statusCheckAdmin)
|
||||
.map { p =>
|
||||
p._1 -> p._2.flatMap(_._2)
|
||||
}
|
||||
.map {
|
||||
case (t1, contexts) =>
|
||||
new ProtectedBranchInfo(t1.userName, t1.repositoryName, true, contexts, t1.statusCheckAdmin)
|
||||
}
|
||||
|
||||
def getProtectedBranchInfo(owner: String, repository: String, branch: String)(implicit session: Session): ProtectedBranchInfo =
|
||||
def getProtectedBranchInfo(owner: String, repository: String, branch: String)(
|
||||
implicit session: Session
|
||||
): ProtectedBranchInfo =
|
||||
getProtectedBranchInfoOpt(owner, repository, branch).getOrElse(ProtectedBranchInfo.disabled(owner, repository))
|
||||
|
||||
def getProtectedBranchList(owner: String, repository: String)(implicit session: Session): List[String] =
|
||||
ProtectedBranches.filter(_.byRepository(owner, repository)).map(_.branch).list
|
||||
|
||||
def enableBranchProtection(owner: String, repository: String, branch:String, includeAdministrators: Boolean, contexts: Seq[String])
|
||||
(implicit session: Session): Unit = {
|
||||
def enableBranchProtection(
|
||||
owner: String,
|
||||
repository: String,
|
||||
branch: String,
|
||||
includeAdministrators: Boolean,
|
||||
contexts: Seq[String]
|
||||
)(implicit session: Session): Unit = {
|
||||
disableBranchProtection(owner, repository, branch)
|
||||
ProtectedBranches.insert(new ProtectedBranch(owner, repository, branch, includeAdministrators && contexts.nonEmpty))
|
||||
contexts.map{ context =>
|
||||
contexts.map { context =>
|
||||
ProtectedBranchContexts.insert(new ProtectedBranchContext(owner, repository, branch, context))
|
||||
}
|
||||
}
|
||||
|
||||
def disableBranchProtection(owner: String, repository: String, branch:String)(implicit session: Session): Unit =
|
||||
def disableBranchProtection(owner: String, repository: String, branch: String)(implicit session: Session): Unit =
|
||||
ProtectedBranches.filter(_.byPrimaryKey(owner, repository, branch)).delete
|
||||
|
||||
}
|
||||
|
||||
object ProtectedBranchService {
|
||||
|
||||
class ProtectedBranchReceiveHook extends ReceiveHook with ProtectedBranchService with RepositoryService with AccountService {
|
||||
override def preReceive(owner: String, repository: String, receivePack: ReceivePack, command: ReceiveCommand, pusher: String)
|
||||
(implicit session: Session): Option[String] = {
|
||||
class ProtectedBranchReceiveHook
|
||||
extends ReceiveHook
|
||||
with ProtectedBranchService
|
||||
with RepositoryService
|
||||
with AccountService {
|
||||
override def preReceive(
|
||||
owner: String,
|
||||
repository: String,
|
||||
receivePack: ReceivePack,
|
||||
command: ReceiveCommand,
|
||||
pusher: String
|
||||
)(implicit session: Session): Option[String] = {
|
||||
val branch = command.getRefName.stripPrefix("refs/heads/")
|
||||
if(branch != command.getRefName){
|
||||
if (branch != command.getRefName) {
|
||||
val repositoryInfo = getRepository(owner, repository)
|
||||
if(command.getType == ReceiveCommand.Type.DELETE && repositoryInfo.exists(_.repository.defaultBranch == branch)){
|
||||
if (command.getType == ReceiveCommand.Type.DELETE && repositoryInfo.exists(
|
||||
_.repository.defaultBranch == branch
|
||||
)) {
|
||||
Some(s"refusing to delete the branch: ${command.getRefName}.")
|
||||
} else {
|
||||
getProtectedBranchInfo(owner, repository, branch).getStopReason(receivePack.isAllowNonFastForwards, command, pusher)
|
||||
getProtectedBranchInfo(owner, repository, branch).getStopReason(
|
||||
receivePack.isAllowNonFastForwards,
|
||||
command,
|
||||
pusher
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
@@ -62,7 +88,6 @@ object ProtectedBranchService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case class ProtectedBranchInfo(
|
||||
owner: String,
|
||||
repository: String,
|
||||
@@ -78,18 +103,22 @@ object ProtectedBranchService {
|
||||
* Include administrators
|
||||
* Enforce required status checks for repository administrators.
|
||||
*/
|
||||
includeAdministrators: Boolean) extends AccountService with RepositoryService with CommitStatusService {
|
||||
includeAdministrators: Boolean
|
||||
) extends AccountService
|
||||
with RepositoryService
|
||||
with CommitStatusService {
|
||||
|
||||
def isAdministrator(pusher: String)(implicit session: Session): Boolean =
|
||||
pusher == owner || getGroupMembers(owner).exists(gm => gm.userName == pusher && gm.isManager) ||
|
||||
getCollaborators(owner, repository).exists { case (collaborator, isGroup) =>
|
||||
if(collaborator.role == Role.ADMIN.name){
|
||||
if(isGroup){
|
||||
getGroupMembers(collaborator.collaboratorName).exists(gm => gm.userName == pusher)
|
||||
} else {
|
||||
collaborator.collaboratorName == pusher
|
||||
}
|
||||
} else false
|
||||
getCollaborators(owner, repository).exists {
|
||||
case (collaborator, isGroup) =>
|
||||
if (collaborator.role == Role.ADMIN.name) {
|
||||
if (isGroup) {
|
||||
getGroupMembers(collaborator.collaboratorName).exists(gm => gm.userName == pusher)
|
||||
} else {
|
||||
collaborator.collaboratorName == pusher
|
||||
}
|
||||
} else false
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,16 +126,18 @@ object ProtectedBranchService {
|
||||
* Can't be deleted
|
||||
* Can't have changes merged into them until required status checks pass
|
||||
*/
|
||||
def getStopReason(isAllowNonFastForwards: Boolean, command: ReceiveCommand, pusher: String)(implicit session: Session): Option[String] = {
|
||||
if(enabled){
|
||||
def getStopReason(isAllowNonFastForwards: Boolean, command: ReceiveCommand, pusher: String)(
|
||||
implicit session: Session
|
||||
): Option[String] = {
|
||||
if (enabled) {
|
||||
command.getType() match {
|
||||
case ReceiveCommand.Type.UPDATE_NONFASTFORWARD if isAllowNonFastForwards =>
|
||||
Some("Cannot force-push to a protected branch")
|
||||
case ReceiveCommand.Type.UPDATE|ReceiveCommand.Type.UPDATE_NONFASTFORWARD if needStatusCheck(pusher) =>
|
||||
case ReceiveCommand.Type.UPDATE | ReceiveCommand.Type.UPDATE_NONFASTFORWARD if needStatusCheck(pusher) =>
|
||||
unSuccessedContexts(command.getNewId.name) match {
|
||||
case s if s.size == 1 => Some(s"""Required status check "${s.toSeq(0)}" is expected""")
|
||||
case s if s.size >= 1 => Some(s"${s.size} of ${contexts.size} required status checks are expected")
|
||||
case _ => None
|
||||
case _ => None
|
||||
}
|
||||
case ReceiveCommand.Type.DELETE =>
|
||||
Some("Cannot delete a protected branch")
|
||||
@@ -116,11 +147,15 @@ object ProtectedBranchService {
|
||||
None
|
||||
}
|
||||
}
|
||||
def unSuccessedContexts(sha1: String)(implicit session: Session): Set[String] = if(contexts.isEmpty){
|
||||
Set.empty
|
||||
} else {
|
||||
contexts.toSet -- getCommitStatues(owner, repository, sha1).filter(_.state == CommitState.SUCCESS).map(_.context).toSet
|
||||
}
|
||||
def unSuccessedContexts(sha1: String)(implicit session: Session): Set[String] =
|
||||
if (contexts.isEmpty) {
|
||||
Set.empty
|
||||
} else {
|
||||
contexts.toSet -- getCommitStatues(owner, repository, sha1)
|
||||
.filter(_.state == CommitState.SUCCESS)
|
||||
.map(_.context)
|
||||
.toSet
|
||||
}
|
||||
def needStatusCheck(pusher: String)(implicit session: Session): Boolean = pusher match {
|
||||
case _ if !enabled => false
|
||||
case _ if contexts.isEmpty => false
|
||||
@@ -129,7 +164,8 @@ object ProtectedBranchService {
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
object ProtectedBranchInfo{
|
||||
def disabled(owner: String, repository: String): ProtectedBranchInfo = ProtectedBranchInfo(owner, repository, false, Nil, false)
|
||||
object ProtectedBranchInfo {
|
||||
def disabled(owner: String, repository: String): ProtectedBranchInfo =
|
||||
ProtectedBranchInfo(owner, repository, false, Nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,38 +14,47 @@ import gitbucket.core.view.helpers
|
||||
import org.eclipse.jgit.api.Git
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
|
||||
trait PullRequestService { self: IssuesService with CommitsService =>
|
||||
import PullRequestService._
|
||||
|
||||
def getPullRequest(owner: String, repository: String, issueId: Int)
|
||||
(implicit s: Session): Option[(Issue, PullRequest)] =
|
||||
getIssue(owner, repository, issueId.toString).flatMap{ issue =>
|
||||
PullRequests.filter(_.byPrimaryKey(owner, repository, issueId)).firstOption.map{
|
||||
pullreq => (issue, pullreq)
|
||||
def getPullRequest(owner: String, repository: String, issueId: Int)(
|
||||
implicit s: Session
|
||||
): Option[(Issue, PullRequest)] =
|
||||
getIssue(owner, repository, issueId.toString).flatMap { issue =>
|
||||
PullRequests.filter(_.byPrimaryKey(owner, repository, issueId)).firstOption.map { pullreq =>
|
||||
(issue, pullreq)
|
||||
}
|
||||
}
|
||||
|
||||
def updateCommitId(owner: String, repository: String, issueId: Int, commitIdTo: String, commitIdFrom: String)
|
||||
(implicit s: Session): Unit =
|
||||
PullRequests.filter(_.byPrimaryKey(owner, repository, issueId))
|
||||
.map(pr => pr.commitIdTo -> pr.commitIdFrom)
|
||||
.update((commitIdTo, commitIdFrom))
|
||||
|
||||
def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])
|
||||
(implicit s: Session): List[PullRequestCount] =
|
||||
def updateCommitId(owner: String, repository: String, issueId: Int, commitIdTo: String, commitIdFrom: String)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
PullRequests
|
||||
.join(Issues).on { (t1, t2) => t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId) }
|
||||
.filter { case (t1, t2) =>
|
||||
(t2.closed === closed.bind) &&
|
||||
(t1.userName === owner.get.bind, owner.isDefined) &&
|
||||
(t1.repositoryName === repository.get.bind, repository.isDefined)
|
||||
.filter(_.byPrimaryKey(owner, repository, issueId))
|
||||
.map(pr => pr.commitIdTo -> pr.commitIdFrom)
|
||||
.update((commitIdTo, commitIdFrom))
|
||||
|
||||
def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])(
|
||||
implicit s: Session
|
||||
): List[PullRequestCount] =
|
||||
PullRequests
|
||||
.join(Issues)
|
||||
.on { (t1, t2) =>
|
||||
t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
|
||||
}
|
||||
.filter {
|
||||
case (t1, t2) =>
|
||||
(t2.closed === closed.bind) &&
|
||||
(t1.userName === owner.get.bind, owner.isDefined) &&
|
||||
(t1.repositoryName === repository.get.bind, repository.isDefined)
|
||||
}
|
||||
.groupBy { case (t1, t2) => t2.openedUserName }
|
||||
.map { case (userName, t) => userName -> t.length }
|
||||
.sortBy(_._2 desc)
|
||||
.list
|
||||
.map { x => PullRequestCount(x._1, x._2) }
|
||||
.map { x =>
|
||||
PullRequestCount(x._1, x._2)
|
||||
}
|
||||
|
||||
// def getAllPullRequestCountGroupByUser(closed: Boolean, userName: String)(implicit s: Session): List[PullRequestCount] =
|
||||
// PullRequests
|
||||
@@ -65,9 +74,17 @@ trait PullRequestService { self: IssuesService with CommitsService =>
|
||||
// .list
|
||||
// .map { x => PullRequestCount(x._1, x._2) }
|
||||
|
||||
def createPullRequest(originUserName: String, originRepositoryName: String, issueId: Int,
|
||||
originBranch: String, requestUserName: String, requestRepositoryName: String, requestBranch: String,
|
||||
commitIdFrom: String, commitIdTo: String)(implicit s: Session): Unit =
|
||||
def createPullRequest(
|
||||
originUserName: String,
|
||||
originRepositoryName: String,
|
||||
issueId: Int,
|
||||
originBranch: String,
|
||||
requestUserName: String,
|
||||
requestRepositoryName: String,
|
||||
requestBranch: String,
|
||||
commitIdFrom: String,
|
||||
commitIdTo: String
|
||||
)(implicit s: Session): Unit =
|
||||
PullRequests insert PullRequest(
|
||||
originUserName,
|
||||
originRepositoryName,
|
||||
@@ -77,17 +94,23 @@ trait PullRequestService { self: IssuesService with CommitsService =>
|
||||
requestRepositoryName,
|
||||
requestBranch,
|
||||
commitIdFrom,
|
||||
commitIdTo)
|
||||
commitIdTo
|
||||
)
|
||||
|
||||
def getPullRequestsByRequest(userName: String, repositoryName: String, branch: String, closed: Option[Boolean])
|
||||
(implicit s: Session): List[PullRequest] =
|
||||
def getPullRequestsByRequest(userName: String, repositoryName: String, branch: String, closed: Option[Boolean])(
|
||||
implicit s: Session
|
||||
): List[PullRequest] =
|
||||
PullRequests
|
||||
.join(Issues).on { (t1, t2) => t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId) }
|
||||
.filter { case (t1, t2) =>
|
||||
(t1.requestUserName === userName.bind) &&
|
||||
(t1.requestRepositoryName === repositoryName.bind) &&
|
||||
(t1.requestBranch === branch.bind) &&
|
||||
(t2.closed === closed.get.bind, closed.isDefined)
|
||||
.join(Issues)
|
||||
.on { (t1, t2) =>
|
||||
t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
|
||||
}
|
||||
.filter {
|
||||
case (t1, t2) =>
|
||||
(t1.requestUserName === userName.bind) &&
|
||||
(t1.requestRepositoryName === repositoryName.bind) &&
|
||||
(t1.requestBranch === branch.bind) &&
|
||||
(t2.closed === closed.get.bind, closed.isDefined)
|
||||
}
|
||||
.map { case (t1, t2) => t1 }
|
||||
.list
|
||||
@@ -99,19 +122,24 @@ trait PullRequestService { self: IssuesService with CommitsService =>
|
||||
* 2. return if exists pull request to other branch
|
||||
* 2. return None
|
||||
*/
|
||||
def getPullRequestFromBranch(userName: String, repositoryName: String, branch: String, defaultBranch: String)
|
||||
(implicit s: Session): Option[(PullRequest, Issue)] =
|
||||
def getPullRequestFromBranch(userName: String, repositoryName: String, branch: String, defaultBranch: String)(
|
||||
implicit s: Session
|
||||
): Option[(PullRequest, Issue)] =
|
||||
PullRequests
|
||||
.join(Issues).on { (t1, t2) => t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId) }
|
||||
.filter { case (t1, t2) =>
|
||||
(t1.requestUserName === userName.bind) &&
|
||||
(t1.requestRepositoryName === repositoryName.bind) &&
|
||||
(t1.requestBranch === branch.bind) &&
|
||||
(t1.userName === userName.bind) &&
|
||||
(t1.repositoryName === repositoryName.bind) &&
|
||||
(t2.closed === false.bind)
|
||||
.join(Issues)
|
||||
.on { (t1, t2) =>
|
||||
t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
|
||||
}
|
||||
.sortBy{ case (t1, t2) => t1.branch =!= defaultBranch.bind }
|
||||
.filter {
|
||||
case (t1, t2) =>
|
||||
(t1.requestUserName === userName.bind) &&
|
||||
(t1.requestRepositoryName === repositoryName.bind) &&
|
||||
(t1.requestBranch === branch.bind) &&
|
||||
(t1.userName === userName.bind) &&
|
||||
(t1.repositoryName === repositoryName.bind) &&
|
||||
(t2.closed === false.bind)
|
||||
}
|
||||
.sortBy { case (t1, t2) => t1.branch =!= defaultBranch.bind }
|
||||
.firstOption
|
||||
|
||||
/**
|
||||
@@ -119,116 +147,172 @@ trait PullRequestService { self: IssuesService with CommitsService =>
|
||||
*/
|
||||
def updatePullRequests(owner: String, repository: String, branch: String)(implicit s: Session): Unit =
|
||||
getPullRequestsByRequest(owner, repository, branch, Some(false)).foreach { pullreq =>
|
||||
if(Repositories.filter(_.byRepository(pullreq.userName, pullreq.repositoryName)).exists.run){
|
||||
if (Repositories.filter(_.byRepository(pullreq.userName, pullreq.repositoryName)).exists.run) {
|
||||
// Update the git repository
|
||||
val (commitIdTo, commitIdFrom) = JGitUtil.updatePullRequest(
|
||||
pullreq.userName, pullreq.repositoryName, pullreq.branch, pullreq.issueId,
|
||||
pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.requestBranch)
|
||||
pullreq.userName,
|
||||
pullreq.repositoryName,
|
||||
pullreq.branch,
|
||||
pullreq.issueId,
|
||||
pullreq.requestUserName,
|
||||
pullreq.requestRepositoryName,
|
||||
pullreq.requestBranch
|
||||
)
|
||||
|
||||
// Collect comment positions
|
||||
val positions = getCommitComments(pullreq.userName, pullreq.repositoryName, pullreq.commitIdTo, true)
|
||||
.collect {
|
||||
case CommitComment(_, _, _, commentId, _, _, Some(file), None, Some(newLine), _, _, _) => (file, commentId, Right(newLine))
|
||||
case CommitComment(_, _, _, commentId, _, _, Some(file), Some(oldLine), None, _, _, _) => (file, commentId, Left(oldLine))
|
||||
case CommitComment(_, _, _, commentId, _, _, Some(file), None, Some(newLine), _, _, _) =>
|
||||
(file, commentId, Right(newLine))
|
||||
case CommitComment(_, _, _, commentId, _, _, Some(file), Some(oldLine), None, _, _, _) =>
|
||||
(file, commentId, Left(oldLine))
|
||||
}
|
||||
.groupBy { case (file, _, _) => file }
|
||||
.map { case (file, comments) => file ->
|
||||
comments.map { case (_, commentId, lineNumber) => (commentId, lineNumber) }
|
||||
.map {
|
||||
case (file, comments) =>
|
||||
file ->
|
||||
comments.map { case (_, commentId, lineNumber) => (commentId, lineNumber) }
|
||||
}
|
||||
|
||||
// Update comments position
|
||||
updatePullRequestCommentPositions(positions, pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.commitIdTo, commitIdTo)
|
||||
updatePullRequestCommentPositions(
|
||||
positions,
|
||||
pullreq.requestUserName,
|
||||
pullreq.requestRepositoryName,
|
||||
pullreq.commitIdTo,
|
||||
commitIdTo
|
||||
)
|
||||
|
||||
// Update commit id in the PULL_REQUEST table
|
||||
updateCommitId(pullreq.userName, pullreq.repositoryName, pullreq.issueId, commitIdTo, commitIdFrom)
|
||||
}
|
||||
}
|
||||
|
||||
def getPullRequestByRequestCommit(userName: String, repositoryName: String, toBranch:String, fromBranch: String, commitId: String)
|
||||
(implicit s: Session): Option[(PullRequest, Issue)] = {
|
||||
if(toBranch == fromBranch){
|
||||
def getPullRequestByRequestCommit(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
toBranch: String,
|
||||
fromBranch: String,
|
||||
commitId: String
|
||||
)(implicit s: Session): Option[(PullRequest, Issue)] = {
|
||||
if (toBranch == fromBranch) {
|
||||
None
|
||||
} else {
|
||||
PullRequests
|
||||
.join(Issues).on { (t1, t2) => t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId) }
|
||||
.filter { case (t1, t2) =>
|
||||
(t1.userName === userName.bind) &&
|
||||
(t1.repositoryName === repositoryName.bind) &&
|
||||
(t1.branch === toBranch.bind) &&
|
||||
(t1.requestUserName === userName.bind) &&
|
||||
(t1.requestRepositoryName === repositoryName.bind) &&
|
||||
(t1.requestBranch === fromBranch.bind) &&
|
||||
(t1.commitIdTo === commitId.bind)
|
||||
.join(Issues)
|
||||
.on { (t1, t2) =>
|
||||
t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
|
||||
}
|
||||
.filter {
|
||||
case (t1, t2) =>
|
||||
(t1.userName === userName.bind) &&
|
||||
(t1.repositoryName === repositoryName.bind) &&
|
||||
(t1.branch === toBranch.bind) &&
|
||||
(t1.requestUserName === userName.bind) &&
|
||||
(t1.requestRepositoryName === repositoryName.bind) &&
|
||||
(t1.requestBranch === fromBranch.bind) &&
|
||||
(t1.commitIdTo === commitId.bind)
|
||||
}
|
||||
.firstOption
|
||||
}
|
||||
}
|
||||
|
||||
private def updatePullRequestCommentPositions(positions: Map[String, Seq[(Int, Either[Int, Int])]], userName: String, repositoryName: String,
|
||||
oldCommitId: String, newCommitId: String)(implicit s: Session): Unit = {
|
||||
private def updatePullRequestCommentPositions(
|
||||
positions: Map[String, Seq[(Int, Either[Int, Int])]],
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
oldCommitId: String,
|
||||
newCommitId: String
|
||||
)(implicit s: Session): Unit = {
|
||||
|
||||
val (_, diffs) = getRequestCompareInfo(userName, repositoryName, oldCommitId, userName, repositoryName, newCommitId)
|
||||
|
||||
val patchs = positions.map { case (file, _) =>
|
||||
diffs.find(x => x.oldPath == file).map { diff =>
|
||||
(diff.oldContent, diff.newContent) match {
|
||||
case (Some(oldContent), Some(newContent)) => {
|
||||
val oldLines = oldContent.replace("\r\n", "\n").split("\n")
|
||||
val newLines = newContent.replace("\r\n", "\n").split("\n")
|
||||
file -> Option(DiffUtils.diff(oldLines.toList.asJava, newLines.toList.asJava))
|
||||
val patchs = positions.map {
|
||||
case (file, _) =>
|
||||
diffs
|
||||
.find(x => x.oldPath == file)
|
||||
.map { diff =>
|
||||
(diff.oldContent, diff.newContent) match {
|
||||
case (Some(oldContent), Some(newContent)) => {
|
||||
val oldLines = oldContent.replace("\r\n", "\n").split("\n")
|
||||
val newLines = newContent.replace("\r\n", "\n").split("\n")
|
||||
file -> Option(DiffUtils.diff(oldLines.toList.asJava, newLines.toList.asJava))
|
||||
}
|
||||
case _ =>
|
||||
file -> None
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
.getOrElse {
|
||||
file -> None
|
||||
}
|
||||
}.getOrElse {
|
||||
file -> None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
positions.foreach { case (file, comments) =>
|
||||
patchs(file) match {
|
||||
case Some(patch) => file -> comments.foreach { case (commentId, lineNumber) => lineNumber match {
|
||||
case Left(oldLine) => updateCommitCommentPosition(commentId, newCommitId, Some(oldLine), None)
|
||||
case Right(newLine) =>
|
||||
var counter = newLine
|
||||
patch.getDeltas.asScala.filter(_.getOriginal.getPosition < newLine).foreach { delta =>
|
||||
delta.getType match {
|
||||
case Delta.TYPE.CHANGE =>
|
||||
if(delta.getOriginal.getPosition <= newLine - 1 && newLine <= delta.getOriginal.getPosition + delta.getRevised.getLines.size){
|
||||
counter = -1
|
||||
} else {
|
||||
counter = counter + (delta.getRevised.getLines.size - delta.getOriginal.getLines.size)
|
||||
}
|
||||
case Delta.TYPE.INSERT => counter = counter + delta.getRevised.getLines.size
|
||||
case Delta.TYPE.DELETE => counter = counter - delta.getOriginal.getLines.size
|
||||
}
|
||||
positions.foreach {
|
||||
case (file, comments) =>
|
||||
patchs(file) match {
|
||||
case Some(patch) =>
|
||||
file -> comments.foreach {
|
||||
case (commentId, lineNumber) =>
|
||||
lineNumber match {
|
||||
case Left(oldLine) => updateCommitCommentPosition(commentId, newCommitId, Some(oldLine), None)
|
||||
case Right(newLine) =>
|
||||
var counter = newLine
|
||||
patch.getDeltas.asScala.filter(_.getOriginal.getPosition < newLine).foreach { delta =>
|
||||
delta.getType match {
|
||||
case Delta.TYPE.CHANGE =>
|
||||
if (delta.getOriginal.getPosition <= newLine - 1 && newLine <= delta.getOriginal.getPosition + delta.getRevised.getLines.size) {
|
||||
counter = -1
|
||||
} else {
|
||||
counter = counter + (delta.getRevised.getLines.size - delta.getOriginal.getLines.size)
|
||||
}
|
||||
case Delta.TYPE.INSERT => counter = counter + delta.getRevised.getLines.size
|
||||
case Delta.TYPE.DELETE => counter = counter - delta.getOriginal.getLines.size
|
||||
}
|
||||
}
|
||||
if (counter >= 0) {
|
||||
updateCommitCommentPosition(commentId, newCommitId, None, Some(counter))
|
||||
}
|
||||
}
|
||||
}
|
||||
if(counter >= 0){
|
||||
updateCommitCommentPosition(commentId, newCommitId, None, Some(counter))
|
||||
case _ =>
|
||||
comments.foreach {
|
||||
case (commentId, lineNumber) =>
|
||||
lineNumber match {
|
||||
case Right(oldLine) => updateCommitCommentPosition(commentId, newCommitId, Some(oldLine), None)
|
||||
case Left(newLine) => updateCommitCommentPosition(commentId, newCommitId, None, Some(newLine))
|
||||
}
|
||||
}
|
||||
}}
|
||||
case _ => comments.foreach { case (commentId, lineNumber) => lineNumber match {
|
||||
case Right(oldLine) => updateCommitCommentPosition(commentId, newCommitId, Some(oldLine), None)
|
||||
case Left(newLine) => updateCommitCommentPosition(commentId, newCommitId, None, Some(newLine))
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getRequestCompareInfo(userName: String, repositoryName: String, branch: String,
|
||||
requestUserName: String, requestRepositoryName: String, requestCommitId: String): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) =
|
||||
def getRequestCompareInfo(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
branch: String,
|
||||
requestUserName: String,
|
||||
requestRepositoryName: String,
|
||||
requestCommitId: String
|
||||
): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) =
|
||||
using(
|
||||
Git.open(getRepositoryDir(userName, repositoryName)),
|
||||
Git.open(getRepositoryDir(requestUserName, requestRepositoryName))
|
||||
){ (oldGit, newGit) =>
|
||||
) { (oldGit, newGit) =>
|
||||
val oldId = oldGit.getRepository.resolve(branch)
|
||||
val newId = newGit.getRepository.resolve(requestCommitId)
|
||||
|
||||
val commits = newGit.log.addRange(oldId, newId).call.iterator.asScala.map { revCommit =>
|
||||
new CommitInfo(revCommit)
|
||||
}.toList.splitWith { (commit1, commit2) =>
|
||||
helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime)
|
||||
}
|
||||
val commits = newGit.log
|
||||
.addRange(oldId, newId)
|
||||
.call
|
||||
.iterator
|
||||
.asScala
|
||||
.map { revCommit =>
|
||||
new CommitInfo(revCommit)
|
||||
}
|
||||
.toList
|
||||
.splitWith { (commit1, commit2) =>
|
||||
helpers.date(commit1.commitTime) == view.helpers.date(commit2.commitTime)
|
||||
}
|
||||
|
||||
val diffs = JGitUtil.getDiffs(newGit, Some(oldId.getName), newId.getName, true, false)
|
||||
|
||||
@@ -251,22 +335,30 @@ object PullRequestService {
|
||||
hasUpdatePermission: Boolean,
|
||||
needStatusCheck: Boolean,
|
||||
hasMergePermission: Boolean,
|
||||
commitIdTo: String){
|
||||
commitIdTo: String
|
||||
) {
|
||||
|
||||
val hasConflict = conflictMessage.isDefined
|
||||
val statuses: List[CommitStatus] =
|
||||
commitStatues ++ (branchProtection.contexts.toSet -- commitStatues.map(_.context).toSet).map(CommitStatus.pending(branchProtection.owner, branchProtection.repository, _))
|
||||
val hasRequiredStatusProblem = needStatusCheck && branchProtection.contexts.exists(context => statuses.find(_.context == context).map(_.state) != Some(CommitState.SUCCESS))
|
||||
val hasProblem = hasRequiredStatusProblem || hasConflict || (statuses.nonEmpty && CommitState.combine(statuses.map(_.state).toSet) != CommitState.SUCCESS)
|
||||
val canUpdate = branchIsOutOfDate && !hasConflict
|
||||
val canMerge = hasMergePermission && !hasConflict && !hasRequiredStatusProblem
|
||||
lazy val commitStateSummary:(CommitState, String) = {
|
||||
commitStatues ++ (branchProtection.contexts.toSet -- commitStatues.map(_.context).toSet)
|
||||
.map(CommitStatus.pending(branchProtection.owner, branchProtection.repository, _))
|
||||
val hasRequiredStatusProblem = needStatusCheck && branchProtection.contexts.exists(
|
||||
context => statuses.find(_.context == context).map(_.state) != Some(CommitState.SUCCESS)
|
||||
)
|
||||
val hasProblem = hasRequiredStatusProblem || hasConflict || (statuses.nonEmpty && CommitState.combine(
|
||||
statuses.map(_.state).toSet
|
||||
) != CommitState.SUCCESS)
|
||||
val canUpdate = branchIsOutOfDate && !hasConflict
|
||||
val canMerge = hasMergePermission && !hasConflict && !hasRequiredStatusProblem
|
||||
lazy val commitStateSummary: (CommitState, String) = {
|
||||
val stateMap = statuses.groupBy(_.state)
|
||||
val state = CommitState.combine(stateMap.keySet)
|
||||
val summary = stateMap.map{ case (keyState, states) => states.size+" "+keyState.name }.mkString(", ")
|
||||
val summary = stateMap.map { case (keyState, states) => states.size + " " + keyState.name }.mkString(", ")
|
||||
state -> summary
|
||||
}
|
||||
lazy val statusesAndRequired:List[(CommitStatus, Boolean)] = statuses.map{ s => s -> branchProtection.contexts.contains(s.context) }
|
||||
lazy val isAllSuccess = commitStateSummary._1==CommitState.SUCCESS
|
||||
lazy val statusesAndRequired: List[(CommitStatus, Boolean)] = statuses.map { s =>
|
||||
s -> branchProtection.contexts.contains(s.context)
|
||||
}
|
||||
lazy val isAllSuccess = commitStateSummary._1 == CommitState.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,25 @@ import gitbucket.core.model.Profile.dateColumnType
|
||||
trait ReleaseService {
|
||||
self: AccountService with RepositoryService =>
|
||||
|
||||
def createReleaseAsset(owner: String, repository: String, tag: String, fileName: String, label: String, size: Long, loginAccount: Account)(implicit s: Session): Unit = {
|
||||
def createReleaseAsset(
|
||||
owner: String,
|
||||
repository: String,
|
||||
tag: String,
|
||||
fileName: String,
|
||||
label: String,
|
||||
size: Long,
|
||||
loginAccount: Account
|
||||
)(implicit s: Session): Unit = {
|
||||
ReleaseAssets insert ReleaseAsset(
|
||||
userName = owner,
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
tag = tag,
|
||||
fileName = fileName,
|
||||
label = label,
|
||||
size = size,
|
||||
uploader = loginAccount.userName,
|
||||
tag = tag,
|
||||
fileName = fileName,
|
||||
label = label,
|
||||
size = size,
|
||||
uploader = loginAccount.userName,
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate
|
||||
updatedDate = currentDate
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,12 +35,16 @@ trait ReleaseService {
|
||||
ReleaseAssets.filter(x => x.byTag(owner, repository, tag)).list
|
||||
}
|
||||
|
||||
def getReleaseAssetsMap(owner: String, repository: String)(implicit s: Session): Map[ReleaseTag, Seq[ReleaseAsset]] = {
|
||||
def getReleaseAssetsMap(owner: String, repository: String)(
|
||||
implicit s: Session
|
||||
): Map[ReleaseTag, Seq[ReleaseAsset]] = {
|
||||
val releases = getReleases(owner, repository)
|
||||
releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.tag))).toMap
|
||||
}
|
||||
|
||||
def getReleaseAsset(owner: String, repository: String, tag: String, fileId: String)(implicit s: Session): Option[ReleaseAsset] = {
|
||||
def getReleaseAsset(owner: String, repository: String, tag: String, fileId: String)(
|
||||
implicit s: Session
|
||||
): Option[ReleaseAsset] = {
|
||||
ReleaseAssets.filter(x => x.byPrimaryKey(owner, repository, tag, fileId)) firstOption
|
||||
}
|
||||
|
||||
@@ -40,17 +52,23 @@ trait ReleaseService {
|
||||
ReleaseAssets.filter(x => x.byTag(owner, repository, tag)) delete
|
||||
}
|
||||
|
||||
def createRelease(owner: String, repository: String, name: String, content: Option[String], tag: String,
|
||||
loginAccount: Account)(implicit context: Context, s: Session): Int = {
|
||||
def createRelease(
|
||||
owner: String,
|
||||
repository: String,
|
||||
name: String,
|
||||
content: Option[String],
|
||||
tag: String,
|
||||
loginAccount: Account
|
||||
)(implicit context: Context, s: Session): Int = {
|
||||
ReleaseTags insert ReleaseTag(
|
||||
userName = owner,
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
name = name,
|
||||
tag = tag,
|
||||
author = loginAccount.userName,
|
||||
content = content,
|
||||
name = name,
|
||||
tag = tag,
|
||||
author = loginAccount.userName,
|
||||
content = content,
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate
|
||||
updatedDate = currentDate
|
||||
)
|
||||
}
|
||||
|
||||
@@ -73,11 +91,15 @@ trait ReleaseService {
|
||||
// else None
|
||||
// }
|
||||
|
||||
def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])(implicit s: Session): Int = {
|
||||
def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])(
|
||||
implicit s: Session
|
||||
): Int = {
|
||||
ReleaseTags
|
||||
.filter (_.byPrimaryKey(owner, repository, tag))
|
||||
.map { t => (t.name, t.content, t.updatedDate) }
|
||||
.update (title, content, currentDate)
|
||||
.filter(_.byPrimaryKey(owner, repository, tag))
|
||||
.map { t =>
|
||||
(t.name, t.content, t.updatedDate)
|
||||
}
|
||||
.update(title, content, currentDate)
|
||||
}
|
||||
|
||||
def deleteRelease(owner: String, repository: String, tag: String)(implicit s: Session): Unit = {
|
||||
|
||||
@@ -33,7 +33,7 @@ object RepositoryCreationService {
|
||||
|
||||
def endCreation(owner: String, repository: String, error: Option[String]): Unit = {
|
||||
error match {
|
||||
case None => Creating.remove(s"${owner}/${repository}")
|
||||
case None => Creating.remove(s"${owner}/${repository}")
|
||||
case Some(error) => Creating.put(s"${owner}/${repository}", Some(error))
|
||||
}
|
||||
}
|
||||
@@ -45,15 +45,33 @@ object RepositoryCreationService {
|
||||
}
|
||||
|
||||
trait RepositoryCreationService {
|
||||
self: AccountService with RepositoryService with LabelsService with WikiService with ActivityService with PrioritiesService =>
|
||||
self: AccountService
|
||||
with RepositoryService
|
||||
with LabelsService
|
||||
with WikiService
|
||||
with ActivityService
|
||||
with PrioritiesService =>
|
||||
|
||||
def createRepository(loginAccount: Account, owner: String, name: String, description: Option[String],
|
||||
isPrivate: Boolean, createReadme: Boolean): Future[Unit] = {
|
||||
def createRepository(
|
||||
loginAccount: Account,
|
||||
owner: String,
|
||||
name: String,
|
||||
description: Option[String],
|
||||
isPrivate: Boolean,
|
||||
createReadme: Boolean
|
||||
): Future[Unit] = {
|
||||
createRepository(loginAccount, owner, name, description, isPrivate, if (createReadme) "README" else "EMPTY", None)
|
||||
}
|
||||
|
||||
def createRepository(loginAccount: Account, owner: String, name: String, description: Option[String],
|
||||
isPrivate: Boolean, initOption: String, sourceUrl: Option[String]): Future[Unit] = Future {
|
||||
def createRepository(
|
||||
loginAccount: Account,
|
||||
owner: String,
|
||||
name: String,
|
||||
description: Option[String],
|
||||
isPrivate: Boolean,
|
||||
initOption: String,
|
||||
sourceUrl: Option[String]
|
||||
): Future[Unit] = Future {
|
||||
RepositoryCreationService.startCreation(owner, name)
|
||||
try {
|
||||
Database() withTransaction { implicit session =>
|
||||
@@ -68,7 +86,6 @@ trait RepositoryCreationService {
|
||||
}
|
||||
} else None
|
||||
|
||||
|
||||
// Insert to the database at first
|
||||
insertRepository(name, owner, description, isPrivate)
|
||||
|
||||
@@ -106,13 +123,26 @@ trait RepositoryCreationService {
|
||||
"===============\n"
|
||||
}
|
||||
|
||||
builder.add(JGitUtil.createDirCacheEntry("README.md", FileMode.REGULAR_FILE,
|
||||
inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
|
||||
builder.add(
|
||||
JGitUtil.createDirCacheEntry(
|
||||
"README.md",
|
||||
FileMode.REGULAR_FILE,
|
||||
inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))
|
||||
)
|
||||
)
|
||||
}
|
||||
builder.finish()
|
||||
|
||||
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter),
|
||||
Constants.HEAD, loginAccount.fullName, loginAccount.mailAddress, "Initial commit")
|
||||
JGitUtil.createNewCommit(
|
||||
git,
|
||||
inserter,
|
||||
headId,
|
||||
builder.getDirCache.writeTree(inserter),
|
||||
Constants.HEAD,
|
||||
loginAccount.fullName,
|
||||
loginAccount.mailAddress,
|
||||
"Initial commit"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,8 +195,9 @@ trait RepositoryCreationService {
|
||||
// Set default collaborators for the private fork
|
||||
if (repository.repository.isPrivate) {
|
||||
// Copy collaborators from the source repository
|
||||
getCollaborators(repository.owner, repository.name).foreach { case (collaborator, _) =>
|
||||
addCollaborator(accountName, repository.name, collaborator.collaboratorName, collaborator.role)
|
||||
getCollaborators(repository.owner, repository.name).foreach {
|
||||
case (collaborator, _) =>
|
||||
addCollaborator(accountName, repository.name, collaborator.collaboratorName, collaborator.role)
|
||||
}
|
||||
// Register an owner of the source repository as a collaborator
|
||||
addCollaborator(accountName, repository.name, repository.owner, Role.ADMIN.name)
|
||||
@@ -180,11 +211,14 @@ trait RepositoryCreationService {
|
||||
// clone repository actually
|
||||
JGitUtil.cloneRepository(
|
||||
getRepositoryDir(repository.owner, repository.name),
|
||||
FileUtil.deleteIfExists(getRepositoryDir(accountName, repository.name)))
|
||||
FileUtil.deleteIfExists(getRepositoryDir(accountName, repository.name))
|
||||
)
|
||||
|
||||
// Create Wiki repository
|
||||
JGitUtil.cloneRepository(getWikiRepositoryDir(repository.owner, repository.name),
|
||||
FileUtil.deleteIfExists(getWikiRepositoryDir(accountName, repository.name)))
|
||||
JGitUtil.cloneRepository(
|
||||
getWikiRepositoryDir(repository.owner, repository.name),
|
||||
FileUtil.deleteIfExists(getWikiRepositoryDir(accountName, repository.name))
|
||||
)
|
||||
|
||||
// Copy LFS files
|
||||
val lfsDir = getLfsDir(repository.owner, repository.name)
|
||||
@@ -216,10 +250,36 @@ trait RepositoryCreationService {
|
||||
}
|
||||
|
||||
def insertDefaultPriorities(userName: String, repositoryName: String)(implicit s: Session): Unit = {
|
||||
createPriority(userName, repositoryName, "highest", Some("All defects at this priority must be fixed before any public product is delivered."), "fc2929")
|
||||
createPriority(userName, repositoryName, "very high", Some("Issues must be addressed before a final product is delivered."), "fc5629")
|
||||
createPriority(userName, repositoryName, "high", Some("Issues should be addressed before a final product is delivered. If the issue cannot be resolved before delivery, it should be prioritized for the next release."), "fc9629")
|
||||
createPriority(userName, repositoryName, "important", Some("Issues can be shipped with a final product, but should be reviewed before the next release."), "fccd29")
|
||||
createPriority(
|
||||
userName,
|
||||
repositoryName,
|
||||
"highest",
|
||||
Some("All defects at this priority must be fixed before any public product is delivered."),
|
||||
"fc2929"
|
||||
)
|
||||
createPriority(
|
||||
userName,
|
||||
repositoryName,
|
||||
"very high",
|
||||
Some("Issues must be addressed before a final product is delivered."),
|
||||
"fc5629"
|
||||
)
|
||||
createPriority(
|
||||
userName,
|
||||
repositoryName,
|
||||
"high",
|
||||
Some(
|
||||
"Issues should be addressed before a final product is delivered. If the issue cannot be resolved before delivery, it should be prioritized for the next release."
|
||||
),
|
||||
"fc9629"
|
||||
)
|
||||
createPriority(
|
||||
userName,
|
||||
repositoryName,
|
||||
"important",
|
||||
Some("Issues can be shipped with a final product, but should be reviewed before the next release."),
|
||||
"fccd29"
|
||||
)
|
||||
createPriority(userName, repositoryName, "default", Some("Default."), "acacac")
|
||||
|
||||
setDefaultPriority(userName, repositoryName, getPriority(userName, repositoryName, "default").map(_.priorityId))
|
||||
|
||||
@@ -17,69 +17,72 @@ trait RepositorySearchService { self: IssuesService =>
|
||||
def countIssues(owner: String, repository: String, query: String)(implicit session: Session): Int =
|
||||
searchIssuesByKeyword(owner, repository, query).length
|
||||
|
||||
def searchIssues(owner: String, repository: String, query: String)(implicit session: Session): List[IssueSearchResult] =
|
||||
searchIssuesByKeyword(owner, repository, query).map { case (issue, commentCount, content) =>
|
||||
IssueSearchResult(
|
||||
issue.issueId,
|
||||
issue.isPullRequest,
|
||||
issue.title,
|
||||
issue.openedUserName,
|
||||
issue.registeredDate,
|
||||
commentCount,
|
||||
getHighlightText(content, query)._1)
|
||||
def searchIssues(owner: String, repository: String, query: String)(
|
||||
implicit session: Session
|
||||
): List[IssueSearchResult] =
|
||||
searchIssuesByKeyword(owner, repository, query).map {
|
||||
case (issue, commentCount, content) =>
|
||||
IssueSearchResult(
|
||||
issue.issueId,
|
||||
issue.isPullRequest,
|
||||
issue.title,
|
||||
issue.openedUserName,
|
||||
issue.registeredDate,
|
||||
commentCount,
|
||||
getHighlightText(content, query)._1
|
||||
)
|
||||
}
|
||||
|
||||
def countFiles(owner: String, repository: String, query: String): Int =
|
||||
using(Git.open(getRepositoryDir(owner, repository))){ git =>
|
||||
if(JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||
}
|
||||
|
||||
def searchFiles(owner: String, repository: String, query: String): List[FileSearchResult] =
|
||||
using(Git.open(getRepositoryDir(owner, repository))){ git =>
|
||||
if(JGitUtil.isEmpty(git)){
|
||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||
if (JGitUtil.isEmpty(git)) {
|
||||
Nil
|
||||
} else {
|
||||
val files = searchRepositoryFiles(git, query)
|
||||
val commits = JGitUtil.getLatestCommitFromPaths(git, files.map(_._1), "HEAD")
|
||||
files.map { case (path, text) =>
|
||||
val (highlightText, lineNumber) = getHighlightText(text, query)
|
||||
FileSearchResult(
|
||||
path,
|
||||
commits(path).getCommitterIdent.getWhen,
|
||||
highlightText,
|
||||
lineNumber)
|
||||
files.map {
|
||||
case (path, text) =>
|
||||
val (highlightText, lineNumber) = getHighlightText(text, query)
|
||||
FileSearchResult(path, commits(path).getCommitterIdent.getWhen, highlightText, lineNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def countWikiPages(owner: String, repository: String, query: String): Int =
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
||||
if(JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||
}
|
||||
|
||||
def searchWikiPages(owner: String, repository: String, query: String): List[FileSearchResult] =
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
||||
if(JGitUtil.isEmpty(git)){
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
if (JGitUtil.isEmpty(git)) {
|
||||
Nil
|
||||
} else {
|
||||
val files = searchRepositoryFiles(git, query)
|
||||
val commits = JGitUtil.getLatestCommitFromPaths(git, files.map(_._1), "HEAD")
|
||||
files.map { case (path, text) =>
|
||||
val (highlightText, lineNumber) = getHighlightText(text, query)
|
||||
FileSearchResult(
|
||||
path.stripSuffix(".md"),
|
||||
commits(path).getCommitterIdent.getWhen,
|
||||
highlightText,
|
||||
lineNumber)
|
||||
files.map {
|
||||
case (path, text) =>
|
||||
val (highlightText, lineNumber) = getHighlightText(text, query)
|
||||
FileSearchResult(
|
||||
path.stripSuffix(".md"),
|
||||
commits(path).getCommitterIdent.getWhen,
|
||||
highlightText,
|
||||
lineNumber
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def searchRepositoryFiles(git: Git, query: String): List[(String, String)] = {
|
||||
val revWalk = new RevWalk(git.getRepository)
|
||||
val objectId = git.getRepository.resolve("HEAD")
|
||||
val revWalk = new RevWalk(git.getRepository)
|
||||
val objectId = git.getRepository.resolve("HEAD")
|
||||
val revCommit = revWalk.parseCommit(objectId)
|
||||
val treeWalk = new TreeWalk(git.getRepository)
|
||||
val treeWalk = new TreeWalk(git.getRepository)
|
||||
treeWalk.setRecursive(true)
|
||||
treeWalk.addTree(revCommit.getTree)
|
||||
|
||||
@@ -88,13 +91,13 @@ trait RepositorySearchService { self: IssuesService =>
|
||||
|
||||
while (treeWalk.next()) {
|
||||
val mode = treeWalk.getFileMode(0)
|
||||
if(mode == FileMode.REGULAR_FILE || mode == FileMode.EXECUTABLE_FILE){
|
||||
if (mode == FileMode.REGULAR_FILE || mode == FileMode.EXECUTABLE_FILE) {
|
||||
JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).foreach { bytes =>
|
||||
if(FileUtil.isText(bytes)){
|
||||
val text = StringUtil.convertFromByteArray(bytes)
|
||||
if (FileUtil.isText(bytes)) {
|
||||
val text = StringUtil.convertFromByteArray(bytes)
|
||||
val lowerText = text.toLowerCase
|
||||
val indices = keywords.map(lowerText.indexOf _)
|
||||
if(!indices.exists(_ < 0)){
|
||||
val indices = keywords.map(lowerText.indexOf _)
|
||||
if (!indices.exists(_ < 0)) {
|
||||
list.append((treeWalk.getPathString, text))
|
||||
}
|
||||
}
|
||||
@@ -111,28 +114,29 @@ trait RepositorySearchService { self: IssuesService =>
|
||||
|
||||
object RepositorySearchService {
|
||||
|
||||
val CodeLimit = 10
|
||||
val CodeLimit = 10
|
||||
val IssueLimit = 10
|
||||
|
||||
def getHighlightText(content: String, query: String): (String, Int) = {
|
||||
val keywords = StringUtil.splitWords(query.toLowerCase)
|
||||
val keywords = StringUtil.splitWords(query.toLowerCase)
|
||||
val lowerText = content.toLowerCase
|
||||
val indices = keywords.map(lowerText.indexOf _)
|
||||
val indices = keywords.map(lowerText.indexOf _)
|
||||
|
||||
if(!indices.exists(_ < 0)){
|
||||
if (!indices.exists(_ < 0)) {
|
||||
val lineNumber = content.substring(0, indices.min).split("\n").size - 1
|
||||
val highlightText = StringUtil.escapeHtml(content.split("\n").drop(lineNumber).take(5).mkString("\n"))
|
||||
.replaceAll("(?i)(" + keywords.map("\\Q" + _ + "\\E").mkString("|") + ")",
|
||||
"<span class=\"highlight\">$1</span>")
|
||||
val highlightText = StringUtil
|
||||
.escapeHtml(content.split("\n").drop(lineNumber).take(5).mkString("\n"))
|
||||
.replaceAll(
|
||||
"(?i)(" + keywords.map("\\Q" + _ + "\\E").mkString("|") + ")",
|
||||
"<span class=\"highlight\">$1</span>"
|
||||
)
|
||||
(highlightText, lineNumber + 1)
|
||||
} else {
|
||||
(content.split("\n").take(5).mkString("\n"), 1)
|
||||
}
|
||||
}
|
||||
|
||||
case class SearchResult(
|
||||
files : List[(String, String)],
|
||||
issues: List[(Issue, Int, String)])
|
||||
case class SearchResult(files: List[(String, String)], issues: List[(Issue, Int, String)])
|
||||
|
||||
case class IssueSearchResult(
|
||||
issueId: Int,
|
||||
@@ -141,12 +145,14 @@ object RepositorySearchService {
|
||||
openedUserName: String,
|
||||
registeredDate: java.util.Date,
|
||||
commentCount: Int,
|
||||
highlightText: String)
|
||||
highlightText: String
|
||||
)
|
||||
|
||||
case class FileSearchResult(
|
||||
path: String,
|
||||
lastModified: java.util.Date,
|
||||
highlightText: String,
|
||||
highlightLineNumber: Int)
|
||||
path: String,
|
||||
lastModified: java.util.Date,
|
||||
highlightText: String,
|
||||
highlightLineNumber: Int
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@@ -23,188 +23,276 @@ trait RepositoryService { self: AccountService =>
|
||||
* @param originRepositoryName specify for the forked repository. (default is None)
|
||||
* @param originUserName specify for the forked repository. (default is None)
|
||||
*/
|
||||
def insertRepository(repositoryName: String, userName: String, description: Option[String], isPrivate: Boolean,
|
||||
originRepositoryName: Option[String] = None, originUserName: Option[String] = None,
|
||||
parentRepositoryName: Option[String] = None, parentUserName: Option[String] = None)
|
||||
(implicit s: Session): Unit = {
|
||||
def insertRepository(
|
||||
repositoryName: String,
|
||||
userName: String,
|
||||
description: Option[String],
|
||||
isPrivate: Boolean,
|
||||
originRepositoryName: Option[String] = None,
|
||||
originUserName: Option[String] = None,
|
||||
parentRepositoryName: Option[String] = None,
|
||||
parentUserName: Option[String] = None
|
||||
)(implicit s: Session): Unit = {
|
||||
Repositories insert
|
||||
Repository(
|
||||
userName = userName,
|
||||
repositoryName = repositoryName,
|
||||
isPrivate = isPrivate,
|
||||
description = description,
|
||||
defaultBranch = "master",
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate,
|
||||
lastActivityDate = currentDate,
|
||||
originUserName = originUserName,
|
||||
userName = userName,
|
||||
repositoryName = repositoryName,
|
||||
isPrivate = isPrivate,
|
||||
description = description,
|
||||
defaultBranch = "master",
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate,
|
||||
lastActivityDate = currentDate,
|
||||
originUserName = originUserName,
|
||||
originRepositoryName = originRepositoryName,
|
||||
parentUserName = parentUserName,
|
||||
parentUserName = parentUserName,
|
||||
parentRepositoryName = parentRepositoryName,
|
||||
options = RepositoryOptions(
|
||||
issuesOption = "PUBLIC", // TODO DISABLE for the forked repository?
|
||||
externalIssuesUrl = None,
|
||||
wikiOption = "PUBLIC", // TODO DISABLE for the forked repository?
|
||||
externalWikiUrl = None,
|
||||
allowFork = true,
|
||||
mergeOptions = "merge-commit,squash,rebase",
|
||||
defaultMergeOption = "merge-commit"
|
||||
options = RepositoryOptions(
|
||||
issuesOption = "PUBLIC", // TODO DISABLE for the forked repository?
|
||||
externalIssuesUrl = None,
|
||||
wikiOption = "PUBLIC", // TODO DISABLE for the forked repository?
|
||||
externalWikiUrl = None,
|
||||
allowFork = true,
|
||||
mergeOptions = "merge-commit,squash,rebase",
|
||||
defaultMergeOption = "merge-commit"
|
||||
)
|
||||
)
|
||||
|
||||
IssueId insert (userName, repositoryName, 0)
|
||||
}
|
||||
|
||||
def renameRepository(oldUserName: String, oldRepositoryName: String, newUserName: String, newRepositoryName: String)
|
||||
(implicit s: Session): Unit = {
|
||||
def renameRepository(oldUserName: String, oldRepositoryName: String, newUserName: String, newRepositoryName: String)(
|
||||
implicit s: Session
|
||||
): Unit = {
|
||||
getAccountByUserName(newUserName).foreach { account =>
|
||||
(Repositories filter { t => t.byRepository(oldUserName, oldRepositoryName) } firstOption).map { repository =>
|
||||
(Repositories filter { t =>
|
||||
t.byRepository(oldUserName, oldRepositoryName)
|
||||
} firstOption).map { repository =>
|
||||
Repositories insert repository.copy(userName = newUserName, repositoryName = newRepositoryName)
|
||||
|
||||
val webHooks = RepositoryWebHooks .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val webHookEvents = RepositoryWebHookEvents.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val milestones = Milestones .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueId = IssueId .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issues = Issues .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val pullRequests = PullRequests .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val labels = Labels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val priorities = Priorities .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueComments = IssueComments .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueLabels = IssueLabels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val commitComments = CommitComments .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val commitStatuses = CommitStatuses .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val collaborators = Collaborators .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val protectedBranches = ProtectedBranches .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val protectedBranchContexts = ProtectedBranchContexts.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val deployKeys = DeployKeys .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val releases = ReleaseTags .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val releaseAssets = ReleaseAssets .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val webHooks = RepositoryWebHooks.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val webHookEvents = RepositoryWebHookEvents.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val milestones = Milestones.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueId = IssueId.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issues = Issues.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val pullRequests = PullRequests.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val labels = Labels.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val priorities = Priorities.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueComments = IssueComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val issueLabels = IssueLabels.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val commitComments = CommitComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val commitStatuses = CommitStatuses.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val collaborators = Collaborators.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val protectedBranches = ProtectedBranches.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val protectedBranchContexts =
|
||||
ProtectedBranchContexts.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val deployKeys = DeployKeys.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val releases = ReleaseTags.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
val releaseAssets = ReleaseAssets.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||
|
||||
Repositories.filter { t =>
|
||||
(t.originUserName === oldUserName.bind) && (t.originRepositoryName === oldRepositoryName.bind)
|
||||
}.map { t => t.originUserName -> t.originRepositoryName }.update(newUserName, newRepositoryName)
|
||||
Repositories
|
||||
.filter { t =>
|
||||
(t.originUserName === oldUserName.bind) && (t.originRepositoryName === oldRepositoryName.bind)
|
||||
}
|
||||
.map { t =>
|
||||
t.originUserName -> t.originRepositoryName
|
||||
}
|
||||
.update(newUserName, newRepositoryName)
|
||||
|
||||
Repositories.filter { t =>
|
||||
(t.parentUserName === oldUserName.bind) && (t.parentRepositoryName === oldRepositoryName.bind)
|
||||
}.map { t => t.parentUserName -> t.parentRepositoryName }.update(newUserName, newRepositoryName)
|
||||
Repositories
|
||||
.filter { t =>
|
||||
(t.parentUserName === oldUserName.bind) && (t.parentRepositoryName === oldRepositoryName.bind)
|
||||
}
|
||||
.map { t =>
|
||||
t.parentUserName -> t.parentRepositoryName
|
||||
}
|
||||
.update(newUserName, newRepositoryName)
|
||||
|
||||
// Updates activity fk before deleting repository because activity is sorted by activityId
|
||||
// and it can't be changed by deleting-and-inserting record.
|
||||
Activities.filter(_.byRepository(oldUserName, oldRepositoryName)).list.foreach { activity =>
|
||||
Activities.filter(_.activityId === activity.activityId.bind)
|
||||
.map(x => (x.userName, x.repositoryName)).update(newUserName, newRepositoryName)
|
||||
Activities
|
||||
.filter(_.activityId === activity.activityId.bind)
|
||||
.map(x => (x.userName, x.repositoryName))
|
||||
.update(newUserName, newRepositoryName)
|
||||
}
|
||||
|
||||
deleteRepository(oldUserName, oldRepositoryName)
|
||||
|
||||
RepositoryWebHooks .insertAll(webHooks .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
RepositoryWebHookEvents.insertAll(webHookEvents .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
Milestones .insertAll(milestones .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
Priorities .insertAll(priorities .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
IssueId .insertAll(issueId .map(_.copy(_1 = newUserName, _2 = newRepositoryName)) :_*)
|
||||
RepositoryWebHooks.insertAll(
|
||||
webHooks.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
RepositoryWebHookEvents.insertAll(
|
||||
webHookEvents.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
Milestones.insertAll(milestones.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
Priorities.insertAll(priorities.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
IssueId.insertAll(issueId.map(_.copy(_1 = newUserName, _2 = newRepositoryName)): _*)
|
||||
|
||||
val newMilestones = Milestones.filter(_.byRepository(newUserName, newRepositoryName)).list
|
||||
val newPriorities = Priorities.filter(_.byRepository(newUserName, newRepositoryName)).list
|
||||
Issues.insertAll(issues.map { x => x.copy(
|
||||
userName = newUserName,
|
||||
repositoryName = newRepositoryName,
|
||||
milestoneId = x.milestoneId.map { id =>
|
||||
newMilestones.find(_.title == milestones.find(_.milestoneId == id).get.title).get.milestoneId
|
||||
},
|
||||
priorityId = x.priorityId.map { id =>
|
||||
newPriorities.find(_.priorityName == priorities.find(_.priorityId == id).get.priorityName).get.priorityId
|
||||
}
|
||||
)} :_*)
|
||||
Issues.insertAll(issues.map { x =>
|
||||
x.copy(
|
||||
userName = newUserName,
|
||||
repositoryName = newRepositoryName,
|
||||
milestoneId = x.milestoneId.map { id =>
|
||||
newMilestones.find(_.title == milestones.find(_.milestoneId == id).get.title).get.milestoneId
|
||||
},
|
||||
priorityId = x.priorityId.map { id =>
|
||||
newPriorities.find(_.priorityName == priorities.find(_.priorityId == id).get.priorityName).get.priorityId
|
||||
}
|
||||
)
|
||||
}: _*)
|
||||
|
||||
PullRequests .insertAll(pullRequests .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
IssueComments .insertAll(issueComments .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
Labels .insertAll(labels .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
CommitComments .insertAll(commitComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
CommitStatuses .insertAll(commitStatuses.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
ProtectedBranches .insertAll(protectedBranches.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
ProtectedBranchContexts.insertAll(protectedBranchContexts.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
DeployKeys .insertAll(deployKeys .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
ReleaseTags .insertAll(releases .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
ReleaseAssets .insertAll(releaseAssets .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
PullRequests.insertAll(pullRequests.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
IssueComments.insertAll(
|
||||
issueComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
Labels.insertAll(labels.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
CommitComments.insertAll(
|
||||
commitComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
CommitStatuses.insertAll(
|
||||
commitStatuses.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
ProtectedBranches.insertAll(
|
||||
protectedBranches.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
ProtectedBranchContexts.insertAll(
|
||||
protectedBranchContexts.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
DeployKeys.insertAll(deployKeys.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
ReleaseTags.insertAll(releases.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*)
|
||||
ReleaseAssets.insertAll(
|
||||
releaseAssets.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
|
||||
// Update source repository of pull requests
|
||||
PullRequests.filter { t =>
|
||||
(t.requestUserName === oldUserName.bind) && (t.requestRepositoryName === oldRepositoryName.bind)
|
||||
}.map { t => t.requestUserName -> t.requestRepositoryName }.update(newUserName, newRepositoryName)
|
||||
PullRequests
|
||||
.filter { t =>
|
||||
(t.requestUserName === oldUserName.bind) && (t.requestRepositoryName === oldRepositoryName.bind)
|
||||
}
|
||||
.map { t =>
|
||||
t.requestUserName -> t.requestRepositoryName
|
||||
}
|
||||
.update(newUserName, newRepositoryName)
|
||||
|
||||
// Convert labelId
|
||||
val oldLabelMap = labels.map(x => (x.labelId, x.labelName)).toMap
|
||||
val newLabelMap = Labels.filter(_.byRepository(newUserName, newRepositoryName)).map(x => (x.labelName, x.labelId)).list.toMap
|
||||
IssueLabels.insertAll(issueLabels.map(x => x.copy(
|
||||
labelId = newLabelMap(oldLabelMap(x.labelId)),
|
||||
userName = newUserName,
|
||||
repositoryName = newRepositoryName
|
||||
)) :_*)
|
||||
val newLabelMap =
|
||||
Labels.filter(_.byRepository(newUserName, newRepositoryName)).map(x => (x.labelName, x.labelId)).list.toMap
|
||||
IssueLabels.insertAll(
|
||||
issueLabels.map(
|
||||
x =>
|
||||
x.copy(
|
||||
labelId = newLabelMap(oldLabelMap(x.labelId)),
|
||||
userName = newUserName,
|
||||
repositoryName = newRepositoryName
|
||||
)
|
||||
): _*
|
||||
)
|
||||
|
||||
// TODO Drop transferred owner from collaborators?
|
||||
Collaborators.insertAll(collaborators.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
Collaborators.insertAll(
|
||||
collaborators.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)): _*
|
||||
)
|
||||
|
||||
// Update activity messages
|
||||
Activities.filter { t =>
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}]%") ||
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}#%") ||
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}@%")
|
||||
}.map { t => t.activityId -> t.message }.list.foreach { case (activityId, message) =>
|
||||
Activities.filter(_.activityId === activityId.bind).map(_.message).update(
|
||||
message
|
||||
.replace(s"[repo:${oldUserName}/${oldRepositoryName}]" ,s"[repo:${newUserName}/${newRepositoryName}]")
|
||||
.replace(s"[branch:${oldUserName}/${oldRepositoryName}#" ,s"[branch:${newUserName}/${newRepositoryName}#")
|
||||
.replace(s"[tag:${oldUserName}/${oldRepositoryName}#" ,s"[tag:${newUserName}/${newRepositoryName}#")
|
||||
.replace(s"[pullreq:${oldUserName}/${oldRepositoryName}#",s"[pullreq:${newUserName}/${newRepositoryName}#")
|
||||
.replace(s"[issue:${oldUserName}/${oldRepositoryName}#" ,s"[issue:${newUserName}/${newRepositoryName}#")
|
||||
.replace(s"[commit:${oldUserName}/${oldRepositoryName}@" ,s"[commit:${newUserName}/${newRepositoryName}@")
|
||||
)
|
||||
}
|
||||
Activities
|
||||
.filter { t =>
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}]%") ||
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}#%") ||
|
||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}@%")
|
||||
}
|
||||
.map { t =>
|
||||
t.activityId -> t.message
|
||||
}
|
||||
.list
|
||||
.foreach {
|
||||
case (activityId, message) =>
|
||||
Activities
|
||||
.filter(_.activityId === activityId.bind)
|
||||
.map(_.message)
|
||||
.update(
|
||||
message
|
||||
.replace(
|
||||
s"[repo:${oldUserName}/${oldRepositoryName}]",
|
||||
s"[repo:${newUserName}/${newRepositoryName}]"
|
||||
)
|
||||
.replace(
|
||||
s"[branch:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[branch:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(s"[tag:${oldUserName}/${oldRepositoryName}#", s"[tag:${newUserName}/${newRepositoryName}#")
|
||||
.replace(
|
||||
s"[pullreq:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[pullreq:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(
|
||||
s"[issue:${oldUserName}/${oldRepositoryName}#",
|
||||
s"[issue:${newUserName}/${newRepositoryName}#"
|
||||
)
|
||||
.replace(
|
||||
s"[commit:${oldUserName}/${oldRepositoryName}@",
|
||||
s"[commit:${newUserName}/${newRepositoryName}@"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def deleteRepository(userName: String, repositoryName: String)(implicit s: Session): Unit = {
|
||||
Activities .filter(_.byRepository(userName, repositoryName)).delete
|
||||
Collaborators .filter(_.byRepository(userName, repositoryName)).delete
|
||||
CommitComments .filter(_.byRepository(userName, repositoryName)).delete
|
||||
IssueLabels .filter(_.byRepository(userName, repositoryName)).delete
|
||||
Labels .filter(_.byRepository(userName, repositoryName)).delete
|
||||
IssueComments .filter(_.byRepository(userName, repositoryName)).delete
|
||||
PullRequests .filter(_.byRepository(userName, repositoryName)).delete
|
||||
Issues .filter(_.byRepository(userName, repositoryName)).delete
|
||||
Priorities .filter(_.byRepository(userName, repositoryName)).delete
|
||||
IssueId .filter(_.byRepository(userName, repositoryName)).delete
|
||||
Milestones .filter(_.byRepository(userName, repositoryName)).delete
|
||||
RepositoryWebHooks .filter(_.byRepository(userName, repositoryName)).delete
|
||||
RepositoryWebHookEvents .filter(_.byRepository(userName, repositoryName)).delete
|
||||
DeployKeys .filter(_.byRepository(userName, repositoryName)).delete
|
||||
ReleaseAssets .filter(_.byRepository(userName, repositoryName)).delete
|
||||
ReleaseTags .filter(_.byRepository(userName, repositoryName)).delete
|
||||
Repositories .filter(_.byRepository(userName, repositoryName)).delete
|
||||
Activities.filter(_.byRepository(userName, repositoryName)).delete
|
||||
Collaborators.filter(_.byRepository(userName, repositoryName)).delete
|
||||
CommitComments.filter(_.byRepository(userName, repositoryName)).delete
|
||||
IssueLabels.filter(_.byRepository(userName, repositoryName)).delete
|
||||
Labels.filter(_.byRepository(userName, repositoryName)).delete
|
||||
IssueComments.filter(_.byRepository(userName, repositoryName)).delete
|
||||
PullRequests.filter(_.byRepository(userName, repositoryName)).delete
|
||||
Issues.filter(_.byRepository(userName, repositoryName)).delete
|
||||
Priorities.filter(_.byRepository(userName, repositoryName)).delete
|
||||
IssueId.filter(_.byRepository(userName, repositoryName)).delete
|
||||
Milestones.filter(_.byRepository(userName, repositoryName)).delete
|
||||
RepositoryWebHooks.filter(_.byRepository(userName, repositoryName)).delete
|
||||
RepositoryWebHookEvents.filter(_.byRepository(userName, repositoryName)).delete
|
||||
DeployKeys.filter(_.byRepository(userName, repositoryName)).delete
|
||||
ReleaseAssets.filter(_.byRepository(userName, repositoryName)).delete
|
||||
ReleaseTags.filter(_.byRepository(userName, repositoryName)).delete
|
||||
Repositories.filter(_.byRepository(userName, repositoryName)).delete
|
||||
|
||||
// Update ORIGIN_USER_NAME and ORIGIN_REPOSITORY_NAME
|
||||
Repositories
|
||||
.filter { x => (x.originUserName === userName.bind) && (x.originRepositoryName === repositoryName.bind) }
|
||||
.map { x => (x.userName, x.repositoryName) }
|
||||
.filter { x =>
|
||||
(x.originUserName === userName.bind) && (x.originRepositoryName === repositoryName.bind)
|
||||
}
|
||||
.map { x =>
|
||||
(x.userName, x.repositoryName)
|
||||
}
|
||||
.list
|
||||
.foreach { case (userName, repositoryName) =>
|
||||
Repositories
|
||||
.filter(_.byRepository(userName, repositoryName))
|
||||
.map(x => (x.originUserName?, x.originRepositoryName?))
|
||||
.update(None, None)
|
||||
.foreach {
|
||||
case (userName, repositoryName) =>
|
||||
Repositories
|
||||
.filter(_.byRepository(userName, repositoryName))
|
||||
.map(x => (x.originUserName ?, x.originRepositoryName ?))
|
||||
.update(None, None)
|
||||
}
|
||||
|
||||
// Update PARENT_USER_NAME and PARENT_REPOSITORY_NAME
|
||||
Repositories
|
||||
.filter { x => (x.parentUserName === userName.bind) && (x.parentRepositoryName === repositoryName.bind) }
|
||||
.map { x => (x.userName, x.repositoryName) }
|
||||
.filter { x =>
|
||||
(x.parentUserName === userName.bind) && (x.parentRepositoryName === repositoryName.bind)
|
||||
}
|
||||
.map { x =>
|
||||
(x.userName, x.repositoryName)
|
||||
}
|
||||
.list
|
||||
.foreach { case (userName, repositoryName) =>
|
||||
Repositories
|
||||
.filter(_.byRepository(userName, repositoryName))
|
||||
.map(x => (x.parentUserName?, x.parentRepositoryName?))
|
||||
.update(None, None)
|
||||
.foreach {
|
||||
case (userName, repositoryName) =>
|
||||
Repositories
|
||||
.filter(_.byRepository(userName, repositoryName))
|
||||
.map(x => (x.parentUserName ?, x.parentRepositoryName ?))
|
||||
.update(None, None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +303,7 @@ trait RepositoryService { self: AccountService =>
|
||||
* @return the list of repository names
|
||||
*/
|
||||
def getRepositoryNamesOfUser(userName: String)(implicit s: Session): List[String] =
|
||||
Repositories filter(_.userName === userName.bind) map (_.repositoryName) list
|
||||
Repositories filter (_.userName === userName.bind) map (_.repositoryName) list
|
||||
|
||||
/**
|
||||
* Returns the specified repository information.
|
||||
@@ -225,11 +313,16 @@ trait RepositoryService { self: AccountService =>
|
||||
* @return the repository information
|
||||
*/
|
||||
def getRepository(userName: String, repositoryName: String)(implicit s: Session): Option[RepositoryInfo] = {
|
||||
(Repositories filter { t => t.byRepository(userName, repositoryName) } firstOption) map { repository =>
|
||||
(Repositories filter { t =>
|
||||
t.byRepository(userName, repositoryName)
|
||||
} firstOption) map { repository =>
|
||||
// for getting issue count and pull request count
|
||||
val issues = Issues.filter { t =>
|
||||
t.byRepository(repository.userName, repository.repositoryName) && (t.closed === false.bind)
|
||||
}.map(_.pullRequest).list
|
||||
val issues = Issues
|
||||
.filter { t =>
|
||||
t.byRepository(repository.userName, repository.repositoryName) && (t.closed === false.bind)
|
||||
}
|
||||
.map(_.pullRequest)
|
||||
.list
|
||||
|
||||
new RepositoryInfo(
|
||||
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName),
|
||||
@@ -240,7 +333,8 @@ trait RepositoryService { self: AccountService =>
|
||||
repository.originUserName.getOrElse(repository.userName),
|
||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||
),
|
||||
getRepositoryManagers(repository.userName))
|
||||
getRepositoryManagers(repository.userName)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,45 +346,66 @@ trait RepositoryService { self: AccountService =>
|
||||
* @return the repository information list
|
||||
*/
|
||||
def getAllRepositories(userName: String)(implicit s: Session): List[(String, String)] = {
|
||||
Repositories.filter { t1 =>
|
||||
(t1.isPrivate === false.bind) ||
|
||||
(t1.userName === userName.bind) || (t1.userName in (GroupMembers.filter(_.userName === userName.bind).map(_.groupName))) ||
|
||||
(Collaborators.filter { t2 => t2.byRepository(t1.userName, t1.repositoryName) &&
|
||||
((t2.collaboratorName === userName.bind) || (t2.collaboratorName in GroupMembers.filter(_.userName === userName.bind).map(_.groupName)))
|
||||
} exists)
|
||||
}.sortBy(_.lastActivityDate desc).map{ t =>
|
||||
(t.userName, t.repositoryName)
|
||||
}.list
|
||||
Repositories
|
||||
.filter { t1 =>
|
||||
(t1.isPrivate === false.bind) ||
|
||||
(t1.userName === userName.bind) || (t1.userName in (GroupMembers
|
||||
.filter(_.userName === userName.bind)
|
||||
.map(_.groupName))) ||
|
||||
(Collaborators.filter { t2 =>
|
||||
t2.byRepository(t1.userName, t1.repositoryName) &&
|
||||
((t2.collaboratorName === userName.bind) || (t2.collaboratorName in GroupMembers
|
||||
.filter(_.userName === userName.bind)
|
||||
.map(_.groupName)))
|
||||
} exists)
|
||||
}
|
||||
.sortBy(_.lastActivityDate desc)
|
||||
.map { t =>
|
||||
(t.userName, t.repositoryName)
|
||||
}
|
||||
.list
|
||||
}
|
||||
|
||||
def getUserRepositories(userName: String, withoutPhysicalInfo: Boolean = false)(implicit s: Session): List[RepositoryInfo] = {
|
||||
Repositories.filter { t1 =>
|
||||
(t1.userName === userName.bind) || (t1.userName in (GroupMembers.filter(_.userName === userName.bind).map(_.groupName))) ||
|
||||
(Collaborators.filter { t2 => t2.byRepository(t1.userName, t1.repositoryName) &&
|
||||
((t2.collaboratorName === userName.bind) || (t2.collaboratorName in GroupMembers.filter(_.userName === userName.bind).map(_.groupName)))
|
||||
} exists)
|
||||
}.sortBy(_.lastActivityDate desc).list.map{ repository =>
|
||||
new RepositoryInfo(
|
||||
if(withoutPhysicalInfo){
|
||||
new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName)
|
||||
} else {
|
||||
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName)
|
||||
},
|
||||
repository,
|
||||
if(withoutPhysicalInfo){
|
||||
-1
|
||||
} else {
|
||||
getForkedCount(
|
||||
repository.originUserName.getOrElse(repository.userName),
|
||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||
)
|
||||
},
|
||||
if(withoutPhysicalInfo){
|
||||
Nil
|
||||
} else {
|
||||
getRepositoryManagers(repository.userName)
|
||||
})
|
||||
}
|
||||
def getUserRepositories(userName: String, withoutPhysicalInfo: Boolean = false)(
|
||||
implicit s: Session
|
||||
): List[RepositoryInfo] = {
|
||||
Repositories
|
||||
.filter { t1 =>
|
||||
(t1.userName === userName.bind) || (t1.userName in (GroupMembers
|
||||
.filter(_.userName === userName.bind)
|
||||
.map(_.groupName))) ||
|
||||
(Collaborators.filter { t2 =>
|
||||
t2.byRepository(t1.userName, t1.repositoryName) &&
|
||||
((t2.collaboratorName === userName.bind) || (t2.collaboratorName in GroupMembers
|
||||
.filter(_.userName === userName.bind)
|
||||
.map(_.groupName)))
|
||||
} exists)
|
||||
}
|
||||
.sortBy(_.lastActivityDate desc)
|
||||
.list
|
||||
.map { repository =>
|
||||
new RepositoryInfo(
|
||||
if (withoutPhysicalInfo) {
|
||||
new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName)
|
||||
} else {
|
||||
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName)
|
||||
},
|
||||
repository,
|
||||
if (withoutPhysicalInfo) {
|
||||
-1
|
||||
} else {
|
||||
getForkedCount(
|
||||
repository.originUserName.getOrElse(repository.userName),
|
||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||
)
|
||||
},
|
||||
if (withoutPhysicalInfo) {
|
||||
Nil
|
||||
} else {
|
||||
getRepositoryManagers(repository.userName)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,53 +418,63 @@ trait RepositoryService { self: AccountService =>
|
||||
* branches and tags
|
||||
* @return the repository information which is sorted in descending order of lastActivityDate.
|
||||
*/
|
||||
def getVisibleRepositories(loginAccount: Option[Account], repositoryUserName: Option[String] = None,
|
||||
withoutPhysicalInfo: Boolean = false)
|
||||
(implicit s: Session): List[RepositoryInfo] = {
|
||||
def getVisibleRepositories(
|
||||
loginAccount: Option[Account],
|
||||
repositoryUserName: Option[String] = None,
|
||||
withoutPhysicalInfo: Boolean = false
|
||||
)(implicit s: Session): List[RepositoryInfo] = {
|
||||
(loginAccount match {
|
||||
// for Administrators
|
||||
case Some(x) if(x.isAdmin) => Repositories
|
||||
case Some(x) if (x.isAdmin) => Repositories
|
||||
// for Normal Users
|
||||
case Some(x) if(!x.isAdmin) =>
|
||||
case Some(x) if (!x.isAdmin) =>
|
||||
Repositories filter { t =>
|
||||
(t.isPrivate === false.bind) || (t.userName === x.userName) ||
|
||||
(t.userName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)) ||
|
||||
(Collaborators.filter { t2 =>
|
||||
t2.byRepository(t.userName, t.repositoryName) &&
|
||||
((t2.collaboratorName === x.userName.bind) || (t2.collaboratorName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)))
|
||||
((t2.collaboratorName === x.userName.bind) || (t2.collaboratorName in GroupMembers
|
||||
.filter(_.userName === x.userName.bind)
|
||||
.map(_.groupName)))
|
||||
} exists)
|
||||
}
|
||||
// for Guests
|
||||
case None => Repositories filter(_.isPrivate === false.bind)
|
||||
case None => Repositories filter (_.isPrivate === false.bind)
|
||||
}).filter { t =>
|
||||
repositoryUserName.map { userName => t.userName === userName.bind } getOrElse LiteralColumn(true)
|
||||
}.sortBy(_.lastActivityDate desc).list.map { repository =>
|
||||
new RepositoryInfo(
|
||||
if(withoutPhysicalInfo){
|
||||
new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName)
|
||||
} else {
|
||||
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName)
|
||||
},
|
||||
repository,
|
||||
if(withoutPhysicalInfo){
|
||||
-1
|
||||
} else {
|
||||
getForkedCount(
|
||||
repository.originUserName.getOrElse(repository.userName),
|
||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||
)
|
||||
},
|
||||
if(withoutPhysicalInfo) {
|
||||
Nil
|
||||
} else {
|
||||
getRepositoryManagers(repository.userName)
|
||||
})
|
||||
}
|
||||
repositoryUserName.map { userName =>
|
||||
t.userName === userName.bind
|
||||
} getOrElse LiteralColumn(true)
|
||||
}
|
||||
.sortBy(_.lastActivityDate desc)
|
||||
.list
|
||||
.map { repository =>
|
||||
new RepositoryInfo(
|
||||
if (withoutPhysicalInfo) {
|
||||
new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName)
|
||||
} else {
|
||||
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName)
|
||||
},
|
||||
repository,
|
||||
if (withoutPhysicalInfo) {
|
||||
-1
|
||||
} else {
|
||||
getForkedCount(
|
||||
repository.originUserName.getOrElse(repository.userName),
|
||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||
)
|
||||
},
|
||||
if (withoutPhysicalInfo) {
|
||||
Nil
|
||||
} else {
|
||||
getRepositoryManagers(repository.userName)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private def getRepositoryManagers(userName: String)(implicit s: Session): Seq[String] =
|
||||
if(getAccountByUserName(userName).exists(_.isGroupAccount)){
|
||||
getGroupMembers(userName).collect { case x if(x.isManager) => x.userName }
|
||||
if (getAccountByUserName(userName).exists(_.isGroupAccount)) {
|
||||
getGroupMembers(userName).collect { case x if (x.isManager) => x.userName }
|
||||
} else {
|
||||
Seq(userName)
|
||||
}
|
||||
@@ -364,24 +489,37 @@ trait RepositoryService { self: AccountService =>
|
||||
/**
|
||||
* Save repository options.
|
||||
*/
|
||||
def saveRepositoryOptions(userName: String, repositoryName: String, description: Option[String], isPrivate: Boolean,
|
||||
issuesOption: String, externalIssuesUrl: Option[String], wikiOption: String, externalWikiUrl: Option[String],
|
||||
allowFork: Boolean, mergeOptions: Seq[String], defaultMergeOption: String)(implicit s: Session): Unit = {
|
||||
def saveRepositoryOptions(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
description: Option[String],
|
||||
isPrivate: Boolean,
|
||||
issuesOption: String,
|
||||
externalIssuesUrl: Option[String],
|
||||
wikiOption: String,
|
||||
externalWikiUrl: Option[String],
|
||||
allowFork: Boolean,
|
||||
mergeOptions: Seq[String],
|
||||
defaultMergeOption: String
|
||||
)(implicit s: Session): Unit = {
|
||||
|
||||
Repositories.filter(_.byRepository(userName, repositoryName))
|
||||
.map { r => (
|
||||
r.description.?,
|
||||
r.isPrivate,
|
||||
r.issuesOption,
|
||||
r.externalIssuesUrl.?,
|
||||
r.wikiOption,
|
||||
r.externalWikiUrl.?,
|
||||
r.allowFork,
|
||||
r.mergeOptions,
|
||||
r.defaultMergeOption,
|
||||
r.updatedDate
|
||||
) }
|
||||
.update (
|
||||
Repositories
|
||||
.filter(_.byRepository(userName, repositoryName))
|
||||
.map { r =>
|
||||
(
|
||||
r.description.?,
|
||||
r.isPrivate,
|
||||
r.issuesOption,
|
||||
r.externalIssuesUrl.?,
|
||||
r.wikiOption,
|
||||
r.externalWikiUrl.?,
|
||||
r.allowFork,
|
||||
r.mergeOptions,
|
||||
r.defaultMergeOption,
|
||||
r.updatedDate
|
||||
)
|
||||
}
|
||||
.update(
|
||||
description,
|
||||
isPrivate,
|
||||
issuesOption,
|
||||
@@ -395,16 +533,22 @@ trait RepositoryService { self: AccountService =>
|
||||
)
|
||||
}
|
||||
|
||||
def saveRepositoryDefaultBranch(userName: String, repositoryName: String,
|
||||
defaultBranch: String)(implicit s: Session): Unit =
|
||||
Repositories.filter(_.byRepository(userName, repositoryName))
|
||||
.map { r => r.defaultBranch }
|
||||
.update (defaultBranch)
|
||||
def saveRepositoryDefaultBranch(userName: String, repositoryName: String, defaultBranch: String)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
Repositories
|
||||
.filter(_.byRepository(userName, repositoryName))
|
||||
.map { r =>
|
||||
r.defaultBranch
|
||||
}
|
||||
.update(defaultBranch)
|
||||
|
||||
/**
|
||||
* Add collaborator (user or group) to the repository.
|
||||
*/
|
||||
def addCollaborator(userName: String, repositoryName: String, collaboratorName: String, role: String)(implicit s: Session): Unit =
|
||||
def addCollaborator(userName: String, repositoryName: String, collaboratorName: String, role: String)(
|
||||
implicit s: Session
|
||||
): Unit =
|
||||
Collaborators insert Collaborator(userName, repositoryName, collaboratorName, role)
|
||||
|
||||
/**
|
||||
@@ -418,9 +562,10 @@ trait RepositoryService { self: AccountService =>
|
||||
*/
|
||||
def getCollaborators(userName: String, repositoryName: String)(implicit s: Session): List[(Collaborator, Boolean)] =
|
||||
Collaborators
|
||||
.join(Accounts).on(_.collaboratorName === _.userName)
|
||||
.join(Accounts)
|
||||
.on(_.collaboratorName === _.userName)
|
||||
.filter { case (t1, t2) => t1.byRepository(userName, repositoryName) }
|
||||
.map { case (t1, t2) => (t1, t2.groupAccount) }
|
||||
.map { case (t1, t2) => (t1, t2.groupAccount) }
|
||||
.sortBy { case (t1, t2) => t1.collaboratorName }
|
||||
.list
|
||||
|
||||
@@ -428,60 +573,79 @@ trait RepositoryService { self: AccountService =>
|
||||
* 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.
|
||||
*/
|
||||
def getCollaboratorUserNames(userName: String, repositoryName: String, filter: Seq[Role] = Nil)(implicit s: Session): List[String] = {
|
||||
def getCollaboratorUserNames(userName: String, repositoryName: String, filter: Seq[Role] = Nil)(
|
||||
implicit s: Session
|
||||
): List[String] = {
|
||||
val q1 = Collaborators
|
||||
.join(Accounts).on { case (t1, t2) => (t1.collaboratorName === t2.userName) && (t2.groupAccount === false.bind) }
|
||||
.join(Accounts)
|
||||
.on { case (t1, t2) => (t1.collaboratorName === t2.userName) && (t2.groupAccount === false.bind) }
|
||||
.filter { case (t1, t2) => t1.byRepository(userName, repositoryName) }
|
||||
.map { case (t1, t2) => (t1.collaboratorName, t1.role) }
|
||||
.map { case (t1, t2) => (t1.collaboratorName, t1.role) }
|
||||
|
||||
val q2 = Collaborators
|
||||
.join(Accounts).on { case (t1, t2) => (t1.collaboratorName === t2.userName) && (t2.groupAccount === true.bind) }
|
||||
.join(GroupMembers).on { case ((t1, t2), t3) => t2.userName === t3.groupName }
|
||||
.join(Accounts)
|
||||
.on { case (t1, t2) => (t1.collaboratorName === t2.userName) && (t2.groupAccount === true.bind) }
|
||||
.join(GroupMembers)
|
||||
.on { case ((t1, t2), t3) => t2.userName === t3.groupName }
|
||||
.filter { case ((t1, t2), t3) => t1.byRepository(userName, repositoryName) }
|
||||
.map { case ((t1, t2), t3) => (t3.userName, t1.role) }
|
||||
.map { case ((t1, t2), t3) => (t3.userName, t1.role) }
|
||||
|
||||
q1.union(q2).list.filter { x => filter.isEmpty || filter.exists(_.name == x._2) }.map(_._1)
|
||||
q1.union(q2)
|
||||
.list
|
||||
.filter { x =>
|
||||
filter.isEmpty || filter.exists(_.name == x._2)
|
||||
}
|
||||
.map(_._1)
|
||||
}
|
||||
|
||||
def hasOwnerRole(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
||||
loginAccount match {
|
||||
case Some(a) if(a.isAdmin) => true
|
||||
case Some(a) if(a.userName == owner) => true
|
||||
case Some(a) if(getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
||||
case Some(a) if(getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN)).contains(a.userName)) => true
|
||||
case _ => false
|
||||
case Some(a) if (a.isAdmin) => true
|
||||
case Some(a) if (a.userName == owner) => true
|
||||
case Some(a) if (getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
||||
case Some(a) if (getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN)).contains(a.userName)) => true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
def hasDeveloperRole(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
||||
def hasDeveloperRole(owner: String, repository: String, loginAccount: Option[Account])(
|
||||
implicit s: Session
|
||||
): Boolean = {
|
||||
loginAccount match {
|
||||
case Some(a) if(a.isAdmin) => true
|
||||
case Some(a) if(a.userName == owner) => true
|
||||
case Some(a) if(getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
||||
case Some(a) if(getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER)).contains(a.userName)) => true
|
||||
case Some(a) if (a.isAdmin) => true
|
||||
case Some(a) if (a.userName == owner) => true
|
||||
case Some(a) if (getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
||||
case Some(a)
|
||||
if (getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER)).contains(a.userName)) =>
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
def hasGuestRole(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
||||
loginAccount match {
|
||||
case Some(a) if(a.isAdmin) => true
|
||||
case Some(a) if(a.userName == owner) => true
|
||||
case Some(a) if(getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
||||
case Some(a) if(getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER, Role.GUEST)).contains(a.userName)) => true
|
||||
case Some(a) if (a.isAdmin) => true
|
||||
case Some(a) if (a.userName == owner) => true
|
||||
case Some(a) if (getGroupMembers(owner).exists(_.userName == a.userName)) => true
|
||||
case Some(a)
|
||||
if (getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER, Role.GUEST))
|
||||
.contains(a.userName)) =>
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
|
||||
def isReadable(repository: Repository, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
||||
if(!repository.isPrivate){
|
||||
if (!repository.isPrivate) {
|
||||
true
|
||||
} else {
|
||||
loginAccount match {
|
||||
case Some(x) if(x.isAdmin) => true
|
||||
case Some(x) if(repository.userName == x.userName) => true
|
||||
case Some(x) if(getGroupMembers(repository.userName).exists(_.userName == x.userName)) => true
|
||||
case Some(x) if(getCollaboratorUserNames(repository.userName, repository.repositoryName).contains(x.userName)) => true
|
||||
case Some(x) if (x.isAdmin) => true
|
||||
case Some(x) if (repository.userName == x.userName) => true
|
||||
case Some(x) if (getGroupMembers(repository.userName).exists(_.userName == x.userName)) => true
|
||||
case Some(x)
|
||||
if (getCollaboratorUserNames(repository.userName, repository.repositoryName).contains(x.userName)) =>
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
}
|
||||
@@ -492,61 +656,81 @@ trait RepositoryService { self: AccountService =>
|
||||
(t.originUserName === userName.bind) && (t.originRepositoryName === repositoryName.bind)
|
||||
}.length).first
|
||||
|
||||
|
||||
def getForkedRepositories(userName: String, repositoryName: String)(implicit s: Session): List[Repository] =
|
||||
Repositories.filter { t =>
|
||||
(t.originUserName === userName.bind) && (t.originRepositoryName === repositoryName.bind)
|
||||
}
|
||||
.sortBy(_.userName asc).list//.map(t => t.userName -> t.repositoryName).list
|
||||
Repositories
|
||||
.filter { t =>
|
||||
(t.originUserName === userName.bind) && (t.originRepositoryName === repositoryName.bind)
|
||||
}
|
||||
.sortBy(_.userName asc)
|
||||
.list //.map(t => t.userName -> t.repositoryName).list
|
||||
|
||||
private val templateExtensions = Seq("md", "markdown")
|
||||
|
||||
/**
|
||||
* Returns content of template set per repository.
|
||||
*
|
||||
* @param repository the repository information
|
||||
* @param fileBaseName the file basename without extension of template
|
||||
* @return The content of template if the repository has it, otherwise empty string.
|
||||
* Returns content of template set per repository.
|
||||
*
|
||||
* @param repository the repository information
|
||||
* @param fileBaseName the file basename without extension of template
|
||||
* @return The content of template if the repository has it, otherwise empty string.
|
||||
*/
|
||||
def getContentTemplate(repository: RepositoryInfo, fileBaseName: String)(implicit s: Session): String = {
|
||||
val withExtFilenames = templateExtensions.map(extension => s"${fileBaseName.toLowerCase()}.${extension}")
|
||||
|
||||
def choiceTemplate(files: List[FileInfo]): Option[FileInfo] =
|
||||
files.find { f =>
|
||||
f.name.toLowerCase() == fileBaseName
|
||||
}.orElse {
|
||||
files.find(f => withExtFilenames.contains(f.name.toLowerCase()))
|
||||
}
|
||||
files
|
||||
.find { f =>
|
||||
f.name.toLowerCase() == fileBaseName
|
||||
}
|
||||
.orElse {
|
||||
files.find(f => withExtFilenames.contains(f.name.toLowerCase()))
|
||||
}
|
||||
|
||||
// Get template file from project root. When didn't find, will lookup default folder.
|
||||
using(Git.open(Directory.getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, ".")).orElse {
|
||||
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, ".gitbucket"))
|
||||
}.map { file =>
|
||||
JGitUtil.getContentFromId(git, file.id, true).collect {
|
||||
case bytes if FileUtil.isText(bytes) => StringUtil.convertFromByteArray(bytes)
|
||||
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, "."))
|
||||
.orElse {
|
||||
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, ".gitbucket"))
|
||||
}
|
||||
} getOrElse None
|
||||
.map { file =>
|
||||
JGitUtil.getContentFromId(git, file.id, true).collect {
|
||||
case bytes if FileUtil.isText(bytes) => StringUtil.convertFromByteArray(bytes)
|
||||
}
|
||||
} getOrElse None
|
||||
} getOrElse ""
|
||||
}
|
||||
}
|
||||
|
||||
object RepositoryService {
|
||||
|
||||
case class RepositoryInfo(owner: String, name: String, repository: Repository,
|
||||
issueCount: Int, pullCount: Int, forkedCount: Int,
|
||||
branchList: Seq[String], tags: Seq[JGitUtil.TagInfo], managers: Seq[String]) {
|
||||
case class RepositoryInfo(
|
||||
owner: String,
|
||||
name: String,
|
||||
repository: Repository,
|
||||
issueCount: Int,
|
||||
pullCount: Int,
|
||||
forkedCount: Int,
|
||||
branchList: Seq[String],
|
||||
tags: Seq[JGitUtil.TagInfo],
|
||||
managers: Seq[String]
|
||||
) {
|
||||
|
||||
/**
|
||||
* Creates instance with issue count and pull request count.
|
||||
*/
|
||||
def this(repo: JGitUtil.RepositoryInfo, model: Repository, issueCount: Int, pullCount: Int, forkedCount: Int, managers: Seq[String]) =
|
||||
def this(
|
||||
repo: JGitUtil.RepositoryInfo,
|
||||
model: Repository,
|
||||
issueCount: Int,
|
||||
pullCount: Int,
|
||||
forkedCount: Int,
|
||||
managers: Seq[String]
|
||||
) =
|
||||
this(repo.owner, repo.name, model, issueCount, pullCount, forkedCount, repo.branchList, repo.tags, managers)
|
||||
|
||||
/**
|
||||
* Creates instance without issue and pull request count.
|
||||
*/
|
||||
def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int, managers: Seq[String]) =
|
||||
def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int, managers: Seq[String]) =
|
||||
this(repo.owner, repo.name, model, 0, 0, forkedCount, repo.branchList, repo.tags, managers)
|
||||
|
||||
def httpUrl(implicit context: Context): String = RepositoryService.httpUrl(owner, name)
|
||||
@@ -554,19 +738,23 @@ object RepositoryService {
|
||||
|
||||
def splitPath(path: String): (String, String) = {
|
||||
val id = branchList.collectFirst {
|
||||
case branch if(path == branch || path.startsWith(branch + "/")) => branch
|
||||
case branch if (path == branch || path.startsWith(branch + "/")) => branch
|
||||
} orElse tags.collectFirst {
|
||||
case tag if(path == tag.name || path.startsWith(tag.name + "/")) => tag.name
|
||||
case tag if (path == tag.name || path.startsWith(tag.name + "/")) => tag.name
|
||||
} getOrElse path.split("/")(0)
|
||||
|
||||
(id, path.substring(id.length).stripPrefix("/"))
|
||||
}
|
||||
}
|
||||
|
||||
def httpUrl(owner: String, name: String)(implicit context: Context): String = s"${context.baseUrl}/git/${owner}/${name}.git"
|
||||
def httpUrl(owner: String, name: String)(implicit context: Context): String =
|
||||
s"${context.baseUrl}/git/${owner}/${name}.git"
|
||||
def sshUrl(owner: String, name: String)(implicit context: Context): Option[String] =
|
||||
if(context.settings.ssh){
|
||||
context.settings.sshAddress.map { x => s"ssh://${x.genericUser}@${x.host}:${x.port}/${owner}/${name}.git" }
|
||||
if (context.settings.ssh) {
|
||||
context.settings.sshAddress.map { x =>
|
||||
s"ssh://${x.genericUser}@${x.host}:${x.port}/${owner}/${name}.git"
|
||||
}
|
||||
} else None
|
||||
def openRepoUrl(openUrl: String)(implicit context: Context): String = s"github-${context.platform}://openRepo/${openUrl}"
|
||||
def openRepoUrl(openUrl: String)(implicit context: Context): String =
|
||||
s"github-${context.platform}://openRepo/${openUrl}"
|
||||
}
|
||||
|
||||
@@ -11,26 +11,32 @@ import Implicits.request2Session
|
||||
* It may be called many times in one request, so each method stores
|
||||
* its result into the cache which available during a request.
|
||||
*/
|
||||
trait RequestCache extends SystemSettingsService with AccountService with IssuesService with RepositoryService
|
||||
with LabelsService with MilestonesService with PrioritiesService {
|
||||
trait RequestCache
|
||||
extends SystemSettingsService
|
||||
with AccountService
|
||||
with IssuesService
|
||||
with RepositoryService
|
||||
with LabelsService
|
||||
with MilestonesService
|
||||
with PrioritiesService {
|
||||
|
||||
private implicit def context2Session(implicit context: Context): Session =
|
||||
request2Session(context.request)
|
||||
|
||||
def getIssue(userName: String, repositoryName: String, issueId: String)(implicit context: Context): Option[Issue] = {
|
||||
context.cache(s"issue.${userName}/${repositoryName}#${issueId}"){
|
||||
context.cache(s"issue.${userName}/${repositoryName}#${issueId}") {
|
||||
super.getIssue(userName, repositoryName, issueId)
|
||||
}
|
||||
}
|
||||
|
||||
def getAccountByUserName(userName: String)(implicit context: Context): Option[Account] = {
|
||||
context.cache(s"account.${userName}"){
|
||||
context.cache(s"account.${userName}") {
|
||||
super.getAccountByUserName(userName)
|
||||
}
|
||||
}
|
||||
|
||||
def getAccountByMailAddress(mailAddress: String)(implicit context: Context): Option[Account] = {
|
||||
context.cache(s"account.${mailAddress}"){
|
||||
context.cache(s"account.${mailAddress}") {
|
||||
super.getAccountByMailAddress(mailAddress)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ trait SystemSettingsService {
|
||||
def baseUrl(implicit request: HttpServletRequest): String = loadSystemSettings().baseUrl(request)
|
||||
|
||||
def saveSystemSettings(settings: SystemSettings): Unit = {
|
||||
defining(new java.util.Properties()){ props =>
|
||||
defining(new java.util.Properties()) { props =>
|
||||
settings.baseUrl.foreach(x => props.setProperty(BaseURL, x.replaceFirst("/\\Z", "")))
|
||||
settings.information.foreach(x => props.setProperty(Information, x))
|
||||
props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString)
|
||||
@@ -28,7 +28,7 @@ trait SystemSettingsService {
|
||||
settings.sshHost.foreach(x => props.setProperty(SshHost, x.trim))
|
||||
settings.sshPort.foreach(x => props.setProperty(SshPort, x.toString))
|
||||
props.setProperty(UseSMTP, settings.useSMTP.toString)
|
||||
if(settings.useSMTP) {
|
||||
if (settings.useSMTP) {
|
||||
settings.smtp.foreach { smtp =>
|
||||
props.setProperty(SmtpHost, smtp.host)
|
||||
smtp.port.foreach(x => props.setProperty(SmtpPort, x.toString))
|
||||
@@ -41,7 +41,7 @@ trait SystemSettingsService {
|
||||
}
|
||||
}
|
||||
props.setProperty(LdapAuthentication, settings.ldapAuthentication.toString)
|
||||
if(settings.ldapAuthentication){
|
||||
if (settings.ldapAuthentication) {
|
||||
settings.ldap.map { ldap =>
|
||||
props.setProperty(LdapHost, ldap.host)
|
||||
ldap.port.foreach(x => props.setProperty(LdapPort, x.toString))
|
||||
@@ -63,21 +63,22 @@ trait SystemSettingsService {
|
||||
props.setProperty(OidcIssuer, oidc.issuer.getValue)
|
||||
props.setProperty(OidcClientId, oidc.clientID.getValue)
|
||||
props.setProperty(OidcClientSecret, oidc.clientSecret.getValue)
|
||||
oidc.jwsAlgorithm.map { x => props.setProperty(OidcJwsAlgorithm, x.getName) }
|
||||
oidc.jwsAlgorithm.map { x =>
|
||||
props.setProperty(OidcJwsAlgorithm, x.getName)
|
||||
}
|
||||
}
|
||||
}
|
||||
props.setProperty(SkinName, settings.skinName.toString)
|
||||
using(new java.io.FileOutputStream(GitBucketConf)){ out =>
|
||||
using(new java.io.FileOutputStream(GitBucketConf)) { out =>
|
||||
props.store(out, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def loadSystemSettings(): SystemSettings = {
|
||||
defining(new java.util.Properties()){ props =>
|
||||
if(GitBucketConf.exists){
|
||||
using(new java.io.FileInputStream(GitBucketConf)){ in =>
|
||||
defining(new java.util.Properties()) { props =>
|
||||
if (GitBucketConf.exists) {
|
||||
using(new java.io.FileInputStream(GitBucketConf)) { in =>
|
||||
props.load(in)
|
||||
}
|
||||
}
|
||||
@@ -93,46 +94,54 @@ trait SystemSettingsService {
|
||||
getValue(props, Ssh, false),
|
||||
getOptionValue[String](props, SshHost, None).map(_.trim),
|
||||
getOptionValue(props, SshPort, Some(DefaultSshPort)),
|
||||
getValue(props, UseSMTP, getValue(props, Notification, false)), // handle migration scenario from only notification to useSMTP
|
||||
if(getValue(props, UseSMTP, getValue(props, Notification, false))){
|
||||
Some(Smtp(
|
||||
getValue(props, SmtpHost, ""),
|
||||
getOptionValue(props, SmtpPort, Some(DefaultSmtpPort)),
|
||||
getOptionValue(props, SmtpUser, None),
|
||||
getOptionValue(props, SmtpPassword, None),
|
||||
getOptionValue[Boolean](props, SmtpSsl, None),
|
||||
getOptionValue[Boolean](props, SmtpStarttls, None),
|
||||
getOptionValue(props, SmtpFromAddress, None),
|
||||
getOptionValue(props, SmtpFromName, None)))
|
||||
getValue(props, UseSMTP, getValue(props, Notification, false)), // handle migration scenario from only notification to useSMTP
|
||||
if (getValue(props, UseSMTP, getValue(props, Notification, false))) {
|
||||
Some(
|
||||
Smtp(
|
||||
getValue(props, SmtpHost, ""),
|
||||
getOptionValue(props, SmtpPort, Some(DefaultSmtpPort)),
|
||||
getOptionValue(props, SmtpUser, None),
|
||||
getOptionValue(props, SmtpPassword, None),
|
||||
getOptionValue[Boolean](props, SmtpSsl, None),
|
||||
getOptionValue[Boolean](props, SmtpStarttls, None),
|
||||
getOptionValue(props, SmtpFromAddress, None),
|
||||
getOptionValue(props, SmtpFromName, None)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
getValue(props, LdapAuthentication, false),
|
||||
if(getValue(props, LdapAuthentication, false)){
|
||||
Some(Ldap(
|
||||
getValue(props, LdapHost, ""),
|
||||
getOptionValue(props, LdapPort, Some(DefaultLdapPort)),
|
||||
getOptionValue(props, LdapBindDN, None),
|
||||
getOptionValue(props, LdapBindPassword, None),
|
||||
getValue(props, LdapBaseDN, ""),
|
||||
getValue(props, LdapUserNameAttribute, ""),
|
||||
getOptionValue(props, LdapAdditionalFilterCondition, None),
|
||||
getOptionValue(props, LdapFullNameAttribute, None),
|
||||
getOptionValue(props, LdapMailAddressAttribute, None),
|
||||
getOptionValue[Boolean](props, LdapTls, None),
|
||||
getOptionValue[Boolean](props, LdapSsl, None),
|
||||
getOptionValue(props, LdapKeystore, None)))
|
||||
if (getValue(props, LdapAuthentication, false)) {
|
||||
Some(
|
||||
Ldap(
|
||||
getValue(props, LdapHost, ""),
|
||||
getOptionValue(props, LdapPort, Some(DefaultLdapPort)),
|
||||
getOptionValue(props, LdapBindDN, None),
|
||||
getOptionValue(props, LdapBindPassword, None),
|
||||
getValue(props, LdapBaseDN, ""),
|
||||
getValue(props, LdapUserNameAttribute, ""),
|
||||
getOptionValue(props, LdapAdditionalFilterCondition, None),
|
||||
getOptionValue(props, LdapFullNameAttribute, None),
|
||||
getOptionValue(props, LdapMailAddressAttribute, None),
|
||||
getOptionValue[Boolean](props, LdapTls, None),
|
||||
getOptionValue[Boolean](props, LdapSsl, None),
|
||||
getOptionValue(props, LdapKeystore, None)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
getValue(props, OidcAuthentication, false),
|
||||
if (getValue(props, OidcAuthentication, false)) {
|
||||
Some(OIDC(
|
||||
getValue(props, OidcIssuer, ""),
|
||||
getValue(props, OidcClientId, ""),
|
||||
getValue(props, OidcClientSecret, ""),
|
||||
getOptionValue(props, OidcJwsAlgorithm, None)
|
||||
))
|
||||
Some(
|
||||
OIDC(
|
||||
getValue(props, OidcIssuer, ""),
|
||||
getValue(props, OidcClientId, ""),
|
||||
getValue(props, OidcClientSecret, ""),
|
||||
getOptionValue(props, OidcJwsAlgorithm, None)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
@@ -164,16 +173,19 @@ object SystemSettingsService {
|
||||
ldap: Option[Ldap],
|
||||
oidcAuthentication: Boolean,
|
||||
oidc: Option[OIDC],
|
||||
skinName: String){
|
||||
skinName: String
|
||||
) {
|
||||
|
||||
def baseUrl(request: HttpServletRequest): String = baseUrl.fold {
|
||||
val url = request.getRequestURL.toString
|
||||
val len = url.length - (request.getRequestURI.length - request.getContextPath.length)
|
||||
url.substring(0, len).stripSuffix("/")
|
||||
} (_.stripSuffix("/"))
|
||||
def baseUrl(request: HttpServletRequest): String =
|
||||
baseUrl.fold {
|
||||
val url = request.getRequestURL.toString
|
||||
val len = url.length - (request.getRequestURI.length - request.getContextPath.length)
|
||||
url.substring(0, len).stripSuffix("/")
|
||||
}(_.stripSuffix("/"))
|
||||
|
||||
def sshAddress:Option[SshAddress] = sshHost.collect { case host if ssh =>
|
||||
SshAddress(host, sshPort.getOrElse(DefaultSshPort), "git")
|
||||
def sshAddress: Option[SshAddress] = sshHost.collect {
|
||||
case host if ssh =>
|
||||
SshAddress(host, sshPort.getOrElse(DefaultSshPort), "git")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,16 +201,18 @@ object SystemSettingsService {
|
||||
mailAttribute: Option[String],
|
||||
tls: Option[Boolean],
|
||||
ssl: Option[Boolean],
|
||||
keystore: Option[String])
|
||||
keystore: Option[String]
|
||||
)
|
||||
|
||||
case class OIDC(
|
||||
issuer: Issuer,
|
||||
clientID: ClientID,
|
||||
clientSecret: Secret,
|
||||
jwsAlgorithm: Option[JWSAlgorithm])
|
||||
case class OIDC(issuer: Issuer, clientID: ClientID, clientSecret: Secret, jwsAlgorithm: Option[JWSAlgorithm])
|
||||
object OIDC {
|
||||
def apply(issuer: String, clientID: String, clientSecret: String, jwsAlgorithm: Option[String]): OIDC =
|
||||
new OIDC(new Issuer(issuer), new ClientID(clientID), new Secret(clientSecret), jwsAlgorithm.map(JWSAlgorithm.parse))
|
||||
new OIDC(
|
||||
new Issuer(issuer),
|
||||
new ClientID(clientID),
|
||||
new Secret(clientSecret),
|
||||
jwsAlgorithm.map(JWSAlgorithm.parse)
|
||||
)
|
||||
}
|
||||
|
||||
case class Smtp(
|
||||
@@ -209,15 +223,12 @@ object SystemSettingsService {
|
||||
ssl: Option[Boolean],
|
||||
starttls: Option[Boolean],
|
||||
fromAddress: Option[String],
|
||||
fromName: Option[String])
|
||||
fromName: Option[String]
|
||||
)
|
||||
|
||||
case class SshAddress(
|
||||
host: String,
|
||||
port: Int,
|
||||
genericUser: String)
|
||||
case class SshAddress(host: String, port: Int, genericUser: String)
|
||||
|
||||
case class Lfs(
|
||||
serverUrl: Option[String])
|
||||
case class Lfs(serverUrl: Option[String])
|
||||
|
||||
val DefaultSshPort = 29418
|
||||
val DefaultSmtpPort = 25
|
||||
@@ -265,8 +276,8 @@ object SystemSettingsService {
|
||||
|
||||
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = {
|
||||
getSystemProperty(key).getOrElse(getEnvironmentVariable(key).getOrElse {
|
||||
defining(props.getProperty(key)){ value =>
|
||||
if(value == null || value.isEmpty){
|
||||
defining(props.getProperty(key)) { value =>
|
||||
if (value == null || value.isEmpty) {
|
||||
default
|
||||
} else {
|
||||
convertType(value).asInstanceOf[A]
|
||||
@@ -277,8 +288,8 @@ object SystemSettingsService {
|
||||
|
||||
private def getOptionValue[A: ClassTag](props: java.util.Properties, key: String, default: Option[A]): Option[A] = {
|
||||
getSystemProperty(key).orElse(getEnvironmentVariable(key).orElse {
|
||||
defining(props.getProperty(key)){ value =>
|
||||
if(value == null || value.isEmpty){
|
||||
defining(props.getProperty(key)) { value =>
|
||||
if (value == null || value.isEmpty) {
|
||||
default
|
||||
} else {
|
||||
Some(convertType(value)).asInstanceOf[Option[A]]
|
||||
|
||||
@@ -3,7 +3,19 @@ package gitbucket.core.service
|
||||
import fr.brouillard.oss.security.xhub.XHub
|
||||
import fr.brouillard.oss.security.xhub.XHub.{XHubConverter, XHubDigest}
|
||||
import gitbucket.core.api._
|
||||
import gitbucket.core.model.{Account, CommitComment, Issue, IssueComment, Label, PullRequest, WebHook, RepositoryWebHook, RepositoryWebHookEvent, AccountWebHook, AccountWebHookEvent}
|
||||
import gitbucket.core.model.{
|
||||
Account,
|
||||
CommitComment,
|
||||
Issue,
|
||||
IssueComment,
|
||||
Label,
|
||||
PullRequest,
|
||||
WebHook,
|
||||
RepositoryWebHook,
|
||||
RepositoryWebHookEvent,
|
||||
AccountWebHook,
|
||||
AccountWebHookEvent
|
||||
}
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import org.apache.http.client.utils.URLEncodedUtils
|
||||
@@ -25,84 +37,156 @@ import gitbucket.core.model.WebHookContentType
|
||||
import org.apache.http.client.entity.EntityBuilder
|
||||
import org.apache.http.entity.ContentType
|
||||
|
||||
|
||||
trait WebHookService {
|
||||
import WebHookService._
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[WebHookService])
|
||||
|
||||
/** get All WebHook informations of repository */
|
||||
def getWebHooks(owner: String, repository: String)(implicit s: Session): List[(RepositoryWebHook, Set[WebHook.Event])] =
|
||||
RepositoryWebHooks.filter(_.byRepository(owner, repository))
|
||||
.join(RepositoryWebHookEvents).on { (w, t) => t.byRepositoryWebHook(w) }
|
||||
def getWebHooks(owner: String, repository: String)(
|
||||
implicit s: Session
|
||||
): List[(RepositoryWebHook, Set[WebHook.Event])] =
|
||||
RepositoryWebHooks
|
||||
.filter(_.byRepository(owner, repository))
|
||||
.join(RepositoryWebHookEvents)
|
||||
.on { (w, t) =>
|
||||
t.byRepositoryWebHook(w)
|
||||
}
|
||||
.map { case (w, t) => w -> t.event }
|
||||
.list.groupBy(_._1).mapValues(_.map(_._2).toSet).toList.sortBy(_._1.url)
|
||||
.list
|
||||
.groupBy(_._1)
|
||||
.mapValues(_.map(_._2).toSet)
|
||||
.toList
|
||||
.sortBy(_._1.url)
|
||||
|
||||
/** get All WebHook informations of repository event */
|
||||
def getWebHooksByEvent(owner: String, repository: String, event: WebHook.Event)(implicit s: Session): List[RepositoryWebHook] =
|
||||
RepositoryWebHooks.filter(_.byRepository(owner, repository))
|
||||
.join(RepositoryWebHookEvents).on { (wh, whe) => whe.byRepositoryWebHook(wh) }
|
||||
.filter { case (wh, whe) => whe.event === event.bind}
|
||||
.map{ case (wh, whe) => wh }
|
||||
.list.distinct
|
||||
def getWebHooksByEvent(owner: String, repository: String, event: WebHook.Event)(
|
||||
implicit s: Session
|
||||
): List[RepositoryWebHook] =
|
||||
RepositoryWebHooks
|
||||
.filter(_.byRepository(owner, repository))
|
||||
.join(RepositoryWebHookEvents)
|
||||
.on { (wh, whe) =>
|
||||
whe.byRepositoryWebHook(wh)
|
||||
}
|
||||
.filter { case (wh, whe) => whe.event === event.bind }
|
||||
.map { case (wh, whe) => wh }
|
||||
.list
|
||||
.distinct
|
||||
|
||||
/** get All WebHook information from repository to url */
|
||||
def getWebHook(owner: String, repository: String, url: String)(implicit s: Session): Option[(RepositoryWebHook, Set[WebHook.Event])] =
|
||||
def getWebHook(owner: String, repository: String, url: String)(
|
||||
implicit s: Session
|
||||
): Option[(RepositoryWebHook, Set[WebHook.Event])] =
|
||||
RepositoryWebHooks
|
||||
.filter(_.byPrimaryKey(owner, repository, url))
|
||||
.join(RepositoryWebHookEvents).on { (w, t) => t.byRepositoryWebHook(w) }
|
||||
.join(RepositoryWebHookEvents)
|
||||
.on { (w, t) =>
|
||||
t.byRepositoryWebHook(w)
|
||||
}
|
||||
.map { case (w, t) => w -> t.event }
|
||||
.list.groupBy(_._1).mapValues(_.map(_._2).toSet).headOption
|
||||
.list
|
||||
.groupBy(_._1)
|
||||
.mapValues(_.map(_._2).toSet)
|
||||
.headOption
|
||||
|
||||
def addWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])(implicit s: Session): Unit = {
|
||||
def addWebHook(
|
||||
owner: String,
|
||||
repository: String,
|
||||
url: String,
|
||||
events: Set[WebHook.Event],
|
||||
ctype: WebHookContentType,
|
||||
token: Option[String]
|
||||
)(implicit s: Session): Unit = {
|
||||
RepositoryWebHooks insert RepositoryWebHook(owner, repository, url, ctype, token)
|
||||
events.map { event: WebHook.Event =>
|
||||
RepositoryWebHookEvents insert RepositoryWebHookEvent(owner, repository, url, event)
|
||||
}
|
||||
}
|
||||
|
||||
def updateWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])(implicit s: Session): Unit = {
|
||||
RepositoryWebHooks.filter(_.byPrimaryKey(owner, repository, url)).map(w => (w.ctype, w.token)).update((ctype, token))
|
||||
def updateWebHook(
|
||||
owner: String,
|
||||
repository: String,
|
||||
url: String,
|
||||
events: Set[WebHook.Event],
|
||||
ctype: WebHookContentType,
|
||||
token: Option[String]
|
||||
)(implicit s: Session): Unit = {
|
||||
RepositoryWebHooks
|
||||
.filter(_.byPrimaryKey(owner, repository, url))
|
||||
.map(w => (w.ctype, w.token))
|
||||
.update((ctype, token))
|
||||
RepositoryWebHookEvents.filter(_.byRepositoryWebHook(owner, repository, url)).delete
|
||||
events.map { event: WebHook.Event =>
|
||||
RepositoryWebHookEvents insert RepositoryWebHookEvent(owner, repository, url, event)
|
||||
}
|
||||
}
|
||||
|
||||
def deleteWebHook(owner: String, repository: String, url :String)(implicit s: Session): Unit =
|
||||
def deleteWebHook(owner: String, repository: String, url: String)(implicit s: Session): Unit =
|
||||
RepositoryWebHooks.filter(_.byPrimaryKey(owner, repository, url)).delete
|
||||
|
||||
/** get All AccountWebHook informations of user */
|
||||
def getAccountWebHooks(owner: String)(implicit s: Session): List[(AccountWebHook, Set[WebHook.Event])] =
|
||||
AccountWebHooks.filter(_.byAccount(owner))
|
||||
.join(AccountWebHookEvents).on { (w, t) => t.byAccountWebHook(w) }
|
||||
AccountWebHooks
|
||||
.filter(_.byAccount(owner))
|
||||
.join(AccountWebHookEvents)
|
||||
.on { (w, t) =>
|
||||
t.byAccountWebHook(w)
|
||||
}
|
||||
.map { case (w, t) => w -> t.event }
|
||||
.list.groupBy(_._1).mapValues(_.map(_._2).toSet).toList.sortBy(_._1.url)
|
||||
.list
|
||||
.groupBy(_._1)
|
||||
.mapValues(_.map(_._2).toSet)
|
||||
.toList
|
||||
.sortBy(_._1.url)
|
||||
|
||||
/** get All AccountWebHook informations of repository event */
|
||||
def getAccountWebHooksByEvent(owner: String, event: WebHook.Event)(implicit s: Session): List[AccountWebHook] =
|
||||
AccountWebHooks.filter(_.byAccount(owner))
|
||||
.join(AccountWebHookEvents).on { (wh, whe) => whe.byAccountWebHook(wh) }
|
||||
.filter { case (wh, whe) => whe.event === event.bind}
|
||||
.map{ case (wh, whe) => wh }
|
||||
.list.distinct
|
||||
AccountWebHooks
|
||||
.filter(_.byAccount(owner))
|
||||
.join(AccountWebHookEvents)
|
||||
.on { (wh, whe) =>
|
||||
whe.byAccountWebHook(wh)
|
||||
}
|
||||
.filter { case (wh, whe) => whe.event === event.bind }
|
||||
.map { case (wh, whe) => wh }
|
||||
.list
|
||||
.distinct
|
||||
|
||||
/** get All AccountWebHook information from repository to url */
|
||||
def getAccountWebHook(owner: String, url: String)(implicit s: Session): Option[(AccountWebHook, Set[WebHook.Event])] =
|
||||
AccountWebHooks
|
||||
.filter(_.byPrimaryKey(owner, url))
|
||||
.join(AccountWebHookEvents).on { (w, t) => t.byAccountWebHook(w) }
|
||||
.join(AccountWebHookEvents)
|
||||
.on { (w, t) =>
|
||||
t.byAccountWebHook(w)
|
||||
}
|
||||
.map { case (w, t) => w -> t.event }
|
||||
.list.groupBy(_._1).mapValues(_.map(_._2).toSet).headOption
|
||||
.list
|
||||
.groupBy(_._1)
|
||||
.mapValues(_.map(_._2).toSet)
|
||||
.headOption
|
||||
|
||||
def addAccountWebHook(owner: String, url :String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])(implicit s: Session): Unit = {
|
||||
def addAccountWebHook(
|
||||
owner: String,
|
||||
url: String,
|
||||
events: Set[WebHook.Event],
|
||||
ctype: WebHookContentType,
|
||||
token: Option[String]
|
||||
)(implicit s: Session): Unit = {
|
||||
AccountWebHooks insert AccountWebHook(owner, url, ctype, token)
|
||||
events.map { event: WebHook.Event =>
|
||||
AccountWebHookEvents insert AccountWebHookEvent(owner, url, event)
|
||||
}
|
||||
}
|
||||
|
||||
def updateAccountWebHook(owner: String, url :String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])(implicit s: Session): Unit = {
|
||||
def updateAccountWebHook(
|
||||
owner: String,
|
||||
url: String,
|
||||
events: Set[WebHook.Event],
|
||||
ctype: WebHookContentType,
|
||||
token: Option[String]
|
||||
)(implicit s: Session): Unit = {
|
||||
AccountWebHooks.filter(_.byPrimaryKey(owner, url)).map(w => (w.ctype, w.token)).update((ctype, token))
|
||||
AccountWebHookEvents.filter(_.byAccountWebHook(owner, url)).delete
|
||||
events.map { event: WebHook.Event =>
|
||||
@@ -110,29 +194,31 @@ trait WebHookService {
|
||||
}
|
||||
}
|
||||
|
||||
def deleteAccountWebHook(owner: String, url :String)(implicit s: Session): Unit =
|
||||
def deleteAccountWebHook(owner: String, url: String)(implicit s: Session): Unit =
|
||||
AccountWebHooks.filter(_.byPrimaryKey(owner, url)).delete
|
||||
|
||||
def callWebHookOf(owner: String, repository: String, event: WebHook.Event)(makePayload: => Option[WebHookPayload])
|
||||
(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
def callWebHookOf(owner: String, repository: String, event: WebHook.Event)(
|
||||
makePayload: => Option[WebHookPayload]
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
val webHooks = getWebHooksByEvent(owner, repository, event)
|
||||
if(webHooks.nonEmpty){
|
||||
if (webHooks.nonEmpty) {
|
||||
makePayload.map(callWebHook(event, webHooks, _))
|
||||
}
|
||||
val accountWebHooks = getAccountWebHooksByEvent(owner, event)
|
||||
if(accountWebHooks.nonEmpty){
|
||||
if (accountWebHooks.nonEmpty) {
|
||||
makePayload.map(callWebHook(event, accountWebHooks, _))
|
||||
}
|
||||
}
|
||||
|
||||
def callWebHook(event: WebHook.Event, webHooks: List[WebHook], payload: WebHookPayload)
|
||||
(implicit c: JsonFormat.Context): List[(WebHook, String, Future[HttpRequest], Future[HttpResponse])] = {
|
||||
def callWebHook(event: WebHook.Event, webHooks: List[WebHook], payload: WebHookPayload)(
|
||||
implicit c: JsonFormat.Context
|
||||
): List[(WebHook, String, Future[HttpRequest], Future[HttpResponse])] = {
|
||||
import org.apache.http.impl.client.HttpClientBuilder
|
||||
import ExecutionContext.Implicits.global // TODO Shouldn't use the default execution context
|
||||
import org.apache.http.protocol.HttpContext
|
||||
import org.apache.http.client.methods.HttpPost
|
||||
|
||||
if(webHooks.nonEmpty){
|
||||
if (webHooks.nonEmpty) {
|
||||
val json = JsonFormat(payload)
|
||||
|
||||
webHooks.map { webHook =>
|
||||
@@ -143,7 +229,7 @@ trait WebHookService {
|
||||
reqPromise.success(res)
|
||||
}
|
||||
}
|
||||
try{
|
||||
try {
|
||||
val httpClient = HttpClientBuilder.create.useSystemProperties.addInterceptorLast(itcp).build
|
||||
logger.debug(s"start web hook invocation for ${webHook.url}")
|
||||
val httpPost = new HttpPost(webHook.url)
|
||||
@@ -154,20 +240,38 @@ trait WebHookService {
|
||||
|
||||
webHook.ctype match {
|
||||
case WebHookContentType.FORM => {
|
||||
val params: java.util.List[NameValuePair] = new java.util.ArrayList()
|
||||
val params: java.util.List[NameValuePair] = new java.util.ArrayList()
|
||||
params.add(new BasicNameValuePair("payload", json))
|
||||
def postContent = new UrlEncodedFormEntity(params, "UTF-8")
|
||||
httpPost.setEntity(postContent)
|
||||
if (webHook.token.exists(_.trim.nonEmpty)) {
|
||||
// TODO find a better way and see how to extract content from postContent
|
||||
val contentAsBytes = URLEncodedUtils.format(params, "UTF-8").getBytes("UTF-8")
|
||||
httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.get, contentAsBytes))
|
||||
httpPost.addHeader(
|
||||
"X-Hub-Signature",
|
||||
XHub.generateHeaderXHubToken(
|
||||
XHubConverter.HEXA_LOWERCASE,
|
||||
XHubDigest.SHA1,
|
||||
webHook.token.get,
|
||||
contentAsBytes
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
case WebHookContentType.JSON => {
|
||||
httpPost.setEntity(EntityBuilder.create().setContentType(ContentType.APPLICATION_JSON).setText(json).build())
|
||||
httpPost.setEntity(
|
||||
EntityBuilder.create().setContentType(ContentType.APPLICATION_JSON).setText(json).build()
|
||||
)
|
||||
if (webHook.token.exists(_.trim.nonEmpty)) {
|
||||
httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.orNull, json.getBytes("UTF-8")))
|
||||
httpPost.addHeader(
|
||||
"X-Hub-Signature",
|
||||
XHub.generateHeaderXHubToken(
|
||||
XHubConverter.HEXA_LOWERCASE,
|
||||
XHubDigest.SHA1,
|
||||
webHook.token.orNull,
|
||||
json.getBytes("UTF-8")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,7 +282,7 @@ trait WebHookService {
|
||||
res
|
||||
} catch {
|
||||
case e: Throwable => {
|
||||
if(!reqPromise.isCompleted){
|
||||
if (!reqPromise.isCompleted) {
|
||||
reqPromise.failure(e)
|
||||
}
|
||||
throw e
|
||||
@@ -198,103 +302,138 @@ trait WebHookService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
trait WebHookPullRequestService extends WebHookService {
|
||||
self: AccountService with RepositoryService with PullRequestService with IssuesService =>
|
||||
|
||||
import WebHookService._
|
||||
// https://developer.github.com/v3/activity/events/types/#issuesevent
|
||||
def callIssuesWebHook(action: String, repository: RepositoryService.RepositoryInfo, issue: Issue, baseUrl: String, sender: Account)
|
||||
(implicit s: Session, context: JsonFormat.Context): Unit = {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Issues){
|
||||
def callIssuesWebHook(
|
||||
action: String,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issue: Issue,
|
||||
baseUrl: String,
|
||||
sender: Account
|
||||
)(implicit s: Session, context: JsonFormat.Context): Unit = {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.Issues) {
|
||||
val users = getAccountsByUserNames(Set(repository.owner, issue.openedUserName), Set(sender))
|
||||
for{
|
||||
for {
|
||||
repoOwner <- users.get(repository.owner)
|
||||
issueUser <- users.get(issue.openedUserName)
|
||||
} yield {
|
||||
WebHookIssuesPayload(
|
||||
action = action,
|
||||
number = issue.issueId,
|
||||
action = action,
|
||||
number = issue.issueId,
|
||||
repository = ApiRepository(repository, ApiUser(repoOwner)),
|
||||
issue = ApiIssue(issue, RepositoryName(repository), ApiUser(issueUser),
|
||||
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))),
|
||||
sender = ApiUser(sender))
|
||||
issue = ApiIssue(
|
||||
issue,
|
||||
RepositoryName(repository),
|
||||
ApiUser(issueUser),
|
||||
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(repository)))
|
||||
),
|
||||
sender = ApiUser(sender)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def callPullRequestWebHook(action: String, repository: RepositoryService.RepositoryInfo, issueId: Int, baseUrl: String, sender: Account)
|
||||
(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
def callPullRequestWebHook(
|
||||
action: String,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issueId: Int,
|
||||
baseUrl: String,
|
||||
sender: Account
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
import WebHookService._
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequest){
|
||||
for{
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequest) {
|
||||
for {
|
||||
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
|
||||
users = getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
|
||||
users = getAccountsByUserNames(
|
||||
Set(repository.owner, pullRequest.requestUserName, issue.openedUserName),
|
||||
Set(sender)
|
||||
)
|
||||
baseOwner <- users.get(repository.owner)
|
||||
headOwner <- users.get(pullRequest.requestUserName)
|
||||
issueUser <- users.get(issue.openedUserName)
|
||||
assignee = issue.assignedUserName.flatMap { userName => getAccountByUserName(userName, false) }
|
||||
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))
|
||||
assignee = issue.assignedUserName.flatMap { userName =>
|
||||
getAccountByUserName(userName, false)
|
||||
}
|
||||
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(repository)))
|
||||
} yield {
|
||||
WebHookPullRequestPayload(
|
||||
action = action,
|
||||
issue = issue,
|
||||
issueUser = issueUser,
|
||||
assignee = assignee,
|
||||
pullRequest = pullRequest,
|
||||
action = action,
|
||||
issue = issue,
|
||||
issueUser = issueUser,
|
||||
assignee = assignee,
|
||||
pullRequest = pullRequest,
|
||||
headRepository = headRepo,
|
||||
headOwner = headOwner,
|
||||
headOwner = headOwner,
|
||||
baseRepository = repository,
|
||||
baseOwner = baseOwner,
|
||||
labels = labels,
|
||||
sender = sender,
|
||||
mergedComment = getMergedComment(repository.owner, repository.name, issueId)
|
||||
baseOwner = baseOwner,
|
||||
labels = labels,
|
||||
sender = sender,
|
||||
mergedComment = getMergedComment(repository.owner, repository.name, issueId)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return Map[(issue, issueUser, pullRequest, baseOwner, headOwner), webHooks] */
|
||||
def getPullRequestsByRequestForWebhook(userName:String, repositoryName:String, branch:String)
|
||||
(implicit s: Session): Map[(Issue, Account, PullRequest, Account, Account), List[RepositoryWebHook]] =
|
||||
(for{
|
||||
is <- Issues if is.closed === false.bind
|
||||
def getPullRequestsByRequestForWebhook(userName: String, repositoryName: String, branch: String)(
|
||||
implicit s: Session
|
||||
): Map[(Issue, Account, PullRequest, Account, Account), List[RepositoryWebHook]] =
|
||||
(for {
|
||||
is <- Issues if is.closed === false.bind
|
||||
pr <- PullRequests if pr.byPrimaryKey(is.userName, is.repositoryName, is.issueId)
|
||||
if pr.requestUserName === userName.bind
|
||||
if pr.requestRepositoryName === repositoryName.bind
|
||||
if pr.requestBranch === branch.bind
|
||||
if pr.requestUserName === userName.bind
|
||||
if pr.requestRepositoryName === repositoryName.bind
|
||||
if pr.requestBranch === branch.bind
|
||||
bu <- Accounts if bu.userName === pr.userName
|
||||
ru <- Accounts if ru.userName === pr.requestUserName
|
||||
iu <- Accounts if iu.userName === is.openedUserName
|
||||
wh <- RepositoryWebHooks if wh.byRepository(is.userName , is.repositoryName)
|
||||
wht <- RepositoryWebHookEvents if wht.event === WebHook.PullRequest.asInstanceOf[WebHook.Event].bind && wht.byRepositoryWebHook(wh)
|
||||
wh <- RepositoryWebHooks if wh.byRepository(is.userName, is.repositoryName)
|
||||
wht <- RepositoryWebHookEvents
|
||||
if wht.event === WebHook.PullRequest.asInstanceOf[WebHook.Event].bind && wht.byRepositoryWebHook(wh)
|
||||
} yield {
|
||||
((is, iu, pr, bu, ru), wh)
|
||||
}).list.groupBy(_._1).mapValues(_.map(_._2))
|
||||
|
||||
def callPullRequestWebHookByRequestBranch(action: String, requestRepository: RepositoryService.RepositoryInfo, requestBranch: String, baseUrl: String, sender: Account)
|
||||
(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
def callPullRequestWebHookByRequestBranch(
|
||||
action: String,
|
||||
requestRepository: RepositoryService.RepositoryInfo,
|
||||
requestBranch: String,
|
||||
baseUrl: String,
|
||||
sender: Account
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
import WebHookService._
|
||||
for{
|
||||
((issue, issueUser, pullRequest, baseOwner, headOwner), webHooks) <- getPullRequestsByRequestForWebhook(requestRepository.owner, requestRepository.name, requestBranch)
|
||||
assignee = issue.assignedUserName.flatMap { userName => getAccountByUserName(userName, false) }
|
||||
for {
|
||||
((issue, issueUser, pullRequest, baseOwner, headOwner), webHooks) <- getPullRequestsByRequestForWebhook(
|
||||
requestRepository.owner,
|
||||
requestRepository.name,
|
||||
requestBranch
|
||||
)
|
||||
assignee = issue.assignedUserName.flatMap { userName =>
|
||||
getAccountByUserName(userName, false)
|
||||
}
|
||||
baseRepo <- getRepository(pullRequest.userName, pullRequest.repositoryName)
|
||||
labels = getIssueLabels(pullRequest.userName, pullRequest.repositoryName, issue.issueId).map(ApiLabel(_, RepositoryName(pullRequest.userName, pullRequest.repositoryName)))
|
||||
labels = getIssueLabels(pullRequest.userName, pullRequest.repositoryName, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(pullRequest.userName, pullRequest.repositoryName)))
|
||||
} yield {
|
||||
val payload = WebHookPullRequestPayload(
|
||||
action = action,
|
||||
issue = issue,
|
||||
issueUser = issueUser,
|
||||
assignee = assignee,
|
||||
pullRequest = pullRequest,
|
||||
action = action,
|
||||
issue = issue,
|
||||
issueUser = issueUser,
|
||||
assignee = assignee,
|
||||
pullRequest = pullRequest,
|
||||
headRepository = requestRepository,
|
||||
headOwner = headOwner,
|
||||
headOwner = headOwner,
|
||||
baseRepository = baseRepo,
|
||||
baseOwner = baseOwner,
|
||||
labels = labels,
|
||||
sender = sender,
|
||||
mergedComment = getMergedComment(baseRepo.owner, baseRepo.name, issue.issueId)
|
||||
baseOwner = baseOwner,
|
||||
labels = labels,
|
||||
sender = sender,
|
||||
mergedComment = getMergedComment(baseRepo.owner, baseRepo.name, issue.issueId)
|
||||
)
|
||||
|
||||
callWebHook(WebHook.PullRequest, webHooks, payload)
|
||||
@@ -305,34 +444,44 @@ trait WebHookPullRequestService extends WebHookService {
|
||||
|
||||
trait WebHookPullRequestReviewCommentService extends WebHookService {
|
||||
self: AccountService with RepositoryService with PullRequestService with IssuesService with CommitsService =>
|
||||
def callPullRequestReviewCommentWebHook(action: String, comment: CommitComment, repository: RepositoryService.RepositoryInfo,
|
||||
issue: Issue, pullRequest: PullRequest, baseUrl: String, sender: Account)
|
||||
(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
def callPullRequestReviewCommentWebHook(
|
||||
action: String,
|
||||
comment: CommitComment,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issue: Issue,
|
||||
pullRequest: PullRequest,
|
||||
baseUrl: String,
|
||||
sender: Account
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
import WebHookService._
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment){
|
||||
val users = getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
|
||||
for{
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment) {
|
||||
val users =
|
||||
getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
|
||||
for {
|
||||
baseOwner <- users.get(repository.owner)
|
||||
headOwner <- users.get(pullRequest.requestUserName)
|
||||
issueUser <- users.get(issue.openedUserName)
|
||||
assignee = issue.assignedUserName.flatMap { userName => getAccountByUserName(userName, false) }
|
||||
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
|
||||
labels = getIssueLabels(pullRequest.userName, pullRequest.repositoryName, issue.issueId).map(ApiLabel(_, RepositoryName(pullRequest.userName, pullRequest.repositoryName)))
|
||||
assignee = issue.assignedUserName.flatMap { userName =>
|
||||
getAccountByUserName(userName, false)
|
||||
}
|
||||
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
|
||||
labels = getIssueLabels(pullRequest.userName, pullRequest.repositoryName, issue.issueId)
|
||||
.map(ApiLabel(_, RepositoryName(pullRequest.userName, pullRequest.repositoryName)))
|
||||
} yield {
|
||||
WebHookPullRequestReviewCommentPayload(
|
||||
action = action,
|
||||
comment = comment,
|
||||
issue = issue,
|
||||
issueUser = issueUser,
|
||||
assignee = assignee,
|
||||
pullRequest = pullRequest,
|
||||
action = action,
|
||||
comment = comment,
|
||||
issue = issue,
|
||||
issueUser = issueUser,
|
||||
assignee = assignee,
|
||||
pullRequest = pullRequest,
|
||||
headRepository = headRepo,
|
||||
headOwner = headOwner,
|
||||
headOwner = headOwner,
|
||||
baseRepository = repository,
|
||||
baseOwner = baseOwner,
|
||||
labels = labels,
|
||||
sender = sender,
|
||||
mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
|
||||
baseOwner = baseOwner,
|
||||
labels = labels,
|
||||
sender = sender,
|
||||
mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -343,26 +492,34 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
|
||||
self: AccountService with RepositoryService with PullRequestService with IssuesService =>
|
||||
|
||||
import WebHookService._
|
||||
def callIssueCommentWebHook(repository: RepositoryService.RepositoryInfo, issue: Issue, issueCommentId: Int, sender: Account)
|
||||
(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.IssueComment){
|
||||
for{
|
||||
def callIssueCommentWebHook(
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
issue: Issue,
|
||||
issueCommentId: Int,
|
||||
sender: Account
|
||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||
callWebHookOf(repository.owner, repository.name, WebHook.IssueComment) {
|
||||
for {
|
||||
issueComment <- getComment(repository.owner, repository.name, issueCommentId.toString())
|
||||
users = getAccountsByUserNames(Set(issue.openedUserName, repository.owner, issueComment.commentedUserName), Set(sender))
|
||||
users = getAccountsByUserNames(
|
||||
Set(issue.openedUserName, repository.owner, issueComment.commentedUserName),
|
||||
Set(sender)
|
||||
)
|
||||
issueUser <- users.get(issue.openedUserName)
|
||||
repoOwner <- users.get(repository.owner)
|
||||
commenter <- users.get(issueComment.commentedUserName)
|
||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||
} yield {
|
||||
WebHookIssueCommentPayload(
|
||||
issue = issue,
|
||||
issueUser = issueUser,
|
||||
comment = issueComment,
|
||||
commentUser = commenter,
|
||||
repository = repository,
|
||||
issue = issue,
|
||||
issueUser = issueUser,
|
||||
comment = issueComment,
|
||||
commentUser = commenter,
|
||||
repository = repository,
|
||||
repositoryUser = repoOwner,
|
||||
sender = sender,
|
||||
labels = labels)
|
||||
sender = sender,
|
||||
labels = labels
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,24 +536,30 @@ object WebHookService {
|
||||
ref_type: String,
|
||||
master_branch: String,
|
||||
repository: ApiRepository
|
||||
) extends FieldSerializable with WebHookPayload {
|
||||
) extends FieldSerializable
|
||||
with WebHookPayload {
|
||||
val pusher_type = "user"
|
||||
}
|
||||
|
||||
object WebHookCreatePayload {
|
||||
|
||||
def apply(git: Git, sender: Account, refName: String, repositoryInfo: RepositoryInfo,
|
||||
commits: List[CommitInfo], repositoryOwner: Account,
|
||||
ref: String, refType: String): WebHookCreatePayload =
|
||||
def apply(
|
||||
git: Git,
|
||||
sender: Account,
|
||||
refName: String,
|
||||
repositoryInfo: RepositoryInfo,
|
||||
commits: List[CommitInfo],
|
||||
repositoryOwner: Account,
|
||||
ref: String,
|
||||
refType: String
|
||||
): WebHookCreatePayload =
|
||||
WebHookCreatePayload(
|
||||
sender = ApiUser(sender),
|
||||
ref = ref,
|
||||
ref_type = refType,
|
||||
description = repositoryInfo.repository.description.getOrElse(""),
|
||||
sender = ApiUser(sender),
|
||||
ref = ref,
|
||||
ref_type = refType,
|
||||
description = repositoryInfo.repository.description.getOrElse(""),
|
||||
master_branch = repositoryInfo.repository.defaultBranch,
|
||||
repository = ApiRepository.forWebhookPayload(
|
||||
repositoryInfo,
|
||||
owner= ApiUser(repositoryOwner))
|
||||
repository = ApiRepository.forWebhookPayload(repositoryInfo, owner = ApiUser(repositoryOwner))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -409,30 +572,38 @@ object WebHookService {
|
||||
after: String,
|
||||
commits: List[ApiCommit],
|
||||
repository: ApiRepository
|
||||
) extends FieldSerializable with WebHookPayload {
|
||||
) extends FieldSerializable
|
||||
with WebHookPayload {
|
||||
val compare = commits.size match {
|
||||
case 0 => ApiPath(s"/${repository.full_name}") // maybe test hook on un-initialized repository
|
||||
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 _ => ApiPath(s"/${repository.full_name}/compare/${before}...${after}")
|
||||
case 0 => ApiPath(s"/${repository.full_name}") // maybe test hook on un-initialized repository
|
||||
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 _ => ApiPath(s"/${repository.full_name}/compare/${before}...${after}")
|
||||
}
|
||||
val head_commit = commits.lastOption
|
||||
}
|
||||
|
||||
object WebHookPushPayload {
|
||||
def apply(git: Git, sender: Account, refName: String, repositoryInfo: RepositoryInfo,
|
||||
commits: List[CommitInfo], repositoryOwner: Account,
|
||||
newId: ObjectId, oldId: ObjectId): WebHookPushPayload =
|
||||
def apply(
|
||||
git: Git,
|
||||
sender: Account,
|
||||
refName: String,
|
||||
repositoryInfo: RepositoryInfo,
|
||||
commits: List[CommitInfo],
|
||||
repositoryOwner: Account,
|
||||
newId: ObjectId,
|
||||
oldId: ObjectId
|
||||
): WebHookPushPayload =
|
||||
WebHookPushPayload(
|
||||
pusher = ApiPusher(sender),
|
||||
sender = ApiUser(sender),
|
||||
ref = refName,
|
||||
before = ObjectId.toString(oldId),
|
||||
after = ObjectId.toString(newId),
|
||||
commits = commits.map{ commit => ApiCommit.forWebhookPayload(git, RepositoryName(repositoryInfo), commit) },
|
||||
repository = ApiRepository.forWebhookPayload(
|
||||
repositoryInfo,
|
||||
owner= ApiUser(repositoryOwner))
|
||||
pusher = ApiPusher(sender),
|
||||
sender = ApiUser(sender),
|
||||
ref = refName,
|
||||
before = ObjectId.toString(oldId),
|
||||
after = ObjectId.toString(newId),
|
||||
commits = commits.map { commit =>
|
||||
ApiCommit.forWebhookPayload(git, RepositoryName(repositoryInfo), commit)
|
||||
},
|
||||
repository = ApiRepository.forWebhookPayload(repositoryInfo, owner = ApiUser(repositoryOwner))
|
||||
)
|
||||
|
||||
def createDummyPayload(sender: Account): WebHookPushPayload =
|
||||
@@ -453,7 +624,8 @@ object WebHookService {
|
||||
number: Int,
|
||||
repository: ApiRepository,
|
||||
issue: ApiIssue,
|
||||
sender: ApiUser) extends WebHookPayload
|
||||
sender: ApiUser
|
||||
) extends WebHookPayload
|
||||
|
||||
// https://developer.github.com/v3/activity/events/types/#pullrequestevent
|
||||
case class WebHookPullRequestPayload(
|
||||
@@ -464,40 +636,42 @@ object WebHookService {
|
||||
sender: ApiUser
|
||||
) extends WebHookPayload
|
||||
|
||||
object WebHookPullRequestPayload{
|
||||
def apply(action: String,
|
||||
issue: Issue,
|
||||
issueUser: Account,
|
||||
assignee: Option[Account],
|
||||
pullRequest: PullRequest,
|
||||
headRepository: RepositoryInfo,
|
||||
headOwner: Account,
|
||||
baseRepository: RepositoryInfo,
|
||||
baseOwner: Account,
|
||||
labels: List[ApiLabel],
|
||||
sender: Account,
|
||||
mergedComment: Option[(IssueComment, Account)]): WebHookPullRequestPayload = {
|
||||
object WebHookPullRequestPayload {
|
||||
def apply(
|
||||
action: String,
|
||||
issue: Issue,
|
||||
issueUser: Account,
|
||||
assignee: Option[Account],
|
||||
pullRequest: PullRequest,
|
||||
headRepository: RepositoryInfo,
|
||||
headOwner: Account,
|
||||
baseRepository: RepositoryInfo,
|
||||
baseOwner: Account,
|
||||
labels: List[ApiLabel],
|
||||
sender: Account,
|
||||
mergedComment: Option[(IssueComment, Account)]
|
||||
): WebHookPullRequestPayload = {
|
||||
|
||||
val headRepoPayload = ApiRepository(headRepository, headOwner)
|
||||
val baseRepoPayload = ApiRepository(baseRepository, baseOwner)
|
||||
val senderPayload = ApiUser(sender)
|
||||
val pr = ApiPullRequest(
|
||||
issue = issue,
|
||||
pullRequest = pullRequest,
|
||||
headRepo = headRepoPayload,
|
||||
baseRepo = baseRepoPayload,
|
||||
user = ApiUser(issueUser),
|
||||
labels = labels,
|
||||
assignee = assignee.map(ApiUser.apply),
|
||||
issue = issue,
|
||||
pullRequest = pullRequest,
|
||||
headRepo = headRepoPayload,
|
||||
baseRepo = baseRepoPayload,
|
||||
user = ApiUser(issueUser),
|
||||
labels = labels,
|
||||
assignee = assignee.map(ApiUser.apply),
|
||||
mergedComment = mergedComment
|
||||
)
|
||||
|
||||
WebHookPullRequestPayload(
|
||||
action = action,
|
||||
number = issue.issueId,
|
||||
repository = pr.base.repo,
|
||||
action = action,
|
||||
number = issue.issueId,
|
||||
repository = pr.base.repo,
|
||||
pull_request = pr,
|
||||
sender = senderPayload
|
||||
sender = senderPayload
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -513,20 +687,28 @@ object WebHookService {
|
||||
|
||||
object WebHookIssueCommentPayload {
|
||||
def apply(
|
||||
issue: Issue,
|
||||
issueUser: Account,
|
||||
comment: IssueComment,
|
||||
commentUser: Account,
|
||||
repository: RepositoryInfo,
|
||||
repositoryUser: Account,
|
||||
sender: Account,
|
||||
labels: List[Label]): WebHookIssueCommentPayload =
|
||||
issue: Issue,
|
||||
issueUser: Account,
|
||||
comment: IssueComment,
|
||||
commentUser: Account,
|
||||
repository: RepositoryInfo,
|
||||
repositoryUser: Account,
|
||||
sender: Account,
|
||||
labels: List[Label]
|
||||
): WebHookIssueCommentPayload =
|
||||
WebHookIssueCommentPayload(
|
||||
action = "created",
|
||||
repository = ApiRepository(repository, repositoryUser),
|
||||
issue = ApiIssue(issue, RepositoryName(repository), ApiUser(issueUser), labels.map(ApiLabel(_, RepositoryName(repository)))),
|
||||
comment = ApiComment(comment, RepositoryName(repository), issue.issueId, ApiUser(commentUser), issue.isPullRequest),
|
||||
sender = ApiUser(sender))
|
||||
action = "created",
|
||||
repository = ApiRepository(repository, repositoryUser),
|
||||
issue = ApiIssue(
|
||||
issue,
|
||||
RepositoryName(repository),
|
||||
ApiUser(issueUser),
|
||||
labels.map(ApiLabel(_, RepositoryName(repository)))
|
||||
),
|
||||
comment =
|
||||
ApiComment(comment, RepositoryName(repository), issue.issueId, ApiUser(commentUser), issue.isPullRequest),
|
||||
sender = ApiUser(sender)
|
||||
)
|
||||
}
|
||||
|
||||
// https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent
|
||||
@@ -559,25 +741,26 @@ object WebHookService {
|
||||
val senderPayload = ApiUser(sender)
|
||||
|
||||
WebHookPullRequestReviewCommentPayload(
|
||||
action = action,
|
||||
comment = ApiPullRequestReviewComment(
|
||||
comment = comment,
|
||||
commentedUser = senderPayload,
|
||||
action = action,
|
||||
comment = ApiPullRequestReviewComment(
|
||||
comment = comment,
|
||||
commentedUser = senderPayload,
|
||||
repositoryName = RepositoryName(baseRepository),
|
||||
issueId = issue.issueId
|
||||
issueId = issue.issueId
|
||||
),
|
||||
pull_request = ApiPullRequest(
|
||||
issue = issue,
|
||||
pullRequest = pullRequest,
|
||||
headRepo = headRepoPayload,
|
||||
baseRepo = baseRepoPayload,
|
||||
user = ApiUser(issueUser),
|
||||
labels = labels,
|
||||
assignee = assignee.map(ApiUser.apply),
|
||||
issue = issue,
|
||||
pullRequest = pullRequest,
|
||||
headRepo = headRepoPayload,
|
||||
baseRepo = baseRepoPayload,
|
||||
user = ApiUser(issueUser),
|
||||
labels = labels,
|
||||
assignee = assignee.map(ApiUser.apply),
|
||||
mergedComment = mergedComment
|
||||
),
|
||||
repository = baseRepoPayload,
|
||||
sender = senderPayload)
|
||||
repository = baseRepoPayload,
|
||||
sender = senderPayload
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,14 +797,15 @@ object WebHookService {
|
||||
sender: Account
|
||||
): WebHookGollumPayload = {
|
||||
WebHookGollumPayload(
|
||||
pages = pages.map { case (action, pageName, sha) =>
|
||||
WebHookGollumPagePayload(
|
||||
action = action,
|
||||
page_name = pageName,
|
||||
title = pageName,
|
||||
sha = sha,
|
||||
html_url = ApiPath(s"/${RepositoryName(repository).fullName}/wiki/${StringUtil.urlDecode(pageName)}")
|
||||
)
|
||||
pages = pages.map {
|
||||
case (action, pageName, sha) =>
|
||||
WebHookGollumPagePayload(
|
||||
action = action,
|
||||
page_name = pageName,
|
||||
title = pageName,
|
||||
sha = sha,
|
||||
html_url = ApiPath(s"/${RepositoryName(repository).fullName}/wiki/${StringUtil.urlDecode(pageName)}")
|
||||
)
|
||||
},
|
||||
repository = ApiRepository(repository, repositoryUser),
|
||||
sender = ApiUser(sender)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user