mirror of
				https://github.com/gitbucket/gitbucket.git
				synced 2025-11-03 20:15:59 +01:00 
			
		
		
		
	update scalafmt
This commit is contained in:
		@@ -1,12 +1,14 @@
 | 
			
		||||
version = "1.5.1"
 | 
			
		||||
version = "3.7.15"
 | 
			
		||||
project.git = true
 | 
			
		||||
 | 
			
		||||
maxColumn = 120
 | 
			
		||||
docstrings = JavaDoc
 | 
			
		||||
docstrings.style = keep
 | 
			
		||||
 | 
			
		||||
align.tokens = ["%", "%%", {code = "=>", owner = "Case"}]
 | 
			
		||||
align.openParenCallSite = false
 | 
			
		||||
align.openParenDefnSite = false
 | 
			
		||||
continuationIndent.callSite = 2
 | 
			
		||||
continuationIndent.defnSite = 2
 | 
			
		||||
danglingParentheses = true
 | 
			
		||||
danglingParentheses.preset = true
 | 
			
		||||
runner.dialect = scala213source3
 | 
			
		||||
rewrite.trailingCommas.style = keep
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										95
									
								
								build.sbt
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								build.sbt
									
									
									
									
									
								
							@@ -30,47 +30,47 @@ resolvers ++= Seq(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
libraryDependencies ++= Seq(
 | 
			
		||||
  "org.eclipse.jgit"                % "org.eclipse.jgit.http.server" % JgitVersion,
 | 
			
		||||
  "org.eclipse.jgit"                % "org.eclipse.jgit.archive"     % JgitVersion,
 | 
			
		||||
  "org.scalatra"                    %% "scalatra-javax"              % ScalatraVersion,
 | 
			
		||||
  "org.scalatra"                    %% "scalatra-json-javax"         % ScalatraVersion,
 | 
			
		||||
  "org.scalatra"                    %% "scalatra-forms-javax"        % ScalatraVersion,
 | 
			
		||||
  "org.json4s"                      %% "json4s-jackson"              % "4.0.6",
 | 
			
		||||
  "commons-io"                      % "commons-io"                   % "2.15.0",
 | 
			
		||||
  "io.github.gitbucket"             % "solidbase"                    % "1.0.5",
 | 
			
		||||
  "io.github.gitbucket"             % "markedj"                      % "1.0.18",
 | 
			
		||||
  "org.apache.commons"              % "commons-compress"             % "1.25.0",
 | 
			
		||||
  "org.apache.commons"              % "commons-email"                % "1.5",
 | 
			
		||||
  "commons-net"                     % "commons-net"                  % "3.10.0",
 | 
			
		||||
  "org.apache.httpcomponents"       % "httpclient"                   % "4.5.14",
 | 
			
		||||
  "org.apache.sshd"                 % "apache-sshd"                  % "2.11.0" exclude ("org.slf4j", "slf4j-jdk14") exclude ("org.apache.sshd", "sshd-mina") exclude ("org.apache.sshd", "sshd-netty"),
 | 
			
		||||
  "org.apache.tika"                 % "tika-core"                    % "2.9.1",
 | 
			
		||||
  "com.github.takezoe"              %% "blocking-slick"              % "0.0.14",
 | 
			
		||||
  "com.novell.ldap"                 % "jldap"                        % "2009-10-07",
 | 
			
		||||
  "com.h2database"                  % "h2"                           % "1.4.199",
 | 
			
		||||
  "org.mariadb.jdbc"                % "mariadb-java-client"          % "2.7.6",
 | 
			
		||||
  "org.postgresql"                  % "postgresql"                   % "42.6.0",
 | 
			
		||||
  "ch.qos.logback"                  % "logback-classic"              % "1.4.11",
 | 
			
		||||
  "com.zaxxer"                      % "HikariCP"                     % "4.0.3" exclude ("org.slf4j", "slf4j-api"),
 | 
			
		||||
  "com.typesafe"                    % "config"                       % "1.4.3",
 | 
			
		||||
  "fr.brouillard.oss.security.xhub" % "xhub4j-core"                  % "1.1.0",
 | 
			
		||||
  "io.github.java-diff-utils"       % "java-diff-utils"              % "4.12",
 | 
			
		||||
  "org.cache2k"                     % "cache2k-all"                  % "1.6.0.Final",
 | 
			
		||||
  "net.coobird"                     % "thumbnailator"                % "0.4.20",
 | 
			
		||||
  "com.github.zafarkhaja"           % "java-semver"                  % "0.9.0",
 | 
			
		||||
  "com.nimbusds"                    % "oauth2-oidc-sdk"              % "11.6",
 | 
			
		||||
  "org.eclipse.jetty"               % "jetty-webapp"                 % JettyVersion % "provided",
 | 
			
		||||
  "javax.servlet"                   % "javax.servlet-api"            % "3.1.0" % "provided",
 | 
			
		||||
  "junit"                           % "junit"                        % "4.13.2" % "test",
 | 
			
		||||
  "org.scalatra"                    %% "scalatra-scalatest-javax"    % ScalatraVersion % "test",
 | 
			
		||||
  "org.mockito"                     % "mockito-core"                 % "5.7.0" % "test",
 | 
			
		||||
  "com.dimafeng"                    %% "testcontainers-scala"        % "0.41.0" % "test",
 | 
			
		||||
  "org.testcontainers"              % "mysql"                        % "1.19.2" % "test",
 | 
			
		||||
  "org.testcontainers"              % "postgresql"                   % "1.19.2" % "test",
 | 
			
		||||
  "net.i2p.crypto"                  % "eddsa"                        % "0.3.0",
 | 
			
		||||
  "is.tagomor.woothee"              % "woothee-java"                 % "1.11.0",
 | 
			
		||||
  "org.ec4j.core"                   % "ec4j-core"                    % "0.3.0",
 | 
			
		||||
  "org.kohsuke"                     % "github-api"                   % "1.317" % "test"
 | 
			
		||||
  "org.eclipse.jgit"          % "org.eclipse.jgit.http.server" % JgitVersion,
 | 
			
		||||
  "org.eclipse.jgit"          % "org.eclipse.jgit.archive"     % JgitVersion,
 | 
			
		||||
  "org.scalatra"             %% "scalatra-javax"               % ScalatraVersion,
 | 
			
		||||
  "org.scalatra"             %% "scalatra-json-javax"          % ScalatraVersion,
 | 
			
		||||
  "org.scalatra"             %% "scalatra-forms-javax"         % ScalatraVersion,
 | 
			
		||||
  "org.json4s"               %% "json4s-jackson"               % "4.0.6",
 | 
			
		||||
  "commons-io"                % "commons-io"                   % "2.15.0",
 | 
			
		||||
  "io.github.gitbucket"       % "solidbase"                    % "1.0.5",
 | 
			
		||||
  "io.github.gitbucket"       % "markedj"                      % "1.0.18",
 | 
			
		||||
  "org.apache.commons"        % "commons-compress"             % "1.25.0",
 | 
			
		||||
  "org.apache.commons"        % "commons-email"                % "1.5",
 | 
			
		||||
  "commons-net"               % "commons-net"                  % "3.10.0",
 | 
			
		||||
  "org.apache.httpcomponents" % "httpclient"                   % "4.5.14",
 | 
			
		||||
  "org.apache.sshd" % "apache-sshd" % "2.11.0" exclude ("org.slf4j", "slf4j-jdk14") exclude ("org.apache.sshd", "sshd-mina") exclude ("org.apache.sshd", "sshd-netty"),
 | 
			
		||||
  "org.apache.tika"                 % "tika-core"                % "2.9.1",
 | 
			
		||||
  "com.github.takezoe"             %% "blocking-slick"           % "0.0.14",
 | 
			
		||||
  "com.novell.ldap"                 % "jldap"                    % "2009-10-07",
 | 
			
		||||
  "com.h2database"                  % "h2"                       % "1.4.199",
 | 
			
		||||
  "org.mariadb.jdbc"                % "mariadb-java-client"      % "2.7.6",
 | 
			
		||||
  "org.postgresql"                  % "postgresql"               % "42.6.0",
 | 
			
		||||
  "ch.qos.logback"                  % "logback-classic"          % "1.4.11",
 | 
			
		||||
  "com.zaxxer"                      % "HikariCP"                 % "4.0.3" exclude ("org.slf4j", "slf4j-api"),
 | 
			
		||||
  "com.typesafe"                    % "config"                   % "1.4.3",
 | 
			
		||||
  "fr.brouillard.oss.security.xhub" % "xhub4j-core"              % "1.1.0",
 | 
			
		||||
  "io.github.java-diff-utils"       % "java-diff-utils"          % "4.12",
 | 
			
		||||
  "org.cache2k"                     % "cache2k-all"              % "1.6.0.Final",
 | 
			
		||||
  "net.coobird"                     % "thumbnailator"            % "0.4.20",
 | 
			
		||||
  "com.github.zafarkhaja"           % "java-semver"              % "0.9.0",
 | 
			
		||||
  "com.nimbusds"                    % "oauth2-oidc-sdk"          % "11.6",
 | 
			
		||||
  "org.eclipse.jetty"               % "jetty-webapp"             % JettyVersion    % "provided",
 | 
			
		||||
  "javax.servlet"                   % "javax.servlet-api"        % "3.1.0"         % "provided",
 | 
			
		||||
  "junit"                           % "junit"                    % "4.13.2"        % "test",
 | 
			
		||||
  "org.scalatra"                   %% "scalatra-scalatest-javax" % ScalatraVersion % "test",
 | 
			
		||||
  "org.mockito"                     % "mockito-core"             % "5.7.0"         % "test",
 | 
			
		||||
  "com.dimafeng"                   %% "testcontainers-scala"     % "0.41.0"        % "test",
 | 
			
		||||
  "org.testcontainers"              % "mysql"                    % "1.19.2"        % "test",
 | 
			
		||||
  "org.testcontainers"              % "postgresql"               % "1.19.2"        % "test",
 | 
			
		||||
  "net.i2p.crypto"                  % "eddsa"                    % "0.3.0",
 | 
			
		||||
  "is.tagomor.woothee"              % "woothee-java"             % "1.11.0",
 | 
			
		||||
  "org.ec4j.core"                   % "ec4j-core"                % "0.3.0",
 | 
			
		||||
  "org.kohsuke"                     % "github-api"               % "1.317"         % "test"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Compiler settings
 | 
			
		||||
@@ -115,8 +115,8 @@ assembly / assemblyMergeStrategy := {
 | 
			
		||||
 | 
			
		||||
// Exclude a war file from published artifacts
 | 
			
		||||
signedArtifacts := {
 | 
			
		||||
  signedArtifacts.value.filterNot {
 | 
			
		||||
    case (_, file) => file.getName.endsWith(".war") || file.getName.endsWith(".war.asc")
 | 
			
		||||
  signedArtifacts.value.filterNot { case (_, file) =>
 | 
			
		||||
    file.getName.endsWith(".war") || file.getName.endsWith(".war.asc")
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -205,10 +205,9 @@ executableKey := {
 | 
			
		||||
    "md5" -> "MD5",
 | 
			
		||||
    "sha1" -> "SHA-1",
 | 
			
		||||
    "sha256" -> "SHA-256"
 | 
			
		||||
  ).foreach {
 | 
			
		||||
    case (extension, algorithm) =>
 | 
			
		||||
      val checksumFile = workDir / (warName + "." + extension)
 | 
			
		||||
      Checksums generate (outputFile, checksumFile, algorithm)
 | 
			
		||||
  ).foreach { case (extension, algorithm) =>
 | 
			
		||||
    val checksumFile = workDir / (warName + "." + extension)
 | 
			
		||||
    Checksums generate (outputFile, checksumFile, algorithm)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // done
 | 
			
		||||
@@ -289,5 +288,5 @@ Jetty / javaOptions ++= Seq(
 | 
			
		||||
  "-Xdebug",
 | 
			
		||||
  "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000",
 | 
			
		||||
  "-Dorg.eclipse.jetty.annotations.AnnotationParser.LEVEL=OFF",
 | 
			
		||||
  //"-Ddev-features=keep-session"
 | 
			
		||||
  // "-Ddev-features=keep-session"
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -76,21 +76,20 @@ object GitBucketCoreModule
 | 
			
		||||
            import JDBCUtil._
 | 
			
		||||
 | 
			
		||||
            val conn = context.get(Solidbase.CONNECTION).asInstanceOf[Connection]
 | 
			
		||||
            val list = conn.select("SELECT * FROM ACTIVITY ORDER BY ACTIVITY_ID") {
 | 
			
		||||
              rs =>
 | 
			
		||||
                Activity(
 | 
			
		||||
                  activityId = UUID.randomUUID().toString,
 | 
			
		||||
                  userName = rs.getString("USER_NAME"),
 | 
			
		||||
                  repositoryName = rs.getString("REPOSITORY_NAME"),
 | 
			
		||||
                  activityUserName = rs.getString("ACTIVITY_USER_NAME"),
 | 
			
		||||
                  activityType = rs.getString("ACTIVITY_TYPE"),
 | 
			
		||||
                  message = rs.getString("MESSAGE"),
 | 
			
		||||
                  additionalInfo = {
 | 
			
		||||
                    val additionalInfo = rs.getString("ADDITIONAL_INFO")
 | 
			
		||||
                    if (rs.wasNull()) None else Some(additionalInfo)
 | 
			
		||||
                  },
 | 
			
		||||
                  activityDate = rs.getTimestamp("ACTIVITY_DATE")
 | 
			
		||||
                )
 | 
			
		||||
            val list = conn.select("SELECT * FROM ACTIVITY ORDER BY ACTIVITY_ID") { rs =>
 | 
			
		||||
              Activity(
 | 
			
		||||
                activityId = UUID.randomUUID().toString,
 | 
			
		||||
                userName = rs.getString("USER_NAME"),
 | 
			
		||||
                repositoryName = rs.getString("REPOSITORY_NAME"),
 | 
			
		||||
                activityUserName = rs.getString("ACTIVITY_USER_NAME"),
 | 
			
		||||
                activityType = rs.getString("ACTIVITY_TYPE"),
 | 
			
		||||
                message = rs.getString("MESSAGE"),
 | 
			
		||||
                additionalInfo = {
 | 
			
		||||
                  val additionalInfo = rs.getString("ADDITIONAL_INFO")
 | 
			
		||||
                  if (rs.wasNull()) None else Some(additionalInfo)
 | 
			
		||||
                },
 | 
			
		||||
                activityDate = rs.getTimestamp("ACTIVITY_DATE")
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
            Using.resource(new FileOutputStream(ActivityLog, true)) { out =>
 | 
			
		||||
              list.foreach { activity =>
 | 
			
		||||
 
 | 
			
		||||
@@ -66,16 +66,17 @@ object ApiBranchProtection {
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  implicit val enforcementLevelSerializer: CustomSerializer[EnforcementLevel] = new CustomSerializer[EnforcementLevel](
 | 
			
		||||
    format =>
 | 
			
		||||
  implicit val enforcementLevelSerializer: CustomSerializer[EnforcementLevel] =
 | 
			
		||||
    new CustomSerializer[EnforcementLevel](format =>
 | 
			
		||||
      (
 | 
			
		||||
        {
 | 
			
		||||
          case JString("off")        => Off
 | 
			
		||||
          case JString("non_admins") => NonAdmins
 | 
			
		||||
          case JString("everyone")   => Everyone
 | 
			
		||||
        }, {
 | 
			
		||||
          case x: EnforcementLevel => JString(x.name)
 | 
			
		||||
        },
 | 
			
		||||
        { case x: EnforcementLevel =>
 | 
			
		||||
          JString(x.name)
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,8 @@ case class ApiComment(id: Int, user: ApiUser, body: String, created_at: Date, up
 | 
			
		||||
  isPullRequest: Boolean
 | 
			
		||||
) {
 | 
			
		||||
  val html_url = ApiPath(
 | 
			
		||||
    s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" } else { "issues" }}/${issueId}#comment-${id}"
 | 
			
		||||
    s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" }
 | 
			
		||||
      else { "issues" }}/${issueId}#comment-${id}"
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,8 @@ object ApiCommits {
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      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,
 | 
			
		||||
@@ -106,7 +107,9 @@ object ApiCommits {
 | 
			
		||||
        message = commitInfo.shortMessage,
 | 
			
		||||
        comment_count = commentCount,
 | 
			
		||||
        tree = Tree(
 | 
			
		||||
          url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/tree/${commitInfo.id}"), // TODO This endpoint has not been implemented yet.
 | 
			
		||||
          url = ApiPath(
 | 
			
		||||
            s"/api/v3/repos/${repositoryName.fullName}/tree/${commitInfo.id}"
 | 
			
		||||
          ), // TODO This endpoint has not been implemented yet.
 | 
			
		||||
          sha = commitInfo.id
 | 
			
		||||
        )
 | 
			
		||||
      ),
 | 
			
		||||
@@ -114,7 +117,9 @@ object ApiCommits {
 | 
			
		||||
      committer = ApiUser(committer),
 | 
			
		||||
      parents = commitInfo.parents.map { parent =>
 | 
			
		||||
        Tree(
 | 
			
		||||
          url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/tree/${parent}"), // TODO This endpoint has not been implemented yet.
 | 
			
		||||
          url = ApiPath(
 | 
			
		||||
            s"/api/v3/repos/${repositoryName.fullName}/tree/${parent}"
 | 
			
		||||
          ), // TODO This endpoint has not been implemented yet.
 | 
			
		||||
          sha = parent
 | 
			
		||||
        )
 | 
			
		||||
      },
 | 
			
		||||
 
 | 
			
		||||
@@ -22,16 +22,15 @@ object ApiContents {
 | 
			
		||||
      ApiContents("dir", fileInfo.name, fileInfo.path, fileInfo.commitId, None, None)(repositoryName)
 | 
			
		||||
    } else {
 | 
			
		||||
      content
 | 
			
		||||
        .map(
 | 
			
		||||
          arr =>
 | 
			
		||||
            ApiContents(
 | 
			
		||||
              "file",
 | 
			
		||||
              fileInfo.name,
 | 
			
		||||
              fileInfo.path,
 | 
			
		||||
              fileInfo.id.getName,
 | 
			
		||||
              Some(Base64.getEncoder.encodeToString(arr)),
 | 
			
		||||
              Some("base64")
 | 
			
		||||
            )(repositoryName)
 | 
			
		||||
        .map(arr =>
 | 
			
		||||
          ApiContents(
 | 
			
		||||
            "file",
 | 
			
		||||
            fileInfo.name,
 | 
			
		||||
            fileInfo.path,
 | 
			
		||||
            fileInfo.id.getName,
 | 
			
		||||
            Some(Base64.getEncoder.encodeToString(arr)),
 | 
			
		||||
            Some("base64")
 | 
			
		||||
          )(repositoryName)
 | 
			
		||||
        )
 | 
			
		||||
        .getOrElse(ApiContents("file", fileInfo.name, fileInfo.path, fileInfo.commitId, None, None)(repositoryName))
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,8 @@ case class ApiIssue(
 | 
			
		||||
  val id = 0 // dummy id
 | 
			
		||||
  val assignee = assignees.headOption
 | 
			
		||||
  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(
 | 
			
		||||
@@ -54,7 +55,8 @@ object ApiIssue {
 | 
			
		||||
      assignees = assignees,
 | 
			
		||||
      labels = labels,
 | 
			
		||||
      milestone = milestone,
 | 
			
		||||
      state = if (issue.closed) { "closed" } else { "open" },
 | 
			
		||||
      state = if (issue.closed) { "closed" }
 | 
			
		||||
      else { "open" },
 | 
			
		||||
      body = issue.content.getOrElse(""),
 | 
			
		||||
      created_at = issue.registeredDate,
 | 
			
		||||
      updated_at = issue.updatedDate
 | 
			
		||||
 
 | 
			
		||||
@@ -27,10 +27,10 @@ case class ApiPullRequest(
 | 
			
		||||
  val id = 0 // dummy id
 | 
			
		||||
  val assignee = assignees.headOption
 | 
			
		||||
  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 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 issue_url           = ApiPath(s"${base.repo.url.path}/issues/${number}")
 | 
			
		||||
  // val issue_url           = ApiPath(s"${base.repo.url.path}/issues/${number}")
 | 
			
		||||
  val commits_url = ApiPath(s"${base.repo.url.path}/pulls/${number}/commits")
 | 
			
		||||
  val 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}")
 | 
			
		||||
@@ -69,7 +69,8 @@ object ApiPullRequest {
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  case class Commit(sha: String, ref: String, repo: ApiRepository)(baseOwner: String) {
 | 
			
		||||
    val label = if (baseOwner == repo.owner.login) { ref } else { s"${repo.owner.login}:${ref}" }
 | 
			
		||||
    val label = if (baseOwner == repo.owner.login) { ref }
 | 
			
		||||
    else { s"${repo.owner.login}:${ref}" }
 | 
			
		||||
    val user = repo.owner
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -40,12 +40,12 @@ object ApiRef {
 | 
			
		||||
    ApiRef(
 | 
			
		||||
      ref = s"refs/tags/${tagInfo.name}",
 | 
			
		||||
      url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/git/refs/tags/${tagInfo.name}"),
 | 
			
		||||
      //the GH api distinguishes between "releases" and plain git tags
 | 
			
		||||
      //for "releases", the api returns a reference to the release object (with type `tag`)
 | 
			
		||||
      //this would be something like s"/api/v3/repos/${repositoryName.fullName}/git/tags/<hash-of-tag>"
 | 
			
		||||
      //with a hash for the tag, which I do not fully understand
 | 
			
		||||
      //since this is not yet implemented in GB, we always return a link to the plain `commit` object,
 | 
			
		||||
      //which GH does for tags that are not annotated
 | 
			
		||||
      // the GH api distinguishes between "releases" and plain git tags
 | 
			
		||||
      // for "releases", the api returns a reference to the release object (with type `tag`)
 | 
			
		||||
      // this would be something like s"/api/v3/repos/${repositoryName.fullName}/git/tags/<hash-of-tag>"
 | 
			
		||||
      // with a hash for the tag, which I do not fully understand
 | 
			
		||||
      // since this is not yet implemented in GB, we always return a link to the plain `commit` object,
 | 
			
		||||
      // which GH does for tags that are not annotated
 | 
			
		||||
      `object` = ApiRefCommit(
 | 
			
		||||
        sha = tagInfo.objectId,
 | 
			
		||||
        url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/commits/${tagInfo.objectId}"),
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,8 @@ object ApiUser {
 | 
			
		||||
  def apply(user: Account): ApiUser = ApiUser(
 | 
			
		||||
    login = user.userName,
 | 
			
		||||
    email = user.mailAddress,
 | 
			
		||||
    `type` = if (user.isGroupAccount) { "Organization" } else { "User" },
 | 
			
		||||
    `type` = if (user.isGroupAccount) { "Organization" }
 | 
			
		||||
    else { "User" },
 | 
			
		||||
    site_admin = user.isAdmin,
 | 
			
		||||
    created_at = user.registeredDate
 | 
			
		||||
  )
 | 
			
		||||
 
 | 
			
		||||
@@ -15,13 +15,12 @@ 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[ApiGroup]() +
 | 
			
		||||
@@ -48,29 +47,32 @@ object JsonFormat {
 | 
			
		||||
    ApiBranchProtection.enforcementLevelSerializer
 | 
			
		||||
 | 
			
		||||
  def apiPathSerializer(c: Context) =
 | 
			
		||||
    new CustomSerializer[ApiPath](
 | 
			
		||||
      _ =>
 | 
			
		||||
        ({
 | 
			
		||||
    new CustomSerializer[ApiPath](_ =>
 | 
			
		||||
      (
 | 
			
		||||
        {
 | 
			
		||||
          case JString(s) if s.startsWith(c.baseUrl) => ApiPath(s.substring(c.baseUrl.length))
 | 
			
		||||
          case JString(s)                            => throw new MappingException("Can't convert " + s + " to ApiPath")
 | 
			
		||||
        }, {
 | 
			
		||||
          case ApiPath(path) => JString(c.baseUrl + path)
 | 
			
		||||
        })
 | 
			
		||||
        },
 | 
			
		||||
        { case ApiPath(path) =>
 | 
			
		||||
          JString(c.baseUrl + path)
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  def sshPathSerializer(c: Context) =
 | 
			
		||||
    new CustomSerializer[SshPath](
 | 
			
		||||
      _ =>
 | 
			
		||||
        ({
 | 
			
		||||
    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
 | 
			
		||||
        })
 | 
			
		||||
        },
 | 
			
		||||
        { case SshPath(path) =>
 | 
			
		||||
          c.sshUrl.map { sshUrl =>
 | 
			
		||||
            JString(sshUrl + path)
 | 
			
		||||
          } getOrElse JNothing
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
 
 | 
			
		||||
@@ -215,9 +215,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
      "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)
 | 
			
		||||
    )
 | 
			
		||||
    )((url, events, ctype, token) => AccountWebHookForm(url, events, WebHookContentType.valueOf(ctype), token))
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Provides duplication check for web hook url. duplicated from RepositorySettingsController.scala
 | 
			
		||||
@@ -340,22 +338,21 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
 | 
			
		||||
  post("/:userName/_edit", editForm)(oneselfOnly { form =>
 | 
			
		||||
    val userName = params("userName")
 | 
			
		||||
    getAccountByUserName(userName).map {
 | 
			
		||||
      account =>
 | 
			
		||||
        updateAccount(
 | 
			
		||||
          account.copy(
 | 
			
		||||
            password = form.password.map(pbkdf2_sha256).getOrElse(account.password),
 | 
			
		||||
            fullName = form.fullName,
 | 
			
		||||
            mailAddress = form.mailAddress,
 | 
			
		||||
            description = form.description,
 | 
			
		||||
            url = form.url
 | 
			
		||||
          )
 | 
			
		||||
    getAccountByUserName(userName).map { account =>
 | 
			
		||||
      updateAccount(
 | 
			
		||||
        account.copy(
 | 
			
		||||
          password = form.password.map(pbkdf2_sha256).getOrElse(account.password),
 | 
			
		||||
          fullName = form.fullName,
 | 
			
		||||
          mailAddress = form.mailAddress,
 | 
			
		||||
          description = form.description,
 | 
			
		||||
          url = form.url
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
        updateImage(userName, form.fileId, form.clearImage)
 | 
			
		||||
        updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
 | 
			
		||||
        flash.update("info", "Account information has been updated.")
 | 
			
		||||
        redirect(s"/${userName}/_edit")
 | 
			
		||||
      updateImage(userName, form.fileId, form.clearImage)
 | 
			
		||||
      updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
 | 
			
		||||
      flash.update("info", "Account information has been updated.")
 | 
			
		||||
      redirect(s"/${userName}/_edit")
 | 
			
		||||
 | 
			
		||||
    } getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
@@ -363,12 +360,11 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
  get("/:userName/_delete")(oneselfOnly {
 | 
			
		||||
    val userName = params("userName")
 | 
			
		||||
 | 
			
		||||
    getAccountByUserName(userName, true).map {
 | 
			
		||||
      account =>
 | 
			
		||||
        if (isLastAdministrator(account)) {
 | 
			
		||||
          flash.update("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.update("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)
 | 
			
		||||
@@ -376,10 +372,10 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
//        FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
 | 
			
		||||
//        FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
 | 
			
		||||
//      }
 | 
			
		||||
          suspendAccount(account)
 | 
			
		||||
          session.invalidate
 | 
			
		||||
          redirect("/")
 | 
			
		||||
        }
 | 
			
		||||
        suspendAccount(account)
 | 
			
		||||
        session.invalidate
 | 
			
		||||
        redirect("/")
 | 
			
		||||
      }
 | 
			
		||||
    } getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -406,7 +402,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
  get("/:userName/_gpg")(oneselfOnly {
 | 
			
		||||
    val userName = params("userName")
 | 
			
		||||
    getAccountByUserName(userName).map { x =>
 | 
			
		||||
      //html.ssh(x, getPublicKeys(x.userName))
 | 
			
		||||
      // html.ssh(x, getPublicKeys(x.userName))
 | 
			
		||||
      html.gpg(x, getGpgPublicKeys(x.userName))
 | 
			
		||||
    } getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
@@ -525,9 +521,8 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
  get("/:userName/_hooks/edit")(managersOnly {
 | 
			
		||||
    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()
 | 
			
		||||
  })
 | 
			
		||||
@@ -584,11 +579,10 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
        "url" -> url,
 | 
			
		||||
        "request" -> Await.result(
 | 
			
		||||
          reqFuture
 | 
			
		||||
            .map(
 | 
			
		||||
              req =>
 | 
			
		||||
                Map(
 | 
			
		||||
                  "headers" -> _headers(req.getAllHeaders),
 | 
			
		||||
                  "payload" -> json
 | 
			
		||||
            .map(req =>
 | 
			
		||||
              Map(
 | 
			
		||||
                "headers" -> _headers(req.getAllHeaders),
 | 
			
		||||
                "payload" -> json
 | 
			
		||||
              )
 | 
			
		||||
            )
 | 
			
		||||
            .recover(toErrorMap),
 | 
			
		||||
@@ -596,12 +590,11 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
        ),
 | 
			
		||||
        "response" -> Await.result(
 | 
			
		||||
          resFuture
 | 
			
		||||
            .map(
 | 
			
		||||
              res =>
 | 
			
		||||
                Map(
 | 
			
		||||
                  "status" -> res.getStatusLine(),
 | 
			
		||||
                  "body" -> EntityUtils.toString(res.getEntity()),
 | 
			
		||||
                  "headers" -> _headers(res.getAllHeaders())
 | 
			
		||||
            .map(res =>
 | 
			
		||||
              Map(
 | 
			
		||||
                "status" -> res.getStatusLine(),
 | 
			
		||||
                "body" -> EntityUtils.toString(res.getEntity()),
 | 
			
		||||
                "headers" -> _headers(res.getAllHeaders())
 | 
			
		||||
              )
 | 
			
		||||
            )
 | 
			
		||||
            .recover(toErrorMap),
 | 
			
		||||
@@ -788,83 +781,83 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
   * Create new repository.
 | 
			
		||||
   */
 | 
			
		||||
  post("/new", newRepositoryForm)(usersOnly { form =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (context.settings.basicBehavior.repositoryOperation.create || loginAccount.isAdmin) {
 | 
			
		||||
          LockUtil.lock(s"${form.owner}/${form.name}") {
 | 
			
		||||
            if (getRepository(form.owner, form.name).isDefined) {
 | 
			
		||||
              // redirect to the repository if repository already exists
 | 
			
		||||
              redirect(s"/${form.owner}/${form.name}")
 | 
			
		||||
            } else if (!canCreateRepository(form.owner, loginAccount)) {
 | 
			
		||||
              // Permission error
 | 
			
		||||
              Forbidden()
 | 
			
		||||
            } else {
 | 
			
		||||
              // create repository asynchronously
 | 
			
		||||
              createRepository(
 | 
			
		||||
                loginAccount,
 | 
			
		||||
                form.owner,
 | 
			
		||||
                form.name,
 | 
			
		||||
                form.description,
 | 
			
		||||
                form.isPrivate,
 | 
			
		||||
                form.initOption,
 | 
			
		||||
                form.sourceUrl,
 | 
			
		||||
                context.settings.defaultBranch
 | 
			
		||||
              )
 | 
			
		||||
              // redirect to the repository
 | 
			
		||||
              redirect(s"/${form.owner}/${form.name}")
 | 
			
		||||
            }
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (context.settings.basicBehavior.repositoryOperation.create || loginAccount.isAdmin) {
 | 
			
		||||
        LockUtil.lock(s"${form.owner}/${form.name}") {
 | 
			
		||||
          if (getRepository(form.owner, form.name).isDefined) {
 | 
			
		||||
            // redirect to the repository if repository already exists
 | 
			
		||||
            redirect(s"/${form.owner}/${form.name}")
 | 
			
		||||
          } else if (!canCreateRepository(form.owner, loginAccount)) {
 | 
			
		||||
            // Permission error
 | 
			
		||||
            Forbidden()
 | 
			
		||||
          } else {
 | 
			
		||||
            // create repository asynchronously
 | 
			
		||||
            createRepository(
 | 
			
		||||
              loginAccount,
 | 
			
		||||
              form.owner,
 | 
			
		||||
              form.name,
 | 
			
		||||
              form.description,
 | 
			
		||||
              form.isPrivate,
 | 
			
		||||
              form.initOption,
 | 
			
		||||
              form.sourceUrl,
 | 
			
		||||
              context.settings.defaultBranch
 | 
			
		||||
            )
 | 
			
		||||
            // redirect to the repository
 | 
			
		||||
            redirect(s"/${form.owner}/${form.name}")
 | 
			
		||||
          }
 | 
			
		||||
        } else Forbidden()
 | 
			
		||||
        }
 | 
			
		||||
      } else Forbidden()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  get("/:owner/:repository/fork")(readableUsersOnly { repository =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (repository.repository.options.allowFork && (context.settings.basicBehavior.repositoryOperation.fork || loginAccount.isAdmin)) {
 | 
			
		||||
          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
 | 
			
		||||
                  }
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
              helper.html.forkrepository(
 | 
			
		||||
                repository,
 | 
			
		||||
                (groups zip managerPermissions).sortBy(_._1)
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (
 | 
			
		||||
        repository.repository.options.allowFork && (context.settings.basicBehavior.repositoryOperation.fork || loginAccount.isAdmin)
 | 
			
		||||
      ) {
 | 
			
		||||
        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
 | 
			
		||||
                }
 | 
			
		||||
              )
 | 
			
		||||
            case _ => redirect(s"/${loginUserName}")
 | 
			
		||||
          }
 | 
			
		||||
        } else BadRequest()
 | 
			
		||||
            }
 | 
			
		||||
            helper.html.forkrepository(
 | 
			
		||||
              repository,
 | 
			
		||||
              (groups zip managerPermissions).sortBy(_._1)
 | 
			
		||||
            )
 | 
			
		||||
          case _ => redirect(s"/${loginUserName}")
 | 
			
		||||
        }
 | 
			
		||||
      } else BadRequest()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  post("/:owner/:repository/fork", accountForm)(readableUsersOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (repository.repository.options.allowFork && (context.settings.basicBehavior.repositoryOperation.fork || loginAccount.isAdmin)) {
 | 
			
		||||
          val loginUserName = loginAccount.userName
 | 
			
		||||
          val accountName = form.accountName
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (
 | 
			
		||||
        repository.repository.options.allowFork && (context.settings.basicBehavior.repositoryOperation.fork || loginAccount.isAdmin)
 | 
			
		||||
      ) {
 | 
			
		||||
        val loginUserName = loginAccount.userName
 | 
			
		||||
        val accountName = form.accountName
 | 
			
		||||
 | 
			
		||||
          if (getRepository(accountName, repository.name).isDefined) {
 | 
			
		||||
            // redirect to the repository if repository already exists
 | 
			
		||||
            redirect(s"/${accountName}/${repository.name}")
 | 
			
		||||
          } else if (!canCreateRepository(accountName, loginAccount)) {
 | 
			
		||||
            // Permission error
 | 
			
		||||
            Forbidden()
 | 
			
		||||
          } else {
 | 
			
		||||
            // fork repository asynchronously
 | 
			
		||||
            forkRepository(accountName, repository, loginUserName)
 | 
			
		||||
            // redirect to the repository
 | 
			
		||||
            redirect(s"/${accountName}/${repository.name}")
 | 
			
		||||
          }
 | 
			
		||||
        } else Forbidden()
 | 
			
		||||
        if (getRepository(accountName, repository.name).isDefined) {
 | 
			
		||||
          // redirect to the repository if repository already exists
 | 
			
		||||
          redirect(s"/${accountName}/${repository.name}")
 | 
			
		||||
        } else if (!canCreateRepository(accountName, loginAccount)) {
 | 
			
		||||
          // Permission error
 | 
			
		||||
          Forbidden()
 | 
			
		||||
        } else {
 | 
			
		||||
          // fork repository asynchronously
 | 
			
		||||
          forkRepository(accountName, repository, loginUserName)
 | 
			
		||||
          // redirect to the repository
 | 
			
		||||
          redirect(s"/${accountName}/${repository.name}")
 | 
			
		||||
        }
 | 
			
		||||
      } else Forbidden()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -891,9 +884,11 @@ trait AccountControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
 | 
			
		||||
  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
 | 
			
		||||
      if (
 | 
			
		||||
        value.split(",").exists {
 | 
			
		||||
          _.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
 | 
			
		||||
        }
 | 
			
		||||
      ) None
 | 
			
		||||
      else Some("Must select one manager at least.")
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -423,10 +423,11 @@ trait AccountManagementControllerBase extends ControllerBase {
 | 
			
		||||
      messages: Messages
 | 
			
		||||
    ): Option[String] = {
 | 
			
		||||
      val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses"))
 | 
			
		||||
      if (extraMailAddresses.exists {
 | 
			
		||||
            case (k, v) =>
 | 
			
		||||
              v.contains(value)
 | 
			
		||||
          }) {
 | 
			
		||||
      if (
 | 
			
		||||
        extraMailAddresses.exists { case (k, v) =>
 | 
			
		||||
          v.contains(value)
 | 
			
		||||
        }
 | 
			
		||||
      ) {
 | 
			
		||||
        Some("These mail addresses are duplicated.")
 | 
			
		||||
      } else {
 | 
			
		||||
        getAccountByMailAddress(value, true)
 | 
			
		||||
@@ -446,10 +447,11 @@ trait AccountManagementControllerBase extends ControllerBase {
 | 
			
		||||
      messages: Messages
 | 
			
		||||
    ): Option[String] = {
 | 
			
		||||
      val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses"))
 | 
			
		||||
      if (Some(value) == params.optionValue("mailAddress") || extraMailAddresses.count {
 | 
			
		||||
            case (k, v) =>
 | 
			
		||||
              v.contains(value)
 | 
			
		||||
          } > 1) {
 | 
			
		||||
      if (
 | 
			
		||||
        Some(value) == params.optionValue("mailAddress") || extraMailAddresses.count { case (k, v) =>
 | 
			
		||||
          v.contains(value)
 | 
			
		||||
        } > 1
 | 
			
		||||
      ) {
 | 
			
		||||
        Some("These mail addresses are duplicated.")
 | 
			
		||||
      } else {
 | 
			
		||||
        getAccountByMailAddress(value, true)
 | 
			
		||||
 
 | 
			
		||||
@@ -75,59 +75,57 @@ class FileUploadController
 | 
			
		||||
  post("/wiki/:owner/:repository") {
 | 
			
		||||
    setMultipartConfig()
 | 
			
		||||
    // 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.resource(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.resource(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()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -135,20 +133,19 @@ class FileUploadController
 | 
			
		||||
    setMultipartConfigForLargeFile()
 | 
			
		||||
    session
 | 
			
		||||
      .get(Keys.Session.LoginAccount)
 | 
			
		||||
      .collect {
 | 
			
		||||
        case _: Account =>
 | 
			
		||||
          val owner = params("owner")
 | 
			
		||||
          val repository = params("repository")
 | 
			
		||||
          val tag = multiParams("splat").head
 | 
			
		||||
          execute(
 | 
			
		||||
            { (file, fileId) =>
 | 
			
		||||
              FileUtils.writeByteArrayToFile(
 | 
			
		||||
                new File(getReleaseFilesDir(owner, repository), FileUtil.checkFilename(tag + "/" + fileId)),
 | 
			
		||||
                file.get()
 | 
			
		||||
              )
 | 
			
		||||
            },
 | 
			
		||||
            _ => true
 | 
			
		||||
          )
 | 
			
		||||
      .collect { case _: Account =>
 | 
			
		||||
        val owner = params("owner")
 | 
			
		||||
        val repository = params("repository")
 | 
			
		||||
        val tag = multiParams("splat").head
 | 
			
		||||
        execute(
 | 
			
		||||
          { (file, fileId) =>
 | 
			
		||||
            FileUtils.writeByteArrayToFile(
 | 
			
		||||
              new File(getReleaseFilesDir(owner, repository), FileUtil.checkFilename(tag + "/" + fileId)),
 | 
			
		||||
              file.get()
 | 
			
		||||
            )
 | 
			
		||||
          },
 | 
			
		||||
          _ => true
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      .getOrElse(BadRequest())
 | 
			
		||||
  }
 | 
			
		||||
@@ -158,9 +155,12 @@ class FileUploadController
 | 
			
		||||
    setMultipartConfig()
 | 
			
		||||
    session.get(Keys.Session.LoginAccount).collect {
 | 
			
		||||
      case loginAccount: Account if loginAccount.isAdmin =>
 | 
			
		||||
        execute({ (file, fileId) =>
 | 
			
		||||
          request2Session(request).conn.importAsSQL(file.getInputStream)
 | 
			
		||||
        }, _ => true)
 | 
			
		||||
        execute(
 | 
			
		||||
          { (file, fileId) =>
 | 
			
		||||
            request2Session(request).conn.importAsSQL(file.getInputStream)
 | 
			
		||||
          },
 | 
			
		||||
          _ => true
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
    redirect("/admin/data")
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -151,10 +151,9 @@ trait IndexControllerBase extends ControllerBase {
 | 
			
		||||
      val redirectURI = new URI(s"$baseUrl/signin/oidc")
 | 
			
		||||
      session.get(Keys.Session.OidcAuthContext) match {
 | 
			
		||||
        case Some(context: OidcAuthContext) =>
 | 
			
		||||
          authenticate(params.toMap, redirectURI, context.state, context.nonce, oidc).map {
 | 
			
		||||
            case (jwt, account) =>
 | 
			
		||||
              session.setAttribute(Keys.Session.OidcSessionContext, OidcSessionContext(jwt))
 | 
			
		||||
              signin(account, context.redirectBackURI)
 | 
			
		||||
          authenticate(params.toMap, redirectURI, context.state, context.nonce, oidc).map { case (jwt, account) =>
 | 
			
		||||
            session.setAttribute(Keys.Session.OidcSessionContext, OidcSessionContext(jwt))
 | 
			
		||||
            signin(account, context.redirectBackURI)
 | 
			
		||||
          } orElse {
 | 
			
		||||
            flash.update("error", "Sorry, authentication failed. Please try again.")
 | 
			
		||||
            session.invalidate()
 | 
			
		||||
@@ -172,12 +171,11 @@ trait IndexControllerBase extends ControllerBase {
 | 
			
		||||
 | 
			
		||||
  get("/signout") {
 | 
			
		||||
    context.settings.oidc.foreach { oidc =>
 | 
			
		||||
      session.get(Keys.Session.OidcSessionContext).foreach {
 | 
			
		||||
        case context: OidcSessionContext =>
 | 
			
		||||
          val redirectURI = new URI(baseUrl)
 | 
			
		||||
          val authenticationRequest = createOIDLogoutRequest(oidc.issuer, oidc.clientID, redirectURI, context.token)
 | 
			
		||||
          session.invalidate()
 | 
			
		||||
          redirect(authenticationRequest.toURI.toString)
 | 
			
		||||
      session.get(Keys.Session.OidcSessionContext).foreach { case context: OidcSessionContext =>
 | 
			
		||||
        val redirectURI = new URI(baseUrl)
 | 
			
		||||
        val authenticationRequest = createOIDLogoutRequest(oidc.issuer, oidc.clientID, redirectURI, context.token)
 | 
			
		||||
        session.invalidate()
 | 
			
		||||
        redirect(authenticationRequest.toURI.toString)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    session.invalidate()
 | 
			
		||||
@@ -244,9 +242,9 @@ trait IndexControllerBase extends ControllerBase {
 | 
			
		||||
            .map { t =>
 | 
			
		||||
              Map(
 | 
			
		||||
                "label" -> s"${avatar(t.userName, 16)}<b>@${StringUtil.escapeHtml(
 | 
			
		||||
                  StringUtil.cutTail(t.userName, 25, "...")
 | 
			
		||||
                )}</b> ${StringUtil
 | 
			
		||||
                  .escapeHtml(StringUtil.cutTail(t.fullName, 25, "..."))}",
 | 
			
		||||
                    StringUtil.cutTail(t.userName, 25, "...")
 | 
			
		||||
                  )}</b> ${StringUtil
 | 
			
		||||
                    .escapeHtml(StringUtil.cutTail(t.fullName, 25, "..."))}",
 | 
			
		||||
                "value" -> t.userName
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
@@ -269,12 +267,13 @@ trait IndexControllerBase extends ControllerBase {
 | 
			
		||||
  get("/:owner/:repository/search")(referrersOnly { repository =>
 | 
			
		||||
    val query = params.getOrElse("q", "").trim
 | 
			
		||||
    val target = params.getOrElse("type", "code")
 | 
			
		||||
    val page = try {
 | 
			
		||||
      val i = params.getOrElse("page", "1").toInt
 | 
			
		||||
      if (i <= 0) 1 else i
 | 
			
		||||
    } catch {
 | 
			
		||||
      case _: NumberFormatException => 1
 | 
			
		||||
    }
 | 
			
		||||
    val page =
 | 
			
		||||
      try {
 | 
			
		||||
        val i = params.getOrElse("page", "1").toInt
 | 
			
		||||
        if (i <= 0) 1 else i
 | 
			
		||||
      } catch {
 | 
			
		||||
        case _: NumberFormatException => 1
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    target.toLowerCase match {
 | 
			
		||||
      case "issues" =>
 | 
			
		||||
 
 | 
			
		||||
@@ -101,27 +101,26 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
 | 
			
		||||
  get("/:owner/:repository/issues/:id")(referrersOnly { repository =>
 | 
			
		||||
    val issueId = params("id")
 | 
			
		||||
    getIssue(repository.owner, repository.name, issueId) map {
 | 
			
		||||
      issue =>
 | 
			
		||||
        if (issue.isPullRequest) {
 | 
			
		||||
          redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
 | 
			
		||||
        } else {
 | 
			
		||||
          html.issue(
 | 
			
		||||
            issue,
 | 
			
		||||
            getComments(repository.owner, repository.name, issueId.toInt),
 | 
			
		||||
            getIssueLabels(repository.owner, repository.name, issueId.toInt),
 | 
			
		||||
            getIssueAssignees(repository.owner, repository.name, issueId.toInt),
 | 
			
		||||
            getAssignableUserNames(repository.owner, repository.name),
 | 
			
		||||
            getMilestonesWithIssueCount(repository.owner, repository.name),
 | 
			
		||||
            getPriorities(repository.owner, repository.name),
 | 
			
		||||
            getLabels(repository.owner, repository.name),
 | 
			
		||||
            getCustomFieldsWithValue(repository.owner, repository.name, issueId.toInt).filter(_._1.enableForIssues),
 | 
			
		||||
            isIssueEditable(repository),
 | 
			
		||||
            isIssueManageable(repository),
 | 
			
		||||
            isIssueCommentManageable(repository),
 | 
			
		||||
            repository
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
    getIssue(repository.owner, repository.name, issueId) map { issue =>
 | 
			
		||||
      if (issue.isPullRequest) {
 | 
			
		||||
        redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
 | 
			
		||||
      } else {
 | 
			
		||||
        html.issue(
 | 
			
		||||
          issue,
 | 
			
		||||
          getComments(repository.owner, repository.name, issueId.toInt),
 | 
			
		||||
          getIssueLabels(repository.owner, repository.name, issueId.toInt),
 | 
			
		||||
          getIssueAssignees(repository.owner, repository.name, issueId.toInt),
 | 
			
		||||
          getAssignableUserNames(repository.owner, repository.name),
 | 
			
		||||
          getMilestonesWithIssueCount(repository.owner, repository.name),
 | 
			
		||||
          getPriorities(repository.owner, repository.name),
 | 
			
		||||
          getLabels(repository.owner, repository.name),
 | 
			
		||||
          getCustomFieldsWithValue(repository.owner, repository.name, issueId.toInt).filter(_._1.enableForIssues),
 | 
			
		||||
          isIssueEditable(repository),
 | 
			
		||||
          isIssueManageable(repository),
 | 
			
		||||
          isIssueCommentManageable(repository),
 | 
			
		||||
          repository
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    } getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -142,130 +141,120 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  post("/:owner/:repository/issues/new", issueCreateForm)(readableUsersOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (isIssueEditable(repository)) { // TODO Should this check is provided by authenticator?
 | 
			
		||||
          val issue = createIssue(
 | 
			
		||||
            repository,
 | 
			
		||||
            form.title,
 | 
			
		||||
            form.content,
 | 
			
		||||
            form.assigneeUserNames.toSeq.flatMap(_.split(",")),
 | 
			
		||||
            form.milestoneId,
 | 
			
		||||
            form.priorityId,
 | 
			
		||||
            form.labelNames.toSeq.flatMap(_.split(",")),
 | 
			
		||||
            loginAccount
 | 
			
		||||
          )
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (isIssueEditable(repository)) { // TODO Should this check is provided by authenticator?
 | 
			
		||||
        val issue = createIssue(
 | 
			
		||||
          repository,
 | 
			
		||||
          form.title,
 | 
			
		||||
          form.content,
 | 
			
		||||
          form.assigneeUserNames.toSeq.flatMap(_.split(",")),
 | 
			
		||||
          form.milestoneId,
 | 
			
		||||
          form.priorityId,
 | 
			
		||||
          form.labelNames.toSeq.flatMap(_.split(",")),
 | 
			
		||||
          loginAccount
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
          // Insert custom field values
 | 
			
		||||
          params.toMap.foreach {
 | 
			
		||||
            case (key, value) =>
 | 
			
		||||
              if (key.startsWith("custom-field-")) {
 | 
			
		||||
                getCustomField(
 | 
			
		||||
                  repository.owner,
 | 
			
		||||
                  repository.name,
 | 
			
		||||
                  key.replaceFirst("^custom-field-", "").toInt
 | 
			
		||||
                ).foreach { field =>
 | 
			
		||||
                  CustomFieldBehavior.validate(field, value, messages) match {
 | 
			
		||||
                    case None =>
 | 
			
		||||
                      insertOrUpdateCustomFieldValue(field, repository.owner, repository.name, issue.issueId, value)
 | 
			
		||||
                    case Some(_) => halt(400)
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
        // Insert custom field values
 | 
			
		||||
        params.toMap.foreach { case (key, value) =>
 | 
			
		||||
          if (key.startsWith("custom-field-")) {
 | 
			
		||||
            getCustomField(
 | 
			
		||||
              repository.owner,
 | 
			
		||||
              repository.name,
 | 
			
		||||
              key.replaceFirst("^custom-field-", "").toInt
 | 
			
		||||
            ).foreach { field =>
 | 
			
		||||
              CustomFieldBehavior.validate(field, value, messages) match {
 | 
			
		||||
                case None =>
 | 
			
		||||
                  insertOrUpdateCustomFieldValue(field, repository.owner, repository.name, issue.issueId, value)
 | 
			
		||||
                case Some(_) => halt(400)
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
          redirect(s"/${issue.userName}/${issue.repositoryName}/issues/${issue.issueId}")
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
        redirect(s"/${issue.userName}/${issue.repositoryName}/issues/${issue.issueId}")
 | 
			
		||||
      } else Unauthorized()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  ajaxPost("/:owner/:repository/issues/edit_title/:id", issueTitleEditForm)(readableUsersOnly { (title, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        getIssue(repository.owner, repository.name, params("id")).map {
 | 
			
		||||
          issue =>
 | 
			
		||||
            if (isEditableContent(repository.owner, repository.name, issue.openedUserName, loginAccount)) {
 | 
			
		||||
              if (issue.title != title) {
 | 
			
		||||
                // update issue
 | 
			
		||||
                updateIssue(repository.owner, repository.name, issue.issueId, title, issue.content)
 | 
			
		||||
                // extract references and create refer comment
 | 
			
		||||
                createReferComment(repository.owner, repository.name, issue.copy(title = title), title, loginAccount)
 | 
			
		||||
                createComment(
 | 
			
		||||
                  repository.owner,
 | 
			
		||||
                  repository.name,
 | 
			
		||||
                  loginAccount.userName,
 | 
			
		||||
                  issue.issueId,
 | 
			
		||||
                  issue.title + "\r\n" + title,
 | 
			
		||||
                  "change_title"
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
              redirect(s"/${repository.owner}/${repository.name}/issues/_data/${issue.issueId}")
 | 
			
		||||
            } else Unauthorized()
 | 
			
		||||
        } getOrElse NotFound()
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      getIssue(repository.owner, repository.name, params("id")).map { issue =>
 | 
			
		||||
        if (isEditableContent(repository.owner, repository.name, issue.openedUserName, loginAccount)) {
 | 
			
		||||
          if (issue.title != title) {
 | 
			
		||||
            // update issue
 | 
			
		||||
            updateIssue(repository.owner, repository.name, issue.issueId, title, issue.content)
 | 
			
		||||
            // extract references and create refer comment
 | 
			
		||||
            createReferComment(repository.owner, repository.name, issue.copy(title = title), title, loginAccount)
 | 
			
		||||
            createComment(
 | 
			
		||||
              repository.owner,
 | 
			
		||||
              repository.name,
 | 
			
		||||
              loginAccount.userName,
 | 
			
		||||
              issue.issueId,
 | 
			
		||||
              issue.title + "\r\n" + title,
 | 
			
		||||
              "change_title"
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
          redirect(s"/${repository.owner}/${repository.name}/issues/_data/${issue.issueId}")
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
      } getOrElse NotFound()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  ajaxPost("/:owner/:repository/issues/edit/:id", issueEditForm)(readableUsersOnly { (content, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        getIssue(repository.owner, repository.name, params("id")).map { issue =>
 | 
			
		||||
          if (isEditableContent(repository.owner, repository.name, issue.openedUserName, loginAccount)) {
 | 
			
		||||
            // update issue
 | 
			
		||||
            updateIssue(repository.owner, repository.name, issue.issueId, issue.title, content)
 | 
			
		||||
            // extract references and create refer comment
 | 
			
		||||
            createReferComment(repository.owner, repository.name, issue, content.getOrElse(""), loginAccount)
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      getIssue(repository.owner, repository.name, params("id")).map { issue =>
 | 
			
		||||
        if (isEditableContent(repository.owner, repository.name, issue.openedUserName, loginAccount)) {
 | 
			
		||||
          // update issue
 | 
			
		||||
          updateIssue(repository.owner, repository.name, issue.issueId, issue.title, content)
 | 
			
		||||
          // extract references and create refer comment
 | 
			
		||||
          createReferComment(repository.owner, repository.name, issue, content.getOrElse(""), loginAccount)
 | 
			
		||||
 | 
			
		||||
            redirect(s"/${repository.owner}/${repository.name}/issues/_data/${issue.issueId}")
 | 
			
		||||
          } else Unauthorized()
 | 
			
		||||
        } getOrElse NotFound()
 | 
			
		||||
          redirect(s"/${repository.owner}/${repository.name}/issues/_data/${issue.issueId}")
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
      } getOrElse NotFound()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  post("/:owner/:repository/issue_comments/new", commentForm)(readableUsersOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
 | 
			
		||||
          val actionOpt =
 | 
			
		||||
            params
 | 
			
		||||
              .get("action")
 | 
			
		||||
              .filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName, loginAccount))
 | 
			
		||||
          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()
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
 | 
			
		||||
        val actionOpt =
 | 
			
		||||
          params
 | 
			
		||||
            .get("action")
 | 
			
		||||
            .filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName, loginAccount))
 | 
			
		||||
        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) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
 | 
			
		||||
          val actionOpt =
 | 
			
		||||
            params
 | 
			
		||||
              .get("action")
 | 
			
		||||
              .filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName, loginAccount))
 | 
			
		||||
          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()
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      getIssue(repository.owner, repository.name, form.issueId.toString).flatMap { issue =>
 | 
			
		||||
        val actionOpt =
 | 
			
		||||
          params
 | 
			
		||||
            .get("action")
 | 
			
		||||
            .filter(_ => isEditableContent(issue.userName, issue.repositoryName, issue.openedUserName, loginAccount))
 | 
			
		||||
        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) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        getComment(repository.owner, repository.name, params("id")).map { comment =>
 | 
			
		||||
          if (isEditableContent(repository.owner, repository.name, comment.commentedUserName, loginAccount)) {
 | 
			
		||||
            updateComment(repository.owner, repository.name, comment.issueId, comment.commentId, form.content)
 | 
			
		||||
            redirect(s"/${repository.owner}/${repository.name}/issue_comments/_data/${comment.commentId}")
 | 
			
		||||
          } else Unauthorized()
 | 
			
		||||
        } getOrElse NotFound()
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      getComment(repository.owner, repository.name, params("id")).map { comment =>
 | 
			
		||||
        if (isEditableContent(repository.owner, repository.name, comment.commentedUserName, loginAccount)) {
 | 
			
		||||
          updateComment(repository.owner, repository.name, comment.issueId, comment.commentId, form.content)
 | 
			
		||||
          redirect(s"/${repository.owner}/${repository.name}/issue_comments/_data/${comment.commentId}")
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
      } getOrElse NotFound()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -280,65 +269,61 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  ajaxGet("/:owner/:repository/issues/_data/:id")(readableUsersOnly { repository =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        getIssue(repository.owner, repository.name, params("id")) map {
 | 
			
		||||
          x =>
 | 
			
		||||
            if (isEditableContent(x.userName, x.repositoryName, x.openedUserName, loginAccount)) {
 | 
			
		||||
              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,
 | 
			
		||||
                      branch = repository.repository.defaultBranch,
 | 
			
		||||
                      enableWikiLink = false,
 | 
			
		||||
                      enableRefsLink = true,
 | 
			
		||||
                      enableAnchor = true,
 | 
			
		||||
                      enableLineBreaks = true,
 | 
			
		||||
                      enableTaskList = true,
 | 
			
		||||
                      hasWritePermission = true
 | 
			
		||||
                    )
 | 
			
		||||
                  )
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      getIssue(repository.owner, repository.name, params("id")) map { x =>
 | 
			
		||||
        if (isEditableContent(x.userName, x.repositoryName, x.openedUserName, loginAccount)) {
 | 
			
		||||
          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,
 | 
			
		||||
                  branch = repository.repository.defaultBranch,
 | 
			
		||||
                  enableWikiLink = false,
 | 
			
		||||
                  enableRefsLink = true,
 | 
			
		||||
                  enableAnchor = true,
 | 
			
		||||
                  enableLineBreaks = true,
 | 
			
		||||
                  enableTaskList = true,
 | 
			
		||||
                  hasWritePermission = true
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
            } else Unauthorized()
 | 
			
		||||
        } getOrElse NotFound()
 | 
			
		||||
              )
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
      } getOrElse NotFound()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  ajaxGet("/:owner/:repository/issue_comments/_data/:id")(readableUsersOnly { repository =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        getComment(repository.owner, repository.name, params("id")) map {
 | 
			
		||||
          x =>
 | 
			
		||||
            if (isEditableContent(x.userName, x.repositoryName, x.commentedUserName, loginAccount)) {
 | 
			
		||||
              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,
 | 
			
		||||
                      branch = repository.repository.defaultBranch,
 | 
			
		||||
                      enableWikiLink = false,
 | 
			
		||||
                      enableRefsLink = true,
 | 
			
		||||
                      enableAnchor = true,
 | 
			
		||||
                      enableLineBreaks = true,
 | 
			
		||||
                      enableTaskList = true,
 | 
			
		||||
                      hasWritePermission = true
 | 
			
		||||
                    )
 | 
			
		||||
                  )
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      getComment(repository.owner, repository.name, params("id")) map { x =>
 | 
			
		||||
        if (isEditableContent(x.userName, x.repositoryName, x.commentedUserName, loginAccount)) {
 | 
			
		||||
          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,
 | 
			
		||||
                  branch = repository.repository.defaultBranch,
 | 
			
		||||
                  enableWikiLink = false,
 | 
			
		||||
                  enableRefsLink = true,
 | 
			
		||||
                  enableAnchor = true,
 | 
			
		||||
                  enableLineBreaks = true,
 | 
			
		||||
                  enableTaskList = true,
 | 
			
		||||
                  hasWritePermission = true
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
            } else Unauthorized()
 | 
			
		||||
        } getOrElse NotFound()
 | 
			
		||||
              )
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
      } getOrElse NotFound()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -377,9 +362,8 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
    milestoneId("milestoneId").map { milestoneId =>
 | 
			
		||||
      getMilestonesWithIssueCount(repository.owner, repository.name)
 | 
			
		||||
        .find(_._1.milestoneId == milestoneId)
 | 
			
		||||
        .map {
 | 
			
		||||
          case (_, openCount, closeCount) =>
 | 
			
		||||
            gitbucket.core.issues.milestones.html.progress(openCount + closeCount, closeCount)
 | 
			
		||||
        .map { case (_, openCount, closeCount) =>
 | 
			
		||||
          gitbucket.core.issues.milestones.html.progress(openCount + closeCount, closeCount)
 | 
			
		||||
        } getOrElse NotFound()
 | 
			
		||||
    } getOrElse Ok()
 | 
			
		||||
  })
 | 
			
		||||
@@ -460,7 +444,7 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
  post("/:owner/:repository/issues/batchedit/assign")(writableUsersOnly { repository =>
 | 
			
		||||
    val value = assignedUserName("value")
 | 
			
		||||
    executeBatch(repository) {
 | 
			
		||||
      //updateAssignedUserName(repository.owner, repository.name, _, value, true)
 | 
			
		||||
      // updateAssignedUserName(repository.owner, repository.name, _, value, true)
 | 
			
		||||
      value match {
 | 
			
		||||
        case Some(assignedUserName) =>
 | 
			
		||||
          registerIssueAssignee(repository.owner, repository.name, _, assignedUserName, true)
 | 
			
		||||
@@ -510,9 +494,9 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
            .map { t =>
 | 
			
		||||
              Map(
 | 
			
		||||
                "label" -> s"""${if (t.isPullRequest) "<i class='octicon octicon-git-pull-request'></i>"
 | 
			
		||||
                else "<i class='octicon octicon-issue-opened'></i>"}<b> #${StringUtil
 | 
			
		||||
                  .escapeHtml(t.issueId.toString)} ${StringUtil
 | 
			
		||||
                  .escapeHtml(StringUtil.cutTail(t.title, 50, "..."))}</b>""",
 | 
			
		||||
                  else "<i class='octicon octicon-issue-opened'></i>"}<b> #${StringUtil
 | 
			
		||||
                    .escapeHtml(t.issueId.toString)} ${StringUtil
 | 
			
		||||
                    .escapeHtml(StringUtil.cutTail(t.title, 50, "..."))}</b>""",
 | 
			
		||||
                "value" -> t.issueId.toString
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
@@ -565,8 +549,8 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests whether an issue or a comment is editable by a logged-in user.
 | 
			
		||||
   */
 | 
			
		||||
  private def isEditableContent(owner: String, repository: String, author: String, loginAccount: Account)(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  private def isEditableContent(owner: String, repository: String, author: String, loginAccount: Account)(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Boolean = {
 | 
			
		||||
    hasDeveloperRole(owner, repository, context.loginAccount) || author == loginAccount.userName
 | 
			
		||||
  }
 | 
			
		||||
@@ -574,8 +558,8 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests whether an issue comment is deletable by a logged-in user.
 | 
			
		||||
   */
 | 
			
		||||
  private def isDeletableComment(owner: String, repository: String, author: String, loginAccount: Account)(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  private def isDeletableComment(owner: String, repository: String, author: String, loginAccount: Account)(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Boolean = {
 | 
			
		||||
    hasOwnerRole(owner, repository, context.loginAccount) || author == loginAccount.userName
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -30,12 +30,14 @@ trait PreProcessControllerBase extends ControllerBase {
 | 
			
		||||
   * But if it's not allowed, demands authentication except some paths.
 | 
			
		||||
   */
 | 
			
		||||
  get(!context.settings.basicBehavior.allowAnonymousAccess, context.loginAccount.isEmpty) {
 | 
			
		||||
    if (!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") &&
 | 
			
		||||
        !context.currentPath.startsWith("/register") && !context.currentPath.endsWith("/info/refs") &&
 | 
			
		||||
        !context.currentPath.startsWith("/plugin-assets") &&
 | 
			
		||||
        !PluginRegistry().getAnonymousAccessiblePaths().exists { path =>
 | 
			
		||||
          context.currentPath.startsWith(path)
 | 
			
		||||
        }) {
 | 
			
		||||
    if (
 | 
			
		||||
      !context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") &&
 | 
			
		||||
      !context.currentPath.startsWith("/register") && !context.currentPath.endsWith("/info/refs") &&
 | 
			
		||||
      !context.currentPath.startsWith("/plugin-assets") &&
 | 
			
		||||
      !PluginRegistry().getAnonymousAccessiblePaths().exists { path =>
 | 
			
		||||
        context.currentPath.startsWith(path)
 | 
			
		||||
      }
 | 
			
		||||
    ) {
 | 
			
		||||
      Unauthorized()
 | 
			
		||||
    } else {
 | 
			
		||||
      pass()
 | 
			
		||||
 
 | 
			
		||||
@@ -113,149 +113,141 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  get("/:owner/:repository/pull/:id")(referrersOnly { repository =>
 | 
			
		||||
    params("id").toIntOpt.flatMap {
 | 
			
		||||
      issueId =>
 | 
			
		||||
        getPullRequest(repository.owner, repository.name, issueId) map {
 | 
			
		||||
          case (issue, pullreq) =>
 | 
			
		||||
            val (commits, diffs) =
 | 
			
		||||
              getRequestCompareInfo(
 | 
			
		||||
                repository.owner,
 | 
			
		||||
                repository.name,
 | 
			
		||||
                pullreq.commitIdFrom,
 | 
			
		||||
                repository.owner,
 | 
			
		||||
                repository.name,
 | 
			
		||||
                pullreq.commitIdTo
 | 
			
		||||
              )
 | 
			
		||||
    params("id").toIntOpt.flatMap { issueId =>
 | 
			
		||||
      getPullRequest(repository.owner, repository.name, issueId) map { case (issue, pullreq) =>
 | 
			
		||||
        val (commits, diffs) =
 | 
			
		||||
          getRequestCompareInfo(
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            pullreq.commitIdFrom,
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            pullreq.commitIdTo
 | 
			
		||||
          )
 | 
			
		||||
 | 
			
		||||
            html.conversation(
 | 
			
		||||
              issue,
 | 
			
		||||
              pullreq,
 | 
			
		||||
              commits.flatten,
 | 
			
		||||
              getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
 | 
			
		||||
              diffs.size,
 | 
			
		||||
              getIssueLabels(repository.owner, repository.name, issueId),
 | 
			
		||||
              getIssueAssignees(repository.owner, repository.name, issueId),
 | 
			
		||||
              getAssignableUserNames(repository.owner, repository.name),
 | 
			
		||||
              getMilestonesWithIssueCount(repository.owner, repository.name),
 | 
			
		||||
              getPriorities(repository.owner, repository.name),
 | 
			
		||||
              getLabels(repository.owner, repository.name),
 | 
			
		||||
              getCustomFieldsWithValue(repository.owner, repository.name, issueId).filter(_._1.enableForPullRequests),
 | 
			
		||||
              isEditable(repository),
 | 
			
		||||
              isManageable(repository),
 | 
			
		||||
              hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount),
 | 
			
		||||
              repository,
 | 
			
		||||
              getRepository(pullreq.requestUserName, pullreq.requestRepositoryName),
 | 
			
		||||
              flash.iterator.map(f => f._1 -> f._2.toString).toMap
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        html.conversation(
 | 
			
		||||
          issue,
 | 
			
		||||
          pullreq,
 | 
			
		||||
          commits.flatten,
 | 
			
		||||
          getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
 | 
			
		||||
          diffs.size,
 | 
			
		||||
          getIssueLabels(repository.owner, repository.name, issueId),
 | 
			
		||||
          getIssueAssignees(repository.owner, repository.name, issueId),
 | 
			
		||||
          getAssignableUserNames(repository.owner, repository.name),
 | 
			
		||||
          getMilestonesWithIssueCount(repository.owner, repository.name),
 | 
			
		||||
          getPriorities(repository.owner, repository.name),
 | 
			
		||||
          getLabels(repository.owner, repository.name),
 | 
			
		||||
          getCustomFieldsWithValue(repository.owner, repository.name, issueId).filter(_._1.enableForPullRequests),
 | 
			
		||||
          isEditable(repository),
 | 
			
		||||
          isManageable(repository),
 | 
			
		||||
          hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount),
 | 
			
		||||
          repository,
 | 
			
		||||
          getRepository(pullreq.requestUserName, pullreq.requestRepositoryName),
 | 
			
		||||
          flash.iterator.map(f => f._1 -> f._2.toString).toMap
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    } getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  get("/:owner/:repository/pull/:id/commits")(referrersOnly { repository =>
 | 
			
		||||
    params("id").toIntOpt.flatMap {
 | 
			
		||||
      issueId =>
 | 
			
		||||
        getPullRequest(repository.owner, repository.name, issueId) map {
 | 
			
		||||
          case (issue, pullreq) =>
 | 
			
		||||
            val (commits, diffs) =
 | 
			
		||||
              getRequestCompareInfo(
 | 
			
		||||
                repository.owner,
 | 
			
		||||
                repository.name,
 | 
			
		||||
                pullreq.commitIdFrom,
 | 
			
		||||
                repository.owner,
 | 
			
		||||
                repository.name,
 | 
			
		||||
                pullreq.commitIdTo
 | 
			
		||||
              )
 | 
			
		||||
    params("id").toIntOpt.flatMap { issueId =>
 | 
			
		||||
      getPullRequest(repository.owner, repository.name, issueId) map { case (issue, pullreq) =>
 | 
			
		||||
        val (commits, diffs) =
 | 
			
		||||
          getRequestCompareInfo(
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            pullreq.commitIdFrom,
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            pullreq.commitIdTo
 | 
			
		||||
          )
 | 
			
		||||
 | 
			
		||||
            val commitsWithStatus = commits.map { day =>
 | 
			
		||||
              day.map { commit =>
 | 
			
		||||
                (commit, getCommitStatusWithSummary(repository.owner, repository.name, commit.id))
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            html.commits(
 | 
			
		||||
              issue,
 | 
			
		||||
              pullreq,
 | 
			
		||||
              commitsWithStatus,
 | 
			
		||||
              getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
 | 
			
		||||
              diffs.size,
 | 
			
		||||
              isManageable(repository),
 | 
			
		||||
              repository
 | 
			
		||||
            )
 | 
			
		||||
        val commitsWithStatus = commits.map { day =>
 | 
			
		||||
          day.map { commit =>
 | 
			
		||||
            (commit, getCommitStatusWithSummary(repository.owner, repository.name, commit.id))
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        html.commits(
 | 
			
		||||
          issue,
 | 
			
		||||
          pullreq,
 | 
			
		||||
          commitsWithStatus,
 | 
			
		||||
          getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
 | 
			
		||||
          diffs.size,
 | 
			
		||||
          isManageable(repository),
 | 
			
		||||
          repository
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    } getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  get("/:owner/:repository/pull/:id/files")(referrersOnly { repository =>
 | 
			
		||||
    params("id").toIntOpt.flatMap {
 | 
			
		||||
      issueId =>
 | 
			
		||||
        getPullRequest(repository.owner, repository.name, issueId) map {
 | 
			
		||||
          case (issue, pullreq) =>
 | 
			
		||||
            val (commits, diffs) =
 | 
			
		||||
              getRequestCompareInfo(
 | 
			
		||||
                repository.owner,
 | 
			
		||||
                repository.name,
 | 
			
		||||
                pullreq.commitIdFrom,
 | 
			
		||||
                repository.owner,
 | 
			
		||||
                repository.name,
 | 
			
		||||
                pullreq.commitIdTo
 | 
			
		||||
              )
 | 
			
		||||
    params("id").toIntOpt.flatMap { issueId =>
 | 
			
		||||
      getPullRequest(repository.owner, repository.name, issueId) map { case (issue, pullreq) =>
 | 
			
		||||
        val (commits, diffs) =
 | 
			
		||||
          getRequestCompareInfo(
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            pullreq.commitIdFrom,
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            pullreq.commitIdTo
 | 
			
		||||
          )
 | 
			
		||||
 | 
			
		||||
            html.files(
 | 
			
		||||
              issue,
 | 
			
		||||
              pullreq,
 | 
			
		||||
              diffs,
 | 
			
		||||
              commits.flatten,
 | 
			
		||||
              getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
 | 
			
		||||
              isManageable(repository),
 | 
			
		||||
              repository
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        html.files(
 | 
			
		||||
          issue,
 | 
			
		||||
          pullreq,
 | 
			
		||||
          diffs,
 | 
			
		||||
          commits.flatten,
 | 
			
		||||
          getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
 | 
			
		||||
          isManageable(repository),
 | 
			
		||||
          repository
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    } getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  ajaxGet("/:owner/:repository/pull/:id/mergeguide")(referrersOnly { repository =>
 | 
			
		||||
    params("id").toIntOpt.flatMap {
 | 
			
		||||
      issueId =>
 | 
			
		||||
        getPullRequest(repository.owner, repository.name, issueId) map {
 | 
			
		||||
          case (issue, pullreq) =>
 | 
			
		||||
            val conflictMessage = LockUtil.lock(s"${repository.owner}/${repository.name}") {
 | 
			
		||||
              checkConflict(repository.owner, repository.name, pullreq.branch, issueId)
 | 
			
		||||
            }
 | 
			
		||||
            val hasMergePermission = hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
 | 
			
		||||
            val branchProtection = getProtectedBranchInfo(repository.owner, repository.name, pullreq.branch)
 | 
			
		||||
            val mergeStatus = PullRequestService.MergeStatus(
 | 
			
		||||
              conflictMessage = conflictMessage,
 | 
			
		||||
              commitStatuses = getCommitStatuses(repository.owner, repository.name, pullreq.commitIdTo),
 | 
			
		||||
              branchProtection = branchProtection,
 | 
			
		||||
              branchIsOutOfDate = JGitUtil.getShaByRef(repository.owner, repository.name, pullreq.branch) != Some(
 | 
			
		||||
                pullreq.commitIdFrom
 | 
			
		||||
              ),
 | 
			
		||||
              needStatusCheck = context.loginAccount.forall { u =>
 | 
			
		||||
                branchProtection.needStatusCheck(u.userName)
 | 
			
		||||
              },
 | 
			
		||||
              hasUpdatePermission = hasDeveloperRole(
 | 
			
		||||
    params("id").toIntOpt.flatMap { issueId =>
 | 
			
		||||
      getPullRequest(repository.owner, repository.name, issueId) map { case (issue, pullreq) =>
 | 
			
		||||
        val conflictMessage = LockUtil.lock(s"${repository.owner}/${repository.name}") {
 | 
			
		||||
          checkConflict(repository.owner, repository.name, pullreq.branch, issueId)
 | 
			
		||||
        }
 | 
			
		||||
        val hasMergePermission = hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
 | 
			
		||||
        val branchProtection = getProtectedBranchInfo(repository.owner, repository.name, pullreq.branch)
 | 
			
		||||
        val mergeStatus = PullRequestService.MergeStatus(
 | 
			
		||||
          conflictMessage = conflictMessage,
 | 
			
		||||
          commitStatuses = getCommitStatuses(repository.owner, repository.name, pullreq.commitIdTo),
 | 
			
		||||
          branchProtection = branchProtection,
 | 
			
		||||
          branchIsOutOfDate = JGitUtil.getShaByRef(repository.owner, repository.name, pullreq.branch) != Some(
 | 
			
		||||
            pullreq.commitIdFrom
 | 
			
		||||
          ),
 | 
			
		||||
          needStatusCheck = context.loginAccount.forall { u =>
 | 
			
		||||
            branchProtection.needStatusCheck(u.userName)
 | 
			
		||||
          },
 | 
			
		||||
          hasUpdatePermission = hasDeveloperRole(
 | 
			
		||||
            pullreq.requestUserName,
 | 
			
		||||
            pullreq.requestRepositoryName,
 | 
			
		||||
            context.loginAccount
 | 
			
		||||
          ) &&
 | 
			
		||||
            context.loginAccount.exists { u =>
 | 
			
		||||
              !getProtectedBranchInfo(
 | 
			
		||||
                pullreq.requestUserName,
 | 
			
		||||
                pullreq.requestRepositoryName,
 | 
			
		||||
                context.loginAccount
 | 
			
		||||
              ) &&
 | 
			
		||||
                context.loginAccount.exists { u =>
 | 
			
		||||
                  !getProtectedBranchInfo(
 | 
			
		||||
                    pullreq.requestUserName,
 | 
			
		||||
                    pullreq.requestRepositoryName,
 | 
			
		||||
                    pullreq.requestBranch
 | 
			
		||||
                  ).needStatusCheck(u.userName)
 | 
			
		||||
                },
 | 
			
		||||
              hasMergePermission = hasMergePermission,
 | 
			
		||||
              commitIdTo = pullreq.commitIdTo
 | 
			
		||||
            )
 | 
			
		||||
            html.mergeguide(
 | 
			
		||||
              mergeStatus,
 | 
			
		||||
              issue,
 | 
			
		||||
              pullreq,
 | 
			
		||||
              repository,
 | 
			
		||||
              getRepository(pullreq.requestUserName, pullreq.requestRepositoryName).get
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
                pullreq.requestBranch
 | 
			
		||||
              ).needStatusCheck(u.userName)
 | 
			
		||||
            },
 | 
			
		||||
          hasMergePermission = hasMergePermission,
 | 
			
		||||
          commitIdTo = pullreq.commitIdTo
 | 
			
		||||
        )
 | 
			
		||||
        html.mergeguide(
 | 
			
		||||
          mergeStatus,
 | 
			
		||||
          issue,
 | 
			
		||||
          pullreq,
 | 
			
		||||
          repository,
 | 
			
		||||
          getRepository(pullreq.requestUserName, pullreq.requestRepositoryName).get
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    } getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -314,7 +306,9 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
      } else {
 | 
			
		||||
        LockUtil.lock(s"${owner}/${name}") {
 | 
			
		||||
          val alias =
 | 
			
		||||
            if (pullreq.repositoryName == pullreq.requestRepositoryName && pullreq.userName == pullreq.requestUserName) {
 | 
			
		||||
            if (
 | 
			
		||||
              pullreq.repositoryName == pullreq.requestRepositoryName && pullreq.userName == pullreq.requestUserName
 | 
			
		||||
            ) {
 | 
			
		||||
              pullreq.branch
 | 
			
		||||
            } else {
 | 
			
		||||
              s"${pullreq.userName}:${pullreq.branch}"
 | 
			
		||||
@@ -383,29 +377,27 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
    val headBranch = params.get("head")
 | 
			
		||||
    (forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match {
 | 
			
		||||
      case (Some(originUserName), Some(originRepositoryName)) =>
 | 
			
		||||
        getRepository(originUserName, originRepositoryName).map {
 | 
			
		||||
          originRepository =>
 | 
			
		||||
            Using.resources(
 | 
			
		||||
              Git.open(getRepositoryDir(originUserName, originRepositoryName)),
 | 
			
		||||
              Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
 | 
			
		||||
            ) { (oldGit, newGit) =>
 | 
			
		||||
              val newBranch = headBranch.getOrElse(JGitUtil.getDefaultBranch(newGit, forkedRepository).get._2)
 | 
			
		||||
              val oldBranch = originRepository.branchList
 | 
			
		||||
                .find(_ == newBranch)
 | 
			
		||||
                .getOrElse(JGitUtil.getDefaultBranch(oldGit, originRepository).get._2)
 | 
			
		||||
        getRepository(originUserName, originRepositoryName).map { originRepository =>
 | 
			
		||||
          Using.resources(
 | 
			
		||||
            Git.open(getRepositoryDir(originUserName, originRepositoryName)),
 | 
			
		||||
            Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
 | 
			
		||||
          ) { (oldGit, newGit) =>
 | 
			
		||||
            val newBranch = headBranch.getOrElse(JGitUtil.getDefaultBranch(newGit, forkedRepository).get._2)
 | 
			
		||||
            val oldBranch = originRepository.branchList
 | 
			
		||||
              .find(_ == newBranch)
 | 
			
		||||
              .getOrElse(JGitUtil.getDefaultBranch(oldGit, originRepository).get._2)
 | 
			
		||||
 | 
			
		||||
              redirect(
 | 
			
		||||
                s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${originUserName}:${oldBranch}...${newBranch}"
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
            redirect(
 | 
			
		||||
              s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${originUserName}:${oldBranch}...${newBranch}"
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        } getOrElse NotFound()
 | 
			
		||||
      case _ =>
 | 
			
		||||
        Using.resource(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))) { git =>
 | 
			
		||||
          JGitUtil.getDefaultBranch(git, forkedRepository).map {
 | 
			
		||||
            case (_, defaultBranch) =>
 | 
			
		||||
              redirect(
 | 
			
		||||
                s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${defaultBranch}...${headBranch.getOrElse(defaultBranch)}"
 | 
			
		||||
              )
 | 
			
		||||
          JGitUtil.getDefaultBranch(git, forkedRepository).map { case (_, defaultBranch) =>
 | 
			
		||||
            redirect(
 | 
			
		||||
              s"/${forkedRepository.owner}/${forkedRepository.name}/compare/${defaultBranch}...${headBranch.getOrElse(defaultBranch)}"
 | 
			
		||||
            )
 | 
			
		||||
          } getOrElse {
 | 
			
		||||
            redirect(s"/${forkedRepository.owner}/${forkedRepository.name}")
 | 
			
		||||
          }
 | 
			
		||||
@@ -445,8 +437,10 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
    val (originOwner, originId) = parseCompareIdentifier(origin, forkedRepository.owner)
 | 
			
		||||
    val (forkedOwner, forkedId) = parseCompareIdentifier(forked, forkedRepository.owner)
 | 
			
		||||
 | 
			
		||||
    (for (originRepositoryName <- getOriginRepositoryName(originOwner, forkedOwner, forkedRepository);
 | 
			
		||||
          originRepository <- getRepository(originOwner, originRepositoryName)) yield {
 | 
			
		||||
    (for (
 | 
			
		||||
      originRepositoryName <- getOriginRepositoryName(originOwner, forkedOwner, forkedRepository);
 | 
			
		||||
      originRepository <- getRepository(originOwner, originRepositoryName)
 | 
			
		||||
    ) yield {
 | 
			
		||||
      val (oldId, newId) =
 | 
			
		||||
        getPullRequestCommitFromTo(originRepository, forkedRepository, originId, forkedId)
 | 
			
		||||
 | 
			
		||||
@@ -566,87 +560,88 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
    val (originOwner, tmpOriginBranch) = parseCompareIdentifier(origin, forkedRepository.owner)
 | 
			
		||||
    val (forkedOwner, tmpForkedBranch) = parseCompareIdentifier(forked, forkedRepository.owner)
 | 
			
		||||
 | 
			
		||||
    (for (originRepositoryName <- if (originOwner == forkedOwner) {
 | 
			
		||||
            Some(forkedRepository.name)
 | 
			
		||||
          } else {
 | 
			
		||||
            forkedRepository.repository.originRepositoryName.orElse {
 | 
			
		||||
              getForkedRepositories(forkedRepository.owner, forkedRepository.name)
 | 
			
		||||
                .find(_.userName == originOwner)
 | 
			
		||||
                .map(_.repositoryName)
 | 
			
		||||
            }
 | 
			
		||||
          };
 | 
			
		||||
          originRepository <- getRepository(originOwner, originRepositoryName)) yield {
 | 
			
		||||
    (for (
 | 
			
		||||
      originRepositoryName <-
 | 
			
		||||
        if (originOwner == forkedOwner) {
 | 
			
		||||
          Some(forkedRepository.name)
 | 
			
		||||
        } else {
 | 
			
		||||
          forkedRepository.repository.originRepositoryName.orElse {
 | 
			
		||||
            getForkedRepositories(forkedRepository.owner, forkedRepository.name)
 | 
			
		||||
              .find(_.userName == originOwner)
 | 
			
		||||
              .map(_.repositoryName)
 | 
			
		||||
          }
 | 
			
		||||
        };
 | 
			
		||||
      originRepository <- getRepository(originOwner, originRepositoryName)
 | 
			
		||||
    ) yield {
 | 
			
		||||
      Using.resources(
 | 
			
		||||
        Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
 | 
			
		||||
        Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
 | 
			
		||||
      ) {
 | 
			
		||||
        case (oldGit, newGit) =>
 | 
			
		||||
          val originBranch = JGitUtil.getDefaultBranch(oldGit, originRepository, tmpOriginBranch).get._2
 | 
			
		||||
          val forkedBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository, tmpForkedBranch).get._2
 | 
			
		||||
          val conflict = LockUtil.lock(s"${originRepository.owner}/${originRepository.name}") {
 | 
			
		||||
            checkConflict(
 | 
			
		||||
              originRepository.owner,
 | 
			
		||||
              originRepository.name,
 | 
			
		||||
              originBranch,
 | 
			
		||||
              forkedRepository.owner,
 | 
			
		||||
              forkedRepository.name,
 | 
			
		||||
              forkedBranch
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
          html.mergecheck(conflict.isDefined)
 | 
			
		||||
      ) { case (oldGit, newGit) =>
 | 
			
		||||
        val originBranch = JGitUtil.getDefaultBranch(oldGit, originRepository, tmpOriginBranch).get._2
 | 
			
		||||
        val forkedBranch = JGitUtil.getDefaultBranch(newGit, forkedRepository, tmpForkedBranch).get._2
 | 
			
		||||
        val conflict = LockUtil.lock(s"${originRepository.owner}/${originRepository.name}") {
 | 
			
		||||
          checkConflict(
 | 
			
		||||
            originRepository.owner,
 | 
			
		||||
            originRepository.name,
 | 
			
		||||
            originBranch,
 | 
			
		||||
            forkedRepository.owner,
 | 
			
		||||
            forkedRepository.name,
 | 
			
		||||
            forkedBranch
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
        html.mergecheck(conflict.isDefined)
 | 
			
		||||
      }
 | 
			
		||||
    }) getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  post("/:owner/:repository/pulls/new", pullRequestForm)(readableUsersOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        val manageable = isManageable(repository)
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      val manageable = isManageable(repository)
 | 
			
		||||
 | 
			
		||||
        val issueId = insertIssue(
 | 
			
		||||
          owner = repository.owner,
 | 
			
		||||
          repository = repository.name,
 | 
			
		||||
          loginUser = loginAccount.userName,
 | 
			
		||||
          title = form.title,
 | 
			
		||||
          content = form.content,
 | 
			
		||||
          milestoneId = if (manageable) form.milestoneId else None,
 | 
			
		||||
          priorityId = if (manageable) form.priorityId else None,
 | 
			
		||||
          isPullRequest = true
 | 
			
		||||
        )
 | 
			
		||||
      val issueId = insertIssue(
 | 
			
		||||
        owner = repository.owner,
 | 
			
		||||
        repository = repository.name,
 | 
			
		||||
        loginUser = loginAccount.userName,
 | 
			
		||||
        title = form.title,
 | 
			
		||||
        content = form.content,
 | 
			
		||||
        milestoneId = if (manageable) form.milestoneId else None,
 | 
			
		||||
        priorityId = if (manageable) form.priorityId else None,
 | 
			
		||||
        isPullRequest = true
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
        createPullRequest(
 | 
			
		||||
          originRepository = repository,
 | 
			
		||||
          issueId = issueId,
 | 
			
		||||
          originBranch = form.targetBranch,
 | 
			
		||||
          requestUserName = form.requestUserName,
 | 
			
		||||
          requestRepositoryName = form.requestRepositoryName,
 | 
			
		||||
          requestBranch = form.requestBranch,
 | 
			
		||||
          commitIdFrom = form.commitIdFrom,
 | 
			
		||||
          commitIdTo = form.commitIdTo,
 | 
			
		||||
          isDraft = form.isDraft,
 | 
			
		||||
          loginAccount = loginAccount,
 | 
			
		||||
          settings = context.settings
 | 
			
		||||
        )
 | 
			
		||||
      createPullRequest(
 | 
			
		||||
        originRepository = repository,
 | 
			
		||||
        issueId = issueId,
 | 
			
		||||
        originBranch = form.targetBranch,
 | 
			
		||||
        requestUserName = form.requestUserName,
 | 
			
		||||
        requestRepositoryName = form.requestRepositoryName,
 | 
			
		||||
        requestBranch = form.requestBranch,
 | 
			
		||||
        commitIdFrom = form.commitIdFrom,
 | 
			
		||||
        commitIdTo = form.commitIdTo,
 | 
			
		||||
        isDraft = form.isDraft,
 | 
			
		||||
        loginAccount = loginAccount,
 | 
			
		||||
        settings = context.settings
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
        if (manageable) {
 | 
			
		||||
          // insert assignees
 | 
			
		||||
          form.assigneeUserNames.foreach { value =>
 | 
			
		||||
            value.split(",").foreach { userName =>
 | 
			
		||||
              registerIssueAssignee(repository.owner, repository.name, issueId, userName)
 | 
			
		||||
            }
 | 
			
		||||
      if (manageable) {
 | 
			
		||||
        // insert assignees
 | 
			
		||||
        form.assigneeUserNames.foreach { value =>
 | 
			
		||||
          value.split(",").foreach { userName =>
 | 
			
		||||
            registerIssueAssignee(repository.owner, repository.name, issueId, userName)
 | 
			
		||||
          }
 | 
			
		||||
          // insert labels
 | 
			
		||||
          form.labelNames.foreach { value =>
 | 
			
		||||
            val labels = getLabels(repository.owner, repository.name)
 | 
			
		||||
            value.split(",").foreach { labelName =>
 | 
			
		||||
              labels.find(_.labelName == labelName).map { label =>
 | 
			
		||||
                registerIssueLabel(repository.owner, repository.name, issueId, label.labelId)
 | 
			
		||||
              }
 | 
			
		||||
        }
 | 
			
		||||
        // insert labels
 | 
			
		||||
        form.labelNames.foreach { value =>
 | 
			
		||||
          val labels = getLabels(repository.owner, repository.name)
 | 
			
		||||
          value.split(",").foreach { labelName =>
 | 
			
		||||
            labels.find(_.labelName == labelName).map { label =>
 | 
			
		||||
              registerIssueLabel(repository.owner, repository.name, issueId, label.labelId)
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
 | 
			
		||||
      redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -656,24 +651,23 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
      context.loginAccount.map(x => Seq(x.mailAddress) ++ getAccountExtraMailAddresses(x.userName)).getOrElse(Nil)
 | 
			
		||||
 | 
			
		||||
    val branches =
 | 
			
		||||
      Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
 | 
			
		||||
        git =>
 | 
			
		||||
          JGitUtil
 | 
			
		||||
            .getBranches(
 | 
			
		||||
              git = git,
 | 
			
		||||
              defaultBranch = repository.repository.defaultBranch,
 | 
			
		||||
              origin = repository.repository.originUserName.isEmpty
 | 
			
		||||
            )
 | 
			
		||||
            .filter { x =>
 | 
			
		||||
              x.mergeInfo.map(_.ahead).getOrElse(0) > 0 && x.mergeInfo.map(_.behind).getOrElse(0) == 0 &&
 | 
			
		||||
              x.commitTime.getTime > thresholdTime &&
 | 
			
		||||
              mailAddresses.contains(x.committerEmailAddress)
 | 
			
		||||
            }
 | 
			
		||||
            .sortBy { br =>
 | 
			
		||||
              (br.mergeInfo.isEmpty, br.commitTime)
 | 
			
		||||
            }
 | 
			
		||||
            .map(_.name)
 | 
			
		||||
            .reverse
 | 
			
		||||
      Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
        JGitUtil
 | 
			
		||||
          .getBranches(
 | 
			
		||||
            git = git,
 | 
			
		||||
            defaultBranch = repository.repository.defaultBranch,
 | 
			
		||||
            origin = repository.repository.originUserName.isEmpty
 | 
			
		||||
          )
 | 
			
		||||
          .filter { x =>
 | 
			
		||||
            x.mergeInfo.map(_.ahead).getOrElse(0) > 0 && x.mergeInfo.map(_.behind).getOrElse(0) == 0 &&
 | 
			
		||||
            x.commitTime.getTime > thresholdTime &&
 | 
			
		||||
            mailAddresses.contains(x.committerEmailAddress)
 | 
			
		||||
          }
 | 
			
		||||
          .sortBy { br =>
 | 
			
		||||
            (br.mergeInfo.isEmpty, br.commitTime)
 | 
			
		||||
          }
 | 
			
		||||
          .map(_.name)
 | 
			
		||||
          .reverse
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    val targetRepository = (for {
 | 
			
		||||
 
 | 
			
		||||
@@ -106,33 +106,31 @@ trait ReleaseControllerBase extends ControllerBase {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  post("/:owner/:repository/releases/*/create", releaseForm)(writableUsersOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        val tagName = multiParams("splat").head
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      val tagName = multiParams("splat").head
 | 
			
		||||
 | 
			
		||||
        // Insert into RELEASE
 | 
			
		||||
        createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount)
 | 
			
		||||
      // Insert into RELEASE
 | 
			
		||||
      createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount)
 | 
			
		||||
 | 
			
		||||
        // Insert into RELEASE_ASSET
 | 
			
		||||
        val files = params.toMap.collect {
 | 
			
		||||
          case (name, value) if name.startsWith("file:") =>
 | 
			
		||||
            val Array(_, fileId) = name.split(":")
 | 
			
		||||
            (fileId, value)
 | 
			
		||||
        }
 | 
			
		||||
        files.foreach {
 | 
			
		||||
          case (fileId, fileName) =>
 | 
			
		||||
            val size =
 | 
			
		||||
              new File(
 | 
			
		||||
                getReleaseFilesDir(repository.owner, repository.name),
 | 
			
		||||
                FileUtil.checkFilename(tagName + "/" + fileId)
 | 
			
		||||
              ).length
 | 
			
		||||
            createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
 | 
			
		||||
        }
 | 
			
		||||
      // Insert into RELEASE_ASSET
 | 
			
		||||
      val files = params.toMap.collect {
 | 
			
		||||
        case (name, value) if name.startsWith("file:") =>
 | 
			
		||||
          val Array(_, fileId) = name.split(":")
 | 
			
		||||
          (fileId, value)
 | 
			
		||||
      }
 | 
			
		||||
      files.foreach { case (fileId, fileName) =>
 | 
			
		||||
        val size =
 | 
			
		||||
          new File(
 | 
			
		||||
            getReleaseFilesDir(repository.owner, repository.name),
 | 
			
		||||
            FileUtil.checkFilename(tagName + "/" + fileId)
 | 
			
		||||
          ).length
 | 
			
		||||
        createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        val releaseInfo = ReleaseInfo(repository.owner, repository.name, loginAccount.userName, form.name, tagName)
 | 
			
		||||
        recordActivity(releaseInfo)
 | 
			
		||||
      val releaseInfo = ReleaseInfo(repository.owner, repository.name, loginAccount.userName, form.name, tagName)
 | 
			
		||||
      recordActivity(releaseInfo)
 | 
			
		||||
 | 
			
		||||
        redirect(s"/${repository.owner}/${repository.name}/releases/${tagName}")
 | 
			
		||||
      redirect(s"/${repository.owner}/${repository.name}/releases/${tagName}")
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -171,48 +169,45 @@ trait ReleaseControllerBase extends ControllerBase {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  post("/:owner/:repository/releases/*/edit", releaseForm)(writableUsersOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        val tagName = multiParams("splat").head
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      val tagName = multiParams("splat").head
 | 
			
		||||
 | 
			
		||||
        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.toMap.collect {
 | 
			
		||||
                case (name, value) if name.startsWith("file:") =>
 | 
			
		||||
                  val Array(_, fileId) = name.split(":")
 | 
			
		||||
                  (fileId, value)
 | 
			
		||||
              }
 | 
			
		||||
              files.foreach {
 | 
			
		||||
                case (fileId, fileName) =>
 | 
			
		||||
                  val size =
 | 
			
		||||
                    new File(
 | 
			
		||||
                      getReleaseFilesDir(repository.owner, repository.name),
 | 
			
		||||
                      FileUtil.checkFilename(tagName + "/" + fileId)
 | 
			
		||||
                    ).length
 | 
			
		||||
                  createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              assets.foreach { asset =>
 | 
			
		||||
                if (!files.exists { case (fileId, _) => fileId == asset.fileName }) {
 | 
			
		||||
                  val file = new File(
 | 
			
		||||
                    getReleaseFilesDir(repository.owner, repository.name),
 | 
			
		||||
                    FileUtil.checkFilename(release.tag + "/" + asset.fileName)
 | 
			
		||||
                  )
 | 
			
		||||
                  FileUtils.forceDelete(file)
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              redirect(s"/${release.userName}/${release.repositoryName}/releases/${tagName}")
 | 
			
		||||
          val files = params.toMap.collect {
 | 
			
		||||
            case (name, value) if name.startsWith("file:") =>
 | 
			
		||||
              val Array(_, fileId) = name.split(":")
 | 
			
		||||
              (fileId, value)
 | 
			
		||||
          }
 | 
			
		||||
          .getOrElse(NotFound())
 | 
			
		||||
          files.foreach { case (fileId, fileName) =>
 | 
			
		||||
            val size =
 | 
			
		||||
              new File(
 | 
			
		||||
                getReleaseFilesDir(repository.owner, repository.name),
 | 
			
		||||
                FileUtil.checkFilename(tagName + "/" + fileId)
 | 
			
		||||
              ).length
 | 
			
		||||
            createReleaseAsset(repository.owner, repository.name, tagName, fileId, fileName, size, loginAccount)
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          assets.foreach { asset =>
 | 
			
		||||
            if (!files.exists { case (fileId, _) => fileId == asset.fileName }) {
 | 
			
		||||
              val file = new File(
 | 
			
		||||
                getReleaseFilesDir(repository.owner, repository.name),
 | 
			
		||||
                FileUtil.checkFilename(release.tag + "/" + asset.fileName)
 | 
			
		||||
              )
 | 
			
		||||
              FileUtils.forceDelete(file)
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          redirect(s"/${release.userName}/${release.repositoryName}/releases/${tagName}")
 | 
			
		||||
        }
 | 
			
		||||
        .getOrElse(NotFound())
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -237,9 +232,12 @@ trait ReleaseControllerBase extends ControllerBase {
 | 
			
		||||
    val assets = getReleaseAssetsMap(repository.owner, repository.name, releases)
 | 
			
		||||
 | 
			
		||||
    val tagsWithReleases = tagsToDisplay.map { tag =>
 | 
			
		||||
      (tag, releases.find(_.tag == tag.name).map { release =>
 | 
			
		||||
        (release, assets(release))
 | 
			
		||||
      })
 | 
			
		||||
      (
 | 
			
		||||
        tag,
 | 
			
		||||
        releases.find(_.tag == tag.name).map { release =>
 | 
			
		||||
          (release, assets(release))
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
    tagsWithReleases
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -286,93 +286,90 @@ trait RepositorySettingsControllerBase extends ControllerBase {
 | 
			
		||||
        Array(h.getName, h.getValue)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
 | 
			
		||||
      git =>
 | 
			
		||||
        import scala.concurrent.duration._
 | 
			
		||||
        import scala.concurrent._
 | 
			
		||||
        import scala.jdk.CollectionConverters._
 | 
			
		||||
        import scala.util.control.NonFatal
 | 
			
		||||
        import org.apache.http.util.EntityUtils
 | 
			
		||||
        import scala.concurrent.ExecutionContext.Implicits.global
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
      import scala.concurrent.duration._
 | 
			
		||||
      import scala.concurrent._
 | 
			
		||||
      import scala.jdk.CollectionConverters._
 | 
			
		||||
      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(
 | 
			
		||||
          userName = repository.owner,
 | 
			
		||||
          repositoryName = repository.name,
 | 
			
		||||
          url = url,
 | 
			
		||||
          ctype = ctype,
 | 
			
		||||
          token = token
 | 
			
		||||
      val url = params("url")
 | 
			
		||||
      val token = Some(params("token"))
 | 
			
		||||
      val ctype = WebHookContentType.valueOf(params("ctype"))
 | 
			
		||||
      val dummyWebHookInfo = RepositoryWebHook(
 | 
			
		||||
        userName = repository.owner,
 | 
			
		||||
        repositoryName = repository.name,
 | 
			
		||||
        url = url,
 | 
			
		||||
        ctype = ctype,
 | 
			
		||||
        token = token
 | 
			
		||||
      )
 | 
			
		||||
      val dummyPayload = {
 | 
			
		||||
        val ownerAccount = getAccountByUserName(repository.owner).get
 | 
			
		||||
        val commits =
 | 
			
		||||
          if (JGitUtil.isEmpty(git)) List.empty
 | 
			
		||||
          else
 | 
			
		||||
            git.log
 | 
			
		||||
              .add(git.getRepository.resolve(repository.repository.defaultBranch))
 | 
			
		||||
              .setMaxCount(4)
 | 
			
		||||
              .call
 | 
			
		||||
              .iterator
 | 
			
		||||
              .asScala
 | 
			
		||||
              .map(new CommitInfo(_))
 | 
			
		||||
              .toList
 | 
			
		||||
        val pushedCommit = commits.drop(1)
 | 
			
		||||
 | 
			
		||||
        WebHookPushPayload(
 | 
			
		||||
          git = git,
 | 
			
		||||
          sender = ownerAccount,
 | 
			
		||||
          refName = "refs/heads/" + repository.repository.defaultBranch,
 | 
			
		||||
          repositoryInfo = repository,
 | 
			
		||||
          commits = pushedCommit,
 | 
			
		||||
          repositoryOwner = ownerAccount,
 | 
			
		||||
          oldId = commits.lastOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId()),
 | 
			
		||||
          newId = commits.headOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId())
 | 
			
		||||
        )
 | 
			
		||||
        val dummyPayload = {
 | 
			
		||||
          val ownerAccount = getAccountByUserName(repository.owner).get
 | 
			
		||||
          val commits =
 | 
			
		||||
            if (JGitUtil.isEmpty(git)) List.empty
 | 
			
		||||
            else
 | 
			
		||||
              git.log
 | 
			
		||||
                .add(git.getRepository.resolve(repository.repository.defaultBranch))
 | 
			
		||||
                .setMaxCount(4)
 | 
			
		||||
                .call
 | 
			
		||||
                .iterator
 | 
			
		||||
                .asScala
 | 
			
		||||
                .map(new CommitInfo(_))
 | 
			
		||||
                .toList
 | 
			
		||||
          val pushedCommit = commits.drop(1)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
          WebHookPushPayload(
 | 
			
		||||
            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, context.settings).head
 | 
			
		||||
 | 
			
		||||
        val (webHook, json, reqFuture, resFuture) =
 | 
			
		||||
          callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload, context.settings).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" -> (s"${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" -> (s"${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
 | 
			
		||||
                  )
 | 
			
		||||
      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.getStatusCode,
 | 
			
		||||
                      "body" -> EntityUtils.toString(res.getEntity()),
 | 
			
		||||
                      "headers" -> _headers(res.getAllHeaders())
 | 
			
		||||
                  )
 | 
			
		||||
              )
 | 
			
		||||
              .recover(toErrorMap),
 | 
			
		||||
            20 seconds
 | 
			
		||||
          ),
 | 
			
		||||
          "response" -> Await.result(
 | 
			
		||||
            resFuture
 | 
			
		||||
              .map(res =>
 | 
			
		||||
                Map(
 | 
			
		||||
                  "status" -> res.getStatusLine.getStatusCode,
 | 
			
		||||
                  "body" -> EntityUtils.toString(res.getEntity()),
 | 
			
		||||
                  "headers" -> _headers(res.getAllHeaders())
 | 
			
		||||
                )
 | 
			
		||||
                .recover(toErrorMap),
 | 
			
		||||
              20 seconds
 | 
			
		||||
            )
 | 
			
		||||
              )
 | 
			
		||||
              .recover(toErrorMap),
 | 
			
		||||
            20 seconds
 | 
			
		||||
          )
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -380,9 +377,8 @@ trait RepositorySettingsControllerBase extends ControllerBase {
 | 
			
		||||
   * Display the web hook edit page.
 | 
			
		||||
   */
 | 
			
		||||
  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()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -406,23 +402,22 @@ trait RepositorySettingsControllerBase extends ControllerBase {
 | 
			
		||||
   * Rename repository.
 | 
			
		||||
   */
 | 
			
		||||
  post("/:owner/:repository/settings/rename", renameForm)(ownerOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (context.settings.basicBehavior.repositoryOperation.rename || loginAccount.isAdmin) {
 | 
			
		||||
          if (repository.name != form.repositoryName) {
 | 
			
		||||
            // Update database and move git repository
 | 
			
		||||
            renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
 | 
			
		||||
            // Record activity log
 | 
			
		||||
            val renameInfo = RenameRepositoryInfo(
 | 
			
		||||
              repository.owner,
 | 
			
		||||
              form.repositoryName,
 | 
			
		||||
              loginAccount.userName,
 | 
			
		||||
              repository.name
 | 
			
		||||
            )
 | 
			
		||||
            recordActivity(renameInfo)
 | 
			
		||||
          }
 | 
			
		||||
          redirect(s"/${repository.owner}/${form.repositoryName}")
 | 
			
		||||
        } else Forbidden()
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (context.settings.basicBehavior.repositoryOperation.rename || loginAccount.isAdmin) {
 | 
			
		||||
        if (repository.name != form.repositoryName) {
 | 
			
		||||
          // Update database and move git repository
 | 
			
		||||
          renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
 | 
			
		||||
          // Record activity log
 | 
			
		||||
          val renameInfo = RenameRepositoryInfo(
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            form.repositoryName,
 | 
			
		||||
            loginAccount.userName,
 | 
			
		||||
            repository.name
 | 
			
		||||
          )
 | 
			
		||||
          recordActivity(renameInfo)
 | 
			
		||||
        }
 | 
			
		||||
        redirect(s"/${repository.owner}/${form.repositoryName}")
 | 
			
		||||
      } else Forbidden()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -430,24 +425,23 @@ trait RepositorySettingsControllerBase extends ControllerBase {
 | 
			
		||||
   * Transfer repository ownership.
 | 
			
		||||
   */
 | 
			
		||||
  post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (context.settings.basicBehavior.repositoryOperation.transfer || loginAccount.isAdmin) {
 | 
			
		||||
          // Change repository owner
 | 
			
		||||
          if (repository.owner != form.newOwner) {
 | 
			
		||||
            // Update database and move git repository
 | 
			
		||||
            renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
 | 
			
		||||
            // Record activity log
 | 
			
		||||
            val renameInfo = RenameRepositoryInfo(
 | 
			
		||||
              form.newOwner,
 | 
			
		||||
              repository.name,
 | 
			
		||||
              loginAccount.userName,
 | 
			
		||||
              repository.owner
 | 
			
		||||
            )
 | 
			
		||||
            recordActivity(renameInfo)
 | 
			
		||||
          }
 | 
			
		||||
          redirect(s"/${form.newOwner}/${repository.name}")
 | 
			
		||||
        } else Forbidden()
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (context.settings.basicBehavior.repositoryOperation.transfer || loginAccount.isAdmin) {
 | 
			
		||||
        // Change repository owner
 | 
			
		||||
        if (repository.owner != form.newOwner) {
 | 
			
		||||
          // Update database and move git repository
 | 
			
		||||
          renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
 | 
			
		||||
          // Record activity log
 | 
			
		||||
          val renameInfo = RenameRepositoryInfo(
 | 
			
		||||
            form.newOwner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            loginAccount.userName,
 | 
			
		||||
            repository.owner
 | 
			
		||||
          )
 | 
			
		||||
          recordActivity(renameInfo)
 | 
			
		||||
        }
 | 
			
		||||
        redirect(s"/${form.newOwner}/${repository.name}")
 | 
			
		||||
      } else Forbidden()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -55,16 +55,14 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
      "bindAddress" -> mapping(
 | 
			
		||||
        "host" -> trim(label("Bind SSH host", optional(text()))),
 | 
			
		||||
        "port" -> trim(label("Bind SSH port", optional(number()))),
 | 
			
		||||
      )(
 | 
			
		||||
        (hostOption, portOption) =>
 | 
			
		||||
          hostOption.map(h => SshAddress(h, portOption.getOrElse(DefaultSshPort), GenericSshUser))
 | 
			
		||||
      )((hostOption, portOption) =>
 | 
			
		||||
        hostOption.map(h => SshAddress(h, portOption.getOrElse(DefaultSshPort), GenericSshUser))
 | 
			
		||||
      ),
 | 
			
		||||
      "publicAddress" -> mapping(
 | 
			
		||||
        "host" -> trim(label("Public SSH host", optional(text()))),
 | 
			
		||||
        "port" -> trim(label("Public SSH port", optional(number()))),
 | 
			
		||||
      )(
 | 
			
		||||
        (hostOption, portOption) =>
 | 
			
		||||
          hostOption.map(h => SshAddress(h, portOption.getOrElse(PublicSshPort), GenericSshUser))
 | 
			
		||||
      )((hostOption, portOption) =>
 | 
			
		||||
        hostOption.map(h => SshAddress(h, portOption.getOrElse(PublicSshPort), GenericSshUser))
 | 
			
		||||
      ),
 | 
			
		||||
    )(Ssh.apply),
 | 
			
		||||
    "useSMTP" -> trim(label("SMTP", boolean())),
 | 
			
		||||
@@ -251,28 +249,27 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
    val conn = request2Session(request).conn
 | 
			
		||||
    val meta = conn.getMetaData
 | 
			
		||||
    val tables = ListBuffer[Table]()
 | 
			
		||||
    Using.resource(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) {
 | 
			
		||||
      rs =>
 | 
			
		||||
        while (rs.next()) {
 | 
			
		||||
          val tableName = rs.getString("TABLE_NAME")
 | 
			
		||||
    Using.resource(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) { rs =>
 | 
			
		||||
      while (rs.next()) {
 | 
			
		||||
        val tableName = rs.getString("TABLE_NAME")
 | 
			
		||||
 | 
			
		||||
          val pkColumns = ListBuffer[String]()
 | 
			
		||||
          Using.resource(meta.getPrimaryKeys(null, null, tableName)) { rs =>
 | 
			
		||||
            while (rs.next()) {
 | 
			
		||||
              pkColumns += rs.getString("COLUMN_NAME").toUpperCase
 | 
			
		||||
            }
 | 
			
		||||
        val pkColumns = ListBuffer[String]()
 | 
			
		||||
        Using.resource(meta.getPrimaryKeys(null, null, tableName)) { rs =>
 | 
			
		||||
          while (rs.next()) {
 | 
			
		||||
            pkColumns += rs.getString("COLUMN_NAME").toUpperCase
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          val columns = ListBuffer[Column]()
 | 
			
		||||
          Using.resource(meta.getColumns(null, "%", tableName, "%")) { rs =>
 | 
			
		||||
            while (rs.next()) {
 | 
			
		||||
              val columnName = rs.getString("COLUMN_NAME").toUpperCase
 | 
			
		||||
              columns += Column(columnName, pkColumns.contains(columnName))
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          tables += Table(tableName.toUpperCase, columns.toSeq)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val columns = ListBuffer[Column]()
 | 
			
		||||
        Using.resource(meta.getColumns(null, "%", tableName, "%")) { rs =>
 | 
			
		||||
          while (rs.next()) {
 | 
			
		||||
            val columnName = rs.getString("COLUMN_NAME").toUpperCase
 | 
			
		||||
            columns += Column(columnName, pkColumns.contains(columnName))
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        tables += Table(tableName.toUpperCase, columns.toSeq)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    html.dbviewer(tables.toSeq)
 | 
			
		||||
  })
 | 
			
		||||
@@ -285,28 +282,26 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
        if (trimmedQuery.nonEmpty) {
 | 
			
		||||
          try {
 | 
			
		||||
            val conn = request2Session(request).conn
 | 
			
		||||
            Using.resource(conn.prepareStatement(query)) {
 | 
			
		||||
              stmt =>
 | 
			
		||||
                if (trimmedQuery.toUpperCase.startsWith("SELECT")) {
 | 
			
		||||
                  Using.resource(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)))
 | 
			
		||||
            Using.resource(conn.prepareStatement(query)) { stmt =>
 | 
			
		||||
              if (trimmedQuery.toUpperCase.startsWith("SELECT")) {
 | 
			
		||||
                Using.resource(stmt.executeQuery()) { rs =>
 | 
			
		||||
                  val meta = rs.getMetaData
 | 
			
		||||
                  val columns = for (i <- 1 to meta.getColumnCount) yield {
 | 
			
		||||
                    meta.getColumnName(i)
 | 
			
		||||
                  }
 | 
			
		||||
                } 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 =>
 | 
			
		||||
@@ -323,7 +318,9 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
  post("/admin/system", form)(adminOnly { form =>
 | 
			
		||||
    saveSystemSettings(form)
 | 
			
		||||
 | 
			
		||||
    if (form.ssh.bindAddress != context.settings.sshBindAddress || form.ssh.publicAddress != context.settings.sshPublicAddress) {
 | 
			
		||||
    if (
 | 
			
		||||
      form.ssh.bindAddress != context.settings.sshBindAddress || form.ssh.publicAddress != context.settings.sshPublicAddress
 | 
			
		||||
    ) {
 | 
			
		||||
      SshServer.stop()
 | 
			
		||||
      for {
 | 
			
		||||
        bindAddress <- form.ssh.bindAddress
 | 
			
		||||
@@ -420,44 +417,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.update("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(pbkdf2_sha256).getOrElse(account.password),
 | 
			
		||||
              fullName = form.fullName,
 | 
			
		||||
              mailAddress = form.mailAddress,
 | 
			
		||||
              isAdmin = form.isAdmin,
 | 
			
		||||
              description = form.description,
 | 
			
		||||
              url = form.url,
 | 
			
		||||
              isRemoved = form.isRemoved
 | 
			
		||||
            )
 | 
			
		||||
          )
 | 
			
		||||
 | 
			
		||||
          updateImage(userName, form.fileId, form.clearImage)
 | 
			
		||||
          updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
 | 
			
		||||
 | 
			
		||||
          // call hooks
 | 
			
		||||
          if (form.isRemoved) PluginRegistry().getAccountHooks.foreach(_.deleted(userName))
 | 
			
		||||
 | 
			
		||||
          redirect("/admin/users")
 | 
			
		||||
    getAccountByUserName(userName, true).map { account =>
 | 
			
		||||
      if (account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(account)) {
 | 
			
		||||
        flash.update("error", "Account can't be turned off because this is last one administrator.")
 | 
			
		||||
        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(pbkdf2_sha256).getOrElse(account.password),
 | 
			
		||||
            fullName = form.fullName,
 | 
			
		||||
            mailAddress = form.mailAddress,
 | 
			
		||||
            isAdmin = form.isAdmin,
 | 
			
		||||
            description = form.description,
 | 
			
		||||
            url = form.url,
 | 
			
		||||
            isRemoved = form.isRemoved
 | 
			
		||||
          )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        updateImage(userName, form.fileId, form.clearImage)
 | 
			
		||||
        updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
 | 
			
		||||
 | 
			
		||||
        // call hooks
 | 
			
		||||
        if (form.isRemoved) PluginRegistry().getAccountHooks.foreach(_.deleted(userName))
 | 
			
		||||
 | 
			
		||||
        redirect("/admin/users")
 | 
			
		||||
      }
 | 
			
		||||
    } getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -498,13 +494,12 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
      }
 | 
			
		||||
      .toList
 | 
			
		||||
 | 
			
		||||
    getAccountByUserName(groupName, true).map {
 | 
			
		||||
      account =>
 | 
			
		||||
        updateGroup(groupName, form.description, form.url, form.isRemoved)
 | 
			
		||||
    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)
 | 
			
		||||
@@ -512,9 +507,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)
 | 
			
		||||
@@ -522,10 +517,10 @@ 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()
 | 
			
		||||
  })
 | 
			
		||||
@@ -565,9 +560,11 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
 | 
			
		||||
  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
 | 
			
		||||
        if (
 | 
			
		||||
          value.split(",").exists {
 | 
			
		||||
            _.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
 | 
			
		||||
          }
 | 
			
		||||
        ) None
 | 
			
		||||
        else Some("Must select one manager at least.")
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -84,9 +84,8 @@ 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)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -146,39 +146,37 @@ trait WikiControllerBase extends ControllerBase {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  get("/:owner/:repository/wiki/:page/_revert/:commitId")(readableUsersOnly { repository =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (isEditable(repository)) {
 | 
			
		||||
          val pageName = StringUtil.urlDecode(params("page"))
 | 
			
		||||
          val Array(from, to) = params("commitId").split("\\.\\.\\.")
 | 
			
		||||
          val branch = getWikiBranch(repository.owner, repository.name)
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (isEditable(repository)) {
 | 
			
		||||
        val pageName = StringUtil.urlDecode(params("page"))
 | 
			
		||||
        val Array(from, to) = params("commitId").split("\\.\\.\\.")
 | 
			
		||||
        val branch = getWikiBranch(repository.owner, repository.name)
 | 
			
		||||
 | 
			
		||||
          if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, Some(pageName), branch)) {
 | 
			
		||||
            redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
 | 
			
		||||
          } else {
 | 
			
		||||
            flash.update("info", "This patch was not able to be reversed.")
 | 
			
		||||
            redirect(
 | 
			
		||||
              s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}"
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
        if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, Some(pageName), branch)) {
 | 
			
		||||
          redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
 | 
			
		||||
        } else {
 | 
			
		||||
          flash.update("info", "This patch was not able to be reversed.")
 | 
			
		||||
          redirect(
 | 
			
		||||
            s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}"
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      } else Unauthorized()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  get("/:owner/:repository/wiki/_revert/:commitId")(readableUsersOnly { repository =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (isEditable(repository)) {
 | 
			
		||||
          val Array(from, to) = params("commitId").split("\\.\\.\\.")
 | 
			
		||||
          val branch = getWikiBranch(repository.owner, repository.name)
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (isEditable(repository)) {
 | 
			
		||||
        val Array(from, to) = params("commitId").split("\\.\\.\\.")
 | 
			
		||||
        val branch = getWikiBranch(repository.owner, repository.name)
 | 
			
		||||
 | 
			
		||||
          if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, None, branch)) {
 | 
			
		||||
            redirect(s"/${repository.owner}/${repository.name}/wiki")
 | 
			
		||||
          } else {
 | 
			
		||||
            flash.update("info", "This patch was not able to be reversed.")
 | 
			
		||||
            redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
 | 
			
		||||
          }
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
        if (revertWikiPage(repository.owner, repository.name, from, to, loginAccount, None, branch)) {
 | 
			
		||||
          redirect(s"/${repository.owner}/${repository.name}/wiki")
 | 
			
		||||
        } else {
 | 
			
		||||
          flash.update("info", "This patch was not able to be reversed.")
 | 
			
		||||
          redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
 | 
			
		||||
        }
 | 
			
		||||
      } else Unauthorized()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -192,36 +190,34 @@ trait WikiControllerBase extends ControllerBase {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  post("/:owner/:repository/wiki/_edit", editForm)(readableUsersOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (isEditable(repository)) {
 | 
			
		||||
          saveWikiPage(
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            form.currentPageName,
 | 
			
		||||
            form.pageName,
 | 
			
		||||
            appendNewLine(convertLineSeparator(form.content, "LF"), "LF"),
 | 
			
		||||
            loginAccount,
 | 
			
		||||
            form.message.getOrElse(""),
 | 
			
		||||
            Some(form.id)
 | 
			
		||||
          ).foreach {
 | 
			
		||||
            commitId =>
 | 
			
		||||
              updateLastActivityDate(repository.owner, repository.name)
 | 
			
		||||
              val wikiEditInfo =
 | 
			
		||||
                EditWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName, commitId)
 | 
			
		||||
              recordActivity(wikiEditInfo)
 | 
			
		||||
              callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
 | 
			
		||||
                getAccountByUserName(repository.owner).map { repositoryUser =>
 | 
			
		||||
                  WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (isEditable(repository)) {
 | 
			
		||||
        saveWikiPage(
 | 
			
		||||
          repository.owner,
 | 
			
		||||
          repository.name,
 | 
			
		||||
          form.currentPageName,
 | 
			
		||||
          form.pageName,
 | 
			
		||||
          appendNewLine(convertLineSeparator(form.content, "LF"), "LF"),
 | 
			
		||||
          loginAccount,
 | 
			
		||||
          form.message.getOrElse(""),
 | 
			
		||||
          Some(form.id)
 | 
			
		||||
        ).foreach { commitId =>
 | 
			
		||||
          updateLastActivityDate(repository.owner, repository.name)
 | 
			
		||||
          val wikiEditInfo =
 | 
			
		||||
            EditWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName, commitId)
 | 
			
		||||
          recordActivity(wikiEditInfo)
 | 
			
		||||
          callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
 | 
			
		||||
            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")
 | 
			
		||||
          }
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
        }
 | 
			
		||||
        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()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -232,64 +228,61 @@ trait WikiControllerBase extends ControllerBase {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  post("/:owner/:repository/wiki/_new", newForm)(readableUsersOnly { (form, repository) =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (isEditable(repository)) {
 | 
			
		||||
          saveWikiPage(
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            form.currentPageName,
 | 
			
		||||
            form.pageName,
 | 
			
		||||
            form.content,
 | 
			
		||||
            loginAccount,
 | 
			
		||||
            form.message.getOrElse(""),
 | 
			
		||||
            None
 | 
			
		||||
          ).foreach {
 | 
			
		||||
            commitId =>
 | 
			
		||||
              updateLastActivityDate(repository.owner, repository.name)
 | 
			
		||||
              val createWikiPageInfo =
 | 
			
		||||
                CreateWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName)
 | 
			
		||||
              recordActivity(createWikiPageInfo)
 | 
			
		||||
              callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
 | 
			
		||||
                getAccountByUserName(repository.owner).map { repositoryUser =>
 | 
			
		||||
                  WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (isEditable(repository)) {
 | 
			
		||||
        saveWikiPage(
 | 
			
		||||
          repository.owner,
 | 
			
		||||
          repository.name,
 | 
			
		||||
          form.currentPageName,
 | 
			
		||||
          form.pageName,
 | 
			
		||||
          form.content,
 | 
			
		||||
          loginAccount,
 | 
			
		||||
          form.message.getOrElse(""),
 | 
			
		||||
          None
 | 
			
		||||
        ).foreach { commitId =>
 | 
			
		||||
          updateLastActivityDate(repository.owner, repository.name)
 | 
			
		||||
          val createWikiPageInfo =
 | 
			
		||||
            CreateWikiPageInfo(repository.owner, repository.name, loginAccount.userName, form.pageName)
 | 
			
		||||
          recordActivity(createWikiPageInfo)
 | 
			
		||||
          callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
 | 
			
		||||
            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")
 | 
			
		||||
          }
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
        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 =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        if (isEditable(repository)) {
 | 
			
		||||
          val pageName = StringUtil.urlDecode(params("page"))
 | 
			
		||||
          deleteWikiPage(
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            pageName,
 | 
			
		||||
            loginAccount.fullName,
 | 
			
		||||
            loginAccount.mailAddress,
 | 
			
		||||
            s"Destroyed ${pageName}"
 | 
			
		||||
          )
 | 
			
		||||
          val deleteWikiInfo = DeleteWikiInfo(
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            loginAccount.userName,
 | 
			
		||||
            pageName
 | 
			
		||||
          )
 | 
			
		||||
          recordActivity(deleteWikiInfo)
 | 
			
		||||
          updateLastActivityDate(repository.owner, repository.name)
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      if (isEditable(repository)) {
 | 
			
		||||
        val pageName = StringUtil.urlDecode(params("page"))
 | 
			
		||||
        deleteWikiPage(
 | 
			
		||||
          repository.owner,
 | 
			
		||||
          repository.name,
 | 
			
		||||
          pageName,
 | 
			
		||||
          loginAccount.fullName,
 | 
			
		||||
          loginAccount.mailAddress,
 | 
			
		||||
          s"Destroyed ${pageName}"
 | 
			
		||||
        )
 | 
			
		||||
        val deleteWikiInfo = DeleteWikiInfo(
 | 
			
		||||
          repository.owner,
 | 
			
		||||
          repository.name,
 | 
			
		||||
          loginAccount.userName,
 | 
			
		||||
          pageName
 | 
			
		||||
        )
 | 
			
		||||
        recordActivity(deleteWikiInfo)
 | 
			
		||||
        updateLastActivityDate(repository.owner, repository.name)
 | 
			
		||||
 | 
			
		||||
          redirect(s"/${repository.owner}/${repository.name}/wiki")
 | 
			
		||||
        } else Unauthorized()
 | 
			
		||||
        redirect(s"/${repository.owner}/${repository.name}/wiki")
 | 
			
		||||
      } else Unauthorized()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -58,27 +58,25 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
 | 
			
		||||
   * https://docs.github.com/en/free-pro-team@latest/rest/reference/git#create-a-reference
 | 
			
		||||
   */
 | 
			
		||||
  post("/api/v3/repos/:owner/:repository/git/refs")(writableUsersOnly { repository =>
 | 
			
		||||
    extractFromJsonBody[CreateARef].map {
 | 
			
		||||
      data =>
 | 
			
		||||
        Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
 | 
			
		||||
          git =>
 | 
			
		||||
            val ref = git.getRepository.findRef(data.ref)
 | 
			
		||||
            if (ref == null) {
 | 
			
		||||
              val update = git.getRepository.updateRef(data.ref)
 | 
			
		||||
              update.setNewObjectId(ObjectId.fromString(data.sha))
 | 
			
		||||
              val result = update.update()
 | 
			
		||||
              result match {
 | 
			
		||||
                case Result.NEW =>
 | 
			
		||||
                  JsonFormat(
 | 
			
		||||
                    ApiRef
 | 
			
		||||
                      .fromRef(RepositoryName(repository.owner, repository.name), git.getRepository.findRef(data.ref))
 | 
			
		||||
                  )
 | 
			
		||||
                case _ => UnprocessableEntity(result.name())
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
              UnprocessableEntity("Ref already exists.")
 | 
			
		||||
            }
 | 
			
		||||
    extractFromJsonBody[CreateARef].map { data =>
 | 
			
		||||
      Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
        val ref = git.getRepository.findRef(data.ref)
 | 
			
		||||
        if (ref == null) {
 | 
			
		||||
          val update = git.getRepository.updateRef(data.ref)
 | 
			
		||||
          update.setNewObjectId(ObjectId.fromString(data.sha))
 | 
			
		||||
          val result = update.update()
 | 
			
		||||
          result match {
 | 
			
		||||
            case Result.NEW =>
 | 
			
		||||
              JsonFormat(
 | 
			
		||||
                ApiRef
 | 
			
		||||
                  .fromRef(RepositoryName(repository.owner, repository.name), git.getRepository.findRef(data.ref))
 | 
			
		||||
              )
 | 
			
		||||
            case _ => UnprocessableEntity(result.name())
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          UnprocessableEntity("Ref already exists.")
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } getOrElse BadRequest()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -88,24 +86,23 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
 | 
			
		||||
   */
 | 
			
		||||
  patch("/api/v3/repos/:owner/:repository/git/refs/*")(writableUsersOnly { repository =>
 | 
			
		||||
    val refName = multiParams("splat").mkString("/")
 | 
			
		||||
    extractFromJsonBody[UpdateARef].map {
 | 
			
		||||
      data =>
 | 
			
		||||
        Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
          val ref = git.getRepository.findRef(refName)
 | 
			
		||||
          if (ref == null) {
 | 
			
		||||
            UnprocessableEntity("Ref does not exist.")
 | 
			
		||||
          } else {
 | 
			
		||||
            val update = git.getRepository.updateRef(ref.getName)
 | 
			
		||||
            update.setNewObjectId(ObjectId.fromString(data.sha))
 | 
			
		||||
            update.setForceUpdate(data.force)
 | 
			
		||||
            val result = update.update()
 | 
			
		||||
            result match {
 | 
			
		||||
              case Result.FORCED | Result.FAST_FORWARD | Result.NO_CHANGE =>
 | 
			
		||||
                JsonFormat(ApiRef.fromRef(RepositoryName(repository), git.getRepository.findRef(refName)))
 | 
			
		||||
              case _ => UnprocessableEntity(result.name())
 | 
			
		||||
            }
 | 
			
		||||
    extractFromJsonBody[UpdateARef].map { data =>
 | 
			
		||||
      Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
        val ref = git.getRepository.findRef(refName)
 | 
			
		||||
        if (ref == null) {
 | 
			
		||||
          UnprocessableEntity("Ref does not exist.")
 | 
			
		||||
        } else {
 | 
			
		||||
          val update = git.getRepository.updateRef(ref.getName)
 | 
			
		||||
          update.setNewObjectId(ObjectId.fromString(data.sha))
 | 
			
		||||
          update.setForceUpdate(data.force)
 | 
			
		||||
          val result = update.update()
 | 
			
		||||
          result match {
 | 
			
		||||
            case Result.FORCED | Result.FAST_FORWARD | Result.NO_CHANGE =>
 | 
			
		||||
              JsonFormat(ApiRef.fromRef(RepositoryName(repository), git.getRepository.findRef(refName)))
 | 
			
		||||
            case _ => UnprocessableEntity(result.name())
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } getOrElse BadRequest()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,8 @@ trait ApiIssueCommentControllerBase extends ControllerBase {
 | 
			
		||||
      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()
 | 
			
		||||
  })
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ trait ApiIssueControllerBase extends ControllerBase {
 | 
			
		||||
    val page = IssueSearchCondition.page(request)
 | 
			
		||||
    // TODO: more api spec condition
 | 
			
		||||
    val condition = IssueSearchCondition(request)
 | 
			
		||||
    //val baseOwner = getAccountByUserName(repository.owner).get
 | 
			
		||||
    // val baseOwner = getAccountByUserName(repository.owner).get
 | 
			
		||||
 | 
			
		||||
    val issues: List[(Issue, Account, List[Account])] =
 | 
			
		||||
      searchIssueByApi(
 | 
			
		||||
@@ -39,17 +39,16 @@ trait ApiIssueControllerBase extends ControllerBase {
 | 
			
		||||
        repos = repository.owner -> repository.name
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
    JsonFormat(issues.map {
 | 
			
		||||
      case (issue, issueUser, assigneeUsers) =>
 | 
			
		||||
        ApiIssue(
 | 
			
		||||
          issue = issue,
 | 
			
		||||
          repositoryName = RepositoryName(repository),
 | 
			
		||||
          user = ApiUser(issueUser),
 | 
			
		||||
          assignees = assigneeUsers.map(ApiUser(_)),
 | 
			
		||||
          labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
            .map(ApiLabel(_, RepositoryName(repository))),
 | 
			
		||||
          issue.milestoneId.flatMap { getApiMilestone(repository, _) }
 | 
			
		||||
        )
 | 
			
		||||
    JsonFormat(issues.map { case (issue, issueUser, assigneeUsers) =>
 | 
			
		||||
      ApiIssue(
 | 
			
		||||
        issue = issue,
 | 
			
		||||
        repositoryName = RepositoryName(repository),
 | 
			
		||||
        user = ApiUser(issueUser),
 | 
			
		||||
        assignees = assigneeUsers.map(ApiUser(_)),
 | 
			
		||||
        labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
          .map(ApiLabel(_, RepositoryName(repository))),
 | 
			
		||||
        issue.milestoneId.flatMap { getApiMilestone(repository, _) }
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -126,7 +125,7 @@ trait ApiIssueControllerBase extends ControllerBase {
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
 * vii. Unlock an issue
 | 
			
		||||
 * https://developer.github.com/v3/issues/#unlock-an-issue
 | 
			
		||||
 */
 | 
			
		||||
   * vii. Unlock an issue
 | 
			
		||||
   * https://developer.github.com/v3/issues/#unlock-an-issue
 | 
			
		||||
   */
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -69,25 +69,24 @@ trait ApiIssueLabelControllerBase extends ControllerBase {
 | 
			
		||||
      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)
 | 
			
		||||
                )
 | 
			
		||||
        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")
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
          } 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()
 | 
			
		||||
@@ -189,7 +188,7 @@ trait ApiIssueLabelControllerBase extends ControllerBase {
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
 * xi Get labels for every issue in a milestone
 | 
			
		||||
 * https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone
 | 
			
		||||
 */
 | 
			
		||||
   * xi Get labels for every issue in a milestone
 | 
			
		||||
   * https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone
 | 
			
		||||
   */
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,10 @@ trait ApiIssueMilestoneControllerBase extends ControllerBase {
 | 
			
		||||
  get("/api/v3/repos/:owner/:repository/milestones")(referrersOnly { repository =>
 | 
			
		||||
    val state = params.getOrElse("state", "all")
 | 
			
		||||
    // TODO "sort", "direction" params should be implemented.
 | 
			
		||||
    val apiMilestones = (for (milestoneWithIssue <- getMilestonesWithIssueCount(repository.owner, repository.name)
 | 
			
		||||
                                .sortBy(p => p._1.milestoneId))
 | 
			
		||||
    val apiMilestones = (for (
 | 
			
		||||
      milestoneWithIssue <- getMilestonesWithIssueCount(repository.owner, repository.name)
 | 
			
		||||
        .sortBy(p => p._1.milestoneId)
 | 
			
		||||
    )
 | 
			
		||||
      yield {
 | 
			
		||||
        ApiMilestone(
 | 
			
		||||
          repository.repository,
 | 
			
		||||
 
 | 
			
		||||
@@ -71,6 +71,6 @@ trait ApiOrganizationControllerBase extends ControllerBase {
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
 * should implement delete an organization API?
 | 
			
		||||
 */
 | 
			
		||||
   * should implement delete an organization API?
 | 
			
		||||
   */
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -48,19 +48,18 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
        repos = repository.owner -> repository.name
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
    JsonFormat(issues.map {
 | 
			
		||||
      case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner, assignees) =>
 | 
			
		||||
        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))),
 | 
			
		||||
          assignees = assignees.map(ApiUser.apply),
 | 
			
		||||
          mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
        )
 | 
			
		||||
    JsonFormat(issues.map { case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner, assignees) =>
 | 
			
		||||
      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))),
 | 
			
		||||
        assignees = assignees.map(ApiUser.apply),
 | 
			
		||||
        mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -89,38 +88,37 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
        case Left(createPullReq) =>
 | 
			
		||||
          val (reqOwner, reqBranch) = parseCompareIdentifier(createPullReq.head, repository.owner)
 | 
			
		||||
          getRepository(reqOwner, repository.name)
 | 
			
		||||
            .flatMap {
 | 
			
		||||
              forkedRepository =>
 | 
			
		||||
                getPullRequestCommitFromTo(repository, forkedRepository, createPullReq.base, reqBranch) match {
 | 
			
		||||
                  case (Some(commitIdFrom), Some(commitIdTo)) =>
 | 
			
		||||
                    val issueId = insertIssue(
 | 
			
		||||
                      owner = repository.owner,
 | 
			
		||||
                      repository = repository.name,
 | 
			
		||||
                      loginUser = context.loginAccount.get.userName,
 | 
			
		||||
                      title = createPullReq.title,
 | 
			
		||||
                      content = createPullReq.body,
 | 
			
		||||
                      milestoneId = None,
 | 
			
		||||
                      priorityId = None,
 | 
			
		||||
                      isPullRequest = true
 | 
			
		||||
                    )
 | 
			
		||||
            .flatMap { forkedRepository =>
 | 
			
		||||
              getPullRequestCommitFromTo(repository, forkedRepository, createPullReq.base, reqBranch) match {
 | 
			
		||||
                case (Some(commitIdFrom), Some(commitIdTo)) =>
 | 
			
		||||
                  val issueId = insertIssue(
 | 
			
		||||
                    owner = repository.owner,
 | 
			
		||||
                    repository = repository.name,
 | 
			
		||||
                    loginUser = context.loginAccount.get.userName,
 | 
			
		||||
                    title = createPullReq.title,
 | 
			
		||||
                    content = createPullReq.body,
 | 
			
		||||
                    milestoneId = None,
 | 
			
		||||
                    priorityId = None,
 | 
			
		||||
                    isPullRequest = true
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
                    createPullRequest(
 | 
			
		||||
                      originRepository = repository,
 | 
			
		||||
                      issueId = issueId,
 | 
			
		||||
                      originBranch = createPullReq.base,
 | 
			
		||||
                      requestUserName = reqOwner,
 | 
			
		||||
                      requestRepositoryName = repository.name,
 | 
			
		||||
                      requestBranch = reqBranch,
 | 
			
		||||
                      commitIdFrom = commitIdFrom.getName,
 | 
			
		||||
                      commitIdTo = commitIdTo.getName,
 | 
			
		||||
                      isDraft = createPullReq.draft.getOrElse(false),
 | 
			
		||||
                      loginAccount = context.loginAccount.get,
 | 
			
		||||
                      settings = context.settings
 | 
			
		||||
                    )
 | 
			
		||||
                    getApiPullRequest(repository, issueId).map(JsonFormat(_))
 | 
			
		||||
                  case _ =>
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
                  createPullRequest(
 | 
			
		||||
                    originRepository = repository,
 | 
			
		||||
                    issueId = issueId,
 | 
			
		||||
                    originBranch = createPullReq.base,
 | 
			
		||||
                    requestUserName = reqOwner,
 | 
			
		||||
                    requestRepositoryName = repository.name,
 | 
			
		||||
                    requestBranch = reqBranch,
 | 
			
		||||
                    commitIdFrom = commitIdFrom.getName,
 | 
			
		||||
                    commitIdTo = commitIdTo.getName,
 | 
			
		||||
                    isDraft = createPullReq.draft.getOrElse(false),
 | 
			
		||||
                    loginAccount = context.loginAccount.get,
 | 
			
		||||
                    settings = context.settings
 | 
			
		||||
                  )
 | 
			
		||||
                  getApiPullRequest(repository, issueId).map(JsonFormat(_))
 | 
			
		||||
                case _ =>
 | 
			
		||||
                  None
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            .getOrElse {
 | 
			
		||||
              NotFound()
 | 
			
		||||
@@ -128,28 +126,27 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
        case Right(createPullReqAlt) =>
 | 
			
		||||
          val (reqOwner, reqBranch) = parseCompareIdentifier(createPullReqAlt.head, repository.owner)
 | 
			
		||||
          getRepository(reqOwner, repository.name)
 | 
			
		||||
            .flatMap {
 | 
			
		||||
              forkedRepository =>
 | 
			
		||||
                getPullRequestCommitFromTo(repository, forkedRepository, createPullReqAlt.base, reqBranch) match {
 | 
			
		||||
                  case (Some(commitIdFrom), Some(commitIdTo)) =>
 | 
			
		||||
                    changeIssueToPullRequest(repository.owner, repository.name, createPullReqAlt.issue)
 | 
			
		||||
                    createPullRequest(
 | 
			
		||||
                      originRepository = repository,
 | 
			
		||||
                      issueId = createPullReqAlt.issue,
 | 
			
		||||
                      originBranch = createPullReqAlt.base,
 | 
			
		||||
                      requestUserName = reqOwner,
 | 
			
		||||
                      requestRepositoryName = repository.name,
 | 
			
		||||
                      requestBranch = reqBranch,
 | 
			
		||||
                      commitIdFrom = commitIdFrom.getName,
 | 
			
		||||
                      commitIdTo = commitIdTo.getName,
 | 
			
		||||
                      isDraft = false,
 | 
			
		||||
                      loginAccount = context.loginAccount.get,
 | 
			
		||||
                      settings = context.settings
 | 
			
		||||
                    )
 | 
			
		||||
                    getApiPullRequest(repository, createPullReqAlt.issue).map(JsonFormat(_))
 | 
			
		||||
                  case _ =>
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
            .flatMap { forkedRepository =>
 | 
			
		||||
              getPullRequestCommitFromTo(repository, forkedRepository, createPullReqAlt.base, reqBranch) match {
 | 
			
		||||
                case (Some(commitIdFrom), Some(commitIdTo)) =>
 | 
			
		||||
                  changeIssueToPullRequest(repository.owner, repository.name, createPullReqAlt.issue)
 | 
			
		||||
                  createPullRequest(
 | 
			
		||||
                    originRepository = repository,
 | 
			
		||||
                    issueId = createPullReqAlt.issue,
 | 
			
		||||
                    originBranch = createPullReqAlt.base,
 | 
			
		||||
                    requestUserName = reqOwner,
 | 
			
		||||
                    requestRepositoryName = repository.name,
 | 
			
		||||
                    requestBranch = reqBranch,
 | 
			
		||||
                    commitIdFrom = commitIdFrom.getName,
 | 
			
		||||
                    commitIdTo = commitIdTo.getName,
 | 
			
		||||
                    isDraft = false,
 | 
			
		||||
                    loginAccount = context.loginAccount.get,
 | 
			
		||||
                    settings = context.settings
 | 
			
		||||
                  )
 | 
			
		||||
                  getApiPullRequest(repository, createPullReqAlt.issue).map(JsonFormat(_))
 | 
			
		||||
                case _ =>
 | 
			
		||||
                  None
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            .getOrElse {
 | 
			
		||||
              NotFound()
 | 
			
		||||
@@ -190,26 +187,24 @@ trait ApiPullRequestControllerBase 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.resource(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.resource(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()
 | 
			
		||||
  })
 | 
			
		||||
  /*
 | 
			
		||||
@@ -240,8 +235,8 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
   */
 | 
			
		||||
  put("/api/v3/repos/:owner/:repository/pulls/:id/merge")(referrersOnly { repository =>
 | 
			
		||||
    (for {
 | 
			
		||||
      //TODO: crash when body is empty
 | 
			
		||||
      //TODO: Implement sha parameter
 | 
			
		||||
      // TODO: crash when body is empty
 | 
			
		||||
      // TODO: Implement sha parameter
 | 
			
		||||
      data <- extractFromJsonBody[MergeAPullRequest]
 | 
			
		||||
      issueId <- params("id").toIntOpt
 | 
			
		||||
      (issue, pullReq) <- getPullRequest(repository.owner, repository.name, issueId)
 | 
			
		||||
@@ -273,7 +268,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
            repository,
 | 
			
		||||
            issueId,
 | 
			
		||||
            context.loginAccount.get,
 | 
			
		||||
            data.commit_message.getOrElse(""), //TODO: Implement commit_title
 | 
			
		||||
            data.commit_message.getOrElse(""), // TODO: Implement commit_title
 | 
			
		||||
            strategy,
 | 
			
		||||
            pullReq.isDraft,
 | 
			
		||||
            context.settings
 | 
			
		||||
 
 | 
			
		||||
@@ -119,40 +119,39 @@ trait ApiReleaseControllerBase extends ControllerBase {
 | 
			
		||||
   * ix. Upload a release asset
 | 
			
		||||
   * https://developer.github.com/v3/repos/releases/#upload-a-release-asset
 | 
			
		||||
   */
 | 
			
		||||
  post("/api/v3/repos/:owner/:repository/releases/:tag/assets")(writableUsersOnly {
 | 
			
		||||
    repository =>
 | 
			
		||||
      val name = params("name")
 | 
			
		||||
      val tag = params("tag")
 | 
			
		||||
      getRelease(repository.owner, repository.name, tag)
 | 
			
		||||
        .map { release =>
 | 
			
		||||
          val fileId = FileUtil.generateFileId
 | 
			
		||||
          val buf = new Array[Byte](request.inputStream.available())
 | 
			
		||||
          request.inputStream.read(buf)
 | 
			
		||||
          FileUtils.writeByteArrayToFile(
 | 
			
		||||
            new File(
 | 
			
		||||
              getReleaseFilesDir(repository.owner, repository.name),
 | 
			
		||||
              FileUtil.checkFilename(tag + "/" + fileId)
 | 
			
		||||
            ),
 | 
			
		||||
            buf
 | 
			
		||||
          )
 | 
			
		||||
          createReleaseAsset(
 | 
			
		||||
            repository.owner,
 | 
			
		||||
            repository.name,
 | 
			
		||||
            tag,
 | 
			
		||||
            fileId,
 | 
			
		||||
            name,
 | 
			
		||||
            request.contentLength.getOrElse(0),
 | 
			
		||||
            context.loginAccount.get
 | 
			
		||||
          )
 | 
			
		||||
          getReleaseAsset(repository.owner, repository.name, tag, fileId)
 | 
			
		||||
            .map { asset =>
 | 
			
		||||
              JsonFormat(ApiReleaseAsset(asset, RepositoryName(repository)))
 | 
			
		||||
            }
 | 
			
		||||
            .getOrElse {
 | 
			
		||||
              ApiError("Unknown error")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .getOrElse(NotFound())
 | 
			
		||||
  post("/api/v3/repos/:owner/:repository/releases/:tag/assets")(writableUsersOnly { repository =>
 | 
			
		||||
    val name = params("name")
 | 
			
		||||
    val tag = params("tag")
 | 
			
		||||
    getRelease(repository.owner, repository.name, tag)
 | 
			
		||||
      .map { release =>
 | 
			
		||||
        val fileId = FileUtil.generateFileId
 | 
			
		||||
        val buf = new Array[Byte](request.inputStream.available())
 | 
			
		||||
        request.inputStream.read(buf)
 | 
			
		||||
        FileUtils.writeByteArrayToFile(
 | 
			
		||||
          new File(
 | 
			
		||||
            getReleaseFilesDir(repository.owner, repository.name),
 | 
			
		||||
            FileUtil.checkFilename(tag + "/" + fileId)
 | 
			
		||||
          ),
 | 
			
		||||
          buf
 | 
			
		||||
        )
 | 
			
		||||
        createReleaseAsset(
 | 
			
		||||
          repository.owner,
 | 
			
		||||
          repository.name,
 | 
			
		||||
          tag,
 | 
			
		||||
          fileId,
 | 
			
		||||
          name,
 | 
			
		||||
          request.contentLength.getOrElse(0),
 | 
			
		||||
          context.loginAccount.get
 | 
			
		||||
        )
 | 
			
		||||
        getReleaseAsset(repository.owner, repository.name, tag, fileId)
 | 
			
		||||
          .map { asset =>
 | 
			
		||||
            JsonFormat(ApiReleaseAsset(asset, RepositoryName(repository)))
 | 
			
		||||
          }
 | 
			
		||||
          .getOrElse {
 | 
			
		||||
            ApiError("Unknown error")
 | 
			
		||||
          }
 | 
			
		||||
      }
 | 
			
		||||
      .getOrElse(NotFound())
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@@ -176,7 +175,7 @@ trait ApiReleaseControllerBase extends ControllerBase {
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
 * xii. Delete a release asset
 | 
			
		||||
 * https://developer.github.com/v3/repos/releases/#edit-a-release-asset
 | 
			
		||||
 */
 | 
			
		||||
   * xii. Delete a release asset
 | 
			
		||||
   * https://developer.github.com/v3/repos/releases/#edit-a-release-asset
 | 
			
		||||
   */
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -47,21 +47,20 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
 | 
			
		||||
   * https://docs.github.com/en/rest/reference/repos#get-a-branch
 | 
			
		||||
   */
 | 
			
		||||
  get("/api/v3/repos/:owner/:repository/branches/*")(referrersOnly { repository =>
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
 | 
			
		||||
      git =>
 | 
			
		||||
        (for {
 | 
			
		||||
          branch <- params.get("splat") if repository.branchList.contains(branch)
 | 
			
		||||
          br <- getBranches(
 | 
			
		||||
            git,
 | 
			
		||||
            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))
 | 
			
		||||
          )
 | 
			
		||||
        }) getOrElse NotFound()
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
      (for {
 | 
			
		||||
        branch <- params.get("splat") if repository.branchList.contains(branch)
 | 
			
		||||
        br <- getBranches(
 | 
			
		||||
          git,
 | 
			
		||||
          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))
 | 
			
		||||
        )
 | 
			
		||||
      }) getOrElse NotFound()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -275,30 +274,29 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
 | 
			
		||||
   */
 | 
			
		||||
  patch("/api/v3/repos/:owner/:repository/branches/*")(ownerOnly { repository =>
 | 
			
		||||
    import gitbucket.core.api._
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
 | 
			
		||||
      git =>
 | 
			
		||||
        (for {
 | 
			
		||||
          branch <- params.get("splat") if repository.branchList.contains(branch)
 | 
			
		||||
          protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
 | 
			
		||||
          br <- getBranches(
 | 
			
		||||
            git,
 | 
			
		||||
            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
 | 
			
		||||
            )
 | 
			
		||||
          } else {
 | 
			
		||||
            disableBranchProtection(repository.owner, repository.name, branch)
 | 
			
		||||
          }
 | 
			
		||||
          JsonFormat(ApiBranch(branch, ApiBranchCommit(br.commitId), protection)(RepositoryName(repository)))
 | 
			
		||||
        }) getOrElse NotFound()
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
      (for {
 | 
			
		||||
        branch <- params.get("splat") if repository.branchList.contains(branch)
 | 
			
		||||
        protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
 | 
			
		||||
        br <- getBranches(
 | 
			
		||||
          git,
 | 
			
		||||
          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
 | 
			
		||||
          )
 | 
			
		||||
        } else {
 | 
			
		||||
          disableBranchProtection(repository.owner, repository.name, branch)
 | 
			
		||||
        }
 | 
			
		||||
        JsonFormat(ApiBranch(branch, ApiBranchCommit(br.commitId), protection)(RepositoryName(repository)))
 | 
			
		||||
      }) getOrElse NotFound()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -43,56 +43,53 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
 | 
			
		||||
    val path = params.get("path").filter(_.nonEmpty)
 | 
			
		||||
    val since = params.get("since").filter(_.nonEmpty)
 | 
			
		||||
    val until = params.get("until").filter(_.nonEmpty)
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(owner, name))) {
 | 
			
		||||
      git =>
 | 
			
		||||
        val repo = git.getRepository
 | 
			
		||||
        Using.resource(new RevWalk(repo)) {
 | 
			
		||||
          revWalk =>
 | 
			
		||||
            val objectId = repo.resolve(sha)
 | 
			
		||||
            revWalk.markStart(revWalk.parseCommit(objectId))
 | 
			
		||||
            if (path.nonEmpty) {
 | 
			
		||||
              revWalk.setTreeFilter(
 | 
			
		||||
                AndTreeFilter.create(PathFilterGroup.createFromStrings(path.get), TreeFilter.ANY_DIFF)
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
            val revfilters = new ListBuffer[(RevFilter)]()
 | 
			
		||||
            if (author.nonEmpty) {
 | 
			
		||||
              revfilters += AuthorRevFilter.create(author.get)
 | 
			
		||||
            }
 | 
			
		||||
            if (since.nonEmpty) {
 | 
			
		||||
              revfilters += CommitTimeRevFilter.after(
 | 
			
		||||
                Date.from(LocalDateTime.parse(since.get, ISO_DATE_TIME).toInstant(ZoneOffset.UTC))
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
            if (until.nonEmpty) {
 | 
			
		||||
              revfilters += CommitTimeRevFilter.before(
 | 
			
		||||
                Date.from(LocalDateTime.parse(until.get, ISO_DATE_TIME).toInstant(ZoneOffset.UTC))
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
            if (page > 1) {
 | 
			
		||||
              revfilters += SkipRevFilter.create(page * per_page - 2)
 | 
			
		||||
            }
 | 
			
		||||
            revfilters += MaxCountRevFilter.create(per_page);
 | 
			
		||||
            revWalk.setRevFilter(
 | 
			
		||||
              if (revfilters.size > 1) {
 | 
			
		||||
                AndRevFilter.create(revfilters.toArray)
 | 
			
		||||
              } else {
 | 
			
		||||
                revfilters(0)
 | 
			
		||||
              }
 | 
			
		||||
            )
 | 
			
		||||
            JsonFormat(revWalk.asScala.map {
 | 
			
		||||
              commit =>
 | 
			
		||||
                val commitInfo = new CommitInfo(commit)
 | 
			
		||||
                ApiCommits(
 | 
			
		||||
                  repositoryName = RepositoryName(repository),
 | 
			
		||||
                  commitInfo = commitInfo,
 | 
			
		||||
                  diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
 | 
			
		||||
                  author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
 | 
			
		||||
                  committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
 | 
			
		||||
                  commentCount = getCommitComment(repository.owner, repository.name, commitInfo.id).size
 | 
			
		||||
                )
 | 
			
		||||
            })
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
 | 
			
		||||
      val repo = git.getRepository
 | 
			
		||||
      Using.resource(new RevWalk(repo)) { revWalk =>
 | 
			
		||||
        val objectId = repo.resolve(sha)
 | 
			
		||||
        revWalk.markStart(revWalk.parseCommit(objectId))
 | 
			
		||||
        if (path.nonEmpty) {
 | 
			
		||||
          revWalk.setTreeFilter(
 | 
			
		||||
            AndTreeFilter.create(PathFilterGroup.createFromStrings(path.get), TreeFilter.ANY_DIFF)
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
        val revfilters = new ListBuffer[(RevFilter)]()
 | 
			
		||||
        if (author.nonEmpty) {
 | 
			
		||||
          revfilters += AuthorRevFilter.create(author.get)
 | 
			
		||||
        }
 | 
			
		||||
        if (since.nonEmpty) {
 | 
			
		||||
          revfilters += CommitTimeRevFilter.after(
 | 
			
		||||
            Date.from(LocalDateTime.parse(since.get, ISO_DATE_TIME).toInstant(ZoneOffset.UTC))
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
        if (until.nonEmpty) {
 | 
			
		||||
          revfilters += CommitTimeRevFilter.before(
 | 
			
		||||
            Date.from(LocalDateTime.parse(until.get, ISO_DATE_TIME).toInstant(ZoneOffset.UTC))
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
        if (page > 1) {
 | 
			
		||||
          revfilters += SkipRevFilter.create(page * per_page - 2)
 | 
			
		||||
        }
 | 
			
		||||
        revfilters += MaxCountRevFilter.create(per_page);
 | 
			
		||||
        revWalk.setRevFilter(
 | 
			
		||||
          if (revfilters.size > 1) {
 | 
			
		||||
            AndRevFilter.create(revfilters.toArray)
 | 
			
		||||
          } else {
 | 
			
		||||
            revfilters(0)
 | 
			
		||||
          }
 | 
			
		||||
        )
 | 
			
		||||
        JsonFormat(revWalk.asScala.map { commit =>
 | 
			
		||||
          val commitInfo = new CommitInfo(commit)
 | 
			
		||||
          ApiCommits(
 | 
			
		||||
            repositoryName = RepositoryName(repository),
 | 
			
		||||
            commitInfo = commitInfo,
 | 
			
		||||
            diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
 | 
			
		||||
            author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
 | 
			
		||||
            committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
 | 
			
		||||
            commentCount = getCommitComment(repository.owner, repository.name, commitInfo.id).size
 | 
			
		||||
          )
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -105,24 +102,23 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
 | 
			
		||||
    val name = repository.name
 | 
			
		||||
    val sha = params("sha")
 | 
			
		||||
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(owner, name))) {
 | 
			
		||||
      git =>
 | 
			
		||||
        val repo = git.getRepository
 | 
			
		||||
        val objectId = repo.resolve(sha)
 | 
			
		||||
        val commitInfo = Using.resource(new RevWalk(repo)) { revWalk =>
 | 
			
		||||
          new CommitInfo(revWalk.parseCommit(objectId))
 | 
			
		||||
        }
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
 | 
			
		||||
      val repo = git.getRepository
 | 
			
		||||
      val objectId = repo.resolve(sha)
 | 
			
		||||
      val commitInfo = Using.resource(new RevWalk(repo)) { revWalk =>
 | 
			
		||||
        new CommitInfo(revWalk.parseCommit(objectId))
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
        JsonFormat(
 | 
			
		||||
          ApiCommits(
 | 
			
		||||
            repositoryName = RepositoryName(repository),
 | 
			
		||||
            commitInfo = commitInfo,
 | 
			
		||||
            diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
 | 
			
		||||
            author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
 | 
			
		||||
            committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
 | 
			
		||||
            commentCount = getCommitComment(repository.owner, repository.name, sha).size
 | 
			
		||||
          )
 | 
			
		||||
      JsonFormat(
 | 
			
		||||
        ApiCommits(
 | 
			
		||||
          repositoryName = RepositoryName(repository),
 | 
			
		||||
          commitInfo = commitInfo,
 | 
			
		||||
          diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
 | 
			
		||||
          author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
 | 
			
		||||
          committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
 | 
			
		||||
          commentCount = getCommitComment(repository.owner, repository.name, sha).size
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,17 +19,16 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
 | 
			
		||||
   * https://docs.github.com/en/rest/reference/repos#get-a-repository-readme
 | 
			
		||||
   */
 | 
			
		||||
  get("/api/v3/repos/:owner/:repository/readme")(referrersOnly { repository =>
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) {
 | 
			
		||||
      git =>
 | 
			
		||||
        val refStr = params.getOrElse("ref", repository.repository.defaultBranch)
 | 
			
		||||
        val files = getFileList(git, refStr, ".", maxFiles = context.settings.repositoryViewer.maxFiles)
 | 
			
		||||
        files // files should be sorted alphabetically.
 | 
			
		||||
          .find { file =>
 | 
			
		||||
            !file.isDirectory && RepositoryService.readmeFiles.contains(file.name.toLowerCase)
 | 
			
		||||
          } match {
 | 
			
		||||
          case Some(x) => getContents(repository = repository, path = x.name, refStr = refStr, ignoreCase = true)
 | 
			
		||||
          case _       => NotFound()
 | 
			
		||||
        }
 | 
			
		||||
    Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
 | 
			
		||||
      val refStr = params.getOrElse("ref", repository.repository.defaultBranch)
 | 
			
		||||
      val files = getFileList(git, refStr, ".", maxFiles = context.settings.repositoryViewer.maxFiles)
 | 
			
		||||
      files // files should be sorted alphabetically.
 | 
			
		||||
        .find { file =>
 | 
			
		||||
          !file.isDirectory && RepositoryService.readmeFiles.contains(file.name.toLowerCase)
 | 
			
		||||
        } match {
 | 
			
		||||
        case Some(x) => getContents(repository = repository, path = x.name, refStr = refStr, ignoreCase = true)
 | 
			
		||||
        case _       => NotFound()
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -134,67 +133,65 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
 | 
			
		||||
   * requested #2112
 | 
			
		||||
   */
 | 
			
		||||
  put("/api/v3/repos/:owner/:repository/contents/*")(writableUsersOnly { repository =>
 | 
			
		||||
    context.withLoginAccount {
 | 
			
		||||
      loginAccount =>
 | 
			
		||||
        JsonFormat(for {
 | 
			
		||||
          data <- extractFromJsonBody[CreateAFile]
 | 
			
		||||
        } yield {
 | 
			
		||||
          val branch = data.branch.getOrElse(repository.repository.defaultBranch)
 | 
			
		||||
          val commit = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
            val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
 | 
			
		||||
            revCommit.name
 | 
			
		||||
          }
 | 
			
		||||
          val paths = multiParams("splat").head.split("/")
 | 
			
		||||
          val path = paths.take(paths.size - 1).toList.mkString("/")
 | 
			
		||||
          Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) {
 | 
			
		||||
            git =>
 | 
			
		||||
              val fileInfo = getFileInfo(git, commit, path, false)
 | 
			
		||||
    context.withLoginAccount { loginAccount =>
 | 
			
		||||
      JsonFormat(for {
 | 
			
		||||
        data <- extractFromJsonBody[CreateAFile]
 | 
			
		||||
      } yield {
 | 
			
		||||
        val branch = data.branch.getOrElse(repository.repository.defaultBranch)
 | 
			
		||||
        val commit = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
          val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
 | 
			
		||||
          revCommit.name
 | 
			
		||||
        }
 | 
			
		||||
        val paths = multiParams("splat").head.split("/")
 | 
			
		||||
        val path = paths.take(paths.size - 1).toList.mkString("/")
 | 
			
		||||
        Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
 | 
			
		||||
          val fileInfo = getFileInfo(git, commit, path, false)
 | 
			
		||||
 | 
			
		||||
              fileInfo match {
 | 
			
		||||
                case Some(f) if !data.sha.contains(f.id.getName) =>
 | 
			
		||||
                  ApiError(
 | 
			
		||||
                    "The blob SHA is not matched.",
 | 
			
		||||
                    Some("https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents")
 | 
			
		||||
          fileInfo match {
 | 
			
		||||
            case Some(f) if !data.sha.contains(f.id.getName) =>
 | 
			
		||||
              ApiError(
 | 
			
		||||
                "The blob SHA is not matched.",
 | 
			
		||||
                Some("https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents")
 | 
			
		||||
              )
 | 
			
		||||
            case _ =>
 | 
			
		||||
              commitFile(
 | 
			
		||||
                repository,
 | 
			
		||||
                branch,
 | 
			
		||||
                path,
 | 
			
		||||
                Some(paths.last),
 | 
			
		||||
                data.sha.map(_ => paths.last),
 | 
			
		||||
                StringUtil.base64Decode(data.content),
 | 
			
		||||
                data.message,
 | 
			
		||||
                commit,
 | 
			
		||||
                loginAccount,
 | 
			
		||||
                data.committer.map(_.name).getOrElse(loginAccount.fullName),
 | 
			
		||||
                data.committer.map(_.email).getOrElse(loginAccount.mailAddress),
 | 
			
		||||
                context.settings
 | 
			
		||||
              ) match {
 | 
			
		||||
                case Left(error) =>
 | 
			
		||||
                  ApiError(s"Failed to commit a file: ${error}", None)
 | 
			
		||||
                case Right((_, None)) =>
 | 
			
		||||
                  ApiError("Failed to commit a file.", None)
 | 
			
		||||
                case Right((commitId, Some(blobId))) =>
 | 
			
		||||
                  Map(
 | 
			
		||||
                    "content" -> ApiContents(
 | 
			
		||||
                      "file",
 | 
			
		||||
                      paths.last,
 | 
			
		||||
                      path,
 | 
			
		||||
                      blobId.name,
 | 
			
		||||
                      Some(data.content),
 | 
			
		||||
                      Some("base64")
 | 
			
		||||
                    )(RepositoryName(repository)),
 | 
			
		||||
                    "commit" -> ApiCommit(
 | 
			
		||||
                      git,
 | 
			
		||||
                      RepositoryName(repository),
 | 
			
		||||
                      new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
 | 
			
		||||
                    )
 | 
			
		||||
                  )
 | 
			
		||||
                case _ =>
 | 
			
		||||
                  commitFile(
 | 
			
		||||
                    repository,
 | 
			
		||||
                    branch,
 | 
			
		||||
                    path,
 | 
			
		||||
                    Some(paths.last),
 | 
			
		||||
                    data.sha.map(_ => paths.last),
 | 
			
		||||
                    StringUtil.base64Decode(data.content),
 | 
			
		||||
                    data.message,
 | 
			
		||||
                    commit,
 | 
			
		||||
                    loginAccount,
 | 
			
		||||
                    data.committer.map(_.name).getOrElse(loginAccount.fullName),
 | 
			
		||||
                    data.committer.map(_.email).getOrElse(loginAccount.mailAddress),
 | 
			
		||||
                    context.settings
 | 
			
		||||
                  ) match {
 | 
			
		||||
                    case Left(error) =>
 | 
			
		||||
                      ApiError(s"Failed to commit a file: ${error}", None)
 | 
			
		||||
                    case Right((_, None)) =>
 | 
			
		||||
                      ApiError("Failed to commit a file.", None)
 | 
			
		||||
                    case Right((commitId, Some(blobId))) =>
 | 
			
		||||
                      Map(
 | 
			
		||||
                        "content" -> ApiContents(
 | 
			
		||||
                          "file",
 | 
			
		||||
                          paths.last,
 | 
			
		||||
                          path,
 | 
			
		||||
                          blobId.name,
 | 
			
		||||
                          Some(data.content),
 | 
			
		||||
                          Some("base64")
 | 
			
		||||
                        )(RepositoryName(repository)),
 | 
			
		||||
                        "commit" -> ApiCommit(
 | 
			
		||||
                          git,
 | 
			
		||||
                          RepositoryName(repository),
 | 
			
		||||
                          new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
 | 
			
		||||
                        )
 | 
			
		||||
                      )
 | 
			
		||||
                  }
 | 
			
		||||
              }
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@@ -205,9 +202,9 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
 * vi. Download a repository archive (tar/zip)
 | 
			
		||||
 * https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-tar
 | 
			
		||||
 * https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-zip
 | 
			
		||||
 */
 | 
			
		||||
   * vi. Download a repository archive (tar/zip)
 | 
			
		||||
   * https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-tar
 | 
			
		||||
   * https://docs.github.com/en/rest/reference/repos#download-a-repository-archive-zip
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -47,9 +47,8 @@ trait ApiRepositoryStatusControllerBase extends ControllerBase {
 | 
			
		||||
      ref <- params.get("ref")
 | 
			
		||||
      sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
 | 
			
		||||
    } yield {
 | 
			
		||||
      JsonFormat(getCommitStatusesWithCreator(repository.owner, repository.name, sha).map {
 | 
			
		||||
        case (status, creator) =>
 | 
			
		||||
          ApiCommitStatus(status, ApiUser(creator))
 | 
			
		||||
      JsonFormat(getCommitStatusesWithCreator(repository.owner, repository.name, sha).map { case (status, creator) =>
 | 
			
		||||
        ApiCommitStatus(status, ApiUser(creator))
 | 
			
		||||
      })
 | 
			
		||||
    }) getOrElse NotFound()
 | 
			
		||||
  })
 | 
			
		||||
 
 | 
			
		||||
@@ -113,8 +113,8 @@ trait ApiRepositoryWebhookControllerBase extends ControllerBase {
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
 * vi. Test the push repository webhook
 | 
			
		||||
 * https://docs.github.com/en/rest/reference/repos#test-the-push-repository-webhook
 | 
			
		||||
 */
 | 
			
		||||
   * vi. Test the push repository webhook
 | 
			
		||||
   * https://docs.github.com/en/rest/reference/repos#test-the-push-repository-webhook
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -40,8 +40,8 @@ case class CustomField(
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
trait CustomFieldBehavior {
 | 
			
		||||
  def createHtml(repository: RepositoryInfo, fieldId: Int, fieldName: String, constraints: Option[String])(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  def createHtml(repository: RepositoryInfo, fieldId: Int, fieldName: String, constraints: Option[String])(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): String
 | 
			
		||||
  def fieldHtml(
 | 
			
		||||
    repository: RepositoryInfo,
 | 
			
		||||
@@ -51,8 +51,8 @@ trait CustomFieldBehavior {
 | 
			
		||||
    constraints: Option[String],
 | 
			
		||||
    value: String,
 | 
			
		||||
    editable: Boolean
 | 
			
		||||
  )(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  )(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): String
 | 
			
		||||
  def validate(name: String, constraints: Option[String], value: String, messages: Messages): Option[String]
 | 
			
		||||
}
 | 
			
		||||
@@ -151,11 +151,11 @@ object CustomFieldBehavior {
 | 
			
		||||
        sb.append("""<div>""")
 | 
			
		||||
        if (value == "") {
 | 
			
		||||
          sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">No ${StringUtil.escapeHtml(
 | 
			
		||||
            fieldName
 | 
			
		||||
          )}</span></span>""")
 | 
			
		||||
              fieldName
 | 
			
		||||
            )}</span></span>""")
 | 
			
		||||
        } else {
 | 
			
		||||
          sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">${StringUtil
 | 
			
		||||
            .escapeHtml(value)}</span></span>""")
 | 
			
		||||
              .escapeHtml(value)}</span></span>""")
 | 
			
		||||
        }
 | 
			
		||||
        sb.toString()
 | 
			
		||||
      } else {
 | 
			
		||||
@@ -179,21 +179,19 @@ object CustomFieldBehavior {
 | 
			
		||||
            val options = new StringBuilder()
 | 
			
		||||
            options.append(
 | 
			
		||||
              s"""<li><a href="javascript:void(0);" class="custom-field-option-$fieldId" data-value=""><i class="octicon octicon-x"></i> Clear ${StringUtil
 | 
			
		||||
                .escapeHtml(fieldName)}</a></li>"""
 | 
			
		||||
                  .escapeHtml(fieldName)}</a></li>"""
 | 
			
		||||
            )
 | 
			
		||||
            constraints.foreach {
 | 
			
		||||
              x =>
 | 
			
		||||
                x.split(",").map(_.trim).foreach {
 | 
			
		||||
                  item =>
 | 
			
		||||
                    options.append(s"""<li>
 | 
			
		||||
            constraints.foreach { x =>
 | 
			
		||||
              x.split(",").map(_.trim).foreach { item =>
 | 
			
		||||
                options.append(s"""<li>
 | 
			
		||||
                 |  <a href="javascript:void(0);" class="custom-field-option-$fieldId" data-value="${StringUtil
 | 
			
		||||
                                        .escapeHtml(item)}">
 | 
			
		||||
                                   .escapeHtml(item)}">
 | 
			
		||||
                 |    ${gitbucket.core.helper.html.checkicon(value.contains(item))}
 | 
			
		||||
                 |    ${StringUtil.escapeHtml(item)}
 | 
			
		||||
                 |  </a>
 | 
			
		||||
                 |</li>
 | 
			
		||||
                 |""".stripMargin)
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            Html(options.toString())
 | 
			
		||||
          }
 | 
			
		||||
@@ -205,11 +203,11 @@ object CustomFieldBehavior {
 | 
			
		||||
      value match {
 | 
			
		||||
        case None =>
 | 
			
		||||
          sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">No ${StringUtil.escapeHtml(
 | 
			
		||||
            fieldName
 | 
			
		||||
          )}</span></span>""")
 | 
			
		||||
              fieldName
 | 
			
		||||
            )}</span></span>""")
 | 
			
		||||
        case Some(value) =>
 | 
			
		||||
          sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">${StringUtil
 | 
			
		||||
            .escapeHtml(value)}</span></span>""")
 | 
			
		||||
              .escapeHtml(value)}</span></span>""")
 | 
			
		||||
      }
 | 
			
		||||
      if (value.isEmpty || issueId.isEmpty) {
 | 
			
		||||
        sb.append(s"""<input type="hidden" id="custom-field-$fieldId" name="custom-field-$fieldId" value=""/>""")
 | 
			
		||||
@@ -220,7 +218,7 @@ object CustomFieldBehavior {
 | 
			
		||||
             |  $$('#custom-field-$fieldId').val(value);
 | 
			
		||||
             |  if (value == '') {
 | 
			
		||||
             |    $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text('No ${StringUtil
 | 
			
		||||
                       .escapeHtml(fieldName)}'));
 | 
			
		||||
                      .escapeHtml(fieldName)}'));
 | 
			
		||||
             |  } else {
 | 
			
		||||
             |    $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text(value));
 | 
			
		||||
             |    $$('a.custom-field-option-$fieldId[data-value=' + value + '] i').addClass('octicon-check');
 | 
			
		||||
@@ -237,7 +235,7 @@ object CustomFieldBehavior {
 | 
			
		||||
             |      $$('a.custom-field-option-$fieldId i.octicon-check').removeClass('octicon-check');
 | 
			
		||||
             |      if (value == '') {
 | 
			
		||||
             |        $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text('No ${StringUtil
 | 
			
		||||
                       .escapeHtml(fieldName)}'));
 | 
			
		||||
                      .escapeHtml(fieldName)}'));
 | 
			
		||||
             |      } else {
 | 
			
		||||
             |        $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text(value));
 | 
			
		||||
             |        $$('a.custom-field-option-$fieldId[data-value=' + value + '] i').addClass('octicon-check');
 | 
			
		||||
@@ -296,14 +294,14 @@ object CustomFieldBehavior {
 | 
			
		||||
      constraints: Option[String],
 | 
			
		||||
      value: String,
 | 
			
		||||
      editable: Boolean
 | 
			
		||||
    )(
 | 
			
		||||
      implicit context: Context
 | 
			
		||||
    )(implicit
 | 
			
		||||
      context: Context
 | 
			
		||||
    ): String = {
 | 
			
		||||
      val sb = new StringBuilder
 | 
			
		||||
      if (value.nonEmpty) {
 | 
			
		||||
        sb.append(
 | 
			
		||||
          s"""<span id="custom-field-$fieldId-label" class="custom-field-label">${StringUtil
 | 
			
		||||
            .escapeHtml(value)}</span>"""
 | 
			
		||||
              .escapeHtml(value)}</span>"""
 | 
			
		||||
        )
 | 
			
		||||
      } else {
 | 
			
		||||
        if (editable) {
 | 
			
		||||
 
 | 
			
		||||
@@ -53,24 +53,24 @@ trait RepositoryComponent extends TemplateComponent { self: Profile =>
 | 
			
		||||
          safeMode
 | 
			
		||||
        )
 | 
			
		||||
      ).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) =>
 | 
			
		||||
        { 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(
 | 
			
		||||
            (
 | 
			
		||||
              (
 | 
			
		||||
 
 | 
			
		||||
@@ -41,8 +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
 | 
			
		||||
  def filter(path: String, userName: Option[String], settings: SystemSettings, isUpdating: Boolean)(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): Boolean
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,16 +9,16 @@ 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,
 | 
			
		||||
  def addedComment(commentId: Int, content: String, issue: Issue, repository: RepositoryInfo)(implicit
 | 
			
		||||
    session: Session,
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Unit = ()
 | 
			
		||||
  def deletedComment(commentId: Int, issue: Issue, repository: RepositoryInfo)(
 | 
			
		||||
    implicit session: Session,
 | 
			
		||||
  def deletedComment(commentId: Int, issue: Issue, repository: RepositoryInfo)(implicit
 | 
			
		||||
    session: Session,
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Unit = ()
 | 
			
		||||
  def updatedComment(commentId: Int, content: String, issue: Issue, repository: RepositoryInfo)(
 | 
			
		||||
    implicit session: Session,
 | 
			
		||||
  def updatedComment(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 = ()
 | 
			
		||||
@@ -29,12 +29,12 @@ trait IssueHook {
 | 
			
		||||
    assigner: Option[String],
 | 
			
		||||
    assigned: Option[String],
 | 
			
		||||
    oldAssigned: Option[String]
 | 
			
		||||
  )(
 | 
			
		||||
    implicit session: Session,
 | 
			
		||||
  )(implicit
 | 
			
		||||
    session: Session,
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Unit = ()
 | 
			
		||||
  def closedByCommitComment(issue: Issue, repository: RepositoryInfo, message: String, pusher: Account)(
 | 
			
		||||
    implicit session: Session
 | 
			
		||||
  def closedByCommitComment(issue: Issue, repository: RepositoryInfo, message: String, pusher: Account)(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): Unit = ()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -340,25 +340,20 @@ abstract class Plugin {
 | 
			
		||||
   * 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)
 | 
			
		||||
    }
 | 
			
		||||
    (anonymousAccessiblePaths ++ anonymousAccessiblePaths(registry, context, settings)).foreach {
 | 
			
		||||
      case (path) =>
 | 
			
		||||
        registry.addAnonymousAccessiblePath(path)
 | 
			
		||||
    (anonymousAccessiblePaths ++ anonymousAccessiblePaths(registry, context, settings)).foreach { case (path) =>
 | 
			
		||||
      registry.addAnonymousAccessiblePath(path)
 | 
			
		||||
    }
 | 
			
		||||
    (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)
 | 
			
		||||
 
 | 
			
		||||
@@ -80,7 +80,7 @@ class PluginRegistry {
 | 
			
		||||
  def getAnonymousAccessiblePaths(): Seq[String] = anonymousAccessiblePaths.asScala.toSeq
 | 
			
		||||
 | 
			
		||||
  def addJavaScript(path: String, script: String): Unit =
 | 
			
		||||
    javaScripts.add((path, script)) //javaScripts += ((path, script))
 | 
			
		||||
    javaScripts.add((path, script)) // javaScripts += ((path, script))
 | 
			
		||||
 | 
			
		||||
  def getJavaScript(currentPath: String): List[String] =
 | 
			
		||||
    javaScripts.asScala.filter(x => currentPath.matches(x._1)).toList.map(_._2)
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,8 @@ trait ReceiveHook {
 | 
			
		||||
    command: ReceiveCommand,
 | 
			
		||||
    pusher: String,
 | 
			
		||||
    mergePullRequest: Boolean
 | 
			
		||||
  )(
 | 
			
		||||
    implicit session: Session
 | 
			
		||||
  )(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): Option[String] = None
 | 
			
		||||
 | 
			
		||||
  def postReceive(
 | 
			
		||||
@@ -24,8 +24,8 @@ trait ReceiveHook {
 | 
			
		||||
    command: ReceiveCommand,
 | 
			
		||||
    pusher: String,
 | 
			
		||||
    mergePullRequest: Boolean
 | 
			
		||||
  )(
 | 
			
		||||
    implicit session: Session
 | 
			
		||||
  )(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): Unit = ()
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -37,9 +37,8 @@ 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)
 | 
			
		||||
      .filter { case (ac, t) =>
 | 
			
		||||
        (ac.userName === t.userName) && (t.tokenHash === tokenToHash(token).bind) && (ac.removed === false.bind)
 | 
			
		||||
      }
 | 
			
		||||
      .map { case (ac, t) => ac }
 | 
			
		||||
      .firstOption
 | 
			
		||||
 
 | 
			
		||||
@@ -51,8 +51,8 @@ trait AccountFederationService {
 | 
			
		||||
   * @param preferredUserName Username
 | 
			
		||||
   * @return Available username
 | 
			
		||||
   */
 | 
			
		||||
  def findAvailableUserName(preferredUserName: Option[String], mailAddress: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def findAvailableUserName(preferredUserName: Option[String], mailAddress: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[String] = {
 | 
			
		||||
    preferredUserName
 | 
			
		||||
      .flatMap(n => extractSafeStringForUserName(n))
 | 
			
		||||
 
 | 
			
		||||
@@ -19,8 +19,8 @@ trait AccountService {
 | 
			
		||||
 | 
			
		||||
  private val logger = LoggerFactory.getLogger(classOf[AccountService])
 | 
			
		||||
 | 
			
		||||
  def authenticate(settings: SystemSettings, userName: String, password: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def authenticate(settings: SystemSettings, userName: String, password: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[Account] = {
 | 
			
		||||
    val account = if (password.isEmpty) {
 | 
			
		||||
      None
 | 
			
		||||
@@ -58,8 +58,8 @@ trait AccountService {
 | 
			
		||||
  /**
 | 
			
		||||
   * Authenticate by LDAP.
 | 
			
		||||
   */
 | 
			
		||||
  private def ldapAuthentication(settings: SystemSettings, userName: String, password: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  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) => {
 | 
			
		||||
@@ -112,15 +112,15 @@ 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
 | 
			
		||||
 | 
			
		||||
  def getAccountByUserNameIgnoreCase(userName: String, includeRemoved: Boolean = false)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getAccountByUserNameIgnoreCase(userName: String, includeRemoved: Boolean = false)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[Account] =
 | 
			
		||||
    Accounts filter (
 | 
			
		||||
      t => (t.userName.toLowerCase === userName.toLowerCase.bind).&&(t.removed === false.bind, !includeRemoved)
 | 
			
		||||
    Accounts filter (t =>
 | 
			
		||||
      (t.userName.toLowerCase === userName.toLowerCase.bind).&&(t.removed === false.bind, !includeRemoved)
 | 
			
		||||
    ) firstOption
 | 
			
		||||
 | 
			
		||||
  def getAccountsByUserNames(userNames: Set[String], knowns: Set[Account], includeRemoved: Boolean = false)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  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
 | 
			
		||||
@@ -135,17 +135,15 @@ trait AccountService {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def getAccountByMailAddress(mailAddress: String, includeRemoved: Boolean = false)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getAccountByMailAddress(mailAddress: String, includeRemoved: Boolean = false)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[Account] =
 | 
			
		||||
    (Accounts joinLeft AccountExtraMailAddresses on { case (a, e) => a.userName === e.userName })
 | 
			
		||||
      .filter {
 | 
			
		||||
        case (a, x) =>
 | 
			
		||||
          ((a.mailAddress.toLowerCase === mailAddress.toLowerCase.bind) ||
 | 
			
		||||
            (x.map { e =>
 | 
			
		||||
                e.extraMailAddress.toLowerCase === mailAddress.toLowerCase.bind
 | 
			
		||||
              }
 | 
			
		||||
              .getOrElse(false.bind))).&&(a.removed === false.bind, !includeRemoved)
 | 
			
		||||
      .filter { case (a, x) =>
 | 
			
		||||
        ((a.mailAddress.toLowerCase === mailAddress.toLowerCase.bind) ||
 | 
			
		||||
          (x.map { e =>
 | 
			
		||||
            e.extraMailAddress.toLowerCase === mailAddress.toLowerCase.bind
 | 
			
		||||
          }.getOrElse(false.bind))).&&(a.removed === false.bind, !includeRemoved)
 | 
			
		||||
      }
 | 
			
		||||
      .map { case (a, e) => a } firstOption
 | 
			
		||||
 | 
			
		||||
@@ -267,8 +265,8 @@ trait AccountService {
 | 
			
		||||
    group
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def updateGroup(groupName: String, description: Option[String], url: Option[String], removed: Boolean)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def updateGroup(groupName: String, description: Option[String], url: Option[String], removed: Boolean)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit =
 | 
			
		||||
    Accounts
 | 
			
		||||
      .filter(_.userName === groupName.bind)
 | 
			
		||||
@@ -277,9 +275,8 @@ trait AccountService {
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -318,8 +315,8 @@ trait AccountService {
 | 
			
		||||
  /*
 | 
			
		||||
   * For account preference
 | 
			
		||||
   */
 | 
			
		||||
  def getAccountPreference(userName: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getAccountPreference(userName: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[AccountPreference] = {
 | 
			
		||||
    AccountPreferences filter (_.byPrimaryKey(userName)) firstOption
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -43,9 +43,8 @@ trait ActivityService {
 | 
			
		||||
 | 
			
		||||
  def getRecentActivitiesByRepos(repos: Set[(String, String)])(implicit context: Context): List[Activity] = {
 | 
			
		||||
    getActivities(includePublic = true) { activity =>
 | 
			
		||||
      repos.exists {
 | 
			
		||||
        case (userName, repositoryName) =>
 | 
			
		||||
          activity.userName == userName && activity.repositoryName == repositoryName
 | 
			
		||||
      repos.exists { case (userName, repositoryName) =>
 | 
			
		||||
        activity.userName == userName && activity.repositoryName == repositoryName
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@@ -65,10 +64,12 @@ trait ActivityService {
 | 
			
		||||
          .get()
 | 
			
		||||
      ) { reader =>
 | 
			
		||||
        var json: String = null
 | 
			
		||||
        while (list.length < 50 && {
 | 
			
		||||
                 json = reader.readLine();
 | 
			
		||||
                 json
 | 
			
		||||
               } != null) {
 | 
			
		||||
        while (
 | 
			
		||||
          list.length < 50 && {
 | 
			
		||||
            json = reader.readLine();
 | 
			
		||||
            json
 | 
			
		||||
          } != null
 | 
			
		||||
        ) {
 | 
			
		||||
          val activity = read[Activity](json)
 | 
			
		||||
          if (filter(activity)) {
 | 
			
		||||
            list += activity
 | 
			
		||||
 
 | 
			
		||||
@@ -47,8 +47,8 @@ trait CommitStatusService {
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  def getCommitStatusWithSummary(userName: String, repositoryName: String, sha: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getCommitStatusWithSummary(userName: String, repositoryName: String, sha: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[(CommitState, List[CommitStatus])] = {
 | 
			
		||||
    val statuses = getCommitStatuses(userName, repositoryName, sha)
 | 
			
		||||
    if (statuses.isEmpty) {
 | 
			
		||||
@@ -62,18 +62,18 @@ trait CommitStatusService {
 | 
			
		||||
  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
 | 
			
		||||
  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 getCommitStatuses(userName: String, repositoryName: String, sha: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getCommitStatuses(userName: String, repositoryName: String, sha: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[CommitStatus] =
 | 
			
		||||
    byCommitStatus(userName, repositoryName, sha).list
 | 
			
		||||
 | 
			
		||||
  def getRecentStatusContexts(userName: String, repositoryName: String, time: java.util.Date)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getRecentStatusContexts(userName: String, repositoryName: String, time: java.util.Date)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[String] =
 | 
			
		||||
    CommitStatuses
 | 
			
		||||
      .filter(t => t.byRepository(userName, repositoryName))
 | 
			
		||||
@@ -82,8 +82,8 @@ trait CommitStatusService {
 | 
			
		||||
      .map(_._1)
 | 
			
		||||
      .list
 | 
			
		||||
 | 
			
		||||
  def getCommitStatusesWithCreator(userName: String, repositoryName: String, sha: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getCommitStatusesWithCreator(userName: String, repositoryName: String, sha: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[(CommitStatus, Account)] =
 | 
			
		||||
    byCommitStatus(userName, repositoryName, sha)
 | 
			
		||||
      .join(Accounts)
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,8 @@ import org.apache.commons.io.FileUtils
 | 
			
		||||
trait CommitsService {
 | 
			
		||||
  self: ActivityService with PullRequestService with WebHookPullRequestReviewCommentService =>
 | 
			
		||||
 | 
			
		||||
  def getCommitComments(owner: String, repository: String, commitId: String, includePullRequest: Boolean)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  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)
 | 
			
		||||
@@ -79,21 +79,20 @@ trait CommitsService {
 | 
			
		||||
    val comment = getCommitComment(repository.owner, repository.name, commentId.toString).get
 | 
			
		||||
    issueId match {
 | 
			
		||||
      case Some(issueId) =>
 | 
			
		||||
        getPullRequest(repository.owner, repository.name, issueId).foreach {
 | 
			
		||||
          case (issue, pullRequest) =>
 | 
			
		||||
            val pullRequestCommentInfo =
 | 
			
		||||
              PullRequestCommentInfo(repository.owner, repository.name, loginAccount.userName, content, issueId)
 | 
			
		||||
            recordActivity(pullRequestCommentInfo)
 | 
			
		||||
            PluginRegistry().getPullRequestHooks.foreach(_.addedComment(commentId, content, issue, repository))
 | 
			
		||||
            callPullRequestReviewCommentWebHook(
 | 
			
		||||
              "create",
 | 
			
		||||
              comment,
 | 
			
		||||
              repository,
 | 
			
		||||
              issue,
 | 
			
		||||
              pullRequest,
 | 
			
		||||
              loginAccount,
 | 
			
		||||
              context.settings
 | 
			
		||||
            )
 | 
			
		||||
        getPullRequest(repository.owner, repository.name, issueId).foreach { case (issue, pullRequest) =>
 | 
			
		||||
          val pullRequestCommentInfo =
 | 
			
		||||
            PullRequestCommentInfo(repository.owner, repository.name, loginAccount.userName, content, issueId)
 | 
			
		||||
          recordActivity(pullRequestCommentInfo)
 | 
			
		||||
          PluginRegistry().getPullRequestHooks.foreach(_.addedComment(commentId, content, issue, repository))
 | 
			
		||||
          callPullRequestReviewCommentWebHook(
 | 
			
		||||
            "create",
 | 
			
		||||
            comment,
 | 
			
		||||
            repository,
 | 
			
		||||
            issue,
 | 
			
		||||
            pullRequest,
 | 
			
		||||
            loginAccount,
 | 
			
		||||
            context.settings
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      case None =>
 | 
			
		||||
        val commitCommentInfo =
 | 
			
		||||
@@ -104,8 +103,8 @@ trait CommitsService {
 | 
			
		||||
    commentId
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def updateCommitCommentPosition(commentId: Int, commitId: String, oldLine: Option[Int], newLine: Option[Int])(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def updateCommitCommentPosition(commentId: Int, commitId: String, oldLine: Option[Int], newLine: Option[Int])(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit =
 | 
			
		||||
    CommitComments
 | 
			
		||||
      .filter(_.byPrimaryKey(commentId))
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,8 @@ trait CustomFieldsService {
 | 
			
		||||
  def getCustomFields(owner: String, repository: String)(implicit s: Session): List[CustomField] =
 | 
			
		||||
    CustomFields.filter(_.byRepository(owner, repository)).sortBy(_.fieldId asc).list
 | 
			
		||||
 | 
			
		||||
  def getCustomFieldsWithValue(owner: String, repository: String, issueId: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getCustomFieldsWithValue(owner: String, repository: String, issueId: Int)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[(CustomField, Option[IssueCustomField])] = {
 | 
			
		||||
    CustomFields
 | 
			
		||||
      .filter(_.byRepository(owner, repository))
 | 
			
		||||
@@ -52,8 +52,8 @@ trait CustomFieldsService {
 | 
			
		||||
    constraints: Option[String],
 | 
			
		||||
    enableForIssues: Boolean,
 | 
			
		||||
    enableForPullRequests: Boolean
 | 
			
		||||
  )(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  )(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit =
 | 
			
		||||
    CustomFields
 | 
			
		||||
      .filter(_.byPrimaryKey(owner, repository, fieldId))
 | 
			
		||||
 
 | 
			
		||||
@@ -17,10 +17,9 @@ trait GpgKeyService {
 | 
			
		||||
 | 
			
		||||
  def addGpgPublicKey(userName: String, title: String, publicKey: String)(implicit s: Session): Unit = {
 | 
			
		||||
    val pubKeyOf = new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(publicKey.getBytes)))
 | 
			
		||||
    pubKeyOf.iterator().asScala.foreach {
 | 
			
		||||
      case keyRing: PGPPublicKeyRing =>
 | 
			
		||||
        val key = keyRing.getPublicKey()
 | 
			
		||||
        GpgKeys.insert(GpgKey(userName = userName, gpgKeyId = key.getKeyID, title = title, publicKey = publicKey))
 | 
			
		||||
    pubKeyOf.iterator().asScala.foreach { case keyRing: PGPPublicKeyRing =>
 | 
			
		||||
      val key = keyRing.getPublicKey()
 | 
			
		||||
      GpgKeys.insert(GpgKey(userName = userName, gpgKeyId = key.getKeyID, title = title, publicKey = publicKey))
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -134,8 +134,8 @@ trait HandleCommentService {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def deleteCommentByApi(repoInfo: RepositoryInfo, comment: IssueComment, issue: Issue)(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
  def deleteCommentByApi(repoInfo: RepositoryInfo, comment: IssueComment, issue: Issue)(implicit
 | 
			
		||||
    context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[IssueComment] = context.loginAccount.flatMap { _ =>
 | 
			
		||||
    comment.action match {
 | 
			
		||||
 
 | 
			
		||||
@@ -41,8 +41,8 @@ trait IssuesService {
 | 
			
		||||
    IssueComments filter (_.byIssue(owner, repository, issueId)) sortBy (_.commentId asc) list
 | 
			
		||||
 | 
			
		||||
  /** @return IssueComment and commentedUser and Issue */
 | 
			
		||||
  def getCommentsForApi(owner: String, repository: String, issueId: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getCommentsForApi(owner: String, repository: String, issueId: Int)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[(IssueComment, Account, Issue)] =
 | 
			
		||||
    IssueComments
 | 
			
		||||
      .filter(_.byIssue(owner, repository, issueId))
 | 
			
		||||
@@ -54,8 +54,8 @@ trait IssuesService {
 | 
			
		||||
      .map { case t1 ~ t2 ~ t3 => (t1, t2, t3) }
 | 
			
		||||
      .list
 | 
			
		||||
 | 
			
		||||
  def getMergedComment(owner: String, repository: String, issueId: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getMergedComment(owner: String, repository: String, issueId: Int)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[(IssueComment, Account)] = {
 | 
			
		||||
    IssueComments
 | 
			
		||||
      .filter(_.byIssue(owner, repository, issueId))
 | 
			
		||||
@@ -74,8 +74,8 @@ trait IssuesService {
 | 
			
		||||
    else None
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def getCommentForApi(owner: String, repository: String, commentId: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getCommentForApi(owner: String, repository: String, commentId: Int)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[(IssueComment, Account, Issue)] =
 | 
			
		||||
    IssueComments
 | 
			
		||||
      .filter(_.byRepository(owner, repository))
 | 
			
		||||
@@ -91,17 +91,16 @@ trait IssuesService {
 | 
			
		||||
  def getIssueLabels(owner: String, repository: String, issueId: Int)(implicit s: Session): List[Label] = {
 | 
			
		||||
    IssueLabels
 | 
			
		||||
      .join(Labels)
 | 
			
		||||
      .on {
 | 
			
		||||
        case t1 ~ t2 =>
 | 
			
		||||
          t1.byLabel(t2.userName, t2.repositoryName, t2.labelId)
 | 
			
		||||
      .on { case t1 ~ t2 =>
 | 
			
		||||
        t1.byLabel(t2.userName, t2.repositoryName, t2.labelId)
 | 
			
		||||
      }
 | 
			
		||||
      .filter { case t1 ~ t2 => t1.byIssue(owner, repository, issueId) }
 | 
			
		||||
      .map { case t1 ~ t2 => t2 }
 | 
			
		||||
      .list
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def getIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[IssueLabel] = {
 | 
			
		||||
    IssueLabels filter (_.byPrimaryKey(owner, repository, issueId, labelId)) firstOption
 | 
			
		||||
  }
 | 
			
		||||
@@ -114,8 +113,8 @@ trait IssuesService {
 | 
			
		||||
   * @param repos Tuple of the repository owner and the repository name
 | 
			
		||||
   * @return the count of the search result
 | 
			
		||||
   */
 | 
			
		||||
  def countIssue(condition: IssueSearchCondition, searchOption: IssueSearchOption, repos: (String, String)*)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def countIssue(condition: IssueSearchCondition, searchOption: IssueSearchOption, repos: (String, String)*)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    Query(searchIssueQuery(repos, condition, searchOption).length).first
 | 
			
		||||
  }
 | 
			
		||||
@@ -136,22 +135,18 @@ trait IssuesService {
 | 
			
		||||
 | 
			
		||||
    searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), IssueSearchOption.Issues)
 | 
			
		||||
      .join(IssueLabels)
 | 
			
		||||
      .on {
 | 
			
		||||
        case t1 ~ t2 =>
 | 
			
		||||
          t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
 | 
			
		||||
      .on { case t1 ~ t2 =>
 | 
			
		||||
        t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
 | 
			
		||||
      }
 | 
			
		||||
      .join(Labels)
 | 
			
		||||
      .on {
 | 
			
		||||
        case t1 ~ t2 ~ t3 =>
 | 
			
		||||
          t2.byLabel(t3.userName, t3.repositoryName, t3.labelId)
 | 
			
		||||
      .on { case t1 ~ t2 ~ t3 =>
 | 
			
		||||
        t2.byLabel(t3.userName, t3.repositoryName, t3.labelId)
 | 
			
		||||
      }
 | 
			
		||||
      .groupBy {
 | 
			
		||||
        case t1 ~ t2 ~ t3 =>
 | 
			
		||||
          t3.labelName
 | 
			
		||||
      .groupBy { case t1 ~ t2 ~ t3 =>
 | 
			
		||||
        t3.labelName
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        case (labelName, t) =>
 | 
			
		||||
          labelName -> t.length
 | 
			
		||||
      .map { case (labelName, t) =>
 | 
			
		||||
        labelName -> t.length
 | 
			
		||||
      }
 | 
			
		||||
      .list
 | 
			
		||||
      .toMap
 | 
			
		||||
@@ -173,17 +168,14 @@ trait IssuesService {
 | 
			
		||||
 | 
			
		||||
    searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), IssueSearchOption.Issues)
 | 
			
		||||
      .join(Priorities)
 | 
			
		||||
      .on {
 | 
			
		||||
        case t1 ~ t2 =>
 | 
			
		||||
          t1.byPriority(t2.userName, t2.repositoryName, t2.priorityId)
 | 
			
		||||
      .on { case t1 ~ t2 =>
 | 
			
		||||
        t1.byPriority(t2.userName, t2.repositoryName, t2.priorityId)
 | 
			
		||||
      }
 | 
			
		||||
      .groupBy {
 | 
			
		||||
        case t1 ~ t2 =>
 | 
			
		||||
          t2.priorityName
 | 
			
		||||
      .groupBy { case t1 ~ t2 =>
 | 
			
		||||
        t2.priorityName
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        case (priorityName, t) =>
 | 
			
		||||
          priorityName -> t.length
 | 
			
		||||
      .map { case (priorityName, t) =>
 | 
			
		||||
        priorityName -> t.length
 | 
			
		||||
      }
 | 
			
		||||
      .list
 | 
			
		||||
      .toMap
 | 
			
		||||
@@ -221,19 +213,18 @@ trait IssuesService {
 | 
			
		||||
      .joinLeft(IssueAssignees)
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => t1.byIssue(t8.userName, t8.repositoryName, t8.issueId) }
 | 
			
		||||
      .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => i asc }
 | 
			
		||||
      .map {
 | 
			
		||||
        case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 =>
 | 
			
		||||
          (
 | 
			
		||||
            t1,
 | 
			
		||||
            t2.commentCount,
 | 
			
		||||
            t4.map(_.labelId),
 | 
			
		||||
            t4.map(_.labelName),
 | 
			
		||||
            t4.map(_.color),
 | 
			
		||||
            t5.map(_.title),
 | 
			
		||||
            t6.map(_.priorityName),
 | 
			
		||||
            t7.map(_.commitIdTo),
 | 
			
		||||
            t8.map(_.assigneeUserName)
 | 
			
		||||
          )
 | 
			
		||||
      .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 =>
 | 
			
		||||
        (
 | 
			
		||||
          t1,
 | 
			
		||||
          t2.commentCount,
 | 
			
		||||
          t4.map(_.labelId),
 | 
			
		||||
          t4.map(_.labelName),
 | 
			
		||||
          t4.map(_.color),
 | 
			
		||||
          t5.map(_.title),
 | 
			
		||||
          t6.map(_.priorityName),
 | 
			
		||||
          t7.map(_.commitIdTo),
 | 
			
		||||
          t8.map(_.assigneeUserName)
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      .list
 | 
			
		||||
      .splitWith { (c1, c2) =>
 | 
			
		||||
@@ -264,8 +255,8 @@ trait IssuesService {
 | 
			
		||||
  /** for api
 | 
			
		||||
   * @return (issue, issueUser, Seq(assigneeUsers))
 | 
			
		||||
   */
 | 
			
		||||
  def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[(Issue, Account, List[Account])] = {
 | 
			
		||||
    // get issues and comment count and labels
 | 
			
		||||
    searchIssueQueryBase(condition, IssueSearchOption.Issues, offset, limit, repos)
 | 
			
		||||
@@ -278,13 +269,11 @@ trait IssuesService {
 | 
			
		||||
      .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => i asc }
 | 
			
		||||
      .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => (t1, t3, t5) }
 | 
			
		||||
      .list
 | 
			
		||||
      .groupBy {
 | 
			
		||||
        case (issue, account, _) =>
 | 
			
		||||
          (issue, account)
 | 
			
		||||
      .groupBy { case (issue, account, _) =>
 | 
			
		||||
        (issue, account)
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        case (_, values) =>
 | 
			
		||||
          (values.head._1, values.head._2, values.flatMap(_._3))
 | 
			
		||||
      .map { case (_, values) =>
 | 
			
		||||
        (values.head._1, values.head._2, values.flatMap(_._3))
 | 
			
		||||
      }
 | 
			
		||||
      .toList
 | 
			
		||||
  }
 | 
			
		||||
@@ -312,21 +301,19 @@ trait IssuesService {
 | 
			
		||||
      .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => i asc }
 | 
			
		||||
      .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => (t1, t5, t2.commentCount, t3, t4, t6, t8) }
 | 
			
		||||
      .list
 | 
			
		||||
      .groupBy {
 | 
			
		||||
        case (issue, openedUser, commentCount, pullRequest, repository, account, assignedUser) =>
 | 
			
		||||
          (issue, openedUser, commentCount, pullRequest, repository, account)
 | 
			
		||||
      .groupBy { case (issue, openedUser, commentCount, pullRequest, repository, account, assignedUser) =>
 | 
			
		||||
        (issue, openedUser, commentCount, pullRequest, repository, account)
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        case (_, values) =>
 | 
			
		||||
          (
 | 
			
		||||
            values.head._1,
 | 
			
		||||
            values.head._2,
 | 
			
		||||
            values.head._3,
 | 
			
		||||
            values.head._4,
 | 
			
		||||
            values.head._5,
 | 
			
		||||
            values.head._6,
 | 
			
		||||
            values.flatMap(_._7)
 | 
			
		||||
          )
 | 
			
		||||
      .map { case (_, values) =>
 | 
			
		||||
        (
 | 
			
		||||
          values.head._1,
 | 
			
		||||
          values.head._2,
 | 
			
		||||
          values.head._3,
 | 
			
		||||
          values.head._4,
 | 
			
		||||
          values.head._5,
 | 
			
		||||
          values.head._6,
 | 
			
		||||
          values.flatMap(_._7)
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      .toList
 | 
			
		||||
  }
 | 
			
		||||
@@ -344,30 +331,29 @@ trait IssuesService {
 | 
			
		||||
        t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
 | 
			
		||||
      }
 | 
			
		||||
      .sortBy { case (t1, t2) => t1.issueId desc }
 | 
			
		||||
      .sortBy {
 | 
			
		||||
        case (t1, t2) =>
 | 
			
		||||
          condition.sort match {
 | 
			
		||||
            case "created" =>
 | 
			
		||||
              condition.direction match {
 | 
			
		||||
                case "asc"  => t1.registeredDate asc
 | 
			
		||||
                case "desc" => t1.registeredDate desc
 | 
			
		||||
              }
 | 
			
		||||
            case "comments" =>
 | 
			
		||||
              condition.direction match {
 | 
			
		||||
                case "asc"  => t2.commentCount asc
 | 
			
		||||
                case "desc" => t2.commentCount desc
 | 
			
		||||
              }
 | 
			
		||||
            case "updated" =>
 | 
			
		||||
              condition.direction match {
 | 
			
		||||
                case "asc"  => t1.updatedDate asc
 | 
			
		||||
                case "desc" => t1.updatedDate desc
 | 
			
		||||
              }
 | 
			
		||||
            case "priority" =>
 | 
			
		||||
              condition.direction match {
 | 
			
		||||
                case "asc"  => t2.priority asc
 | 
			
		||||
                case "desc" => t2.priority desc
 | 
			
		||||
              }
 | 
			
		||||
          }
 | 
			
		||||
      .sortBy { case (t1, t2) =>
 | 
			
		||||
        condition.sort match {
 | 
			
		||||
          case "created" =>
 | 
			
		||||
            condition.direction match {
 | 
			
		||||
              case "asc"  => t1.registeredDate asc
 | 
			
		||||
              case "desc" => t1.registeredDate desc
 | 
			
		||||
            }
 | 
			
		||||
          case "comments" =>
 | 
			
		||||
            condition.direction match {
 | 
			
		||||
              case "asc"  => t2.commentCount asc
 | 
			
		||||
              case "desc" => t2.commentCount desc
 | 
			
		||||
            }
 | 
			
		||||
          case "updated" =>
 | 
			
		||||
            condition.direction match {
 | 
			
		||||
              case "asc"  => t1.updatedDate asc
 | 
			
		||||
              case "desc" => t1.updatedDate desc
 | 
			
		||||
            }
 | 
			
		||||
          case "priority" =>
 | 
			
		||||
            condition.direction match {
 | 
			
		||||
              case "asc"  => t2.priority asc
 | 
			
		||||
              case "desc" => t2.priority desc
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      .drop(offset)
 | 
			
		||||
      .take(limit)
 | 
			
		||||
@@ -380,8 +366,8 @@ trait IssuesService {
 | 
			
		||||
    repos: Seq[(String, String)],
 | 
			
		||||
    condition: IssueSearchCondition,
 | 
			
		||||
    searchOption: IssueSearchOption
 | 
			
		||||
  )(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  )(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ) = {
 | 
			
		||||
    val query = Issues filter { t1 =>
 | 
			
		||||
      (if (repos.sizeIs == 1) {
 | 
			
		||||
@@ -395,14 +381,14 @@ trait IssuesService {
 | 
			
		||||
        case _        => t1.closed === true || t1.closed === false
 | 
			
		||||
      }).&&(t1.milestoneId.? isEmpty, condition.milestone.contains(None))
 | 
			
		||||
        .&&(t1.priorityId.? isEmpty, condition.priority.contains(None))
 | 
			
		||||
        //.&&(t1.assignedUserName.? isEmpty, condition.assigned == Some(None))
 | 
			
		||||
        // .&&(t1.assignedUserName.? isEmpty, condition.assigned == Some(None))
 | 
			
		||||
        .&&(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) &&
 | 
			
		||||
      (searchOption match {
 | 
			
		||||
        case IssueSearchOption.Issues       => t1.pullRequest === false
 | 
			
		||||
        case IssueSearchOption.PullRequests => t1.pullRequest === true
 | 
			
		||||
        case IssueSearchOption.Both         => t1.pullRequest === false || t1.pullRequest === true
 | 
			
		||||
      })
 | 
			
		||||
      // Milestone filter
 | 
			
		||||
        // Milestone filter
 | 
			
		||||
        .&&(
 | 
			
		||||
          Milestones filter { t2 =>
 | 
			
		||||
            (t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) &&
 | 
			
		||||
@@ -451,40 +437,46 @@ trait IssuesService {
 | 
			
		||||
        // Mentioned filter
 | 
			
		||||
        .&&(
 | 
			
		||||
          (t1.openedUserName === condition.mentioned.get.bind) || (IssueAssignees filter { t1 =>
 | 
			
		||||
            t1.byIssue(t1.userName, t1.repositoryName, t1.issueId) && t1.assigneeUserName === condition.mentioned.get.bind
 | 
			
		||||
            t1.byIssue(
 | 
			
		||||
              t1.userName,
 | 
			
		||||
              t1.repositoryName,
 | 
			
		||||
              t1.issueId
 | 
			
		||||
            ) && t1.assigneeUserName === condition.mentioned.get.bind
 | 
			
		||||
          } exists) ||
 | 
			
		||||
            (IssueComments filter { t2 =>
 | 
			
		||||
              (t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) && (t2.commentedUserName === condition.mentioned.get.bind)
 | 
			
		||||
              (t2.byIssue(
 | 
			
		||||
                t1.userName,
 | 
			
		||||
                t1.repositoryName,
 | 
			
		||||
                t1.issueId
 | 
			
		||||
              )) && (t2.commentedUserName === condition.mentioned.get.bind)
 | 
			
		||||
            } exists),
 | 
			
		||||
          condition.mentioned.isDefined
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    condition.others.foldLeft(query) {
 | 
			
		||||
      case (query, cond) =>
 | 
			
		||||
        def condQuery(f: Rep[String] => Rep[Boolean]): Query[Profile.Issues, Issue, Seq] = {
 | 
			
		||||
          query.filter { t1 =>
 | 
			
		||||
            IssueCustomFields
 | 
			
		||||
              .join(CustomFields)
 | 
			
		||||
              .on { (t2, t3) =>
 | 
			
		||||
                t2.userName === t3.userName && t2.repositoryName === t3.repositoryName && t2.fieldId === t3.fieldId
 | 
			
		||||
              }
 | 
			
		||||
              .filter {
 | 
			
		||||
                case (t2, t3) =>
 | 
			
		||||
                  t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) && t3.fieldName === cond.name.bind && f(
 | 
			
		||||
                    t2.value
 | 
			
		||||
                )
 | 
			
		||||
    condition.others.foldLeft(query) { case (query, cond) =>
 | 
			
		||||
      def condQuery(f: Rep[String] => Rep[Boolean]): Query[Profile.Issues, Issue, Seq] = {
 | 
			
		||||
        query.filter { t1 =>
 | 
			
		||||
          IssueCustomFields
 | 
			
		||||
            .join(CustomFields)
 | 
			
		||||
            .on { (t2, t3) =>
 | 
			
		||||
              t2.userName === t3.userName && t2.repositoryName === t3.repositoryName && t2.fieldId === t3.fieldId
 | 
			
		||||
            }
 | 
			
		||||
            .filter { case (t2, t3) =>
 | 
			
		||||
              t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) && t3.fieldName === cond.name.bind && f(
 | 
			
		||||
                t2.value
 | 
			
		||||
              )
 | 
			
		||||
            } exists
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        cond.operator match {
 | 
			
		||||
          case "eq"  => condQuery(_ === cond.value.bind)
 | 
			
		||||
          case "lt"  => condQuery(_ < cond.value.bind)
 | 
			
		||||
          case "gt"  => condQuery(_ > cond.value.bind)
 | 
			
		||||
          case "lte" => condQuery(_ <= cond.value.bind)
 | 
			
		||||
          case "gte" => condQuery(_ >= cond.value.bind)
 | 
			
		||||
          case _     => throw new IllegalArgumentException("Unsupported operator")
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      cond.operator match {
 | 
			
		||||
        case "eq"  => condQuery(_ === cond.value.bind)
 | 
			
		||||
        case "lt"  => condQuery(_ < cond.value.bind)
 | 
			
		||||
        case "gt"  => condQuery(_ > cond.value.bind)
 | 
			
		||||
        case "lte" => condQuery(_ <= cond.value.bind)
 | 
			
		||||
        case "gte" => condQuery(_ >= cond.value.bind)
 | 
			
		||||
        case _     => throw new IllegalArgumentException("Unsupported operator")
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -523,11 +515,12 @@ trait IssuesService {
 | 
			
		||||
          .filter(_.byPrimaryKey(owner, repository))
 | 
			
		||||
          .map(_.issueId)
 | 
			
		||||
          .update(id) > 0
 | 
			
		||||
    } get
 | 
			
		||||
      } get
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def registerIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int, insertComment: Boolean = false)(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
    implicit
 | 
			
		||||
    context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    if (insertComment) {
 | 
			
		||||
@@ -546,7 +539,8 @@ trait IssuesService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def deleteIssueLabel(owner: String, repository: String, issueId: Int, labelId: Int, insertComment: Boolean = false)(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
    implicit
 | 
			
		||||
    context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    if (insertComment) {
 | 
			
		||||
@@ -564,8 +558,8 @@ trait IssuesService {
 | 
			
		||||
    IssueLabels filter (_.byPrimaryKey(owner, repository, issueId, labelId)) delete
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def deleteAllIssueLabels(owner: String, repository: String, issueId: Int, insertComment: Boolean = false)(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
  def deleteAllIssueLabels(owner: String, repository: String, issueId: Int, insertComment: Boolean = false)(implicit
 | 
			
		||||
    context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    if (insertComment) {
 | 
			
		||||
@@ -604,8 +598,8 @@ trait IssuesService {
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def updateIssue(owner: String, repository: String, issueId: Int, title: String, content: Option[String])(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def updateIssue(owner: String, repository: String, issueId: Int, title: String, content: Option[String])(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    Issues
 | 
			
		||||
      .filter(_.byPrimaryKey(owner, repository, issueId))
 | 
			
		||||
@@ -624,8 +618,8 @@ trait IssuesService {
 | 
			
		||||
      .update(true)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def getIssueAssignees(owner: String, repository: String, issueId: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getIssueAssignees(owner: String, repository: String, issueId: Int)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[IssueAssignee] = {
 | 
			
		||||
    IssueAssignees.filter(_.byIssue(owner, repository, issueId)).sortBy(_.assigneeUserName).list
 | 
			
		||||
  }
 | 
			
		||||
@@ -636,8 +630,8 @@ trait IssuesService {
 | 
			
		||||
    issueId: Int,
 | 
			
		||||
    assigneeUserName: String,
 | 
			
		||||
    insertComment: Boolean = false
 | 
			
		||||
  )(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
  )(implicit
 | 
			
		||||
    context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    val assigner = context.loginAccount.map(_.userName)
 | 
			
		||||
@@ -665,8 +659,8 @@ trait IssuesService {
 | 
			
		||||
    issueId: Int,
 | 
			
		||||
    assigneeUserName: String,
 | 
			
		||||
    insertComment: Boolean = false
 | 
			
		||||
  )(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
  )(implicit
 | 
			
		||||
    context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    val assigner = context.loginAccount.map(_.userName)
 | 
			
		||||
@@ -688,8 +682,8 @@ trait IssuesService {
 | 
			
		||||
    IssueAssignees filter (_.byPrimaryKey(owner, repository, issueId, assigneeUserName)) delete
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def deleteAllIssueAssignees(owner: String, repository: String, issueId: Int, insertComment: Boolean = false)(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
  def deleteAllIssueAssignees(owner: String, repository: String, issueId: Int, insertComment: Boolean = false)(implicit
 | 
			
		||||
    context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    val assigner = context.loginAccount.map(_.userName)
 | 
			
		||||
@@ -773,15 +767,15 @@ trait IssuesService {
 | 
			
		||||
      .update(priorityId, currentDate)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def updateComment(owner: String, repository: String, issueId: Int, commentId: Int, content: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def updateComment(owner: String, repository: String, issueId: Int, commentId: Int, content: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    Issues.filter(_.byPrimaryKey(owner, repository, issueId)).map(_.updatedDate).update(currentDate)
 | 
			
		||||
    IssueComments.filter(_.byPrimaryKey(commentId)).map(t => (t.content, t.updatedDate)).update(content, currentDate)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def deleteComment(owner: String, repository: String, issueId: Int, commentId: Int)(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
  def deleteComment(owner: String, repository: String, issueId: Int, commentId: Int)(implicit
 | 
			
		||||
    context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    Issues.filter(_.byPrimaryKey(owner, repository, issueId)).map(_.updatedDate).update(currentDate)
 | 
			
		||||
@@ -820,10 +814,10 @@ trait IssuesService {
 | 
			
		||||
   * @param query the keywords separated by whitespace.
 | 
			
		||||
   * @return issues with comment count and matched content of issue or comment
 | 
			
		||||
   */
 | 
			
		||||
  def searchIssuesByKeyword(owner: String, repository: String, query: String, pullRequest: Boolean)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def searchIssuesByKeyword(owner: String, repository: String, query: String, pullRequest: Boolean)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[(Issue, Int, String)] = {
 | 
			
		||||
    //import slick.driver.JdbcDriver.likeEncode
 | 
			
		||||
    // import slick.driver.JdbcDriver.likeEncode
 | 
			
		||||
    val keywords = splitWords(query.toLowerCase)
 | 
			
		||||
 | 
			
		||||
    // Search Issue
 | 
			
		||||
@@ -832,61 +826,52 @@ trait IssuesService {
 | 
			
		||||
        t.byRepository(owner, repository) && t.pullRequest === pullRequest.bind
 | 
			
		||||
      }
 | 
			
		||||
      .join(IssueOutline)
 | 
			
		||||
      .on {
 | 
			
		||||
        case (t1, t2) =>
 | 
			
		||||
          t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
 | 
			
		||||
      .on { case (t1, t2) =>
 | 
			
		||||
        t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
 | 
			
		||||
      }
 | 
			
		||||
      .filter {
 | 
			
		||||
        case (t1, t2) =>
 | 
			
		||||
          keywords
 | 
			
		||||
            .map { keyword =>
 | 
			
		||||
              (t1.title.toLowerCase.like(s"%${likeEncode(keyword)}%", '^')) ||
 | 
			
		||||
              (t1.content.toLowerCase.like(s"%${likeEncode(keyword)}%", '^'))
 | 
			
		||||
            }
 | 
			
		||||
            .reduceLeft(_ && _)
 | 
			
		||||
      .filter { case (t1, t2) =>
 | 
			
		||||
        keywords
 | 
			
		||||
          .map { keyword =>
 | 
			
		||||
            (t1.title.toLowerCase.like(s"%${likeEncode(keyword)}%", '^')) ||
 | 
			
		||||
            (t1.content.toLowerCase.like(s"%${likeEncode(keyword)}%", '^'))
 | 
			
		||||
          }
 | 
			
		||||
          .reduceLeft(_ && _)
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        case (t1, t2) =>
 | 
			
		||||
          (t1, 0, t1.content.?, t2.commentCount)
 | 
			
		||||
      .map { case (t1, t2) =>
 | 
			
		||||
        (t1, 0, t1.content.?, t2.commentCount)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    // Search IssueComment
 | 
			
		||||
    val comments = IssueComments
 | 
			
		||||
      .filter(_.byRepository(owner, repository))
 | 
			
		||||
      .join(Issues)
 | 
			
		||||
      .on {
 | 
			
		||||
        case (t1, t2) =>
 | 
			
		||||
          t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
 | 
			
		||||
      .on { case (t1, t2) =>
 | 
			
		||||
        t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
 | 
			
		||||
      }
 | 
			
		||||
      .join(IssueOutline)
 | 
			
		||||
      .on {
 | 
			
		||||
        case ((t1, t2), t3) =>
 | 
			
		||||
          t2.byIssue(t3.userName, t3.repositoryName, t3.issueId)
 | 
			
		||||
      .on { case ((t1, t2), t3) =>
 | 
			
		||||
        t2.byIssue(t3.userName, t3.repositoryName, t3.issueId)
 | 
			
		||||
      }
 | 
			
		||||
      .filter {
 | 
			
		||||
        case ((t1, t2), t3) =>
 | 
			
		||||
          t2.pullRequest === pullRequest.bind &&
 | 
			
		||||
            keywords
 | 
			
		||||
              .map { query =>
 | 
			
		||||
                t1.content.toLowerCase.like(s"%${likeEncode(query)}%", '^')
 | 
			
		||||
              }
 | 
			
		||||
              .reduceLeft(_ && _)
 | 
			
		||||
      .filter { case ((t1, t2), t3) =>
 | 
			
		||||
        t2.pullRequest === pullRequest.bind &&
 | 
			
		||||
        keywords
 | 
			
		||||
          .map { query =>
 | 
			
		||||
            t1.content.toLowerCase.like(s"%${likeEncode(query)}%", '^')
 | 
			
		||||
          }
 | 
			
		||||
          .reduceLeft(_ && _)
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        case ((t1, t2), t3) =>
 | 
			
		||||
          (t2, t1.commentId, t1.content.?, t3.commentCount)
 | 
			
		||||
      .map { case ((t1, t2), t3) =>
 | 
			
		||||
        (t2, t1.commentId, t1.content.?, t3.commentCount)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    issues
 | 
			
		||||
      .union(comments)
 | 
			
		||||
      .sortBy {
 | 
			
		||||
        case (issue, commentId, _, _) =>
 | 
			
		||||
          issue.issueId.desc -> commentId
 | 
			
		||||
      .sortBy { case (issue, commentId, _, _) =>
 | 
			
		||||
        issue.issueId.desc -> commentId
 | 
			
		||||
      }
 | 
			
		||||
      .list
 | 
			
		||||
      .splitWith {
 | 
			
		||||
        case ((issue1, _, _, _), (issue2, _, _, _)) =>
 | 
			
		||||
          issue1.issueId == issue2.issueId
 | 
			
		||||
      .splitWith { case ((issue1, _, _, _), (issue2, _, _, _)) =>
 | 
			
		||||
        issue1.issueId == issue2.issueId
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        _.head match {
 | 
			
		||||
@@ -896,8 +881,8 @@ trait IssuesService {
 | 
			
		||||
      .toList
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def closeIssuesFromMessage(message: String, userName: String, owner: String, repository: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def closeIssuesFromMessage(message: String, userName: String, owner: String, repository: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Seq[Int] = {
 | 
			
		||||
    extractCloseId(message).flatMap { issueId =>
 | 
			
		||||
      for (issue <- getIssue(owner, repository, issueId) if !issue.closed) yield {
 | 
			
		||||
@@ -911,37 +896,37 @@ trait IssuesService {
 | 
			
		||||
  def createReferComment(owner: String, repository: String, fromIssue: Issue, message: String, loginAccount: Account)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  ): Unit = {
 | 
			
		||||
    extractGlobalIssueId(message).foreach {
 | 
			
		||||
      case (_referredOwner, _referredRepository, referredIssueId) =>
 | 
			
		||||
        val referredOwner = _referredOwner.getOrElse(owner)
 | 
			
		||||
        val referredRepository = _referredRepository.getOrElse(repository)
 | 
			
		||||
        getRepository(referredOwner, referredRepository).foreach { repo =>
 | 
			
		||||
          if (isReadable(repo.repository, Option(loginAccount))) {
 | 
			
		||||
            getIssue(referredOwner, referredRepository, referredIssueId.get).foreach { _ =>
 | 
			
		||||
              val (content, action) = if (owner == referredOwner && repository == referredRepository) {
 | 
			
		||||
                (s"${fromIssue.issueId}:${fromIssue.title}", "refer")
 | 
			
		||||
              } else {
 | 
			
		||||
                (s"${fromIssue.issueId}:${owner}:${repository}:${fromIssue.title}", "refer_global")
 | 
			
		||||
              }
 | 
			
		||||
              referredIssueId.foreach(
 | 
			
		||||
                x =>
 | 
			
		||||
                  // Not add if refer comment already exist.
 | 
			
		||||
                  if (!getComments(referredOwner, referredRepository, x.toInt).exists { x =>
 | 
			
		||||
                        (x.action == "refer" || x.action == "refer_global") && x.content == content
 | 
			
		||||
                      }) {
 | 
			
		||||
                    createComment(
 | 
			
		||||
                      referredOwner,
 | 
			
		||||
                      referredRepository,
 | 
			
		||||
                      loginAccount.userName,
 | 
			
		||||
                      x.toInt,
 | 
			
		||||
                      content,
 | 
			
		||||
                      action
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
              )
 | 
			
		||||
    extractGlobalIssueId(message).foreach { case (_referredOwner, _referredRepository, referredIssueId) =>
 | 
			
		||||
      val referredOwner = _referredOwner.getOrElse(owner)
 | 
			
		||||
      val referredRepository = _referredRepository.getOrElse(repository)
 | 
			
		||||
      getRepository(referredOwner, referredRepository).foreach { repo =>
 | 
			
		||||
        if (isReadable(repo.repository, Option(loginAccount))) {
 | 
			
		||||
          getIssue(referredOwner, referredRepository, referredIssueId.get).foreach { _ =>
 | 
			
		||||
            val (content, action) = if (owner == referredOwner && repository == referredRepository) {
 | 
			
		||||
              (s"${fromIssue.issueId}:${fromIssue.title}", "refer")
 | 
			
		||||
            } else {
 | 
			
		||||
              (s"${fromIssue.issueId}:${owner}:${repository}:${fromIssue.title}", "refer_global")
 | 
			
		||||
            }
 | 
			
		||||
            referredIssueId.foreach(x =>
 | 
			
		||||
              // Not add if refer comment already exist.
 | 
			
		||||
              if (
 | 
			
		||||
                !getComments(referredOwner, referredRepository, x.toInt).exists { x =>
 | 
			
		||||
                  (x.action == "refer" || x.action == "refer_global") && x.content == content
 | 
			
		||||
                }
 | 
			
		||||
              ) {
 | 
			
		||||
                createComment(
 | 
			
		||||
                  referredOwner,
 | 
			
		||||
                  referredRepository,
 | 
			
		||||
                  loginAccount.userName,
 | 
			
		||||
                  x.toInt,
 | 
			
		||||
                  content,
 | 
			
		||||
                  action
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -958,13 +943,13 @@ trait IssuesService {
 | 
			
		||||
  def getAssignableUserNames(owner: String, repository: String)(implicit s: Session): List[String] = {
 | 
			
		||||
    (getCollaboratorUserNames(owner, repository, Seq(Role.ADMIN, Role.DEVELOPER)) :::
 | 
			
		||||
      (getAccountByUserName(owner) match {
 | 
			
		||||
      case Some(x) if x.isGroupAccount =>
 | 
			
		||||
        getGroupMembers(owner).map(_.userName)
 | 
			
		||||
      case Some(_) =>
 | 
			
		||||
        List(owner)
 | 
			
		||||
      case None =>
 | 
			
		||||
        Nil
 | 
			
		||||
    })).distinct.sorted
 | 
			
		||||
        case Some(x) if x.isGroupAccount =>
 | 
			
		||||
          getGroupMembers(owner).map(_.userName)
 | 
			
		||||
        case Some(_) =>
 | 
			
		||||
          List(owner)
 | 
			
		||||
        case None =>
 | 
			
		||||
          Nil
 | 
			
		||||
      })).distinct.sorted
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1090,9 +1075,8 @@ object IssuesService {
 | 
			
		||||
            dim(0) -> dim(1)
 | 
			
		||||
        }
 | 
			
		||||
        .groupBy(_._1)
 | 
			
		||||
        .map {
 | 
			
		||||
          case (key, values) =>
 | 
			
		||||
            key -> values.map(_._2).toSeq
 | 
			
		||||
        .map { case (key, values) =>
 | 
			
		||||
          key -> values.map(_._2).toSeq
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      val (sort, direction) = conditions.get("sort").flatMap(_.headOption).getOrElse("created-desc") match {
 | 
			
		||||
@@ -1123,9 +1107,8 @@ object IssuesService {
 | 
			
		||||
            val dim = x.split(">")
 | 
			
		||||
            dim(0) -> ("gt", dim(1))
 | 
			
		||||
        }
 | 
			
		||||
        .map {
 | 
			
		||||
          case (key, (operator, value)) =>
 | 
			
		||||
            CustomFieldCondition(key.stripPrefix("custom."), value, operator)
 | 
			
		||||
        .map { case (key, (operator, value)) =>
 | 
			
		||||
          CustomFieldCondition(key.stripPrefix("custom."), value, operator)
 | 
			
		||||
        }
 | 
			
		||||
        .toSeq
 | 
			
		||||
 | 
			
		||||
@@ -1134,7 +1117,7 @@ object IssuesService {
 | 
			
		||||
        conditions.get("milestone").flatMap(_.headOption) match {
 | 
			
		||||
          case None         => None
 | 
			
		||||
          case Some("none") => Some(None)
 | 
			
		||||
          case Some(x)      => Some(Some(x)) //milestones.get(x).map(x => Some(x))
 | 
			
		||||
          case Some(x)      => Some(Some(x)) // milestones.get(x).map(x => Some(x))
 | 
			
		||||
        },
 | 
			
		||||
        conditions.get("priority").map(_.headOption), // TODO
 | 
			
		||||
        conditions.get("author").flatMap(_.headOption),
 | 
			
		||||
 
 | 
			
		||||
@@ -30,8 +30,8 @@ trait LabelsService {
 | 
			
		||||
    createLabel(owner, repository, labelName, color)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def updateLabel(owner: String, repository: String, labelId: Int, labelName: String, color: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def updateLabel(owner: String, repository: String, labelId: Int, labelName: String, color: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit =
 | 
			
		||||
    Labels
 | 
			
		||||
      .filter(_.byPrimaryKey(owner, repository, labelId))
 | 
			
		||||
 
 | 
			
		||||
@@ -121,8 +121,8 @@ trait MergeService {
 | 
			
		||||
    afterCommitId: ObjectId,
 | 
			
		||||
    loginAccount: Account,
 | 
			
		||||
    settings: SystemSettings
 | 
			
		||||
  )(
 | 
			
		||||
    implicit s: Session,
 | 
			
		||||
  )(implicit
 | 
			
		||||
    s: Session,
 | 
			
		||||
    c: JsonFormat.Context
 | 
			
		||||
  ): Unit = {
 | 
			
		||||
    callWebHookOf(repository.owner, repository.name, WebHook.Push, settings) {
 | 
			
		||||
@@ -220,7 +220,14 @@ trait MergeService {
 | 
			
		||||
    requestRepositoryName: String,
 | 
			
		||||
    requestBranch: String
 | 
			
		||||
  ): Option[String] =
 | 
			
		||||
    tryMergeRemote(userName, repositoryName, branch, requestUserName, requestRepositoryName, requestBranch).left.toOption
 | 
			
		||||
    tryMergeRemote(
 | 
			
		||||
      userName,
 | 
			
		||||
      repositoryName,
 | 
			
		||||
      branch,
 | 
			
		||||
      requestUserName,
 | 
			
		||||
      requestRepositoryName,
 | 
			
		||||
      requestBranch
 | 
			
		||||
    ).left.toOption
 | 
			
		||||
 | 
			
		||||
  def pullRemote(
 | 
			
		||||
    localRepository: RepositoryInfo,
 | 
			
		||||
@@ -236,84 +243,90 @@ trait MergeService {
 | 
			
		||||
    val localRepositoryName = localRepository.name
 | 
			
		||||
    val remoteUserName = remoteRepository.owner
 | 
			
		||||
    val remoteRepositoryName = remoteRepository.name
 | 
			
		||||
    tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map {
 | 
			
		||||
      case (newTreeId, oldBaseId, oldHeadId) =>
 | 
			
		||||
        Using.resource(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
 | 
			
		||||
          val existIds = JGitUtil.getAllCommitIds(git).toSet
 | 
			
		||||
    tryMergeRemote(
 | 
			
		||||
      localUserName,
 | 
			
		||||
      localRepositoryName,
 | 
			
		||||
      localBranch,
 | 
			
		||||
      remoteUserName,
 | 
			
		||||
      remoteRepositoryName,
 | 
			
		||||
      remoteBranch
 | 
			
		||||
    ).map { case (newTreeId, oldBaseId, oldHeadId) =>
 | 
			
		||||
      Using.resource(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
 | 
			
		||||
        val existIds = JGitUtil.getAllCommitIds(git).toSet
 | 
			
		||||
 | 
			
		||||
          val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
 | 
			
		||||
          val newCommit =
 | 
			
		||||
            Util.createMergeCommit(git.getRepository, newTreeId, committer, message, Seq(oldBaseId, oldHeadId))
 | 
			
		||||
          Util.updateRefs(git.getRepository, s"refs/heads/${localBranch}", newCommit, false, committer, Some("merge"))
 | 
			
		||||
        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"))
 | 
			
		||||
 | 
			
		||||
          val commits = git.log
 | 
			
		||||
            .addRange(oldBaseId, newCommit)
 | 
			
		||||
            .call
 | 
			
		||||
            .iterator
 | 
			
		||||
            .asScala
 | 
			
		||||
            .map(c => new JGitUtil.CommitInfo(c))
 | 
			
		||||
            .toList
 | 
			
		||||
        val commits = git.log
 | 
			
		||||
          .addRange(oldBaseId, newCommit)
 | 
			
		||||
          .call
 | 
			
		||||
          .iterator
 | 
			
		||||
          .asScala
 | 
			
		||||
          .map(c => new JGitUtil.CommitInfo(c))
 | 
			
		||||
          .toList
 | 
			
		||||
 | 
			
		||||
        commits.foreach { commit =>
 | 
			
		||||
          if (!existIds.contains(commit.id)) {
 | 
			
		||||
            createIssueComment(localUserName, localRepositoryName, commit)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // record activity
 | 
			
		||||
        val pushInfo = PushInfo(
 | 
			
		||||
          localUserName,
 | 
			
		||||
          localRepositoryName,
 | 
			
		||||
          loginAccount.userName,
 | 
			
		||||
          localBranch,
 | 
			
		||||
          commits
 | 
			
		||||
        )
 | 
			
		||||
        recordActivity(pushInfo)
 | 
			
		||||
 | 
			
		||||
        // close issue by commit message
 | 
			
		||||
        if (localBranch == localRepository.repository.defaultBranch) {
 | 
			
		||||
          commits.foreach { commit =>
 | 
			
		||||
            if (!existIds.contains(commit.id)) {
 | 
			
		||||
              createIssueComment(localUserName, localRepositoryName, commit)
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // record activity
 | 
			
		||||
          val pushInfo = PushInfo(
 | 
			
		||||
            localUserName,
 | 
			
		||||
            localRepositoryName,
 | 
			
		||||
            loginAccount.userName,
 | 
			
		||||
            localBranch,
 | 
			
		||||
            commits
 | 
			
		||||
          )
 | 
			
		||||
          recordActivity(pushInfo)
 | 
			
		||||
 | 
			
		||||
          // close issue by commit message
 | 
			
		||||
          if (localBranch == localRepository.repository.defaultBranch) {
 | 
			
		||||
            commits.foreach { commit =>
 | 
			
		||||
              closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
 | 
			
		||||
                .foreach { issueId =>
 | 
			
		||||
                  getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
 | 
			
		||||
                    callIssuesWebHook("closed", localRepository, issue, loginAccount, settings)
 | 
			
		||||
                    val closeIssueInfo = CloseIssueInfo(
 | 
			
		||||
                      localRepository.owner,
 | 
			
		||||
                      localRepository.name,
 | 
			
		||||
                      localUserName,
 | 
			
		||||
                      issue.issueId,
 | 
			
		||||
                      issue.title
 | 
			
		||||
            closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
 | 
			
		||||
              .foreach { issueId =>
 | 
			
		||||
                getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
 | 
			
		||||
                  callIssuesWebHook("closed", localRepository, issue, loginAccount, settings)
 | 
			
		||||
                  val closeIssueInfo = CloseIssueInfo(
 | 
			
		||||
                    localRepository.owner,
 | 
			
		||||
                    localRepository.name,
 | 
			
		||||
                    localUserName,
 | 
			
		||||
                    issue.issueId,
 | 
			
		||||
                    issue.title
 | 
			
		||||
                  )
 | 
			
		||||
                  recordActivity(closeIssueInfo)
 | 
			
		||||
                  PluginRegistry().getIssueHooks
 | 
			
		||||
                    .foreach(
 | 
			
		||||
                      _.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
 | 
			
		||||
                    )
 | 
			
		||||
                    recordActivity(closeIssueInfo)
 | 
			
		||||
                    PluginRegistry().getIssueHooks
 | 
			
		||||
                      .foreach(
 | 
			
		||||
                        _.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
 | 
			
		||||
                      )
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          pullRequest.foreach { pullRequest =>
 | 
			
		||||
            callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push, settings) {
 | 
			
		||||
              for {
 | 
			
		||||
                ownerAccount <- getAccountByUserName(localRepository.owner)
 | 
			
		||||
              } yield {
 | 
			
		||||
                WebHookService.WebHookPushPayload(
 | 
			
		||||
                  git,
 | 
			
		||||
                  loginAccount,
 | 
			
		||||
                  pullRequest.requestBranch,
 | 
			
		||||
                  localRepository,
 | 
			
		||||
                  commits,
 | 
			
		||||
                  ownerAccount,
 | 
			
		||||
                  oldId = oldBaseId,
 | 
			
		||||
                  newId = newCommit
 | 
			
		||||
                )
 | 
			
		||||
              }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pullRequest.foreach { pullRequest =>
 | 
			
		||||
          callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push, settings) {
 | 
			
		||||
            for {
 | 
			
		||||
              ownerAccount <- getAccountByUserName(localRepository.owner)
 | 
			
		||||
            } yield {
 | 
			
		||||
              WebHookService.WebHookPushPayload(
 | 
			
		||||
                git,
 | 
			
		||||
                loginAccount,
 | 
			
		||||
                pullRequest.requestBranch,
 | 
			
		||||
                localRepository,
 | 
			
		||||
                commits,
 | 
			
		||||
                ownerAccount,
 | 
			
		||||
                oldId = oldBaseId,
 | 
			
		||||
                newId = newCommit
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        oldBaseId
 | 
			
		||||
      }
 | 
			
		||||
      oldBaseId
 | 
			
		||||
    }.toOption
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -621,11 +634,12 @@ object MergeService {
 | 
			
		||||
 | 
			
		||||
    def checkConflictForce(): Option[String] = {
 | 
			
		||||
      val merger = MergeStrategy.RECURSIVE.newMerger(git.getRepository, true)
 | 
			
		||||
      val conflicted = try {
 | 
			
		||||
        !merger.merge(mergeBaseTip, mergeTip)
 | 
			
		||||
      } catch {
 | 
			
		||||
        case e: NoMergeBaseException => true
 | 
			
		||||
      }
 | 
			
		||||
      val conflicted =
 | 
			
		||||
        try {
 | 
			
		||||
          !merger.merge(mergeBaseTip, mergeTip)
 | 
			
		||||
        } catch {
 | 
			
		||||
          case e: NoMergeBaseException => true
 | 
			
		||||
        }
 | 
			
		||||
      val mergeTipCommit = Using.resource(new RevWalk(git.getRepository))(_.parseCommit(mergeTip))
 | 
			
		||||
      val committer = mergeTipCommit.getCommitterIdent
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -46,8 +46,8 @@ 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
 | 
			
		||||
  def getMilestonesWithIssueCount(owner: String, repository: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[(Milestone, Int, Int)] = {
 | 
			
		||||
    val counts = Issues
 | 
			
		||||
      .filter { t =>
 | 
			
		||||
@@ -78,13 +78,12 @@ trait MilestonesService {
 | 
			
		||||
  def getApiMilestone(repository: RepositoryInfo, milestoneId: Int)(implicit s: Session): Option[ApiMilestone] = {
 | 
			
		||||
    getMilestonesWithIssueCount(repository.owner, repository.name)
 | 
			
		||||
      .find(p => p._1.milestoneId == milestoneId)
 | 
			
		||||
      .map(
 | 
			
		||||
        milestoneWithIssue =>
 | 
			
		||||
          ApiMilestone(
 | 
			
		||||
            repository.repository,
 | 
			
		||||
            milestoneWithIssue._1,
 | 
			
		||||
            milestoneWithIssue._2,
 | 
			
		||||
            milestoneWithIssue._3
 | 
			
		||||
      .map(milestoneWithIssue =>
 | 
			
		||||
        ApiMilestone(
 | 
			
		||||
          repository.repository,
 | 
			
		||||
          milestoneWithIssue._1,
 | 
			
		||||
          milestoneWithIssue._2,
 | 
			
		||||
          milestoneWithIssue._3
 | 
			
		||||
        )
 | 
			
		||||
      )
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -59,12 +59,11 @@ trait PrioritiesService {
 | 
			
		||||
    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)
 | 
			
		||||
      )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -93,12 +92,11 @@ 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)
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,8 +8,8 @@ import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
 | 
			
		||||
 | 
			
		||||
trait ProtectedBranchService {
 | 
			
		||||
  import ProtectedBranchService._
 | 
			
		||||
  private def getProtectedBranchInfoOpt(owner: String, repository: String, branch: String)(
 | 
			
		||||
    implicit session: Session
 | 
			
		||||
  private def getProtectedBranchInfoOpt(owner: String, repository: String, branch: String)(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): Option[ProtectedBranchInfo] =
 | 
			
		||||
    ProtectedBranches
 | 
			
		||||
      .joinLeft(ProtectedBranchContexts)
 | 
			
		||||
@@ -22,13 +22,12 @@ trait ProtectedBranchService {
 | 
			
		||||
      .map { p =>
 | 
			
		||||
        p._1 -> p._2.flatMap(_._2)
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        case (t1, contexts) =>
 | 
			
		||||
          new ProtectedBranchInfo(t1.userName, t1.repositoryName, t1.branch, true, contexts, t1.statusCheckAdmin)
 | 
			
		||||
      .map { case (t1, contexts) =>
 | 
			
		||||
        new ProtectedBranchInfo(t1.userName, t1.repositoryName, t1.branch, true, contexts, t1.statusCheckAdmin)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
  def getProtectedBranchInfo(owner: String, repository: String, branch: String)(
 | 
			
		||||
    implicit session: Session
 | 
			
		||||
  def getProtectedBranchInfo(owner: String, repository: String, branch: String)(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): ProtectedBranchInfo =
 | 
			
		||||
    getProtectedBranchInfoOpt(owner, repository, branch).getOrElse(
 | 
			
		||||
      ProtectedBranchInfo.disabled(owner, repository, branch)
 | 
			
		||||
@@ -88,9 +87,11 @@ object ProtectedBranchService {
 | 
			
		||||
      val branch = command.getRefName.stripPrefix("refs/heads/")
 | 
			
		||||
      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(
 | 
			
		||||
@@ -128,15 +129,14 @@ object ProtectedBranchService {
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -144,8 +144,8 @@ 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
 | 
			
		||||
    def getStopReason(isAllowNonFastForwards: Boolean, command: ReceiveCommand, pusher: String)(implicit
 | 
			
		||||
      session: Session
 | 
			
		||||
    ): Option[String] = {
 | 
			
		||||
      if (enabled) {
 | 
			
		||||
        command.getType() match {
 | 
			
		||||
 
 | 
			
		||||
@@ -34,8 +34,8 @@ trait PullRequestService {
 | 
			
		||||
    with ActivityService =>
 | 
			
		||||
  import PullRequestService._
 | 
			
		||||
 | 
			
		||||
  def getPullRequest(owner: String, repository: String, issueId: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  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 =>
 | 
			
		||||
@@ -43,24 +43,24 @@ trait PullRequestService {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  def updateCommitId(owner: String, repository: String, issueId: Int, commitIdTo: String, commitIdFrom: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  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 updateDraftToPullRequest(owner: String, repository: String, issueId: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def updateDraftToPullRequest(owner: String, repository: String, issueId: Int)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit =
 | 
			
		||||
    PullRequests
 | 
			
		||||
      .filter(_.byPrimaryKey(owner, repository, issueId))
 | 
			
		||||
      .map(pr => pr.isDraft)
 | 
			
		||||
      .update(false)
 | 
			
		||||
 | 
			
		||||
  def updateBaseBranch(owner: String, repository: String, issueId: Int, baseBranch: String, commitIdTo: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def updateBaseBranch(owner: String, repository: String, issueId: Int, baseBranch: String, commitIdTo: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit = {
 | 
			
		||||
    PullRequests
 | 
			
		||||
      .filter(_.byPrimaryKey(owner, repository, issueId))
 | 
			
		||||
@@ -68,19 +68,18 @@ trait PullRequestService {
 | 
			
		||||
      .update((baseBranch, commitIdTo))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  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)
 | 
			
		||||
      .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 }
 | 
			
		||||
@@ -182,12 +181,11 @@ trait PullRequestService {
 | 
			
		||||
      .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)
 | 
			
		||||
      .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
 | 
			
		||||
@@ -200,12 +198,11 @@ trait PullRequestService {
 | 
			
		||||
      .on { (t1, t2) =>
 | 
			
		||||
        t1.byPrimaryKey(t2.userName, t2.repositoryName, t2.issueId)
 | 
			
		||||
      }
 | 
			
		||||
      .filter {
 | 
			
		||||
        case (t1, t2) =>
 | 
			
		||||
          (t1.requestUserName === userName.bind)
 | 
			
		||||
            .&&(t1.requestRepositoryName === repositoryName.bind)
 | 
			
		||||
            .&&(t1.branch === branch.bind)
 | 
			
		||||
            .&&(t2.closed === closed.get.bind, closed.isDefined)
 | 
			
		||||
      .filter { case (t1, t2) =>
 | 
			
		||||
        (t1.requestUserName === userName.bind)
 | 
			
		||||
          .&&(t1.requestRepositoryName === repositoryName.bind)
 | 
			
		||||
          .&&(t1.branch === branch.bind)
 | 
			
		||||
          .&&(t2.closed === closed.get.bind, closed.isDefined)
 | 
			
		||||
      }
 | 
			
		||||
      .map { case (t1, t2) => t1 }
 | 
			
		||||
      .list
 | 
			
		||||
@@ -217,22 +214,21 @@ trait PullRequestService {
 | 
			
		||||
   *   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
 | 
			
		||||
  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)
 | 
			
		||||
      .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
 | 
			
		||||
@@ -247,8 +243,8 @@ trait PullRequestService {
 | 
			
		||||
    pusherAccount: Account,
 | 
			
		||||
    action: String,
 | 
			
		||||
    settings: SystemSettings
 | 
			
		||||
  )(
 | 
			
		||||
    implicit s: Session,
 | 
			
		||||
  )(implicit
 | 
			
		||||
    s: Session,
 | 
			
		||||
    c: JsonFormat.Context
 | 
			
		||||
  ): Unit = {
 | 
			
		||||
    getPullRequestsByRequest(owner, repository, branch, Some(false)).foreach { pullreq =>
 | 
			
		||||
@@ -273,10 +269,9 @@ trait PullRequestService {
 | 
			
		||||
              (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
 | 
			
		||||
@@ -312,64 +307,63 @@ trait PullRequestService {
 | 
			
		||||
    body: Option[String],
 | 
			
		||||
    state: Option[String],
 | 
			
		||||
    base: Option[String]
 | 
			
		||||
  )(
 | 
			
		||||
    implicit s: Session,
 | 
			
		||||
  )(implicit
 | 
			
		||||
    s: Session,
 | 
			
		||||
    c: JsonFormat.Context
 | 
			
		||||
  ): Unit = {
 | 
			
		||||
    getPullRequest(repository.owner, repository.name, issueId).foreach {
 | 
			
		||||
      case (issue, pr) =>
 | 
			
		||||
        if (Repositories.filter(_.byRepository(pr.userName, pr.repositoryName)).exists.run) {
 | 
			
		||||
          // Update base branch
 | 
			
		||||
          base.foreach { _base =>
 | 
			
		||||
            if (pr.branch != _base) {
 | 
			
		||||
              Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
                getBranches(git, repository.repository.defaultBranch, origin = true)
 | 
			
		||||
                  .find(_.name == _base)
 | 
			
		||||
                  .foreach(br => updateBaseBranch(repository.owner, repository.name, issueId, br.name, br.commitId))
 | 
			
		||||
              }
 | 
			
		||||
              createComment(
 | 
			
		||||
                repository.owner,
 | 
			
		||||
                repository.name,
 | 
			
		||||
                loginAccount.userName,
 | 
			
		||||
                issue.issueId,
 | 
			
		||||
                pr.branch + "\r\n" + _base,
 | 
			
		||||
                "change_base_branch"
 | 
			
		||||
              )
 | 
			
		||||
    getPullRequest(repository.owner, repository.name, issueId).foreach { case (issue, pr) =>
 | 
			
		||||
      if (Repositories.filter(_.byRepository(pr.userName, pr.repositoryName)).exists.run) {
 | 
			
		||||
        // Update base branch
 | 
			
		||||
        base.foreach { _base =>
 | 
			
		||||
          if (pr.branch != _base) {
 | 
			
		||||
            Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
 | 
			
		||||
              getBranches(git, repository.repository.defaultBranch, origin = true)
 | 
			
		||||
                .find(_.name == _base)
 | 
			
		||||
                .foreach(br => updateBaseBranch(repository.owner, repository.name, issueId, br.name, br.commitId))
 | 
			
		||||
            }
 | 
			
		||||
            createComment(
 | 
			
		||||
              repository.owner,
 | 
			
		||||
              repository.name,
 | 
			
		||||
              loginAccount.userName,
 | 
			
		||||
              issue.issueId,
 | 
			
		||||
              pr.branch + "\r\n" + _base,
 | 
			
		||||
              "change_base_branch"
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
          // Update title and content
 | 
			
		||||
          title.foreach { _title =>
 | 
			
		||||
            updateIssue(repository.owner, repository.name, issueId, _title, body)
 | 
			
		||||
            if (issue.title != _title) {
 | 
			
		||||
              createComment(
 | 
			
		||||
                repository.owner,
 | 
			
		||||
                repository.name,
 | 
			
		||||
                loginAccount.userName,
 | 
			
		||||
                issue.issueId,
 | 
			
		||||
                issue.title + "\r\n" + _title,
 | 
			
		||||
                "change_title"
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          // Update state
 | 
			
		||||
          val action = (state, issue.closed) match {
 | 
			
		||||
            case (Some("open"), true) =>
 | 
			
		||||
              updateClosed(repository.owner, repository.name, issueId, closed = false)
 | 
			
		||||
              "reopened"
 | 
			
		||||
            case (Some("closed"), false) =>
 | 
			
		||||
              updateClosed(repository.owner, repository.name, issueId, closed = true)
 | 
			
		||||
              "closed"
 | 
			
		||||
            case _ => "edited"
 | 
			
		||||
          }
 | 
			
		||||
          // Call web hook
 | 
			
		||||
          callPullRequestWebHookByRequestBranch(
 | 
			
		||||
            action,
 | 
			
		||||
            getRepository(repository.owner, repository.name).get,
 | 
			
		||||
            pr.requestBranch,
 | 
			
		||||
            loginAccount,
 | 
			
		||||
            settings
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
        // Update title and content
 | 
			
		||||
        title.foreach { _title =>
 | 
			
		||||
          updateIssue(repository.owner, repository.name, issueId, _title, body)
 | 
			
		||||
          if (issue.title != _title) {
 | 
			
		||||
            createComment(
 | 
			
		||||
              repository.owner,
 | 
			
		||||
              repository.name,
 | 
			
		||||
              loginAccount.userName,
 | 
			
		||||
              issue.issueId,
 | 
			
		||||
              issue.title + "\r\n" + _title,
 | 
			
		||||
              "change_title"
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        // Update state
 | 
			
		||||
        val action = (state, issue.closed) match {
 | 
			
		||||
          case (Some("open"), true) =>
 | 
			
		||||
            updateClosed(repository.owner, repository.name, issueId, closed = false)
 | 
			
		||||
            "reopened"
 | 
			
		||||
          case (Some("closed"), false) =>
 | 
			
		||||
            updateClosed(repository.owner, repository.name, issueId, closed = true)
 | 
			
		||||
            "closed"
 | 
			
		||||
          case _ => "edited"
 | 
			
		||||
        }
 | 
			
		||||
        // Call web hook
 | 
			
		||||
        callPullRequestWebHookByRequestBranch(
 | 
			
		||||
          action,
 | 
			
		||||
          getRepository(repository.owner, repository.name).get,
 | 
			
		||||
          pr.requestBranch,
 | 
			
		||||
          loginAccount,
 | 
			
		||||
          settings
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -388,15 +382,14 @@ trait PullRequestService {
 | 
			
		||||
        .on { (t1, t2) =>
 | 
			
		||||
          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)
 | 
			
		||||
        .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
 | 
			
		||||
    }
 | 
			
		||||
@@ -412,63 +405,61 @@ trait PullRequestService {
 | 
			
		||||
 | 
			
		||||
    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 = convertLineSeparator(oldContent, "LF").split("\n")
 | 
			
		||||
                val newLines = convertLineSeparator(newContent, "LF").split("\n")
 | 
			
		||||
                file -> Option(DiffUtils.diff(oldLines.toList.asJava, newLines.toList.asJava))
 | 
			
		||||
              }
 | 
			
		||||
              case _ =>
 | 
			
		||||
                file -> None
 | 
			
		||||
    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 = convertLineSeparator(oldContent, "LF").split("\n")
 | 
			
		||||
              val newLines = convertLineSeparator(newContent, "LF").split("\n")
 | 
			
		||||
              file -> Option(DiffUtils.diff(oldLines.toList.asJava, newLines.toList.asJava))
 | 
			
		||||
            }
 | 
			
		||||
            case _ =>
 | 
			
		||||
              file -> None
 | 
			
		||||
          }
 | 
			
		||||
          .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(_.getSource.getPosition < newLine).foreach { delta =>
 | 
			
		||||
                      delta.getType match {
 | 
			
		||||
                        case DeltaType.CHANGE =>
 | 
			
		||||
                          if (delta.getSource.getPosition <= newLine - 1 && newLine <= delta.getSource.getPosition + delta.getTarget.getLines.size) {
 | 
			
		||||
                            counter = -1
 | 
			
		||||
                          } else {
 | 
			
		||||
                            counter = counter + (delta.getTarget.getLines.size - delta.getSource.getLines.size)
 | 
			
		||||
                          }
 | 
			
		||||
                        case DeltaType.INSERT => counter = counter + delta.getTarget.getLines.size
 | 
			
		||||
                        case DeltaType.DELETE => counter = counter - delta.getSource.getLines.size
 | 
			
		||||
                        case DeltaType.EQUAL  => // Do nothing
 | 
			
		||||
    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(_.getSource.getPosition < newLine).foreach { delta =>
 | 
			
		||||
                  delta.getType match {
 | 
			
		||||
                    case DeltaType.CHANGE =>
 | 
			
		||||
                      if (
 | 
			
		||||
                        delta.getSource.getPosition <= newLine - 1 && newLine <= delta.getSource.getPosition + delta.getTarget.getLines.size
 | 
			
		||||
                      ) {
 | 
			
		||||
                        counter = -1
 | 
			
		||||
                      } else {
 | 
			
		||||
                        counter = counter + (delta.getTarget.getLines.size - delta.getSource.getLines.size)
 | 
			
		||||
                      }
 | 
			
		||||
                    }
 | 
			
		||||
                    if (counter >= 0) {
 | 
			
		||||
                      updateCommitCommentPosition(commentId, newCommitId, None, Some(counter))
 | 
			
		||||
                    }
 | 
			
		||||
                    case DeltaType.INSERT => counter = counter + delta.getTarget.getLines.size
 | 
			
		||||
                    case DeltaType.DELETE => counter = counter - delta.getSource.getLines.size
 | 
			
		||||
                    case DeltaType.EQUAL  => // Do nothing
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
                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))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
          }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -540,20 +531,19 @@ trait PullRequestService {
 | 
			
		||||
      (commits, diffs)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  def getPullRequestComments(userName: String, repositoryName: String, issueId: Int, commits: Seq[CommitInfo])(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getPullRequestComments(userName: String, repositoryName: String, issueId: Int, commits: Seq[CommitInfo])(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Seq[Comment] = {
 | 
			
		||||
    (commits.flatMap(commit => getCommitComments(userName, repositoryName, commit.id, true)) ++ getComments(
 | 
			
		||||
      userName,
 | 
			
		||||
      repositoryName,
 | 
			
		||||
      issueId
 | 
			
		||||
    )).groupBy {
 | 
			
		||||
        case x: IssueComment                        => (Some(x.commentId), None, None, None)
 | 
			
		||||
        case x: CommitComment if x.fileName.isEmpty => (Some(x.commentId), None, None, None)
 | 
			
		||||
        case x: CommitComment                       => (None, x.fileName, x.originalOldLine, x.originalNewLine)
 | 
			
		||||
        case x                                      => throw new MatchError(x)
 | 
			
		||||
      }
 | 
			
		||||
      .toSeq
 | 
			
		||||
      case x: IssueComment                        => (Some(x.commentId), None, None, None)
 | 
			
		||||
      case x: CommitComment if x.fileName.isEmpty => (Some(x.commentId), None, None, None)
 | 
			
		||||
      case x: CommitComment                       => (None, x.fileName, x.originalOldLine, x.originalNewLine)
 | 
			
		||||
      case x                                      => throw new MatchError(x)
 | 
			
		||||
    }.toSeq
 | 
			
		||||
      .map {
 | 
			
		||||
        // Normal comment
 | 
			
		||||
        case ((Some(_), _, _, _), comments) =>
 | 
			
		||||
@@ -578,8 +568,8 @@ trait PullRequestService {
 | 
			
		||||
      .sortWith(_.registeredDate before _.registeredDate)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def markMergeAndClosePullRequest(userName: String, owner: String, repository: String, pull: PullRequest)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def markMergeAndClosePullRequest(userName: String, owner: String, repository: String, pull: PullRequest)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit = {
 | 
			
		||||
    createComment(owner, repository, userName, pull.issueId, "Merged by user", "merge")
 | 
			
		||||
    createComment(owner, repository, userName, pull.issueId, "Close", "close")
 | 
			
		||||
@@ -609,33 +599,32 @@ trait PullRequestService {
 | 
			
		||||
    Using.resources(
 | 
			
		||||
      Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
 | 
			
		||||
      Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
 | 
			
		||||
    ) {
 | 
			
		||||
      case (oldGit, newGit) =>
 | 
			
		||||
        if (originRepository.branchList.contains(originId)) {
 | 
			
		||||
          val forkedId2 =
 | 
			
		||||
            forkedRepository.tags.collectFirst { case x if x.name == forkedId => x.commitId }.getOrElse(forkedId)
 | 
			
		||||
    ) { case (oldGit, newGit) =>
 | 
			
		||||
      if (originRepository.branchList.contains(originId)) {
 | 
			
		||||
        val forkedId2 =
 | 
			
		||||
          forkedRepository.tags.collectFirst { case x if x.name == forkedId => x.commitId }.getOrElse(forkedId)
 | 
			
		||||
 | 
			
		||||
          val originId2 = JGitUtil.getForkedCommitId(
 | 
			
		||||
            oldGit,
 | 
			
		||||
            newGit,
 | 
			
		||||
            originRepository.owner,
 | 
			
		||||
            originRepository.name,
 | 
			
		||||
            originId,
 | 
			
		||||
            forkedRepository.owner,
 | 
			
		||||
            forkedRepository.name,
 | 
			
		||||
            forkedId2
 | 
			
		||||
          )
 | 
			
		||||
        val originId2 = JGitUtil.getForkedCommitId(
 | 
			
		||||
          oldGit,
 | 
			
		||||
          newGit,
 | 
			
		||||
          originRepository.owner,
 | 
			
		||||
          originRepository.name,
 | 
			
		||||
          originId,
 | 
			
		||||
          forkedRepository.owner,
 | 
			
		||||
          forkedRepository.name,
 | 
			
		||||
          forkedId2
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
          (Option(oldGit.getRepository.resolve(originId2)), Option(newGit.getRepository.resolve(forkedId2)))
 | 
			
		||||
        (Option(oldGit.getRepository.resolve(originId2)), Option(newGit.getRepository.resolve(forkedId2)))
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
          val originId2 =
 | 
			
		||||
            originRepository.tags.collectFirst { case x if x.name == originId => x.commitId }.getOrElse(originId)
 | 
			
		||||
          val forkedId2 =
 | 
			
		||||
            forkedRepository.tags.collectFirst { case x if x.name == forkedId => x.commitId }.getOrElse(forkedId)
 | 
			
		||||
      } else {
 | 
			
		||||
        val originId2 =
 | 
			
		||||
          originRepository.tags.collectFirst { case x if x.name == originId => x.commitId }.getOrElse(originId)
 | 
			
		||||
        val forkedId2 =
 | 
			
		||||
          forkedRepository.tags.collectFirst { case x if x.name == forkedId => x.commitId }.getOrElse(forkedId)
 | 
			
		||||
 | 
			
		||||
          (Option(oldGit.getRepository.resolve(originId2)), Option(newGit.getRepository.resolve(forkedId2)))
 | 
			
		||||
        }
 | 
			
		||||
        (Option(oldGit.getRepository.resolve(originId2)), Option(newGit.getRepository.resolve(forkedId2)))
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -661,8 +650,8 @@ object PullRequestService {
 | 
			
		||||
    val statuses: List[CommitStatus] =
 | 
			
		||||
      commitStatuses ++ (branchProtection.contexts.toSet -- commitStatuses.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 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
 | 
			
		||||
 
 | 
			
		||||
@@ -36,14 +36,14 @@ trait ReleaseService {
 | 
			
		||||
    ReleaseAssets.filter(x => x.byTag(owner, repository, tag)).list
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def getReleaseAssetsMap(owner: String, repository: String, releases: Seq[ReleaseTag])(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getReleaseAssetsMap(owner: String, repository: String, releases: Seq[ReleaseTag])(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Map[ReleaseTag, Seq[ReleaseAsset]] = {
 | 
			
		||||
    releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.tag))).toMap
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def getReleaseAsset(owner: String, repository: String, tag: String, fileId: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  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
 | 
			
		||||
  }
 | 
			
		||||
@@ -76,8 +76,8 @@ trait ReleaseService {
 | 
			
		||||
    ReleaseTags.filter(x => x.byRepository(owner, repository)).sortBy(x => x.updatedDate).list
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def getReleases(owner: String, repository: String, tags: Seq[JGitUtil.TagInfo])(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getReleases(owner: String, repository: String, tags: Seq[JGitUtil.TagInfo])(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Seq[ReleaseTag] = {
 | 
			
		||||
    ReleaseTags
 | 
			
		||||
      .filter(x => x.byRepository(owner, repository))
 | 
			
		||||
@@ -89,8 +89,8 @@ trait ReleaseService {
 | 
			
		||||
    ReleaseTags.filter(_.byTag(owner, repository, tag)).firstOption
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    ReleaseTags
 | 
			
		||||
      .filter(_.byPrimaryKey(owner, repository, tag))
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,8 @@ trait RepositoryCommitFileService {
 | 
			
		||||
      path,
 | 
			
		||||
      newFileName,
 | 
			
		||||
      oldFileName,
 | 
			
		||||
      if (content.nonEmpty) { content.getBytes(charset) } else { Array.emptyByteArray },
 | 
			
		||||
      if (content.nonEmpty) { content.getBytes(charset) }
 | 
			
		||||
      else { Array.emptyByteArray },
 | 
			
		||||
      message,
 | 
			
		||||
      commit,
 | 
			
		||||
      loginAccount,
 | 
			
		||||
 
 | 
			
		||||
@@ -92,7 +92,7 @@ trait RepositoryCreationService {
 | 
			
		||||
    RepositoryCreationService.startCreation(owner, name)
 | 
			
		||||
    try {
 | 
			
		||||
      Database() withTransaction { implicit session =>
 | 
			
		||||
        //val ownerAccount = getAccountByUserName(owner).get
 | 
			
		||||
        // val ownerAccount = getAccountByUserName(owner).get
 | 
			
		||||
        val loginUserName = loginAccount.userName
 | 
			
		||||
 | 
			
		||||
        val copyRepositoryDir = if (initOption == "COPY") {
 | 
			
		||||
@@ -214,9 +214,8 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -14,26 +14,25 @@ import scala.util.Using
 | 
			
		||||
trait RepositorySearchService { self: IssuesService =>
 | 
			
		||||
  import RepositorySearchService._
 | 
			
		||||
 | 
			
		||||
  def countIssues(owner: String, repository: String, query: String, pullRequest: Boolean)(
 | 
			
		||||
    implicit session: Session
 | 
			
		||||
  def countIssues(owner: String, repository: String, query: String, pullRequest: Boolean)(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): Int =
 | 
			
		||||
    searchIssuesByKeyword(owner, repository, query, pullRequest).length
 | 
			
		||||
 | 
			
		||||
  def searchIssues(owner: String, repository: String, query: String, pullRequest: Boolean)(
 | 
			
		||||
    implicit session: Session
 | 
			
		||||
  def searchIssues(owner: String, repository: String, query: String, pullRequest: Boolean)(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): List[IssueSearchResult] =
 | 
			
		||||
    searchIssuesByKeyword(owner, repository, query, pullRequest).map {
 | 
			
		||||
      case (issue, commentCount, content) =>
 | 
			
		||||
        IssueSearchResult(
 | 
			
		||||
          issue.issueId,
 | 
			
		||||
          issue.isPullRequest,
 | 
			
		||||
          issue.title,
 | 
			
		||||
          issue.closed,
 | 
			
		||||
          issue.openedUserName,
 | 
			
		||||
          issue.registeredDate,
 | 
			
		||||
          commentCount,
 | 
			
		||||
          getHighlightText(content, query)._1
 | 
			
		||||
        )
 | 
			
		||||
    searchIssuesByKeyword(owner, repository, query, pullRequest).map { case (issue, commentCount, content) =>
 | 
			
		||||
      IssueSearchResult(
 | 
			
		||||
        issue.issueId,
 | 
			
		||||
        issue.isPullRequest,
 | 
			
		||||
        issue.title,
 | 
			
		||||
        issue.closed,
 | 
			
		||||
        issue.openedUserName,
 | 
			
		||||
        issue.registeredDate,
 | 
			
		||||
        commentCount,
 | 
			
		||||
        getHighlightText(content, query)._1
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  def countFiles(owner: String, repository: String, query: String): Int =
 | 
			
		||||
@@ -48,10 +47,9 @@ trait RepositorySearchService { self: IssuesService =>
 | 
			
		||||
      } 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)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@@ -68,15 +66,14 @@ trait RepositorySearchService { self: IssuesService =>
 | 
			
		||||
      } 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
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -187,12 +187,11 @@ trait RepositoryService {
 | 
			
		||||
          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
 | 
			
		||||
            issueLabels.map(x =>
 | 
			
		||||
              x.copy(
 | 
			
		||||
                labelId = newLabelMap(oldLabelMap(x.labelId)),
 | 
			
		||||
                userName = newUserName,
 | 
			
		||||
                repositoryName = newRepositoryName
 | 
			
		||||
              )
 | 
			
		||||
            ): _*
 | 
			
		||||
          )
 | 
			
		||||
@@ -275,12 +274,11 @@ trait RepositoryService {
 | 
			
		||||
        (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
 | 
			
		||||
@@ -292,12 +290,11 @@ trait RepositoryService {
 | 
			
		||||
        (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)
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -321,31 +318,29 @@ trait RepositoryService {
 | 
			
		||||
    (Repositories
 | 
			
		||||
      .join(Accounts)
 | 
			
		||||
      .on(_.userName === _.userName)
 | 
			
		||||
      .filter {
 | 
			
		||||
        case (t1, t2) =>
 | 
			
		||||
          t1.byRepository(userName, repositoryName) && t2.removed === false.bind
 | 
			
		||||
    } firstOption) map {
 | 
			
		||||
      case (repository, account) =>
 | 
			
		||||
        // 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
 | 
			
		||||
      .filter { case (t1, t2) =>
 | 
			
		||||
        t1.byRepository(userName, repositoryName) && t2.removed === false.bind
 | 
			
		||||
      } firstOption) map { case (repository, account) =>
 | 
			
		||||
      // 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
 | 
			
		||||
 | 
			
		||||
        new RepositoryInfo(
 | 
			
		||||
          JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName),
 | 
			
		||||
          repository,
 | 
			
		||||
          issues.count(_ == false),
 | 
			
		||||
          issues.count(_ == true),
 | 
			
		||||
          getForkedCount(
 | 
			
		||||
            repository.originUserName.getOrElse(repository.userName),
 | 
			
		||||
            repository.originRepositoryName.getOrElse(repository.repositoryName)
 | 
			
		||||
          ),
 | 
			
		||||
          getOpenMilestones(repository.userName, repository.repositoryName),
 | 
			
		||||
          getRepositoryManagers(repository.userName, repository.repositoryName)
 | 
			
		||||
        )
 | 
			
		||||
      new RepositoryInfo(
 | 
			
		||||
        JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName),
 | 
			
		||||
        repository,
 | 
			
		||||
        issues.count(_ == false),
 | 
			
		||||
        issues.count(_ == true),
 | 
			
		||||
        getForkedCount(
 | 
			
		||||
          repository.originUserName.getOrElse(repository.userName),
 | 
			
		||||
          repository.originRepositoryName.getOrElse(repository.repositoryName)
 | 
			
		||||
        ),
 | 
			
		||||
        getOpenMilestones(repository.userName, repository.repositoryName),
 | 
			
		||||
        getRepositoryManagers(repository.userName, repository.repositoryName)
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -396,8 +391,8 @@ trait RepositoryService {
 | 
			
		||||
   * Returns the list of repositories which are owned by the specified user.
 | 
			
		||||
   * This list includes group repositories if the specified user is a member of the group.
 | 
			
		||||
   */
 | 
			
		||||
  def getUserRepositories(userName: String, withoutPhysicalInfo: Boolean = false)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getUserRepositories(userName: String, withoutPhysicalInfo: Boolean = false)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[RepositoryInfo] = {
 | 
			
		||||
    Repositories
 | 
			
		||||
      .filter { t1 =>
 | 
			
		||||
@@ -464,15 +459,14 @@ trait RepositoryService {
 | 
			
		||||
        Repositories
 | 
			
		||||
          .join(Accounts)
 | 
			
		||||
          .on(_.userName === _.userName)
 | 
			
		||||
          .filter {
 | 
			
		||||
            case (t1, t2) =>
 | 
			
		||||
              (t2.removed === false.bind) && ((t1.isPrivate === false.bind && !limit.bind) || (t1.userName === x.userName) ||
 | 
			
		||||
                (t1.userName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)) ||
 | 
			
		||||
                (Collaborators.filter { t3 =>
 | 
			
		||||
                  t3.byRepository(t1.userName, t1.repositoryName) &&
 | 
			
		||||
                  ((t3.collaboratorName === x.userName.bind) ||
 | 
			
		||||
          .filter { case (t1, t2) =>
 | 
			
		||||
            (t2.removed === false.bind) && ((t1.isPrivate === false.bind && !limit.bind) || (t1.userName === x.userName) ||
 | 
			
		||||
              (t1.userName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)) ||
 | 
			
		||||
              (Collaborators.filter { t3 =>
 | 
			
		||||
                t3.byRepository(t1.userName, t1.repositoryName) &&
 | 
			
		||||
                ((t3.collaboratorName === x.userName.bind) ||
 | 
			
		||||
                  (t3.collaboratorName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)))
 | 
			
		||||
                } exists))
 | 
			
		||||
              } exists))
 | 
			
		||||
          }
 | 
			
		||||
          .map { case (t1, t2) => t1 }
 | 
			
		||||
      // for Guests
 | 
			
		||||
@@ -483,17 +477,16 @@ trait RepositoryService {
 | 
			
		||||
          .filter { case (t1, t2) => t1.isPrivate === false.bind && t2.removed === false.bind }
 | 
			
		||||
          .map { case (t1, t2) => t1 }
 | 
			
		||||
    }).filter { t =>
 | 
			
		||||
        repositoryUserName.map { userName =>
 | 
			
		||||
          t.userName === userName.bind
 | 
			
		||||
        } getOrElse LiteralColumn(true)
 | 
			
		||||
      }
 | 
			
		||||
      .sortBy(_.lastActivityDate desc)
 | 
			
		||||
      repositoryUserName.map { userName =>
 | 
			
		||||
        t.userName === userName.bind
 | 
			
		||||
      } getOrElse LiteralColumn(true)
 | 
			
		||||
    }.sortBy(_.lastActivityDate desc)
 | 
			
		||||
      .list
 | 
			
		||||
      .map(createRepositoryInfo(_, withoutPhysicalInfo))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private def createRepositoryInfo(repository: Repository, withoutPhysicalInfo: Boolean = false)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  private def createRepositoryInfo(repository: Repository, withoutPhysicalInfo: Boolean = false)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): RepositoryInfo = {
 | 
			
		||||
    new RepositoryInfo(
 | 
			
		||||
      if (withoutPhysicalInfo) {
 | 
			
		||||
@@ -586,8 +579,8 @@ trait RepositoryService {
 | 
			
		||||
      )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def saveRepositoryDefaultBranch(userName: String, repositoryName: String, defaultBranch: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def saveRepositoryDefaultBranch(userName: String, repositoryName: String, defaultBranch: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit =
 | 
			
		||||
    Repositories
 | 
			
		||||
      .filter(_.byRepository(userName, repositoryName))
 | 
			
		||||
@@ -599,16 +592,16 @@ trait RepositoryService {
 | 
			
		||||
  /**
 | 
			
		||||
   * Add collaborator (user or group) to the repository.
 | 
			
		||||
   */
 | 
			
		||||
  def addCollaborator(userName: String, repositoryName: String, collaboratorName: String, role: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def addCollaborator(userName: String, repositoryName: String, collaboratorName: String, role: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit =
 | 
			
		||||
    Collaborators insert Collaborator(userName, repositoryName, collaboratorName, role)
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Remove specified collaborator from the repository.
 | 
			
		||||
   */
 | 
			
		||||
  def removeCollaborator(userName: String, repositoryName: String, collaboratorName: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def removeCollaborator(userName: String, repositoryName: String, collaboratorName: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Unit =
 | 
			
		||||
    Collaborators.filter(_.byPrimaryKey(userName, repositoryName, collaboratorName)).delete
 | 
			
		||||
 | 
			
		||||
@@ -634,8 +627,8 @@ trait RepositoryService {
 | 
			
		||||
   * Returns the list of all collaborator name and permission which is sorted with ascending order.
 | 
			
		||||
   * 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
 | 
			
		||||
  def getCollaboratorUserNames(userName: String, repositoryName: String, filter: Seq[Role] = Nil)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[String] = {
 | 
			
		||||
    val q1 = Collaborators
 | 
			
		||||
      .join(Accounts)
 | 
			
		||||
@@ -669,8 +662,8 @@ trait RepositoryService {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def hasDeveloperRole(owner: String, repository: String, loginAccount: Option[Account])(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def hasDeveloperRole(owner: String, repository: String, loginAccount: Option[Account])(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Boolean = {
 | 
			
		||||
    loginAccount match {
 | 
			
		||||
      case Some(a) if (a.isAdmin)                                               => true
 | 
			
		||||
@@ -731,7 +724,7 @@ trait RepositoryService {
 | 
			
		||||
        (t.originUserName === userName.bind) && (t.originRepositoryName === repositoryName.bind)
 | 
			
		||||
      }
 | 
			
		||||
      .sortBy(_.userName asc)
 | 
			
		||||
      .list //.map(t => t.userName -> t.repositoryName).list
 | 
			
		||||
      .list // .map(t => t.userName -> t.repositoryName).list
 | 
			
		||||
 | 
			
		||||
  private val templateExtensions = Seq("md", "markdown")
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,8 +25,8 @@ trait RequestCache
 | 
			
		||||
  private implicit def context2Session(implicit context: Context): Session =
 | 
			
		||||
    request2Session(context.request)
 | 
			
		||||
 | 
			
		||||
  def getIssueFromCache(userName: String, repositoryName: String, issueId: String)(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  def getIssueFromCache(userName: String, repositoryName: String, issueId: String)(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Option[Issue] = {
 | 
			
		||||
    context.cache(s"issue.${userName}/${repositoryName}#${issueId}") {
 | 
			
		||||
      super.getIssue(userName, repositoryName, issueId)
 | 
			
		||||
@@ -45,19 +45,18 @@ trait RequestCache
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def getRepositoryInfoFromCache(userName: String, repositoryName: String)(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  def getRepositoryInfoFromCache(userName: String, repositoryName: String)(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Option[Repository] = {
 | 
			
		||||
    context.cache(s"repository.${userName}/${repositoryName}") {
 | 
			
		||||
      Repositories
 | 
			
		||||
        .join(Accounts)
 | 
			
		||||
        .on(_.userName === _.userName)
 | 
			
		||||
        .filter {
 | 
			
		||||
          case (t1, t2) =>
 | 
			
		||||
            t1.byRepository(userName, repositoryName) && t2.removed === false.bind
 | 
			
		||||
        .filter { case (t1, t2) =>
 | 
			
		||||
          t1.byRepository(userName, repositoryName) && t2.removed === false.bind
 | 
			
		||||
        }
 | 
			
		||||
        .map {
 | 
			
		||||
          case (t1, t2) => t1
 | 
			
		||||
        .map { case (t1, t2) =>
 | 
			
		||||
          t1
 | 
			
		||||
        }
 | 
			
		||||
        .firstOption
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -45,8 +45,8 @@ trait WebHookService {
 | 
			
		||||
  private val logger = LoggerFactory.getLogger(classOf[WebHookService])
 | 
			
		||||
 | 
			
		||||
  /** get All WebHook informations of repository */
 | 
			
		||||
  def getWebHooks(owner: String, repository: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getWebHooks(owner: String, repository: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[(RepositoryWebHook, Set[WebHook.Event])] =
 | 
			
		||||
    RepositoryWebHooks
 | 
			
		||||
      .filter(_.byRepository(owner, repository))
 | 
			
		||||
@@ -63,8 +63,8 @@ trait WebHookService {
 | 
			
		||||
      .sortBy(_._1.url)
 | 
			
		||||
 | 
			
		||||
  /** get All WebHook informations of repository event */
 | 
			
		||||
  def getWebHooksByEvent(owner: String, repository: String, event: WebHook.Event)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getWebHooksByEvent(owner: String, repository: String, event: WebHook.Event)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): List[RepositoryWebHook] =
 | 
			
		||||
    RepositoryWebHooks
 | 
			
		||||
      .filter(_.byRepository(owner, repository))
 | 
			
		||||
@@ -78,8 +78,8 @@ trait WebHookService {
 | 
			
		||||
      .distinct
 | 
			
		||||
 | 
			
		||||
  /** get All WebHook information from repository to url */
 | 
			
		||||
  def getWebHook(owner: String, repository: String, url: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getWebHook(owner: String, repository: String, url: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[(RepositoryWebHook, Set[WebHook.Event])] =
 | 
			
		||||
    RepositoryWebHooks
 | 
			
		||||
      .filter(_.byRepositoryUrl(owner, repository, url))
 | 
			
		||||
@@ -95,8 +95,8 @@ trait WebHookService {
 | 
			
		||||
      .headOption
 | 
			
		||||
 | 
			
		||||
  /** get All WebHook informations of repository */
 | 
			
		||||
  def getWebHookById(id: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  def getWebHookById(id: Int)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[(RepositoryWebHook, Set[WebHook.Event])] =
 | 
			
		||||
    RepositoryWebHooks
 | 
			
		||||
      .filter(_.byId(id))
 | 
			
		||||
@@ -451,8 +451,8 @@ trait WebHookPullRequestService extends WebHookService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @return Map[(issue, issueUser, pullRequest, baseOwner, headOwner), webHooks] */
 | 
			
		||||
  def getPullRequestsByRequestForWebhook(userName: String, repositoryName: String, branch: String)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  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
 | 
			
		||||
@@ -652,8 +652,8 @@ object WebHookService {
 | 
			
		||||
  ) 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 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}")
 | 
			
		||||
    }
 | 
			
		||||
@@ -878,15 +878,14 @@ 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -20,8 +20,10 @@ abstract class ControllerFilter extends Filter {
 | 
			
		||||
      requestPath + "/"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!checkPath.startsWith("/upload/") && !checkPath.startsWith("/git/") && !checkPath.startsWith("/git-lfs/") &&
 | 
			
		||||
        !checkPath.startsWith("/assets/") && !checkPath.startsWith("/plugin-assets/")) {
 | 
			
		||||
    if (
 | 
			
		||||
      !checkPath.startsWith("/upload/") && !checkPath.startsWith("/git/") && !checkPath.startsWith("/git-lfs/") &&
 | 
			
		||||
      !checkPath.startsWith("/assets/") && !checkPath.startsWith("/plugin-assets/")
 | 
			
		||||
    ) {
 | 
			
		||||
      val continue = process(request, response, checkPath)
 | 
			
		||||
      if (!continue) {
 | 
			
		||||
        return ()
 | 
			
		||||
@@ -41,33 +43,29 @@ class CompositeScalatraFilter extends ControllerFilter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override def init(filterConfig: FilterConfig): Unit = {
 | 
			
		||||
    filters.foreach {
 | 
			
		||||
      case (filter, _) =>
 | 
			
		||||
        filter.init(filterConfig)
 | 
			
		||||
    filters.foreach { case (filter, _) =>
 | 
			
		||||
      filter.init(filterConfig)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override def destroy(): Unit = {
 | 
			
		||||
    filters.foreach {
 | 
			
		||||
      case (filter, _) =>
 | 
			
		||||
        filter.destroy()
 | 
			
		||||
    filters.foreach { case (filter, _) =>
 | 
			
		||||
      filter.destroy()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override def process(request: ServletRequest, response: ServletResponse, checkPath: String): Boolean = {
 | 
			
		||||
    filters
 | 
			
		||||
      .filter {
 | 
			
		||||
        case (_, path) =>
 | 
			
		||||
          val start = path.replaceFirst("/\\*$", "/")
 | 
			
		||||
          checkPath.startsWith(start)
 | 
			
		||||
      .filter { case (_, path) =>
 | 
			
		||||
        val start = path.replaceFirst("/\\*$", "/")
 | 
			
		||||
        checkPath.startsWith(start)
 | 
			
		||||
      }
 | 
			
		||||
      .foreach {
 | 
			
		||||
        case (filter, _) =>
 | 
			
		||||
          val mockChain = new MockFilterChain()
 | 
			
		||||
          filter.doFilter(request, response, mockChain)
 | 
			
		||||
          if (mockChain.continue == false) {
 | 
			
		||||
            return false
 | 
			
		||||
          }
 | 
			
		||||
      .foreach { case (filter, _) =>
 | 
			
		||||
        val mockChain = new MockFilterChain()
 | 
			
		||||
        filter.doFilter(request, response, mockChain)
 | 
			
		||||
        if (mockChain.continue == false) {
 | 
			
		||||
          return false
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    true
 | 
			
		||||
 
 | 
			
		||||
@@ -43,10 +43,9 @@ class GitAuthenticationFilter extends Filter with RepositoryService with Account
 | 
			
		||||
    try {
 | 
			
		||||
      PluginRegistry()
 | 
			
		||||
        .getRepositoryRouting(request.gitRepositoryPath)
 | 
			
		||||
        .map {
 | 
			
		||||
          case GitRepositoryRouting(_, _, filter) =>
 | 
			
		||||
            // served by plug-ins
 | 
			
		||||
            pluginRepository(request, wrappedResponse, chain, settings, isUpdating, filter)
 | 
			
		||||
        .map { case GitRepositoryRouting(_, _, filter) =>
 | 
			
		||||
          // served by plug-ins
 | 
			
		||||
          pluginRepository(request, wrappedResponse, chain, settings, isUpdating, filter)
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        .getOrElse {
 | 
			
		||||
@@ -130,19 +129,17 @@ class GitAuthenticationFilter extends Filter with RepositoryService with Account
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            case None =>
 | 
			
		||||
              () =>
 | 
			
		||||
                {
 | 
			
		||||
                  logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.")
 | 
			
		||||
                  response.sendError(HttpServletResponse.SC_NOT_FOUND)
 | 
			
		||||
                }
 | 
			
		||||
              () => {
 | 
			
		||||
                logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.")
 | 
			
		||||
                response.sendError(HttpServletResponse.SC_NOT_FOUND)
 | 
			
		||||
              }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      case _ =>
 | 
			
		||||
        () =>
 | 
			
		||||
          {
 | 
			
		||||
            logger.debug(s"Not enough path arguments: ${request.paths.mkString(", ")}")
 | 
			
		||||
            response.sendError(HttpServletResponse.SC_NOT_FOUND)
 | 
			
		||||
          }
 | 
			
		||||
        () => {
 | 
			
		||||
          logger.debug(s"Not enough path arguments: ${request.paths.mkString(", ")}")
 | 
			
		||||
          response.sendError(HttpServletResponse.SC_NOT_FOUND)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    action()
 | 
			
		||||
@@ -159,8 +156,8 @@ class GitAuthenticationFilter extends Filter with RepositoryService with Account
 | 
			
		||||
   * @param s database session
 | 
			
		||||
   * @return an account or none
 | 
			
		||||
   */
 | 
			
		||||
  private def authenticateByHeader(authorizationHeader: String, settings: SystemSettings)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  private def authenticateByHeader(authorizationHeader: String, settings: SystemSettings)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Option[Account] = {
 | 
			
		||||
    val Array(username, password) = AuthUtil.decodeAuthHeader(authorizationHeader).split(":", 2)
 | 
			
		||||
    authenticate(settings, username, password).orElse {
 | 
			
		||||
 
 | 
			
		||||
@@ -171,10 +171,9 @@ class GitBucketRepositoryResolver extends RepositoryResolver[HttpServletRequest]
 | 
			
		||||
    // Rewrite repository path if routing is marched
 | 
			
		||||
    PluginRegistry()
 | 
			
		||||
      .getRepositoryRouting("/" + name)
 | 
			
		||||
      .map {
 | 
			
		||||
        case GitRepositoryRouting(urlPattern, localPath, _) =>
 | 
			
		||||
          val path = urlPattern.r.replaceFirstIn(name, localPath)
 | 
			
		||||
          new FileRepository(new File(Directory.GitBucketHome, path))
 | 
			
		||||
      .map { case GitRepositoryRouting(urlPattern, localPath, _) =>
 | 
			
		||||
        val path = urlPattern.r.replaceFirstIn(name, localPath)
 | 
			
		||||
        new FileRepository(new File(Directory.GitBucketHome, path))
 | 
			
		||||
      }
 | 
			
		||||
      .getOrElse {
 | 
			
		||||
        new FileRepository(new File(Directory.RepositoryHome, name))
 | 
			
		||||
@@ -341,7 +340,11 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
 | 
			
		||||
                  pushedIds.add(commit.id)
 | 
			
		||||
                  createIssueComment(owner, repository, commit)
 | 
			
		||||
                  // close issues
 | 
			
		||||
                  if (refName(1) == "heads" && branchName == defaultBranch && command.getType == ReceiveCommand.Type.UPDATE) {
 | 
			
		||||
                  if (
 | 
			
		||||
                    refName(
 | 
			
		||||
                      1
 | 
			
		||||
                    ) == "heads" && branchName == defaultBranch && command.getType == ReceiveCommand.Type.UPDATE
 | 
			
		||||
                  ) {
 | 
			
		||||
                    getAccountByUserName(pusher).foreach { pusherAccount =>
 | 
			
		||||
                      closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository).foreach { issueId =>
 | 
			
		||||
                        getIssue(owner, repository, issueId.toString).foreach { issue =>
 | 
			
		||||
@@ -362,9 +365,11 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
 | 
			
		||||
            // set PR as merged
 | 
			
		||||
            val pulls = getPullRequestsByBranch(owner, repository, branchName, Some(false))
 | 
			
		||||
            pulls.foreach { pull =>
 | 
			
		||||
              if (commits.exists { c =>
 | 
			
		||||
                    c.id == pull.commitIdTo
 | 
			
		||||
                  }) {
 | 
			
		||||
              if (
 | 
			
		||||
                commits.exists { c =>
 | 
			
		||||
                  c.id == pull.commitIdTo
 | 
			
		||||
                }
 | 
			
		||||
              ) {
 | 
			
		||||
                markMergeAndClosePullRequest(pusher, owner, repository, pull)
 | 
			
		||||
                getAccountByUserName(pusher).foreach { pusherAccount =>
 | 
			
		||||
                  callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount, settings)
 | 
			
		||||
@@ -490,40 +495,38 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          commitIds.foreach {
 | 
			
		||||
            case (oldCommitId, newCommitId) =>
 | 
			
		||||
              val commits = Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
 | 
			
		||||
                JGitUtil.getCommitLog(git, oldCommitId, newCommitId).flatMap { commit =>
 | 
			
		||||
                  val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
 | 
			
		||||
                  diffs.collect {
 | 
			
		||||
                    case diff if diff.newPath.toLowerCase.endsWith(".md") =>
 | 
			
		||||
                      val action = mapToAction(diff.changeType)
 | 
			
		||||
                      val fileName = diff.newPath
 | 
			
		||||
                      updateLastActivityDate(owner, repository)
 | 
			
		||||
                      buildWikiRecord(action, owner, repository, commit, fileName).foreach(recordActivity)
 | 
			
		||||
                      (action, fileName, commit.id)
 | 
			
		||||
                  }
 | 
			
		||||
          commitIds.foreach { case (oldCommitId, newCommitId) =>
 | 
			
		||||
            val commits = Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
 | 
			
		||||
              JGitUtil.getCommitLog(git, oldCommitId, newCommitId).flatMap { commit =>
 | 
			
		||||
                val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
 | 
			
		||||
                diffs.collect {
 | 
			
		||||
                  case diff if diff.newPath.toLowerCase.endsWith(".md") =>
 | 
			
		||||
                    val action = mapToAction(diff.changeType)
 | 
			
		||||
                    val fileName = diff.newPath
 | 
			
		||||
                    updateLastActivityDate(owner, repository)
 | 
			
		||||
                    buildWikiRecord(action, owner, repository, commit, fileName).foreach(recordActivity)
 | 
			
		||||
                    (action, fileName, commit.id)
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
              val pages = commits
 | 
			
		||||
                .groupBy { case (_, fileName, _) => fileName }
 | 
			
		||||
                .map {
 | 
			
		||||
                  case (fileName, commits) =>
 | 
			
		||||
                    val (commitHeadAction, _, _) = commits.head
 | 
			
		||||
                    val (_, _, commitLastId) = commits.last
 | 
			
		||||
                    (commitHeadAction, fileName, commitLastId)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
              callWebHookOf(owner, repository, WebHook.Gollum, settings) {
 | 
			
		||||
                for {
 | 
			
		||||
                  pusherAccount <- getAccountByUserName(pusher)
 | 
			
		||||
                  repositoryUser <- getAccountByUserName(owner)
 | 
			
		||||
                  repositoryInfo <- getRepository(owner, repository)
 | 
			
		||||
                } yield {
 | 
			
		||||
                  WebHookGollumPayload(pages.toSeq, repositoryInfo, repositoryUser, pusherAccount)
 | 
			
		||||
                }
 | 
			
		||||
            val pages = commits
 | 
			
		||||
              .groupBy { case (_, fileName, _) => fileName }
 | 
			
		||||
              .map { case (fileName, commits) =>
 | 
			
		||||
                val (commitHeadAction, _, _) = commits.head
 | 
			
		||||
                val (_, _, commitLastId) = commits.last
 | 
			
		||||
                (commitHeadAction, fileName, commitLastId)
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
            callWebHookOf(owner, repository, WebHook.Gollum, settings) {
 | 
			
		||||
              for {
 | 
			
		||||
                pusherAccount <- getAccountByUserName(pusher)
 | 
			
		||||
                repositoryUser <- getAccountByUserName(owner)
 | 
			
		||||
                repositoryInfo <- getRepository(owner, repository)
 | 
			
		||||
              } yield {
 | 
			
		||||
                WebHookGollumPayload(pages.toSeq, repositoryInfo, repositoryUser, pusherAccount)
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } catch {
 | 
			
		||||
 
 | 
			
		||||
@@ -153,8 +153,8 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
 | 
			
		||||
                  logger.info(s"Extract to ${file.getAbsolutePath}")
 | 
			
		||||
 | 
			
		||||
                  FileUtils.forceMkdirParent(file)
 | 
			
		||||
                  Using.resources(in, new FileOutputStream(file)) {
 | 
			
		||||
                    case (in, out) => IOUtils.copy(in, out)
 | 
			
		||||
                  Using.resources(in, new FileOutputStream(file)) { case (in, out) =>
 | 
			
		||||
                    IOUtils.copy(in, out)
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              case _ => ()
 | 
			
		||||
 
 | 
			
		||||
@@ -16,32 +16,31 @@ class PluginAssetsServlet extends HttpServlet {
 | 
			
		||||
 | 
			
		||||
    assetsMappings
 | 
			
		||||
      .find { case (prefix, _, _) => path.startsWith("/plugin-assets" + prefix) }
 | 
			
		||||
      .foreach {
 | 
			
		||||
        case (prefix, resourcePath, classLoader) =>
 | 
			
		||||
          val ifNoneMatch = req.getHeader("If-None-Match")
 | 
			
		||||
          PluginRegistry.getPluginInfoFromClassLoader(classLoader).map { info =>
 | 
			
		||||
            val etag = s""""${info.pluginJar.lastModified}"""" // ETag must wrapped with double quote
 | 
			
		||||
            if (ifNoneMatch == etag) {
 | 
			
		||||
              resp.setStatus(304)
 | 
			
		||||
            } else {
 | 
			
		||||
              val resourceName = path.substring(("/plugin-assets" + prefix).length)
 | 
			
		||||
              Option(classLoader.getResourceAsStream(resourcePath.stripPrefix("/") + resourceName))
 | 
			
		||||
                .map { in =>
 | 
			
		||||
                  try {
 | 
			
		||||
                    val bytes = IOUtils.toByteArray(in)
 | 
			
		||||
                    resp.setContentLength(bytes.length)
 | 
			
		||||
                    resp.setContentType(FileUtil.getMimeType(path, bytes))
 | 
			
		||||
                    resp.setHeader("ETag", etag)
 | 
			
		||||
                    resp.getOutputStream.write(bytes)
 | 
			
		||||
                  } finally {
 | 
			
		||||
                    in.close()
 | 
			
		||||
                  }
 | 
			
		||||
      .foreach { case (prefix, resourcePath, classLoader) =>
 | 
			
		||||
        val ifNoneMatch = req.getHeader("If-None-Match")
 | 
			
		||||
        PluginRegistry.getPluginInfoFromClassLoader(classLoader).map { info =>
 | 
			
		||||
          val etag = s""""${info.pluginJar.lastModified}"""" // ETag must wrapped with double quote
 | 
			
		||||
          if (ifNoneMatch == etag) {
 | 
			
		||||
            resp.setStatus(304)
 | 
			
		||||
          } else {
 | 
			
		||||
            val resourceName = path.substring(("/plugin-assets" + prefix).length)
 | 
			
		||||
            Option(classLoader.getResourceAsStream(resourcePath.stripPrefix("/") + resourceName))
 | 
			
		||||
              .map { in =>
 | 
			
		||||
                try {
 | 
			
		||||
                  val bytes = IOUtils.toByteArray(in)
 | 
			
		||||
                  resp.setContentLength(bytes.length)
 | 
			
		||||
                  resp.setContentType(FileUtil.getMimeType(path, bytes))
 | 
			
		||||
                  resp.setHeader("ETag", etag)
 | 
			
		||||
                  resp.getOutputStream.write(bytes)
 | 
			
		||||
                } finally {
 | 
			
		||||
                  in.close()
 | 
			
		||||
                }
 | 
			
		||||
                .getOrElse {
 | 
			
		||||
                  resp.setStatus(404)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
              }
 | 
			
		||||
              .getOrElse {
 | 
			
		||||
                resp.setStatus(404)
 | 
			
		||||
              }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,32 +14,29 @@ class PluginControllerFilter extends ControllerFilter {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override def destroy(): Unit = {
 | 
			
		||||
    PluginRegistry().getControllers().foreach {
 | 
			
		||||
      case (controller, _) =>
 | 
			
		||||
        controller.destroy()
 | 
			
		||||
    PluginRegistry().getControllers().foreach { case (controller, _) =>
 | 
			
		||||
      controller.destroy()
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override def process(request: ServletRequest, response: ServletResponse, checkPath: String): Boolean = {
 | 
			
		||||
    PluginRegistry()
 | 
			
		||||
      .getControllers()
 | 
			
		||||
      .filter {
 | 
			
		||||
        case (_, path) =>
 | 
			
		||||
          val start = path.replaceFirst("/\\*$", "/")
 | 
			
		||||
          checkPath.startsWith(start)
 | 
			
		||||
      .filter { case (_, path) =>
 | 
			
		||||
        val start = path.replaceFirst("/\\*$", "/")
 | 
			
		||||
        checkPath.startsWith(start)
 | 
			
		||||
      }
 | 
			
		||||
      .foreach {
 | 
			
		||||
        case (controller, _) =>
 | 
			
		||||
          controller match {
 | 
			
		||||
            case x: ControllerBase if (x.config == null) => x.init(filterConfig)
 | 
			
		||||
            case _                                       => ()
 | 
			
		||||
          }
 | 
			
		||||
          val mockChain = new MockFilterChain()
 | 
			
		||||
          controller.doFilter(request, response, mockChain)
 | 
			
		||||
      .foreach { case (controller, _) =>
 | 
			
		||||
        controller match {
 | 
			
		||||
          case x: ControllerBase if (x.config == null) => x.init(filterConfig)
 | 
			
		||||
          case _                                       => ()
 | 
			
		||||
        }
 | 
			
		||||
        val mockChain = new MockFilterChain()
 | 
			
		||||
        controller.doFilter(request, response, mockChain)
 | 
			
		||||
 | 
			
		||||
          if (mockChain.continue == false) {
 | 
			
		||||
            return false
 | 
			
		||||
          }
 | 
			
		||||
        if (mockChain.continue == false) {
 | 
			
		||||
          return false
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    true
 | 
			
		||||
 
 | 
			
		||||
@@ -101,8 +101,8 @@ abstract class DefaultGitCommand(val owner: String, val repoName: String) extend
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected def isReadableUser(authType: AuthType, repositoryInfo: RepositoryService.RepositoryInfo)(
 | 
			
		||||
    implicit session: Session
 | 
			
		||||
  protected def isReadableUser(authType: AuthType, repositoryInfo: RepositoryService.RepositoryInfo)(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): Boolean = {
 | 
			
		||||
    authType match {
 | 
			
		||||
      case AuthType.UserAuthType(username) => {
 | 
			
		||||
@@ -120,8 +120,8 @@ abstract class DefaultGitCommand(val owner: String, val repoName: String) extend
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected def isWritableUser(authType: AuthType, repositoryInfo: RepositoryService.RepositoryInfo)(
 | 
			
		||||
    implicit session: Session
 | 
			
		||||
  protected def isWritableUser(authType: AuthType, repositoryInfo: RepositoryService.RepositoryInfo)(implicit
 | 
			
		||||
    session: Session
 | 
			
		||||
  ): Boolean = {
 | 
			
		||||
    authType match {
 | 
			
		||||
      case AuthType.UserAuthType(username) => {
 | 
			
		||||
 
 | 
			
		||||
@@ -56,8 +56,8 @@ class PublicKeyAuthenticator(genericUser: String)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private def authenticateLoginUser(userName: String, key: PublicKey, session: ServerSession)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  private def authenticateLoginUser(userName: String, key: PublicKey, session: ServerSession)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Boolean = {
 | 
			
		||||
    val authenticated = getPublicKeys(userName).map(_.publicKey).flatMap(SshUtil.str2PublicKey).contains(key)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ object DatabaseConfig {
 | 
			
		||||
    ConfigFactory.parseFile(file)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private lazy val dbUrl = getValue("db.url", config.getString) //config.getString("db.url")
 | 
			
		||||
  private lazy val dbUrl = getValue("db.url", config.getString) // config.getString("db.url")
 | 
			
		||||
 | 
			
		||||
  def url(directory: Option[String]): String = {
 | 
			
		||||
    val sb = new StringBuilder()
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ object Directory {
 | 
			
		||||
        case None => {
 | 
			
		||||
          val oldHome = new File(System.getProperty("user.home"), "gitbucket")
 | 
			
		||||
          if (oldHome.exists && oldHome.isDirectory && new File(oldHome, "version").exists) {
 | 
			
		||||
            //FileUtils.moveDirectory(oldHome, newHome)
 | 
			
		||||
            // FileUtils.moveDirectory(oldHome, newHome)
 | 
			
		||||
            oldHome
 | 
			
		||||
          } else {
 | 
			
		||||
            new File(System.getProperty("user.home"), ".gitbucket")
 | 
			
		||||
 
 | 
			
		||||
@@ -12,9 +12,8 @@ import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider
 | 
			
		||||
object GpgUtil {
 | 
			
		||||
  def str2GpgKeyId(keyStr: String): Option[Long] = {
 | 
			
		||||
    val pubKeyOf = new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(keyStr.getBytes)))
 | 
			
		||||
    pubKeyOf.iterator().asScala.collectFirst {
 | 
			
		||||
      case keyRing: PGPPublicKeyRing =>
 | 
			
		||||
        keyRing.getPublicKey().getKeyID
 | 
			
		||||
    pubKeyOf.iterator().asScala.collectFirst { case keyRing: PGPPublicKeyRing =>
 | 
			
		||||
      keyRing.getPublicKey().getKeyID
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -37,23 +36,22 @@ object GpgUtil {
 | 
			
		||||
      new BcPGPObjectFactory(new ArmoredInputStream(new ByteArrayInputStream(signInfo.signArmored)))
 | 
			
		||||
        .iterator()
 | 
			
		||||
        .asScala
 | 
			
		||||
        .flatMap {
 | 
			
		||||
          case signList: PGPSignatureList =>
 | 
			
		||||
            signList
 | 
			
		||||
              .iterator()
 | 
			
		||||
              .asScala
 | 
			
		||||
              .flatMap { sign =>
 | 
			
		||||
                getGpgKey(sign.getKeyID)
 | 
			
		||||
                  .map { pubKey =>
 | 
			
		||||
                    sign.init(new BcPGPContentVerifierBuilderProvider, pubKey)
 | 
			
		||||
                    sign.update(signInfo.target)
 | 
			
		||||
                    (sign, pubKey)
 | 
			
		||||
                  }
 | 
			
		||||
                  .collect {
 | 
			
		||||
                    case (sign, pubKey) if sign.verify() =>
 | 
			
		||||
                      JGitUtil.GpgVerifyInfo(pubKey.getUserIDs.next, pubKey.getKeyID.toHexString.toUpperCase)
 | 
			
		||||
                  }
 | 
			
		||||
              }
 | 
			
		||||
        .flatMap { case signList: PGPSignatureList =>
 | 
			
		||||
          signList
 | 
			
		||||
            .iterator()
 | 
			
		||||
            .asScala
 | 
			
		||||
            .flatMap { sign =>
 | 
			
		||||
              getGpgKey(sign.getKeyID)
 | 
			
		||||
                .map { pubKey =>
 | 
			
		||||
                  sign.init(new BcPGPContentVerifierBuilderProvider, pubKey)
 | 
			
		||||
                  sign.update(signInfo.target)
 | 
			
		||||
                  (sign, pubKey)
 | 
			
		||||
                }
 | 
			
		||||
                .collect {
 | 
			
		||||
                  case (sign, pubKey) if sign.verify() =>
 | 
			
		||||
                    JGitUtil.GpgVerifyInfo(pubKey.getUserIDs.next, pubKey.getKeyID.toHexString.toUpperCase)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        .toList
 | 
			
		||||
        .headOption
 | 
			
		||||
 
 | 
			
		||||
@@ -54,12 +54,11 @@ object JDBCUtil {
 | 
			
		||||
 | 
			
		||||
    private def execute[T](sql: String, params: Any*)(f: (PreparedStatement) => T): T = {
 | 
			
		||||
      Using.resource(conn.prepareStatement(sql)) { stmt =>
 | 
			
		||||
        params.zipWithIndex.foreach {
 | 
			
		||||
          case (p, i) =>
 | 
			
		||||
            p match {
 | 
			
		||||
              case x: Int    => stmt.setInt(i + 1, x)
 | 
			
		||||
              case x: String => stmt.setString(i + 1, x)
 | 
			
		||||
            }
 | 
			
		||||
        params.zipWithIndex.foreach { case (p, i) =>
 | 
			
		||||
          p match {
 | 
			
		||||
            case x: Int    => stmt.setInt(i + 1, x)
 | 
			
		||||
            case x: String => stmt.setString(i + 1, x)
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        f(stmt)
 | 
			
		||||
      }
 | 
			
		||||
@@ -136,19 +135,18 @@ object JDBCUtil {
 | 
			
		||||
              sb.append(columns.map(_._1).mkString(", "))
 | 
			
		||||
              sb.append(") VALUES (")
 | 
			
		||||
 | 
			
		||||
              val values = columns.map {
 | 
			
		||||
                case (columnName, columnType) =>
 | 
			
		||||
                  if (rs.getObject(columnName) == null) {
 | 
			
		||||
                    null
 | 
			
		||||
                  } else {
 | 
			
		||||
                    columnType match {
 | 
			
		||||
                      case Types.BOOLEAN | Types.BIT                                   => rs.getBoolean(columnName)
 | 
			
		||||
                      case Types.VARCHAR | Types.CLOB | Types.CHAR | Types.LONGVARCHAR => rs.getString(columnName)
 | 
			
		||||
                      case Types.INTEGER                                               => rs.getInt(columnName)
 | 
			
		||||
                      case Types.BIGINT                                                => rs.getLong(columnName)
 | 
			
		||||
                      case Types.TIMESTAMP                                             => rs.getTimestamp(columnName)
 | 
			
		||||
                    }
 | 
			
		||||
              val values = columns.map { case (columnName, columnType) =>
 | 
			
		||||
                if (rs.getObject(columnName) == null) {
 | 
			
		||||
                  null
 | 
			
		||||
                } else {
 | 
			
		||||
                  columnType match {
 | 
			
		||||
                    case Types.BOOLEAN | Types.BIT                                   => rs.getBoolean(columnName)
 | 
			
		||||
                    case Types.VARCHAR | Types.CLOB | Types.CHAR | Types.LONGVARCHAR => rs.getString(columnName)
 | 
			
		||||
                    case Types.INTEGER                                               => rs.getInt(columnName)
 | 
			
		||||
                    case Types.BIGINT                                                => rs.getLong(columnName)
 | 
			
		||||
                    case Types.TIMESTAMP                                             => rs.getTimestamp(columnName)
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              val columnValues = values.map {
 | 
			
		||||
 
 | 
			
		||||
@@ -454,27 +454,26 @@ object JGitUtil {
 | 
			
		||||
      def appendLastCommits(
 | 
			
		||||
        fileList: List[(ObjectId, FileMode, String, String, Option[String])]
 | 
			
		||||
      ): List[(ObjectId, FileMode, String, String, Option[String], Option[RevCommit])] = {
 | 
			
		||||
        fileList.map {
 | 
			
		||||
          case (id, mode, name, path, opt) =>
 | 
			
		||||
            if (maxFiles > 0 && fileList.size >= maxFiles) {
 | 
			
		||||
              // Don't attempt to get the last commit if the number of files is very large.
 | 
			
		||||
              (id, mode, name, path, opt, None)
 | 
			
		||||
            } else if (commitCount < 10000) {
 | 
			
		||||
              (id, mode, name, path, opt, Some(getCommit(path)))
 | 
			
		||||
            } else if (isCacheEnabled()) {
 | 
			
		||||
              // Use in-memory cache if the commit count is too big.
 | 
			
		||||
              val cached = objectCommitCache.getEntry(id)
 | 
			
		||||
              if (cached == null) {
 | 
			
		||||
                val commit = getCommit(path)
 | 
			
		||||
                objectCommitCache.put(id, commit)
 | 
			
		||||
                (id, mode, name, path, opt, Some(commit))
 | 
			
		||||
              } else {
 | 
			
		||||
                (id, mode, name, path, opt, Some(cached.getValue))
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
        fileList.map { case (id, mode, name, path, opt) =>
 | 
			
		||||
          if (maxFiles > 0 && fileList.size >= maxFiles) {
 | 
			
		||||
            // Don't attempt to get the last commit if the number of files is very large.
 | 
			
		||||
            (id, mode, name, path, opt, None)
 | 
			
		||||
          } else if (commitCount < 10000) {
 | 
			
		||||
            (id, mode, name, path, opt, Some(getCommit(path)))
 | 
			
		||||
          } else if (isCacheEnabled()) {
 | 
			
		||||
            // Use in-memory cache if the commit count is too big.
 | 
			
		||||
            val cached = objectCommitCache.getEntry(id)
 | 
			
		||||
            if (cached == null) {
 | 
			
		||||
              val commit = getCommit(path)
 | 
			
		||||
              objectCommitCache.put(id, commit)
 | 
			
		||||
              (id, mode, name, path, opt, Some(commit))
 | 
			
		||||
            } else {
 | 
			
		||||
              (id, mode, name, path, opt, Some(cached.getValue))
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            val commit = getCommit(path)
 | 
			
		||||
            (id, mode, name, path, opt, Some(commit))
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -503,23 +502,22 @@ object JGitUtil {
 | 
			
		||||
 | 
			
		||||
      appendLastCommits(fileList)
 | 
			
		||||
        .map(simplifyPath)
 | 
			
		||||
        .map {
 | 
			
		||||
          case (objectId, fileMode, name, path, linkUrl, commit) =>
 | 
			
		||||
            FileInfo(
 | 
			
		||||
              objectId,
 | 
			
		||||
              fileMode == FileMode.TREE || fileMode == FileMode.GITLINK,
 | 
			
		||||
              name,
 | 
			
		||||
              path,
 | 
			
		||||
              getSummaryMessage(
 | 
			
		||||
                commit.map(_.getFullMessage).getOrElse(""),
 | 
			
		||||
                commit.map(_.getShortMessage).getOrElse("")
 | 
			
		||||
              ),
 | 
			
		||||
              commit.map(_.getName).getOrElse(""),
 | 
			
		||||
              commit.map(_.getAuthorIdent.getWhen).orNull,
 | 
			
		||||
              commit.map(_.getAuthorIdent.getName).getOrElse(""),
 | 
			
		||||
              commit.map(_.getAuthorIdent.getEmailAddress).getOrElse(""),
 | 
			
		||||
              linkUrl
 | 
			
		||||
            )
 | 
			
		||||
        .map { case (objectId, fileMode, name, path, linkUrl, commit) =>
 | 
			
		||||
          FileInfo(
 | 
			
		||||
            objectId,
 | 
			
		||||
            fileMode == FileMode.TREE || fileMode == FileMode.GITLINK,
 | 
			
		||||
            name,
 | 
			
		||||
            path,
 | 
			
		||||
            getSummaryMessage(
 | 
			
		||||
              commit.map(_.getFullMessage).getOrElse(""),
 | 
			
		||||
              commit.map(_.getShortMessage).getOrElse("")
 | 
			
		||||
            ),
 | 
			
		||||
            commit.map(_.getName).getOrElse(""),
 | 
			
		||||
            commit.map(_.getAuthorIdent.getWhen).orNull,
 | 
			
		||||
            commit.map(_.getAuthorIdent.getName).getOrElse(""),
 | 
			
		||||
            commit.map(_.getAuthorIdent.getEmailAddress).getOrElse(""),
 | 
			
		||||
            linkUrl
 | 
			
		||||
          )
 | 
			
		||||
        }
 | 
			
		||||
        .sortWith { (file1, file2) =>
 | 
			
		||||
          (file1.isDirectory, file2.isDirectory) match {
 | 
			
		||||
@@ -714,10 +712,9 @@ object JGitUtil {
 | 
			
		||||
          toCommit.getParentCount match {
 | 
			
		||||
            case 0 =>
 | 
			
		||||
              df.scan(
 | 
			
		||||
                  new EmptyTreeIterator(),
 | 
			
		||||
                  new CanonicalTreeParser(null, git.getRepository.newObjectReader(), toCommit.getTree)
 | 
			
		||||
                )
 | 
			
		||||
                .asScala
 | 
			
		||||
                new EmptyTreeIterator(),
 | 
			
		||||
                new CanonicalTreeParser(null, git.getRepository.newObjectReader(), toCommit.getTree)
 | 
			
		||||
              ).asScala
 | 
			
		||||
            case _ => df.scan(toCommit.getParent(0), toCommit.getTree).asScala
 | 
			
		||||
          }
 | 
			
		||||
        case Some(from) =>
 | 
			
		||||
@@ -788,8 +785,10 @@ object JGitUtil {
 | 
			
		||||
      } else {
 | 
			
		||||
        val oldIsImage = FileUtil.isImage(diff.getOldPath)
 | 
			
		||||
        val newIsImage = FileUtil.isImage(diff.getNewPath)
 | 
			
		||||
        val patch = if (oldIsImage || newIsImage) None else Some(makePatchFromDiffEntry(git, diff)) // TODO use DiffFormatter
 | 
			
		||||
        val tooLarge = patch.exists(_.count(_ == '\n') > 1000) // Don't show diff if the file has more than 1000 lines diff
 | 
			
		||||
        val patch =
 | 
			
		||||
          if (oldIsImage || newIsImage) None else Some(makePatchFromDiffEntry(git, diff)) // TODO use DiffFormatter
 | 
			
		||||
        val tooLarge =
 | 
			
		||||
          patch.exists(_.count(_ == '\n') > 1000) // Don't show diff if the file has more than 1000 lines diff
 | 
			
		||||
        val includeContent = tooLarge || !fetchContent || oldIsImage || newIsImage
 | 
			
		||||
        DiffInfo(
 | 
			
		||||
          changeType = diff.getChangeType,
 | 
			
		||||
@@ -921,10 +920,9 @@ object JGitUtil {
 | 
			
		||||
      Some(if (revstr.isEmpty) repository.repository.defaultBranch else revstr),
 | 
			
		||||
      repository.branchList.headOption
 | 
			
		||||
    ).flatMap {
 | 
			
		||||
        case Some(rev) => Some((git.getRepository.resolve(rev), rev))
 | 
			
		||||
        case None      => None
 | 
			
		||||
      }
 | 
			
		||||
      .find(_._1 != null)
 | 
			
		||||
      case Some(rev) => Some((git.getRepository.resolve(rev), rev))
 | 
			
		||||
      case None      => None
 | 
			
		||||
    }.find(_._1 != null)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def createTag(git: Git, name: String, message: Option[String], commitId: String) = {
 | 
			
		||||
@@ -1301,7 +1299,8 @@ object JGitUtil {
 | 
			
		||||
              c.getAuthorIdent.getWhen,
 | 
			
		||||
              Option(git.log.add(c).addPath(blame.getSourcePath(i)).setSkip(1).setMaxCount(2).call.iterator.next)
 | 
			
		||||
                .map(_.name),
 | 
			
		||||
              if (blame.getSourcePath(i) == path) { None } else { Some(blame.getSourcePath(i)) },
 | 
			
		||||
              if (blame.getSourcePath(i) == path) { None }
 | 
			
		||||
              else { Some(blame.getSourcePath(i)) },
 | 
			
		||||
              c.getCommitterIdent.getWhen,
 | 
			
		||||
              c.getShortMessage,
 | 
			
		||||
              Set.empty
 | 
			
		||||
 
 | 
			
		||||
@@ -128,13 +128,14 @@ object LDAPUtil {
 | 
			
		||||
 | 
			
		||||
    val cachedInstance = provider.get()
 | 
			
		||||
    if (cachedInstance == null) {
 | 
			
		||||
      val cls = try {
 | 
			
		||||
        Class.forName("com.sun.net.ssl.internal.ssl.Provider")
 | 
			
		||||
      } catch {
 | 
			
		||||
        case e: ClassNotFoundException =>
 | 
			
		||||
          Class.forName("com.ibm.jsse.IBMJSSEProvider")
 | 
			
		||||
        case e: Throwable => throw e
 | 
			
		||||
      }
 | 
			
		||||
      val cls =
 | 
			
		||||
        try {
 | 
			
		||||
          Class.forName("com.sun.net.ssl.internal.ssl.Provider")
 | 
			
		||||
        } catch {
 | 
			
		||||
          case e: ClassNotFoundException =>
 | 
			
		||||
            Class.forName("com.ibm.jsse.IBMJSSEProvider")
 | 
			
		||||
          case e: Throwable => throw e
 | 
			
		||||
        }
 | 
			
		||||
      val newInstance = cls
 | 
			
		||||
        .getDeclaredConstructor()
 | 
			
		||||
        .newInstance()
 | 
			
		||||
@@ -234,8 +235,8 @@ object LDAPUtil {
 | 
			
		||||
      case x  => "(&(" + x + ")(" + userNameAttribute + "=" + userName + "))"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getEntries(conn.search(baseDN, LDAPConnection.SCOPE_SUB, filterCond, null, false)).collectFirst {
 | 
			
		||||
      case x => x.getDN
 | 
			
		||||
    getEntries(conn.search(baseDN, LDAPConnection.SCOPE_SUB, filterCond, null, false)).collectFirst { case x =>
 | 
			
		||||
      x.getDN
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -62,9 +62,8 @@ class Mailer(settings: SystemSettings) {
 | 
			
		||||
        smtp.fromAddress
 | 
			
		||||
          .map(_ -> smtp.fromName.getOrElse(loginAccount.map(_.userName).getOrElse("GitBucket")))
 | 
			
		||||
          .orElse(Some("notifications@gitbucket.com" -> loginAccount.map(_.userName).getOrElse("GitBucket")))
 | 
			
		||||
          .foreach {
 | 
			
		||||
            case (address, name) =>
 | 
			
		||||
              email.setFrom(address, name)
 | 
			
		||||
          .foreach { case (address, name) =>
 | 
			
		||||
            email.setFrom(address, name)
 | 
			
		||||
          }
 | 
			
		||||
        email.setCharset("UTF-8")
 | 
			
		||||
        email.setSubject(subject)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ object RepositoryName {
 | 
			
		||||
  def apply(fullName: String): RepositoryName = {
 | 
			
		||||
    fullName.split("/").toList match {
 | 
			
		||||
      case owner :: name :: Nil => RepositoryName(owner, name)
 | 
			
		||||
      case _                    => throw new IllegalArgumentException(s"${fullName} is not repositoryName (only 'owner/name')")
 | 
			
		||||
      case _ => throw new IllegalArgumentException(s"${fullName} is not repositoryName (only 'owner/name')")
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  def apply(repository: gitbucket.core.model.Repository): RepositoryName =
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,9 @@ trait AvatarImageProvider { self: RequestCache =>
 | 
			
		||||
      // by user name
 | 
			
		||||
      getAccountByUserNameFromCache(userName).map { account =>
 | 
			
		||||
        if (account.image.isEmpty && context.settings.basicBehavior.gravatar) {
 | 
			
		||||
          s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g"""
 | 
			
		||||
          s"""https://www.gravatar.com/avatar/${StringUtil.md5(
 | 
			
		||||
              account.mailAddress.toLowerCase
 | 
			
		||||
            )}?s=${size}&d=retro&r=g"""
 | 
			
		||||
        } else {
 | 
			
		||||
          s"""${context.path}/${account.userName}/_avatar?${helpers.hashDate(account.updatedDate)}"""
 | 
			
		||||
        }
 | 
			
		||||
@@ -30,7 +32,9 @@ trait AvatarImageProvider { self: RequestCache =>
 | 
			
		||||
      // by mail address
 | 
			
		||||
      getAccountByMailAddressFromCache(mailAddress).map { account =>
 | 
			
		||||
        if (account.image.isEmpty && context.settings.basicBehavior.gravatar) {
 | 
			
		||||
          s"""https://www.gravatar.com/avatar/${StringUtil.md5(account.mailAddress.toLowerCase)}?s=${size}&d=retro&r=g"""
 | 
			
		||||
          s"""https://www.gravatar.com/avatar/${StringUtil.md5(
 | 
			
		||||
              account.mailAddress.toLowerCase
 | 
			
		||||
            )}?s=${size}&d=retro&r=g"""
 | 
			
		||||
        } else {
 | 
			
		||||
          s"""${context.path}/${account.userName}/_avatar?${helpers.hashDate(account.updatedDate)}"""
 | 
			
		||||
        }
 | 
			
		||||
@@ -45,13 +49,15 @@ trait AvatarImageProvider { self: RequestCache =>
 | 
			
		||||
 | 
			
		||||
    if (tooltip) {
 | 
			
		||||
      Html(
 | 
			
		||||
        s"""<img src="${src}" class="${if (size > 20) { "avatar" } else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
 | 
			
		||||
        s"""<img src="${src}" class="${if (size > 20) { "avatar" }
 | 
			
		||||
          else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
 | 
			
		||||
           |     alt="@${StringUtil.escapeHtml(userName)}"
 | 
			
		||||
           |     data-toggle="tooltip" title="${StringUtil.escapeHtml(userName)}" />""".stripMargin
 | 
			
		||||
      )
 | 
			
		||||
    } else {
 | 
			
		||||
      Html(
 | 
			
		||||
        s"""<img src="${src}" class="${if (size > 20) { "avatar" } else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
 | 
			
		||||
        s"""<img src="${src}" class="${if (size > 20) { "avatar" }
 | 
			
		||||
          else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
 | 
			
		||||
           |     alt="@${StringUtil.escapeHtml(userName)}" />""".stripMargin
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -10,13 +10,14 @@ trait LinkConverter { self: RequestCache =>
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a link to the issue or the pull request from the issue id.
 | 
			
		||||
   */
 | 
			
		||||
  protected def createIssueLink(owner: String, repository: String, issueId: Int, title: String)(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  protected def createIssueLink(owner: String, repository: String, issueId: Int, title: String)(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): String = {
 | 
			
		||||
    getIssueFromCache(owner, repository, issueId.toString) match {
 | 
			
		||||
      case Some(issue) =>
 | 
			
		||||
        s"""<a href="${context.path}/${owner}/${repository}/${if (issue.isPullRequest) "pull" else "issues"}/${issueId}"><strong>${StringUtil
 | 
			
		||||
          .escapeHtml(title)}</strong> #${issueId}</a>"""
 | 
			
		||||
        s"""<a href="${context.path}/${owner}/${repository}/${if (issue.isPullRequest) "pull"
 | 
			
		||||
          else "issues"}/${issueId}"><strong>${StringUtil
 | 
			
		||||
            .escapeHtml(title)}</strong> #${issueId}</a>"""
 | 
			
		||||
      case None =>
 | 
			
		||||
        s"Unknown #${issueId}"
 | 
			
		||||
    }
 | 
			
		||||
@@ -25,13 +26,14 @@ trait LinkConverter { self: RequestCache =>
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a global link to the issue or the pull request from the issue id.
 | 
			
		||||
   */
 | 
			
		||||
  protected def createGlobalIssueLink(owner: String, repository: String, issueId: Int, title: String)(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  protected def createGlobalIssueLink(owner: String, repository: String, issueId: Int, title: String)(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): String = {
 | 
			
		||||
    getIssueFromCache(owner, repository, issueId.toString) match {
 | 
			
		||||
      case Some(issue) =>
 | 
			
		||||
        s"""<a href="${context.path}/${owner}/${repository}/${if (issue.isPullRequest) "pull" else "issues"}/${issueId}"><strong>${StringUtil
 | 
			
		||||
          .escapeHtml(title)}</strong> ${owner}/${repository}#${issueId}</a>"""
 | 
			
		||||
        s"""<a href="${context.path}/${owner}/${repository}/${if (issue.isPullRequest) "pull"
 | 
			
		||||
          else "issues"}/${issueId}"><strong>${StringUtil
 | 
			
		||||
            .escapeHtml(title)}</strong> ${owner}/${repository}#${issueId}</a>"""
 | 
			
		||||
      case None =>
 | 
			
		||||
        s"Unknown ${owner}/${repository}#${issueId}"
 | 
			
		||||
    }
 | 
			
		||||
@@ -53,12 +55,12 @@ trait LinkConverter { self: RequestCache =>
 | 
			
		||||
      else text
 | 
			
		||||
 | 
			
		||||
    escaped
 | 
			
		||||
    // convert username/project@SHA to link
 | 
			
		||||
      // convert username/project@SHA to link
 | 
			
		||||
      .replaceBy("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)/([a-zA-Z0-9\\-_\\.]+)@([a-f0-9]{40})(?=(\\W|$))".r) { m =>
 | 
			
		||||
        getAccountByUserNameFromCache(m.group(2)).map { _ =>
 | 
			
		||||
          s"""<code><a href="${context.path}/${m.group(2)}/${m.group(3)}/commit/${m.group(4)}">${m.group(2)}/${m.group(
 | 
			
		||||
            3
 | 
			
		||||
          )}@${m.group(4).substring(0, 7)}</a></code>"""
 | 
			
		||||
              3
 | 
			
		||||
            )}@${m.group(4).substring(0, 7)}</a></code>"""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -68,13 +70,13 @@ trait LinkConverter { self: RequestCache =>
 | 
			
		||||
          getIssueFromCache(m.group(2), m.group(3), m.group(4)) match {
 | 
			
		||||
            case Some(pull) if (pull.isPullRequest) =>
 | 
			
		||||
              Some(s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/pull/${m
 | 
			
		||||
                .group(4)}" title="${pull.title}">${m.group(2)}/${m.group(
 | 
			
		||||
                3
 | 
			
		||||
              )}#${m.group(4)}</a>""")
 | 
			
		||||
                  .group(4)}" title="${pull.title}">${m.group(2)}/${m.group(
 | 
			
		||||
                  3
 | 
			
		||||
                )}#${m.group(4)}</a>""")
 | 
			
		||||
            case Some(issue) =>
 | 
			
		||||
              Some(s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/issues/${m
 | 
			
		||||
                .group(4)}" title="${issue.title}">${m.group(2)}/${m
 | 
			
		||||
                .group(3)}#${m.group(4)}</a>""")
 | 
			
		||||
                  .group(4)}" title="${issue.title}">${m.group(2)}/${m
 | 
			
		||||
                  .group(3)}#${m.group(4)}</a>""")
 | 
			
		||||
            case None =>
 | 
			
		||||
              Some(s"""${m.group(2)}/${m.group(3)}#${m.group(4)}""")
 | 
			
		||||
          }
 | 
			
		||||
@@ -84,8 +86,8 @@ trait LinkConverter { self: RequestCache =>
 | 
			
		||||
      .replaceBy(("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)@([a-f0-9]{40})(?=(\\W|$))").r) { m =>
 | 
			
		||||
        getAccountByUserNameFromCache(m.group(2)).map { _ =>
 | 
			
		||||
          s"""<code><a href="${context.path}/${m.group(2)}/${repository.name}/commit/${m.group(3)}">${m.group(2)}@${m
 | 
			
		||||
            .group(3)
 | 
			
		||||
            .substring(0, 7)}</a></code>"""
 | 
			
		||||
              .group(3)
 | 
			
		||||
              .substring(0, 7)}</a></code>"""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@@ -94,10 +96,10 @@ trait LinkConverter { self: RequestCache =>
 | 
			
		||||
        getIssueFromCache(m.group(2), repository.name, m.group(3)) match {
 | 
			
		||||
          case Some(issue) if (issue.isPullRequest) =>
 | 
			
		||||
            Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/pull/${m.group(3)}">${m.group(2)}#${m
 | 
			
		||||
              .group(3)}</a>""")
 | 
			
		||||
                .group(3)}</a>""")
 | 
			
		||||
          case Some(_) =>
 | 
			
		||||
            Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/issues/${m.group(3)}">${m.group(2)}#${m
 | 
			
		||||
              .group(3)}</a>""")
 | 
			
		||||
                .group(3)}</a>""")
 | 
			
		||||
          case None =>
 | 
			
		||||
            Some(s"""${m.group(2)}#${m.group(3)}""")
 | 
			
		||||
        }
 | 
			
		||||
@@ -109,12 +111,12 @@ trait LinkConverter { self: RequestCache =>
 | 
			
		||||
        getIssueFromCache(repository.owner, repository.name, m.group(3)) match {
 | 
			
		||||
          case Some(pull) if (pull.isPullRequest) =>
 | 
			
		||||
            Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/pull/${m
 | 
			
		||||
              .group(3)}" title="${pull.title}">${prefix}${m
 | 
			
		||||
              .group(3)}</a>""")
 | 
			
		||||
                .group(3)}" title="${pull.title}">${prefix}${m
 | 
			
		||||
                .group(3)}</a>""")
 | 
			
		||||
          case Some(issue) =>
 | 
			
		||||
            Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/issues/${m
 | 
			
		||||
              .group(3)}"  title="${issue.title}">${prefix}${m
 | 
			
		||||
              .group(3)}</a>""")
 | 
			
		||||
                .group(3)}"  title="${issue.title}">${prefix}${m
 | 
			
		||||
                .group(3)}</a>""")
 | 
			
		||||
          case None =>
 | 
			
		||||
            Some(s"""${m.group(2)}${m.group(3)}""")
 | 
			
		||||
        }
 | 
			
		||||
@@ -130,7 +132,7 @@ trait LinkConverter { self: RequestCache =>
 | 
			
		||||
      // convert commit id to link
 | 
			
		||||
      .replaceBy("(?<=(^|[^\\w/@]))([a-f0-9]{40})(?=(\\W|$))".r) { m =>
 | 
			
		||||
        Some(s"""<code><a href="${context.path}/${repository.owner}/${repository.name}/commit/${m
 | 
			
		||||
          .group(2)}">${m.group(2).substring(0, 7)}</a></code>""")
 | 
			
		||||
            .group(2)}">${m.group(2).substring(0, 7)}</a></code>""")
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -160,8 +160,8 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a link to the issue or the pull request from the issue id.
 | 
			
		||||
   */
 | 
			
		||||
  def issueLink(owner: String, repository: String, issueId: Int, title: String)(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  def issueLink(owner: String, repository: String, issueId: Int, title: String)(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Html = {
 | 
			
		||||
    Html(createIssueLink(owner, repository, issueId, title))
 | 
			
		||||
  }
 | 
			
		||||
@@ -169,8 +169,8 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a global link to the issue or the pull request from the issue id.
 | 
			
		||||
   */
 | 
			
		||||
  def issueGlobalLink(owner: String, repository: String, issueId: Int, title: String)(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  def issueGlobalLink(owner: String, repository: String, issueId: Int, title: String)(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Html = {
 | 
			
		||||
    Html(createGlobalIssueLink(owner, repository, issueId, title))
 | 
			
		||||
  }
 | 
			
		||||
@@ -179,8 +179,8 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
 | 
			
		||||
   * Returns <img> which displays the avatar icon for the given user name.
 | 
			
		||||
   * This method looks up Gravatar if avatar icon has not been configured in user settings.
 | 
			
		||||
   */
 | 
			
		||||
  def avatar(userName: String, size: Int, tooltip: Boolean = false, mailAddress: String = "")(
 | 
			
		||||
    implicit context: Context
 | 
			
		||||
  def avatar(userName: String, size: Int, tooltip: Boolean = false, mailAddress: String = "")(implicit
 | 
			
		||||
    context: Context
 | 
			
		||||
  ): Html =
 | 
			
		||||
    getAvatarImageHtml(userName, size, mailAddress, tooltip)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -257,7 +257,7 @@ class ApiIntegrationTest extends AnyFunSuite {
 | 
			
		||||
        assert(label1.getUrl == "http://localhost:19999/api/v3/repos/root/issue_label_test/labels/bug")
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    // Replace labels (Cannot test because GHLabel.setLabels() doesn't use the replace endpoint)
 | 
			
		||||
      // Replace labels (Cannot test because GHLabel.setLabels() doesn't use the replace endpoint)
 | 
			
		||||
//      {
 | 
			
		||||
//        issue.setLabels("enhancement", "invalid", "question")
 | 
			
		||||
//
 | 
			
		||||
 
 | 
			
		||||
@@ -297,7 +297,7 @@ object ApiSpecModels {
 | 
			
		||||
      committerEmailAddress = account.mailAddress,
 | 
			
		||||
      None,
 | 
			
		||||
      None
 | 
			
		||||
  )
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
  val apiCommitListItem = ApiCommitListItem(
 | 
			
		||||
    commit = commitInfo(sha1),
 | 
			
		||||
@@ -443,7 +443,7 @@ object ApiSpecModels {
 | 
			
		||||
 | 
			
		||||
  val apiPusher = ApiPusher(account)
 | 
			
		||||
 | 
			
		||||
  //have both urls as https, as the expected samples are using https
 | 
			
		||||
  // have both urls as https, as the expected samples are using https
 | 
			
		||||
  val gitHubContext = JsonFormat.Context("https://api.github.com", Some("https://api.github.com"))
 | 
			
		||||
 | 
			
		||||
  val apiRefHeadsMaster = ApiRef(
 | 
			
		||||
@@ -797,7 +797,7 @@ object ApiSpecModels {
 | 
			
		||||
 | 
			
		||||
  val jsonPusher = """{"name":"octocat","email":"octocat@example.com"}"""
 | 
			
		||||
 | 
			
		||||
  //I checked all refs in gitbucket repo, and there appears to be only type "commit" and type "tag"
 | 
			
		||||
  // I checked all refs in gitbucket repo, and there appears to be only type "commit" and type "tag"
 | 
			
		||||
  val jsonRef = """{"ref":"refs/heads/featureA","object":{"sha":"6dcb09b5b57875f334f61aebed695e2e4193db5e"}}"""
 | 
			
		||||
 | 
			
		||||
  val jsonRefHeadsMain =
 | 
			
		||||
 
 | 
			
		||||
@@ -28,9 +28,19 @@ import MergeServiceSpec._
 | 
			
		||||
import org.json4s.JsonAST.{JArray, JString}
 | 
			
		||||
 | 
			
		||||
class MergeServiceSpec extends AnyFunSpec with ServiceSpecBase {
 | 
			
		||||
  val service = new MergeService with AccountService with ActivityService with IssuesService with LabelsService
 | 
			
		||||
  with MilestonesService with RepositoryService with PrioritiesService with PullRequestService with CommitsService
 | 
			
		||||
  with WebHookPullRequestService with WebHookPullRequestReviewCommentService with RequestCache {
 | 
			
		||||
  val service = new MergeService
 | 
			
		||||
    with AccountService
 | 
			
		||||
    with ActivityService
 | 
			
		||||
    with IssuesService
 | 
			
		||||
    with LabelsService
 | 
			
		||||
    with MilestonesService
 | 
			
		||||
    with RepositoryService
 | 
			
		||||
    with PrioritiesService
 | 
			
		||||
    with PullRequestService
 | 
			
		||||
    with CommitsService
 | 
			
		||||
    with WebHookPullRequestService
 | 
			
		||||
    with WebHookPullRequestReviewCommentService
 | 
			
		||||
    with RequestCache {
 | 
			
		||||
    override protected def getReceiveHooks(): Seq[ReceiveHook] = Nil
 | 
			
		||||
  }
 | 
			
		||||
  val branch = "master"
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,11 @@ class PullRequestServiceSpec
 | 
			
		||||
        generateNewUserWithDBRepository("user1", "repo2")
 | 
			
		||||
        generateNewUserWithDBRepository("user2", "repo1")
 | 
			
		||||
        generateNewPullRequest("user1/repo1/master", "user1/repo1/head2", loginUser = "root") // not target branch
 | 
			
		||||
        generateNewPullRequest("user1/repo1/head1", "user1/repo1/master", loginUser = "root") // not target branch ( swap from, to )
 | 
			
		||||
        generateNewPullRequest(
 | 
			
		||||
          "user1/repo1/head1",
 | 
			
		||||
          "user1/repo1/master",
 | 
			
		||||
          loginUser = "root"
 | 
			
		||||
        ) // not target branch ( swap from, to )
 | 
			
		||||
        generateNewPullRequest("user1/repo1/master", "user2/repo1/head1", loginUser = "root") // other user
 | 
			
		||||
        generateNewPullRequest("user1/repo1/master", "user1/repo2/head1", loginUser = "root") // other repository
 | 
			
		||||
        val r1 = swap(generateNewPullRequest("user1/repo1/master2", "user1/repo1/head1", loginUser = "root"))
 | 
			
		||||
 
 | 
			
		||||
@@ -108,10 +108,21 @@ trait ServiceSpecBase {
 | 
			
		||||
 | 
			
		||||
  def user(name: String)(implicit s: Session): Account = AccountService.getAccountByUserName(name).get
 | 
			
		||||
 | 
			
		||||
  lazy val dummyService = new RepositoryService with AccountService with ActivityService with IssuesService
 | 
			
		||||
  with MergeService with PullRequestService with CommitsService with CommitStatusService with LabelsService
 | 
			
		||||
  with MilestonesService with PrioritiesService with WebHookService with WebHookPullRequestService
 | 
			
		||||
  with WebHookPullRequestReviewCommentService with RequestCache {
 | 
			
		||||
  lazy val dummyService = new RepositoryService
 | 
			
		||||
    with AccountService
 | 
			
		||||
    with ActivityService
 | 
			
		||||
    with IssuesService
 | 
			
		||||
    with MergeService
 | 
			
		||||
    with PullRequestService
 | 
			
		||||
    with CommitsService
 | 
			
		||||
    with CommitStatusService
 | 
			
		||||
    with LabelsService
 | 
			
		||||
    with MilestonesService
 | 
			
		||||
    with PrioritiesService
 | 
			
		||||
    with WebHookService
 | 
			
		||||
    with WebHookPullRequestService
 | 
			
		||||
    with WebHookPullRequestReviewCommentService
 | 
			
		||||
    with RequestCache {
 | 
			
		||||
    override def fetchAsPullRequest(
 | 
			
		||||
      userName: String,
 | 
			
		||||
      repositoryName: String,
 | 
			
		||||
@@ -133,8 +144,7 @@ trait ServiceSpecBase {
 | 
			
		||||
    ac
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def generateNewIssue(userName: String, repositoryName: String, loginUser: String = "root")(
 | 
			
		||||
    implicit
 | 
			
		||||
  def generateNewIssue(userName: String, repositoryName: String, loginUser: String = "root")(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    dummyService.insertIssue(
 | 
			
		||||
@@ -149,8 +159,7 @@ trait ServiceSpecBase {
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def generateNewPullRequest(base: String, request: String, loginUser: String)(
 | 
			
		||||
    implicit
 | 
			
		||||
  def generateNewPullRequest(base: String, request: String, loginUser: String)(implicit
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): (Issue, PullRequest) = {
 | 
			
		||||
    implicit val context = Context(createSystemSettings(), None, this.request)
 | 
			
		||||
 
 | 
			
		||||
@@ -5,9 +5,19 @@ import org.scalatest.funsuite.AnyFunSuite
 | 
			
		||||
import gitbucket.core.model.WebHookContentType
 | 
			
		||||
 | 
			
		||||
class WebHookServiceSpec extends AnyFunSuite with ServiceSpecBase {
 | 
			
		||||
  lazy val service = new WebHookPullRequestService with AccountService with ActivityService with RepositoryService
 | 
			
		||||
  with MergeService with PullRequestService with IssuesService with CommitsService with LabelsService
 | 
			
		||||
  with MilestonesService with PrioritiesService with WebHookPullRequestReviewCommentService with RequestCache
 | 
			
		||||
  lazy val service = new WebHookPullRequestService
 | 
			
		||||
    with AccountService
 | 
			
		||||
    with ActivityService
 | 
			
		||||
    with RepositoryService
 | 
			
		||||
    with MergeService
 | 
			
		||||
    with PullRequestService
 | 
			
		||||
    with IssuesService
 | 
			
		||||
    with CommitsService
 | 
			
		||||
    with LabelsService
 | 
			
		||||
    with MilestonesService
 | 
			
		||||
    with PrioritiesService
 | 
			
		||||
    with WebHookPullRequestReviewCommentService
 | 
			
		||||
    with RequestCache
 | 
			
		||||
 | 
			
		||||
  test("WebHookPullRequestService.getPullRequestsByRequestForWebhook") {
 | 
			
		||||
    withTestDB { implicit session =>
 | 
			
		||||
 
 | 
			
		||||
@@ -101,11 +101,12 @@ object GitSpecUtil {
 | 
			
		||||
    val merger = MergeStrategy.RECURSIVE.newMerger(repository, true)
 | 
			
		||||
    val mergeBaseTip = repository.resolve(into)
 | 
			
		||||
    val mergeTip = repository.resolve(branch)
 | 
			
		||||
    val conflicted = try {
 | 
			
		||||
      !merger.merge(mergeBaseTip, mergeTip)
 | 
			
		||||
    } catch {
 | 
			
		||||
      case e: NoMergeBaseException => true
 | 
			
		||||
    }
 | 
			
		||||
    val conflicted =
 | 
			
		||||
      try {
 | 
			
		||||
        !merger.merge(mergeBaseTip, mergeTip)
 | 
			
		||||
      } catch {
 | 
			
		||||
        case e: NoMergeBaseException => true
 | 
			
		||||
      }
 | 
			
		||||
    if (conflicted) {
 | 
			
		||||
      throw new RuntimeException("conflict!")
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -88,41 +88,65 @@ class StringUtilSpec extends AnyFunSpec {
 | 
			
		||||
    it("should convert GitBucket repository url") {
 | 
			
		||||
      assert(
 | 
			
		||||
        StringUtil
 | 
			
		||||
          .getRepositoryViewerUrl("http://localhost:8080/git/root/gitbucket.git", baseUrl) == "http://localhost:8080/root/gitbucket"
 | 
			
		||||
          .getRepositoryViewerUrl(
 | 
			
		||||
            "http://localhost:8080/git/root/gitbucket.git",
 | 
			
		||||
            baseUrl
 | 
			
		||||
          ) == "http://localhost:8080/root/gitbucket"
 | 
			
		||||
      )
 | 
			
		||||
      assert(
 | 
			
		||||
        StringUtil
 | 
			
		||||
          .getRepositoryViewerUrl("http://root@localhost:8080/git/root/gitbucket.git", baseUrl) == "http://localhost:8080/root/gitbucket"
 | 
			
		||||
          .getRepositoryViewerUrl(
 | 
			
		||||
            "http://root@localhost:8080/git/root/gitbucket.git",
 | 
			
		||||
            baseUrl
 | 
			
		||||
          ) == "http://localhost:8080/root/gitbucket"
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
    it("should convert GitHub repository url") {
 | 
			
		||||
      assert(
 | 
			
		||||
        StringUtil
 | 
			
		||||
          .getRepositoryViewerUrl("https://github.com/root/gitbucket.git", baseUrl) == "https://github.com/root/gitbucket"
 | 
			
		||||
          .getRepositoryViewerUrl(
 | 
			
		||||
            "https://github.com/root/gitbucket.git",
 | 
			
		||||
            baseUrl
 | 
			
		||||
          ) == "https://github.com/root/gitbucket"
 | 
			
		||||
      )
 | 
			
		||||
      assert(
 | 
			
		||||
        StringUtil
 | 
			
		||||
          .getRepositoryViewerUrl("https://root@github.com/root/gitbucket.git", baseUrl) == "https://github.com/root/gitbucket"
 | 
			
		||||
          .getRepositoryViewerUrl(
 | 
			
		||||
            "https://root@github.com/root/gitbucket.git",
 | 
			
		||||
            baseUrl
 | 
			
		||||
          ) == "https://github.com/root/gitbucket"
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
    it("should convert BitBucket repository url") {
 | 
			
		||||
      assert(
 | 
			
		||||
        StringUtil
 | 
			
		||||
          .getRepositoryViewerUrl("https://bitbucket.org/root/gitbucket.git", baseUrl) == "https://bitbucket.org/root/gitbucket"
 | 
			
		||||
          .getRepositoryViewerUrl(
 | 
			
		||||
            "https://bitbucket.org/root/gitbucket.git",
 | 
			
		||||
            baseUrl
 | 
			
		||||
          ) == "https://bitbucket.org/root/gitbucket"
 | 
			
		||||
      )
 | 
			
		||||
      assert(
 | 
			
		||||
        StringUtil
 | 
			
		||||
          .getRepositoryViewerUrl("https://root@bitbucket.org/root/gitbucket.git", baseUrl) == "https://bitbucket.org/root/gitbucket"
 | 
			
		||||
          .getRepositoryViewerUrl(
 | 
			
		||||
            "https://root@bitbucket.org/root/gitbucket.git",
 | 
			
		||||
            baseUrl
 | 
			
		||||
          ) == "https://bitbucket.org/root/gitbucket"
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
    it("should convert GitLab repository url") {
 | 
			
		||||
      assert(
 | 
			
		||||
        StringUtil
 | 
			
		||||
          .getRepositoryViewerUrl("https://gitlab.com/root/gitbucket.git", baseUrl) == "https://gitlab.com/root/gitbucket"
 | 
			
		||||
          .getRepositoryViewerUrl(
 | 
			
		||||
            "https://gitlab.com/root/gitbucket.git",
 | 
			
		||||
            baseUrl
 | 
			
		||||
          ) == "https://gitlab.com/root/gitbucket"
 | 
			
		||||
      )
 | 
			
		||||
      assert(
 | 
			
		||||
        StringUtil
 | 
			
		||||
          .getRepositoryViewerUrl("https://root@gitlab.com/root/gitbucket.git", baseUrl) == "https://gitlab.com/root/gitbucket"
 | 
			
		||||
          .getRepositoryViewerUrl(
 | 
			
		||||
            "https://root@gitlab.com/root/gitbucket.git",
 | 
			
		||||
            baseUrl
 | 
			
		||||
          ) == "https://gitlab.com/root/gitbucket"
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -202,8 +202,7 @@ class AvatarImageProviderSpec extends AnyFunSpec {
 | 
			
		||||
   */
 | 
			
		||||
  class AvatarImageProviderImpl(account: Option[Account]) extends AvatarImageProvider with RequestCache {
 | 
			
		||||
 | 
			
		||||
    def toHtml(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false)(
 | 
			
		||||
      implicit
 | 
			
		||||
    def toHtml(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false)(implicit
 | 
			
		||||
      context: Context
 | 
			
		||||
    ): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user