mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-10-30 10:06:07 +01:00
Compare commits
86 Commits
4.36.0
...
optimize-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cc3a0d36e | ||
|
|
7f665c649b | ||
|
|
dcbadb4071 | ||
|
|
e3096d15ff | ||
|
|
a83c24e7b3 | ||
|
|
73457c9658 | ||
|
|
cfc8d9f3f1 | ||
|
|
8f423b83ea | ||
|
|
1e7ac532b6 | ||
|
|
0fd1db4596 | ||
|
|
0755b7ab7f | ||
|
|
052382e5c4 | ||
|
|
f44a63cec1 | ||
|
|
603d67354a | ||
|
|
aafa423b9f | ||
|
|
2da9d0a801 | ||
|
|
b60c112a74 | ||
|
|
843ed6df37 | ||
|
|
0f0a849677 | ||
|
|
682901ccbb | ||
|
|
048fdb8837 | ||
|
|
3353616789 | ||
|
|
b6cb4c865f | ||
|
|
1fcfd093f7 | ||
|
|
3f27c6e733 | ||
|
|
b6bd9bfc3b | ||
|
|
6c392f0056 | ||
|
|
9a38de9a23 | ||
|
|
8883600090 | ||
|
|
ab822a3c27 | ||
|
|
0e4d64de23 | ||
|
|
fbc6bd36bd | ||
|
|
ed90ca2dce | ||
|
|
537ef92149 | ||
|
|
d51afa7d40 | ||
|
|
975cffff48 | ||
|
|
d92e9c00e8 | ||
|
|
12d72cbb19 | ||
|
|
d8e03bed1f | ||
|
|
f48c087cd8 | ||
|
|
917663e0df | ||
|
|
556ddbc926 | ||
|
|
1c6f37b8e8 | ||
|
|
720a329a50 | ||
|
|
220a8f076a | ||
|
|
43be8333c7 | ||
|
|
08706ab4df | ||
|
|
b1196657e0 | ||
|
|
334bd0c919 | ||
|
|
cf0f896972 | ||
|
|
d21ca3ff8a | ||
|
|
83f1f16de7 | ||
|
|
0fa2ccf107 | ||
|
|
18e3dd431b | ||
|
|
f25dee2249 | ||
|
|
575ffa9580 | ||
|
|
f17af5aeb0 | ||
|
|
639f153589 | ||
|
|
fb07098c13 | ||
|
|
74fc08b039 | ||
|
|
2776e00004 | ||
|
|
5932fce303 | ||
|
|
39c9fc4261 | ||
|
|
89ea4509a3 | ||
|
|
d19d838ead | ||
|
|
633a2699a8 | ||
|
|
e79bca4a3c | ||
|
|
f8ab480d20 | ||
|
|
a14129e340 | ||
|
|
5ec39df6e0 | ||
|
|
956e0c6321 | ||
|
|
0660a9203a | ||
|
|
1b660272a1 | ||
|
|
d9ef9b874d | ||
|
|
7824f796ee | ||
|
|
838a8d4c7b | ||
|
|
8db9f77f91 | ||
|
|
4f82a469d9 | ||
|
|
0f6ae8559b | ||
|
|
3f4b2eec35 | ||
|
|
a020601d3a | ||
|
|
d511205588 | ||
|
|
fc896b2ea1 | ||
|
|
3819670535 | ||
|
|
067669a18c | ||
|
|
bef7cee8db |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
38
build.sbt
38
build.sbt
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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) =>
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.",
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)) =>
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"/>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -4,20 +4,17 @@
|
|||||||
@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>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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="@<user>"<name>" />""".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="@<user>"<name>"
|
||||||
|
| data-toggle="tooltip" title="<user>"<name>" />""".stripMargin
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user