Compare commits

...

86 Commits

Author SHA1 Message Date
Naoki Takezoe
9cc3a0d36e Optimize Get-Contents API 2021-11-05 20:21:39 +09:00
Naoki Takezoe
7f665c649b Fix duplicated diff issue in exporting patch feature(#2913) 2021-11-05 19:31:52 +09:00
Scala Steward
dcbadb4071 Update sbt-scoverage to 1.9.2 2021-11-03 02:44:03 +09:00
Mira
e3096d15ff resolve #2907 API - not milestone data in issue list (#2908) 2021-11-03 01:23:18 +09:00
Scala Steward
a83c24e7b3 Update scala-library to 2.13.7 2021-11-02 07:15:21 +09:00
Scala Steward
73457c9658 Update testcontainers-scala to 0.39.11 2021-11-02 06:59:06 +09:00
Naoki Takezoe
cfc8d9f3f1 Relax max password length limitation to 40 (#2906) 2021-10-31 11:30:39 +09:00
Scala Steward
8f423b83ea Update postgresql to 42.3.1 2021-10-30 07:15:00 +09:00
Scala Steward
1e7ac532b6 Update testcontainers-scala to 0.39.10 2021-10-28 12:17:14 +09:00
Scala Steward
0fd1db4596 Update mysql, postgresql to 1.16.2 2021-10-23 06:48:28 +09:00
Scala Steward
0755b7ab7f Update testcontainers-scala to 0.39.9 2021-10-22 07:58:09 +09:00
Scala Steward
052382e5c4 Update github-api to 1.135 2021-10-22 07:22:16 +09:00
Scala Steward
f44a63cec1 Update oauth2-oidc-sdk to 9.19 2021-10-20 22:36:12 +09:00
Scala Steward
603d67354a Update mysql, postgresql to 1.16.1 2021-10-19 22:06:56 +09:00
Scala Steward
aafa423b9f Update postgresql to 42.3.0 2021-10-19 06:59:29 +09:00
Mészáros István
2da9d0a801 Add HTTPS support for JettyLauncher (#2896) 2021-10-14 11:27:26 +09:00
Scala Steward
b60c112a74 Update sbt-scoverage to 1.9.1 2021-10-11 19:04:31 +09:00
Scala Steward
843ed6df37 Update mockito-core to 4.0.0 2021-10-07 23:48:03 +09:00
Scala Steward
0f0a849677 Update jetty-continuation, jetty-http, ... to 9.4.44.v20210927 2021-10-01 12:15:06 +09:00
Scala Steward
682901ccbb Update oauth2-oidc-sdk to 9.18 2021-09-30 17:50:08 +09:00
Naoki Takezoe
048fdb8837 Remove unnecessary workaround for scala-xml version conflict (#2888) 2021-09-27 03:00:15 +09:00
scala-steward-bot
3353616789 Update scalatra, scalatra-forms, ... to 2.8.2 (#2886) 2021-09-25 19:07:58 +09:00
scala-steward-bot
b6cb4c865f Update scalatra, scalatra-forms, ... to 2.8.1 (#2885) 2021-09-25 16:00:15 +09:00
kenji yoshida
1fcfd093f7 add jdk 17 test 2021-09-25 10:51:36 +09:00
Scala Steward
3f27c6e733 Update postgresql to 42.2.24 2021-09-23 21:00:09 +09:00
Scala Steward
b6bd9bfc3b Update oauth2-oidc-sdk to 9.17 2021-09-22 07:27:44 +09:00
Scala Steward
6c392f0056 Update oauth2-oidc-sdk to 9.16 2021-09-21 21:18:30 +09:00
Scala Steward
9a38de9a23 Update testcontainers-scala to 0.39.8 2021-09-20 19:57:18 +09:00
Scala Steward
8883600090 Update sbt-scoverage to 1.9.0 2021-09-17 01:27:52 +09:00
Naoki Takezoe
ab822a3c27 Fix Wiki page editing bug when over 100 pages (#2869) 2021-09-13 00:48:38 +09:00
Scala Steward
0e4d64de23 Update org.eclipse.jgit.archive, ... to 5.13.0.202109080827-r 2021-09-12 23:08:48 +09:00
Scala Steward
fbc6bd36bd Update logback-classic to 1.2.6 2021-09-10 13:09:30 +09:00
Naoki Takezoe
ed90ca2dce Bump gitbucket-pages-plugin to 1.10.0 (#2856) 2021-09-10 09:03:51 +09:00
Scala Steward
537ef92149 Update github-api to 1.133 2021-09-09 15:56:16 +09:00
Scala Steward
d51afa7d40 Update java-diff-utils to 4.11 2021-09-08 21:27:57 +09:00
Scala Steward
975cffff48 Update sbt-assembly to 1.1.0 2021-09-01 23:08:42 +09:00
Scala Steward
d92e9c00e8 Update testcontainers-scala to 0.39.7 2021-08-31 14:52:37 +09:00
KN4CK3R
12d72cbb19 Add support for "all" in issue list API (#2859) 2021-08-31 00:38:38 +09:00
Scala Steward
d8e03bed1f Update mockito-core to 3.12.4 2021-08-26 08:43:19 +09:00
Scala Steward
f48c087cd8 Update mockito-core to 3.12.3 2021-08-25 08:39:46 +09:00
Scala Steward
917663e0df Update mockito-core to 3.12.2 2021-08-25 06:11:05 +09:00
Scala Steward
556ddbc926 Update tika-core to 2.1.0 2021-08-24 23:07:23 +09:00
Scala Steward
1c6f37b8e8 Update testcontainers-scala to 0.39.6 2021-08-23 15:26:54 +09:00
Scala Steward
720a329a50 Update oauth2-oidc-sdk to 9.15 2021-08-21 07:09:24 +09:00
Scala Steward
220a8f076a Update mockito-core to 3.12.1 2021-08-21 07:09:08 +09:00
Scala Steward
43be8333c7 Update oauth2-oidc-sdk to 9.14 2021-08-20 21:08:36 +09:00
Scala Steward
08706ab4df Update mockito-core to 3.12.0 2021-08-20 08:04:50 +09:00
Naoki Takezoe
b1196657e0 Release GitBucket 4.36.2 2021-08-16 01:11:21 +09:00
Naoki Takezoe
334bd0c919 Escape user name in avatar image tag (#2845) 2021-08-16 01:06:16 +09:00
Scala Steward
cf0f896972 Update mariadb-java-client to 2.7.4 2021-08-12 07:10:24 +09:00
Scala Steward
d21ca3ff8a Update oauth2-oidc-sdk to 9.12 2021-08-11 19:08:07 +09:00
xuwei-k
83f1f16de7 remove == true 2021-08-09 16:56:55 +09:00
xuwei-k
0fa2ccf107 use keys 2021-08-09 16:48:03 +09:00
xuwei-k
18e3dd431b use until instead of to 2021-08-09 16:48:03 +09:00
xuwei-k
f25dee2249 .getOrElse(None) => .flatten 2021-08-09 16:48:03 +09:00
kenji yoshida
575ffa9580 avoid Array.toString 2021-08-09 16:43:09 +09:00
xuwei-k
f17af5aeb0 use forall or exists instead of map(f).getOrElse 2021-08-09 16:40:37 +09:00
xuwei-k
639f153589 use sizeIs 2021-08-09 16:38:16 +09:00
xuwei-k
fb07098c13 map(f).flatten => flatMap 2021-08-09 16:30:40 +09:00
xuwei-k
74fc08b039 use foreach instead of map 2021-08-09 16:28:15 +09:00
xuwei-k
2776e00004 fix typo 2021-08-09 16:25:55 +09:00
xuwei-k
5932fce303 remove semicolons 2021-08-09 16:12:41 +09:00
xuwei-k
39c9fc4261 remove redundant toString 2021-08-09 16:10:23 +09:00
kenji yoshida
89ea4509a3 use SAM conversion 2021-08-09 16:08:07 +09:00
kenji yoshida
d19d838ead getOrElse(null) => orNull 2021-08-09 16:05:22 +09:00
kenji yoshida
633a2699a8 find(f).isDefined => exists(f) 2021-08-09 16:03:22 +09:00
kenji yoshida
e79bca4a3c remove unnecessary local variable initial values 2021-08-09 16:01:02 +09:00
kenji yoshida
f8ab480d20 remove unused import 2021-08-09 15:55:52 +09:00
kenji yoshida
a14129e340 use diamond operator 2021-08-09 15:55:03 +09:00
Scala Steward
5ec39df6e0 Update oauth2-oidc-sdk to 9.11 2021-08-02 07:07:47 +09:00
kenji yoshida
956e0c6321 fix warning in build.sbt
02a0cfa0a6/io/src/main/scala/sbt/io/IO.scala (L611-L630)

```
/home/runner/work/gitbucket/gitbucket/build.sbt:205: warning: method jar in object IO is deprecated (since 1.3.2): Please specify whether to use a static timestamp
  IO jar (contentMappings.map { case (file, path) => (file, path.toString) }, outputFile, manifest)
     ^
```
2021-07-31 16:14:15 +09:00
Scala Steward
0660a9203a Update oauth2-oidc-sdk to 9.10.2 2021-07-27 23:30:19 +09:00
kenji yoshida
1b660272a1 prepare Scala 3 2021-07-27 07:23:01 +09:00
Scala Steward
d9ef9b874d Update logback-classic to 1.2.5 2021-07-27 06:56:21 +09:00
Scala Steward
7824f796ee Update json4s-jackson to 4.0.3 2021-07-27 03:01:31 +09:00
Scala Steward
838a8d4c7b Update scalatra, scalatra-forms, ... to 2.8.0 (#2834)
Co-authored-by: kenji yoshida <6b656e6a69@gmail.com>
2021-07-25 09:30:05 +09:00
Scala Steward
8db9f77f91 Update github-api to 1.132 2021-07-23 10:22:45 +09:00
Naoki Takezoe
4f82a469d9 Release GitBucket 4.36.1 2021-07-22 14:30:44 +09:00
Naoki Takezoe
0f6ae8559b Bump gist-plugin to 4.21.0 (#2832) 2021-07-22 14:27:34 +09:00
Scala Steward
3f4b2eec35 Update logback-classic to 1.2.4 (#2826) 2021-07-21 13:14:38 +09:00
Lilian BENOIT
a020601d3a exclude dependency org.slf4j from HikariCP (#2830) 2021-07-21 09:17:51 +09:00
Scala Steward
d511205588 Update tika-core to 2.0.0 2021-07-21 07:19:21 +09:00
Scala Steward
fc896b2ea1 Update mysql, postgresql to 1.16.0 2021-07-21 07:09:17 +09:00
Pedro Rijo
3819670535 Update sbt-dependency-graph plugin installation (#2828)
* Update sbt-dependency-graph plugin installation

According with https://github.com/sbt/sbt-dependency-graph#usage-instructions, for sbt >= 1.4.x we should install the plugin with `addDependencyTreePlugin` instead of `addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.10.0-RC1")`

* Fix scalafmt
2021-07-20 18:56:07 +09:00
Lilian BENOIT
067669a18c unprotect plugin-assets (#2825) 2021-07-18 18:31:00 +09:00
Naoki Takezoe
bef7cee8db Fix typo 2021-07-17 17:34:51 +09:00
52 changed files with 464 additions and 288 deletions

View File

@@ -8,7 +8,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
strategy: strategy:
matrix: matrix:
java: [8, 11] java: [8, 11, 17]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Cache - name: Cache

View File

@@ -1,9 +1,15 @@
# Changelog # Changelog
All changes to the project will be documented in this file. All changes to the project will be documented in this file.
### 4.36.2 - 16 Aug 2021
- Escape user name in avatar image tag
### 4.36.1 - 22 Jul 2021
- Bump gitbucket-gist-plugin to 4.21.0
### 4.36.0 - 17 Jul 2021 ### 4.36.0 - 17 Jul 2021
- Tag selector in the repository viewer - Tag selector in the repository viewer
- Link iusses/pull requests of other repositories - Link issues/pull requests of other repositories
- Files and lines can be linked in the diff view - Files and lines can be linked in the diff view
- Option to disable XSS protection - Option to disable XSS protection

View File

@@ -63,9 +63,15 @@ Support
What's New in 4.36.x What's New in 4.36.x
------------- -------------
### 4.36.2 - 16 Aug 2021
- Escape user name in avatar image tag
### 4.36.1 - 22 Jul 2021
- Bump gitbucket-gist-plugin to 4.21.0
### 4.36.0 - 17 Jul 2021 ### 4.36.0 - 17 Jul 2021
- Tag selector in the repository viewer - Tag selector in the repository viewer
- Link iusses/pull requests of other repositories - Link issues/pull requests of other repositories
- Files and lines can be linked in the diff view - Files and lines can be linked in the diff view
- Option to disable XSS protection - Option to disable XSS protection

View File

@@ -3,10 +3,10 @@ import com.jsuereth.sbtpgp.PgpKeys._
val Organization = "io.github.gitbucket" val Organization = "io.github.gitbucket"
val Name = "gitbucket" val Name = "gitbucket"
val GitBucketVersion = "4.36.0" val GitBucketVersion = "4.36.2"
val ScalatraVersion = "2.7.1" val ScalatraVersion = "2.8.2"
val JettyVersion = "9.4.43.v20210629" val JettyVersion = "9.4.44.v20210927"
val JgitVersion = "5.12.0.202106070339-r" val JgitVersion = "5.13.0.202109080827-r"
lazy val root = (project in file(".")) lazy val root = (project in file("."))
.enablePlugins(SbtTwirl, ScalatraPlugin) .enablePlugins(SbtTwirl, ScalatraPlugin)
@@ -15,7 +15,7 @@ sourcesInBase := false
organization := Organization organization := Organization
name := Name name := Name
version := GitBucketVersion version := GitBucketVersion
scalaVersion := "2.13.6" scalaVersion := "2.13.7"
scalafmtOnCompile := true scalafmtOnCompile := true
@@ -34,7 +34,7 @@ libraryDependencies ++= Seq(
"org.scalatra" %% "scalatra" % ScalatraVersion cross CrossVersion.for3Use2_13, "org.scalatra" %% "scalatra" % ScalatraVersion cross CrossVersion.for3Use2_13,
"org.scalatra" %% "scalatra-json" % ScalatraVersion cross CrossVersion.for3Use2_13, "org.scalatra" %% "scalatra-json" % ScalatraVersion cross CrossVersion.for3Use2_13,
"org.scalatra" %% "scalatra-forms" % ScalatraVersion cross CrossVersion.for3Use2_13, "org.scalatra" %% "scalatra-forms" % ScalatraVersion cross CrossVersion.for3Use2_13,
"org.json4s" %% "json4s-jackson" % "3.6.11" cross CrossVersion.for3Use2_13, "org.json4s" %% "json4s-jackson" % "4.0.3" cross CrossVersion.for3Use2_13,
"commons-io" % "commons-io" % "2.11.0", "commons-io" % "commons-io" % "2.11.0",
"io.github.gitbucket" % "solidbase" % "1.0.3", "io.github.gitbucket" % "solidbase" % "1.0.3",
"io.github.gitbucket" % "markedj" % "1.0.16", "io.github.gitbucket" % "markedj" % "1.0.16",
@@ -43,33 +43,33 @@ libraryDependencies ++= Seq(
"commons-net" % "commons-net" % "3.8.0", "commons-net" % "commons-net" % "3.8.0",
"org.apache.httpcomponents" % "httpclient" % "4.5.13", "org.apache.httpcomponents" % "httpclient" % "4.5.13",
"org.apache.sshd" % "apache-sshd" % "2.1.0" exclude ("org.slf4j", "slf4j-jdk14") exclude ("org.apache.sshd", "sshd-mina") exclude ("org.apache.sshd", "sshd-netty"), "org.apache.sshd" % "apache-sshd" % "2.1.0" exclude ("org.slf4j", "slf4j-jdk14") exclude ("org.apache.sshd", "sshd-mina") exclude ("org.apache.sshd", "sshd-netty"),
"org.apache.tika" % "tika-core" % "1.27", "org.apache.tika" % "tika-core" % "2.1.0",
"com.github.takezoe" %% "blocking-slick-32" % "0.0.12" cross CrossVersion.for3Use2_13, "com.github.takezoe" %% "blocking-slick-32" % "0.0.12" cross CrossVersion.for3Use2_13,
"com.novell.ldap" % "jldap" % "2009-10-07", "com.novell.ldap" % "jldap" % "2009-10-07",
"com.h2database" % "h2" % "1.4.199", "com.h2database" % "h2" % "1.4.199",
"org.mariadb.jdbc" % "mariadb-java-client" % "2.7.3", "org.mariadb.jdbc" % "mariadb-java-client" % "2.7.4",
"org.postgresql" % "postgresql" % "42.2.23", "org.postgresql" % "postgresql" % "42.3.1",
"ch.qos.logback" % "logback-classic" % "1.2.3", "ch.qos.logback" % "logback-classic" % "1.2.6",
"com.zaxxer" % "HikariCP" % "4.0.3", "com.zaxxer" % "HikariCP" % "4.0.3" exclude ("org.slf4j", "slf4j-api"),
"com.typesafe" % "config" % "1.4.1", "com.typesafe" % "config" % "1.4.1",
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0", "fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0",
"io.github.java-diff-utils" % "java-diff-utils" % "4.10", "io.github.java-diff-utils" % "java-diff-utils" % "4.11",
"org.cache2k" % "cache2k-all" % "1.6.0.Final", "org.cache2k" % "cache2k-all" % "1.6.0.Final",
"net.coobird" % "thumbnailator" % "0.4.14", "net.coobird" % "thumbnailator" % "0.4.14",
"com.github.zafarkhaja" % "java-semver" % "0.9.0", "com.github.zafarkhaja" % "java-semver" % "0.9.0",
"com.nimbusds" % "oauth2-oidc-sdk" % "9.10.1", "com.nimbusds" % "oauth2-oidc-sdk" % "9.19",
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided", "org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided", "javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
"junit" % "junit" % "4.13.2" % "test", "junit" % "junit" % "4.13.2" % "test",
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test" cross CrossVersion.for3Use2_13, "org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test" cross CrossVersion.for3Use2_13,
"org.mockito" % "mockito-core" % "3.11.2" % "test", "org.mockito" % "mockito-core" % "4.0.0" % "test",
"com.dimafeng" %% "testcontainers-scala" % "0.39.5" % "test", "com.dimafeng" %% "testcontainers-scala" % "0.39.11" % "test",
"org.testcontainers" % "mysql" % "1.15.3" % "test", "org.testcontainers" % "mysql" % "1.16.2" % "test",
"org.testcontainers" % "postgresql" % "1.15.3" % "test", "org.testcontainers" % "postgresql" % "1.16.2" % "test",
"net.i2p.crypto" % "eddsa" % "0.3.0", "net.i2p.crypto" % "eddsa" % "0.3.0",
"is.tagomor.woothee" % "woothee-java" % "1.11.0", "is.tagomor.woothee" % "woothee-java" % "1.11.0",
"org.ec4j.core" % "ec4j-core" % "0.3.0", "org.ec4j.core" % "ec4j-core" % "0.3.0",
"org.kohsuke" % "github-api" % "1.131" % "test" "org.kohsuke" % "github-api" % "1.135" % "test"
) )
libraryDependencies ~= { libraryDependencies ~= {
@@ -200,7 +200,7 @@ executableKey := {
manifest.getMainAttributes put (AttrName.MANIFEST_VERSION, "1.0") manifest.getMainAttributes put (AttrName.MANIFEST_VERSION, "1.0")
manifest.getMainAttributes put (AttrName.MAIN_CLASS, "JettyLauncher") manifest.getMainAttributes put (AttrName.MAIN_CLASS, "JettyLauncher")
val outputFile = workDir / warName val outputFile = workDir / warName
IO jar (contentMappings.map { case (file, path) => (file, path.toString) }, outputFile, manifest) IO jar (contentMappings.map { case (file, path) => (file, path.toString) }, outputFile, manifest, None)
// generate checksums // generate checksums
Seq( Seq(

View File

@@ -1,10 +1,11 @@
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature") scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3")
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.5.1") addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.5.1")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.0.0") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.1.0")
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.4") addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.4")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2")
addSbtPlugin("com.typesafe.sbt" % "sbt-license-report" % "1.2.0") addSbtPlugin("com.typesafe.sbt" % "sbt-license-report" % "1.2.0")
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.9.2")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.8.2")
addDependencyTreePlugin

View File

@@ -1,51 +1,100 @@
import org.eclipse.jetty.server.ConnectionFactory; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.SecuredRedirectHandler;
import org.eclipse.jetty.server.handler.StatisticsHandler; import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.server.session.DefaultSessionCache; import org.eclipse.jetty.server.session.DefaultSessionCache;
import org.eclipse.jetty.server.session.FileSessionDataStore; import org.eclipse.jetty.server.session.FileSessionDataStore;
import org.eclipse.jetty.server.session.SessionCache; import org.eclipse.jetty.server.session.SessionCache;
import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import java.io.File; import java.io.File;
import java.net.InetAddress;
import java.net.URL; import java.net.URL;
import java.net.InetSocketAddress;
import java.security.ProtectionDomain; import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toSet;
public class JettyLauncher { public class JettyLauncher {
private interface Defaults {
String CONNECTORS = "http";
String HOST = "0.0.0.0";
int HTTP_PORT = 8080;
int HTTPS_PORT = 8443;
boolean REDIRECT_HTTPS = false;
}
private interface Connectors {
String HTTP = "http";
String HTTPS = "https";
}
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
System.setProperty("java.awt.headless", "true"); System.setProperty("java.awt.headless", "true");
String host = null; String connectors = getEnvironmentVariable("gitbucket.connectors");
String port = null; String host = getEnvironmentVariable("gitbucket.host");
InetSocketAddress address = null; String port = getEnvironmentVariable("gitbucket.port");
String contextPath = "/"; String securePort = getEnvironmentVariable("gitbucket.securePort");
String tmpDirPath=""; String keyStorePath = getEnvironmentVariable("gitbucket.keyStorePath");
boolean forceHttps = false; String keyStorePassword = getEnvironmentVariable("gitbucket.keyStorePassword");
String keyManagerPassword = getEnvironmentVariable("gitbucket.keyManagerPassword");
String redirectHttps = getEnvironmentVariable("gitbucket.redirectHttps");
String contextPath = getEnvironmentVariable("gitbucket.prefix");
String tmpDirPath = getEnvironmentVariable("gitbucket.tempDir");
boolean saveSessions = false; boolean saveSessions = false;
host = getEnvironmentVariable("gitbucket.host");
port = getEnvironmentVariable("gitbucket.port");
contextPath = getEnvironmentVariable("gitbucket.prefix");
tmpDirPath = getEnvironmentVariable("gitbucket.tempDir");
for(String arg: args) { for(String arg: args) {
if(arg.equals("--save_sessions")) { if(arg.equals("--save_sessions")) {
saveSessions = true; saveSessions = true;
} }
if(arg.startsWith("--") && arg.contains("=")) { if(arg.startsWith("--") && arg.contains("=")) {
String[] dim = arg.split("="); String[] dim = arg.split("=", 2);
if(dim.length >= 2) { if(dim.length == 2) {
switch (dim[0]) { switch (dim[0]) {
case "--connectors":
connectors = dim[1];
break;
case "--host": case "--host":
host = dim[1]; host = dim[1];
break; break;
case "--port": case "--port":
port = dim[1]; port = dim[1];
break; break;
case "--secure_port":
securePort = dim[1];
break;
case "--key_store_path":
keyStorePath = dim[1];
break;
case "--key_store_password":
keyStorePassword = dim[1];
break;
case "--key_manager_password":
keyManagerPassword = dim[1];
break;
case "--redirect_https":
redirectHttps = dim[1];
break;
case "--prefix": case "--prefix":
contextPath = dim[1]; contextPath = dim[1];
break; break;
@@ -67,38 +116,69 @@ public class JettyLauncher {
contextPath = "/" + contextPath; contextPath = "/" + contextPath;
} }
if(host != null) { final String hostName = InetAddress.getByName(fallback(host, Defaults.HOST)).getHostName();
address = new InetSocketAddress(host, getPort(port));
} else { final Server server = new Server();
address = new InetSocketAddress(getPort(port));
final Set<String> connectorsSet = Stream.of(fallback(connectors, Defaults.CONNECTORS)
.toLowerCase().split(",")).map(String::trim).collect(toSet());
final List<ServerConnector> connectorInstances = new ArrayList<>();
final HttpConfiguration httpConfig = new HttpConfiguration();
httpConfig.setSendServerVersion(false);
if (connectorsSet.contains(Connectors.HTTPS)) {
httpConfig.setSecurePort(fallback(securePort, Defaults.HTTPS_PORT, Integer::parseInt));
} }
Server server = new Server(address); if (connectorsSet.contains(Connectors.HTTP)) {
final ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(httpConfig));
connector.setHost(hostName);
connector.setPort(fallback(port, Defaults.HTTP_PORT, Integer::parseInt));
// SelectChannelConnector connector = new SelectChannelConnector(); connectorInstances.add(connector);
// if(host != null) {
// connector.setHost(host);
// }
// connector.setMaxIdleTime(1000 * 60 * 60);
// connector.setSoLingerTime(-1);
// connector.setPort(port);
// server.addConnector(connector);
// Disabling Server header
for (Connector connector : server.getConnectors()) {
for (ConnectionFactory factory : connector.getConnectionFactories()) {
if (factory instanceof HttpConnectionFactory) {
((HttpConnectionFactory) factory).getHttpConfiguration().setSendServerVersion(false);
}
}
} }
if (connectorsSet.contains(Connectors.HTTPS)) {
final SslContextFactory sslContextFactory = new SslContextFactory.Server();
sslContextFactory.setKeyStorePath(requireNonNull(keyStorePath,
"You must specify a path to an SSL keystore via the --key_store_path command line argument" +
" or GITBUCKET_KEYSTOREPATH environment variable."));
sslContextFactory.setKeyStorePassword(requireNonNull(keyStorePassword,
"You must specify a an SSL keystore password via the --key_store_password argument" +
" or GITBUCKET_KEYSTOREPASSWORD environment variable."));
sslContextFactory.setKeyManagerPassword(requireNonNull(keyManagerPassword,
"You must specify a key manager password via the --key_manager_password' argument" +
" or GITBUCKET_KEYMANAGERPASSWORD environment variable."));
final HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
httpsConfig.addCustomizer(new SecureRequestCustomizer());
final ServerConnector connector = new ServerConnector(server,
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()),
new HttpConnectionFactory(httpsConfig));
connector.setHost(hostName);
connector.setPort(fallback(securePort, Defaults.HTTPS_PORT, Integer::parseInt));
connectorInstances.add(connector);
}
require(!connectorInstances.isEmpty(),
"No server connectors could be configured, please check your --connectors command line argument" +
" or GITBUCKET_CONNECTORS environment variable.");
server.setConnectors(connectorInstances.toArray(new ServerConnector[0]));
WebAppContext context = new WebAppContext(); WebAppContext context = new WebAppContext();
if(saveSessions) { if(saveSessions) {
File sessDir = new File(getGitBucketHome(), "sessions"); File sessDir = new File(getGitBucketHome(), "sessions");
if(!sessDir.exists()){ if(!sessDir.exists()){
sessDir.mkdirs(); mkdir(sessDir);
} }
SessionHandler sessions = context.getSessionHandler(); SessionHandler sessions = context.getSessionHandler();
SessionCache cache = new DefaultSessionCache(sessions); SessionCache cache = new DefaultSessionCache(sessions);
@@ -112,7 +192,7 @@ public class JettyLauncher {
if(tmpDirPath == null || tmpDirPath.equals("")){ if(tmpDirPath == null || tmpDirPath.equals("")){
tmpDir = new File(getGitBucketHome(), "tmp"); tmpDir = new File(getGitBucketHome(), "tmp");
if(!tmpDir.exists()){ if(!tmpDir.exists()){
tmpDir.mkdirs(); mkdir(tmpDir);
} }
} else { } else {
tmpDir = new File(tmpDirPath); tmpDir = new File(tmpDirPath);
@@ -136,13 +216,16 @@ public class JettyLauncher {
context.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml"); context.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml");
context.setServer(server); context.setServer(server);
context.setWar(location.toExternalForm()); context.setWar(location.toExternalForm());
if (forceHttps) {
context.setInitParameter("org.scalatra.ForceHttps", "true"); final HandlerList handlers = new HandlerList();
if (fallback(redirectHttps, Defaults.REDIRECT_HTTPS, Boolean::parseBoolean)) {
handlers.addHandler(new SecuredRedirectHandler());
} }
Handler handler = addStatisticsHandler(context); handlers.addHandler(addStatisticsHandler(context));
server.setHandler(handler); server.setHandler(handlers);
server.setStopAtShutdown(true); server.setStopAtShutdown(true);
server.setStopTimeout(7_000); server.setStopTimeout(7_000);
server.start(); server.start();
@@ -170,11 +253,28 @@ public class JettyLauncher {
} }
} }
private static int getPort(String port){ private static <T, R> T fallback(R value, T defaultValue, Function<R, T> converter) {
if(port == null) { return value == null ? defaultValue : converter.apply(value);
return 8080; }
} else {
return Integer.parseInt(port); private static <T> T fallback(T value, T defaultValue) {
return fallback(value, defaultValue, identity());
}
private static void require(boolean condition, String message) {
if (!condition) {
throw new IllegalArgumentException(message);
}
}
private static <T> T requireNonNull(T value, String message) {
require(value != null, message);
return value;
}
private static void mkdir(File dir) {
if (!dir.mkdirs()) {
throw new RuntimeException("Unable to create directory: " + dir);
} }
} }

View File

@@ -20,15 +20,15 @@ public class PatchUtil {
public static String apply(String source, String patch, FileHeader fh) public static String apply(String source, String patch, FileHeader fh)
throws IOException, PatchApplyException { throws IOException, PatchApplyException {
RawText rt = new RawText(source.getBytes("UTF-8")); RawText rt = new RawText(source.getBytes("UTF-8"));
List<String> oldLines = new ArrayList<String>(rt.size()); List<String> oldLines = new ArrayList<>(rt.size());
for (int i = 0; i < rt.size(); i++) for (int i = 0; i < rt.size(); i++)
oldLines.add(rt.getString(i)); oldLines.add(rt.getString(i));
List<String> newLines = new ArrayList<String>(oldLines); List<String> newLines = new ArrayList<>(oldLines);
for (HunkHeader hh : fh.getHunks()) { for (HunkHeader hh : fh.getHunks()) {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(patch.getBytes("UTF-8"), hh.getStartOffset(), hh.getEndOffset() - hh.getStartOffset()); out.write(patch.getBytes("UTF-8"), hh.getStartOffset(), hh.getEndOffset() - hh.getStartOffset());
RawText hrt = new RawText(out.toByteArray()); RawText hrt = new RawText(out.toByteArray());
List<String> hunkLines = new ArrayList<String>(hrt.size()); List<String> hunkLines = new ArrayList<>(hrt.size());
for (int i = 0; i < hrt.size(); i++) for (int i = 0; i < hrt.size(); i++)
hunkLines.add(hrt.getString(i)); hunkLines.add(hrt.getString(i));
int pos = 0; int pos = 0;

View File

@@ -1,4 +1,4 @@
notifications:1.10.0 notifications:1.10.0
gist:4.20.0 gist:4.21.0
emoji:4.6.0 emoji:4.6.0
pages:1.9.0 pages:1.10.0

View File

@@ -3,7 +3,6 @@ package gitbucket.core
import java.io.FileOutputStream import java.io.FileOutputStream
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.sql.Connection import java.sql.Connection
import java.util
import java.util.UUID import java.util.UUID
import gitbucket.core.model.Activity import gitbucket.core.model.Activity
@@ -84,7 +83,7 @@ object GitBucketCoreModule
new Version( new Version(
"4.34.0", "4.34.0",
new Migration() { new Migration() {
override def migrate(moduleId: String, version: String, context: util.Map[String, AnyRef]): Unit = { override def migrate(moduleId: String, version: String, context: java.util.Map[String, AnyRef]): Unit = {
implicit val formats: Formats = Serialization.formats(NoTypeHints) implicit val formats: Formats = Serialization.formats(NoTypeHints)
import JDBCUtil._ import JDBCUtil._
@@ -118,5 +117,7 @@ object GitBucketCoreModule
new Version("4.35.1"), new Version("4.35.1"),
new Version("4.35.2"), new Version("4.35.2"),
new Version("4.35.3"), new Version("4.35.3"),
new Version("4.36.0", new LiquibaseMigration("update/gitbucket-core_4.36.xml")) new Version("4.36.0", new LiquibaseMigration("update/gitbucket-core_4.36.xml")),
new Version("4.36.1"),
new Version("4.36.2")
) )

View File

@@ -17,7 +17,8 @@ case class ApiIssue(
state: String, state: String,
created_at: Date, created_at: Date,
updated_at: Date, updated_at: Date,
body: String body: String,
milestone: Option[ApiMilestone]
)(repositoryName: RepositoryName, isPullRequest: Boolean) { )(repositoryName: RepositoryName, isPullRequest: Boolean) {
val id = 0 // dummy id val id = 0 // dummy id
val assignees = List(assignee).flatten val assignees = List(assignee).flatten
@@ -43,7 +44,8 @@ object ApiIssue {
repositoryName: RepositoryName, repositoryName: RepositoryName,
user: ApiUser, user: ApiUser,
assignee: Option[ApiUser], assignee: Option[ApiUser],
labels: List[ApiLabel] labels: List[ApiLabel],
milestone: Option[ApiMilestone]
): ApiIssue = ): ApiIssue =
ApiIssue( ApiIssue(
number = issue.issueId, number = issue.issueId,
@@ -51,6 +53,7 @@ object ApiIssue {
user = user, user = user,
assignee = assignee, assignee = assignee,
labels = labels, labels = labels,
milestone = milestone,
state = if (issue.closed) { "closed" } else { "open" }, state = if (issue.closed) { "closed" } else { "open" },
body = issue.content.getOrElse(""), body = issue.content.getOrElse(""),
created_at = issue.registeredDate, created_at = issue.registeredDate,

View File

@@ -86,7 +86,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
val newForm = mapping( val newForm = mapping(
"userName" -> trim(label("User name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))), "userName" -> trim(label("User name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
"password" -> trim(label("Password", text(required, maxlength(20)))), "password" -> trim(label("Password", text(required, maxlength(40)))),
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))), "fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))), "mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
"extraMailAddresses" -> list( "extraMailAddresses" -> list(
@@ -98,7 +98,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
)(AccountNewForm.apply) )(AccountNewForm.apply)
val editForm = mapping( val editForm = mapping(
"password" -> trim(label("Password", optional(text(maxlength(20))))), "password" -> trim(label("Password", optional(text(maxlength(40))))),
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))), "fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))), "mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
"extraMailAddresses" -> list( "extraMailAddresses" -> list(
@@ -434,7 +434,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
post("/:userName/_personalToken", personalTokenForm)(oneselfOnly { form => post("/:userName/_personalToken", personalTokenForm)(oneselfOnly { form =>
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName).map { x => getAccountByUserName(userName).foreach { x =>
val (tokenId, token) = generateAccessToken(userName, form.note) val (tokenId, token) = generateAccessToken(userName, form.note)
flash.update("generatedToken", (tokenId, token)) flash.update("generatedToken", (tokenId, token))
} }

View File

@@ -32,6 +32,7 @@ trait PreProcessControllerBase extends ControllerBase {
get(!context.settings.allowAnonymousAccess, context.loginAccount.isEmpty) { get(!context.settings.allowAnonymousAccess, context.loginAccount.isEmpty) {
if (!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") && if (!context.currentPath.startsWith("/assets") && !context.currentPath.startsWith("/signin") &&
!context.currentPath.startsWith("/register") && !context.currentPath.endsWith("/info/refs") && !context.currentPath.startsWith("/register") && !context.currentPath.endsWith("/info/refs") &&
!context.currentPath.startsWith("/plugin-assets") &&
!PluginRegistry().getAnonymousAccessiblePaths().exists { path => !PluginRegistry().getAnonymousAccessiblePaths().exists { path =>
context.currentPath.startsWith(path) context.currentPath.startsWith(path)
}) { }) {

View File

@@ -242,25 +242,21 @@ trait PullRequestsControllerBase extends ControllerBase {
branchIsOutOfDate = JGitUtil.getShaByRef(repository.owner, repository.name, pullreq.branch) != Some( branchIsOutOfDate = JGitUtil.getShaByRef(repository.owner, repository.name, pullreq.branch) != Some(
pullreq.commitIdFrom pullreq.commitIdFrom
), ),
needStatusCheck = context.loginAccount needStatusCheck = context.loginAccount.forall { u =>
.map { u => branchProtection.needStatusCheck(u.userName)
branchProtection.needStatusCheck(u.userName) },
}
.getOrElse(true),
hasUpdatePermission = hasDeveloperRole( hasUpdatePermission = hasDeveloperRole(
pullreq.requestUserName, pullreq.requestUserName,
pullreq.requestRepositoryName, pullreq.requestRepositoryName,
context.loginAccount context.loginAccount
) && ) &&
context.loginAccount context.loginAccount.exists { u =>
.map { u => !getProtectedBranchInfo(
!getProtectedBranchInfo( pullreq.requestUserName,
pullreq.requestUserName, pullreq.requestRepositoryName,
pullreq.requestRepositoryName, pullreq.requestBranch
pullreq.requestBranch ).needStatusCheck(u.userName)
).needStatusCheck(u.userName) },
}
.getOrElse(false),
hasMergePermission = hasMergePermission, hasMergePermission = hasMergePermission,
commitIdTo = pullreq.commitIdTo commitIdTo = pullreq.commitIdTo
) )
@@ -494,8 +490,7 @@ trait PullRequestsControllerBase extends ControllerBase {
(repository.userName, repository.repositoryName, repository.defaultBranch) (repository.userName, repository.repositoryName, repository.defaultBranch)
}, },
commits.flatten commits.flatten
.map(commit => getCommitComments(forkedRepository.owner, forkedRepository.name, commit.id, false)) .flatMap(commit => getCommitComments(forkedRepository.owner, forkedRepository.name, commit.id, false))
.flatten
.toList, .toList,
originId, originId,
forkedId, forkedId,

View File

@@ -685,7 +685,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
getPathObjectId(git, path, revCommit).map { getPathObjectId(git, path, revCommit).map {
objectId => objectId =>
if (raw) { if (raw) {
// Download (This route is left for backword compatibility) // Download (This route is left for backward compatibility)
responseRawFile(git, objectId, path, repository) responseRawFile(git, objectId, path, repository)
} else { } else {
val info = EditorConfigUtil.getEditorConfigInfo(git, id, path) val info = EditorConfigUtil.getEditorConfigInfo(git, id, path)

View File

@@ -186,7 +186,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
val newUserForm = mapping( val newUserForm = mapping(
"userName" -> trim(label("Username", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))), "userName" -> trim(label("Username", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
"password" -> trim(label("Password", text(required, maxlength(20)))), "password" -> trim(label("Password", text(required, maxlength(40)))),
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))), "fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))), "mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
"extraMailAddresses" -> list( "extraMailAddresses" -> list(
@@ -200,7 +200,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
val editUserForm = mapping( val editUserForm = mapping(
"userName" -> trim(label("Username", text(required, maxlength(100), identifier))), "userName" -> trim(label("Username", text(required, maxlength(100), identifier))),
"password" -> trim(label("Password", optional(text(maxlength(20))))), "password" -> trim(label("Password", optional(text(maxlength(40))))),
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))), "fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))), "mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
"extraMailAddresses" -> list( "extraMailAddresses" -> list(
@@ -361,8 +361,8 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
}) })
get("/admin/users")(adminOnly { get("/admin/users")(adminOnly {
val includeRemoved = params.get("includeRemoved").map(_.toBoolean).getOrElse(false) val includeRemoved = params.get("includeRemoved").exists(_.toBoolean)
val includeGroups = params.get("includeGroups").map(_.toBoolean).getOrElse(false) val includeGroups = params.get("includeGroups").exists(_.toBoolean)
val users = getAllUsers(includeRemoved, includeGroups) val users = getAllUsers(includeRemoved, includeGroups)
val members = users.collect { val members = users.collect {
case account if (account.isGroupAccount) => case account if (account.isGroupAccount) =>

View File

@@ -47,7 +47,8 @@ trait ApiIssueControllerBase extends ControllerBase {
user = ApiUser(issueUser), user = ApiUser(issueUser),
assignee = assignedUser.map(ApiUser(_)), assignee = assignedUser.map(ApiUser(_)),
labels = getIssueLabels(repository.owner, repository.name, issue.issueId) labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
.map(ApiLabel(_, RepositoryName(repository))) .map(ApiLabel(_, RepositoryName(repository))),
issue.milestoneId.flatMap { getApiMilestone(repository, _) }
) )
}) })
}) })
@@ -69,7 +70,8 @@ trait ApiIssueControllerBase extends ControllerBase {
RepositoryName(repository), RepositoryName(repository),
ApiUser(openedUser), ApiUser(openedUser),
issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)), issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository))) getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository))),
issue.milestoneId.flatMap { getApiMilestone(repository, _) }
) )
) )
}) getOrElse NotFound() }) getOrElse NotFound()
@@ -103,7 +105,8 @@ trait ApiIssueControllerBase extends ControllerBase {
ApiUser(loginAccount), ApiUser(loginAccount),
issue.assignedUserName.flatMap(getAccountByUserName(_)).map(ApiUser(_)), issue.assignedUserName.flatMap(getAccountByUserName(_)).map(ApiUser(_)),
getIssueLabels(repository.owner, repository.name, issue.issueId) getIssueLabels(repository.owner, repository.name, issue.issueId)
.map(ApiLabel(_, RepositoryName(repository))) .map(ApiLabel(_, RepositoryName(repository))),
issue.milestoneId.flatMap { getApiMilestone(repository, _) }
) )
) )
}) getOrElse NotFound() }) getOrElse NotFound()

View File

@@ -121,7 +121,7 @@ trait ApiIssueLabelControllerBase extends ControllerBase {
*/ */
post("/api/v3/repos/:owner/:repository/issues/:id/labels")(writableUsersOnly { repository => post("/api/v3/repos/:owner/:repository/issues/:id/labels")(writableUsersOnly { repository =>
JsonFormat(for { JsonFormat(for {
data <- extractFromJsonBody[Seq[String]]; data <- extractFromJsonBody[Seq[String]]
issueId <- params("id").toIntOpt issueId <- params("id").toIntOpt
} yield { } yield {
data.map { labelName => data.map { labelName =>
@@ -160,7 +160,7 @@ trait ApiIssueLabelControllerBase extends ControllerBase {
*/ */
put("/api/v3/repos/:owner/:repository/issues/:id/labels")(writableUsersOnly { repository => put("/api/v3/repos/:owner/:repository/issues/:id/labels")(writableUsersOnly { repository =>
JsonFormat(for { JsonFormat(for {
data <- extractFromJsonBody[Seq[String]]; data <- extractFromJsonBody[Seq[String]]
issueId <- params("id").toIntOpt issueId <- params("id").toIntOpt
} yield { } yield {
deleteAllIssueLabels(repository.owner, repository.name, issueId, true) deleteAllIssueLabels(repository.owner, repository.name, issueId, true)

View File

@@ -102,17 +102,4 @@ trait ApiIssueMilestoneControllerBase extends ControllerBase {
NoContent() NoContent()
}) })
private def getApiMilestone(repository: RepositoryInfo, milestoneId: Int): Option[ApiMilestone] = {
getMilestonesWithIssueCount(repository.owner, repository.name)
.find(p => p._1.milestoneId == milestoneId)
.map(
milestoneWithIssue =>
ApiMilestone(
repository.repository,
milestoneWithIssue._1,
milestoneWithIssue._2,
milestoneWithIssue._3
)
)
}
} }

View File

@@ -86,7 +86,7 @@ trait ApiReleaseControllerBase extends ControllerBase {
/** /**
* vi. Edit a release * vi. Edit a release
* https://developer.github.com/v3/repos/releases/#edit-a-release * https://developer.github.com/v3/repos/releases/#edit-a-release
* Incompatiblity info: GitHub API requires :release_id, but GitBucket API requires :tag_name * Incompatibility info: GitHub API requires :release_id, but GitBucket API requires :tag_name
*/ */
patch("/api/v3/repos/:owner/:repository/releases/:tag")(writableUsersOnly { repository => patch("/api/v3/repos/:owner/:repository/releases/:tag")(writableUsersOnly { repository =>
(for { (for {
@@ -103,7 +103,7 @@ trait ApiReleaseControllerBase extends ControllerBase {
/** /**
* vii. Delete a release * vii. Delete a release
* https://developer.github.com/v3/repos/releases/#delete-a-release * https://developer.github.com/v3/repos/releases/#delete-a-release
* Incompatiblity info: GitHub API requires :release_id, but GitBucket API requires :tag_name * Incompatibility info: GitHub API requires :release_id, but GitBucket API requires :tag_name
*/ */
delete("/api/v3/repos/:owner/:repository/releases/:tag")(writableUsersOnly { repository => delete("/api/v3/repos/:owner/:repository/releases/:tag")(writableUsersOnly { repository =>
val tag = params("tag") val tag = params("tag")

View File

@@ -40,7 +40,7 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true), diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress), author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress), committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
commentCount = getCommitComment(repository.owner, repository.name, commitInfo.id.toString).size commentCount = getCommitComment(repository.owner, repository.name, commitInfo.id).size
) )
}) })
} }

View File

@@ -3,7 +3,7 @@ import gitbucket.core.api.{ApiCommit, ApiContents, ApiError, CreateAFile, JsonFo
import gitbucket.core.controller.ControllerBase import gitbucket.core.controller.ControllerBase
import gitbucket.core.service.{RepositoryCommitFileService, RepositoryService} import gitbucket.core.service.{RepositoryCommitFileService, RepositoryService}
import gitbucket.core.util.Directory.getRepositoryDir import gitbucket.core.util.Directory.getRepositoryDir
import gitbucket.core.util.JGitUtil.{CommitInfo, FileInfo, getContentFromId, getFileList} import gitbucket.core.util.JGitUtil.{CommitInfo, FileInfo, getContentFromId, getFileList, getSummaryMessage}
import gitbucket.core.util._ import gitbucket.core.util._
import gitbucket.core.view.helpers.{isRenderable, renderMarkup} import gitbucket.core.view.helpers.{isRenderable, renderMarkup}
import gitbucket.core.util.Implicits._ import gitbucket.core.util.Implicits._
@@ -49,19 +49,21 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
getContents(repository, multiParams("splat").head, params.getOrElse("ref", repository.repository.defaultBranch)) getContents(repository, multiParams("splat").head, params.getOrElse("ref", repository.repository.defaultBranch))
}) })
private def getFileInfo(git: Git, revision: String, pathStr: String, ignoreCase: Boolean): Option[FileInfo] = { private def getFileInfo(git: Git, revision: String, pathStr: String): Option[FileInfo] = {
val (dirName, fileName) = pathStr.lastIndexOf('/') match { val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(revision))
case -1 => getPathObjectId(git, pathStr, revCommit).map { objectId =>
(".", pathStr) FileInfo(
case n => id = objectId,
(pathStr.take(n), pathStr.drop(n + 1)) isDirectory = false,
} name = pathStr.split("/").last,
if (ignoreCase) { path = pathStr.split("/").dropRight(1).mkString("/"),
getFileList(git, revision, dirName, maxFiles = context.settings.repositoryViewer.maxFiles) message = getSummaryMessage(revCommit.getFullMessage, revCommit.getShortMessage),
.find(_.name.toLowerCase.equals(fileName.toLowerCase)) commitId = revCommit.getName,
} else { time = revCommit.getAuthorIdent.getWhen,
getFileList(git, revision, dirName, maxFiles = context.settings.repositoryViewer.maxFiles) author = revCommit.getAuthorIdent.getName,
.find(_.name.equals(fileName)) mailAddress = revCommit.getAuthorIdent.getEmailAddress,
linkUrl = None
)
} }
} }
@@ -74,16 +76,17 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git => Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
val fileList = getFileList(git, refStr, path, maxFiles = context.settings.repositoryViewer.maxFiles) val fileList = getFileList(git, refStr, path, maxFiles = context.settings.repositoryViewer.maxFiles)
if (fileList.isEmpty) { // file or NotFound if (fileList.isEmpty) { // file or NotFound
getFileInfo(git, refStr, path, ignoreCase) val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(refStr))
.flatMap { f => getPathObjectId(git, path, revCommit)
.flatMap { objectId =>
val largeFile = params.get("large_file").exists(s => s.equals("true")) val largeFile = params.get("large_file").exists(s => s.equals("true"))
val content = getContentFromId(git, f.id, largeFile) val content = getContentFromId(git, objectId, largeFile)
request.getHeader("Accept") match { request.getHeader("Accept") match {
case "application/vnd.github.v3.raw" => { case "application/vnd.github.v3.raw" => {
contentType = "application/vnd.github.v3.raw" contentType = "application/vnd.github.v3.raw"
content content
} }
case "application/vnd.github.v3.html" if isRenderable(f.name) => { case "application/vnd.github.v3.html" if isRenderable(path) => {
contentType = "application/vnd.github.v3.html" contentType = "application/vnd.github.v3.html"
content.map { c => content.map { c =>
List( List(
@@ -114,11 +117,12 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
} }
} }
case _ => case _ =>
Some(JsonFormat(ApiContents(f, RepositoryName(repository), content))) getFileInfo(git, refStr, path).map { f =>
JsonFormat(ApiContents(f, RepositoryName(repository), content))
}
} }
} }
.getOrElse(NotFound()) .getOrElse(NotFound())
} else { // directory } else { // directory
JsonFormat(fileList.map { f => JsonFormat(fileList.map { f =>
ApiContents(f, RepositoryName(repository), None) ApiContents(f, RepositoryName(repository), None)
@@ -148,9 +152,7 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
val path = paths.take(paths.size - 1).toList.mkString("/") val path = paths.take(paths.size - 1).toList.mkString("/")
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) {
git => git =>
val fileInfo = getFileInfo(git, commit, path, false) getFileInfo(git, commit, path) match {
fileInfo match {
case Some(f) if !data.sha.contains(f.id.getName) => case Some(f) if !data.sha.contains(f.id.getName) =>
ApiError( ApiError(
"The blob SHA is not matched.", "The blob SHA is not matched.",

View File

@@ -47,7 +47,7 @@ trait AccountService {
case _ => None case _ => None
} }
case account if (!account.isGroupAccount && account.password == sha1(password)) => Some(account) case account if (!account.isGroupAccount && account.password == sha1(password)) => Some(account)
} getOrElse None }.flatten
} }
/** /**

View File

@@ -39,9 +39,7 @@ trait ActivityService {
if (isPublic == false) { if (isPublic == false) {
list += activity list += activity
} else { } else {
if (!getRepositoryInfoFromCache(activity.userName, activity.repositoryName) if (!getRepositoryInfoFromCache(activity.userName, activity.repositoryName).forall(_.isPrivate)) {
.map(_.isPrivate)
.getOrElse(true)) {
list += activity list += activity
} }
} }
@@ -61,9 +59,7 @@ trait ActivityService {
var json: String = null 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) val activity = read[Activity](json)
if (!getRepositoryInfoFromCache(activity.userName, activity.repositoryName) if (!getRepositoryInfoFromCache(activity.userName, activity.repositoryName).forall(_.isPrivate)) {
.map(_.isPrivate)
.getOrElse(true)) {
list += activity list += activity
} }
} }
@@ -83,9 +79,7 @@ trait ActivityService {
val activity = read[Activity](json) val activity = read[Activity](json)
if (owners.contains(activity.userName)) { if (owners.contains(activity.userName)) {
list += activity list += activity
} else if (!getRepositoryInfoFromCache(activity.userName, activity.repositoryName) } else if (!getRepositoryInfoFromCache(activity.userName, activity.repositoryName).forall(_.isPrivate)) {
.map(_.isPrivate)
.getOrElse(true)) {
list += activity list += activity
} }
} }

View File

@@ -336,13 +336,16 @@ trait IssuesService {
implicit s: Session implicit s: Session
) = ) =
Issues filter { t1 => Issues filter { t1 =>
(if (repos.size == 1) { (if (repos.sizeIs == 1) {
t1.byRepository(repos.head._1, repos.head._2) t1.byRepository(repos.head._1, repos.head._2)
} else { } else {
((t1.userName ++ "/" ++ t1.repositoryName) inSetBind (repos.map { case (owner, repo) => s"$owner/$repo" })) ((t1.userName ++ "/" ++ t1.repositoryName) inSetBind (repos.map { case (owner, repo) => s"$owner/$repo" }))
}) && }) &&
(t1.closed === (condition.state == "closed").bind) (condition.state match {
.&&(t1.milestoneId.? isEmpty, condition.milestone == Some(None)) case "open" => t1.closed === false
case "closed" => t1.closed === true
case _ => t1.closed === true || t1.closed === false
}).&&(t1.milestoneId.? isEmpty, condition.milestone == Some(None))
.&&(t1.priorityId.? isEmpty, condition.priority == Some(None)) .&&(t1.priorityId.? isEmpty, condition.priority == Some(None))
.&&(t1.assignedUserName.? isEmpty, condition.assigned == Some(None)) .&&(t1.assignedUserName.? isEmpty, condition.assigned == Some(None))
.&&(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) && .&&(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) &&
@@ -939,7 +942,7 @@ object IssuesService {
case x => Some(x) case x => Some(x)
}, },
param(request, "mentioned"), param(request, "mentioned"),
param(request, "state", Seq("open", "closed")).getOrElse("open"), param(request, "state", Seq("open", "closed", "all")).getOrElse("open"),
param(request, "sort", Seq("created", "comments", "updated", "priority")).getOrElse("created"), param(request, "sort", Seq("created", "comments", "updated", "priority")).getOrElse("created"),
param(request, "direction", Seq("asc", "desc")).getOrElse("desc"), param(request, "direction", Seq("asc", "desc")).getOrElse("desc"),
param(request, "visibility"), param(request, "visibility"),
@@ -960,7 +963,7 @@ object IssuesService {
case x => Some(x) case x => Some(x)
}, },
param(request, "mentioned"), param(request, "mentioned"),
param(request, "state", Seq("open", "closed")).getOrElse("open"), param(request, "state", Seq("open", "closed", "all")).getOrElse("open"),
param(request, "sort", Seq("created", "comments", "updated", "priority")).getOrElse("created"), param(request, "sort", Seq("created", "comments", "updated", "priority")).getOrElse("created"),
param(request, "direction", Seq("asc", "desc")).getOrElse("desc"), param(request, "direction", Seq("asc", "desc")).getOrElse("desc"),
param(request, "visibility"), param(request, "visibility"),

View File

@@ -1,9 +1,11 @@
package gitbucket.core.service package gitbucket.core.service
import gitbucket.core.api.ApiMilestone
import gitbucket.core.model.Milestone import gitbucket.core.model.Milestone
import gitbucket.core.model.Profile._ import gitbucket.core.model.Profile._
import gitbucket.core.model.Profile.profile.blockingApi._ import gitbucket.core.model.Profile.profile.blockingApi._
import gitbucket.core.model.Profile.dateColumnType import gitbucket.core.model.Profile.dateColumnType
import gitbucket.core.service.RepositoryService.RepositoryInfo
trait MilestonesService { trait MilestonesService {
@@ -73,4 +75,17 @@ trait MilestonesService {
.sortBy(t => (t.dueDate.asc, t.closedDate.desc, t.milestoneId.desc)) .sortBy(t => (t.dueDate.asc, t.closedDate.desc, t.milestoneId.desc))
.list .list
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
)
)
}
} }

View File

@@ -71,7 +71,7 @@ object ProtectedBranchService {
pusher: String, pusher: String,
mergePullRequest: Boolean mergePullRequest: Boolean
)(implicit session: Session): Option[String] = { )(implicit session: Session): Option[String] = {
if (mergePullRequest == true) { if (mergePullRequest) {
None None
} else { } else {
checkBranchProtection(owner, repository, receivePack, command, pusher) checkBranchProtection(owner, repository, receivePack, command, pusher)
@@ -153,9 +153,9 @@ object ProtectedBranchService {
Some("Cannot force-push to a protected branch") Some("Cannot force-push to a protected branch")
case ReceiveCommand.Type.UPDATE | ReceiveCommand.Type.UPDATE_NONFASTFORWARD if needStatusCheck(pusher) => case ReceiveCommand.Type.UPDATE | ReceiveCommand.Type.UPDATE_NONFASTFORWARD if needStatusCheck(pusher) =>
unSuccessedContexts(command.getNewId.name) match { unSuccessedContexts(command.getNewId.name) match {
case s if s.size == 1 => Some(s"""Required status check "${s.toSeq(0)}" is expected""") case s if s.sizeIs == 1 => Some(s"""Required status check "${s.toSeq(0)}" is expected""")
case s if s.size >= 1 => Some(s"${s.size} of ${contexts.size} required status checks are expected") case s if s.sizeIs >= 1 => Some(s"${s.size} of ${contexts.size} required status checks are expected")
case _ => None case _ => None
} }
case ReceiveCommand.Type.DELETE => case ReceiveCommand.Type.DELETE =>
Some("Cannot delete a protected branch") Some("Cannot delete a protected branch")

View File

@@ -509,10 +509,11 @@ trait PullRequestService {
def getPullRequestComments(userName: String, repositoryName: String, issueId: Int, commits: Seq[CommitInfo])( def getPullRequestComments(userName: String, repositoryName: String, issueId: Int, commits: Seq[CommitInfo])(
implicit s: Session implicit s: Session
): Seq[Comment] = { ): Seq[Comment] = {
(commits (commits.flatMap(commit => getCommitComments(userName, repositoryName, commit.id, true)) ++ getComments(
.map(commit => getCommitComments(userName, repositoryName, commit.id, true)) userName,
.flatten ++ getComments(userName, repositoryName, issueId)) repositoryName,
.groupBy { issueId
)).groupBy {
case x: IssueComment => (Some(x.commentId), None, None, None) case x: IssueComment => (Some(x.commentId), None, None, None)
case x: CommitComment if x.fileName.isEmpty => (Some(x.commentId), None, None, None) case x: CommitComment if x.fileName.isEmpty => (Some(x.commentId), None, None, None)
case x: CommitComment => (None, x.fileName, x.originalOldLine, x.originalNewLine) case x: CommitComment => (None, x.fileName, x.originalOldLine, x.originalNewLine)

View File

@@ -25,7 +25,7 @@ object RepositoryCreationService {
private val Creating = new ConcurrentHashMap[String, Option[String]]() private val Creating = new ConcurrentHashMap[String, Option[String]]()
def isCreating(owner: String, repository: String): Boolean = { def isCreating(owner: String, repository: String): Boolean = {
Option(Creating.get(s"${owner}/${repository}")).map(_.isEmpty).getOrElse(false) Option(Creating.get(s"${owner}/${repository}")).exists(_.isEmpty)
} }
def startCreation(owner: String, repository: String): Unit = { def startCreation(owner: String, repository: String): Unit = {
@@ -40,7 +40,7 @@ object RepositoryCreationService {
} }
def getCreationError(owner: String, repository: String): Option[String] = { def getCreationError(owner: String, repository: String): Option[String] = {
Option(Creating.remove(s"${owner}/${repository}")).getOrElse(None) Option(Creating.remove(s"${owner}/${repository}")).flatten
} }
} }

View File

@@ -766,7 +766,8 @@ trait RepositoryService {
JGitUtil.getContentFromId(git, file.id, true).collect { JGitUtil.getContentFromId(git, file.id, true).collect {
case bytes if FileUtil.isText(bytes) => StringUtil.convertFromByteArray(bytes) case bytes if FileUtil.isText(bytes) => StringUtil.convertFromByteArray(bytes)
} }
} getOrElse None }
.flatten
} getOrElse "" } getOrElse ""
} }
} }

View File

@@ -72,7 +72,7 @@ trait SystemSettingsService {
} }
} }
} }
props.setProperty(SkinName, settings.skinName.toString) props.setProperty(SkinName, settings.skinName)
settings.userDefinedCss.foreach(x => props.setProperty(UserDefinedCss, x)) settings.userDefinedCss.foreach(x => props.setProperty(UserDefinedCss, x))
props.setProperty(ShowMailAddress, settings.showMailAddress.toString) props.setProperty(ShowMailAddress, settings.showMailAddress.toString)
props.setProperty(WebHookBlockPrivateAddress, settings.webHook.blockPrivateAddress.toString) props.setProperty(WebHookBlockPrivateAddress, settings.webHook.blockPrivateAddress.toString)

View File

@@ -35,6 +35,7 @@ import org.apache.http.HttpRequest
import org.apache.http.HttpResponse import org.apache.http.HttpResponse
import gitbucket.core.model.WebHookContentType import gitbucket.core.model.WebHookContentType
import gitbucket.core.service.SystemSettingsService.SystemSettings import gitbucket.core.service.SystemSettingsService.SystemSettings
import gitbucket.core.view.helpers.getApiMilestone
import org.apache.http.client.entity.EntityBuilder import org.apache.http.client.entity.EntityBuilder
import org.apache.http.entity.ContentType import org.apache.http.entity.ContentType
@@ -394,7 +395,8 @@ trait WebHookPullRequestService extends WebHookService {
ApiUser(issueUser), ApiUser(issueUser),
issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)), issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
getIssueLabels(repository.owner, repository.name, issue.issueId) getIssueLabels(repository.owner, repository.name, issue.issueId)
.map(ApiLabel(_, RepositoryName(repository))) .map(ApiLabel(_, RepositoryName(repository))),
getApiMilestone(repository, issue.milestoneId getOrElse (0))
), ),
sender = ApiUser(sender) sender = ApiUser(sender)
) )
@@ -576,6 +578,7 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
commenter <- users.get(issueComment.commentedUserName) commenter <- users.get(issueComment.commentedUserName)
assignedUser = issue.assignedUserName.flatMap(users.get(_)) assignedUser = issue.assignedUserName.flatMap(users.get(_))
labels = getIssueLabels(repository.owner, repository.name, issue.issueId) labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
milestone = getApiMilestone(repository, issue.milestoneId getOrElse (0))
} yield { } yield {
WebHookIssueCommentPayload( WebHookIssueCommentPayload(
issue = issue, issue = issue,
@@ -586,7 +589,8 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
repositoryUser = repoOwner, repositoryUser = repoOwner,
assignedUser = assignedUser, assignedUser = assignedUser,
sender = sender, sender = sender,
labels = labels labels = labels,
milestone = milestone
) )
} }
} }
@@ -760,7 +764,8 @@ object WebHookService {
repositoryUser: Account, repositoryUser: Account,
assignedUser: Option[Account], assignedUser: Option[Account],
sender: Account, sender: Account,
labels: List[Label] labels: List[Label],
milestone: Option[ApiMilestone]
): WebHookIssueCommentPayload = ): WebHookIssueCommentPayload =
WebHookIssueCommentPayload( WebHookIssueCommentPayload(
action = "created", action = "created",
@@ -770,7 +775,8 @@ object WebHookService {
RepositoryName(repository), RepositoryName(repository),
ApiUser(issueUser), ApiUser(issueUser),
assignedUser.map(ApiUser(_)), assignedUser.map(ApiUser(_)),
labels.map(ApiLabel(_, RepositoryName(repository))) labels.map(ApiLabel(_, RepositoryName(repository))),
milestone
), ),
comment = comment =
ApiComment(comment, RepositoryName(repository), issue.issueId, ApiUser(commentUser), issue.isPullRequest), ApiComment(comment, RepositoryName(repository), issue.issueId, ApiUser(commentUser), issue.isPullRequest),

View File

@@ -75,13 +75,15 @@ trait WikiService {
def getWikiPage(owner: String, repository: String, pageName: String): Option[WikiPageInfo] = { def getWikiPage(owner: String, repository: String, pageName: String): Option[WikiPageInfo] = {
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git => Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
if (!JGitUtil.isEmpty(git)) { if (!JGitUtil.isEmpty(git)) {
JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file => val fileName = pageName + ".md"
JGitUtil.getLatestCommitFromPath(git, fileName, "master").map { latestCommit =>
val content = JGitUtil.getContentFromPath(git, latestCommit.getTree, fileName, true)
WikiPageInfo( WikiPageInfo(
file.name, fileName,
StringUtil.convertFromByteArray(git.getRepository.open(file.id).getBytes), StringUtil.convertFromByteArray(content.getOrElse(Array.empty)),
file.author, latestCommit.getAuthorIdent.getName,
file.time, latestCommit.getAuthorIdent.getWhen,
file.commitId latestCommit.getName
) )
} }
} else None } else None
@@ -145,7 +147,7 @@ trait WikiService {
if (!p.getErrors.isEmpty) { if (!p.getErrors.isEmpty) {
throw new PatchFormatException(p.getErrors()) throw new PatchFormatException(p.getErrors())
} }
val revertInfo = (p.getFiles.asScala.map { fh => val revertInfo = p.getFiles.asScala.flatMap { fh =>
fh.getChangeType match { fh.getChangeType match {
case DiffEntry.ChangeType.MODIFY => { case DiffEntry.ChangeType.MODIFY => {
val source = val source =
@@ -174,7 +176,7 @@ trait WikiService {
} }
case _ => Nil case _ => Nil
} }
}).flatten }
if (revertInfo.nonEmpty) { if (revertInfo.nonEmpty) {
val builder = DirCache.newInCore.builder() val builder = DirCache.newInCore.builder()
@@ -255,8 +257,7 @@ trait WikiService {
created = false created = false
updated = JGitUtil updated = JGitUtil
.getContentFromId(git, tree.getEntryObjectId, true) .getContentFromId(git, tree.getEntryObjectId, true)
.map(new String(_, "UTF-8") != content) .exists(new String(_, "UTF-8") != content)
.getOrElse(false)
} }
} }
} }

View File

@@ -139,7 +139,7 @@ class GitAuthenticationFilter extends Filter with RepositoryService with Account
case _ => case _ =>
() => () =>
{ {
logger.debug(s"Not enough path arguments: ${request.paths}") logger.debug(s"Not enough path arguments: ${request.paths.mkString(", ")}")
response.sendError(HttpServletResponse.SC_NOT_FOUND) response.sendError(HttpServletResponse.SC_NOT_FOUND)
} }
} }

View File

@@ -347,9 +347,9 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
// set PR as merged // set PR as merged
val pulls = getPullRequestsByBranch(owner, repository, branchName, Some(false)) val pulls = getPullRequestsByBranch(owner, repository, branchName, Some(false))
pulls.foreach { pull => pulls.foreach { pull =>
if (commits.find { c => if (commits.exists { c =>
c.id == pull.commitIdTo c.id == pull.commitIdTo
}.isDefined) { }) {
markMergeAndClosePullRequest(pusher, owner, repository, pull) markMergeAndClosePullRequest(pusher, owner, repository, pull)
getAccountByUserName(pusher).foreach { pusherAccount => getAccountByUserName(pusher).foreach { pusherAccount =>
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount, settings) callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount, settings)

View File

@@ -144,11 +144,9 @@ class DefaultGitUploadPack(owner: String, repoName: String)
override protected def runTask(authType: AuthType): Unit = { override protected def runTask(authType: AuthType): Unit = {
val execute = Database() withSession { implicit session => val execute = Database() withSession { implicit session =>
getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", "")) getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", "")).exists { repositoryInfo =>
.map { repositoryInfo => !repositoryInfo.repository.isPrivate || isReadableUser(authType, repositoryInfo)
!repositoryInfo.repository.isPrivate || isReadableUser(authType, repositoryInfo) }
}
.getOrElse(false)
} }
if (execute) { if (execute) {
@@ -169,11 +167,9 @@ class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String, ss
override protected def runTask(authType: AuthType): Unit = { override protected def runTask(authType: AuthType): Unit = {
val execute = Database() withSession { implicit session => val execute = Database() withSession { implicit session =>
getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", "")) getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", "")).exists { repositoryInfo =>
.map { repositoryInfo => isWritableUser(authType, repositoryInfo)
isWritableUser(authType, repositoryInfo) }
}
.getOrElse(false)
} }
if (execute) { if (execute) {

View File

@@ -38,7 +38,7 @@ trait OwnerAuthenticator { self: ControllerBase with RepositoryService with Acco
case Some(x) if (repository.owner == x.userName) => action(repository) case Some(x) if (repository.owner == x.userName) => action(repository)
// TODO Repository management is allowed for only group managers? // TODO Repository management is allowed for only group managers?
case Some(x) if (getGroupMembers(repository.owner).exists { m => case Some(x) if (getGroupMembers(repository.owner).exists { m =>
m.userName == x.userName && m.isManager == true m.userName == x.userName && m.isManager
}) => }) =>
action(repository) action(repository)
case Some(x) if (getCollaboratorUserNames(userName, repoName, Seq(Role.ADMIN)).contains(x.userName)) => case Some(x) if (getCollaboratorUserNames(userName, repoName, Seq(Role.ADMIN)).contains(x.userName)) =>

View File

@@ -52,7 +52,7 @@ object EditorConfigUtil {
} }
override def getParent: ResourcePath = { override def getParent: ResourcePath = {
Option(path.getParentPath).map { new JGitResourcePath(repo, revStr, _) }.getOrElse(null) Option(path.getParentPath).map { new JGitResourcePath(repo, revStr, _) }.orNull
} }
override def openRandomReader(): Resource.RandomReader = { override def openRandomReader(): Resource.RandomReader = {
@@ -70,7 +70,7 @@ object EditorConfigUtil {
private class JGitResourcePath(repo: Repository, revStr: String, path: Ec4jPath) extends ResourcePath { private class JGitResourcePath(repo: Repository, revStr: String, path: Ec4jPath) extends ResourcePath {
override def getParent: ResourcePath = { override def getParent: ResourcePath = {
Option(path.getParentPath).map { new JGitResourcePath(repo, revStr, _) }.getOrElse(null) Option(path.getParentPath).map { new JGitResourcePath(repo, revStr, _) }.orNull
} }
override def getPath: Ec4jPath = { override def getPath: Ec4jPath = {

View File

@@ -227,7 +227,7 @@ object JDBCUtil {
if (noPreds.isEmpty) { if (noPreds.isEmpty) {
if (hasPreds.isEmpty) done else sys.error(hasPreds.toString) if (hasPreds.isEmpty) done else sys.error(hasPreds.toString)
} else { } else {
val found = noPreds.map { _._1 } val found = noPreds.keys
tsort(hasPreds.map { case (k, v) => (k, v -- found) }, done ++ found) tsort(hasPreds.map { case (k, v) => (k, v -- found) }, done ++ found)
} }
} }

View File

@@ -37,9 +37,8 @@ object JGitUtil {
private val logger = LoggerFactory.getLogger(JGitUtil.getClass) private val logger = LoggerFactory.getLogger(JGitUtil.getClass)
implicit val objectDatabaseReleasable: Releasable[ObjectDatabase] = new Releasable[ObjectDatabase] { implicit val objectDatabaseReleasable: Releasable[ObjectDatabase] =
override def release(resource: ObjectDatabase): Unit = resource.close() _.close()
}
/** /**
* The repository data. * The repository data.
@@ -383,7 +382,7 @@ object JGitUtil {
path: String = ".", path: String = ".",
baseUrl: Option[String] = None, baseUrl: Option[String] = None,
commitCount: Int = 0, commitCount: Int = 0,
maxFiles: Int = 100 maxFiles: Int = 5
): List[FileInfo] = { ): List[FileInfo] = {
Using.resource(new RevWalk(git.getRepository)) { revWalk => Using.resource(new RevWalk(git.getRepository)) { revWalk =>
val objectId = git.getRepository.resolve(revision) val objectId = git.getRepository.resolve(revision)
@@ -513,7 +512,7 @@ object JGitUtil {
/** /**
* Returns the first line of the commit message. * Returns the first line of the commit message.
*/ */
private def getSummaryMessage(fullMessage: String, shortMessage: String): String = { def getSummaryMessage(fullMessage: String, shortMessage: String): String = {
val i = fullMessage.trim.indexOf('\n') val i = fullMessage.trim.indexOf('\n')
val firstLine = if (i >= 0) fullMessage.trim.substring(0, i).trim else fullMessage val firstLine = if (i >= 0) fullMessage.trim.substring(0, i).trim else fullMessage
if (firstLine.length > shortMessage.length) shortMessage else firstLine if (firstLine.length > shortMessage.length) shortMessage else firstLine
@@ -659,9 +658,13 @@ object JGitUtil {
*/ */
def getLatestCommitFromPaths(git: Git, paths: List[String], revision: String): Map[String, RevCommit] = { def getLatestCommitFromPaths(git: Git, paths: List[String], revision: String): Map[String, RevCommit] = {
val start = getRevCommitFromId(git, git.getRepository.resolve(revision)) val start = getRevCommitFromId(git, git.getRepository.resolve(revision))
paths.map { path => paths.flatMap { path =>
val commit = git.log.add(start).addPath(path).setMaxCount(1).call.iterator.next val commit = git.log.add(start).addPath(path).setMaxCount(1).call.iterator.next
(path, commit) if (commit == null) {
None
} else {
Some((path, commit))
}
}.toMap }.toMap
} }
@@ -672,11 +675,10 @@ object JGitUtil {
df.setDiffComparator(RawTextComparator.DEFAULT) df.setDiffComparator(RawTextComparator.DEFAULT)
df.setDetectRenames(true) df.setDetectRenames(true)
getDiffEntries(git, from, to) getDiffEntries(git, from, to)
.map { entry => .foreach { entry =>
df.format(entry) df.format(entry)
new String(out.toByteArray, "UTF-8")
} }
.mkString("\n") new String(out.toByteArray, "UTF-8")
} }
private def getDiffEntries(git: Git, from: Option[String], to: String): Seq[DiffEntry] = { private def getDiffEntries(git: Git, from: Option[String], to: String): Seq[DiffEntry] = {
@@ -1259,7 +1261,7 @@ object JGitUtil {
val blame = blamer.call() val blame = blamer.call()
var blameMap = Map[String, JGitUtil.BlameInfo]() var blameMap = Map[String, JGitUtil.BlameInfo]()
var idLine = List[(String, Int)]() var idLine = List[(String, Int)]()
0.to(blame.getResultContents().size() - 1).map { i => 0.until(blame.getResultContents().size()).foreach { i =>
val c = blame.getSourceCommit(i) val c = blame.getSourceCommit(i)
if (!blameMap.contains(c.name)) { if (!blameMap.contains(c.name)) {
blameMap += c.name -> JGitUtil.BlameInfo( blameMap += c.name -> JGitUtil.BlameInfo(

View File

@@ -41,7 +41,7 @@ class Mailer(settings: SystemSettings) {
htmlMsg: Option[String] = None, htmlMsg: Option[String] = None,
loginAccount: Option[Account] = None loginAccount: Option[Account] = None
): Option[HtmlEmail] = { ): Option[HtmlEmail] = {
if (settings.notification == true) { if (settings.notification) {
settings.smtp.map { smtp => settings.smtp.map { smtp =>
val email = new HtmlEmail val email = new HtmlEmail
email.setHostName(smtp.host) email.setHostName(smtp.host)
@@ -51,7 +51,7 @@ class Mailer(settings: SystemSettings) {
} }
smtp.ssl.foreach { ssl => smtp.ssl.foreach { ssl =>
email.setSSLOnConnect(ssl) email.setSSLOnConnect(ssl)
if (ssl == true) { if (ssl) {
email.setSslSmtpPort(smtp.port.get.toString) email.setSslSmtpPort(smtp.port.get.toString)
} }
} }

View File

@@ -184,8 +184,7 @@ object StringUtil {
def removeUserName(baseUrl: String): String = baseUrl.replaceFirst("(https?://).+@", "$1") def removeUserName(baseUrl: String): String = baseUrl.replaceFirst("(https?://).+@", "$1")
gitRepositoryUrl match { gitRepositoryUrl match {
case GitBucketUrlPattern(base, user, repository) case GitBucketUrlPattern(base, user, repository) if baseUrl.exists(removeUserName(base).startsWith) =>
if baseUrl.map(removeUserName(base).startsWith).getOrElse(false) =>
s"${removeUserName(base)}/$user/$repository" s"${removeUserName(base)}/$user/$repository"
case GitHubUrlPattern(_, user, repository) => s"https://github.com/$user/$repository" case GitHubUrlPattern(_, user, repository) => s"https://github.com/$user/$repository"
case BitBucketUrlPattern(_, user, repository) => s"https://bitbucket.org/$user/$repository" case BitBucketUrlPattern(_, user, repository) => s"https://bitbucket.org/$user/$repository"

View File

@@ -45,11 +45,14 @@ trait AvatarImageProvider { self: RequestCache =>
if (tooltip) { if (tooltip) {
Html( Html(
s"""<img src="${src}" class="${if (size > 20) { "avatar" } else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;" data-toggle="tooltip" title="${userName}" alt="@${userName}" />""" 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 { } else {
Html( Html(
s"""<img src="${src}" class="${if (size > 20) { "avatar" } else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;" alt="@${userName}" />""" s"""<img src="${src}" class="${if (size > 20) { "avatar" } else { "avatar-mini" }}" style="width: ${size}px; height: ${size}px;"
| alt="@${StringUtil.escapeHtml(userName)}" />""".stripMargin
) )
} }
} }

View File

@@ -336,7 +336,7 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
)(implicit context: Context): Html = { )(implicit context: Context): Html = {
val avatarHtml = avatar(userName, size, tooltip, mailAddress) val avatarHtml = avatar(userName, size, tooltip, mailAddress)
val contentHtml = if (label == true) Html(avatarHtml.body + " " + userName) else avatarHtml val contentHtml = if (label) Html(avatarHtml.body + " " + userName) else avatarHtml
userWithContent(userName, mailAddress)(contentHtml) userWithContent(userName, mailAddress)(contentHtml)
} }
@@ -446,14 +446,14 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
} }
result.append(c) result.append(c)
} }
case '>' if tag == true => { case '>' if tag => {
tag = false tag = false
result.append(c) result.append(c)
} }
case _ if tag == false => { case _ if tag == false => {
text.append(c) text.append(c)
} }
case _ if tag == true => { case _ if tag => {
result.append(c) result.append(c)
} }
} }

View File

@@ -1,18 +1,18 @@
@(account: gitbucket.core.model.Account, @(account: gitbucket.core.model.Account,
personalTokens: List[gitbucket.core.model.AccessToken], personalTokens: List[gitbucket.core.model.AccessToken],
gneratedToken: Option[(gitbucket.core.model.AccessToken, String)])(implicit context: gitbucket.core.controller.Context) generatedToken: Option[(gitbucket.core.model.AccessToken, String)])(implicit context: gitbucket.core.controller.Context)
@gitbucket.core.html.main("Applications"){ @gitbucket.core.html.main("Applications"){
@gitbucket.core.account.html.menu("application", context.loginAccount.get.userName, false){ @gitbucket.core.account.html.menu("application", context.loginAccount.get.userName, false){
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading strong">Personal access tokens</div> <div class="panel-heading strong">Personal access tokens</div>
<div class="panel-body"> <div class="panel-body">
@if(personalTokens.isEmpty && gneratedToken.isEmpty){ @if(personalTokens.isEmpty && generatedToken.isEmpty){
No tokens. No tokens.
} else { } else {
Tokens you have generated which can be used to access the GitBucket API. Tokens you have generated which can be used to access the GitBucket API.
<hr style="margin-top: 10px;"> <hr style="margin-top: 10px;">
} }
@gneratedToken.map { case (token, tokenString) => @generatedToken.map { case (token, tokenString) =>
<div class="alert alert-info"> <div class="alert alert-info">
Make sure to copy your new personal access token now. You won't be able to see it again! Make sure to copy your new personal access token now. You won't be able to see it again!
</div> </div>

View File

@@ -9,32 +9,22 @@
@gitbucket.core.html.main(s"Compare Revisions - ${repository.owner}/${repository.name}", Some(repository)){ @gitbucket.core.html.main(s"Compare Revisions - ${repository.owner}/${repository.name}", Some(repository)){
@gitbucket.core.helper.html.information(info) @gitbucket.core.helper.html.information(info)
@gitbucket.core.html.menu("wiki", repository){ @gitbucket.core.html.menu("wiki", repository){
<ul class="nav nav-tabs fill-width"> <div class="pull-right">
<li> @if(pageName.isDefined){
<h1 class="wiki-title"><span class="muted">Compare Revisions</span></h1> <a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)">View Page</a>
</li> <a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_history">Back to Page History</a>
<li class="pull-right"> } else {
<div class="btn-group"> <a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/_history">Back to Wiki History</a>
@if(pageName.isDefined){ }
<a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)">View Page</a> @if(isEditable) {
<a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_history">Back to Page History</a> @if(pageName.isDefined) {
<a href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_revert/@from...@to" class="btn btn-danger">Revert Changes</a>
} else { } else {
<a class="btn btn-small btn-default" href="@helpers.url(repository)/wiki/_history">Back to Wiki History</a> <a href="@helpers.url(repository)/wiki/_revert/@from...@to" class="btn btn-danger">Revert Changes</a>
} }
</div> }
</li>
</ul>
<div class="pull-left">
@gitbucket.core.helper.html.diff(diffs, repository, None, None, false, None, false, false)
</div> </div>
@if(isEditable){ <h1 class="body-title"><span class="muted">Compare Revisions</span></h1>
<div> @gitbucket.core.helper.html.diff(diffs, repository, None, None, false, None, false, false)
@if(pageName.isDefined){
<a href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_revert/@from...@to" class="btn">Revert Changes</a>
} else {
<a href="@helpers.url(repository)/wiki/_revert/@from...@to" class="btn">Revert Changes</a>
}
</div>
}
} }
} }

View File

@@ -11,7 +11,7 @@
<a class="btn btn-danger" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_delete" id="delete">Delete Page</a> <a class="btn btn-danger" href="@helpers.url(repository)/wiki/@helpers.urlEncode(pageName)/_delete" id="delete">Delete Page</a>
} }
</div> </div>
<h1 class="wiki-title"><span class="muted">Editing</span> @if(pageName.isEmpty){New Page} else {@pageName}</h1> <h1 class="body-title"><span class="muted">Editing</span> @if(pageName.isEmpty){New Page} else {@pageName}</h1>
<form action="@helpers.url(repository)/wiki/@if(page.isEmpty){_new} else {_edit}" method="POST" validate="true" autocomplete="off"> <form action="@helpers.url(repository)/wiki/@if(page.isEmpty){_new} else {_edit}" method="POST" validate="true" autocomplete="off">
<span id="error-pageName" class="error"></span> <span id="error-pageName" class="error"></span>
<input type="text" name="pageName" value="@pageName" class="form-control" style="font-weight: bold; margin-bottom: 10px;" placeholder="Input a page name." aria-label="Page name"/> <input type="text" name="pageName" value="@pageName" class="form-control" style="font-weight: bold; margin-bottom: 10px;" placeholder="Input a page name." aria-label="Page name"/>

View File

@@ -15,7 +15,7 @@
} }
</div> </div>
} }
<h1 class="wiki-title"> <h1 class="body-title">
@if(pageName.isEmpty){ @if(pageName.isEmpty){
<span class="muted">History</span> <span class="muted">History</span>
} else { } else {

View File

@@ -4,17 +4,14 @@
@import gitbucket.core.view.helpers @import gitbucket.core.view.helpers
@gitbucket.core.html.main(s"Pages - ${repository.owner}/${repository.name}", Some(repository)){ @gitbucket.core.html.main(s"Pages - ${repository.owner}/${repository.name}", Some(repository)){
@gitbucket.core.html.menu("wiki", repository){ @gitbucket.core.html.menu("wiki", repository){
<ul class="nav nav-tabs fill-width"> <div class="pull-right">
<li> @if(isEditable){
<h1 class="wiki-title"><span class="muted">Pages</span></h1> <a class="btn btn-default" href="@helpers.url(repository)/wiki/_new">New Page</a>
</li> }
<li class="pull-right"> </div>
@if(isEditable){ <h1 class="body-title"><span class="muted">Pages</span></h1>
<a class="btn btn-default" href="@helpers.url(repository)/wiki/_new">New Page</a> <hr>
} <ul>
</li>
</ul>
<ul class="pull-left">
@pages.map { page => @pages.map { page =>
<li><a href="@helpers.url(repository)/wiki/@helpers.urlEncode(page)">@page</a></li> <li><a href="@helpers.url(repository)/wiki/@helpers.urlEncode(page)">@page</a></li>
} }

View File

@@ -150,7 +150,7 @@ class ApiIntegrationTest extends AnyFunSuite {
.content("create") .content("create")
.message("Create content") .message("Create content")
.path("README.md") .path("README.md")
.commit(); .commit()
assert(createResult.getContent.isFile == true) assert(createResult.getContent.isFile == true)
assert(IOUtils.toString(createResult.getContent.read(), "UTF-8") == "create") assert(IOUtils.toString(createResult.getContent.read(), "UTF-8") == "create")
@@ -168,7 +168,7 @@ class ApiIntegrationTest extends AnyFunSuite {
.message("Update content") .message("Update content")
.path("README.md") .path("README.md")
.sha(content1.getSha) .sha(content1.getSha)
.commit(); .commit()
assert(updateResult.getContent.isFile == true) assert(updateResult.getContent.isFile == true)
assert(IOUtils.toString(updateResult.getContent.read(), "UTF-8") == "update") assert(IOUtils.toString(updateResult.getContent.read(), "UTF-8") == "update")

View File

@@ -177,6 +177,16 @@ object ApiSpecModels {
updatedDate = date1 updatedDate = date1
) )
val milestone = Milestone(
userName = repo1Name.owner,
repositoryName = repo1Name.name,
milestoneId = 1,
title = "Test milestone",
description = Some("Milestone description"),
dueDate = Some(date1),
closedDate = Some(date1)
)
// APIs // APIs
val apiUser = ApiUser(account) val apiUser = ApiUser(account)
@@ -193,12 +203,20 @@ object ApiSpecModels {
repositoryName = repo1Name repositoryName = repo1Name
) )
val apiMilestone = ApiMilestone(
repository = repository,
milestone = milestone,
open_issue_count = 1,
closed_issue_count = 1
)
val apiIssue = ApiIssue( val apiIssue = ApiIssue(
issue = issue, issue = issue,
repositoryName = repo1Name, repositoryName = repo1Name,
user = apiUser, user = apiUser,
assignee = Some(apiUser), assignee = Some(apiUser),
labels = List(apiLabel) labels = List(apiLabel),
milestone = Some(apiMilestone)
) )
val apiNotAssignedIssue = ApiIssue( val apiNotAssignedIssue = ApiIssue(
@@ -206,7 +224,8 @@ object ApiSpecModels {
repositoryName = repo1Name, repositoryName = repo1Name,
user = apiUser, user = apiUser,
assignee = None, assignee = None,
labels = List(apiLabel) labels = List(apiLabel),
milestone = Some(apiMilestone)
) )
val apiIssuePR = ApiIssue( val apiIssuePR = ApiIssue(
@@ -214,7 +233,8 @@ object ApiSpecModels {
repositoryName = repo1Name, repositoryName = repo1Name,
user = apiUser, user = apiUser,
assignee = Some(apiUser), assignee = Some(apiUser),
labels = List(apiLabel) labels = List(apiLabel),
milestone = Some(apiMilestone)
) )
val apiComment = ApiComment( val apiComment = ApiComment(
@@ -471,6 +491,19 @@ object ApiSpecModels {
val jsonLabel = val jsonLabel =
"""{"name":"bug","color":"f29513","url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/labels/bug"}""" """{"name":"bug","color":"f29513","url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/labels/bug"}"""
val jsonMilestone = """{
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/milestones/1",
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/milestone/1",
|"id":1,
|"number":1,
|"state":"closed",
|"title":"Test milestone",
|"description":"Milestone description",
|"open_issues":1,"closed_issues":1,
|"closed_at":"2011-04-14T16:00:49Z",
|"due_on":"2011-04-14T16:00:49Z"
|}""".stripMargin
val jsonIssue = s"""{ val jsonIssue = s"""{
|"number":1347, |"number":1347,
|"title":"Found a bug", |"title":"Found a bug",
@@ -481,6 +514,7 @@ object ApiSpecModels {
|"created_at":"2011-04-14T16:00:49Z", |"created_at":"2011-04-14T16:00:49Z",
|"updated_at":"2011-04-14T16:00:49Z", |"updated_at":"2011-04-14T16:00:49Z",
|"body":"I'm having a problem with this.", |"body":"I'm having a problem with this.",
|"milestone":$jsonMilestone,
|"id":0, |"id":0,
|"assignees":[$jsonUser], |"assignees":[$jsonUser],
|"comments_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/issues/1347/comments", |"comments_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/issues/1347/comments",
@@ -496,6 +530,7 @@ object ApiSpecModels {
|"created_at":"2011-04-14T16:00:49Z", |"created_at":"2011-04-14T16:00:49Z",
|"updated_at":"2011-04-14T16:00:49Z", |"updated_at":"2011-04-14T16:00:49Z",
|"body":"I'm having a problem with this.", |"body":"I'm having a problem with this.",
|"milestone":$jsonMilestone,
|"id":0, |"id":0,
|"assignees":[], |"assignees":[],
|"comments_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/issues/1347/comments", |"comments_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/issues/1347/comments",
@@ -512,6 +547,7 @@ object ApiSpecModels {
|"created_at":"2011-04-14T16:00:49Z", |"created_at":"2011-04-14T16:00:49Z",
|"updated_at":"2011-04-14T16:00:49Z", |"updated_at":"2011-04-14T16:00:49Z",
|"body":"Please pull these awesome changes", |"body":"Please pull these awesome changes",
|"milestone":$jsonMilestone,
|"id":0, |"id":0,
|"assignees":[$jsonUser], |"assignees":[$jsonUser],
|"comments_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/issues/1347/comments", |"comments_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/issues/1347/comments",

View File

@@ -121,7 +121,8 @@ class WebHookJsonFormatSpec extends AnyFunSuite {
repositoryUser = account, repositoryUser = account,
assignedUser = Some(account), assignedUser = Some(account),
sender = account, sender = account,
labels = List(label) labels = List(label),
milestone = Some(apiMilestone)
) )
val expected = s"""{ val expected = s"""{
|"action":"created", |"action":"created",

View File

@@ -35,7 +35,8 @@ class AvatarImageProviderSpec extends AnyFunSpec {
assert( assert(
provider.toHtml("user", 32).toString == provider.toHtml("user", 32).toString ==
"""<img src="https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s=32&d=retro&r=g" class="avatar" style="width: 32px; height: 32px;" alt="@user" />""" """<img src="https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s=32&d=retro&r=g" class="avatar" style="width: 32px; height: 32px;"
| alt="@user" />""".stripMargin
) )
} }
@@ -47,7 +48,8 @@ class AvatarImageProviderSpec extends AnyFunSpec {
assert( assert(
provider.toHtml("user", 32).toString == provider.toHtml("user", 32).toString ==
s"""<img src="/user/_avatar?${date}" class="avatar" style="width: 32px; height: 32px;" alt="@user" />""" s"""<img src="/user/_avatar?${date}" class="avatar" style="width: 32px; height: 32px;"
| alt="@user" />""".stripMargin
) )
} }
@@ -59,7 +61,8 @@ class AvatarImageProviderSpec extends AnyFunSpec {
assert( assert(
provider.toHtml("user", 32).toString == provider.toHtml("user", 32).toString ==
s"""<img src="/user/_avatar?${date}" class="avatar" style="width: 32px; height: 32px;" alt="@user" />""" s"""<img src="/user/_avatar?${date}" class="avatar" style="width: 32px; height: 32px;"
| alt="@user" />""".stripMargin
) )
} }
@@ -69,7 +72,8 @@ class AvatarImageProviderSpec extends AnyFunSpec {
assert( assert(
provider.toHtml("user", 20, "hoge@hoge.com").toString == provider.toHtml("user", 20, "hoge@hoge.com").toString ==
"""<img src="https://www.gravatar.com/avatar/4712f9b0e63f56ad952ad387eaa23b9c?s=20&d=retro&r=g" class="avatar-mini" style="width: 20px; height: 20px;" alt="@user" />""" """<img src="https://www.gravatar.com/avatar/4712f9b0e63f56ad952ad387eaa23b9c?s=20&d=retro&r=g" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@user" />""".stripMargin
) )
} }
@@ -79,7 +83,8 @@ class AvatarImageProviderSpec extends AnyFunSpec {
assert( assert(
provider.toHtml("user", 20).toString == provider.toHtml("user", 20).toString ==
"""<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;" alt="@user" />""" """<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@user" />""".stripMargin
) )
} }
@@ -89,7 +94,8 @@ class AvatarImageProviderSpec extends AnyFunSpec {
assert( assert(
provider.toHtml("user", 20, "hoge@hoge.com").toString == provider.toHtml("user", 20, "hoge@hoge.com").toString ==
"""<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;" alt="@user" />""" """<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@user" />""".stripMargin
) )
} }
@@ -99,7 +105,27 @@ class AvatarImageProviderSpec extends AnyFunSpec {
assert( assert(
provider.toHtml("user", 20, "hoge@hoge.com", true).toString == provider.toHtml("user", 20, "hoge@hoge.com", true).toString ==
"""<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;" data-toggle="tooltip" title="user" alt="@user" />""" """<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@user"
| data-toggle="tooltip" title="user" />""".stripMargin
)
}
it("should escape user name") {
implicit val context = Context(createSystemSettings(false), None, request)
val provider = new AvatarImageProviderImpl(None)
assert(
provider.toHtml("""<user>"<name>""", 20, "hoge@hoge.com").toString ==
"""<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@&lt;user&gt;&quot;&lt;name&gt;" />""".stripMargin
)
assert(
provider.toHtml("""<user>"<name>""", 20, "hoge@hoge.com", true).toString ==
"""<img src="/_unknown/_avatar" class="avatar-mini" style="width: 20px; height: 20px;"
| alt="@&lt;user&gt;&quot;&lt;name&gt;"
| data-toggle="tooltip" title="&lt;user&gt;&quot;&lt;name&gt;" />""".stripMargin
) )
} }
} }