mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-05-08 23:27:43 +02:00
Compare commits
95 Commits
4.33.0
...
disable-gi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f16f395539 | ||
|
|
3534b7172d | ||
|
|
42a7f974e9 | ||
|
|
b8e02d995a | ||
|
|
e0d038aa92 | ||
|
|
3d01df2bdc | ||
|
|
6f08f1fd23 | ||
|
|
3dd366b394 | ||
|
|
8cf4528959 | ||
|
|
5d77bc5d98 | ||
|
|
572c9ef558 | ||
|
|
7c736c526e | ||
|
|
696d354f3c | ||
|
|
501cbf54ab | ||
|
|
c15d69d566 | ||
|
|
83eb933230 | ||
|
|
43bec9b0df | ||
|
|
279305c202 | ||
|
|
ecbb86c006 | ||
|
|
921fb17ef0 | ||
|
|
0362de7d35 | ||
|
|
cf0d8ea2d0 | ||
|
|
0e9026447d | ||
|
|
cf4d9cb03c | ||
|
|
2a1edeaca3 | ||
|
|
eebabf9b08 | ||
|
|
d882fcad12 | ||
|
|
f976290282 | ||
|
|
f3f9d5dae2 | ||
|
|
71dffd1089 | ||
|
|
f411e98c9a | ||
|
|
1baa489bc7 | ||
|
|
f996b0fc4a | ||
|
|
eb6ba1c800 | ||
|
|
8fac1baa3c | ||
|
|
2d327543b9 | ||
|
|
8a080efe9d | ||
|
|
aaaf61e29e | ||
|
|
1294323df5 | ||
|
|
9ef4e75746 | ||
|
|
ded4ab702d | ||
|
|
f893d045c7 | ||
|
|
ef2e3adcfb | ||
|
|
a1908c5398 | ||
|
|
ec4f0d6531 | ||
|
|
9ef366237c | ||
|
|
9197ad2600 | ||
|
|
2cb7ecd851 | ||
|
|
7d1ad4ce66 | ||
|
|
c274acc8f4 | ||
|
|
7a0d48dd7a | ||
|
|
9c6f9048e1 | ||
|
|
4ff4acfd7e | ||
|
|
d8e9f07721 | ||
|
|
662c5638dd | ||
|
|
02b830d034 | ||
|
|
3734529e5c | ||
|
|
715ec24389 | ||
|
|
5615b23548 | ||
|
|
ca2eeb48cf | ||
|
|
b063c0a80c | ||
|
|
ee8b5692bd | ||
|
|
7a749dca67 | ||
|
|
0378f26ee6 | ||
|
|
4d281273c1 | ||
|
|
e23e11e1a8 | ||
|
|
772835dd22 | ||
|
|
bbf2e57548 | ||
|
|
aecda130b6 | ||
|
|
1d48030e7c | ||
|
|
929ba2fa19 | ||
|
|
6fefa947ca | ||
|
|
072cd2e846 | ||
|
|
5d20cd0365 | ||
|
|
03ec055f66 | ||
|
|
4b6a5e5d49 | ||
|
|
6d21e38159 | ||
|
|
a47c8249bf | ||
|
|
59d7a672b3 | ||
|
|
564e95d36e | ||
|
|
9aee99be74 | ||
|
|
aecd8b503d | ||
|
|
fa65eeea35 | ||
|
|
fe43584c3d | ||
|
|
c255b13dfc | ||
|
|
917b204e5b | ||
|
|
6225fd79fc | ||
|
|
cbec567ef4 | ||
|
|
84b62242d3 | ||
|
|
f17f74c30b | ||
|
|
63b04d5a27 | ||
|
|
f02073a24c | ||
|
|
cb87d126de | ||
|
|
941f8002e5 | ||
|
|
90f4f5cd89 |
29
.github/workflows/build.yml
vendored
Normal file
29
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
java: [8, 11]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
- name: Run tests
|
||||
run: sbt scalafmtSbtCheck scalafmtCheck test:scalafmtCheck test
|
||||
- name: Build executable
|
||||
run: sbt executable
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: gitbucket-java${{ matrix.java }}-${{ github.sha }}
|
||||
path: ./target/executable/gitbucket.*
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -27,4 +27,5 @@ project/plugins/project/
|
||||
|
||||
# Metals specific
|
||||
.metals
|
||||
.bloop
|
||||
.bloop
|
||||
project/metals.sbt
|
||||
@@ -1,3 +1,4 @@
|
||||
version = "1.5.1"
|
||||
project.git = true
|
||||
|
||||
maxColumn = 120
|
||||
|
||||
17
.travis.yml
17
.travis.yml
@@ -1,17 +0,0 @@
|
||||
language: scala
|
||||
sudo: true
|
||||
jdk:
|
||||
- openjdk8
|
||||
- openjdk11
|
||||
script:
|
||||
- sbt scalafmtSbtCheck scalafmtCheck test:scalafmtCheck test
|
||||
before_script:
|
||||
- sudo /etc/init.d/mysql stop
|
||||
- sudo /etc/init.d/postgresql stop
|
||||
- sudo chmod +x /usr/local/bin/sbt
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.ivy2/cache
|
||||
- $HOME/.sbt/boot
|
||||
- $HOME/.sbt/launchers
|
||||
- $HOME/.coursier
|
||||
@@ -3,10 +3,10 @@ All changes to the project will be documented in this file.
|
||||
|
||||
### 4.33.0 - 31 Dec 2019
|
||||
|
||||
- All CLI options are configurable by environmental variables
|
||||
- All CLI options are configurable by environment variables
|
||||
- Folding pull request files
|
||||
- WebHook security options
|
||||
- Add asignee and asignees properties to some Web APIs' response
|
||||
- Add assignee and assignees properties to some Web APIs' response
|
||||
|
||||
### 4.32.0 - 7 Aug 2019
|
||||
|
||||
|
||||
22
README.md
22
README.md
@@ -1,4 +1,4 @@
|
||||
GitBucket [](https://gitter.im/gitbucket/gitbucket) [](https://travis-ci.org/gitbucket/gitbucket) [](https://maven-badges.herokuapp.com/maven-central/io.github.gitbucket/gitbucket_2.13) [](https://github.com/gitbucket/gitbucket/blob/master/LICENSE)
|
||||
GitBucket [](https://gitter.im/gitbucket/gitbucket) [](https://github.com/gitbucket/gitbucket/actions?query=workflow%3Abuild+branch%3Amaster) [](https://maven-badges.herokuapp.com/maven-central/io.github.gitbucket/gitbucket_2.13) [](https://github.com/gitbucket/gitbucket/blob/master/LICENSE)
|
||||
=========
|
||||
|
||||
GitBucket is a Git web platform powered by Scala offering:
|
||||
@@ -31,20 +31,6 @@ GitBucket requires **Java8**. You have to install it, if it is not already insta
|
||||
1. Download the latest **gitbucket.war** from [the releases page](https://github.com/gitbucket/gitbucket/releases) and run it by `java -jar gitbucket.war`.
|
||||
2. Go to `http://[hostname]:8080/` and log in with ID: **root** / Pass: **root**.
|
||||
|
||||
You can specify following options:
|
||||
|
||||
- `--port=[NUMBER]`
|
||||
- `--prefix=[CONTEXTPATH]`
|
||||
- `--host=[HOSTNAME]`
|
||||
- `--gitbucket.home=[DATA_DIR]`
|
||||
- `--temp_dir=[TEMP_DIR]`
|
||||
- `--max_file_size=[MAX_FILE_SIZE]`
|
||||
- `--upload_timeout=[MAX_UPLOAD_TIMEOUT]`
|
||||
|
||||
`TEMP_DIR` is used as the [temporary directory for the jetty application context](https://www.eclipse.org/jetty/documentation/9.3.x/ref-temporary-directories.html). This is the directory into which the `gitbucket.war` file is unpacked, the source files are compiled, etc. If given this parameter **must** match the path of an existing directory or the application will quit reporting an error; if not given the path used will be a `tmp` directory inside the gitbucket home.
|
||||
|
||||
`MAX_FILE_SIZE` is the max file size in bytes for upload files *( default is 3 MB -> 3 x 1024 x 1024 )*.
|
||||
|
||||
You can also deploy `gitbucket.war` to a servlet container which supports Servlet 3.0 (like Jetty, Tomcat, JBoss, etc)
|
||||
|
||||
For more information about installation on Mac or Windows Server (with IIS), or configuration of Apache or Nginx and also integration with other tools or services such as Jenkins or Slack, see [Wiki](https://github.com/gitbucket/gitbucket/wiki).
|
||||
@@ -69,13 +55,13 @@ Support
|
||||
- If you can't find same question and report, send it to [gitter room](https://gitter.im/gitbucket/gitbucket) before raising an issue.
|
||||
- The highest priority of GitBucket is the ease of installation and API compatibility with GitHub, so your feature request might be rejected if they go against those principles.
|
||||
|
||||
What's New in 4.32.x
|
||||
What's New in 4.33.x
|
||||
-------------
|
||||
### 4.33.0 - 31 Dec 2019
|
||||
|
||||
- All CLI options are configurable by environmental variables
|
||||
- All CLI options are configurable by environment variables
|
||||
- Folding pull request files
|
||||
- WebHook security options
|
||||
- Add asignee and asignees properties to some Web APIs' response
|
||||
- Add assignee and assignees properties to some Web APIs' response
|
||||
|
||||
See the [change log](CHANGELOG.md) for all of the updates.
|
||||
|
||||
32
build.sbt
32
build.sbt
@@ -5,8 +5,8 @@ val Organization = "io.github.gitbucket"
|
||||
val Name = "gitbucket"
|
||||
val GitBucketVersion = "4.33.0"
|
||||
val ScalatraVersion = "2.7.0-RC1"
|
||||
val JettyVersion = "9.4.25.v20191220"
|
||||
val JgitVersion = "5.6.0.201912101111-r"
|
||||
val JettyVersion = "9.4.30.v20200611"
|
||||
val JgitVersion = "5.8.0.202006091008-r"
|
||||
|
||||
lazy val root = (project in file("."))
|
||||
.enablePlugins(SbtTwirl, ScalatraPlugin)
|
||||
@@ -36,42 +36,42 @@ libraryDependencies ++= Seq(
|
||||
"org.scalatra" %% "scalatra" % ScalatraVersion,
|
||||
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
|
||||
"org.scalatra" %% "scalatra-forms" % ScalatraVersion,
|
||||
"org.json4s" %% "json4s-jackson" % "3.6.7",
|
||||
"commons-io" % "commons-io" % "2.6",
|
||||
"org.json4s" %% "json4s-jackson" % "3.6.9",
|
||||
"commons-io" % "commons-io" % "2.7",
|
||||
"io.github.gitbucket" % "solidbase" % "1.0.3",
|
||||
"io.github.gitbucket" % "markedj" % "1.0.16",
|
||||
"org.apache.commons" % "commons-compress" % "1.19",
|
||||
"org.apache.commons" % "commons-compress" % "1.20",
|
||||
"org.apache.commons" % "commons-email" % "1.5",
|
||||
"commons-net" % "commons-net" % "3.6",
|
||||
"org.apache.httpcomponents" % "httpclient" % "4.5.10",
|
||||
"org.apache.httpcomponents" % "httpclient" % "4.5.12",
|
||||
"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.23",
|
||||
"org.apache.tika" % "tika-core" % "1.24.1",
|
||||
"com.github.takezoe" %% "blocking-slick-32" % "0.0.12",
|
||||
"com.novell.ldap" % "jldap" % "2009-10-07",
|
||||
"com.h2database" % "h2" % "1.4.199",
|
||||
"org.mariadb.jdbc" % "mariadb-java-client" % "2.5.2",
|
||||
"org.mariadb.jdbc" % "mariadb-java-client" % "2.6.0",
|
||||
"org.postgresql" % "postgresql" % "42.2.6",
|
||||
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
||||
"com.zaxxer" % "HikariCP" % "3.4.1",
|
||||
"com.zaxxer" % "HikariCP" % "3.4.5",
|
||||
"com.typesafe" % "config" % "1.4.0",
|
||||
"com.typesafe.akka" %% "akka-actor" % "2.5.27",
|
||||
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0",
|
||||
"com.github.bkromhout" % "java-diff-utils" % "2.1.1",
|
||||
"org.cache2k" % "cache2k-all" % "1.2.4.Final",
|
||||
"com.enragedginger" %% "akka-quartz-scheduler" % "1.8.1-akka-2.5.x" exclude ("com.mchange", "c3p0") exclude ("com.zaxxer", "HikariCP-java6"),
|
||||
"net.coobird" % "thumbnailator" % "0.4.8",
|
||||
"net.coobird" % "thumbnailator" % "0.4.11",
|
||||
"com.github.zafarkhaja" % "java-semver" % "0.9.0",
|
||||
"com.nimbusds" % "oauth2-oidc-sdk" % "5.64.4",
|
||||
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
|
||||
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
|
||||
"junit" % "junit" % "4.12" % "test",
|
||||
"junit" % "junit" % "4.13" % "test",
|
||||
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
|
||||
"org.mockito" % "mockito-core" % "3.2.4" % "test",
|
||||
"com.dimafeng" %% "testcontainers-scala" % "0.34.2" % "test",
|
||||
"org.testcontainers" % "mysql" % "1.12.4" % "test",
|
||||
"org.testcontainers" % "postgresql" % "1.12.4" % "test",
|
||||
"org.mockito" % "mockito-core" % "3.3.3" % "test",
|
||||
"com.dimafeng" %% "testcontainers-scala" % "0.37.0" % "test",
|
||||
"org.testcontainers" % "mysql" % "1.14.3" % "test",
|
||||
"org.testcontainers" % "postgresql" % "1.14.3" % "test",
|
||||
"net.i2p.crypto" % "eddsa" % "0.3.0",
|
||||
"is.tagomor.woothee" % "woothee-java" % "1.10.1",
|
||||
"is.tagomor.woothee" % "woothee-java" % "1.11.0",
|
||||
"org.ec4j.core" % "ec4j-core" % "0.0.3"
|
||||
)
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
sbt.version=1.3.5
|
||||
sbt.version=1.3.12
|
||||
|
||||
@@ -2,8 +2,8 @@ scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
|
||||
|
||||
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.1")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.5.0")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.10")
|
||||
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.3")
|
||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
|
||||
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.4")
|
||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.2")
|
||||
addSbtPlugin("com.typesafe.sbt" % "sbt-license-report" % "1.2.0")
|
||||
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2")
|
||||
|
||||
@@ -4,6 +4,10 @@ import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||
import org.eclipse.jetty.server.session.DefaultSessionCache;
|
||||
import org.eclipse.jetty.server.session.FileSessionDataStore;
|
||||
import org.eclipse.jetty.server.session.SessionCache;
|
||||
import org.eclipse.jetty.server.session.SessionHandler;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
|
||||
import java.io.File;
|
||||
@@ -21,6 +25,7 @@ public class JettyLauncher {
|
||||
String contextPath = "/";
|
||||
String tmpDirPath="";
|
||||
boolean forceHttps = false;
|
||||
boolean saveSessions = false;
|
||||
|
||||
host = getEnvironmentVariable("gitbucket.host");
|
||||
port = getEnvironmentVariable("gitbucket.port");
|
||||
@@ -28,6 +33,9 @@ public class JettyLauncher {
|
||||
tmpDirPath = getEnvironmentVariable("gitbucket.tempDir");
|
||||
|
||||
for(String arg: args) {
|
||||
if(arg.equals("--save_sessions")) {
|
||||
saveSessions = true;
|
||||
}
|
||||
if(arg.startsWith("--") && arg.contains("=")) {
|
||||
String[] dim = arg.split("=");
|
||||
if(dim.length >= 2) {
|
||||
@@ -41,12 +49,6 @@ public class JettyLauncher {
|
||||
case "--prefix":
|
||||
contextPath = dim[1];
|
||||
break;
|
||||
case "--max_file_size":
|
||||
System.setProperty("gitbucket.maxFileSize", dim[1]);
|
||||
break;
|
||||
case "--upload_timeout":
|
||||
System.setProperty("gitbucket.UploadTimeout", dim[1]);
|
||||
break;
|
||||
case "--gitbucket.home":
|
||||
System.setProperty("gitbucket.home", dim[1]);
|
||||
break;
|
||||
@@ -56,9 +58,6 @@ public class JettyLauncher {
|
||||
case "--plugin_dir":
|
||||
System.setProperty("gitbucket.pluginDir", dim[1]);
|
||||
break;
|
||||
case "--validate_password":
|
||||
System.setProperty("gitbucket.validate.password", dim[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,6 +95,19 @@ public class JettyLauncher {
|
||||
|
||||
WebAppContext context = new WebAppContext();
|
||||
|
||||
if(saveSessions) {
|
||||
File sessDir = new File(getGitBucketHome(), "sessions");
|
||||
if(!sessDir.exists()){
|
||||
sessDir.mkdirs();
|
||||
}
|
||||
SessionHandler sessions = context.getSessionHandler();
|
||||
SessionCache cache = new DefaultSessionCache(sessions);
|
||||
FileSessionDataStore fsds = new FileSessionDataStore();
|
||||
fsds.setStoreDir(sessDir);
|
||||
cache.setSessionDataStore(fsds);
|
||||
sessions.setSessionCache(cache);
|
||||
}
|
||||
|
||||
File tmpDir;
|
||||
if(tmpDirPath == null || tmpDirPath.equals("")){
|
||||
tmpDir = new File(getGitBucketHome(), "tmp");
|
||||
|
||||
@@ -60,13 +60,12 @@
|
||||
<!-- ACCESS_TOKEN -->
|
||||
<!--================================================================================================-->
|
||||
<createTable tableName="ACCESS_TOKEN">
|
||||
<column name="ACCESS_TOKEN_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
||||
<column name="ACCESS_TOKEN_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_ACCESS_TOKEN_PK" primaryKey="true" />
|
||||
<column name="TOKEN_HASH" type="varchar(40)" nullable="false"/>
|
||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="NOTE" type="text" nullable="false"/>
|
||||
</createTable>
|
||||
|
||||
<addPrimaryKey constraintName="IDX_ACCESS_TOKEN_PK" tableName="ACCESS_TOKEN" columnNames="ACCESS_TOKEN_ID"/>
|
||||
<addUniqueConstraint constraintName="IDX_ACCESS_TOKEN_TOKEN_HASH" tableName="ACCESS_TOKEN" columnNames="TOKEN_HASH"/>
|
||||
<addForeignKeyConstraint constraintName="IDX_ACCESS_TOKEN_FK0" baseTableName="ACCESS_TOKEN" baseColumnNames="USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME" onDelete="CASCADE" onUpdate="CASCADE"/>
|
||||
|
||||
@@ -74,7 +73,7 @@
|
||||
<!-- ACTIVITY -->
|
||||
<!--================================================================================================-->
|
||||
<createTable tableName="ACTIVITY">
|
||||
<column name="ACTIVITY_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
||||
<column name="ACTIVITY_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_ACTIVITY_PK" primaryKey="true"/>
|
||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="ACTIVITY_USER_NAME" type="varchar(100)" nullable="false"/>
|
||||
@@ -84,7 +83,6 @@
|
||||
<column name="ACTIVITY_DATE" type="datetime" nullable="false"/>
|
||||
</createTable>
|
||||
|
||||
<addPrimaryKey constraintName="IDX_ACTIVITY_PK" tableName="ACTIVITY" columnNames="ACTIVITY_ID"/>
|
||||
<addForeignKeyConstraint constraintName="IDX_ACTIVITY_FK1" baseTableName="ACTIVITY" baseColumnNames="ACTIVITY_USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/>
|
||||
<addForeignKeyConstraint constraintName="IDX_ACTIVITY_FK0" baseTableName="ACTIVITY" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
|
||||
|
||||
@@ -108,7 +106,7 @@
|
||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="COMMIT_ID" type="varchar(100)" nullable="false"/>
|
||||
<column name="COMMENT_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
||||
<column name="COMMENT_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_COMMIT_COMMENT_PK" primaryKey="true" />
|
||||
<column name="COMMENTED_USER_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="CONTENT" type="text" nullable="false"/>
|
||||
<column name="FILE_NAME" type="varchar(260)" nullable="true"/>
|
||||
@@ -119,14 +117,13 @@
|
||||
<column name="ISSUE_ID" type="int" nullable="true"/>
|
||||
</createTable>
|
||||
|
||||
<addPrimaryKey constraintName="IDX_COMMIT_COMMENT_PK" tableName="COMMIT_COMMENT" columnNames="COMMENT_ID"/>
|
||||
<addForeignKeyConstraint constraintName="IDX_COMMIT_COMMENT_FK0" baseTableName="COMMIT_COMMENT" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
|
||||
|
||||
<!--================================================================================================-->
|
||||
<!-- COMMIT_STATUS -->
|
||||
<!--================================================================================================-->
|
||||
<createTable tableName="COMMIT_STATUS">
|
||||
<column name="COMMIT_STATUS_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
||||
<column name="COMMIT_STATUS_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_COMMIT_STATUS_PK" primaryKey="true" />
|
||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="COMMIT_ID" type="varchar(40)" nullable="false"/>
|
||||
@@ -139,7 +136,6 @@
|
||||
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
||||
</createTable>
|
||||
|
||||
<addPrimaryKey constraintName="IDX_COMMIT_STATUS_PK" tableName="COMMIT_STATUS" columnNames="COMMIT_STATUS_ID"/>
|
||||
<addUniqueConstraint constraintName="IDX_COMMIT_STATUS_1" tableName="COMMIT_STATUS" columnNames="USER_NAME, REPOSITORY_NAME, COMMIT_ID, CONTEXT"/>
|
||||
<addForeignKeyConstraint constraintName="IDX_COMMIT_STATUS_FK3" baseTableName="COMMIT_STATUS" baseColumnNames="CREATOR" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME" onDelete="CASCADE" onUpdate="CASCADE"/>
|
||||
<addForeignKeyConstraint constraintName="IDX_COMMIT_STATUS_FK2" baseTableName="COMMIT_STATUS" baseColumnNames="USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME" onDelete="CASCADE" onUpdate="CASCADE"/>
|
||||
@@ -218,7 +214,7 @@
|
||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="ISSUE_ID" type="int" nullable="false"/>
|
||||
<column name="COMMENT_ID" type="int" nullable="false" autoIncrement="true" unique="true"/>
|
||||
<column name="COMMENT_ID" type="int" nullable="false" autoIncrement="true" unique="true" primaryKeyName="IDX_ISSUE_COMMENT_PK" primaryKey="true" />
|
||||
<column name="ACTION" type="varchar(20)" nullable="false"/>
|
||||
<column name="COMMENTED_USER_NAME" type="varchar(100)" nullable="false"/>
|
||||
<column name="CONTENT" type="text" nullable="false"/>
|
||||
@@ -226,7 +222,6 @@
|
||||
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
||||
</createTable>
|
||||
|
||||
<addPrimaryKey constraintName="IDX_ISSUE_COMMENT_PK" tableName="ISSUE_COMMENT" columnNames="COMMENT_ID"/>
|
||||
<addForeignKeyConstraint constraintName="IDX_ISSUE_COMMENT_FK0" baseTableName="ISSUE_COMMENT" baseColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID" referencedTableName="ISSUE" referencedColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID"/>
|
||||
|
||||
<!--================================================================================================-->
|
||||
|
||||
@@ -16,7 +16,7 @@ class ScalatraBootstrap extends LifeCycle with SystemSettingsService {
|
||||
context.getSessionCookieConfig.setSecure(true)
|
||||
}
|
||||
|
||||
// Register TransactionFilter and BasicAuthenticationFilter at first
|
||||
// Register TransactionFilter at first
|
||||
context.addFilter("transactionFilter", new TransactionFilter)
|
||||
context
|
||||
.getFilterRegistration("transactionFilter")
|
||||
|
||||
@@ -21,7 +21,8 @@ case class ApiPullRequest(
|
||||
body: String,
|
||||
user: ApiUser,
|
||||
labels: List[ApiLabel],
|
||||
assignee: Option[ApiUser]
|
||||
assignee: Option[ApiUser],
|
||||
draft: Option[Boolean]
|
||||
) {
|
||||
val id = 0 // dummy id
|
||||
val html_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}")
|
||||
@@ -62,7 +63,8 @@ object ApiPullRequest {
|
||||
body = issue.content.getOrElse(""),
|
||||
user = user,
|
||||
labels = labels,
|
||||
assignee = assignee
|
||||
assignee = assignee,
|
||||
draft = Some(pullRequest.isDraft)
|
||||
)
|
||||
|
||||
case class Commit(sha: String, ref: String, repo: ApiRepository)(baseOwner: String) {
|
||||
|
||||
@@ -6,7 +6,7 @@ case class ApiReleaseAsset(name: String, size: Long)(tag: String, fileName: Stri
|
||||
val label = name
|
||||
val file_id = fileName
|
||||
val browser_download_url = ApiPath(
|
||||
s"/api/v3/repos/${repositoryName.fullName}/releases/${tag}/assets/${fileName}"
|
||||
s"/${repositoryName.fullName}/releases/${tag}/assets/${fileName}"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ case class CreateAPullRequest(
|
||||
head: String,
|
||||
base: String,
|
||||
body: Option[String],
|
||||
maintainer_can_modify: Option[Boolean]
|
||||
maintainer_can_modify: Option[Boolean],
|
||||
draft: Option[Boolean]
|
||||
)
|
||||
|
||||
case class CreateAPullRequestAlt(
|
||||
|
||||
@@ -16,6 +16,7 @@ import gitbucket.core.util._
|
||||
import org.scalatra.i18n.Messages
|
||||
import org.scalatra.BadRequest
|
||||
import org.scalatra.forms._
|
||||
import org.scalatra.Forbidden
|
||||
|
||||
class AccountController
|
||||
extends AccountControllerBase
|
||||
@@ -82,7 +83,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
val newForm = mapping(
|
||||
"userName" -> trim(label("User name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"password" -> trim(label("Password", text(required, maxlength(20), password))),
|
||||
"password" -> trim(label("Password", text(required, maxlength(20)))),
|
||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
||||
"extraMailAddresses" -> list(
|
||||
@@ -94,7 +95,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
)(AccountNewForm.apply)
|
||||
|
||||
val editForm = mapping(
|
||||
"password" -> trim(label("Password", optional(text(maxlength(20), password)))),
|
||||
"password" -> trim(label("Password", optional(text(maxlength(20))))),
|
||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||
"extraMailAddresses" -> list(
|
||||
@@ -691,27 +692,28 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
* Create new repository.
|
||||
*/
|
||||
post("/new", newRepositoryForm)(usersOnly { form =>
|
||||
LockUtil.lock(s"${form.owner}/${form.name}") {
|
||||
if (getRepository(form.owner, form.name).isEmpty) {
|
||||
createRepository(
|
||||
context.loginAccount.get,
|
||||
form.owner,
|
||||
form.name,
|
||||
form.description,
|
||||
form.isPrivate,
|
||||
form.initOption,
|
||||
form.sourceUrl
|
||||
)
|
||||
if (context.settings.repositoryOperation.create || context.loginAccount.get.isAdmin) {
|
||||
LockUtil.lock(s"${form.owner}/${form.name}") {
|
||||
if (getRepository(form.owner, form.name).isEmpty) {
|
||||
createRepository(
|
||||
context.loginAccount.get,
|
||||
form.owner,
|
||||
form.name,
|
||||
form.description,
|
||||
form.isPrivate,
|
||||
form.initOption,
|
||||
form.sourceUrl
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// redirect to the repository
|
||||
redirect(s"/${form.owner}/${form.name}")
|
||||
// redirect to the repository
|
||||
redirect(s"/${form.owner}/${form.name}")
|
||||
} else Forbidden()
|
||||
})
|
||||
|
||||
get("/:owner/:repository/fork")(readableUsersOnly { repository =>
|
||||
if (repository.repository.options.allowFork) {
|
||||
val loginAccount = context.loginAccount.get
|
||||
val loginAccount = context.loginAccount.get
|
||||
if (repository.repository.options.allowFork && (context.settings.repositoryOperation.fork || loginAccount.isAdmin)) {
|
||||
val loginUserName = loginAccount.userName
|
||||
val groups = getGroupsByUserName(loginUserName)
|
||||
groups match {
|
||||
@@ -735,8 +737,8 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
})
|
||||
|
||||
post("/:owner/:repository/fork", accountForm)(readableUsersOnly { (form, repository) =>
|
||||
if (repository.repository.options.allowFork) {
|
||||
val loginAccount = context.loginAccount.get
|
||||
val loginAccount = context.loginAccount.get
|
||||
if (repository.repository.options.allowFork && (context.settings.repositoryOperation.fork || loginAccount.isAdmin)) {
|
||||
val loginUserName = loginAccount.userName
|
||||
val accountName = form.accountName
|
||||
|
||||
@@ -750,7 +752,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
// redirect to the repository
|
||||
redirect(s"/${accountName}/${repository.name}")
|
||||
}
|
||||
} else BadRequest()
|
||||
} else Forbidden()
|
||||
})
|
||||
|
||||
private def existsAccount: Constraint = new Constraint() {
|
||||
|
||||
@@ -27,7 +27,12 @@ trait DashboardControllerBase extends ControllerBase {
|
||||
self: IssuesService with PullRequestService with RepositoryService with AccountService with UsersAuthenticator =>
|
||||
|
||||
get("/dashboard/repos")(usersOnly {
|
||||
val repos = getVisibleRepositories(context.loginAccount, withoutPhysicalInfo = true)
|
||||
val repos = getVisibleRepositories(
|
||||
context.loginAccount,
|
||||
None,
|
||||
withoutPhysicalInfo = true,
|
||||
limit = context.settings.limitVisibleRepositories
|
||||
)
|
||||
html.repos(getGroupNames(context.loginAccount.get.userName), repos, repos)
|
||||
})
|
||||
|
||||
@@ -93,7 +98,12 @@ trait DashboardControllerBase extends ControllerBase {
|
||||
},
|
||||
filter,
|
||||
getGroupNames(userName),
|
||||
getVisibleRepositories(context.loginAccount, withoutPhysicalInfo = true)
|
||||
getVisibleRepositories(
|
||||
context.loginAccount,
|
||||
None,
|
||||
withoutPhysicalInfo = true,
|
||||
limit = context.settings.limitVisibleRepositories
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -118,7 +128,12 @@ trait DashboardControllerBase extends ControllerBase {
|
||||
},
|
||||
filter,
|
||||
getGroupNames(userName),
|
||||
getVisibleRepositories(context.loginAccount, withoutPhysicalInfo = true)
|
||||
getVisibleRepositories(
|
||||
context.loginAccount,
|
||||
None,
|
||||
withoutPhysicalInfo = true,
|
||||
limit = context.settings.limitVisibleRepositories
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.scalatra.servlet.{FileItem, FileUploadSupport, MultipartConfig}
|
||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||
|
||||
import scala.util.Using
|
||||
import gitbucket.core.service.SystemSettingsService
|
||||
|
||||
/**
|
||||
* Provides Ajax based file upload functionality.
|
||||
@@ -28,11 +29,11 @@ class FileUploadController
|
||||
with FileUploadSupport
|
||||
with RepositoryService
|
||||
with AccountService
|
||||
with ReleaseService {
|
||||
|
||||
configureMultipartHandling(MultipartConfig(maxFileSize = Some(FileUtil.MaxFileSize)))
|
||||
with ReleaseService
|
||||
with SystemSettingsService {
|
||||
|
||||
post("/image") {
|
||||
setMultipartConfig()
|
||||
execute(
|
||||
{ (file, fileId) =>
|
||||
FileUtils
|
||||
@@ -44,6 +45,7 @@ class FileUploadController
|
||||
}
|
||||
|
||||
post("/tmp") {
|
||||
setMultipartConfig()
|
||||
execute(
|
||||
{ (file, fileId) =>
|
||||
FileUtils
|
||||
@@ -55,6 +57,7 @@ class FileUploadController
|
||||
}
|
||||
|
||||
post("/file/:owner/:repository") {
|
||||
setMultipartConfig()
|
||||
execute(
|
||||
{ (file, fileId) =>
|
||||
FileUtils.writeByteArrayToFile(
|
||||
@@ -70,6 +73,7 @@ class FileUploadController
|
||||
}
|
||||
|
||||
post("/wiki/:owner/:repository") {
|
||||
setMultipartConfig()
|
||||
// Don't accept not logged-in users
|
||||
session.get(Keys.Session.LoginAccount).collect {
|
||||
case loginAccount: Account =>
|
||||
@@ -128,6 +132,7 @@ class FileUploadController
|
||||
}
|
||||
|
||||
post("/release/:owner/:repository/:tag") {
|
||||
setMultipartConfigForLargeFile()
|
||||
session
|
||||
.get(Keys.Session.LoginAccount)
|
||||
.collect {
|
||||
@@ -150,6 +155,7 @@ class FileUploadController
|
||||
|
||||
post("/import") {
|
||||
import JDBCUtil._
|
||||
setMultipartConfig()
|
||||
session.get(Keys.Session.LoginAccount).collect {
|
||||
case loginAccount: Account if loginAccount.isAdmin =>
|
||||
execute({ (file, fileId) =>
|
||||
@@ -159,6 +165,18 @@ class FileUploadController
|
||||
redirect("/admin/data")
|
||||
}
|
||||
|
||||
private def setMultipartConfig(): Unit = {
|
||||
val settings = loadSystemSettings()
|
||||
val config = MultipartConfig(maxFileSize = Some(settings.upload.maxFileSize))
|
||||
config.apply(request.getServletContext())
|
||||
}
|
||||
|
||||
private def setMultipartConfigForLargeFile(): Unit = {
|
||||
val settings = loadSystemSettings()
|
||||
val config = MultipartConfig(maxFileSize = Some(settings.upload.largeMaxFileSize))
|
||||
config.apply(request.getServletContext())
|
||||
}
|
||||
|
||||
private def onlyWikiEditable(owner: String, repository: String, loginAccount: Account)(action: => Any): Any = {
|
||||
implicit val session = Database.getSession(request)
|
||||
getRepository(owner, repository) match {
|
||||
|
||||
@@ -65,7 +65,12 @@ trait IndexControllerBase extends ControllerBase {
|
||||
val visibleOwnerSet: Set[String] = Set(account.userName) ++ getGroupsByUserName(account.userName)
|
||||
gitbucket.core.html.index(
|
||||
getRecentActivitiesByOwners(visibleOwnerSet),
|
||||
getVisibleRepositories(Some(account), withoutPhysicalInfo = true),
|
||||
getVisibleRepositories(
|
||||
Some(account),
|
||||
None,
|
||||
withoutPhysicalInfo = true,
|
||||
limit = context.settings.limitVisibleRepositories
|
||||
),
|
||||
showBannerToCreatePersonalAccessToken = hasAccountFederation(account.userName) && !hasAccessToken(
|
||||
account.userName
|
||||
)
|
||||
@@ -279,11 +284,28 @@ trait IndexControllerBase extends ControllerBase {
|
||||
get("/search") {
|
||||
val query = params.getOrElse("query", "").trim.toLowerCase
|
||||
val visibleRepositories =
|
||||
getVisibleRepositories(context.loginAccount, repositoryUserName = None, withoutPhysicalInfo = true)
|
||||
val repositories = visibleRepositories.filter { repository =>
|
||||
getVisibleRepositories(
|
||||
context.loginAccount,
|
||||
None,
|
||||
withoutPhysicalInfo = true,
|
||||
limit = context.settings.limitVisibleRepositories
|
||||
)
|
||||
|
||||
val repositories = {
|
||||
context.settings.limitVisibleRepositories match {
|
||||
case true =>
|
||||
getVisibleRepositories(
|
||||
context.loginAccount,
|
||||
None,
|
||||
withoutPhysicalInfo = true,
|
||||
limit = false
|
||||
)
|
||||
case false => visibleRepositories
|
||||
}
|
||||
}.filter { repository =>
|
||||
repository.name.toLowerCase.indexOf(query) >= 0 || repository.owner.toLowerCase.indexOf(query) >= 0
|
||||
}
|
||||
|
||||
gitbucket.core.search.html.repositories(query, repositories, visibleRepositories)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package gitbucket.core.controller
|
||||
|
||||
import java.io.File
|
||||
|
||||
import gitbucket.core.service.{AccountService, ActivityService, ReleaseService, RepositoryService}
|
||||
import gitbucket.core.service.{AccountService, ActivityService, PaginationHelper, ReleaseService, RepositoryService}
|
||||
import gitbucket.core.util._
|
||||
import gitbucket.core.util.Directory._
|
||||
import gitbucket.core.util.Implicits._
|
||||
@@ -10,6 +10,7 @@ import org.scalatra.forms._
|
||||
import gitbucket.core.releases.html
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.eclipse.jgit.api.Git
|
||||
|
||||
import scala.util.Using
|
||||
|
||||
class ReleaseController
|
||||
@@ -42,17 +43,14 @@ trait ReleaseControllerBase extends ControllerBase {
|
||||
)(ReleaseForm.apply)
|
||||
|
||||
get("/:owner/:repository/releases")(referrersOnly { repository =>
|
||||
val releases = getReleases(repository.owner, repository.name)
|
||||
val assets = getReleaseAssetsMap(repository.owner, repository.name)
|
||||
val page = PaginationHelper.page(params.get("page"))
|
||||
|
||||
html.list(
|
||||
repository,
|
||||
repository.tags.reverse.map { tag =>
|
||||
(tag, releases.find(_.tag == tag.name).map { release =>
|
||||
(release, assets(release))
|
||||
})
|
||||
},
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
||||
fetchReleases(repository, page),
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
||||
page,
|
||||
repository.tags.size
|
||||
)
|
||||
})
|
||||
|
||||
@@ -215,4 +213,21 @@ trait ReleaseControllerBase extends ControllerBase {
|
||||
redirect(s"/${repository.owner}/${repository.name}/releases")
|
||||
})
|
||||
|
||||
private def fetchReleases(repository: RepositoryService.RepositoryInfo, page: Int) = {
|
||||
|
||||
import gitbucket.core.service.ReleaseService._
|
||||
|
||||
val (offset, limit) = ((page - 1) * ReleaseLimit, ReleaseLimit)
|
||||
val tagsToDisplay = repository.tags.reverse.slice(offset, offset + limit)
|
||||
|
||||
val releases = getReleases(repository.owner, repository.name, tagsToDisplay)
|
||||
val assets = getReleaseAssetsMap(repository.owner, repository.name, releases)
|
||||
|
||||
val tagsWithReleases = tagsToDisplay.map { tag =>
|
||||
(tag, releases.find(_.tag == tag.name).map { release =>
|
||||
(release, assets(release))
|
||||
})
|
||||
}
|
||||
tagsWithReleases
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.eclipse.jgit.lib.Constants
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
|
||||
import scala.util.Using
|
||||
import org.scalatra.Forbidden
|
||||
|
||||
class RepositorySettingsController
|
||||
extends RepositorySettingsControllerBase
|
||||
@@ -44,7 +45,6 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
|
||||
// for repository options
|
||||
case class OptionsForm(
|
||||
repositoryName: String,
|
||||
description: Option[String],
|
||||
isPrivate: Boolean,
|
||||
issuesOption: String,
|
||||
@@ -57,9 +57,6 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
)
|
||||
|
||||
val optionsForm = mapping(
|
||||
"repositoryName" -> trim(
|
||||
label("Repository Name", text(required, maxlength(100), repository, renameRepositoryName))
|
||||
),
|
||||
"description" -> trim(label("Description", optional(text()))),
|
||||
"isPrivate" -> trim(label("Repository Type", boolean())),
|
||||
"issuesOption" -> trim(label("Issues Option", text(required, featureOption))),
|
||||
@@ -104,6 +101,15 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
(url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
|
||||
)
|
||||
|
||||
// for rename repository
|
||||
case class RenameRepositoryForm(repositoryName: String)
|
||||
|
||||
val renameForm = mapping(
|
||||
"repositoryName" -> trim(
|
||||
label("New repository name", text(required, maxlength(100), repository, renameRepositoryName))
|
||||
)
|
||||
)(RenameRepositoryForm.apply)
|
||||
|
||||
// for transfer ownership
|
||||
case class TransferOwnerShipForm(newOwner: String)
|
||||
|
||||
@@ -144,13 +150,8 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
form.mergeOptions,
|
||||
form.defaultMergeOption
|
||||
)
|
||||
// Change repository name
|
||||
if (repository.name != form.repositoryName) {
|
||||
// Update database
|
||||
renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
|
||||
}
|
||||
flash.update("info", "Repository settings has been updated.")
|
||||
redirect(s"/${repository.owner}/${form.repositoryName}/settings/options")
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/options")
|
||||
})
|
||||
|
||||
/** branch settings */
|
||||
@@ -364,24 +365,40 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
html.danger(_, flash.get("info"))
|
||||
})
|
||||
|
||||
/**
|
||||
* Rename repository.
|
||||
*/
|
||||
post("/:owner/:repository/settings/rename", renameForm)(ownerOnly { (form, repository) =>
|
||||
if (context.settings.repositoryOperation.rename || context.loginAccount.get.isAdmin) {
|
||||
if (repository.name != form.repositoryName) {
|
||||
renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
|
||||
}
|
||||
redirect(s"/${repository.owner}/${form.repositoryName}")
|
||||
} else Forbidden()
|
||||
})
|
||||
|
||||
/**
|
||||
* Transfer repository ownership.
|
||||
*/
|
||||
post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
|
||||
// Change repository owner
|
||||
if (repository.owner != form.newOwner) {
|
||||
renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
|
||||
}
|
||||
redirect(s"/${form.newOwner}/${repository.name}")
|
||||
if (context.settings.repositoryOperation.transfer || context.loginAccount.get.isAdmin) {
|
||||
// Change repository owner
|
||||
if (repository.owner != form.newOwner) {
|
||||
renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
|
||||
}
|
||||
redirect(s"/${form.newOwner}/${repository.name}")
|
||||
} else Forbidden()
|
||||
})
|
||||
|
||||
/**
|
||||
* Delete the repository.
|
||||
*/
|
||||
post("/:owner/:repository/settings/delete")(ownerOnly { repository =>
|
||||
// Delete the repository and related files
|
||||
deleteRepository(repository.repository)
|
||||
redirect(s"/${repository.owner}")
|
||||
if (context.settings.repositoryOperation.delete || context.loginAccount.get.isAdmin) {
|
||||
// Delete the repository and related files
|
||||
deleteRepository(repository.repository)
|
||||
redirect(s"/${repository.owner}")
|
||||
} else Forbidden()
|
||||
})
|
||||
|
||||
/**
|
||||
|
||||
@@ -928,8 +928,9 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
defining(JGitUtil.getRevCommitFromId(git, objectId)) { revCommit =>
|
||||
val lastModifiedCommit =
|
||||
if (path == ".") revCommit else JGitUtil.getLastModifiedCommit(git, revCommit, path)
|
||||
val commitCount = JGitUtil.getCommitCount(git, lastModifiedCommit.getName)
|
||||
// get files
|
||||
val files = JGitUtil.getFileList(git, revision, path, context.settings.baseUrl)
|
||||
val files = JGitUtil.getFileList(git, revision, path, context.settings.baseUrl, commitCount)
|
||||
val parentPath = if (path == ".") Nil else path.split("/").toList
|
||||
// process README.md or README.markdown
|
||||
val readme = files
|
||||
@@ -950,7 +951,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
repository,
|
||||
if (path == ".") Nil else path.split("/").toList, // current path
|
||||
new JGitUtil.CommitInfo(lastModifiedCommit), // last modified commit
|
||||
JGitUtil.getCommitCount(git, lastModifiedCommit.getName),
|
||||
commitCount,
|
||||
files,
|
||||
readme,
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
||||
|
||||
@@ -38,14 +38,22 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
"information" -> trim(label("Information", optional(text()))),
|
||||
"allowAccountRegistration" -> trim(label("Account registration", boolean())),
|
||||
"allowAnonymousAccess" -> trim(label("Anonymous access", boolean())),
|
||||
"isCreateRepoOptionPublic" -> trim(label("Default option to create a new repository", boolean())),
|
||||
"isCreateRepoOptionPublic" -> trim(label("Default visibility of new repository", boolean())),
|
||||
"repositoryOperation" -> mapping(
|
||||
"create" -> trim(label("Allow all users to create repository", boolean())),
|
||||
"delete" -> trim(label("Allow all users to delete repository", boolean())),
|
||||
"rename" -> trim(label("Allow all users to rename repository", boolean())),
|
||||
"transfer" -> trim(label("Allow all users to transfer repository", boolean())),
|
||||
"fork" -> trim(label("Allow all users to fork repository", boolean()))
|
||||
)(RepositoryOperation.apply),
|
||||
"gravatar" -> trim(label("Gravatar", boolean())),
|
||||
"notification" -> trim(label("Notification", boolean())),
|
||||
"activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))),
|
||||
"limitVisibleRepositories" -> trim(label("limitVisibleRepositories", boolean())),
|
||||
"ssh" -> mapping(
|
||||
"enabled" -> trim(label("SSH access", boolean())),
|
||||
"host" -> trim(label("SSH host", optional(text()))),
|
||||
"port" -> trim(label("SSH port", optional(number()))),
|
||||
"port" -> trim(label("SSH port", optional(number())))
|
||||
)(Ssh.apply),
|
||||
"useSMTP" -> trim(label("SMTP", boolean())),
|
||||
"smtp" -> optionalIfNotChecked(
|
||||
@@ -90,11 +98,18 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
)(OIDC.apply)
|
||||
),
|
||||
"skinName" -> trim(label("AdminLTE skin name", text(required))),
|
||||
"userDefinedCss" -> trim(label("User-defined CSS", optional(text()))),
|
||||
"showMailAddress" -> trim(label("Show mail address", boolean())),
|
||||
"webhook" -> mapping(
|
||||
"blockPrivateAddress" -> trim(label("Block private address", boolean())),
|
||||
"whitelist" -> trim(label("Whitelist", multiLineText()))
|
||||
)(WebHook.apply)
|
||||
)(WebHook.apply),
|
||||
"upload" -> mapping(
|
||||
"maxFileSize" -> trim(label("Max file size", long(required))),
|
||||
"timeout" -> trim(label("Timeout", long(required))),
|
||||
"largeMaxFileSize" -> trim(label("Max file size for large file", long(required))),
|
||||
"largeTimeout" -> trim(label("Timeout for large file", long(required)))
|
||||
)(Upload.apply)
|
||||
)(SystemSettings.apply).verifying { settings =>
|
||||
Vector(
|
||||
if (settings.ssh.enabled && settings.baseUrl.isEmpty) {
|
||||
@@ -170,7 +185,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
val newUserForm = mapping(
|
||||
"userName" -> trim(label("Username", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||
"password" -> trim(label("Password", text(required, maxlength(20), password))),
|
||||
"password" -> trim(label("Password", text(required, maxlength(20)))),
|
||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
||||
"extraMailAddresses" -> list(
|
||||
@@ -184,7 +199,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
val editUserForm = mapping(
|
||||
"userName" -> trim(label("Username", text(required, maxlength(100), identifier))),
|
||||
"password" -> trim(label("Password", optional(text(maxlength(20), password)))),
|
||||
"password" -> trim(label("Password", optional(text(maxlength(20))))),
|
||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||
"extraMailAddresses" -> list(
|
||||
@@ -529,24 +544,26 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
private def members: Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
if (value.split(",").exists {
|
||||
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
||||
}) None
|
||||
else Some("Must select one manager at least.")
|
||||
}
|
||||
}
|
||||
|
||||
protected def disableByNotYourself(paramName: String): Constraint = new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
params.get(paramName).flatMap { userName =>
|
||||
if (userName == context.loginAccount.get.userName && params.get("removed") == Some("true"))
|
||||
Some("You can't disable your account yourself")
|
||||
else
|
||||
None
|
||||
private def members: Constraint =
|
||||
new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
if (value.split(",").exists {
|
||||
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
||||
}) None
|
||||
else Some("Must select one manager at least.")
|
||||
}
|
||||
}
|
||||
|
||||
protected def disableByNotYourself(paramName: String): Constraint =
|
||||
new Constraint() {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
params.get(paramName).flatMap { userName =>
|
||||
if (userName == context.loginAccount.get.userName && params.get("removed") == Some("true"))
|
||||
Some("You can't disable your account yourself")
|
||||
else
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import gitbucket.core.util.Directory.getRepositoryDir
|
||||
import gitbucket.core.util.ReferrerAuthenticator
|
||||
import gitbucket.core.util.Implicits._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Using
|
||||
@@ -12,11 +13,23 @@ import scala.util.Using
|
||||
trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||
self: ReferrerAuthenticator =>
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[ApiGitReferenceControllerBase])
|
||||
|
||||
/*
|
||||
* i. Get a reference
|
||||
* https://developer.github.com/v3/git/refs/#get-a-reference
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/git/ref/*")(referrersOnly { repository =>
|
||||
getRef()
|
||||
})
|
||||
|
||||
// Some versions of GHE support this path
|
||||
get("/api/v3/repos/:owner/:repository/git/refs/*")(referrersOnly { repository =>
|
||||
logger.warn("git/refs/ endpoint may not be compatible with GitHub API v3. Consider using git/ref/ endpoint instead")
|
||||
getRef()
|
||||
})
|
||||
|
||||
private def getRef() = {
|
||||
val revstr = multiParams("splat").head
|
||||
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||
val ref = git.getRepository().findRef(revstr)
|
||||
@@ -38,7 +51,8 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* ii. Get all references
|
||||
* https://developer.github.com/v3/git/refs/#get-all-references
|
||||
|
||||
@@ -114,7 +114,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
||||
requestBranch = reqBranch,
|
||||
commitIdFrom = commitIdFrom.getName,
|
||||
commitIdTo = commitIdTo.getName,
|
||||
isDraft = false,
|
||||
isDraft = createPullReq.draft.getOrElse(false),
|
||||
loginAccount = context.loginAccount.get,
|
||||
settings = context.settings
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ import gitbucket.core.util.JGitUtil.CommitInfo
|
||||
import gitbucket.core.util.{JGitUtil, ReferrerAuthenticator, RepositoryName}
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.revwalk.RevWalk
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Using
|
||||
|
||||
trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
||||
@@ -17,6 +18,33 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
||||
* i. List commits on a repository
|
||||
* https://developer.github.com/v3/repos/commits/#list-commits-on-a-repository
|
||||
*/
|
||||
get("/api/v3/repos/:owner/:repository/commits")(referrersOnly { repository =>
|
||||
val owner = repository.owner
|
||||
val name = repository.name
|
||||
// TODO: The following parameters need to be implemented. [:path, :author, :since, :until]
|
||||
val sha = if (request.body.nonEmpty) (parse(request.body) \ "sha").extract[String] else "refs/heads/master";
|
||||
Using.resource(Git.open(getRepositoryDir(owner, name))) {
|
||||
git =>
|
||||
val repo = git.getRepository
|
||||
Using.resource(new RevWalk(repo)) {
|
||||
revWalk =>
|
||||
val objectId = repo.resolve(sha)
|
||||
revWalk.markStart(revWalk.parseCommit(objectId))
|
||||
JsonFormat(revWalk.asScala.take(30).map {
|
||||
commit =>
|
||||
val commitInfo = new CommitInfo(commit)
|
||||
ApiCommits(
|
||||
repositoryName = RepositoryName(repository),
|
||||
commitInfo = commitInfo,
|
||||
diffs = JGitUtil.getDiffs(git, commitInfo.parents.headOption, commitInfo.id, false, true),
|
||||
author = getAccount(commitInfo.authorName, commitInfo.authorEmailAddress),
|
||||
committer = getAccount(commitInfo.committerName, commitInfo.committerEmailAddress),
|
||||
commentCount = getCommitComment(repository.owner, repository.name, commitInfo.id.toString).size
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
* ii. Get a single commit
|
||||
|
||||
@@ -131,6 +131,23 @@ trait ActivityService {
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordReopenPullRequestActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
activityUserName: String,
|
||||
issueId: Int,
|
||||
title: String
|
||||
)(implicit s: Session): Unit =
|
||||
Activities insert Activity(
|
||||
userName,
|
||||
repositoryName,
|
||||
activityUserName,
|
||||
"reopen_issue",
|
||||
s"[user:${activityUserName}] reopened pull request [issue:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title),
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCommentIssueActivity(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
|
||||
@@ -39,7 +39,10 @@ trait HandleCommentService {
|
||||
))
|
||||
case "reopen" if (issue.closed) =>
|
||||
false ->
|
||||
(Some("reopen") -> Some(recordReopenIssueActivity _))
|
||||
(Some("reopen") -> Some(
|
||||
if (issue.isPullRequest) recordReopenPullRequestActivity _
|
||||
else recordReopenIssueActivity _
|
||||
))
|
||||
}
|
||||
.map {
|
||||
case (closed, t) =>
|
||||
|
||||
@@ -203,7 +203,7 @@ trait IssuesService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the search result against issues.
|
||||
* Returns the search result against issues.
|
||||
*
|
||||
* @param condition the search condition
|
||||
* @param pullRequest if true then returns only pull requests, false then returns only issues.
|
||||
@@ -910,13 +910,9 @@ object IssuesService {
|
||||
param(request, "groups").map(_.split(",").toSet).getOrElse(Set.empty)
|
||||
)
|
||||
|
||||
def page(request: HttpServletRequest) =
|
||||
try {
|
||||
val i = param(request, "page").getOrElse("1").toInt
|
||||
if (i <= 0) 1 else i
|
||||
} catch {
|
||||
case e: NumberFormatException => 1
|
||||
}
|
||||
def page(request: HttpServletRequest) = {
|
||||
PaginationHelper.page(param(request, "page"))
|
||||
}
|
||||
}
|
||||
|
||||
case class CommitStatusInfo(
|
||||
|
||||
15
src/main/scala/gitbucket/core/service/PaginationHelper.scala
Normal file
15
src/main/scala/gitbucket/core/service/PaginationHelper.scala
Normal file
@@ -0,0 +1,15 @@
|
||||
package gitbucket.core.service
|
||||
|
||||
import scala.util.Try
|
||||
|
||||
object PaginationHelper {
|
||||
|
||||
def page(page: Option[String]) = {
|
||||
|
||||
page
|
||||
.flatMap(pageStr => Try(pageStr.toInt).toOption)
|
||||
.map(Math.max(1, _)) // remove negative pages
|
||||
.getOrElse(1)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
package gitbucket.core.service
|
||||
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.model.{Account, ReleaseTag, ReleaseAsset}
|
||||
import gitbucket.core.model.{Account, ReleaseAsset, ReleaseTag}
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.model.Profile.dateColumnType
|
||||
import gitbucket.core.util.JGitUtil
|
||||
|
||||
trait ReleaseService {
|
||||
self: AccountService with RepositoryService =>
|
||||
@@ -35,10 +36,9 @@ trait ReleaseService {
|
||||
ReleaseAssets.filter(x => x.byTag(owner, repository, tag)).list
|
||||
}
|
||||
|
||||
def getReleaseAssetsMap(owner: String, repository: String)(
|
||||
def getReleaseAssetsMap(owner: String, repository: String, releases: Seq[ReleaseTag])(
|
||||
implicit s: Session
|
||||
): Map[ReleaseTag, Seq[ReleaseAsset]] = {
|
||||
val releases = getReleases(owner, repository)
|
||||
releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.tag))).toMap
|
||||
}
|
||||
|
||||
@@ -76,20 +76,18 @@ trait ReleaseService {
|
||||
ReleaseTags.filter(x => x.byRepository(owner, repository)).sortBy(x => x.updatedDate).list
|
||||
}
|
||||
|
||||
def getRelease(owner: String, repository: String, tag: String)(implicit s: Session): Option[ReleaseTag] = {
|
||||
//Releases filter (_.byPrimaryKey(owner, repository, releaseId)) firstOption
|
||||
ReleaseTags filter (_.byTag(owner, repository, tag)) firstOption
|
||||
def getReleases(owner: String, repository: String, tags: Seq[JGitUtil.TagInfo])(
|
||||
implicit s: Session
|
||||
): Seq[ReleaseTag] = {
|
||||
ReleaseTags
|
||||
.filter(x => x.byRepository(owner, repository))
|
||||
.filter(x => x.tag inSetBind tags.map(_.name))
|
||||
.sortBy(x => x.updatedDate)
|
||||
.list
|
||||
}
|
||||
def getRelease(owner: String, repository: String, tag: String)(implicit s: Session): Option[ReleaseTag] = {
|
||||
ReleaseTags.filter(_.byTag(owner, repository, tag)).firstOption
|
||||
}
|
||||
|
||||
// def getReleaseByTag(owner: String, repository: String, tag: String)(implicit s: Session): Option[Release] = {
|
||||
// Releases filter (_.byTag(owner, repository, tag)) firstOption
|
||||
// }
|
||||
//
|
||||
// def getRelease(owner: String, repository: String, releaseId: String)(implicit s: Session): Option[Release] = {
|
||||
// if (isInteger(releaseId))
|
||||
// getRelease(owner, repository, releaseId.toInt)
|
||||
// else None
|
||||
// }
|
||||
|
||||
def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])(
|
||||
implicit s: Session
|
||||
@@ -107,3 +105,9 @@ trait ReleaseService {
|
||||
ReleaseTags filter (_.byPrimaryKey(owner, repository, tag)) delete
|
||||
}
|
||||
}
|
||||
|
||||
object ReleaseService {
|
||||
|
||||
val ReleaseLimit = 10
|
||||
|
||||
}
|
||||
|
||||
@@ -459,6 +459,7 @@ trait RepositoryService {
|
||||
/**
|
||||
* Returns the list of visible repositories for the specified user.
|
||||
* If repositoryUserName is given then filters results by repository owner.
|
||||
* This function is for plugin compatibility.
|
||||
*
|
||||
* @param loginAccount the logged in account
|
||||
* @param repositoryUserName the repository owner (if None then returns all repositories which are visible for logged in user)
|
||||
@@ -470,23 +471,42 @@ trait RepositoryService {
|
||||
loginAccount: Option[Account],
|
||||
repositoryUserName: Option[String] = None,
|
||||
withoutPhysicalInfo: Boolean = false
|
||||
)(implicit s: Session): List[RepositoryInfo] =
|
||||
getVisibleRepositories(loginAccount, repositoryUserName, withoutPhysicalInfo, false)
|
||||
|
||||
/**
|
||||
* Returns the list of visible repositories for the specified user.
|
||||
* If repositoryUserName is given then filters results by repository owner.
|
||||
*
|
||||
* @param loginAccount the logged in account
|
||||
* @param repositoryUserName the repository owner (if None then returns all repositories which are visible for logged in user)
|
||||
* @param withoutPhysicalInfo if true then the result does not include physical repository information such as commit count,
|
||||
* branches and tags
|
||||
* @param limit if true then result will include only repositories associated with the login account.
|
||||
* @return the repository information which is sorted in descending order of lastActivityDate.
|
||||
*/
|
||||
def getVisibleRepositories(
|
||||
loginAccount: Option[Account],
|
||||
repositoryUserName: Option[String],
|
||||
withoutPhysicalInfo: Boolean,
|
||||
limit: Boolean
|
||||
)(implicit s: Session): List[RepositoryInfo] = {
|
||||
(loginAccount match {
|
||||
// for Administrators
|
||||
case Some(x) if (x.isAdmin) =>
|
||||
case Some(x) if (x.isAdmin && !limit) =>
|
||||
Repositories
|
||||
.join(Accounts)
|
||||
.on(_.userName === _.userName)
|
||||
.filter { case (t1, t2) => t2.removed === false.bind }
|
||||
.map { case (t1, t2) => t1 }
|
||||
// for Normal Users
|
||||
case Some(x) if (!x.isAdmin) =>
|
||||
case Some(x) if (!x.isAdmin || limit) =>
|
||||
Repositories
|
||||
.join(Accounts)
|
||||
.on(_.userName === _.userName)
|
||||
.filter {
|
||||
case (t1, t2) =>
|
||||
(t2.removed === false.bind) && ((t1.isPrivate === false.bind) || (t1.userName === x.userName) ||
|
||||
(t2.removed === false.bind) && ((t1.isPrivate === false.bind && !limit.bind) || (t1.userName === x.userName) ||
|
||||
(t1.userName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)) ||
|
||||
(Collaborators.filter { t3 =>
|
||||
t3.byRepository(t1.userName, t1.repositoryName) &&
|
||||
|
||||
@@ -21,9 +21,15 @@ trait SystemSettingsService {
|
||||
props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString)
|
||||
props.setProperty(AllowAnonymousAccess, settings.allowAnonymousAccess.toString)
|
||||
props.setProperty(IsCreateRepoOptionPublic, settings.isCreateRepoOptionPublic.toString)
|
||||
props.setProperty(RepositoryOperationCreate, settings.repositoryOperation.create.toString)
|
||||
props.setProperty(RepositoryOperationDelete, settings.repositoryOperation.delete.toString)
|
||||
props.setProperty(RepositoryOperationRename, settings.repositoryOperation.rename.toString)
|
||||
props.setProperty(RepositoryOperationTransfer, settings.repositoryOperation.transfer.toString)
|
||||
props.setProperty(RepositoryOperationFork, settings.repositoryOperation.fork.toString)
|
||||
props.setProperty(Gravatar, settings.gravatar.toString)
|
||||
props.setProperty(Notification, settings.notification.toString)
|
||||
settings.activityLogLimit.foreach(x => props.setProperty(ActivityLogLimit, x.toString))
|
||||
props.setProperty(LimitVisibleRepositories, settings.limitVisibleRepositories.toString)
|
||||
props.setProperty(SshEnabled, settings.ssh.enabled.toString)
|
||||
settings.ssh.sshHost.foreach(x => props.setProperty(SshHost, x.trim))
|
||||
settings.ssh.sshPort.foreach(x => props.setProperty(SshPort, x.toString))
|
||||
@@ -69,9 +75,14 @@ trait SystemSettingsService {
|
||||
}
|
||||
}
|
||||
props.setProperty(SkinName, settings.skinName.toString)
|
||||
settings.userDefinedCss.foreach(x => props.setProperty(UserDefinedCss, x))
|
||||
props.setProperty(ShowMailAddress, settings.showMailAddress.toString)
|
||||
props.setProperty(WebHookBlockPrivateAddress, settings.webHook.blockPrivateAddress.toString)
|
||||
props.setProperty(WebHookWhitelist, settings.webHook.whitelist.mkString("\n"))
|
||||
props.setProperty(UploadMaxFileSize, settings.upload.maxFileSize.toString)
|
||||
props.setProperty(UploadTimeout, settings.upload.timeout.toString)
|
||||
props.setProperty(UploadLargeMaxFileSize, settings.upload.largeMaxFileSize.toString)
|
||||
props.setProperty(UploadLargeTimeout, settings.upload.largeTimeout.toString)
|
||||
|
||||
Using.resource(new java.io.FileOutputStream(GitBucketConf)) { out =>
|
||||
props.store(out, null)
|
||||
@@ -92,15 +103,27 @@ trait SystemSettingsService {
|
||||
getValue(props, AllowAccountRegistration, false),
|
||||
getValue(props, AllowAnonymousAccess, true),
|
||||
getValue(props, IsCreateRepoOptionPublic, true),
|
||||
RepositoryOperation(
|
||||
create = getValue(props, RepositoryOperationCreate, true),
|
||||
delete = getValue(props, RepositoryOperationDelete, true),
|
||||
rename = getValue(props, RepositoryOperationRename, true),
|
||||
transfer = getValue(props, RepositoryOperationTransfer, true),
|
||||
fork = getValue(props, RepositoryOperationFork, true)
|
||||
),
|
||||
getValue(props, Gravatar, false),
|
||||
getValue(props, Notification, false),
|
||||
getOptionValue[Int](props, ActivityLogLimit, None),
|
||||
getValue(props, LimitVisibleRepositories, false),
|
||||
Ssh(
|
||||
getValue(props, SshEnabled, false),
|
||||
getOptionValue[String](props, SshHost, None).map(_.trim),
|
||||
getOptionValue(props, SshPort, Some(DefaultSshPort))
|
||||
),
|
||||
getValue(props, UseSMTP, getValue(props, Notification, false)), // handle migration scenario from only notification to useSMTP
|
||||
getValue(
|
||||
props,
|
||||
UseSMTP,
|
||||
getValue(props, Notification, false)
|
||||
), // handle migration scenario from only notification to useSMTP
|
||||
if (getValue(props, UseSMTP, getValue(props, Notification, false))) {
|
||||
Some(
|
||||
Smtp(
|
||||
@@ -148,8 +171,15 @@ trait SystemSettingsService {
|
||||
None
|
||||
},
|
||||
getValue(props, SkinName, "skin-blue"),
|
||||
getOptionValue(props, UserDefinedCss, None),
|
||||
getValue(props, ShowMailAddress, false),
|
||||
WebHook(getValue(props, WebHookBlockPrivateAddress, false), getSeqValue(props, WebHookWhitelist, ""))
|
||||
WebHook(getValue(props, WebHookBlockPrivateAddress, false), getSeqValue(props, WebHookWhitelist, "")),
|
||||
Upload(
|
||||
getValue(props, UploadMaxFileSize, 3 * 1024 * 1024),
|
||||
getValue(props, UploadTimeout, 3 * 10000),
|
||||
getValue(props, UploadLargeMaxFileSize, 3 * 1024 * 1024),
|
||||
getValue(props, UploadLargeTimeout, 3 * 10000)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -167,9 +197,11 @@ object SystemSettingsService {
|
||||
allowAccountRegistration: Boolean,
|
||||
allowAnonymousAccess: Boolean,
|
||||
isCreateRepoOptionPublic: Boolean,
|
||||
repositoryOperation: RepositoryOperation,
|
||||
gravatar: Boolean,
|
||||
notification: Boolean,
|
||||
activityLogLimit: Option[Int],
|
||||
limitVisibleRepositories: Boolean,
|
||||
ssh: Ssh,
|
||||
useSMTP: Boolean,
|
||||
smtp: Option[Smtp],
|
||||
@@ -178,8 +210,10 @@ object SystemSettingsService {
|
||||
oidcAuthentication: Boolean,
|
||||
oidc: Option[OIDC],
|
||||
skinName: String,
|
||||
userDefinedCss: Option[String],
|
||||
showMailAddress: Boolean,
|
||||
webHook: WebHook
|
||||
webHook: WebHook,
|
||||
upload: Upload
|
||||
) {
|
||||
|
||||
def baseUrl(request: HttpServletRequest): String =
|
||||
@@ -198,12 +232,21 @@ object SystemSettingsService {
|
||||
.fold(base)(_ + base.dropWhile(_ != ':'))
|
||||
}
|
||||
|
||||
def sshAddress: Option[SshAddress] = ssh.sshHost.collect {
|
||||
case host if ssh.enabled =>
|
||||
SshAddress(host, ssh.sshPort.getOrElse(DefaultSshPort), "git")
|
||||
}
|
||||
def sshAddress: Option[SshAddress] =
|
||||
ssh.sshHost.collect {
|
||||
case host if ssh.enabled =>
|
||||
SshAddress(host, ssh.sshPort.getOrElse(DefaultSshPort), "git")
|
||||
}
|
||||
}
|
||||
|
||||
case class RepositoryOperation(
|
||||
create: Boolean,
|
||||
delete: Boolean,
|
||||
rename: Boolean,
|
||||
transfer: Boolean,
|
||||
fork: Boolean
|
||||
)
|
||||
|
||||
case class Ssh(
|
||||
enabled: Boolean,
|
||||
sshHost: Option[String],
|
||||
@@ -251,13 +294,15 @@ object SystemSettingsService {
|
||||
host: String,
|
||||
port: Int,
|
||||
user: Option[String],
|
||||
password: Option[String],
|
||||
password: Option[String]
|
||||
)
|
||||
|
||||
case class SshAddress(host: String, port: Int, genericUser: String)
|
||||
|
||||
case class WebHook(blockPrivateAddress: Boolean, whitelist: Seq[String])
|
||||
|
||||
case class Upload(maxFileSize: Long, timeout: Long, largeMaxFileSize: Long, largeTimeout: Long)
|
||||
|
||||
val DefaultSshPort = 29418
|
||||
val DefaultSmtpPort = 25
|
||||
val DefaultLdapPort = 389
|
||||
@@ -267,9 +312,15 @@ object SystemSettingsService {
|
||||
private val AllowAccountRegistration = "allow_account_registration"
|
||||
private val AllowAnonymousAccess = "allow_anonymous_access"
|
||||
private val IsCreateRepoOptionPublic = "is_create_repository_option_public"
|
||||
private val RepositoryOperationCreate = "repository_operation_create"
|
||||
private val RepositoryOperationDelete = "repository_operation_delete"
|
||||
private val RepositoryOperationRename = "repository_operation_rename"
|
||||
private val RepositoryOperationTransfer = "repository_operation_transfer"
|
||||
private val RepositoryOperationFork = "repository_operation_fork"
|
||||
private val Gravatar = "gravatar"
|
||||
private val Notification = "notification"
|
||||
private val ActivityLogLimit = "activity_log_limit"
|
||||
private val LimitVisibleRepositories = "limitVisibleRepositories"
|
||||
private val SshEnabled = "ssh"
|
||||
private val SshHost = "ssh.host"
|
||||
private val SshPort = "ssh.port"
|
||||
@@ -301,14 +352,14 @@ object SystemSettingsService {
|
||||
private val OidcClientSecret = "oidc.client_secret"
|
||||
private val OidcJwsAlgorithm = "oidc.jws_algorithm"
|
||||
private val SkinName = "skinName"
|
||||
private val UserDefinedCss = "userDefinedCss"
|
||||
private val ShowMailAddress = "showMailAddress"
|
||||
private val PluginNetworkInstall = "plugin.networkInstall"
|
||||
private val PluginProxyHost = "plugin.proxy.host"
|
||||
private val PluginProxyPort = "plugin.proxy.port"
|
||||
private val PluginProxyUser = "plugin.proxy.user"
|
||||
private val PluginProxyPassword = "plugin.proxy.password"
|
||||
private val WebHookBlockPrivateAddress = "webhook.block_private_address"
|
||||
private val WebHookWhitelist = "webhook.whitelist"
|
||||
private val UploadMaxFileSize = "upload.maxFileSize"
|
||||
private val UploadTimeout = "upload.timeout"
|
||||
private val UploadLargeMaxFileSize = "upload.largeMaxFileSize"
|
||||
private val UploadLargeTimeout = "upload.largeTimeout"
|
||||
|
||||
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = {
|
||||
getConfigValue(key).getOrElse {
|
||||
|
||||
@@ -24,7 +24,7 @@ class ApiAuthenticationFilter extends Filter with AccessTokenService with Accoun
|
||||
val response = res.asInstanceOf[HttpServletResponse]
|
||||
Option(request.getHeader("Authorization"))
|
||||
.map {
|
||||
case auth if auth.startsWith("token ") =>
|
||||
case auth if auth.toLowerCase().startsWith("token ") =>
|
||||
AccessTokenService.getAccountByAccessToken(auth.substring(6).trim).toRight(())
|
||||
case auth if auth.startsWith("Basic ") => doBasicAuth(auth, loadSystemSettings(), request).toRight(())
|
||||
case _ => Left(())
|
||||
|
||||
@@ -7,7 +7,7 @@ import Directory._
|
||||
import ConfigUtil._
|
||||
import com.github.takezoe.slick.blocking.{BlockingH2Driver, BlockingJdbcProfile, BlockingMySQLDriver}
|
||||
import liquibase.database.AbstractJdbcDatabase
|
||||
import liquibase.database.core.{H2Database, MySQLDatabase, PostgresDatabase}
|
||||
import liquibase.database.core.{H2Database, MariaDBDatabase, MySQLDatabase, PostgresDatabase}
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
import scala.reflect.ClassTag
|
||||
@@ -78,6 +78,8 @@ object DatabaseType {
|
||||
H2
|
||||
} else if (url.startsWith("jdbc:mysql:")) {
|
||||
MySQL
|
||||
} else if (url.startsWith("jdbc:mariadb:")) {
|
||||
MariaDb
|
||||
} else if (url.startsWith("jdbc:postgresql:")) {
|
||||
PostgreSQL
|
||||
} else {
|
||||
@@ -97,6 +99,12 @@ object DatabaseType {
|
||||
val liquiDriver = new MySQLDatabase()
|
||||
}
|
||||
|
||||
object MariaDb extends DatabaseType {
|
||||
val jdbcDriver = "org.mariadb.jdbc.Driver"
|
||||
val slickDriver = BlockingMySQLDriver
|
||||
val liquiDriver = new MariaDBDatabase()
|
||||
}
|
||||
|
||||
object PostgreSQL extends DatabaseType {
|
||||
val jdbcDriver = "org.postgresql.Driver2"
|
||||
val slickDriver = BlockingPostgresDriver
|
||||
|
||||
@@ -62,7 +62,7 @@ object Directory {
|
||||
new File(getRepositoryFilesDir(owner, repository), "releases")
|
||||
|
||||
/**
|
||||
* Directory for files which are attached to issue.
|
||||
* Directory for Git LFS files.
|
||||
*/
|
||||
def getLfsDir(owner: String, repository: String): File =
|
||||
new File(getRepositoryFilesDir(owner, repository), "lfs")
|
||||
|
||||
@@ -92,12 +92,4 @@ object FileUtil {
|
||||
name
|
||||
}
|
||||
|
||||
lazy val MaxFileSize: Long = {
|
||||
ConfigUtil.getConfigValue[Long]("gitbucket.maxFileSize").getOrElse(3 * 1024 * 1024)
|
||||
}
|
||||
|
||||
lazy val UploadTimeout: Long = {
|
||||
ConfigUtil.getConfigValue[Long]("gitbucket.UploadTimeout").getOrElse(3 * 10000)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -290,6 +290,11 @@ object JGitUtil {
|
||||
.entryCapacity(10000)
|
||||
.build()
|
||||
|
||||
private val objectCommitCache = new Cache2kBuilder[ObjectId, RevCommit]() {}
|
||||
.name("object-commit")
|
||||
.entryCapacity(10000)
|
||||
.build()
|
||||
|
||||
def removeCache(git: Git): Unit = {
|
||||
val dir = git.getRepository.getDirectory
|
||||
val keyPrefix = dir.getAbsolutePath + "@"
|
||||
@@ -368,9 +373,16 @@ object JGitUtil {
|
||||
* @param revision the branch name or commit id
|
||||
* @param path the directory path (optional)
|
||||
* @param baseUrl the base url of GitBucket instance. This parameter is used to generate links of submodules (optional)
|
||||
* @return HTML of the file list
|
||||
* @param commitCount the number of commit of this repository (optional). If this number is greater than threshold, the commit info is cached in memory.
|
||||
* @return The list of files in the specified directory. If the number of files are greater than threshold, the returned file list won't include the commit info.
|
||||
*/
|
||||
def getFileList(git: Git, revision: String, path: String = ".", baseUrl: Option[String] = None): List[FileInfo] = {
|
||||
def getFileList(
|
||||
git: Git,
|
||||
revision: String,
|
||||
path: String = ".",
|
||||
baseUrl: Option[String] = None,
|
||||
commitCount: Int = 0
|
||||
): List[FileInfo] = {
|
||||
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||
val objectId = git.getRepository.resolve(revision)
|
||||
if (objectId == null) return Nil
|
||||
@@ -388,104 +400,85 @@ object JGitUtil {
|
||||
Using.resource(treeWalk)(f)
|
||||
}
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def simplifyPath(
|
||||
tuple: (ObjectId, FileMode, String, String, Option[String], RevCommit)
|
||||
): (ObjectId, FileMode, String, String, Option[String], RevCommit) = tuple match {
|
||||
case (oid, FileMode.TREE, name, path, _, commit) =>
|
||||
(Using.resource(new TreeWalk(git.getRepository)) { walk =>
|
||||
walk.addTree(oid)
|
||||
// single tree child, or None
|
||||
if (walk.next() && walk.getFileMode(0) == FileMode.TREE) {
|
||||
Some(
|
||||
(
|
||||
walk.getObjectId(0),
|
||||
walk.getFileMode(0),
|
||||
name + "/" + walk.getNameString,
|
||||
path + "/" + walk.getNameString,
|
||||
None,
|
||||
commit
|
||||
)
|
||||
).filterNot(_ => walk.next())
|
||||
} else {
|
||||
None
|
||||
tuple: (ObjectId, FileMode, String, String, Option[String], Option[RevCommit])
|
||||
): (ObjectId, FileMode, String, String, Option[String], Option[RevCommit]) =
|
||||
tuple match {
|
||||
case (oid, FileMode.TREE, name, path, _, commit) =>
|
||||
(Using.resource(new TreeWalk(git.getRepository)) { walk =>
|
||||
walk.addTree(oid)
|
||||
// single tree child, or None
|
||||
if (walk.next() && walk.getFileMode(0) == FileMode.TREE) {
|
||||
Some(
|
||||
(
|
||||
walk.getObjectId(0),
|
||||
walk.getFileMode(0),
|
||||
name + "/" + walk.getNameString,
|
||||
path + "/" + walk.getNameString,
|
||||
None,
|
||||
commit
|
||||
)
|
||||
).filterNot(_ => walk.next())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) match {
|
||||
case Some(child) => simplifyPath(child)
|
||||
case _ => tuple
|
||||
}
|
||||
}) match {
|
||||
case Some(child) => simplifyPath(child)
|
||||
case _ => tuple
|
||||
}
|
||||
case _ => tuple
|
||||
}
|
||||
case _ => tuple
|
||||
}
|
||||
|
||||
def tupleAdd(tuple: (ObjectId, FileMode, String, String, Option[String]), rev: RevCommit) = tuple match {
|
||||
case (oid, fmode, name, path, opt) => (oid, fmode, name, path, opt, rev)
|
||||
}
|
||||
|
||||
@tailrec
|
||||
def findLastCommits(
|
||||
result: List[(ObjectId, FileMode, String, String, Option[String], RevCommit)],
|
||||
restList: List[((ObjectId, FileMode, String, String, Option[String]), Map[RevCommit, RevCommit])],
|
||||
revIterator: java.util.Iterator[RevCommit]
|
||||
): List[(ObjectId, FileMode, String, String, Option[String], RevCommit)] = {
|
||||
if (restList.isEmpty) {
|
||||
result
|
||||
} else if (!revIterator.hasNext) { // maybe, revCommit has only 1 log. other case, restList be empty
|
||||
result ++ restList.map { case (tuple, map) => tupleAdd(tuple, map.values.headOption.getOrElse(revCommit)) }
|
||||
} else {
|
||||
val newCommit = revIterator.next
|
||||
val (thisTimeChecks, skips) = restList.partition {
|
||||
case (tuple, parentsMap) => parentsMap.contains(newCommit)
|
||||
}
|
||||
if (thisTimeChecks.isEmpty) {
|
||||
findLastCommits(result, restList, revIterator)
|
||||
} else {
|
||||
var nextRest = skips
|
||||
var nextResult = result
|
||||
// Map[(name, oid), (tuple, parentsMap)]
|
||||
val rest = scala.collection.mutable.Map(thisTimeChecks.map { t =>
|
||||
(t._1._3 -> t._1._1) -> t
|
||||
}: _*)
|
||||
lazy val newParentsMap = newCommit.getParents.map(_ -> newCommit).toMap
|
||||
useTreeWalk(newCommit) { walk =>
|
||||
while (walk.next) {
|
||||
rest.remove(walk.getNameString -> walk.getObjectId(0)).foreach {
|
||||
case (tuple, _) =>
|
||||
if (newParentsMap.isEmpty) {
|
||||
nextResult +:= tupleAdd(tuple, newCommit)
|
||||
} else {
|
||||
nextRest +:= tuple -> newParentsMap
|
||||
}
|
||||
}
|
||||
def appendLastCommits(
|
||||
fileList: List[(ObjectId, FileMode, String, String, Option[String])]
|
||||
): List[(ObjectId, FileMode, String, String, Option[String], Option[RevCommit])] = {
|
||||
fileList.map {
|
||||
case (id, mode, name, path, opt) =>
|
||||
// Don't attempt to get the last commit if the number of files is very large.
|
||||
if (fileList.size >= 100) {
|
||||
(id, mode, name, path, opt, None)
|
||||
} else if (commitCount < 10000) {
|
||||
val i = git
|
||||
(id, mode, name, path, opt, Some(getCommit(path)))
|
||||
} else {
|
||||
val cached = objectCommitCache.getEntry(id)
|
||||
if (cached == null) {
|
||||
val commit = getCommit(path)
|
||||
objectCommitCache.put(id, commit)
|
||||
(id, mode, name, path, opt, Some(commit))
|
||||
} else {
|
||||
(id, mode, name, path, opt, Some(cached.getValue))
|
||||
}
|
||||
}
|
||||
rest.values.foreach {
|
||||
case (tuple, parentsMap) =>
|
||||
val restParentsMap = parentsMap - newCommit
|
||||
if (restParentsMap.isEmpty) {
|
||||
nextResult +:= tupleAdd(tuple, parentsMap(newCommit))
|
||||
} else {
|
||||
nextRest +:= tuple -> restParentsMap
|
||||
}
|
||||
}
|
||||
findLastCommits(nextResult, nextRest, revIterator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getCommit(path: String): RevCommit = {
|
||||
git
|
||||
.log()
|
||||
.addPath(path)
|
||||
.add(revCommit)
|
||||
.setMaxCount(1)
|
||||
.call()
|
||||
.iterator()
|
||||
.next()
|
||||
}
|
||||
|
||||
var fileList: List[(ObjectId, FileMode, String, String, Option[String])] = Nil
|
||||
useTreeWalk(revCommit) { treeWalk =>
|
||||
while (treeWalk.next()) {
|
||||
val linkUrl = if (treeWalk.getFileMode(0) == FileMode.GITLINK) {
|
||||
getSubmodules(git, revCommit.getTree, baseUrl).find(_.path == treeWalk.getPathString).map(_.viewerUrl)
|
||||
} else None
|
||||
fileList +:= (treeWalk.getObjectId(0), treeWalk.getFileMode(0), treeWalk.getNameString, treeWalk.getPathString, linkUrl)
|
||||
fileList +:= (treeWalk.getObjectId(0), treeWalk.getFileMode(
|
||||
0
|
||||
), treeWalk.getNameString, treeWalk.getPathString, linkUrl)
|
||||
}
|
||||
}
|
||||
revWalk.markStart(revCommit)
|
||||
val it = revWalk.iterator
|
||||
val lastCommit = it.next
|
||||
val nextParentsMap = Option(lastCommit).map(_.getParents.map(_ -> lastCommit).toMap).getOrElse(Map())
|
||||
findLastCommits(List.empty, fileList.map(a => a -> nextParentsMap), it)
|
||||
|
||||
appendLastCommits(fileList)
|
||||
.map(simplifyPath)
|
||||
.map {
|
||||
case (objectId, fileMode, name, path, linkUrl, commit) =>
|
||||
@@ -494,11 +487,14 @@ object JGitUtil {
|
||||
fileMode == FileMode.TREE || fileMode == FileMode.GITLINK,
|
||||
name,
|
||||
path,
|
||||
getSummaryMessage(commit.getFullMessage, commit.getShortMessage),
|
||||
commit.getName,
|
||||
commit.getAuthorIdent.getWhen,
|
||||
commit.getAuthorIdent.getName,
|
||||
commit.getAuthorIdent.getEmailAddress,
|
||||
getSummaryMessage(
|
||||
commit.map(_.getFullMessage).getOrElse(""),
|
||||
commit.map(_.getShortMessage).getOrElse("")
|
||||
),
|
||||
commit.map(_.getName).getOrElse(""),
|
||||
commit.map(_.getAuthorIdent.getWhen).orNull,
|
||||
commit.map(_.getAuthorIdent.getName).getOrElse(""),
|
||||
commit.map(_.getAuthorIdent.getEmailAddress).getOrElse(""),
|
||||
linkUrl
|
||||
)
|
||||
}
|
||||
@@ -1004,11 +1000,12 @@ object JGitUtil {
|
||||
*/
|
||||
def getContentFromPath(git: Git, revTree: RevTree, path: String, fetchLargeFile: Boolean): Option[Array[Byte]] = {
|
||||
@scala.annotation.tailrec
|
||||
def getPathObjectId(path: String, walk: TreeWalk): Option[ObjectId] = walk.next match {
|
||||
case true if (walk.getPathString == path) => Some(walk.getObjectId(0))
|
||||
case true => getPathObjectId(path, walk)
|
||||
case false => None
|
||||
}
|
||||
def getPathObjectId(path: String, walk: TreeWalk): Option[ObjectId] =
|
||||
walk.next match {
|
||||
case true if (walk.getPathString == path) => Some(walk.getObjectId(0))
|
||||
case true => getPathObjectId(path, walk)
|
||||
case false => None
|
||||
}
|
||||
|
||||
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||
treeWalk.addTree(revTree)
|
||||
|
||||
@@ -127,8 +127,14 @@ object LDAPUtil {
|
||||
private def getSslProvider(): Provider = {
|
||||
val cachedInstance = provider.get()
|
||||
if (cachedInstance == null) {
|
||||
val newInstance = Class
|
||||
.forName("com.sun.net.ssl.internal.ssl.Provider")
|
||||
val cls = try {
|
||||
Class.forName("com.sun.net.ssl.internal.ssl.Provider")
|
||||
} catch {
|
||||
case e: ClassNotFoundException =>
|
||||
Class.forName("com.ibm.jsse.IBMJSSEProvider")
|
||||
case e: Throwable => throw e
|
||||
}
|
||||
val newInstance = cls
|
||||
.getDeclaredConstructor()
|
||||
.newInstance()
|
||||
.asInstanceOf[Provider]
|
||||
@@ -149,7 +155,7 @@ object LDAPUtil {
|
||||
keystore: String,
|
||||
error: String
|
||||
)(f: LDAPConnection => Either[String, A]): Either[String, A] = {
|
||||
if (tls) {
|
||||
if (tls || ssl) {
|
||||
// Dynamically set Sun as the security provider
|
||||
Security.addProvider(getSslProvider())
|
||||
|
||||
|
||||
@@ -19,21 +19,6 @@ trait Validations {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constraint for the password.
|
||||
*/
|
||||
val password: Constraint = new Constraint() {
|
||||
lazy val validatePassword = ConfigUtil.getConfigValue[Boolean]("gitbucket.validate.password").getOrElse(true)
|
||||
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
if (validatePassword == true && !value.matches("[a-zA-Z0-9\\-_.]+")) {
|
||||
Some(s"${name} contains invalid character.")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constraint for the repository identifier.
|
||||
*/
|
||||
|
||||
@@ -494,4 +494,8 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
|
||||
case class CommentDiffLine(newLine: Option[String], oldLine: Option[String], `type`: String, text: String)
|
||||
|
||||
def appendQueryString(baseUrl: String, queryString: String): String = {
|
||||
s"$baseUrl${if (baseUrl.contains("?")) "&" else "?"}$queryString"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,12 +7,16 @@
|
||||
<form action="@context.path/admin/system" method="POST" validate="true" class="form-horizontal" autocomplete="off">
|
||||
<ul class="nav nav-tabs fill-width" id="pullreq-tab">
|
||||
<li><a href="#system">System settings</a></li>
|
||||
<li><a href="#integrations">Integrations</a></li>
|
||||
<li><a href="#authentication">Authentication</a></li>
|
||||
</ul>
|
||||
<div class="tab-content fill-width" style="padding-top: 20px;">
|
||||
<div class="tab-pane" id="system">
|
||||
@settings_system(info)
|
||||
</div>
|
||||
<div class="tab-pane" id="integrations">
|
||||
@settings_integrations(info)
|
||||
</div>
|
||||
<div class="tab-pane" id="authentication">
|
||||
@settings_authentication(info)
|
||||
</div>
|
||||
@@ -30,6 +34,9 @@ $(function(){
|
||||
if(location.hash == '#authentication'){
|
||||
$('li:has(a[href="#authentication"])').addClass('active');
|
||||
$('div#authentication').addClass('active');
|
||||
} else if(location.hash == '#integrations'){
|
||||
$('li:has(a[href="#integrations"])').addClass('active');
|
||||
$('div#integrations').addClass('active');
|
||||
} else {
|
||||
$('li:has(a[href="#system"])').addClass('active');
|
||||
$('div#system').addClass('active');
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
@(info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
||||
@import gitbucket.core.util.DatabaseConfig
|
||||
<!--====================================================================-->
|
||||
<!-- Services -->
|
||||
<!--====================================================================-->
|
||||
<label class="strong">Services</label>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="gravatar"@if(context.settings.gravatar){ checked}/>
|
||||
Use Gravatar for profile images
|
||||
</label>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- SSH access -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">SSH access</label>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="sshEnabled" name="ssh.enabled"@if(context.settings.ssh.enabled){ checked}/>
|
||||
Enable SSH access to git repository
|
||||
<span class="muted normal">(Both SSH host and Base URL are required if SSH access is enabled)</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="ssh">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="sshHost">SSH host</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="sshHost" name="ssh.host" class="form-control" value="@context.settings.ssh.sshHost"/>
|
||||
<span id="error-ssh_host" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="sshPort">SSH port</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="sshPort" name="ssh.port" class="form-control" value="@context.settings.ssh.sshPort"/>
|
||||
<span id="error-ssh_port" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--====================================================================-->
|
||||
<!-- Communication email -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">Communication</label>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="useSMTP" name="useSMTP" @if(context.settings.useSMTP){ checked}/>
|
||||
SMTP
|
||||
<span class="muted normal">(Enable notification as well as SMTP configuration if you want to send notification email too)</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="useSMTP">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpHost">SMTP host</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="smtpHost" name="smtp.host" class="form-control" value="@context.settings.smtp.map(_.host)"/>
|
||||
<span id="error-smtp_host" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpPort">SMTP port</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="smtpPort" name="smtp.port" class="form-control input-mini" value="@context.settings.smtp.map(_.port)"/>
|
||||
<span id="error-smtp_port" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpUser">SMTP user</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="smtpUser" name="smtp.user" class="form-control" value="@context.settings.smtp.map(_.user)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpPassword">SMTP password</label>
|
||||
<div class="col-md-10">
|
||||
<input type="password" id="smtpPassword" name="smtp.password" class="form-control" value="@context.settings.smtp.map(_.password)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpSsl">Enable SSL</label>
|
||||
<div class="col-md-10">
|
||||
<input type="checkbox" id="smtpSsl" name="smtp.ssl"@if(context.settings.smtp.flatMap(_.ssl).getOrElse(false)){ checked}/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpStarttls">Enable STARTTLS</label>
|
||||
<div class="col-md-10">
|
||||
<input type="checkbox" id="smtpStarttls" name="smtp.starttls"@if(context.settings.smtp.flatMap(_.starttls).getOrElse(false)){ checked}/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="fromAddress">FROM address</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="fromAddress" name="smtp.fromAddress" class="form-control" value="@context.settings.smtp.map(_.fromAddress)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="fromName">FROM name</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="fromName" name="smtp.fromName" class="form-control" value="@context.settings.smtp.map(_.fromName)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
Send test mail to:
|
||||
<input type="text" id="testAddress" size="30"/>
|
||||
<input type="button" id="sendTestMail" value="Send"/>
|
||||
</div>
|
||||
</div>
|
||||
<!--====================================================================-->
|
||||
<!-- Notification email -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">Notifications</label>
|
||||
<fieldset>
|
||||
<label class="checkbox" for="notification">
|
||||
<input type="checkbox" id="notification" name="notification"@if(context.settings.notification){ checked}/>
|
||||
Send notifications
|
||||
</label>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- Web hook -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">Web hook</label>
|
||||
<fieldset>
|
||||
<label class="checkbox" for="blockPrivateAddress">
|
||||
<input type="checkbox" id="blockPrivateAddress" name="webhook.blockPrivateAddress"@if(context.settings.webHook.blockPrivateAddress){ checked}/>
|
||||
Block sending to private addresses
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="webhook">
|
||||
<label><span class="strong">IP whitelist</span></label>
|
||||
<fieldset>
|
||||
<textarea name="webhook.whitelist" class="form-control" style="height: 100px;">@context.settings.webHook.whitelist.mkString("\n")</textarea>
|
||||
</fieldset>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
$('#sendTestMail').click(function(){
|
||||
var host = $('#smtpHost' ).val();
|
||||
var port = $('#smtpPort' ).val();
|
||||
var user = $('#smtpUser' ).val();
|
||||
var password = $('#smtpPassword').val();
|
||||
var ssl = $('#smtpSsl' ).prop('checked');
|
||||
var starttls = $('#smtpStarttls').prop('checked');
|
||||
var fromAddress = $('#fromAddress' ).val();
|
||||
var fromName = $('#fromName' ).val();
|
||||
var testAddress = $('#testAddress' ).val();
|
||||
|
||||
if(host == ''){
|
||||
alert('SMTP Host is required.');
|
||||
$('#smtpHost').focus();
|
||||
} else if(testAddress == ''){
|
||||
alert('Destination is required.');
|
||||
$('#testAddress').focus();
|
||||
} else {
|
||||
$.post('@context.path/admin/system/sendmail', {
|
||||
'smtp.host': host,
|
||||
'smtp.port': port,
|
||||
'smtp.user': user,
|
||||
'smtp.password': password,
|
||||
'smtp.ssl': ssl,
|
||||
'smtp.starttls': starttls,
|
||||
'smtp.fromAddress': fromAddress,
|
||||
'smtp.fromName': fromName,
|
||||
'testAddress': testAddress
|
||||
}, function(data, status){
|
||||
if(data != ''){
|
||||
alert(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('#sshEnabled').change(function(){
|
||||
$('.ssh input').prop('disabled', !$(this).prop('checked'));
|
||||
}).change();
|
||||
|
||||
$('#useSMTP').change(function(){
|
||||
$('.useSMTP input').prop('disabled', !$(this).prop('checked'));
|
||||
|
||||
// With only SMTP in current version, notification cannot be enabled if no communication channel exists
|
||||
$('#notification').prop('disabled', !$(this).prop('checked'));
|
||||
|
||||
if (!$(this).prop('checked')) {
|
||||
// With only SMTP in current version, if SMTP is unchecked then we disable notification
|
||||
$('#notification').prop('checked', false);
|
||||
}
|
||||
}).change();
|
||||
|
||||
$('#blockPrivateAddress').change(function(){
|
||||
$('.webhook textarea').prop('disabled', !$(this).prop('checked'));
|
||||
}).change();
|
||||
});
|
||||
</script>
|
||||
@@ -94,6 +94,14 @@
|
||||
</div>
|
||||
</div>
|
||||
<!--====================================================================-->
|
||||
<!-- User-defined CSS -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label><span class="strong">User-defined CSS</span></label>
|
||||
<fieldset>
|
||||
<textarea name="userDefinedCss" class="form-control" style="height: 100px;">@context.settings.userDefinedCss</textarea>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- Account registration -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
@@ -108,8 +116,80 @@
|
||||
<span class="strong">Deny</span> <span class="normal">- Only administrators can create accounts.</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- Repository operations -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">Default permissions when creating a new repository</label>
|
||||
<label class="strong">Repository operation</label>
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2">Create</label>
|
||||
<div class="col-md-10">
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.create" value="true"@if(context.settings.repositoryOperation.create){ checked}>
|
||||
<span class="strong">All users</span> <span class="normal">- All users can create repository.</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.create" value="false"@if(!context.settings.repositoryOperation.create){ checked}>
|
||||
<span class="strong">Admin only</span> <span class="normal">- Only administrators can create repository.</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2">Delete</label>
|
||||
<div class="col-md-10">
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.delete" value="true"@if(context.settings.repositoryOperation.delete){ checked}>
|
||||
<span class="strong">All users</span> <span class="normal">- All users can delete repository.</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.delete" value="false"@if(!context.settings.repositoryOperation.delete){ checked}>
|
||||
<span class="strong">Admin only</span> <span class="normal">- Only administrators can delete repository.</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2">Rename</label>
|
||||
<div class="col-md-10">
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.rename" value="true"@if(context.settings.repositoryOperation.rename){ checked}>
|
||||
<span class="strong">All users</span> <span class="normal">- All users can rename repository.</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.rename" value="false"@if(!context.settings.repositoryOperation.rename){ checked}>
|
||||
<span class="strong">Admin only</span> <span class="normal">- Only administrators can rename repository.</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2">Transfer</label>
|
||||
<div class="col-md-10">
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.transfer" value="true"@if(context.settings.repositoryOperation.transfer){ checked}>
|
||||
<span class="strong">All users</span> <span class="normal">- All users can transfer repository.</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.transfer" value="false"@if(!context.settings.repositoryOperation.transfer){ checked}>
|
||||
<span class="strong">Admin only</span> <span class="normal">- Only administrators can transfer repository.</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2">Fork</label>
|
||||
<div class="col-md-10">
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.fork" value="true"@if(context.settings.repositoryOperation.fork){ checked}>
|
||||
<span class="strong">All users</span> <span class="normal">- All users can fork repository.</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="repositoryOperation.fork" value="false"@if(!context.settings.repositoryOperation.fork){ checked}>
|
||||
<span class="strong">Admin only</span> <span class="normal">- Only administrators can fork repository.</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<hr>
|
||||
<label class="strong">Default visibility when creating a new repository</label>
|
||||
<fieldset>
|
||||
<label class="radio">
|
||||
<input type="radio" name="isCreateRepoOptionPublic" value="true"@if(context.settings.isCreateRepoOptionPublic){ checked}>
|
||||
@@ -151,6 +231,41 @@
|
||||
</label>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- File upload -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">File upload</label>
|
||||
<fieldset>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="uploadMaxFileSize">Max size</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="uploadMaxFileSize" name="upload.maxFileSize" class="form-control input-mini" value="@context.settings.upload.maxFileSize"/>
|
||||
<span id="error-upload_maxFileSize" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="uploadTimeout">Timeout</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="uploadTimeout" name="upload.timeout" class="form-control input-mini" value="@context.settings.upload.timeout"/>
|
||||
<span id="error-upload_timeout" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="uploadLargeMaxFileSize">Max size for large files</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="uploadLargeMaxFileSize" name="upload.largeMaxFileSize" class="form-control input-mini" value="@context.settings.upload.largeMaxFileSize"/>
|
||||
<span id="error-upload_largeMaxFileSize" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="uploadLargeTimeout">Timeout for large files</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="uploadLargeTimeout" name="upload.largeTimeout" class="form-control input-mini" value="@context.settings.upload.largeTimeout"/>
|
||||
<span id="error-upload_largeTimeout" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- Activity -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
@@ -165,141 +280,20 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- Services -->
|
||||
<!-- Sidebar -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">Services</label>
|
||||
<label><span class="strong">Show Repositories in Sidebar</span></label>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" name="gravatar"@if(context.settings.gravatar){ checked}/>
|
||||
Use Gravatar for profile images
|
||||
<label class="radio">
|
||||
<input type="radio" name="limitVisibleRepositories" value="false"@if(!context.settings.limitVisibleRepositories){ checked}>
|
||||
<span class="strong">All</span> <span class="normal">- Show all repositories in sidebar.</span>
|
||||
</label>
|
||||
<label class="radio">
|
||||
<input type="radio" name="limitVisibleRepositories" value="true"@if(context.settings.limitVisibleRepositories){ checked}>
|
||||
<span class="strong">Limited</span> <span class="normal">- Limit the visible repositories in sidebar.</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- SSH access -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">SSH access</label>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="sshEnabled" name="ssh.enabled"@if(context.settings.ssh.enabled){ checked}/>
|
||||
Enable SSH access to git repository
|
||||
<span class="muted normal">(Both SSH host and Base URL are required if SSH access is enabled)</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="ssh">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="sshHost">SSH host</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="sshHost" name="ssh.host" class="form-control" value="@context.settings.ssh.sshHost"/>
|
||||
<span id="error-ssh_host" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="sshPort">SSH port</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="sshPort" name="ssh.port" class="form-control" value="@context.settings.ssh.sshPort"/>
|
||||
<span id="error-ssh_port" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--====================================================================-->
|
||||
<!-- Communication email -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">Communication</label>
|
||||
<fieldset>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="useSMTP" name="useSMTP" @if(context.settings.useSMTP){ checked}/>
|
||||
SMTP
|
||||
<span class="muted normal">(Enable notification as well as SMTP configuration if you want to send notification email too)</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="useSMTP">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpHost">SMTP host</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="smtpHost" name="smtp.host" class="form-control" value="@context.settings.smtp.map(_.host)"/>
|
||||
<span id="error-smtp_host" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpPort">SMTP port</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="smtpPort" name="smtp.port" class="form-control input-mini" value="@context.settings.smtp.map(_.port)"/>
|
||||
<span id="error-smtp_port" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpUser">SMTP user</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="smtpUser" name="smtp.user" class="form-control" value="@context.settings.smtp.map(_.user)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpPassword">SMTP password</label>
|
||||
<div class="col-md-10">
|
||||
<input type="password" id="smtpPassword" name="smtp.password" class="form-control" value="@context.settings.smtp.map(_.password)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpSsl">Enable SSL</label>
|
||||
<div class="col-md-10">
|
||||
<input type="checkbox" id="smtpSsl" name="smtp.ssl"@if(context.settings.smtp.flatMap(_.ssl).getOrElse(false)){ checked}/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="smtpStarttls">Enable STARTTLS</label>
|
||||
<div class="col-md-10">
|
||||
<input type="checkbox" id="smtpStarttls" name="smtp.starttls"@if(context.settings.smtp.flatMap(_.starttls).getOrElse(false)){ checked}/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="fromAddress">FROM address</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="fromAddress" name="smtp.fromAddress" class="form-control" value="@context.settings.smtp.map(_.fromAddress)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-2" for="fromName">FROM name</label>
|
||||
<div class="col-md-10">
|
||||
<input type="text" id="fromName" name="smtp.fromName" class="form-control" value="@context.settings.smtp.map(_.fromName)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
Send test mail to:
|
||||
<input type="text" id="testAddress" size="30"/>
|
||||
<input type="button" id="sendTestMail" value="Send"/>
|
||||
</div>
|
||||
</div>
|
||||
<!--====================================================================-->
|
||||
<!-- Notification email -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">Notifications</label>
|
||||
<fieldset>
|
||||
<label class="checkbox" for="notification">
|
||||
<input type="checkbox" id="notification" name="notification"@if(context.settings.notification){ checked}/>
|
||||
Send notifications
|
||||
</label>
|
||||
</fieldset>
|
||||
<!--====================================================================-->
|
||||
<!-- Web hook -->
|
||||
<!--====================================================================-->
|
||||
<hr>
|
||||
<label class="strong">Web hook</label>
|
||||
<fieldset>
|
||||
<label class="checkbox" for="blockPrivateAddress">
|
||||
<input type="checkbox" id="blockPrivateAddress" name="webhook.blockPrivateAddress"@if(context.settings.webHook.blockPrivateAddress){ checked}/>
|
||||
Block sending to private addresses
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="webhook">
|
||||
<label><span class="strong">IP whitelist</span></label>
|
||||
<fieldset>
|
||||
<textarea name="webhook.whitelist" class="form-control" style="height: 100px;">@context.settings.webHook.whitelist.mkString("\n")</textarea>
|
||||
</fieldset>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
$('#skinName').change(function(evt) {
|
||||
@@ -309,61 +303,5 @@ $(function(){
|
||||
themeCss.attr('href', themeCss.attr('href').replace(oldVal, that.val()));
|
||||
$(document.body).removeClass(oldVal).addClass(that.val());
|
||||
});
|
||||
|
||||
$('#sendTestMail').click(function(){
|
||||
var host = $('#smtpHost' ).val();
|
||||
var port = $('#smtpPort' ).val();
|
||||
var user = $('#smtpUser' ).val();
|
||||
var password = $('#smtpPassword').val();
|
||||
var ssl = $('#smtpSsl' ).prop('checked');
|
||||
var starttls = $('#smtpStarttls').prop('checked');
|
||||
var fromAddress = $('#fromAddress' ).val();
|
||||
var fromName = $('#fromName' ).val();
|
||||
var testAddress = $('#testAddress' ).val();
|
||||
|
||||
if(host == ''){
|
||||
alert('SMTP Host is required.');
|
||||
$('#smtpHost').focus();
|
||||
} else if(testAddress == ''){
|
||||
alert('Destination is required.');
|
||||
$('#testAddress').focus();
|
||||
} else {
|
||||
$.post('@context.path/admin/system/sendmail', {
|
||||
'smtp.host': host,
|
||||
'smtp.port': port,
|
||||
'smtp.user': user,
|
||||
'smtp.password': password,
|
||||
'smtp.ssl': ssl,
|
||||
'smtp.starttls': starttls,
|
||||
'smtp.fromAddress': fromAddress,
|
||||
'smtp.fromName': fromName,
|
||||
'testAddress': testAddress
|
||||
}, function(data, status){
|
||||
if(data != ''){
|
||||
alert(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('#sshEnabled').change(function(){
|
||||
$('.ssh input').prop('disabled', !$(this).prop('checked'));
|
||||
}).change();
|
||||
|
||||
$('#useSMTP').change(function(){
|
||||
$('.useSMTP input').prop('disabled', !$(this).prop('checked'));
|
||||
|
||||
// With only SMTP in current version, notification cannot be enabled if no communication channel exists
|
||||
$('#notification').prop('disabled', !$(this).prop('checked'));
|
||||
|
||||
if (!$(this).prop('checked')) {
|
||||
// With only SMTP in current version, if SMTP is unchecked then we disable notification
|
||||
$('#notification').prop('checked', false);
|
||||
}
|
||||
}).change();
|
||||
|
||||
$('#blockPrivateAddress').change(function(){
|
||||
$('.webhook textarea').prop('disabled', !$(this).prop('checked'));
|
||||
}).change();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -65,9 +65,8 @@ $(function(){
|
||||
}
|
||||
@dropzone(clickable: Boolean, textareaId: Option[String]) = {
|
||||
url: '@context.path/upload/file/@repository.owner/@repository.name',
|
||||
maxFilesize: @{FileUtil.MaxFileSize / 1024 / 1024},
|
||||
//timeout defaults to 30 secs
|
||||
timeout: @{FileUtil.UploadTimeout},
|
||||
maxFilesize: @{context.settings.upload.maxFileSize / 1024 / 1024},
|
||||
timeout: @{context.settings.upload.timeout},
|
||||
clickable: @clickable,
|
||||
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
|
||||
success: function(file, id) {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
@(latestUpdatedDate: java.util.Date,
|
||||
recentOnly: Boolean = true)
|
||||
@import gitbucket.core.view.helpers
|
||||
<span data-toggle="tooltip" title="@helpers.datetime(latestUpdatedDate)">
|
||||
@if(recentOnly){
|
||||
@helpers.datetimeAgoRecentOnly(latestUpdatedDate)
|
||||
} else {
|
||||
@helpers.datetimeAgo(latestUpdatedDate)
|
||||
}
|
||||
</span>
|
||||
@if(latestUpdatedDate != null){
|
||||
<span data-toggle="tooltip" title="@helpers.datetime(latestUpdatedDate)">
|
||||
@if(recentOnly){
|
||||
@helpers.datetimeAgoRecentOnly(latestUpdatedDate)
|
||||
} else {
|
||||
@helpers.datetimeAgo(latestUpdatedDate)
|
||||
}
|
||||
</span>
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@
|
||||
} else {
|
||||
@if(diff.newContent != None || diff.oldContent != None){
|
||||
<div id="diffText-@i" class="diffText"></div>
|
||||
<input type="hidden" id="newText-@i" data-file-name="@diff.oldPath" value="@diff.newContent">
|
||||
<input type="hidden" id="oldText-@i" data-file-name="@diff.newPath" value="@diff.oldContent">
|
||||
<input type="hidden" id="newText-@i" data-file-name="@diff.oldPath" data-val="@diff.newContent">
|
||||
<input type="hidden" id="oldText-@i" data-file-name="@diff.newPath" data-val="@diff.oldContent">
|
||||
} else {
|
||||
@if(diff.newIsImage || diff.oldIsImage){
|
||||
<div class="diff-image-render diff2up">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@(page: Int, count: Int, limit: Int, width: Int, baseURL: String)
|
||||
@import gitbucket.core.view.helpers
|
||||
@(page: Int, count: Int, limit: Int, width: Int, baseURL: String = "")
|
||||
@defining(gitbucket.core.view.Pagination(page, count, limit, width)){ p =>
|
||||
@if(p.count > p.limit){
|
||||
<div>
|
||||
@@ -6,7 +7,7 @@
|
||||
@if(page == 1){
|
||||
<li class="disabled"><span>◀</span></li>
|
||||
} else {
|
||||
<li><a href="@baseURL&page=@(page - 1)">◀</a></li>
|
||||
<li><a href="@helpers.appendQueryString(baseURL, s"page=${page - 1 }")">◀</a></li>
|
||||
}
|
||||
@for(i <- 1 to p.max){
|
||||
@if(i == p.max && p.omitRight){
|
||||
@@ -16,7 +17,7 @@
|
||||
<li class="active"><span>@i</span></li>
|
||||
} else {
|
||||
@if(p.visibleFor(i)){
|
||||
<li><a href="@baseURL&page=@i">@i</a></li>
|
||||
<li><a href="@helpers.appendQueryString(baseURL, s"page=$i")">@i</a></li>
|
||||
}
|
||||
}
|
||||
@if(i == 1 && p.omitLeft){
|
||||
@@ -26,7 +27,7 @@
|
||||
@if(page == p.max){
|
||||
<li class="disabled"><span>▶</span></li>
|
||||
} else {
|
||||
<li><a href="@baseURL&page=@(page + 1)">▶</a></li>
|
||||
<li><a href="@helpers.appendQueryString(baseURL, s"page=${page + 1}")">▶</a></li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
isEditable: Boolean,
|
||||
isManageable: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||
|
||||
@import gitbucket.core.view.helpers
|
||||
|
||||
@gitbucket.core.html.main((if(target == "issues") "Issues" else "Pull requests") + s" - ${repository.owner}/${repository.name}", Some(repository)){
|
||||
@gitbucket.core.html.menu(target, repository){
|
||||
<ul class="nav nav-pills pull-left" style="line-height: 14px; margin-bottom: 10px;">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<link href="@helpers.assets("/vendors/jquery-ui/jquery-ui.structure.min.css")" rel="stylesheet">
|
||||
<link href="@helpers.assets("/vendors/jquery-ui/jquery-ui.theme.min.css")" rel="stylesheet">
|
||||
<link href="@helpers.assets("/common/css/gitbucket.css")" rel="stylesheet">
|
||||
<script src="@helpers.assets("/vendors/jquery/jquery-3.4.1.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/jquery/jquery-3.5.1.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/jquery-ui/jquery-ui.min.js")"></script>
|
||||
<script src="@helpers.assets("/vendors/dropzone/dropzone.min.js")"></script>
|
||||
<script src="@helpers.assets("/common/js/validation.js")"></script>
|
||||
@@ -55,6 +55,9 @@
|
||||
<meta name="go-import" content="@context.baseUrl.replaceFirst("^https?://", "")/@repository.owner/@repository.name git @repository.httpUrl" />
|
||||
}
|
||||
<script src="@helpers.assets("/vendors/AdminLTE-2.4.2/js/adminlte.min.js")" type="text/javascript"></script>
|
||||
@context.settings.userDefinedCss.map { css =>
|
||||
<style type="text/css">@css</style>
|
||||
}
|
||||
</head>
|
||||
<body class="@context.settings.skinName page-load @if(body.toString.contains("menu-item-hover")){sidebar-mini} @if(context.sidebarCollapse){sidebar-collapse}">
|
||||
<div class="wrapper">
|
||||
@@ -69,7 +72,7 @@
|
||||
<nav class="navbar navbar-static-top" role="navigation">
|
||||
<!-- Sidebar toggle button-->
|
||||
@if(body.toString.contains("main-sidebar")){
|
||||
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button">
|
||||
<a href="#" class="sidebar-toggle" data-toggle="push-menu" role="button" title="Toggle navigation">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
</a>
|
||||
}
|
||||
@@ -99,7 +102,9 @@
|
||||
<ul class="dropdown-menu pull-right" style="width: auto;">
|
||||
<li>
|
||||
<ul class="menu">
|
||||
<li><a href="@context.path/new">New repository</a></li>
|
||||
@if(context.settings.repositoryOperation.create || context.loginAccount.get.isAdmin){
|
||||
<li><a href="@context.path/new">New repository</a></li>
|
||||
}
|
||||
<li><a href="@context.path/groups/new">New group</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
@gitbucket.core.plugin.PluginRegistry().getRepositoryHeaders.map { repositoryHeaderComponent =>
|
||||
@repositoryHeaderComponent(repository, context)
|
||||
}
|
||||
@if(repository.repository.options.allowFork) {
|
||||
@if(repository.repository.options.allowFork && (context.settings.repositoryOperation.fork || context.loginAccount.map(_.isAdmin).getOrElse(false))) {
|
||||
@if(context.loginAccount.isEmpty){
|
||||
<a class="btn btn-default btn-sm" href="@context.path/signin?redirect=@helpers.urlEncode(s"${context.path}/${repository.owner}/${repository.name}")">
|
||||
<span class="strong"><i class="octicon octicon-repo-forked"></i>Fork</span><span class="muted">: @repository.forkedCount</span>
|
||||
|
||||
@@ -71,10 +71,9 @@ $(function(){
|
||||
});
|
||||
|
||||
$("#drop").dropzone({
|
||||
maxFilesize: @{gitbucket.core.util.FileUtil.MaxFileSize / 1024 / 1024},
|
||||
url: '@context.path/upload/release/@repository.owner/@repository.name/@helpers.encodeRefName(tag.name)',
|
||||
//timeout defaults to 30 secs
|
||||
timeout: @{gitbucket.core.util.FileUtil.UploadTimeout},
|
||||
maxFilesize: @{context.settings.upload.largeMaxFileSize / 1024 / 1024},
|
||||
timeout: @{context.settings.upload.largeTimeout},
|
||||
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
|
||||
success: function(file, id) {
|
||||
var attach =
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
releases: Seq[(gitbucket.core.util.JGitUtil.TagInfo, Option[(gitbucket.core.model.ReleaseTag, Seq[gitbucket.core.model.ReleaseAsset])])],
|
||||
hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||
releases: Seq[(gitbucket.core.util.JGitUtil.TagInfo, Option[(gitbucket.core.model.ReleaseTag, Seq[gitbucket.core.model.ReleaseAsset])])],
|
||||
hasWritePermission: Boolean,
|
||||
page: Int, totalReleases: Int)(implicit context: gitbucket.core.controller.Context)
|
||||
|
||||
@import gitbucket.core.view.helpers
|
||||
@gitbucket.core.html.main("Releases" + s" - ${repository.owner}/${repository.name}", Some(repository)){
|
||||
@gitbucket.core.html.menu("releases", repository){
|
||||
@@ -61,5 +63,9 @@
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="pull-right">
|
||||
@gitbucket.core.helper.html.paginator(page, totalReleases, gitbucket.core.service.ReleaseService.ReleaseLimit, 10)
|
||||
</div>
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,6 @@ $(function(){
|
||||
$('#btn-code').click(function(){
|
||||
$('#editor').show();
|
||||
$('#preview').hide();
|
||||
$('#btn-code').appendClass('active');
|
||||
$('#btn-preview').removeClass('active');
|
||||
});
|
||||
|
||||
@@ -146,7 +145,6 @@ $(function(){
|
||||
$('#editor').hide();
|
||||
$('#preview').show();
|
||||
$('#btn-code').removeClass('active');
|
||||
$('#btn-preview').addClass('active');
|
||||
|
||||
@if(fileName.map(helpers.isRenderable _).getOrElse(false)) {
|
||||
// update preview
|
||||
|
||||
@@ -51,11 +51,12 @@
|
||||
$(function(){
|
||||
$('#upload-area').dropzone({
|
||||
url: '@context.path/upload/tmp',
|
||||
maxFilesize: @{FileUtil.MaxFileSize / 1024 / 1024},
|
||||
maxFilesize: @{context.settings.upload.maxFileSize / 1024 / 1024},
|
||||
timeout: @{context.settings.upload.timeout},
|
||||
clickable: true,
|
||||
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
|
||||
success: function(file, id) {
|
||||
file.previewElement.remove();
|
||||
$(file.previewElement).remove();
|
||||
$('#upload-files').append($('<li class="upload-file">')
|
||||
.append($('<span>').data('id', id).text(file.name))
|
||||
.append($('<a class="delete" href="javascript:void(0);" style="margin-left: 4px;">(delete)</a>')));
|
||||
|
||||
@@ -7,32 +7,8 @@
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Danger Zone</div>
|
||||
<div class="panel-body">
|
||||
<form id="transfer-form" method="post" action="@helpers.url(repository)/settings/transfer" validate="true" autocomplete="off">
|
||||
<fieldset class="form-group">
|
||||
<label class="strong">Transfer Ownership</label>
|
||||
<div>
|
||||
Transfer this repo to another user or to group.
|
||||
<div class="pull-right">
|
||||
@gitbucket.core.helper.html.account("newOwner", 200, true, true)
|
||||
<input type="submit" class="btn btn-danger" value="Transfer"/>
|
||||
<div>
|
||||
<span id="error-newOwner" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<form id="delete-form" method="post" action="@helpers.url(repository)/settings/delete">
|
||||
<fieldset class="border-top form-group">
|
||||
<label class="strong">Delete repository</label>
|
||||
<div>
|
||||
Once you delete a repository, there is no going back.
|
||||
<input type="submit" class="btn btn-danger pull-right" value="Delete this repository"/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<form id="gc-form" method="post" action="@helpers.url(repository)/settings/gc">
|
||||
<fieldset class="border-top form-group">
|
||||
<fieldset class="form-group">
|
||||
<label class="strong">Garbage collection</label>
|
||||
<div>
|
||||
Run garbage collection for this git repository immediately.
|
||||
@@ -40,6 +16,51 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
@if(context.settings.repositoryOperation.rename || context.loginAccount.get.isAdmin){
|
||||
<form id="rename-form" method="post" action="@helpers.url(repository)/settings/rename" validate="true" autocomplete="off">
|
||||
<fieldset class="border-top form-group">
|
||||
<label class="strong">Rename repository</label>
|
||||
<div>
|
||||
Rename this repository. The current URL will be unavailable.
|
||||
<div class="pull-right">
|
||||
<input type="text" name="repositoryName" id="repositoryName" class="form-control" style="width: 200px; display: inline; vertical-align: middle;" value="@repository.name"/>
|
||||
<input type="submit" class="btn btn-danger" value="Rename"/>
|
||||
<div>
|
||||
<span id="error-repositoryName" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
}
|
||||
@if(context.settings.repositoryOperation.transfer || context.loginAccount.get.isAdmin){
|
||||
<form id="transfer-form" method="post" action="@helpers.url(repository)/settings/transfer" validate="true" autocomplete="off">
|
||||
<fieldset class="border-top form-group">
|
||||
<label class="strong">Transfer Ownership</label>
|
||||
<div>
|
||||
Transfer this repo to another user or to group.
|
||||
<div class="pull-right">
|
||||
@gitbucket.core.helper.html.account("newOwner", 200, true, true)
|
||||
<input type="submit" class="btn btn-danger" value="Transfer"/>
|
||||
<div>
|
||||
<span id="error-newOwner" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
}
|
||||
@if(context.settings.repositoryOperation.delete || context.loginAccount.get.isAdmin){
|
||||
<form id="delete-form" method="post" action="@helpers.url(repository)/settings/delete">
|
||||
<fieldset class="border-top form-group">
|
||||
<label class="strong">Delete repository</label>
|
||||
<div>
|
||||
Once you delete a repository, there is no going back.
|
||||
<input type="submit" class="btn btn-danger pull-right" value="Delete this repository"/>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -50,6 +71,13 @@ $(function(){
|
||||
$('#delete-form').submit(function(){
|
||||
return confirm('Once you delete a repository, there is no going back.\nAre you sure?');
|
||||
});
|
||||
$('#rename-form').submit(function(){
|
||||
if($('#rename-form').data('validated') === true){
|
||||
return confirm('Rename this repository as you entered. The current URL will be unavailable.\nAre you sure?');
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
$('#transfer-form').submit(function(){
|
||||
if($('#transfer-form').data('validated') === true){
|
||||
return confirm('Transfer to the repository owner you entered.\nAre you sure?');
|
||||
|
||||
@@ -9,11 +9,6 @@
|
||||
<div class="panel-heading strong">Settings</div>
|
||||
<div class="panel-body">
|
||||
<fieldset class="form-group">
|
||||
<label for="repositoryName" class="strong">Repository Name:</label>
|
||||
<input type="text" name="repositoryName" id="repositoryName" class="form-control" value="@repository.name"/>
|
||||
<span id="error-repositoryName" class="error"></span>
|
||||
</fieldset>
|
||||
<fieldset class="border-top form-group">
|
||||
<label for="description" class="strong">Description:</label>
|
||||
<input type="text" name="description" id="description" class="form-control" value="@repository.repository.description"/>
|
||||
</fieldset>
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
<div class="content-wrapper main-center">
|
||||
<div class="content body">
|
||||
<div class="signin-form">
|
||||
@context.settings.information.map { information =>
|
||||
<div class="alert alert-info" style="background-color: white; color: #555; border-color: #4183c4; font-size: small; line-height: 120%;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
@Html(information)
|
||||
</div>
|
||||
@if(context.settings.allowAnonymousAccess){
|
||||
@context.settings.information.map { information =>
|
||||
<div class="alert alert-info" style="background-color: white; color: #555; border-color: #4183c4; font-size: small; line-height: 120%;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
@Html(information)
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@gitbucket.core.helper.html.error(error)
|
||||
@gitbucket.core.html.signinform(context.settings, userName, password)
|
||||
|
||||
@@ -48,7 +48,8 @@ $(function(){
|
||||
try {
|
||||
$('#content1').dropzone({
|
||||
url: '@context.path/upload/wiki/@repository.owner/@repository.name',
|
||||
maxFilesize: @{FileUtil.MaxFileSize / 1024 / 1024},
|
||||
maxFilesize: @{context.settings.upload.maxFileSize / 1024 / 1024},
|
||||
timeout: @{context.settings.upload.timeout},
|
||||
clickable: false,
|
||||
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
|
||||
success: function(file, id) {
|
||||
@@ -59,7 +60,8 @@ $(function(){
|
||||
});
|
||||
$('.clickable').dropzone({
|
||||
url: '@context.path/upload/wiki/@repository.owner/@repository.name',
|
||||
maxFilesize: @{FileUtil.MaxFileSize / 1024 / 1024},
|
||||
maxFilesize: @{context.settings.upload.maxFileSize / 1024 / 1024},
|
||||
timeout: @{context.settings.upload.timeout},
|
||||
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
|
||||
success: function(file, id) {
|
||||
var attachFile = (file.type.match(/image\/.*/) ? '\n![' + file.name.split('.')[0] : '\n[' + file.name) + '](' + file.name + ')';
|
||||
|
||||
@@ -80,16 +80,6 @@
|
||||
</cookie-config>
|
||||
</session-config>
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Optional configurations -->
|
||||
<!-- ===================================================================== -->
|
||||
<!--
|
||||
<context-param>
|
||||
<param-name>gitbucket.home</param-name>
|
||||
<param-value>PATH_TO_DATADIR</param-value>
|
||||
</context-param>
|
||||
-->
|
||||
|
||||
<mime-mapping>
|
||||
<extension>svg</extension>
|
||||
<mime-type>image/svg+xml</mime-type>
|
||||
|
||||
@@ -76,12 +76,12 @@ function displayErrors(data, elem){
|
||||
* @param ignoreSpace {Number} 0: include, 1: ignore
|
||||
*/
|
||||
function diffUsingJS(oldTextId, newTextId, outputId, viewType, ignoreSpace) {
|
||||
var old = $('#'+oldTextId), head = $('#' + newTextId);
|
||||
var old = $('#' + oldTextId), head = $('#' + newTextId);
|
||||
var render = new JsDiffRender({
|
||||
oldText: old.val(),
|
||||
oldTextName: old.attr('data-file-name'),
|
||||
newText: head.val(),
|
||||
newTextName: head.attr('data-file-name'),
|
||||
oldText : old.data('val'),
|
||||
oldTextName: old.data('file-name'),
|
||||
newText : head.data('val'),
|
||||
newTextName: head.data('file-name'),
|
||||
ignoreSpace: ignoreSpace,
|
||||
contextSize: 4
|
||||
});
|
||||
|
||||
598
src/main/webapp/assets/vendors/dropzone/dropzone.js
vendored
598
src/main/webapp/assets/vendors/dropzone/dropzone.js
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
src/main/webapp/assets/vendors/jquery/jquery-3.5.1.min.js
vendored
Normal file
2
src/main/webapp/assets/vendors/jquery/jquery-3.5.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -110,7 +110,7 @@ table.diff ins{
|
||||
background-color: #a6f3a6;
|
||||
text-decoration: none;
|
||||
}
|
||||
table.diff del{
|
||||
table.diff .body.delete del{
|
||||
background-color: #f8cbcb;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@@ -520,6 +520,7 @@ object ApiSpecModels {
|
||||
|"user":$jsonUser,
|
||||
|"labels":[$jsonLabel],
|
||||
|"assignee":$jsonUser,
|
||||
|"draft":true,
|
||||
|"id":0,
|
||||
|"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/pull/1347",
|
||||
|"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/pulls/1347",
|
||||
@@ -706,7 +707,7 @@ object ApiSpecModels {
|
||||
|"size":100,
|
||||
|"label":"release.zip",
|
||||
|"file_id":"${assetFileName}",
|
||||
|"browser_download_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/releases/tag1/assets/${assetFileName}"
|
||||
|"browser_download_url":"http://gitbucket.exmple.com/octocat/Hello-World/releases/tag1/assets/${assetFileName}"
|
||||
|}""".stripMargin
|
||||
|
||||
val jsonRelease =
|
||||
|
||||
@@ -12,7 +12,7 @@ import java.sql.DriverManager
|
||||
import java.io.File
|
||||
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.service.SystemSettingsService.{Ssh, SystemSettings}
|
||||
import gitbucket.core.service.SystemSettingsService.{RepositoryOperation, Ssh, SystemSettings}
|
||||
import javax.servlet.http.{HttpServletRequest, HttpSession}
|
||||
import org.scalatestplus.mockito.MockitoSugar
|
||||
import org.mockito.Mockito._
|
||||
@@ -36,9 +36,17 @@ trait ServiceSpecBase extends MockitoSugar {
|
||||
allowAccountRegistration = false,
|
||||
allowAnonymousAccess = true,
|
||||
isCreateRepoOptionPublic = true,
|
||||
repositoryOperation = RepositoryOperation(
|
||||
create = true,
|
||||
delete = true,
|
||||
rename = true,
|
||||
transfer = true,
|
||||
fork = true
|
||||
),
|
||||
gravatar = false,
|
||||
notification = false,
|
||||
activityLogLimit = None,
|
||||
limitVisibleRepositories = false,
|
||||
ssh = Ssh(
|
||||
enabled = false,
|
||||
sshHost = None,
|
||||
@@ -51,10 +59,17 @@ trait ServiceSpecBase extends MockitoSugar {
|
||||
oidcAuthentication = false,
|
||||
oidc = None,
|
||||
skinName = "skin-blue",
|
||||
userDefinedCss = None,
|
||||
showMailAddress = false,
|
||||
webHook = SystemSettingsService.WebHook(
|
||||
blockPrivateAddress = false,
|
||||
whitelist = Nil
|
||||
),
|
||||
upload = SystemSettingsService.Upload(
|
||||
maxFileSize = 3 * 1024 * 1024,
|
||||
timeout = 30 * 10000,
|
||||
largeMaxFileSize = 3 * 1024 * 1024,
|
||||
largeTimeout = 30 * 10000
|
||||
)
|
||||
)
|
||||
|
||||
@@ -107,7 +122,8 @@ trait ServiceSpecBase extends MockitoSugar {
|
||||
}
|
||||
|
||||
def generateNewIssue(userName: String, repositoryName: String, loginUser: String = "root")(
|
||||
implicit s: Session
|
||||
implicit
|
||||
s: Session
|
||||
): Int = {
|
||||
dummyService.insertIssue(
|
||||
owner = userName,
|
||||
@@ -123,7 +139,8 @@ trait ServiceSpecBase extends MockitoSugar {
|
||||
}
|
||||
|
||||
def generateNewPullRequest(base: String, request: String, loginUser: String)(
|
||||
implicit s: Session
|
||||
implicit
|
||||
s: Session
|
||||
): (Issue, PullRequest) = {
|
||||
implicit val context = Context(createSystemSettings(), None, this.request)
|
||||
val Array(baseUserName, baseRepositoryName, baesBranch) = base.split("/")
|
||||
|
||||
@@ -7,7 +7,7 @@ import javax.servlet.http.{HttpServletRequest, HttpSession}
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.model.Account
|
||||
import gitbucket.core.service.RequestCache
|
||||
import gitbucket.core.service.SystemSettingsService.{Ssh, SystemSettings, WebHook}
|
||||
import gitbucket.core.service.SystemSettingsService.{RepositoryOperation, Ssh, SystemSettings, Upload, WebHook}
|
||||
import org.mockito.Mockito._
|
||||
import org.scalatest.FunSpec
|
||||
import org.scalatestplus.mockito.MockitoSugar
|
||||
@@ -122,9 +122,17 @@ class AvatarImageProviderSpec extends FunSpec with MockitoSugar {
|
||||
allowAccountRegistration = false,
|
||||
allowAnonymousAccess = true,
|
||||
isCreateRepoOptionPublic = true,
|
||||
repositoryOperation = RepositoryOperation(
|
||||
create = true,
|
||||
delete = true,
|
||||
rename = true,
|
||||
transfer = true,
|
||||
fork = true
|
||||
),
|
||||
gravatar = useGravatar,
|
||||
notification = false,
|
||||
activityLogLimit = None,
|
||||
limitVisibleRepositories = false,
|
||||
ssh = Ssh(
|
||||
enabled = false,
|
||||
sshHost = None,
|
||||
@@ -137,10 +145,17 @@ class AvatarImageProviderSpec extends FunSpec with MockitoSugar {
|
||||
oidcAuthentication = false,
|
||||
oidc = None,
|
||||
skinName = "skin-blue",
|
||||
userDefinedCss = None,
|
||||
showMailAddress = false,
|
||||
webHook = WebHook(
|
||||
blockPrivateAddress = false,
|
||||
whitelist = Nil
|
||||
),
|
||||
upload = Upload(
|
||||
maxFileSize = 3 * 1024 * 1024,
|
||||
timeout = 30 * 10000,
|
||||
largeMaxFileSize = 3 * 1024 * 1024,
|
||||
largeTimeout = 30 * 10000
|
||||
)
|
||||
)
|
||||
|
||||
@@ -150,7 +165,8 @@ class AvatarImageProviderSpec extends FunSpec with MockitoSugar {
|
||||
class AvatarImageProviderImpl(account: Option[Account]) extends AvatarImageProvider with RequestCache {
|
||||
|
||||
def toHtml(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false)(
|
||||
implicit context: Context
|
||||
implicit
|
||||
context: Context
|
||||
): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip)
|
||||
|
||||
override def getAccountByMailAddress(mailAddress: String)(implicit context: Context): Option[Account] = account
|
||||
|
||||
Reference in New Issue
Block a user