mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-02 11:36:05 +01:00
Compare commits
172 Commits
4.31.1
...
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 | ||
|
|
a6e7761141 | ||
|
|
eb053a66d7 | ||
|
|
5257c83563 | ||
|
|
04bc92001f | ||
|
|
d939082e1f | ||
|
|
a943a5985d | ||
|
|
9e19821256 | ||
|
|
455183a13e | ||
|
|
c241c08904 | ||
|
|
08389cb1a0 | ||
|
|
94f9d42fc4 | ||
|
|
f35ecce3c7 | ||
|
|
2837bb40b0 | ||
|
|
54331f976d | ||
|
|
b975e74de3 | ||
|
|
c45ab34f43 | ||
|
|
4ee442f697 | ||
|
|
3c25d322f2 | ||
|
|
265c6b3e0f | ||
|
|
fad4503aec | ||
|
|
d8f70bfde3 | ||
|
|
d7dfb44efc | ||
|
|
409330a9fb | ||
|
|
0fd7e07831 | ||
|
|
91bd26d2a9 | ||
|
|
e5c4cf3298 | ||
|
|
c874d3fd84 | ||
|
|
c06f95256e | ||
|
|
2f1e05833e | ||
|
|
719cad00d6 | ||
|
|
b372c71fbf | ||
|
|
6b252a7018 | ||
|
|
6575258b6c | ||
|
|
d33280f9af | ||
|
|
863bb80ad1 | ||
|
|
e4266f31a6 | ||
|
|
0405fccb69 | ||
|
|
9a41adcec8 | ||
|
|
1d54920165 | ||
|
|
7ace37cd07 | ||
|
|
eb6398654d | ||
|
|
10a4c3e2a4 | ||
|
|
d494014011 | ||
|
|
91bf562b91 | ||
|
|
ec3961555f | ||
|
|
33b46869b6 | ||
|
|
88db21ef07 | ||
|
|
d53948f4a9 | ||
|
|
f97992a776 | ||
|
|
f9d99703cb | ||
|
|
b015cdde74 | ||
|
|
92b35bd458 | ||
|
|
3c8026f135 | ||
|
|
6a3f51a784 | ||
|
|
160c4a8a72 | ||
|
|
c4ff760bda | ||
|
|
aaed8f595a | ||
|
|
3c0a2e8385 | ||
|
|
0eef0f9aa5 | ||
|
|
169e2f16fb | ||
|
|
3800391a0e | ||
|
|
1f564808d5 | ||
|
|
433639dd04 | ||
|
|
f1e4116672 | ||
|
|
6cf00c5c66 | ||
|
|
71248cd9b7 | ||
|
|
841e6d110c | ||
|
|
f7defffeab | ||
|
|
13e6f5f6cf | ||
|
|
130cbf0b24 | ||
|
|
642d85b6bf | ||
|
|
8fe7f85e1a | ||
|
|
d1fb794783 | ||
|
|
e7aedb405a | ||
|
|
9dc148dace | ||
|
|
8ad0b25023 | ||
|
|
f648d60abb |
6
.github/ISSUE_TEMPLATE.md
vendored
6
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,8 +1,8 @@
|
|||||||
### Before submitting an issue to GitBucket I have first:
|
### Before submitting an issue to GitBucket I have first:
|
||||||
|
|
||||||
- [] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
- [ ] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
||||||
- [] searched for similar already existing issue
|
- [ ] searched for similar already existing issue
|
||||||
- [] read the documentation and [wiki](https://github.com/gitbucket/gitbucket/wiki)
|
- [ ] read the documentation and [wiki](https://github.com/gitbucket/gitbucket/wiki)
|
||||||
|
|
||||||
*(if you have performed all the above, remove the paragraph and continue describing the issue with template below)*
|
*(if you have performed all the above, remove the paragraph and continue describing the issue with template below)*
|
||||||
|
|
||||||
|
|||||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,8 +1,8 @@
|
|||||||
### Before submitting a pull-request to GitBucket I have first:
|
### Before submitting a pull-request to GitBucket I have first:
|
||||||
|
|
||||||
- [] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
- [ ] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/.github/CONTRIBUTING.md)
|
||||||
- [] rebased my branch over master
|
- [ ] rebased my branch over master
|
||||||
- [] verified that project is compiling
|
- [ ] verified that project is compiling
|
||||||
- [] verified that tests are passing
|
- [ ] verified that tests are passing
|
||||||
- [] squashed my commits as appropriate *(keep several commits if it is relevant to understand the PR)*
|
- [ ] squashed my commits as appropriate *(keep several commits if it is relevant to understand the PR)*
|
||||||
- [] [marked as closed using commit message](https://help.github.com/articles/closing-issues-via-commit-messages/) all issue ID that this PR should correct
|
- [ ] [marked as closed using commit message](https://help.github.com/articles/closing-issues-via-commit-messages/) all issue ID that this PR should correct
|
||||||
|
|||||||
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.*
|
||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -24,3 +24,8 @@ project/plugins/project/
|
|||||||
.idea/
|
.idea/
|
||||||
.idea_modules/
|
.idea_modules/
|
||||||
*.iml
|
*.iml
|
||||||
|
|
||||||
|
# Metals specific
|
||||||
|
.metals
|
||||||
|
.bloop
|
||||||
|
project/metals.sbt
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
version = "1.5.1"
|
||||||
project.git = true
|
project.git = true
|
||||||
|
|
||||||
maxColumn = 120
|
maxColumn = 120
|
||||||
|
|||||||
19
.travis.yml
19
.travis.yml
@@ -1,19 +0,0 @@
|
|||||||
language: scala
|
|
||||||
sudo: true
|
|
||||||
jdk:
|
|
||||||
- oraclejdk8
|
|
||||||
- oraclejdk11
|
|
||||||
- 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
|
|
||||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -1,6 +1,25 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All changes to the project will be documented in this file.
|
All changes to the project will be documented in this file.
|
||||||
|
|
||||||
|
### 4.33.0 - 31 Dec 2019
|
||||||
|
|
||||||
|
- All CLI options are configurable by environment variables
|
||||||
|
- Folding pull request files
|
||||||
|
- WebHook security options
|
||||||
|
- Add assignee and assignees properties to some Web APIs' response
|
||||||
|
|
||||||
|
### 4.32.0 - 7 Aug 2019
|
||||||
|
|
||||||
|
- Bump to Scala 2.13.0 and Scalatra 2.7.0
|
||||||
|
- Draft pull request
|
||||||
|
- Drop network installation of plugins
|
||||||
|
- Compare view works for commit id
|
||||||
|
- Apply default priority to pull requests
|
||||||
|
- Focus title after clicking issue / pull request edit button
|
||||||
|
|
||||||
|
### 4.31.2 - 7 Apr 2019
|
||||||
|
- Bug and security fix
|
||||||
|
|
||||||
### 4.31.1 - 17 Mar 2019
|
### 4.31.1 - 17 Mar 2019
|
||||||
- Bug fix
|
- Bug fix
|
||||||
|
|
||||||
|
|||||||
32
README.md
32
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.12) [](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:
|
GitBucket is a Git web platform powered by Scala offering:
|
||||||
@@ -22,7 +22,7 @@ The current version of GitBucket provides many features such as:
|
|||||||
- Account and group management with LDAP integration
|
- Account and group management with LDAP integration
|
||||||
- a Plug-in system
|
- a Plug-in system
|
||||||
|
|
||||||
If you want to try the development version of GitBucket, see the [Developer's Guide](https://github.com/gitbucket/gitbucket/blob/master/doc/how_to_run.md).
|
If you want to try the development version of GitBucket, see the [Developer's Guide](https://github.com/gitbucket/gitbucket/blob/master/doc/readme.md).
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
--------
|
--------
|
||||||
@@ -31,19 +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`.
|
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**.
|
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]`
|
|
||||||
|
|
||||||
`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 for upload files.
|
|
||||||
|
|
||||||
You can also deploy `gitbucket.war` to a servlet container which supports Servlet 3.0 (like Jetty, Tomcat, JBoss, etc)
|
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).
|
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).
|
||||||
@@ -68,16 +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.
|
- 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.
|
- 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.31.x
|
What's New in 4.33.x
|
||||||
-------------
|
-------------
|
||||||
### 4.31.1 - 17 Mar 2019
|
### 4.33.0 - 31 Dec 2019
|
||||||
- Bug fix
|
|
||||||
|
|
||||||
### 4.31.0 - 17 Mar 2019
|
- All CLI options are configurable by environment variables
|
||||||
- Docker support in CI plugin
|
- Folding pull request files
|
||||||
- Verify GPG key signed commit
|
- WebHook security options
|
||||||
- OAuth2 Token (sent as a parameter) authentication support and new APIs in Web API
|
- Add assignee and assignees properties to some Web APIs' response
|
||||||
- OGP (Open Graph protocol) support
|
|
||||||
- Username completion with avatars
|
|
||||||
|
|
||||||
See the [change log](CHANGELOG.md) for all of the updates.
|
See the [change log](CHANGELOG.md) for all of the updates.
|
||||||
|
|||||||
81
build.sbt
81
build.sbt
@@ -3,10 +3,10 @@ import com.typesafe.sbt.pgp.PgpKeys._
|
|||||||
|
|
||||||
val Organization = "io.github.gitbucket"
|
val Organization = "io.github.gitbucket"
|
||||||
val Name = "gitbucket"
|
val Name = "gitbucket"
|
||||||
val GitBucketVersion = "4.31.1"
|
val GitBucketVersion = "4.33.0"
|
||||||
val ScalatraVersion = "2.6.3"
|
val ScalatraVersion = "2.7.0-RC1"
|
||||||
val JettyVersion = "9.4.14.v20181114"
|
val JettyVersion = "9.4.30.v20200611"
|
||||||
val JgitVersion = "5.2.0.201812061821-r"
|
val JgitVersion = "5.8.0.202006091008-r"
|
||||||
|
|
||||||
lazy val root = (project in file("."))
|
lazy val root = (project in file("."))
|
||||||
.enablePlugins(SbtTwirl, ScalatraPlugin)
|
.enablePlugins(SbtTwirl, ScalatraPlugin)
|
||||||
@@ -17,7 +17,7 @@ sourcesInBase := false
|
|||||||
organization := Organization
|
organization := Organization
|
||||||
name := Name
|
name := Name
|
||||||
version := GitBucketVersion
|
version := GitBucketVersion
|
||||||
scalaVersion := "2.12.8"
|
scalaVersion := "2.13.1"
|
||||||
|
|
||||||
scalafmtOnCompile := true
|
scalafmtOnCompile := true
|
||||||
|
|
||||||
@@ -27,9 +27,7 @@ coverageExcludedPackages := ".*\\.html\\..*"
|
|||||||
resolvers ++= Seq(
|
resolvers ++= Seq(
|
||||||
Classpaths.typesafeReleases,
|
Classpaths.typesafeReleases,
|
||||||
Resolver.jcenterRepo,
|
Resolver.jcenterRepo,
|
||||||
"amateras" at "http://amateras.sourceforge.jp/mvn/",
|
"sonatype-snapshot" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||||
"sonatype-snapshot" at "https://oss.sonatype.org/content/repositories/snapshots/",
|
|
||||||
"amateras-snapshot" at "http://amateras.sourceforge.jp/mvn-snapshot/"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
@@ -38,46 +36,47 @@ libraryDependencies ++= Seq(
|
|||||||
"org.scalatra" %% "scalatra" % ScalatraVersion,
|
"org.scalatra" %% "scalatra" % ScalatraVersion,
|
||||||
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
|
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
|
||||||
"org.scalatra" %% "scalatra-forms" % ScalatraVersion,
|
"org.scalatra" %% "scalatra-forms" % ScalatraVersion,
|
||||||
"org.json4s" %% "json4s-jackson" % "3.5.2",
|
"org.json4s" %% "json4s-jackson" % "3.6.9",
|
||||||
"commons-io" % "commons-io" % "2.6",
|
"commons-io" % "commons-io" % "2.7",
|
||||||
"io.github.gitbucket" % "solidbase" % "1.0.3",
|
"io.github.gitbucket" % "solidbase" % "1.0.3",
|
||||||
"io.github.gitbucket" % "markedj" % "1.0.15",
|
"io.github.gitbucket" % "markedj" % "1.0.16",
|
||||||
"org.apache.commons" % "commons-compress" % "1.18",
|
"org.apache.commons" % "commons-compress" % "1.20",
|
||||||
"org.apache.commons" % "commons-email" % "1.5",
|
"org.apache.commons" % "commons-email" % "1.5",
|
||||||
"org.apache.httpcomponents" % "httpclient" % "4.5.6",
|
"commons-net" % "commons-net" % "3.6",
|
||||||
|
"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.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.19.1",
|
"org.apache.tika" % "tika-core" % "1.24.1",
|
||||||
"com.github.takezoe" %% "blocking-slick-32" % "0.0.11",
|
"com.github.takezoe" %% "blocking-slick-32" % "0.0.12",
|
||||||
"com.novell.ldap" % "jldap" % "2009-10-07",
|
"com.novell.ldap" % "jldap" % "2009-10-07",
|
||||||
"com.h2database" % "h2" % "1.4.197",
|
"com.h2database" % "h2" % "1.4.199",
|
||||||
"org.mariadb.jdbc" % "mariadb-java-client" % "2.3.0",
|
"org.mariadb.jdbc" % "mariadb-java-client" % "2.6.0",
|
||||||
"org.postgresql" % "postgresql" % "42.2.5",
|
"org.postgresql" % "postgresql" % "42.2.6",
|
||||||
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
"ch.qos.logback" % "logback-classic" % "1.2.3",
|
||||||
"com.zaxxer" % "HikariCP" % "3.2.0",
|
"com.zaxxer" % "HikariCP" % "3.4.5",
|
||||||
"com.typesafe" % "config" % "1.3.3",
|
"com.typesafe" % "config" % "1.4.0",
|
||||||
"com.typesafe.akka" %% "akka-actor" % "2.5.18",
|
"com.typesafe.akka" %% "akka-actor" % "2.5.27",
|
||||||
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0",
|
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.1.0",
|
||||||
"com.github.bkromhout" % "java-diff-utils" % "2.1.1",
|
"com.github.bkromhout" % "java-diff-utils" % "2.1.1",
|
||||||
"org.cache2k" % "cache2k-all" % "1.2.0.Final",
|
"org.cache2k" % "cache2k-all" % "1.2.4.Final",
|
||||||
"com.enragedginger" %% "akka-quartz-scheduler" % "1.7.0-akka-2.5.x" exclude ("c3p0", "c3p0") exclude ("com.zaxxer", "HikariCP-java6"),
|
"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.github.zafarkhaja" % "java-semver" % "0.9.0",
|
||||||
"com.nimbusds" % "oauth2-oidc-sdk" % "5.64.4",
|
"com.nimbusds" % "oauth2-oidc-sdk" % "5.64.4",
|
||||||
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
|
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
|
||||||
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
|
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
|
||||||
"junit" % "junit" % "4.12" % "test",
|
"junit" % "junit" % "4.13" % "test",
|
||||||
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
|
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
|
||||||
"org.mockito" % "mockito-core" % "2.23.4" % "test",
|
"org.mockito" % "mockito-core" % "3.3.3" % "test",
|
||||||
"com.dimafeng" %% "testcontainers-scala" % "0.22.0" % "test",
|
"com.dimafeng" %% "testcontainers-scala" % "0.37.0" % "test",
|
||||||
"org.testcontainers" % "mysql" % "1.10.3" % "test",
|
"org.testcontainers" % "mysql" % "1.14.3" % "test",
|
||||||
"org.testcontainers" % "postgresql" % "1.10.3" % "test",
|
"org.testcontainers" % "postgresql" % "1.14.3" % "test",
|
||||||
"net.i2p.crypto" % "eddsa" % "0.3.0",
|
"net.i2p.crypto" % "eddsa" % "0.3.0",
|
||||||
"is.tagomor.woothee" % "woothee-java" % "1.8.0",
|
"is.tagomor.woothee" % "woothee-java" % "1.11.0",
|
||||||
"org.ec4j.core" % "ec4j-core" % "0.0.3"
|
"org.ec4j.core" % "ec4j-core" % "0.0.3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Compiler settings
|
// Compiler settings
|
||||||
scalacOptions := Seq("-deprecation", "-language:postfixOps", "-opt:l:method", "-Xfuture")
|
scalacOptions := Seq("-deprecation", "-language:postfixOps", "-opt:l:method")
|
||||||
javacOptions in compile ++= Seq("-target", "8", "-source", "8")
|
javacOptions in compile ++= Seq("-target", "8", "-source", "8")
|
||||||
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"
|
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"
|
||||||
|
|
||||||
@@ -165,8 +164,8 @@ executableKey := {
|
|||||||
plugins.foreach { plugin =>
|
plugins.foreach { plugin =>
|
||||||
plugin.trim.split(":") match {
|
plugin.trim.split(":") match {
|
||||||
case Array(pluginId, pluginVersion) =>
|
case Array(pluginId, pluginVersion) =>
|
||||||
val url = "https://plugins.gitbucket-community.org/releases/" +
|
val url = "https://github.com/" +
|
||||||
s"gitbucket-${pluginId}-plugin/gitbucket-${pluginId}-plugin-gitbucket_${version.value}-${pluginVersion}.jar"
|
s"gitbucket/gitbucket-${pluginId}-plugin/releases/download/${pluginVersion}/gitbucket-${pluginId}-plugin-${pluginVersion}.jar"
|
||||||
log info s"Download: ${url}"
|
log info s"Download: ${url}"
|
||||||
IO transfer (new java.net.URL(url).openStream, pluginsDir / url.substring(url.lastIndexOf("/") + 1))
|
IO transfer (new java.net.URL(url).openStream, pluginsDir / url.substring(url.lastIndexOf("/") + 1))
|
||||||
case _ => ()
|
case _ => ()
|
||||||
@@ -258,3 +257,17 @@ licenseOverrides := {
|
|||||||
case DepModuleInfo("com.github.bkromhout", "java-diff-utils", _) =>
|
case DepModuleInfo("com.github.bkromhout", "java-diff-utils", _) =>
|
||||||
LicenseInfo(LicenseCategory.Apache, "Apache-2.0", "http://www.apache.org/licenses/LICENSE-2.0")
|
LicenseInfo(LicenseCategory.Apache, "Apache-2.0", "http://www.apache.org/licenses/LICENSE-2.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testOptions in Test ++= {
|
||||||
|
if (scala.util.Properties.isWin) {
|
||||||
|
Seq(
|
||||||
|
Tests.Exclude(
|
||||||
|
Set(
|
||||||
|
"gitbucket.core.GitBucketCoreModuleSpec"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ To build war file, run the following command:
|
|||||||
$ sbt package
|
$ sbt package
|
||||||
```
|
```
|
||||||
|
|
||||||
`gitbucket_2.12-x.x.x.war` is generated into `target/scala-2.12`.
|
`gitbucket_2.13-x.x.x.war` is generated into `target/scala-2.13`.
|
||||||
|
|
||||||
To build an executable war file, run
|
To build an executable war file, run
|
||||||
|
|
||||||
@@ -58,4 +58,4 @@ If you don't have docker, you can skip docker tests which require docker as foll
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ sbt "testOnly * -- -l ExternalDBTest"
|
$ sbt "testOnly * -- -l ExternalDBTest"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
sbt.version=1.2.6
|
sbt.version=1.3.12
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
|
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
|
||||||
|
|
||||||
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.0")
|
addSbtPlugin("com.geirsson" % "sbt-scalafmt" % "1.5.1")
|
||||||
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.3.15")
|
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.5.0")
|
||||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
|
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")
|
||||||
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.3")
|
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.4")
|
||||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.2")
|
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.2")
|
||||||
addSbtPlugin("com.typesafe.sbt" % "sbt-license-report" % "1.2.0")
|
addSbtPlugin("com.typesafe.sbt" % "sbt-license-report" % "1.2.0")
|
||||||
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2")
|
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.9.2")
|
||||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1")
|
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.6.1")
|
||||||
|
|
||||||
addSbtCoursier
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0")
|
|
||||||
@@ -4,6 +4,10 @@ import org.eclipse.jetty.server.Handler;
|
|||||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
import org.eclipse.jetty.server.handler.StatisticsHandler;
|
||||||
|
import org.eclipse.jetty.server.session.DefaultSessionCache;
|
||||||
|
import org.eclipse.jetty.server.session.FileSessionDataStore;
|
||||||
|
import org.eclipse.jetty.server.session.SessionCache;
|
||||||
|
import org.eclipse.jetty.server.session.SessionHandler;
|
||||||
import org.eclipse.jetty.webapp.WebAppContext;
|
import org.eclipse.jetty.webapp.WebAppContext;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -16,13 +20,22 @@ public class JettyLauncher {
|
|||||||
System.setProperty("java.awt.headless", "true");
|
System.setProperty("java.awt.headless", "true");
|
||||||
|
|
||||||
String host = null;
|
String host = null;
|
||||||
int port = 8080;
|
String port = null;
|
||||||
InetSocketAddress address = null;
|
InetSocketAddress address = null;
|
||||||
String contextPath = "/";
|
String contextPath = "/";
|
||||||
String tmpDirPath="";
|
String tmpDirPath="";
|
||||||
boolean forceHttps = false;
|
boolean forceHttps = false;
|
||||||
|
boolean saveSessions = false;
|
||||||
|
|
||||||
|
host = getEnvironmentVariable("gitbucket.host");
|
||||||
|
port = getEnvironmentVariable("gitbucket.port");
|
||||||
|
contextPath = getEnvironmentVariable("gitbucket.prefix");
|
||||||
|
tmpDirPath = getEnvironmentVariable("gitbucket.tempDir");
|
||||||
|
|
||||||
for(String arg: args) {
|
for(String arg: args) {
|
||||||
|
if(arg.equals("--save_sessions")) {
|
||||||
|
saveSessions = true;
|
||||||
|
}
|
||||||
if(arg.startsWith("--") && arg.contains("=")) {
|
if(arg.startsWith("--") && arg.contains("=")) {
|
||||||
String[] dim = arg.split("=");
|
String[] dim = arg.split("=");
|
||||||
if(dim.length >= 2) {
|
if(dim.length >= 2) {
|
||||||
@@ -31,16 +44,10 @@ public class JettyLauncher {
|
|||||||
host = dim[1];
|
host = dim[1];
|
||||||
break;
|
break;
|
||||||
case "--port":
|
case "--port":
|
||||||
port = Integer.parseInt(dim[1]);
|
port = dim[1];
|
||||||
break;
|
break;
|
||||||
case "--prefix":
|
case "--prefix":
|
||||||
contextPath = dim[1];
|
contextPath = dim[1];
|
||||||
if (!contextPath.startsWith("/")) {
|
|
||||||
contextPath = "/" + contextPath;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "--max_file_size":
|
|
||||||
System.setProperty("gitbucket.maxFileSize", dim[1]);
|
|
||||||
break;
|
break;
|
||||||
case "--gitbucket.home":
|
case "--gitbucket.home":
|
||||||
System.setProperty("gitbucket.home", dim[1]);
|
System.setProperty("gitbucket.home", dim[1]);
|
||||||
@@ -51,18 +58,19 @@ public class JettyLauncher {
|
|||||||
case "--plugin_dir":
|
case "--plugin_dir":
|
||||||
System.setProperty("gitbucket.pluginDir", dim[1]);
|
System.setProperty("gitbucket.pluginDir", dim[1]);
|
||||||
break;
|
break;
|
||||||
case "--validate_password":
|
|
||||||
System.setProperty("gitbucket.validate.password", dim[1]);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (contextPath != null && !contextPath.startsWith("/")) {
|
||||||
|
contextPath = "/" + contextPath;
|
||||||
|
}
|
||||||
|
|
||||||
if(host != null) {
|
if(host != null) {
|
||||||
address = new InetSocketAddress(host, port);
|
address = new InetSocketAddress(host, getPort(port));
|
||||||
} else {
|
} else {
|
||||||
address = new InetSocketAddress(port);
|
address = new InetSocketAddress(getPort(port));
|
||||||
}
|
}
|
||||||
|
|
||||||
Server server = new Server(address);
|
Server server = new Server(address);
|
||||||
@@ -87,8 +95,21 @@ public class JettyLauncher {
|
|||||||
|
|
||||||
WebAppContext context = new WebAppContext();
|
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;
|
File tmpDir;
|
||||||
if(tmpDirPath.equals("")){
|
if(tmpDirPath == null || tmpDirPath.equals("")){
|
||||||
tmpDir = new File(getGitBucketHome(), "tmp");
|
tmpDir = new File(getGitBucketHome(), "tmp");
|
||||||
if(!tmpDir.exists()){
|
if(!tmpDir.exists()){
|
||||||
tmpDir.mkdirs();
|
tmpDir.mkdirs();
|
||||||
@@ -111,7 +132,7 @@ public class JettyLauncher {
|
|||||||
ProtectionDomain domain = JettyLauncher.class.getProtectionDomain();
|
ProtectionDomain domain = JettyLauncher.class.getProtectionDomain();
|
||||||
URL location = domain.getCodeSource().getLocation();
|
URL location = domain.getCodeSource().getLocation();
|
||||||
|
|
||||||
context.setContextPath(contextPath);
|
context.setContextPath(contextPath == null ? "" : contextPath);
|
||||||
context.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml");
|
context.setDescriptor(location.toExternalForm() + "/WEB-INF/web.xml");
|
||||||
context.setServer(server);
|
context.setServer(server);
|
||||||
context.setWar(location.toExternalForm());
|
context.setWar(location.toExternalForm());
|
||||||
@@ -140,6 +161,23 @@ public class JettyLauncher {
|
|||||||
return new File(System.getProperty("user.home"), ".gitbucket");
|
return new File(System.getProperty("user.home"), ".gitbucket");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getEnvironmentVariable(String key){
|
||||||
|
String value = System.getenv(key.toUpperCase().replace('.', '_'));
|
||||||
|
if (value != null && value.length() == 0){
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getPort(String port){
|
||||||
|
if(port == null) {
|
||||||
|
return 8080;
|
||||||
|
} else {
|
||||||
|
return Integer.parseInt(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Handler addStatisticsHandler(Handler handler) {
|
private static Handler addStatisticsHandler(Handler handler) {
|
||||||
// The graceful shutdown is implemented via the statistics handler.
|
// The graceful shutdown is implemented via the statistics handler.
|
||||||
// See the following: https://bugs.eclipse.org/bugs/show_bug.cgi?id=420142
|
// See the following: https://bugs.eclipse.org/bugs/show_bug.cgi?id=420142
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
notifications:1.7.0
|
notifications:1.8.0
|
||||||
|
gist:4.18.0
|
||||||
|
emoji:4.6.0
|
||||||
|
pages:1.8.0
|
||||||
|
|||||||
@@ -60,13 +60,12 @@
|
|||||||
<!-- ACCESS_TOKEN -->
|
<!-- ACCESS_TOKEN -->
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
<createTable tableName="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="TOKEN_HASH" type="varchar(40)" nullable="false"/>
|
||||||
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="NOTE" type="text" nullable="false"/>
|
<column name="NOTE" type="text" nullable="false"/>
|
||||||
</createTable>
|
</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"/>
|
<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"/>
|
<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 -->
|
<!-- ACTIVITY -->
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
<createTable tableName="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="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="REPOSITORY_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"/>
|
<column name="ACTIVITY_USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
@@ -84,7 +83,6 @@
|
|||||||
<column name="ACTIVITY_DATE" type="datetime" nullable="false"/>
|
<column name="ACTIVITY_DATE" type="datetime" nullable="false"/>
|
||||||
</createTable>
|
</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_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"/>
|
<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="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="REPOSITORY_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="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="COMMENTED_USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="CONTENT" type="text" nullable="false"/>
|
<column name="CONTENT" type="text" nullable="false"/>
|
||||||
<column name="FILE_NAME" type="varchar(260)" nullable="true"/>
|
<column name="FILE_NAME" type="varchar(260)" nullable="true"/>
|
||||||
@@ -119,14 +117,13 @@
|
|||||||
<column name="ISSUE_ID" type="int" nullable="true"/>
|
<column name="ISSUE_ID" type="int" nullable="true"/>
|
||||||
</createTable>
|
</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"/>
|
<addForeignKeyConstraint constraintName="IDX_COMMIT_COMMENT_FK0" baseTableName="COMMIT_COMMENT" baseColumnNames="USER_NAME, REPOSITORY_NAME" referencedTableName="REPOSITORY" referencedColumnNames="USER_NAME, REPOSITORY_NAME"/>
|
||||||
|
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
<!-- COMMIT_STATUS -->
|
<!-- COMMIT_STATUS -->
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
<createTable tableName="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="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
<column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="COMMIT_ID" type="varchar(40)" nullable="false"/>
|
<column name="COMMIT_ID" type="varchar(40)" nullable="false"/>
|
||||||
@@ -139,7 +136,6 @@
|
|||||||
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
||||||
</createTable>
|
</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"/>
|
<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_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"/>
|
<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="USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="REPOSITORY_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="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="ACTION" type="varchar(20)" nullable="false"/>
|
||||||
<column name="COMMENTED_USER_NAME" type="varchar(100)" nullable="false"/>
|
<column name="COMMENTED_USER_NAME" type="varchar(100)" nullable="false"/>
|
||||||
<column name="CONTENT" type="text" nullable="false"/>
|
<column name="CONTENT" type="text" nullable="false"/>
|
||||||
@@ -226,7 +222,6 @@
|
|||||||
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
|
||||||
</createTable>
|
</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"/>
|
<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"/>
|
||||||
|
|
||||||
<!--================================================================================================-->
|
<!--================================================================================================-->
|
||||||
|
|||||||
6
src/main/resources/update/gitbucket-core_4.32.xml
Normal file
6
src/main/resources/update/gitbucket-core_4.32.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<changeSet>
|
||||||
|
<addColumn tableName="PULL_REQUEST">
|
||||||
|
<column name="IS_DRAFT" type="boolean" nullable="false" defaultValueBoolean="false" />
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
@@ -16,7 +16,7 @@ class ScalatraBootstrap extends LifeCycle with SystemSettingsService {
|
|||||||
context.getSessionCookieConfig.setSecure(true)
|
context.getSessionCookieConfig.setSecure(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register TransactionFilter and BasicAuthenticationFilter at first
|
// Register TransactionFilter at first
|
||||||
context.addFilter("transactionFilter", new TransactionFilter)
|
context.addFilter("transactionFilter", new TransactionFilter)
|
||||||
context
|
context
|
||||||
.getFilterRegistration("transactionFilter")
|
.getFilterRegistration("transactionFilter")
|
||||||
|
|||||||
@@ -62,5 +62,8 @@ object GitBucketCoreModule
|
|||||||
new Version("4.30.0"),
|
new Version("4.30.0"),
|
||||||
new Version("4.30.1"),
|
new Version("4.30.1"),
|
||||||
new Version("4.31.0", new LiquibaseMigration("update/gitbucket-core_4.31.xml")),
|
new Version("4.31.0", new LiquibaseMigration("update/gitbucket-core_4.31.xml")),
|
||||||
new Version("4.31.1")
|
new Version("4.31.1"),
|
||||||
|
new Version("4.31.2"),
|
||||||
|
new Version("4.32.0", new LiquibaseMigration("update/gitbucket-core_4.32.xml")),
|
||||||
|
new Version("4.33.0")
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ case class ApiIssue(
|
|||||||
number: Int,
|
number: Int,
|
||||||
title: String,
|
title: String,
|
||||||
user: ApiUser,
|
user: ApiUser,
|
||||||
|
assignee: Option[ApiUser],
|
||||||
labels: List[ApiLabel],
|
labels: List[ApiLabel],
|
||||||
state: String,
|
state: String,
|
||||||
created_at: Date,
|
created_at: Date,
|
||||||
@@ -19,6 +20,7 @@ case class ApiIssue(
|
|||||||
body: String
|
body: String
|
||||||
)(repositoryName: RepositoryName, isPullRequest: Boolean) {
|
)(repositoryName: RepositoryName, isPullRequest: Boolean) {
|
||||||
val id = 0 // dummy id
|
val id = 0 // dummy id
|
||||||
|
val assignees = List(assignee).flatten
|
||||||
val comments_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/issues/${number}/comments")
|
val comments_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/issues/${number}/comments")
|
||||||
val html_url = ApiPath(s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" } else { "issues" }}/${number}")
|
val html_url = ApiPath(s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" } else { "issues" }}/${number}")
|
||||||
val pull_request = if (isPullRequest) {
|
val pull_request = if (isPullRequest) {
|
||||||
@@ -36,11 +38,18 @@ case class ApiIssue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
object ApiIssue {
|
object ApiIssue {
|
||||||
def apply(issue: Issue, repositoryName: RepositoryName, user: ApiUser, labels: List[ApiLabel]): ApiIssue =
|
def apply(
|
||||||
|
issue: Issue,
|
||||||
|
repositoryName: RepositoryName,
|
||||||
|
user: ApiUser,
|
||||||
|
assignee: Option[ApiUser],
|
||||||
|
labels: List[ApiLabel]
|
||||||
|
): ApiIssue =
|
||||||
ApiIssue(
|
ApiIssue(
|
||||||
number = issue.issueId,
|
number = issue.issueId,
|
||||||
title = issue.title,
|
title = issue.title,
|
||||||
user = user,
|
user = user,
|
||||||
|
assignee = assignee,
|
||||||
labels = labels,
|
labels = labels,
|
||||||
state = if (issue.closed) { "closed" } else { "open" },
|
state = if (issue.closed) { "closed" } else { "open" },
|
||||||
body = issue.content.getOrElse(""),
|
body = issue.content.getOrElse(""),
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ case class ApiPullRequest(
|
|||||||
body: String,
|
body: String,
|
||||||
user: ApiUser,
|
user: ApiUser,
|
||||||
labels: List[ApiLabel],
|
labels: List[ApiLabel],
|
||||||
assignee: Option[ApiUser]
|
assignee: Option[ApiUser],
|
||||||
|
draft: Option[Boolean]
|
||||||
) {
|
) {
|
||||||
val id = 0 // dummy id
|
val id = 0 // dummy id
|
||||||
val html_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}")
|
val html_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}")
|
||||||
@@ -62,7 +63,8 @@ object ApiPullRequest {
|
|||||||
body = issue.content.getOrElse(""),
|
body = issue.content.getOrElse(""),
|
||||||
user = user,
|
user = user,
|
||||||
labels = labels,
|
labels = labels,
|
||||||
assignee = assignee
|
assignee = assignee,
|
||||||
|
draft = Some(pullRequest.isDraft)
|
||||||
)
|
)
|
||||||
|
|
||||||
case class Commit(sha: String, ref: String, repo: ApiRepository)(baseOwner: String) {
|
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 label = name
|
||||||
val file_id = fileName
|
val file_id = fileName
|
||||||
val browser_download_url = ApiPath(
|
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,
|
head: String,
|
||||||
base: String,
|
base: String,
|
||||||
body: Option[String],
|
body: Option[String],
|
||||||
maintainer_can_modify: Option[Boolean]
|
maintainer_can_modify: Option[Boolean],
|
||||||
|
draft: Option[Boolean]
|
||||||
)
|
)
|
||||||
|
|
||||||
case class CreateAPullRequestAlt(
|
case class CreateAPullRequestAlt(
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import java.io.File
|
|||||||
import gitbucket.core.account.html
|
import gitbucket.core.account.html
|
||||||
import gitbucket.core.helper
|
import gitbucket.core.helper
|
||||||
import gitbucket.core.model._
|
import gitbucket.core.model._
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
|
||||||
import gitbucket.core.service._
|
import gitbucket.core.service._
|
||||||
import gitbucket.core.service.WebHookService._
|
import gitbucket.core.service.WebHookService._
|
||||||
import gitbucket.core.ssh.SshUtil
|
import gitbucket.core.ssh.SshUtil
|
||||||
@@ -17,6 +16,7 @@ import gitbucket.core.util._
|
|||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
import org.scalatra.BadRequest
|
import org.scalatra.BadRequest
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
|
import org.scalatra.Forbidden
|
||||||
|
|
||||||
class AccountController
|
class AccountController
|
||||||
extends AccountControllerBase
|
extends AccountControllerBase
|
||||||
@@ -83,7 +83,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
val newForm = mapping(
|
val newForm = mapping(
|
||||||
"userName" -> trim(label("User name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
"userName" -> trim(label("User name", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||||
"password" -> trim(label("Password", text(required, maxlength(20), password))),
|
"password" -> trim(label("Password", text(required, maxlength(20)))),
|
||||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
||||||
"extraMailAddresses" -> list(
|
"extraMailAddresses" -> list(
|
||||||
@@ -95,7 +95,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
)(AccountNewForm.apply)
|
)(AccountNewForm.apply)
|
||||||
|
|
||||||
val editForm = mapping(
|
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)))),
|
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||||
"extraMailAddresses" -> list(
|
"extraMailAddresses" -> list(
|
||||||
@@ -256,12 +256,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
account,
|
account,
|
||||||
members,
|
members,
|
||||||
extraMailAddresses,
|
extraMailAddresses,
|
||||||
context.loginAccount.exists(
|
isGroupManager(context.loginAccount, members)
|
||||||
x =>
|
|
||||||
members.exists { member =>
|
|
||||||
member.userName == x.userName && member.isManager
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,12 +268,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
if (account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
if (account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||||
getVisibleRepositories(context.loginAccount, Some(userName)),
|
getVisibleRepositories(context.loginAccount, Some(userName)),
|
||||||
extraMailAddresses,
|
extraMailAddresses,
|
||||||
context.loginAccount.exists(
|
isGroupManager(context.loginAccount, members)
|
||||||
x =>
|
|
||||||
members.exists { member =>
|
|
||||||
member.userName == x.userName && member.isManager
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,7 +337,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
updateImage(userName, form.fileId, form.clearImage)
|
updateImage(userName, form.fileId, form.clearImage)
|
||||||
updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
|
updateAccountExtraMailAddresses(userName, form.extraMailAddresses.filter(_ != ""))
|
||||||
flash += "info" -> "Account information has been updated."
|
flash.update("info", "Account information has been updated.")
|
||||||
redirect(s"/${userName}/_edit")
|
redirect(s"/${userName}/_edit")
|
||||||
|
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
@@ -359,7 +349,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
getAccountByUserName(userName, true).map {
|
getAccountByUserName(userName, true).map {
|
||||||
account =>
|
account =>
|
||||||
if (isLastAdministrator(account)) {
|
if (isLastAdministrator(account)) {
|
||||||
flash += "error" -> "Account can't be removed because this is last one administrator."
|
flash.update("error", "Account can't be removed because this is last one administrator.")
|
||||||
redirect(s"/${userName}/_edit")
|
redirect(s"/${userName}/_edit")
|
||||||
} else {
|
} else {
|
||||||
// // Remove repositories
|
// // Remove repositories
|
||||||
@@ -439,7 +429,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
getAccountByUserName(userName).map { x =>
|
getAccountByUserName(userName).map { x =>
|
||||||
val (tokenId, token) = generateAccessToken(userName, form.note)
|
val (tokenId, token) = generateAccessToken(userName, form.note)
|
||||||
flash += "generatedToken" -> (tokenId, token)
|
flash.update("generatedToken", (tokenId, token))
|
||||||
}
|
}
|
||||||
redirect(s"/${userName}/_application")
|
redirect(s"/${userName}/_application")
|
||||||
})
|
})
|
||||||
@@ -475,7 +465,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
post("/:userName/_hooks/new", accountWebHookForm(false))(managersOnly { form =>
|
post("/:userName/_hooks/new", accountWebHookForm(false))(managersOnly { form =>
|
||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
addAccountWebHook(userName, form.url, form.events, form.ctype, form.token)
|
addAccountWebHook(userName, form.url, form.events, form.ctype, form.token)
|
||||||
flash += "info" -> s"Webhook ${form.url} created"
|
flash.update("info", s"Webhook ${form.url} created")
|
||||||
redirect(s"/${userName}/_hooks")
|
redirect(s"/${userName}/_hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -485,7 +475,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
get("/:userName/_hooks/delete")(managersOnly {
|
get("/:userName/_hooks/delete")(managersOnly {
|
||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
deleteAccountWebHook(userName, params("url"))
|
deleteAccountWebHook(userName, params("url"))
|
||||||
flash += "info" -> s"Webhook ${params("url")} deleted"
|
flash.update("info", s"Webhook ${params("url")} deleted")
|
||||||
redirect(s"/${userName}/_hooks")
|
redirect(s"/${userName}/_hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -508,7 +498,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
post("/:userName/_hooks/edit", accountWebHookForm(true))(managersOnly { form =>
|
post("/:userName/_hooks/edit", accountWebHookForm(true))(managersOnly { form =>
|
||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
updateAccountWebHook(userName, form.url, form.events, form.ctype, form.token)
|
updateAccountWebHook(userName, form.url, form.events, form.ctype, form.token)
|
||||||
flash += "info" -> s"webhook ${form.url} updated"
|
flash.update("info", s"webhook ${form.url} updated")
|
||||||
redirect(s"/${userName}/_hooks")
|
redirect(s"/${userName}/_hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -537,13 +527,14 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
WebHookPushPayload.createDummyPayload(ownerAccount)
|
WebHookPushPayload.createDummyPayload(ownerAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
val (webHook, json, reqFuture, resFuture) = callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload).head
|
val (webHook, json, reqFuture, resFuture) =
|
||||||
|
callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload, context.settings).head
|
||||||
|
|
||||||
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
||||||
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
||||||
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
||||||
case e: org.apache.http.client.ClientProtocolException => Map("error" -> ("invalid url"))
|
case e: org.apache.http.client.ClientProtocolException => Map("error" -> ("invalid url"))
|
||||||
case NonFatal(e) => Map("error" -> (e.getClass + " " + e.getMessage))
|
case NonFatal(e) => Map("error" -> (s"${e.getClass} ${e.getMessage}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType = formats("json")
|
contentType = formats("json")
|
||||||
@@ -683,7 +674,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
updateImage(form.groupName, form.fileId, form.clearImage)
|
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||||
|
|
||||||
flash += "info" -> "Account information has been updated."
|
flash.update("info", "Account information has been updated.")
|
||||||
redirect(s"/${groupName}/_editgroup")
|
redirect(s"/${groupName}/_editgroup")
|
||||||
|
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
@@ -701,27 +692,28 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
* Create new repository.
|
* Create new repository.
|
||||||
*/
|
*/
|
||||||
post("/new", newRepositoryForm)(usersOnly { form =>
|
post("/new", newRepositoryForm)(usersOnly { form =>
|
||||||
LockUtil.lock(s"${form.owner}/${form.name}") {
|
if (context.settings.repositoryOperation.create || context.loginAccount.get.isAdmin) {
|
||||||
if (getRepository(form.owner, form.name).isEmpty) {
|
LockUtil.lock(s"${form.owner}/${form.name}") {
|
||||||
createRepository(
|
if (getRepository(form.owner, form.name).isEmpty) {
|
||||||
context.loginAccount.get,
|
createRepository(
|
||||||
form.owner,
|
context.loginAccount.get,
|
||||||
form.name,
|
form.owner,
|
||||||
form.description,
|
form.name,
|
||||||
form.isPrivate,
|
form.description,
|
||||||
form.initOption,
|
form.isPrivate,
|
||||||
form.sourceUrl
|
form.initOption,
|
||||||
)
|
form.sourceUrl
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
// redirect to the repository
|
||||||
|
redirect(s"/${form.owner}/${form.name}")
|
||||||
// redirect to the repository
|
} else Forbidden()
|
||||||
redirect(s"/${form.owner}/${form.name}")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/fork")(readableUsersOnly { repository =>
|
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 loginUserName = loginAccount.userName
|
||||||
val groups = getGroupsByUserName(loginUserName)
|
val groups = getGroupsByUserName(loginUserName)
|
||||||
groups match {
|
groups match {
|
||||||
@@ -745,8 +737,8 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/fork", accountForm)(readableUsersOnly { (form, repository) =>
|
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 loginUserName = loginAccount.userName
|
||||||
val accountName = form.accountName
|
val accountName = form.accountName
|
||||||
|
|
||||||
@@ -760,7 +752,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
// redirect to the repository
|
// redirect to the repository
|
||||||
redirect(s"/${accountName}/${repository.name}")
|
redirect(s"/${accountName}/${repository.name}")
|
||||||
}
|
}
|
||||||
} else BadRequest()
|
} else Forbidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
private def existsAccount: Constraint = new Constraint() {
|
private def existsAccount: Constraint = new Constraint() {
|
||||||
@@ -823,4 +815,13 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def isGroupManager(account: Option[Account], members: Seq[GroupMember]): Boolean = {
|
||||||
|
account.exists { account =>
|
||||||
|
account.isAdmin || members.exists { member =>
|
||||||
|
member.userName == account.userName && member.isManager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import javax.servlet.{FilterChain, ServletRequest, ServletResponse}
|
|||||||
import is.tagomor.woothee.Classifier
|
import is.tagomor.woothee.Classifier
|
||||||
|
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
|
import scala.util.Using
|
||||||
import net.coobird.thumbnailator.Thumbnails
|
import net.coobird.thumbnailator.Thumbnails
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
@@ -240,7 +241,7 @@ abstract class ControllerBase
|
|||||||
case false => None
|
case false => None
|
||||||
}
|
}
|
||||||
|
|
||||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||||
treeWalk.addTree(revCommit.getTree)
|
treeWalk.addTree(revCommit.getTree)
|
||||||
treeWalk.setRecursive(true)
|
treeWalk.setRecursive(true)
|
||||||
_getPathObjectId(path, treeWalk)
|
_getPathObjectId(path, treeWalk)
|
||||||
@@ -268,7 +269,7 @@ abstract class ControllerBase
|
|||||||
response.setContentLength(attrs("size").toInt)
|
response.setContentLength(attrs("size").toInt)
|
||||||
val oid = attrs("oid").split(":")(1)
|
val oid = attrs("oid").split(":")(1)
|
||||||
|
|
||||||
using(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))) { in =>
|
Using.resource(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))) { in =>
|
||||||
IOUtils.copy(in, response.getOutputStream)
|
IOUtils.copy(in, response.getOutputStream)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -324,6 +325,8 @@ case class Context(
|
|||||||
trait AccountManagementControllerBase extends ControllerBase {
|
trait AccountManagementControllerBase extends ControllerBase {
|
||||||
self: AccountService =>
|
self: AccountService =>
|
||||||
|
|
||||||
|
private val logger = LoggerFactory.getLogger(getClass)
|
||||||
|
|
||||||
protected def updateImage(userName: String, fileId: Option[String], clearImage: Boolean): Unit =
|
protected def updateImage(userName: String, fileId: Option[String], clearImage: Boolean): Unit =
|
||||||
if (clearImage) {
|
if (clearImage) {
|
||||||
getAccountByUserName(userName).flatMap(_.image).foreach { image =>
|
getAccountByUserName(userName).flatMap(_.image).foreach { image =>
|
||||||
@@ -331,17 +334,21 @@ trait AccountManagementControllerBase extends ControllerBase {
|
|||||||
updateAvatarImage(userName, None)
|
updateAvatarImage(userName, None)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fileId.foreach { fileId =>
|
try {
|
||||||
val filename = "avatar." + FileUtil.getExtension(session.getAndRemove(Keys.Session.Upload(fileId)).get)
|
fileId.foreach { fileId =>
|
||||||
val uploadDir = getUserUploadDir(userName)
|
val filename = "avatar." + FileUtil.getExtension(session.getAndRemove(Keys.Session.Upload(fileId)).get)
|
||||||
if (!uploadDir.exists) {
|
val uploadDir = getUserUploadDir(userName)
|
||||||
uploadDir.mkdirs()
|
if (!uploadDir.exists) {
|
||||||
|
uploadDir.mkdirs()
|
||||||
|
}
|
||||||
|
Thumbnails
|
||||||
|
.of(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(fileId)))
|
||||||
|
.size(324, 324)
|
||||||
|
.toFile(new File(uploadDir, FileUtil.checkFilename(filename)))
|
||||||
|
updateAvatarImage(userName, Some(filename))
|
||||||
}
|
}
|
||||||
Thumbnails
|
} catch {
|
||||||
.of(new File(getTemporaryDir(session.getId), FileUtil.checkFilename(fileId)))
|
case e: Exception => logger.info("Error while updateImage" + e.getMessage)
|
||||||
.size(324, 324)
|
|
||||||
.toFile(new File(uploadDir, FileUtil.checkFilename(filename)))
|
|
||||||
updateAvatarImage(userName, Some(filename))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +366,7 @@ trait AccountManagementControllerBase extends ControllerBase {
|
|||||||
params: Map[String, Seq[String]],
|
params: Map[String, Seq[String]],
|
||||||
messages: Messages
|
messages: Messages
|
||||||
): Option[String] = {
|
): Option[String] = {
|
||||||
val extraMailAddresses = params.filterKeys(k => k.startsWith("extraMailAddresses"))
|
val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses"))
|
||||||
if (extraMailAddresses.exists {
|
if (extraMailAddresses.exists {
|
||||||
case (k, v) =>
|
case (k, v) =>
|
||||||
v.contains(value)
|
v.contains(value)
|
||||||
@@ -382,7 +389,7 @@ trait AccountManagementControllerBase extends ControllerBase {
|
|||||||
params: Map[String, Seq[String]],
|
params: Map[String, Seq[String]],
|
||||||
messages: Messages
|
messages: Messages
|
||||||
): Option[String] = {
|
): Option[String] = {
|
||||||
val extraMailAddresses = params.filterKeys(k => k.startsWith("extraMailAddresses"))
|
val extraMailAddresses = params.view.filterKeys(k => k.startsWith("extraMailAddresses"))
|
||||||
if (Some(value) == params.optionValue("mailAddress") || extraMailAddresses.count {
|
if (Some(value) == params.optionValue("mailAddress") || extraMailAddresses.count {
|
||||||
case (k, v) =>
|
case (k, v) =>
|
||||||
v.contains(value)
|
v.contains(value)
|
||||||
|
|||||||
@@ -27,7 +27,12 @@ trait DashboardControllerBase extends ControllerBase {
|
|||||||
self: IssuesService with PullRequestService with RepositoryService with AccountService with UsersAuthenticator =>
|
self: IssuesService with PullRequestService with RepositoryService with AccountService with UsersAuthenticator =>
|
||||||
|
|
||||||
get("/dashboard/repos")(usersOnly {
|
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)
|
html.repos(getGroupNames(context.loginAccount.get.userName), repos, repos)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -93,7 +98,12 @@ trait DashboardControllerBase extends ControllerBase {
|
|||||||
},
|
},
|
||||||
filter,
|
filter,
|
||||||
getGroupNames(userName),
|
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,
|
filter,
|
||||||
getGroupNames(userName),
|
getGroupNames(userName),
|
||||||
getVisibleRepositories(context.loginAccount, withoutPhysicalInfo = true)
|
getVisibleRepositories(
|
||||||
|
context.loginAccount,
|
||||||
|
None,
|
||||||
|
withoutPhysicalInfo = true,
|
||||||
|
limit = context.settings.limitVisibleRepositories
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ import org.scalatra._
|
|||||||
import org.scalatra.servlet.{FileItem, FileUploadSupport, MultipartConfig}
|
import org.scalatra.servlet.{FileItem, FileUploadSupport, MultipartConfig}
|
||||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||||
|
|
||||||
|
import scala.util.Using
|
||||||
|
import gitbucket.core.service.SystemSettingsService
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides Ajax based file upload functionality.
|
* Provides Ajax based file upload functionality.
|
||||||
*
|
*
|
||||||
@@ -26,11 +29,11 @@ class FileUploadController
|
|||||||
with FileUploadSupport
|
with FileUploadSupport
|
||||||
with RepositoryService
|
with RepositoryService
|
||||||
with AccountService
|
with AccountService
|
||||||
with ReleaseService {
|
with ReleaseService
|
||||||
|
with SystemSettingsService {
|
||||||
configureMultipartHandling(MultipartConfig(maxFileSize = Some(FileUtil.MaxFileSize)))
|
|
||||||
|
|
||||||
post("/image") {
|
post("/image") {
|
||||||
|
setMultipartConfig()
|
||||||
execute(
|
execute(
|
||||||
{ (file, fileId) =>
|
{ (file, fileId) =>
|
||||||
FileUtils
|
FileUtils
|
||||||
@@ -42,6 +45,7 @@ class FileUploadController
|
|||||||
}
|
}
|
||||||
|
|
||||||
post("/tmp") {
|
post("/tmp") {
|
||||||
|
setMultipartConfig()
|
||||||
execute(
|
execute(
|
||||||
{ (file, fileId) =>
|
{ (file, fileId) =>
|
||||||
FileUtils
|
FileUtils
|
||||||
@@ -53,6 +57,7 @@ class FileUploadController
|
|||||||
}
|
}
|
||||||
|
|
||||||
post("/file/:owner/:repository") {
|
post("/file/:owner/:repository") {
|
||||||
|
setMultipartConfig()
|
||||||
execute(
|
execute(
|
||||||
{ (file, fileId) =>
|
{ (file, fileId) =>
|
||||||
FileUtils.writeByteArrayToFile(
|
FileUtils.writeByteArrayToFile(
|
||||||
@@ -68,6 +73,7 @@ class FileUploadController
|
|||||||
}
|
}
|
||||||
|
|
||||||
post("/wiki/:owner/:repository") {
|
post("/wiki/:owner/:repository") {
|
||||||
|
setMultipartConfig()
|
||||||
// Don't accept not logged-in users
|
// Don't accept not logged-in users
|
||||||
session.get(Keys.Session.LoginAccount).collect {
|
session.get(Keys.Session.LoginAccount).collect {
|
||||||
case loginAccount: Account =>
|
case loginAccount: Account =>
|
||||||
@@ -80,7 +86,7 @@ class FileUploadController
|
|||||||
{ (file, fileId) =>
|
{ (file, fileId) =>
|
||||||
val fileName = file.getName
|
val fileName = file.getName
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) {
|
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) {
|
||||||
git =>
|
git =>
|
||||||
val builder = DirCache.newInCore.builder()
|
val builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
@@ -126,6 +132,7 @@ class FileUploadController
|
|||||||
}
|
}
|
||||||
|
|
||||||
post("/release/:owner/:repository/:tag") {
|
post("/release/:owner/:repository/:tag") {
|
||||||
|
setMultipartConfigForLargeFile()
|
||||||
session
|
session
|
||||||
.get(Keys.Session.LoginAccount)
|
.get(Keys.Session.LoginAccount)
|
||||||
.collect {
|
.collect {
|
||||||
@@ -148,6 +155,7 @@ class FileUploadController
|
|||||||
|
|
||||||
post("/import") {
|
post("/import") {
|
||||||
import JDBCUtil._
|
import JDBCUtil._
|
||||||
|
setMultipartConfig()
|
||||||
session.get(Keys.Session.LoginAccount).collect {
|
session.get(Keys.Session.LoginAccount).collect {
|
||||||
case loginAccount: Account if loginAccount.isAdmin =>
|
case loginAccount: Account if loginAccount.isAdmin =>
|
||||||
execute({ (file, fileId) =>
|
execute({ (file, fileId) =>
|
||||||
@@ -157,6 +165,18 @@ class FileUploadController
|
|||||||
redirect("/admin/data")
|
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 = {
|
private def onlyWikiEditable(owner: String, repository: String, loginAccount: Account)(action: => Any): Any = {
|
||||||
implicit val session = Database.getSession(request)
|
implicit val session = Database.getSession(request)
|
||||||
getRepository(owner, repository) match {
|
getRepository(owner, repository) match {
|
||||||
|
|||||||
@@ -65,7 +65,12 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
val visibleOwnerSet: Set[String] = Set(account.userName) ++ getGroupsByUserName(account.userName)
|
val visibleOwnerSet: Set[String] = Set(account.userName) ++ getGroupsByUserName(account.userName)
|
||||||
gitbucket.core.html.index(
|
gitbucket.core.html.index(
|
||||||
getRecentActivitiesByOwners(visibleOwnerSet),
|
getRecentActivitiesByOwners(visibleOwnerSet),
|
||||||
getVisibleRepositories(Some(account), withoutPhysicalInfo = true),
|
getVisibleRepositories(
|
||||||
|
Some(account),
|
||||||
|
None,
|
||||||
|
withoutPhysicalInfo = true,
|
||||||
|
limit = context.settings.limitVisibleRepositories
|
||||||
|
),
|
||||||
showBannerToCreatePersonalAccessToken = hasAccountFederation(account.userName) && !hasAccessToken(
|
showBannerToCreatePersonalAccessToken = hasAccountFederation(account.userName) && !hasAccessToken(
|
||||||
account.userName
|
account.userName
|
||||||
)
|
)
|
||||||
@@ -83,7 +88,7 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
get("/signin") {
|
get("/signin") {
|
||||||
val redirect = params.get("redirect")
|
val redirect = params.get("redirect")
|
||||||
if (redirect.isDefined && redirect.get.startsWith("/")) {
|
if (redirect.isDefined && redirect.get.startsWith("/")) {
|
||||||
flash += Keys.Flash.Redirect -> redirect.get
|
flash.update(Keys.Flash.Redirect, redirect.get)
|
||||||
}
|
}
|
||||||
gitbucket.core.html.signin(flash.get("userName"), flash.get("password"), flash.get("error"))
|
gitbucket.core.html.signin(flash.get("userName"), flash.get("password"), flash.get("error"))
|
||||||
}
|
}
|
||||||
@@ -96,9 +101,9 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
case _ => signin(account)
|
case _ => signin(account)
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
flash += "userName" -> form.userName
|
flash.update("userName", form.userName)
|
||||||
flash += "password" -> form.password
|
flash.update("password", form.password)
|
||||||
flash += "error" -> "Sorry, your Username and/or Password is incorrect. Please try again."
|
flash.update("error", "Sorry, your Username and/or Password is incorrect. Please try again.")
|
||||||
redirect("/signin")
|
redirect("/signin")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,15 +137,15 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
val redirectURI = new URI(s"$baseUrl/signin/oidc")
|
val redirectURI = new URI(s"$baseUrl/signin/oidc")
|
||||||
session.get(Keys.Session.OidcContext) match {
|
session.get(Keys.Session.OidcContext) match {
|
||||||
case Some(context: OidcContext) =>
|
case Some(context: OidcContext) =>
|
||||||
authenticate(params, redirectURI, context.state, context.nonce, oidc) map { account =>
|
authenticate(params.toMap, redirectURI, context.state, context.nonce, oidc).map { account =>
|
||||||
signin(account, context.redirectBackURI)
|
signin(account, context.redirectBackURI)
|
||||||
} orElse {
|
} orElse {
|
||||||
flash += "error" -> "Sorry, authentication failed. Please try again."
|
flash.update("error", "Sorry, authentication failed. Please try again.")
|
||||||
session.invalidate()
|
session.invalidate()
|
||||||
redirect("/signin")
|
redirect("/signin")
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
flash += "error" -> "Sorry, something wrong. Please try again."
|
flash.update("error", "Sorry, something wrong. Please try again.")
|
||||||
session.invalidate()
|
session.invalidate()
|
||||||
redirect("/signin")
|
redirect("/signin")
|
||||||
}
|
}
|
||||||
@@ -227,7 +232,7 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
} getOrElse ""
|
} getOrElse ""
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO Move to RepositoryViwerController?
|
// TODO Move to RepositoryViewrController?
|
||||||
get("/:owner/:repository/search")(referrersOnly { repository =>
|
get("/:owner/:repository/search")(referrersOnly { repository =>
|
||||||
defining(params.getOrElse("q", "").trim, params.getOrElse("type", "code")) {
|
defining(params.getOrElse("q", "").trim, params.getOrElse("type", "code")) {
|
||||||
case (query, target) =>
|
case (query, target) =>
|
||||||
@@ -279,11 +284,28 @@ trait IndexControllerBase extends ControllerBase {
|
|||||||
get("/search") {
|
get("/search") {
|
||||||
val query = params.getOrElse("query", "").trim.toLowerCase
|
val query = params.getOrElse("query", "").trim.toLowerCase
|
||||||
val visibleRepositories =
|
val visibleRepositories =
|
||||||
getVisibleRepositories(context.loginAccount, repositoryUserName = None, withoutPhysicalInfo = true)
|
getVisibleRepositories(
|
||||||
val repositories = visibleRepositories.filter { repository =>
|
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
|
repository.name.toLowerCase.indexOf(query) >= 0 || repository.owner.toLowerCase.indexOf(query) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
gitbucket.core.search.html.repositories(query, repositories, visibleRepositories)
|
gitbucket.core.search.html.repositories(query, repositories, visibleRepositories)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ trait IssuesControllerBase extends ControllerBase {
|
|||||||
form.assignedUserName,
|
form.assignedUserName,
|
||||||
form.milestoneId,
|
form.milestoneId,
|
||||||
form.priorityId,
|
form.priorityId,
|
||||||
form.labelNames.toArray.flatMap(_.split(",")),
|
form.labelNames.toSeq.flatMap(_.split(",")),
|
||||||
context.loginAccount.get
|
context.loginAccount.get
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package gitbucket.core.controller
|
package gitbucket.core.controller
|
||||||
|
|
||||||
import gitbucket.core.issues.milestones.html
|
import gitbucket.core.issues.milestones.html
|
||||||
import gitbucket.core.service.{RepositoryService, MilestonesService, AccountService}
|
import gitbucket.core.service.{AccountService, MilestonesService, RepositoryService}
|
||||||
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
|
import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
|
||||||
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
|
import org.scalatra.i18n.Messages
|
||||||
|
|
||||||
class MilestonesController
|
class MilestonesController
|
||||||
extends MilestonesControllerBase
|
extends MilestonesControllerBase
|
||||||
@@ -20,7 +22,7 @@ trait MilestonesControllerBase extends ControllerBase {
|
|||||||
case class MilestoneForm(title: String, description: Option[String], dueDate: Option[java.util.Date])
|
case class MilestoneForm(title: String, description: Option[String], dueDate: Option[java.util.Date])
|
||||||
|
|
||||||
val milestoneForm = mapping(
|
val milestoneForm = mapping(
|
||||||
"title" -> trim(label("Title", text(required, maxlength(100)))),
|
"title" -> trim(label("Title", text(required, maxlength(100), uniqueMilestone))),
|
||||||
"description" -> trim(label("Description", optional(text()))),
|
"description" -> trim(label("Description", optional(text()))),
|
||||||
"dueDate" -> trim(label("Due Date", optional(date())))
|
"dueDate" -> trim(label("Due Date", optional(date())))
|
||||||
)(MilestoneForm.apply)
|
)(MilestoneForm.apply)
|
||||||
@@ -86,4 +88,29 @@ trait MilestonesControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
private def uniqueMilestone: Constraint = new Constraint() {
|
||||||
|
override def validate(
|
||||||
|
name: String,
|
||||||
|
value: String,
|
||||||
|
params: Map[String, Seq[String]],
|
||||||
|
messages: Messages
|
||||||
|
): Option[String] = {
|
||||||
|
for {
|
||||||
|
owner <- params.optionValue("owner")
|
||||||
|
repository <- params.optionValue("repository")
|
||||||
|
_ <- params.optionValue("milestoneId") match {
|
||||||
|
// existing milestone
|
||||||
|
case Some(id) =>
|
||||||
|
getMilestones(owner, repository)
|
||||||
|
.find(m => m.title.equalsIgnoreCase(value) && m.milestoneId.toString != id)
|
||||||
|
// new milestone
|
||||||
|
case None =>
|
||||||
|
getMilestones(owner, repository)
|
||||||
|
.find(m => m.title.equalsIgnoreCase(value))
|
||||||
|
}
|
||||||
|
} yield {
|
||||||
|
"Milestone already exists."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package gitbucket.core.controller
|
package gitbucket.core.controller
|
||||||
|
|
||||||
import gitbucket.core.model.{CommitComment, CommitComments, IssueComment, WebHook}
|
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
|
||||||
import gitbucket.core.pulls.html
|
import gitbucket.core.pulls.html
|
||||||
import gitbucket.core.service.CommitStatusService
|
import gitbucket.core.service.CommitStatusService
|
||||||
import gitbucket.core.service.MergeService
|
import gitbucket.core.service.MergeService
|
||||||
@@ -15,11 +13,9 @@ import gitbucket.core.util.Implicits._
|
|||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.{ObjectId, PersonIdent}
|
|
||||||
import org.eclipse.jgit.revwalk.RevWalk
|
|
||||||
import org.scalatra.BadRequest
|
import org.scalatra.BadRequest
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.util.Using
|
||||||
|
|
||||||
class PullRequestsController
|
class PullRequestsController
|
||||||
extends PullRequestsControllerBase
|
extends PullRequestsControllerBase
|
||||||
@@ -69,6 +65,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
"requestBranch" -> trim(text(required, maxlength(100))),
|
"requestBranch" -> trim(text(required, maxlength(100))),
|
||||||
"commitIdFrom" -> trim(text(required, maxlength(40))),
|
"commitIdFrom" -> trim(text(required, maxlength(40))),
|
||||||
"commitIdTo" -> trim(text(required, maxlength(40))),
|
"commitIdTo" -> trim(text(required, maxlength(40))),
|
||||||
|
"isDraft" -> trim(boolean(required)),
|
||||||
"assignedUserName" -> trim(optional(text())),
|
"assignedUserName" -> trim(optional(text())),
|
||||||
"milestoneId" -> trim(optional(number())),
|
"milestoneId" -> trim(optional(number())),
|
||||||
"priorityId" -> trim(optional(number())),
|
"priorityId" -> trim(optional(number())),
|
||||||
@@ -77,7 +74,8 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
val mergeForm = mapping(
|
val mergeForm = mapping(
|
||||||
"message" -> trim(label("Message", text(required))),
|
"message" -> trim(label("Message", text(required))),
|
||||||
"strategy" -> trim(label("Strategy", text(required)))
|
"strategy" -> trim(label("Strategy", text(required))),
|
||||||
|
"isDraft" -> trim(boolean(required))
|
||||||
)(MergeForm.apply)
|
)(MergeForm.apply)
|
||||||
|
|
||||||
case class PullRequestForm(
|
case class PullRequestForm(
|
||||||
@@ -90,13 +88,14 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
commitIdFrom: String,
|
commitIdFrom: String,
|
||||||
commitIdTo: String,
|
commitIdTo: String,
|
||||||
|
isDraft: Boolean,
|
||||||
assignedUserName: Option[String],
|
assignedUserName: Option[String],
|
||||||
milestoneId: Option[Int],
|
milestoneId: Option[Int],
|
||||||
priorityId: Option[Int],
|
priorityId: Option[Int],
|
||||||
labelNames: Option[String]
|
labelNames: Option[String]
|
||||||
)
|
)
|
||||||
|
|
||||||
case class MergeForm(message: String, strategy: String)
|
case class MergeForm(message: String, strategy: String, isDraft: Boolean)
|
||||||
|
|
||||||
get("/:owner/:repository/pulls")(referrersOnly { repository =>
|
get("/:owner/:repository/pulls")(referrersOnly { repository =>
|
||||||
val q = request.getParameter("q")
|
val q = request.getParameter("q")
|
||||||
@@ -133,7 +132,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount),
|
hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount),
|
||||||
repository,
|
repository,
|
||||||
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName),
|
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName),
|
||||||
flash.toMap.map(f => f._1 -> f._2.toString)
|
flash.iterator.map(f => f._1 -> f._2.toString).toMap
|
||||||
)
|
)
|
||||||
|
|
||||||
// html.pullreq(
|
// html.pullreq(
|
||||||
@@ -266,11 +265,11 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
val repository = getRepository(owner, name).get
|
val repository = getRepository(owner, name).get
|
||||||
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
||||||
if (branchProtection.enabled) {
|
if (branchProtection.enabled) {
|
||||||
flash += "error" -> s"branch ${pullreq.requestBranch} is protected."
|
flash.update("error", s"branch ${pullreq.requestBranch} is protected.")
|
||||||
} else {
|
} else {
|
||||||
if (repository.repository.defaultBranch != pullreq.requestBranch) {
|
if (repository.repository.defaultBranch != pullreq.requestBranch) {
|
||||||
val userName = context.loginAccount.get.userName
|
val userName = context.loginAccount.get.userName
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
git.branchDelete().setForce(true).setBranchNames(pullreq.requestBranch).call()
|
git.branchDelete().setForce(true).setBranchNames(pullreq.requestBranch).call()
|
||||||
recordDeleteBranchActivity(repository.owner, repository.name, userName, pullreq.requestBranch)
|
recordDeleteBranchActivity(repository.owner, repository.name, userName, pullreq.requestBranch)
|
||||||
}
|
}
|
||||||
@@ -283,9 +282,10 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
"delete_branch"
|
"delete_branch"
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
flash += "error" -> s"""Can't delete the default branch "${pullreq.requestBranch}"."""
|
flash.update("error", s"""Can't delete the default branch "${pullreq.requestBranch}".""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect(s"/${baseRepository.owner}/${baseRepository.name}/pull/${issueId}")
|
redirect(s"/${baseRepository.owner}/${baseRepository.name}/pull/${issueId}")
|
||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
})
|
})
|
||||||
@@ -303,7 +303,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
} yield {
|
} yield {
|
||||||
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
|
||||||
if (branchProtection.needStatusCheck(loginAccount.userName)) {
|
if (branchProtection.needStatusCheck(loginAccount.userName)) {
|
||||||
flash += "error" -> s"branch ${pullreq.requestBranch} is protected need status check."
|
flash.update("error", s"branch ${pullreq.requestBranch} is protected need status check.")
|
||||||
} else {
|
} else {
|
||||||
LockUtil.lock(s"${owner}/${name}") {
|
LockUtil.lock(s"${owner}/${name}") {
|
||||||
val alias =
|
val alias =
|
||||||
@@ -312,9 +312,11 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
} else {
|
} else {
|
||||||
s"${pullreq.userName}:${pullreq.branch}"
|
s"${pullreq.userName}:${pullreq.branch}"
|
||||||
}
|
}
|
||||||
val existIds = using(Git.open(Directory.getRepositoryDir(owner, name))) { git =>
|
val existIds = Using
|
||||||
JGitUtil.getAllCommitIds(git)
|
.resource(Git.open(Directory.getRepositoryDir(owner, name))) { git =>
|
||||||
}.toSet
|
JGitUtil.getAllCommitIds(git)
|
||||||
|
}
|
||||||
|
.toSet
|
||||||
pullRemote(
|
pullRemote(
|
||||||
repository,
|
repository,
|
||||||
pullreq.requestBranch,
|
pullreq.requestBranch,
|
||||||
@@ -322,14 +324,15 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
pullreq.branch,
|
pullreq.branch,
|
||||||
loginAccount,
|
loginAccount,
|
||||||
s"Merge branch '${alias}' into ${pullreq.requestBranch}",
|
s"Merge branch '${alias}' into ${pullreq.requestBranch}",
|
||||||
Some(pullreq)
|
Some(pullreq),
|
||||||
|
context.settings
|
||||||
) match {
|
) match {
|
||||||
case None => // conflict
|
case None => // conflict
|
||||||
flash += "error" -> s"Can't automatic merging branch '${alias}' into ${pullreq.requestBranch}."
|
flash.update("error", s"Can't automatic merging branch '${alias}' into ${pullreq.requestBranch}.")
|
||||||
case Some(oldId) =>
|
case Some(oldId) =>
|
||||||
// update pull request
|
// update pull request
|
||||||
updatePullRequests(owner, name, pullreq.requestBranch, loginAccount, "synchronize")
|
updatePullRequests(owner, name, pullreq.requestBranch, loginAccount, "synchronize", context.settings)
|
||||||
flash += "info" -> s"Merge branch '${alias}' into ${pullreq.requestBranch}"
|
flash.update("info", s"Merge branch '${alias}' into ${pullreq.requestBranch}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,14 +341,34 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}) getOrElse NotFound()
|
}) getOrElse NotFound()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
post("/:owner/:repository/pull/:id/update_draft")(readableUsersOnly { baseRepository =>
|
||||||
|
(for {
|
||||||
|
issueId <- params("id").toIntOpt
|
||||||
|
(_, pullreq) <- getPullRequest(baseRepository.owner, baseRepository.name, issueId)
|
||||||
|
owner = pullreq.requestUserName
|
||||||
|
name = pullreq.requestRepositoryName
|
||||||
|
if hasDeveloperRole(owner, name, context.loginAccount)
|
||||||
|
} yield {
|
||||||
|
updateDraftToPullRequest(baseRepository.owner, baseRepository.name, issueId)
|
||||||
|
}) getOrElse NotFound()
|
||||||
|
})
|
||||||
|
|
||||||
post("/:owner/:repository/pull/:id/merge", mergeForm)(writableUsersOnly { (form, repository) =>
|
post("/:owner/:repository/pull/:id/merge", mergeForm)(writableUsersOnly { (form, repository) =>
|
||||||
params("id").toIntOpt.flatMap { issueId =>
|
params("id").toIntOpt.flatMap { issueId =>
|
||||||
val owner = repository.owner
|
val owner = repository.owner
|
||||||
val name = repository.name
|
val name = repository.name
|
||||||
|
|
||||||
mergePullRequest(repository, issueId, context.loginAccount.get, form.message, form.strategy) match {
|
mergePullRequest(
|
||||||
|
repository,
|
||||||
|
issueId,
|
||||||
|
context.loginAccount.get,
|
||||||
|
form.message,
|
||||||
|
form.strategy,
|
||||||
|
form.isDraft,
|
||||||
|
context.settings
|
||||||
|
) match {
|
||||||
case Right(objectId) => redirect(s"/${owner}/${name}/pull/${issueId}")
|
case Right(objectId) => redirect(s"/${owner}/${name}/pull/${issueId}")
|
||||||
case Left(message) => Some(BadRequest())
|
case Left(message) => Some(BadRequest(message))
|
||||||
}
|
}
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
})
|
})
|
||||||
@@ -356,7 +379,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
case (Some(originUserName), Some(originRepositoryName)) => {
|
case (Some(originUserName), Some(originRepositoryName)) => {
|
||||||
getRepository(originUserName, originRepositoryName).map {
|
getRepository(originUserName, originRepositoryName).map {
|
||||||
originRepository =>
|
originRepository =>
|
||||||
using(
|
Using.resources(
|
||||||
Git.open(getRepositoryDir(originUserName, originRepositoryName)),
|
Git.open(getRepositoryDir(originUserName, originRepositoryName)),
|
||||||
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
||||||
) { (oldGit, newGit) =>
|
) { (oldGit, newGit) =>
|
||||||
@@ -372,7 +395,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
}
|
}
|
||||||
case _ => {
|
case _ => {
|
||||||
using(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))) { git =>
|
||||||
JGitUtil.getDefaultBranch(git, forkedRepository).map {
|
JGitUtil.getDefaultBranch(git, forkedRepository).map {
|
||||||
case (_, defaultBranch) =>
|
case (_, defaultBranch) =>
|
||||||
redirect(
|
redirect(
|
||||||
@@ -464,6 +487,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
getAssignableUserNames(originRepository.owner, originRepository.name),
|
getAssignableUserNames(originRepository.owner, originRepository.name),
|
||||||
getMilestones(originRepository.owner, originRepository.name),
|
getMilestones(originRepository.owner, originRepository.name),
|
||||||
getPriorities(originRepository.owner, originRepository.name),
|
getPriorities(originRepository.owner, originRepository.name),
|
||||||
|
getDefaultPriority(originRepository.owner, originRepository.name),
|
||||||
getLabels(originRepository.owner, originRepository.name)
|
getLabels(originRepository.owner, originRepository.name)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -493,7 +517,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
originRepository <- getRepository(originOwner, originRepositoryName)) yield {
|
originRepository <- getRepository(originOwner, originRepositoryName)) yield {
|
||||||
using(
|
Using.resources(
|
||||||
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
||||||
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
||||||
) {
|
) {
|
||||||
@@ -542,7 +566,9 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
requestBranch = form.requestBranch,
|
requestBranch = form.requestBranch,
|
||||||
commitIdFrom = form.commitIdFrom,
|
commitIdFrom = form.commitIdFrom,
|
||||||
commitIdTo = form.commitIdTo,
|
commitIdTo = form.commitIdTo,
|
||||||
loginAccount = context.loginAccount.get
|
isDraft = form.isDraft,
|
||||||
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
|
|
||||||
// insert labels
|
// insert labels
|
||||||
@@ -567,7 +593,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
context.loginAccount.map(x => Seq(x.mailAddress) ++ getAccountExtraMailAddresses(x.userName)).getOrElse(Nil)
|
context.loginAccount.map(x => Seq(x.mailAddress) ++ getAccountExtraMailAddresses(x.userName)).getOrElse(Nil)
|
||||||
|
|
||||||
val branches =
|
val branches =
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
JGitUtil
|
JGitUtil
|
||||||
.getBranches(
|
.getBranches(
|
||||||
|
|||||||
@@ -2,16 +2,17 @@ package gitbucket.core.controller
|
|||||||
|
|
||||||
import java.io.File
|
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._
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import gitbucket.core.releases.html
|
import gitbucket.core.releases.html
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
class ReleaseController
|
class ReleaseController
|
||||||
extends ReleaseControllerBase
|
extends ReleaseControllerBase
|
||||||
with RepositoryService
|
with RepositoryService
|
||||||
@@ -42,17 +43,14 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
)(ReleaseForm.apply)
|
)(ReleaseForm.apply)
|
||||||
|
|
||||||
get("/:owner/:repository/releases")(referrersOnly { repository =>
|
get("/:owner/:repository/releases")(referrersOnly { repository =>
|
||||||
val releases = getReleases(repository.owner, repository.name)
|
val page = PaginationHelper.page(params.get("page"))
|
||||||
val assets = getReleaseAssetsMap(repository.owner, repository.name)
|
|
||||||
|
|
||||||
html.list(
|
html.list(
|
||||||
repository,
|
repository,
|
||||||
repository.tags.reverse.map { tag =>
|
fetchReleases(repository, page),
|
||||||
(tag, releases.find(_.tag == tag.name).map { release =>
|
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
||||||
(release, assets(release))
|
page,
|
||||||
})
|
repository.tags.size
|
||||||
},
|
|
||||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -106,7 +104,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount)
|
createRelease(repository.owner, repository.name, form.name, form.content, tagName, loginAccount)
|
||||||
|
|
||||||
// Insert into RELEASE_ASSET
|
// Insert into RELEASE_ASSET
|
||||||
val files = params.collect {
|
val files = params.toMap.collect {
|
||||||
case (name, value) if name.startsWith("file:") =>
|
case (name, value) if name.startsWith("file:") =>
|
||||||
val Array(_, fileId) = name.split(":")
|
val Array(_, fileId) = name.split(":")
|
||||||
(fileId, value)
|
(fileId, value)
|
||||||
@@ -130,7 +128,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
val Seq(previousTag, currentTag) = multiParams("splat")
|
val Seq(previousTag, currentTag) = multiParams("splat")
|
||||||
val previousTagId = repository.tags.collectFirst { case x if x.name == previousTag => x.id }.getOrElse("")
|
val previousTagId = repository.tags.collectFirst { case x if x.name == previousTag => x.id }.getOrElse("")
|
||||||
|
|
||||||
val commitLog = using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
val commitLog = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val commits = JGitUtil.getCommitLog(git, previousTagId, currentTag).reverse
|
val commits = JGitUtil.getCommitLog(git, previousTagId, currentTag).reverse
|
||||||
commits
|
commits
|
||||||
.map { commit =>
|
.map { commit =>
|
||||||
@@ -174,7 +172,7 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
val assets = getReleaseAssets(repository.owner, repository.name, tagName)
|
val assets = getReleaseAssets(repository.owner, repository.name, tagName)
|
||||||
deleteReleaseAssets(repository.owner, repository.name, tagName)
|
deleteReleaseAssets(repository.owner, repository.name, tagName)
|
||||||
|
|
||||||
val files = params.collect {
|
val files = params.toMap.collect {
|
||||||
case (name, value) if name.startsWith("file:") =>
|
case (name, value) if name.startsWith("file:") =>
|
||||||
val Array(_, fileId) = name.split(":")
|
val Array(_, fileId) = name.split(":")
|
||||||
(fileId, value)
|
(fileId, value)
|
||||||
@@ -215,4 +213,21 @@ trait ReleaseControllerBase extends ControllerBase {
|
|||||||
redirect(s"/${repository.owner}/${repository.name}/releases")
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,15 @@ import gitbucket.core.util.JGitUtil._
|
|||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
|
import gitbucket.core.model.WebHookContentType
|
||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.Constants
|
import org.eclipse.jgit.lib.Constants
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
import gitbucket.core.model.WebHookContentType
|
|
||||||
|
import scala.util.Using
|
||||||
|
import org.scalatra.Forbidden
|
||||||
|
|
||||||
class RepositorySettingsController
|
class RepositorySettingsController
|
||||||
extends RepositorySettingsControllerBase
|
extends RepositorySettingsControllerBase
|
||||||
@@ -42,7 +45,6 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
// for repository options
|
// for repository options
|
||||||
case class OptionsForm(
|
case class OptionsForm(
|
||||||
repositoryName: String,
|
|
||||||
description: Option[String],
|
description: Option[String],
|
||||||
isPrivate: Boolean,
|
isPrivate: Boolean,
|
||||||
issuesOption: String,
|
issuesOption: String,
|
||||||
@@ -55,9 +57,6 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val optionsForm = mapping(
|
val optionsForm = mapping(
|
||||||
"repositoryName" -> trim(
|
|
||||||
label("Repository Name", text(required, maxlength(100), repository, renameRepositoryName))
|
|
||||||
),
|
|
||||||
"description" -> trim(label("Description", optional(text()))),
|
"description" -> trim(label("Description", optional(text()))),
|
||||||
"isPrivate" -> trim(label("Repository Type", boolean())),
|
"isPrivate" -> trim(label("Repository Type", boolean())),
|
||||||
"issuesOption" -> trim(label("Issues Option", text(required, featureOption))),
|
"issuesOption" -> trim(label("Issues Option", text(required, featureOption))),
|
||||||
@@ -102,6 +101,15 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
(url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
|
(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
|
// for transfer ownership
|
||||||
case class TransferOwnerShipForm(newOwner: String)
|
case class TransferOwnerShipForm(newOwner: String)
|
||||||
|
|
||||||
@@ -142,13 +150,8 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
form.mergeOptions,
|
form.mergeOptions,
|
||||||
form.defaultMergeOption
|
form.defaultMergeOption
|
||||||
)
|
)
|
||||||
// Change repository name
|
flash.update("info", "Repository settings has been updated.")
|
||||||
if (repository.name != form.repositoryName) {
|
redirect(s"/${repository.owner}/${repository.name}/settings/options")
|
||||||
// Update database
|
|
||||||
renameRepository(repository.owner, repository.name, repository.owner, form.repositoryName)
|
|
||||||
}
|
|
||||||
flash += "info" -> "Repository settings has been updated."
|
|
||||||
redirect(s"/${repository.owner}/${form.repositoryName}/settings/options")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/** branch settings */
|
/** branch settings */
|
||||||
@@ -164,10 +167,10 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
} else {
|
} else {
|
||||||
saveRepositoryDefaultBranch(repository.owner, repository.name, form.defaultBranch)
|
saveRepositoryDefaultBranch(repository.owner, repository.name, form.defaultBranch)
|
||||||
// Change repository HEAD
|
// Change repository HEAD
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + form.defaultBranch)
|
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + form.defaultBranch)
|
||||||
}
|
}
|
||||||
flash += "info" -> "Repository default branch has been updated."
|
flash.update("info", "Repository default branch has been updated.")
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/branches")
|
redirect(s"/${repository.owner}/${repository.name}/settings/branches")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -231,7 +234,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/hooks/new", webHookForm(false))(ownerOnly { (form, repository) =>
|
post("/:owner/:repository/settings/hooks/new", webHookForm(false))(ownerOnly { (form, repository) =>
|
||||||
addWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
addWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
||||||
flash += "info" -> s"Webhook ${form.url} created"
|
flash.update("info", s"Webhook ${form.url} created")
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -240,7 +243,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
get("/:owner/:repository/settings/hooks/delete")(ownerOnly { repository =>
|
get("/:owner/:repository/settings/hooks/delete")(ownerOnly { repository =>
|
||||||
deleteWebHook(repository.owner, repository.name, params("url"))
|
deleteWebHook(repository.owner, repository.name, params("url"))
|
||||||
flash += "info" -> s"Webhook ${params("url")} deleted"
|
flash.update("info", s"Webhook ${params("url")} deleted")
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -252,11 +255,11 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
Array(h.getName, h.getValue)
|
Array(h.getName, h.getValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import scala.concurrent._
|
import scala.concurrent._
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import org.apache.http.util.EntityUtils
|
import org.apache.http.util.EntityUtils
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
@@ -292,13 +295,14 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val (webHook, json, reqFuture, resFuture) = callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload).head
|
val (webHook, json, reqFuture, resFuture) =
|
||||||
|
callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload, context.settings).head
|
||||||
|
|
||||||
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
val toErrorMap: PartialFunction[Throwable, Map[String, String]] = {
|
||||||
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
case e: java.net.UnknownHostException => Map("error" -> ("Unknown host " + e.getMessage))
|
||||||
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
case e: java.lang.IllegalArgumentException => Map("error" -> ("invalid url"))
|
||||||
case e: org.apache.http.client.ClientProtocolException => Map("error" -> ("invalid url"))
|
case e: org.apache.http.client.ClientProtocolException => Map("error" -> ("invalid url"))
|
||||||
case NonFatal(e) => Map("error" -> (e.getClass + " " + e.getMessage))
|
case NonFatal(e) => Map("error" -> (s"${e.getClass} ${e.getMessage}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
contentType = formats("json")
|
contentType = formats("json")
|
||||||
@@ -350,7 +354,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/hooks/edit", webHookForm(true))(ownerOnly { (form, repository) =>
|
post("/:owner/:repository/settings/hooks/edit", webHookForm(true))(ownerOnly { (form, repository) =>
|
||||||
updateWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
updateWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
||||||
flash += "info" -> s"webhook ${form.url} updated"
|
flash.update("info", s"webhook ${form.url} updated")
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -361,24 +365,40 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
html.danger(_, flash.get("info"))
|
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.
|
* Transfer repository ownership.
|
||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
|
post("/:owner/:repository/settings/transfer", transferForm)(ownerOnly { (form, repository) =>
|
||||||
// Change repository owner
|
if (context.settings.repositoryOperation.transfer || context.loginAccount.get.isAdmin) {
|
||||||
if (repository.owner != form.newOwner) {
|
// Change repository owner
|
||||||
renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
|
if (repository.owner != form.newOwner) {
|
||||||
}
|
renameRepository(repository.owner, repository.name, form.newOwner, repository.name)
|
||||||
redirect(s"/${form.newOwner}/${repository.name}")
|
}
|
||||||
|
redirect(s"/${form.newOwner}/${repository.name}")
|
||||||
|
} else Forbidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete the repository.
|
* Delete the repository.
|
||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/delete")(ownerOnly { repository =>
|
post("/:owner/:repository/settings/delete")(ownerOnly { repository =>
|
||||||
// Delete the repository and related files
|
if (context.settings.repositoryOperation.delete || context.loginAccount.get.isAdmin) {
|
||||||
deleteRepository(repository.repository)
|
// Delete the repository and related files
|
||||||
redirect(s"/${repository.owner}")
|
deleteRepository(repository.repository)
|
||||||
|
redirect(s"/${repository.owner}")
|
||||||
|
} else Forbidden()
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -386,11 +406,11 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
post("/:owner/:repository/settings/gc")(ownerOnly { repository =>
|
post("/:owner/:repository/settings/gc")(ownerOnly { repository =>
|
||||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
git.gc().call()
|
git.gc().call()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flash += "info" -> "Garbage collection has been executed."
|
flash.update("info", "Garbage collection has been executed.")
|
||||||
redirect(s"/${repository.owner}/${repository.name}/settings/danger")
|
redirect(s"/${repository.owner}/${repository.name}/settings/danger")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package gitbucket.core.controller
|
package gitbucket.core.controller
|
||||||
|
|
||||||
import java.io.File
|
import java.io.{File, FileInputStream, FileOutputStream}
|
||||||
|
|
||||||
|
import scala.util.Using
|
||||||
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
import gitbucket.core.repo.html
|
import gitbucket.core.repo.html
|
||||||
@@ -258,11 +259,11 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
def getSummary(statuses: List[CommitStatus]): (CommitState, String) = {
|
def getSummary(statuses: List[CommitStatus]): (CommitState, String) = {
|
||||||
val stateMap = statuses.groupBy(_.state)
|
val stateMap = statuses.groupBy(_.state)
|
||||||
val state = CommitState.combine(stateMap.keySet)
|
val state = CommitState.combine(stateMap.keySet)
|
||||||
val summary = stateMap.map { case (keyState, states) => states.size + " " + keyState.name }.mkString(", ")
|
val summary = stateMap.map { case (keyState, states) => s"${states.size} ${keyState.name}" }.mkString(", ")
|
||||||
state -> summary
|
state -> summary
|
||||||
}
|
}
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
def getTags(sha: String): List[String] = {
|
def getTags(sha: String): List[String] = {
|
||||||
JGitUtil.getTagsOnCommit(git, sha)
|
JGitUtil.getTagsOnCommit(git, sha)
|
||||||
@@ -315,7 +316,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||||
.needStatusCheck(context.loginAccount.get.userName)
|
.needStatusCheck(context.loginAccount.get.userName)
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||||
|
|
||||||
html.editor(
|
html.editor(
|
||||||
@@ -351,9 +352,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
repository = repository,
|
repository = repository,
|
||||||
branch = form.branch,
|
branch = form.branch,
|
||||||
path = form.path,
|
path = form.path,
|
||||||
files = files,
|
files = files.toIndexedSeq,
|
||||||
message = form.message.getOrElse("Add files via upload"),
|
message = form.message.getOrElse("Add files via upload"),
|
||||||
loginAccount = context.loginAccount.get
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
) {
|
) {
|
||||||
case (git, headTip, builder, inserter) =>
|
case (git, headTip, builder, inserter) =>
|
||||||
JGitUtil.processTree(git, headTip) { (path, tree) =>
|
JGitUtil.processTree(git, headTip) { (path, tree) =>
|
||||||
@@ -384,7 +386,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch)
|
||||||
.needStatusCheck(context.loginAccount.get.userName)
|
.needStatusCheck(context.loginAccount.get.userName)
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||||
|
|
||||||
@@ -411,7 +413,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
get("/:owner/:repository/remove/*")(writableUsersOnly { repository =>
|
get("/:owner/:repository/remove/*")(writableUsersOnly { repository =>
|
||||||
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
val (branch, path) = repository.splitPath(multiParams("splat").head)
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||||
|
|
||||||
@@ -440,7 +442,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
charset = form.charset,
|
charset = form.charset,
|
||||||
message = form.message.getOrElse(s"Create ${form.newFileName}"),
|
message = form.message.getOrElse(s"Create ${form.newFileName}"),
|
||||||
commit = form.commit,
|
commit = form.commit,
|
||||||
loginAccount = context.loginAccount.get
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
|
|
||||||
redirect(
|
redirect(
|
||||||
@@ -464,7 +467,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
|
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
|
||||||
},
|
},
|
||||||
commit = form.commit,
|
commit = form.commit,
|
||||||
loginAccount = context.loginAccount.get
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
|
|
||||||
redirect(
|
redirect(
|
||||||
@@ -484,11 +488,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
charset = "",
|
charset = "",
|
||||||
message = form.message.getOrElse(s"Delete ${form.fileName}"),
|
message = form.message.getOrElse(s"Delete ${form.fileName}"),
|
||||||
commit = form.commit,
|
commit = form.commit,
|
||||||
loginAccount = context.loginAccount.get
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
|
|
||||||
println(form.path)
|
|
||||||
|
|
||||||
redirect(
|
redirect(
|
||||||
s"/${repository.owner}/${repository.name}/tree/${form.branch}${if (form.path.length == 0) "" else "/" + form.path}"
|
s"/${repository.owner}/${repository.name}/tree/${form.branch}${if (form.path.length == 0) "" else "/" + form.path}"
|
||||||
)
|
)
|
||||||
@@ -496,7 +499,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
get("/:owner/:repository/raw/*")(referrersOnly { repository =>
|
get("/:owner/:repository/raw/*")(referrersOnly { repository =>
|
||||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||||
|
|
||||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||||
@@ -511,7 +514,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val blobRoute = get("/:owner/:repository/blob/*")(referrersOnly { repository =>
|
val blobRoute = get("/:owner/:repository/blob/*")(referrersOnly { repository =>
|
||||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||||
val raw = params.get("raw").getOrElse("false").toBoolean
|
val raw = params.get("raw").getOrElse("false").toBoolean
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||||
getPathObjectId(git, path, revCommit).map {
|
getPathObjectId(git, path, revCommit).map {
|
||||||
@@ -551,7 +554,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
ajaxGet("/:owner/:repository/get-blame/*")(referrersOnly { repository =>
|
ajaxGet("/:owner/:repository/get-blame/*")(referrersOnly { repository =>
|
||||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||||
contentType = formats("json")
|
contentType = formats("json")
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
val last = git.log.add(git.getRepository.resolve(id)).addPath(path).setMaxCount(1).call.iterator.next.name
|
val last = git.log.add(git.getRepository.resolve(id)).addPath(path).setMaxCount(1).call.iterator.next.name
|
||||||
Serialization.write(
|
Serialization.write(
|
||||||
@@ -586,7 +589,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val id = params("id")
|
val id = params("id")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
defining(JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))) {
|
defining(JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))) {
|
||||||
revCommit =>
|
revCommit =>
|
||||||
@@ -615,7 +618,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
get("/:owner/:repository/patch/:id")(referrersOnly { repository =>
|
get("/:owner/:repository/patch/:id")(referrersOnly { repository =>
|
||||||
try {
|
try {
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val diff = JGitUtil.getPatch(git, None, params("id"))
|
val diff = JGitUtil.getPatch(git, None, params("id"))
|
||||||
contentType = formats("txt")
|
contentType = formats("txt")
|
||||||
diff
|
diff
|
||||||
@@ -628,7 +631,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
get("/:owner/:repository/patch/*...*")(referrersOnly { repository =>
|
get("/:owner/:repository/patch/*...*")(referrersOnly { repository =>
|
||||||
try {
|
try {
|
||||||
val Seq(fromId, toId) = multiParams("splat")
|
val Seq(fromId, toId) = multiParams("splat")
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val diff = JGitUtil.getPatch(git, Some(fromId), toId)
|
val diff = JGitUtil.getPatch(git, Some(fromId), toId)
|
||||||
contentType = formats("txt")
|
contentType = formats("txt")
|
||||||
diff
|
diff
|
||||||
@@ -748,7 +751,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
get("/:owner/:repository/branches")(referrersOnly { repository =>
|
get("/:owner/:repository/branches")(referrersOnly { repository =>
|
||||||
val protectedBranches = getProtectedBranchList(repository.owner, repository.name).toSet
|
val protectedBranches = getProtectedBranchList(repository.owner, repository.name).toSet
|
||||||
val branches = using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
val branches = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
JGitUtil
|
JGitUtil
|
||||||
.getBranches(
|
.getBranches(
|
||||||
@@ -788,14 +791,14 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
* Creates a tag.
|
* Creates a tag.
|
||||||
*/
|
*/
|
||||||
post("/:owner/:repository/tag", tagForm)(writableUsersOnly { (form, repository) =>
|
post("/:owner/:repository/tag", tagForm)(writableUsersOnly { (form, repository) =>
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
JGitUtil.createTag(git, form.tagName, form.message, form.commitId)
|
JGitUtil.createTag(git, form.tagName, form.message, form.commitId)
|
||||||
} match {
|
} match {
|
||||||
case Right(message) =>
|
case Right(message) =>
|
||||||
flash += "info" -> message
|
flash.update("info", message)
|
||||||
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
||||||
case Left(message) =>
|
case Left(message) =>
|
||||||
flash += "error" -> message
|
flash.update("error", message)
|
||||||
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
redirect(s"/${repository.owner}/${repository.name}/commit/${form.commitId}")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -806,16 +809,16 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
post("/:owner/:repository/branches")(writableUsersOnly { repository =>
|
post("/:owner/:repository/branches")(writableUsersOnly { repository =>
|
||||||
val newBranchName = params.getOrElse("new", halt(400))
|
val newBranchName = params.getOrElse("new", halt(400))
|
||||||
val fromBranchName = params.getOrElse("from", halt(400))
|
val fromBranchName = params.getOrElse("from", halt(400))
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
JGitUtil.createBranch(git, fromBranchName, newBranchName)
|
JGitUtil.createBranch(git, fromBranchName, newBranchName)
|
||||||
} match {
|
} match {
|
||||||
case Right(message) =>
|
case Right(message) =>
|
||||||
flash += "info" -> message
|
flash.update("info", message)
|
||||||
redirect(
|
redirect(
|
||||||
s"/${repository.owner}/${repository.name}/tree/${StringUtil.urlEncode(newBranchName).replace("%2F", "/")}"
|
s"/${repository.owner}/${repository.name}/tree/${StringUtil.urlEncode(newBranchName).replace("%2F", "/")}"
|
||||||
)
|
)
|
||||||
case Left(message) =>
|
case Left(message) =>
|
||||||
flash += "error" -> message
|
flash.update("error", message)
|
||||||
redirect(s"/${repository.owner}/${repository.name}/tree/${fromBranchName}")
|
redirect(s"/${repository.owner}/${repository.name}/tree/${fromBranchName}")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -827,7 +830,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val branchName = multiParams("splat").head
|
val branchName = multiParams("splat").head
|
||||||
val userName = context.loginAccount.get.userName
|
val userName = context.loginAccount.get.userName
|
||||||
if (repository.repository.defaultBranch != branchName) {
|
if (repository.repository.defaultBranch != branchName) {
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
git.branchDelete().setForce(true).setBranchNames(branchName).call()
|
git.branchDelete().setForce(true).setBranchNames(branchName).call()
|
||||||
recordDeleteBranchActivity(repository.owner, repository.name, userName, branchName)
|
recordDeleteBranchActivity(repository.owner, repository.name, userName, branchName)
|
||||||
}
|
}
|
||||||
@@ -879,7 +882,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
* Displays the file find of branch.
|
* Displays the file find of branch.
|
||||||
*/
|
*/
|
||||||
get("/:owner/:repository/find/*")(referrersOnly { repository =>
|
get("/:owner/:repository/find/*")(referrersOnly { repository =>
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val ref = multiParams("splat").head
|
val ref = multiParams("splat").head
|
||||||
JGitUtil.getTreeId(git, ref).map { treeId =>
|
JGitUtil.getTreeId(git, ref).map { treeId =>
|
||||||
html.find(ref, treeId, repository)
|
html.find(ref, treeId, repository)
|
||||||
@@ -891,7 +894,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
* Get all file list of branch.
|
* Get all file list of branch.
|
||||||
*/
|
*/
|
||||||
ajaxGet("/:owner/:repository/tree-list/:tree")(referrersOnly { repository =>
|
ajaxGet("/:owner/:repository/tree-list/:tree")(referrersOnly { repository =>
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val treeId = params("tree")
|
val treeId = params("tree")
|
||||||
contentType = formats("json")
|
contentType = formats("json")
|
||||||
Map("paths" -> JGitUtil.getAllFileListByTreeId(git, treeId))
|
Map("paths" -> JGitUtil.getAllFileListByTreeId(git, treeId))
|
||||||
@@ -915,7 +918,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
* @return HTML of the file list
|
* @return HTML of the file list
|
||||||
*/
|
*/
|
||||||
private def fileList(repository: RepositoryService.RepositoryInfo, revstr: String = "", path: String = ".") = {
|
private def fileList(repository: RepositoryService.RepositoryInfo, revstr: String = "", path: String = ".") = {
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
if (JGitUtil.isEmpty(git)) {
|
if (JGitUtil.isEmpty(git)) {
|
||||||
html.guide(repository, hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
html.guide(repository, hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
|
||||||
} else {
|
} else {
|
||||||
@@ -925,8 +928,9 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
defining(JGitUtil.getRevCommitFromId(git, objectId)) { revCommit =>
|
defining(JGitUtil.getRevCommitFromId(git, objectId)) { revCommit =>
|
||||||
val lastModifiedCommit =
|
val lastModifiedCommit =
|
||||||
if (path == ".") revCommit else JGitUtil.getLastModifiedCommit(git, revCommit, path)
|
if (path == ".") revCommit else JGitUtil.getLastModifiedCommit(git, revCommit, path)
|
||||||
|
val commitCount = JGitUtil.getCommitCount(git, lastModifiedCommit.getName)
|
||||||
// get files
|
// 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
|
val parentPath = if (path == ".") Nil else path.split("/").toList
|
||||||
// process README.md or README.markdown
|
// process README.md or README.markdown
|
||||||
val readme = files
|
val readme = files
|
||||||
@@ -947,7 +951,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
repository,
|
repository,
|
||||||
if (path == ".") Nil else path.split("/").toList, // current path
|
if (path == ".") Nil else path.split("/").toList, // current path
|
||||||
new JGitUtil.CommitInfo(lastModifiedCommit), // last modified commit
|
new JGitUtil.CommitInfo(lastModifiedCommit), // last modified commit
|
||||||
JGitUtil.getCommitCount(git, lastModifiedCommit.getName),
|
commitCount,
|
||||||
files,
|
files,
|
||||||
readme,
|
readme,
|
||||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
||||||
@@ -974,16 +978,16 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
def archive(revision: String, archiveFormat: String, archive: ArchiveOutputStream)(
|
def archive(revision: String, archiveFormat: String, archive: ArchiveOutputStream)(
|
||||||
entryCreator: (String, Long, java.util.Date, Int) => ArchiveEntry
|
entryCreator: (String, Long, java.util.Date, Int) => ArchiveEntry
|
||||||
): Unit = {
|
): Unit = {
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val oid = git.getRepository.resolve(revision)
|
val oid = git.getRepository.resolve(revision)
|
||||||
val commit = JGitUtil.getRevCommitFromId(git, oid)
|
val commit = JGitUtil.getRevCommitFromId(git, oid)
|
||||||
val date = commit.getCommitterIdent.getWhen
|
val date = commit.getCommitterIdent.getWhen
|
||||||
val sha1 = oid.getName()
|
val sha1 = oid.getName()
|
||||||
val repositorySuffix = (if (sha1.startsWith(revision)) sha1 else revision).replace('/', '-')
|
val repositorySuffix = (if (sha1.startsWith(revision)) sha1 else revision).replace('/', '-')
|
||||||
val pathSuffix = if (path.isEmpty) "" else '-' + path.replace('/', '-')
|
val pathSuffix = if (path.isEmpty) "" else s"-${path.replace('/', '-')}"
|
||||||
val baseName = repository.name + "-" + repositorySuffix + pathSuffix
|
val baseName = repository.name + "-" + repositorySuffix + pathSuffix
|
||||||
|
|
||||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||||
treeWalk.addTree(commit.getTree)
|
treeWalk.addTree(commit.getTree)
|
||||||
treeWalk.setRecursive(true)
|
treeWalk.setRecursive(true)
|
||||||
if (!path.isEmpty) {
|
if (!path.isEmpty) {
|
||||||
@@ -994,24 +998,31 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val entryPath =
|
val entryPath =
|
||||||
if (path.isEmpty) baseName + "/" + treeWalk.getPathString
|
if (path.isEmpty) baseName + "/" + treeWalk.getPathString
|
||||||
else path.split("/").last + treeWalk.getPathString.substring(path.length)
|
else path.split("/").last + treeWalk.getPathString.substring(path.length)
|
||||||
val size = JGitUtil.getContentSize(git.getRepository.open(treeWalk.getObjectId(0)))
|
|
||||||
val mode = treeWalk.getFileMode.getBits
|
val mode = treeWalk.getFileMode.getBits
|
||||||
val entry: ArchiveEntry = entryCreator(entryPath, size, date, mode)
|
|
||||||
JGitUtil.openFile(git, repository, commit.getTree, treeWalk.getPathString) { in =>
|
JGitUtil.openFile(git, repository, commit.getTree, treeWalk.getPathString) { in =>
|
||||||
|
val tempFile = File.createTempFile("gitbucket", ".archive")
|
||||||
|
val size = Using.resource(new FileOutputStream(tempFile)) { out =>
|
||||||
|
IOUtils.copy(
|
||||||
|
EolStreamTypeUtil.wrapInputStream(
|
||||||
|
in,
|
||||||
|
EolStreamTypeUtil
|
||||||
|
.detectStreamType(
|
||||||
|
OperationType.CHECKOUT_OP,
|
||||||
|
git.getRepository.getConfig.get(WorkingTreeOptions.KEY),
|
||||||
|
treeWalk.getAttributes
|
||||||
|
)
|
||||||
|
),
|
||||||
|
out
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val entry: ArchiveEntry = entryCreator(entryPath, size, date, mode)
|
||||||
archive.putArchiveEntry(entry)
|
archive.putArchiveEntry(entry)
|
||||||
IOUtils.copy(
|
Using.resource(new FileInputStream(tempFile)) { in =>
|
||||||
EolStreamTypeUtil.wrapInputStream(
|
IOUtils.copy(in, archive)
|
||||||
in,
|
}
|
||||||
EolStreamTypeUtil
|
|
||||||
.detectStreamType(
|
|
||||||
OperationType.CHECKOUT_OP,
|
|
||||||
git.getRepository.getConfig.get(WorkingTreeOptions.KEY),
|
|
||||||
treeWalk.getAttributes
|
|
||||||
)
|
|
||||||
),
|
|
||||||
archive
|
|
||||||
)
|
|
||||||
archive.closeArchiveEntry()
|
archive.closeArchiveEntry()
|
||||||
|
tempFile.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1032,9 +1043,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
contentType = "application/octet-stream"
|
contentType = "application/octet-stream"
|
||||||
response.setBufferSize(1024 * 1024)
|
response.setBufferSize(1024 * 1024)
|
||||||
using(new ZipArchiveOutputStream(response.getOutputStream)) { zip =>
|
Using.resource(new ZipArchiveOutputStream(response.getOutputStream)) { zip =>
|
||||||
archive(revision, ".zip", zip) { (path, size, date, mode) =>
|
archive(revision, ".zip", zip) { (path, size, date, mode) =>
|
||||||
val entry = new ZipArchiveEntry(path)
|
val entry = new ZipArchiveEntry(path)
|
||||||
|
entry.setSize(size)
|
||||||
entry.setUnixMode(mode)
|
entry.setUnixMode(mode)
|
||||||
entry.setTime(date.getTime)
|
entry.setTime(date.getTime)
|
||||||
entry
|
entry
|
||||||
@@ -1048,17 +1060,18 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
contentType = "application/octet-stream"
|
contentType = "application/octet-stream"
|
||||||
response.setBufferSize(1024 * 1024)
|
response.setBufferSize(1024 * 1024)
|
||||||
using(compressor match {
|
Using.resource(compressor match {
|
||||||
case "gz" => new GzipCompressorOutputStream(response.getOutputStream)
|
case "gz" => new GzipCompressorOutputStream(response.getOutputStream)
|
||||||
case "bz2" => new BZip2CompressorOutputStream(response.getOutputStream)
|
case "bz2" => new BZip2CompressorOutputStream(response.getOutputStream)
|
||||||
case "xz" => new XZCompressorOutputStream(response.getOutputStream)
|
case "xz" => new XZCompressorOutputStream(response.getOutputStream)
|
||||||
}) { compressorOutputStream =>
|
}) { compressorOutputStream =>
|
||||||
using(new TarArchiveOutputStream(compressorOutputStream)) { tar =>
|
Using.resource(new TarArchiveOutputStream(compressorOutputStream)) { tar =>
|
||||||
tar.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR)
|
tar.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR)
|
||||||
tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU)
|
||||||
tar.setAddPaxHeadersForNonAsciiNames(true)
|
tar.setAddPaxHeadersForNonAsciiNames(true)
|
||||||
archive(revision, ".tar.gz", tar) { (path, size, date, mode) =>
|
archive(revision, ".tar.gz", tar) { (path, size, date, mode) =>
|
||||||
val entry = new TarArchiveEntry(path)
|
val entry = new TarArchiveEntry(path)
|
||||||
|
entry.setSize(size)
|
||||||
entry.setModTime(date)
|
entry.setModTime(date)
|
||||||
entry.setMode(mode)
|
entry.setMode(mode)
|
||||||
entry
|
entry
|
||||||
@@ -1081,7 +1094,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val branch = params("branch")
|
val branch = params("branch")
|
||||||
|
|
||||||
LockUtil.lock(s"${owner}/${repository}") {
|
LockUtil.lock(s"${owner}/${repository}") {
|
||||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||||
val headName = s"refs/heads/${branch}"
|
val headName = s"refs/heads/${branch}"
|
||||||
val headTip = git.getRepository.resolve(headName)
|
val headTip = git.getRepository.resolve(headName)
|
||||||
if (headTip.getName != value) {
|
if (headTip.getName != value) {
|
||||||
|
|||||||
@@ -2,14 +2,11 @@ package gitbucket.core.controller
|
|||||||
|
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
|
||||||
import com.github.zafarkhaja.semver.{Version => Semver}
|
|
||||||
import gitbucket.core.GitBucketCoreModule
|
|
||||||
import gitbucket.core.admin.html
|
import gitbucket.core.admin.html
|
||||||
import gitbucket.core.plugin.{PluginInfoBase, PluginRegistry, PluginRepository}
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
import gitbucket.core.service.SystemSettingsService._
|
import gitbucket.core.service.SystemSettingsService._
|
||||||
import gitbucket.core.service.{AccountService, RepositoryService}
|
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||||
import gitbucket.core.ssh.SshServer
|
import gitbucket.core.ssh.SshServer
|
||||||
import gitbucket.core.util.Directory._
|
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.StringUtil._
|
import gitbucket.core.util.StringUtil._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
@@ -21,8 +18,8 @@ import org.scalatra._
|
|||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
class SystemSettingsController
|
class SystemSettingsController
|
||||||
extends SystemSettingsControllerBase
|
extends SystemSettingsControllerBase
|
||||||
@@ -41,14 +38,22 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
"information" -> trim(label("Information", optional(text()))),
|
"information" -> trim(label("Information", optional(text()))),
|
||||||
"allowAccountRegistration" -> trim(label("Account registration", boolean())),
|
"allowAccountRegistration" -> trim(label("Account registration", boolean())),
|
||||||
"allowAnonymousAccess" -> trim(label("Anonymous access", boolean())),
|
"allowAnonymousAccess" -> trim(label("Anonymous access", boolean())),
|
||||||
"isCreateRepoOptionPublic" -> trim(label("Default option to create a new repository", boolean())),
|
"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())),
|
"gravatar" -> trim(label("Gravatar", boolean())),
|
||||||
"notification" -> trim(label("Notification", boolean())),
|
"notification" -> trim(label("Notification", boolean())),
|
||||||
"activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))),
|
"activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))),
|
||||||
|
"limitVisibleRepositories" -> trim(label("limitVisibleRepositories", boolean())),
|
||||||
"ssh" -> mapping(
|
"ssh" -> mapping(
|
||||||
"enabled" -> trim(label("SSH access", boolean())),
|
"enabled" -> trim(label("SSH access", boolean())),
|
||||||
"host" -> trim(label("SSH host", optional(text()))),
|
"host" -> trim(label("SSH host", optional(text()))),
|
||||||
"port" -> trim(label("SSH port", optional(number()))),
|
"port" -> trim(label("SSH port", optional(number())))
|
||||||
)(Ssh.apply),
|
)(Ssh.apply),
|
||||||
"useSMTP" -> trim(label("SMTP", boolean())),
|
"useSMTP" -> trim(label("SMTP", boolean())),
|
||||||
"smtp" -> optionalIfNotChecked(
|
"smtp" -> optionalIfNotChecked(
|
||||||
@@ -93,17 +98,18 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
)(OIDC.apply)
|
)(OIDC.apply)
|
||||||
),
|
),
|
||||||
"skinName" -> trim(label("AdminLTE skin name", text(required))),
|
"skinName" -> trim(label("AdminLTE skin name", text(required))),
|
||||||
|
"userDefinedCss" -> trim(label("User-defined CSS", optional(text()))),
|
||||||
"showMailAddress" -> trim(label("Show mail address", boolean())),
|
"showMailAddress" -> trim(label("Show mail address", boolean())),
|
||||||
"pluginNetworkInstall" -> trim(label("Network plugin installation", boolean())),
|
"webhook" -> mapping(
|
||||||
"proxy" -> optionalIfNotChecked(
|
"blockPrivateAddress" -> trim(label("Block private address", boolean())),
|
||||||
"useProxy",
|
"whitelist" -> trim(label("Whitelist", multiLineText()))
|
||||||
mapping(
|
)(WebHook.apply),
|
||||||
"host" -> trim(label("Proxy host", text(required))),
|
"upload" -> mapping(
|
||||||
"port" -> trim(label("Proxy port", number())),
|
"maxFileSize" -> trim(label("Max file size", long(required))),
|
||||||
"user" -> trim(label("Keystore", optional(text()))),
|
"timeout" -> trim(label("Timeout", long(required))),
|
||||||
"password" -> trim(label("Keystore", optional(text())))
|
"largeMaxFileSize" -> trim(label("Max file size for large file", long(required))),
|
||||||
)(Proxy.apply)
|
"largeTimeout" -> trim(label("Timeout for large file", long(required)))
|
||||||
)
|
)(Upload.apply)
|
||||||
)(SystemSettings.apply).verifying { settings =>
|
)(SystemSettings.apply).verifying { settings =>
|
||||||
Vector(
|
Vector(
|
||||||
if (settings.ssh.enabled && settings.baseUrl.isEmpty) {
|
if (settings.ssh.enabled && settings.baseUrl.isEmpty) {
|
||||||
@@ -179,7 +185,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
val newUserForm = mapping(
|
val newUserForm = mapping(
|
||||||
"userName" -> trim(label("Username", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
"userName" -> trim(label("Username", text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
|
||||||
"password" -> trim(label("Password", text(required, maxlength(20), password))),
|
"password" -> trim(label("Password", text(required, maxlength(20)))),
|
||||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress()))),
|
||||||
"extraMailAddresses" -> list(
|
"extraMailAddresses" -> list(
|
||||||
@@ -193,7 +199,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
val editUserForm = mapping(
|
val editUserForm = mapping(
|
||||||
"userName" -> trim(label("Username", text(required, maxlength(100), identifier))),
|
"userName" -> trim(label("Username", text(required, maxlength(100), identifier))),
|
||||||
"password" -> trim(label("Password", optional(text(maxlength(20), password)))),
|
"password" -> trim(label("Password", optional(text(maxlength(20))))),
|
||||||
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
"fullName" -> trim(label("Full Name", text(required, maxlength(100)))),
|
||||||
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
"mailAddress" -> trim(label("Mail Address", text(required, maxlength(100), uniqueMailAddress("userName")))),
|
||||||
"extraMailAddresses" -> list(
|
"extraMailAddresses" -> list(
|
||||||
@@ -229,30 +235,30 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
val conn = request2Session(request).conn
|
val conn = request2Session(request).conn
|
||||||
val meta = conn.getMetaData
|
val meta = conn.getMetaData
|
||||||
val tables = ListBuffer[Table]()
|
val tables = ListBuffer[Table]()
|
||||||
using(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) {
|
Using.resource(meta.getTables(null, "%", "%", Array("TABLE", "VIEW"))) {
|
||||||
rs =>
|
rs =>
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
val tableName = rs.getString("TABLE_NAME")
|
val tableName = rs.getString("TABLE_NAME")
|
||||||
|
|
||||||
val pkColumns = ListBuffer[String]()
|
val pkColumns = ListBuffer[String]()
|
||||||
using(meta.getPrimaryKeys(null, null, tableName)) { rs =>
|
Using.resource(meta.getPrimaryKeys(null, null, tableName)) { rs =>
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
pkColumns += rs.getString("COLUMN_NAME").toUpperCase
|
pkColumns += rs.getString("COLUMN_NAME").toUpperCase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val columns = ListBuffer[Column]()
|
val columns = ListBuffer[Column]()
|
||||||
using(meta.getColumns(null, "%", tableName, "%")) { rs =>
|
Using.resource(meta.getColumns(null, "%", tableName, "%")) { rs =>
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
val columnName = rs.getString("COLUMN_NAME").toUpperCase
|
val columnName = rs.getString("COLUMN_NAME").toUpperCase
|
||||||
columns += Column(columnName, pkColumns.contains(columnName))
|
columns += Column(columnName, pkColumns.contains(columnName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tables += Table(tableName.toUpperCase, columns)
|
tables += Table(tableName.toUpperCase, columns.toSeq)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
html.dbviewer(tables)
|
html.dbviewer(tables.toSeq)
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/admin/dbviewer/_query")(adminOnly {
|
post("/admin/dbviewer/_query")(adminOnly {
|
||||||
@@ -263,10 +269,10 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
if (trimmedQuery.nonEmpty) {
|
if (trimmedQuery.nonEmpty) {
|
||||||
try {
|
try {
|
||||||
val conn = request2Session(request).conn
|
val conn = request2Session(request).conn
|
||||||
using(conn.prepareStatement(query)) {
|
Using.resource(conn.prepareStatement(query)) {
|
||||||
stmt =>
|
stmt =>
|
||||||
if (trimmedQuery.toUpperCase.startsWith("SELECT")) {
|
if (trimmedQuery.toUpperCase.startsWith("SELECT")) {
|
||||||
using(stmt.executeQuery()) {
|
Using.resource(stmt.executeQuery()) {
|
||||||
rs =>
|
rs =>
|
||||||
val meta = rs.getMetaData
|
val meta = rs.getMetaData
|
||||||
val columns = for (i <- 1 to meta.getColumnCount) yield {
|
val columns = for (i <- 1 to meta.getColumnCount) yield {
|
||||||
@@ -309,7 +315,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
} SshServer.start(sshAddress, baseUrl)
|
} SshServer.start(sshAddress, baseUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
flash += "info" -> "System settings has been updated."
|
flash.update("info", "System settings has been updated.")
|
||||||
redirect("/admin/system")
|
redirect("/admin/system")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -332,63 +338,12 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
get("/admin/plugins")(adminOnly {
|
get("/admin/plugins")(adminOnly {
|
||||||
// Installed plugins
|
html.plugins(PluginRegistry().getPlugins(), flash.get("info"))
|
||||||
val enabledPlugins = PluginRegistry().getPlugins()
|
|
||||||
val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion
|
|
||||||
val gitbucketSemver = Semver.valueOf(gitbucketVersion)
|
|
||||||
|
|
||||||
// Plugins in the remote repository
|
|
||||||
val repositoryPlugins = if (context.settings.pluginNetworkInstall) {
|
|
||||||
PluginRepository
|
|
||||||
.getPlugins()
|
|
||||||
.map {
|
|
||||||
meta =>
|
|
||||||
(meta, meta.versions.reverse.find {
|
|
||||||
version =>
|
|
||||||
val semver = Semver.valueOf(version.version)
|
|
||||||
gitbucketVersion == version.gitbucketVersion && !enabledPlugins.exists { plugin =>
|
|
||||||
if (plugin.pluginId == meta.id) {
|
|
||||||
Semver.valueOf(plugin.pluginVersion) match {
|
|
||||||
case x if x.greaterThan(semver) => true
|
|
||||||
case x if x.equals(semver) =>
|
|
||||||
plugin.gitbucketVersion match {
|
|
||||||
case None => true
|
|
||||||
case Some(x) => Semver.valueOf(x).greaterThanOrEqualTo(gitbucketSemver)
|
|
||||||
}
|
|
||||||
case _ => false
|
|
||||||
}
|
|
||||||
} else false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.collect {
|
|
||||||
case (meta, Some(version)) =>
|
|
||||||
new PluginInfoBase(
|
|
||||||
pluginId = meta.id,
|
|
||||||
pluginName = meta.name,
|
|
||||||
pluginVersion = version.version,
|
|
||||||
gitbucketVersion = Some(version.gitbucketVersion),
|
|
||||||
description = meta.description
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else Nil
|
|
||||||
|
|
||||||
// Merge
|
|
||||||
val plugins = (enabledPlugins.map((_, true)) ++ repositoryPlugins.map((_, false)))
|
|
||||||
.groupBy(_._1.pluginId)
|
|
||||||
.map {
|
|
||||||
case (pluginId, plugins) =>
|
|
||||||
val (plugin, enabled) = plugins.head
|
|
||||||
(plugin, enabled, if (plugins.length > 1) plugins.last._1.pluginVersion else "")
|
|
||||||
}
|
|
||||||
.toList
|
|
||||||
|
|
||||||
html.plugins(plugins, flash.get("info"))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/admin/plugins/_reload")(adminOnly {
|
post("/admin/plugins/_reload")(adminOnly {
|
||||||
PluginRegistry.reload(request.getServletContext(), loadSystemSettings(), request2Session(request).conn)
|
PluginRegistry.reload(request.getServletContext(), loadSystemSettings(), request2Session(request).conn)
|
||||||
flash += "info" -> "All plugins were reloaded."
|
flash.update("info", "All plugins were reloaded.")
|
||||||
redirect("/admin/plugins")
|
redirect("/admin/plugins")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -398,37 +353,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
if (PluginRegistry().getPlugins().exists(_.pluginId == pluginId)) {
|
if (PluginRegistry().getPlugins().exists(_.pluginId == pluginId)) {
|
||||||
PluginRegistry
|
PluginRegistry
|
||||||
.uninstall(pluginId, request.getServletContext, loadSystemSettings(), request2Session(request).conn)
|
.uninstall(pluginId, request.getServletContext, loadSystemSettings(), request2Session(request).conn)
|
||||||
flash += "info" -> s"${pluginId} was uninstalled."
|
flash.update("info", s"${pluginId} was uninstalled.")
|
||||||
}
|
|
||||||
|
|
||||||
redirect("/admin/plugins")
|
|
||||||
})
|
|
||||||
|
|
||||||
post("/admin/plugins/:pluginId/:version/_install")(adminOnly {
|
|
||||||
if (context.settings.pluginNetworkInstall) {
|
|
||||||
val pluginId = params("pluginId")
|
|
||||||
val version = params("version")
|
|
||||||
val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion
|
|
||||||
|
|
||||||
PluginRepository
|
|
||||||
.getPlugins()
|
|
||||||
.collectFirst {
|
|
||||||
case meta if meta.id == pluginId =>
|
|
||||||
(meta, meta.versions.find(x => x.gitbucketVersion == gitbucketVersion && x.version == version))
|
|
||||||
}
|
|
||||||
.foreach {
|
|
||||||
case (meta, version) =>
|
|
||||||
version.foreach { version =>
|
|
||||||
PluginRegistry.install(
|
|
||||||
pluginId,
|
|
||||||
new java.net.URL(version.url),
|
|
||||||
request.getServletContext,
|
|
||||||
loadSystemSettings(),
|
|
||||||
request2Session(request).conn
|
|
||||||
)
|
|
||||||
flash += "info" -> s"${pluginId}:${version.version} was installed."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect("/admin/plugins")
|
redirect("/admin/plugins")
|
||||||
@@ -476,7 +401,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
getAccountByUserName(userName, true).map {
|
getAccountByUserName(userName, true).map {
|
||||||
account =>
|
account =>
|
||||||
if (account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(account)) {
|
if (account.isAdmin && (form.isRemoved || !form.isAdmin) && isLastAdministrator(account)) {
|
||||||
flash += "error" -> "Account can't be turned off because this is last one administrator."
|
flash.update("error", "Account can't be turned off because this is last one administrator.")
|
||||||
redirect(s"/admin/users/${userName}/_edituser")
|
redirect(s"/admin/users/${userName}/_edituser")
|
||||||
} else {
|
} else {
|
||||||
if (form.isRemoved) {
|
if (form.isRemoved) {
|
||||||
@@ -601,31 +526,44 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName)
|
response.setHeader("Content-Disposition", "attachment; filename=" + file.getName)
|
||||||
response.setContentLength(file.length.toInt)
|
response.setContentLength(file.length.toInt)
|
||||||
|
|
||||||
using(new FileInputStream(file)) { in =>
|
Using.resource(new FileInputStream(file)) { in =>
|
||||||
IOUtils.copy(in, response.outputStream)
|
IOUtils.copy(in, response.outputStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
()
|
()
|
||||||
})
|
})
|
||||||
|
|
||||||
private def members: Constraint = new Constraint() {
|
private def multiLineText(constraints: Constraint*): SingleValueType[Seq[String]] =
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
new SingleValueType[Seq[String]](constraints: _*) {
|
||||||
if (value.split(",").exists {
|
def convert(value: String, messages: Messages): Seq[String] = {
|
||||||
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
if (value == null) {
|
||||||
}) None
|
Nil
|
||||||
else Some("Must select one manager at least.")
|
} else {
|
||||||
}
|
value.split("\n").toIndexedSeq.map(_.trim)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
protected def disableByNotYourself(paramName: String): Constraint = new Constraint() {
|
}
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
|
||||||
params.get(paramName).flatMap { userName =>
|
private def members: Constraint =
|
||||||
if (userName == context.loginAccount.get.userName && params.get("removed") == Some("true"))
|
new Constraint() {
|
||||||
Some("You can't disable your account yourself")
|
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||||
else
|
if (value.split(",").exists {
|
||||||
None
|
_.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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import gitbucket.core.util.Directory._
|
|||||||
import org.scalatra.forms._
|
import org.scalatra.forms._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
class WikiController
|
class WikiController
|
||||||
extends WikiControllerBase
|
extends WikiControllerBase
|
||||||
@@ -90,7 +91,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
get("/:owner/:repository/wiki/:page/_history")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/:page/_history")(referrersOnly { repository =>
|
||||||
val pageName = StringUtil.urlDecode(params("page"))
|
val pageName = StringUtil.urlDecode(params("page"))
|
||||||
|
|
||||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match {
|
JGitUtil.getCommitLog(git, "master", path = pageName + ".md") match {
|
||||||
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository, isEditable(repository))
|
case Right((logs, hasNext)) => html.history(Some(pageName), logs, repository, isEditable(repository))
|
||||||
case Left(_) => NotFound()
|
case Left(_) => NotFound()
|
||||||
@@ -102,7 +103,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
val pageName = StringUtil.urlDecode(params("page"))
|
val pageName = StringUtil.urlDecode(params("page"))
|
||||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||||
|
|
||||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
html.compare(
|
html.compare(
|
||||||
Some(pageName),
|
Some(pageName),
|
||||||
from,
|
from,
|
||||||
@@ -118,7 +119,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
get("/:owner/:repository/wiki/_compare/:commitId")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/_compare/:commitId")(referrersOnly { repository =>
|
||||||
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
val Array(from, to) = params("commitId").split("\\.\\.\\.")
|
||||||
|
|
||||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
html.compare(
|
html.compare(
|
||||||
None,
|
None,
|
||||||
from,
|
from,
|
||||||
@@ -139,7 +140,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))) {
|
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, Some(pageName))) {
|
||||||
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
|
redirect(s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}")
|
||||||
} else {
|
} else {
|
||||||
flash += "info" -> "This patch was not able to be reversed."
|
flash.update("info", "This patch was not able to be reversed.")
|
||||||
redirect(
|
redirect(
|
||||||
s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}"
|
s"/${repository.owner}/${repository.name}/wiki/${StringUtil.urlEncode(pageName)}/_compare/${from}...${to}"
|
||||||
)
|
)
|
||||||
@@ -154,7 +155,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)) {
|
if (revertWikiPage(repository.owner, repository.name, from, to, context.loginAccount.get, None)) {
|
||||||
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
redirect(s"/${repository.owner}/${repository.name}/wiki")
|
||||||
} else {
|
} else {
|
||||||
flash += "info" -> "This patch was not able to be reversed."
|
flash.update("info", "This patch was not able to be reversed.")
|
||||||
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
|
redirect(s"/${repository.owner}/${repository.name}/wiki/_compare/${from}...${to}")
|
||||||
}
|
}
|
||||||
} else Unauthorized()
|
} else Unauthorized()
|
||||||
@@ -190,7 +191,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
form.pageName,
|
form.pageName,
|
||||||
commitId
|
commitId
|
||||||
)
|
)
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum) {
|
callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
|
||||||
getAccountByUserName(repository.owner).map { repositoryUser =>
|
getAccountByUserName(repository.owner).map { repositoryUser =>
|
||||||
WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
WebHookGollumPayload("edited", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
||||||
}
|
}
|
||||||
@@ -228,7 +229,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
commitId =>
|
commitId =>
|
||||||
updateLastActivityDate(repository.owner, repository.name)
|
updateLastActivityDate(repository.owner, repository.name)
|
||||||
recordCreateWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName)
|
recordCreateWikiPageActivity(repository.owner, repository.name, loginAccount.userName, form.pageName)
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.Gollum) {
|
callWebHookOf(repository.owner, repository.name, WebHook.Gollum, context.settings) {
|
||||||
getAccountByUserName(repository.owner).map { repositoryUser =>
|
getAccountByUserName(repository.owner).map { repositoryUser =>
|
||||||
WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
WebHookGollumPayload("created", form.pageName, commitId, repository, repositoryUser, loginAccount)
|
||||||
}
|
}
|
||||||
@@ -269,7 +270,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/_history")(referrersOnly { repository =>
|
||||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
JGitUtil.getCommitLog(git, "master") match {
|
JGitUtil.getCommitLog(git, "master") match {
|
||||||
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
|
case Right((logs, hasNext)) => html.history(None, logs, repository, isEditable(repository))
|
||||||
case Left(_) => NotFound()
|
case Left(_) => NotFound()
|
||||||
@@ -279,7 +280,7 @@ trait WikiControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository =>
|
get("/:owner/:repository/wiki/_blob/*")(referrersOnly { repository =>
|
||||||
val path = multiParams("splat").head
|
val path = multiParams("splat").head
|
||||||
using(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getWikiRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve("master"))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve("master"))
|
||||||
|
|
||||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||||
|
|||||||
@@ -3,21 +3,35 @@ import gitbucket.core.api.{ApiObject, ApiRef, JsonFormat}
|
|||||||
import gitbucket.core.controller.ControllerBase
|
import gitbucket.core.controller.ControllerBase
|
||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util.ReferrerAuthenticator
|
import gitbucket.core.util.ReferrerAuthenticator
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import scala.collection.JavaConverters._
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiGitReferenceControllerBase extends ControllerBase {
|
trait ApiGitReferenceControllerBase extends ControllerBase {
|
||||||
self: ReferrerAuthenticator =>
|
self: ReferrerAuthenticator =>
|
||||||
|
|
||||||
|
private val logger = LoggerFactory.getLogger(classOf[ApiGitReferenceControllerBase])
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* i. Get a reference
|
* i. Get a reference
|
||||||
* https://developer.github.com/v3/git/refs/#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 =>
|
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
|
val revstr = multiParams("splat").head
|
||||||
using(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||||
val ref = git.getRepository().findRef(revstr)
|
val ref = git.getRepository().findRef(revstr)
|
||||||
|
|
||||||
if (ref != null) {
|
if (ref != null) {
|
||||||
@@ -37,7 +51,8 @@ trait ApiGitReferenceControllerBase extends ControllerBase {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ii. Get all references
|
* ii. Get all references
|
||||||
* https://developer.github.com/v3/git/refs/#get-all-references
|
* https://developer.github.com/v3/git/refs/#get-all-references
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ trait ApiIssueControllerBase extends ControllerBase {
|
|||||||
val condition = IssueSearchCondition(request)
|
val condition = IssueSearchCondition(request)
|
||||||
val baseOwner = getAccountByUserName(repository.owner).get
|
val baseOwner = getAccountByUserName(repository.owner).get
|
||||||
|
|
||||||
val issues: List[(Issue, Account)] =
|
val issues: List[(Issue, Account, Option[Account])] =
|
||||||
searchIssueByApi(
|
searchIssueByApi(
|
||||||
condition = condition,
|
condition = condition,
|
||||||
offset = (page - 1) * PullRequestLimit,
|
offset = (page - 1) * PullRequestLimit,
|
||||||
@@ -40,11 +40,12 @@ trait ApiIssueControllerBase extends ControllerBase {
|
|||||||
)
|
)
|
||||||
|
|
||||||
JsonFormat(issues.map {
|
JsonFormat(issues.map {
|
||||||
case (issue, issueUser) =>
|
case (issue, issueUser, assignedUser) =>
|
||||||
ApiIssue(
|
ApiIssue(
|
||||||
issue = issue,
|
issue = issue,
|
||||||
repositoryName = RepositoryName(repository),
|
repositoryName = RepositoryName(repository),
|
||||||
user = ApiUser(issueUser),
|
user = ApiUser(issueUser),
|
||||||
|
assignee = assignedUser.map(ApiUser(_)),
|
||||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||||
.map(ApiLabel(_, RepositoryName(repository)))
|
.map(ApiLabel(_, RepositoryName(repository)))
|
||||||
)
|
)
|
||||||
@@ -59,13 +60,15 @@ trait ApiIssueControllerBase extends ControllerBase {
|
|||||||
(for {
|
(for {
|
||||||
issueId <- params("id").toIntOpt
|
issueId <- params("id").toIntOpt
|
||||||
issue <- getIssue(repository.owner, repository.name, issueId.toString)
|
issue <- getIssue(repository.owner, repository.name, issueId.toString)
|
||||||
openedUser <- getAccountByUserName(issue.openedUserName)
|
users = getAccountsByUserNames(Set(issue.openedUserName) ++ issue.assignedUserName, Set())
|
||||||
|
openedUser <- users.get(issue.openedUserName)
|
||||||
} yield {
|
} yield {
|
||||||
JsonFormat(
|
JsonFormat(
|
||||||
ApiIssue(
|
ApiIssue(
|
||||||
issue,
|
issue,
|
||||||
RepositoryName(repository),
|
RepositoryName(repository),
|
||||||
ApiUser(openedUser),
|
ApiUser(openedUser),
|
||||||
|
issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
|
||||||
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))
|
getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository)))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -98,6 +101,7 @@ trait ApiIssueControllerBase extends ControllerBase {
|
|||||||
issue,
|
issue,
|
||||||
RepositoryName(repository),
|
RepositoryName(repository),
|
||||||
ApiUser(loginAccount),
|
ApiUser(loginAccount),
|
||||||
|
issue.assignedUserName.flatMap(getAccountByUserName(_)).map(ApiUser(_)),
|
||||||
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||||
.map(ApiLabel(_, RepositoryName(repository)))
|
.map(ApiLabel(_, RepositoryName(repository)))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ import gitbucket.core.service.PullRequestService.PullRequestLimit
|
|||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.scalatra.NoContent
|
import org.scalatra.NoContent
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
trait ApiPullRequestControllerBase extends ControllerBase {
|
trait ApiPullRequestControllerBase extends ControllerBase {
|
||||||
self: AccountService
|
self: AccountService
|
||||||
@@ -114,7 +114,9 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
|||||||
requestBranch = reqBranch,
|
requestBranch = reqBranch,
|
||||||
commitIdFrom = commitIdFrom.getName,
|
commitIdFrom = commitIdFrom.getName,
|
||||||
commitIdTo = commitIdTo.getName,
|
commitIdTo = commitIdTo.getName,
|
||||||
loginAccount = context.loginAccount.get
|
isDraft = createPullReq.draft.getOrElse(false),
|
||||||
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
getApiPullRequest(repository, issueId).map(JsonFormat(_))
|
getApiPullRequest(repository, issueId).map(JsonFormat(_))
|
||||||
case _ =>
|
case _ =>
|
||||||
@@ -141,7 +143,9 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
|||||||
requestBranch = reqBranch,
|
requestBranch = reqBranch,
|
||||||
commitIdFrom = commitIdFrom.getName,
|
commitIdFrom = commitIdFrom.getName,
|
||||||
commitIdTo = commitIdTo.getName,
|
commitIdTo = commitIdTo.getName,
|
||||||
loginAccount = context.loginAccount.get
|
isDraft = false,
|
||||||
|
loginAccount = context.loginAccount.get,
|
||||||
|
settings = context.settings
|
||||||
)
|
)
|
||||||
getApiPullRequest(repository, createPullReqAlt.issue).map(JsonFormat(_))
|
getApiPullRequest(repository, createPullReqAlt.issue).map(JsonFormat(_))
|
||||||
case _ =>
|
case _ =>
|
||||||
@@ -171,7 +175,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
|
|||||||
issueId =>
|
issueId =>
|
||||||
getPullRequest(owner, name, issueId) map {
|
getPullRequest(owner, name, issueId) map {
|
||||||
case (issue, pullreq) =>
|
case (issue, pullreq) =>
|
||||||
using(Git.open(getRepositoryDir(owner, name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
|
||||||
val oldId = git.getRepository.resolve(pullreq.commitIdFrom)
|
val oldId = git.getRepository.resolve(pullreq.commitIdFrom)
|
||||||
val newId = git.getRepository.resolve(pullreq.commitIdTo)
|
val newId = git.getRepository.resolve(pullreq.commitIdTo)
|
||||||
val repoFullName = RepositoryName(repository)
|
val repoFullName = RepositoryName(repository)
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import gitbucket.core.api._
|
|||||||
import gitbucket.core.controller.ControllerBase
|
import gitbucket.core.controller.ControllerBase
|
||||||
import gitbucket.core.service.{AccountService, ProtectedBranchService, RepositoryService}
|
import gitbucket.core.service.{AccountService, ProtectedBranchService, RepositoryService}
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.JGitUtil.getBranches
|
import gitbucket.core.util.JGitUtil.getBranches
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
||||||
self: RepositoryService
|
self: RepositoryService
|
||||||
@@ -25,7 +25,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
* https://developer.github.com/v3/repos/branches/#list-branches
|
* https://developer.github.com/v3/repos/branches/#list-branches
|
||||||
*/
|
*/
|
||||||
get("/api/v3/repos/:owner/:repository/branches")(referrersOnly { repository =>
|
get("/api/v3/repos/:owner/:repository/branches")(referrersOnly { repository =>
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
JsonFormat(
|
JsonFormat(
|
||||||
JGitUtil
|
JGitUtil
|
||||||
.getBranches(
|
.getBranches(
|
||||||
@@ -45,7 +45,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
* https://developer.github.com/v3/repos/branches/#get-branch
|
* https://developer.github.com/v3/repos/branches/#get-branch
|
||||||
*/
|
*/
|
||||||
get("/api/v3/repos/:owner/:repository/branches/*")(referrersOnly { repository =>
|
get("/api/v3/repos/:owner/:repository/branches/*")(referrersOnly { repository =>
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
(for {
|
(for {
|
||||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||||
@@ -214,7 +214,7 @@ trait ApiRepositoryBranchControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
patch("/api/v3/repos/:owner/:repository/branches/*")(ownerOnly { repository =>
|
patch("/api/v3/repos/:owner/:repository/branches/*")(ownerOnly { repository =>
|
||||||
import gitbucket.core.api._
|
import gitbucket.core.api._
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) {
|
||||||
git =>
|
git =>
|
||||||
(for {
|
(for {
|
||||||
branch <- params.get("splat") if repository.branchList.contains(branch)
|
branch <- params.get("splat") if repository.branchList.contains(branch)
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import gitbucket.core.util.Directory.getRepositoryDir
|
|||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
import gitbucket.core.util.{JGitUtil, ReferrerAuthenticator, RepositoryName}
|
import gitbucket.core.util.{JGitUtil, ReferrerAuthenticator, RepositoryName}
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.revwalk.RevWalk
|
import org.eclipse.jgit.revwalk.RevWalk
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
||||||
self: AccountService with CommitsService with ReferrerAuthenticator =>
|
self: AccountService with CommitsService with ReferrerAuthenticator =>
|
||||||
@@ -17,6 +18,33 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
|||||||
* i. List commits on a repository
|
* i. List commits on a repository
|
||||||
* https://developer.github.com/v3/repos/commits/#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
|
* ii. Get a single commit
|
||||||
@@ -27,11 +55,11 @@ trait ApiRepositoryCommitControllerBase extends ControllerBase {
|
|||||||
val name = repository.name
|
val name = repository.name
|
||||||
val sha = params("sha")
|
val sha = params("sha")
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(owner, name))) {
|
Using.resource(Git.open(getRepositoryDir(owner, name))) {
|
||||||
git =>
|
git =>
|
||||||
val repo = git.getRepository
|
val repo = git.getRepository
|
||||||
val objectId = repo.resolve(sha)
|
val objectId = repo.resolve(sha)
|
||||||
val commitInfo = using(new RevWalk(repo)) { revWalk =>
|
val commitInfo = Using.resource(new RevWalk(repo)) { revWalk =>
|
||||||
new CommitInfo(revWalk.parseCommit(objectId))
|
new CommitInfo(revWalk.parseCommit(objectId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import gitbucket.core.service.{RepositoryCommitFileService, RepositoryService}
|
|||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util.JGitUtil.{FileInfo, getContentFromId, getFileList}
|
import gitbucket.core.util.JGitUtil.{FileInfo, getContentFromId, getFileList}
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import gitbucket.core.view.helpers.{isRenderable, renderMarkup}
|
import gitbucket.core.view.helpers.{isRenderable, renderMarkup}
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
||||||
self: ReferrerAuthenticator with WritableUsersAuthenticator with RepositoryCommitFileService =>
|
self: ReferrerAuthenticator with WritableUsersAuthenticator with RepositoryCommitFileService =>
|
||||||
@@ -45,7 +45,7 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
|||||||
getFileList(git, revision, dirName).find(f => f.name.equals(fileName))
|
getFileList(git, revision, dirName).find(f => f.name.equals(fileName))
|
||||||
}
|
}
|
||||||
|
|
||||||
using(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
Using.resource(Git.open(getRepositoryDir(params("owner"), params("repository")))) { git =>
|
||||||
val fileList = getFileList(git, refStr, path)
|
val fileList = getFileList(git, refStr, path)
|
||||||
if (fileList.isEmpty) { // file or NotFound
|
if (fileList.isEmpty) { // file or NotFound
|
||||||
getFileInfo(git, refStr, path)
|
getFileInfo(git, refStr, path)
|
||||||
@@ -113,7 +113,7 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
|||||||
data <- extractFromJsonBody[CreateAFile]
|
data <- extractFromJsonBody[CreateAFile]
|
||||||
} yield {
|
} yield {
|
||||||
val branch = data.branch.getOrElse(repository.repository.defaultBranch)
|
val branch = data.branch.getOrElse(repository.repository.defaultBranch)
|
||||||
val commit = using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
val commit = Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
|
||||||
revCommit.name
|
revCommit.name
|
||||||
}
|
}
|
||||||
@@ -127,17 +127,14 @@ trait ApiRepositoryContentsControllerBase extends ControllerBase {
|
|||||||
branch,
|
branch,
|
||||||
path,
|
path,
|
||||||
Some(paths.last),
|
Some(paths.last),
|
||||||
if (data.sha.isDefined) {
|
data.sha.map(_ => paths.last),
|
||||||
Some(paths.last)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
StringUtil.base64Decode(data.content),
|
StringUtil.base64Decode(data.content),
|
||||||
data.message,
|
data.message,
|
||||||
commit,
|
commit,
|
||||||
context.loginAccount.get,
|
context.loginAccount.get,
|
||||||
data.committer.map(_.name).getOrElse(context.loginAccount.get.fullName),
|
data.committer.map(_.name).getOrElse(context.loginAccount.get.fullName),
|
||||||
data.committer.map(_.email).getOrElse(context.loginAccount.get.mailAddress)
|
data.committer.map(_.email).getOrElse(context.loginAccount.get.mailAddress),
|
||||||
|
context.settings
|
||||||
)
|
)
|
||||||
ApiContents("file", paths.last, path, objectId.name, None, None)(RepositoryName(repository))
|
ApiContents("file", paths.last, path, objectId.name, None, None)(RepositoryName(repository))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import gitbucket.core.servlet.Database
|
|||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
|
|
||||||
import scala.concurrent.Await
|
import scala.concurrent.Await
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait ApiRepositoryControllerBase extends ControllerBase {
|
trait ApiRepositoryControllerBase extends ControllerBase {
|
||||||
self: RepositoryService
|
self: RepositoryService
|
||||||
@@ -193,7 +193,7 @@ trait ApiRepositoryControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
get("/api/v3/repos/:owner/:repository/raw/*")(referrersOnly { repository =>
|
get("/api/v3/repos/:owner/:repository/raw/*")(referrersOnly { repository =>
|
||||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||||
|
|
||||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import gitbucket.core.controller.ControllerBase
|
|||||||
import gitbucket.core.service.{AccountService, RepositoryService}
|
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||||
import gitbucket.core.util.{AdminAuthenticator, UsersAuthenticator}
|
import gitbucket.core.util.{AdminAuthenticator, UsersAuthenticator}
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
|
import gitbucket.core.util.StringUtil._
|
||||||
import org.scalatra.NoContent
|
import org.scalatra.NoContent
|
||||||
|
|
||||||
trait ApiUserControllerBase extends ControllerBase {
|
trait ApiUserControllerBase extends ControllerBase {
|
||||||
@@ -70,7 +71,7 @@ trait ApiUserControllerBase extends ControllerBase {
|
|||||||
} yield {
|
} yield {
|
||||||
val user = createAccount(
|
val user = createAccount(
|
||||||
data.login,
|
data.login,
|
||||||
data.password,
|
pbkdf2_sha256(data.password),
|
||||||
data.fullName.getOrElse(data.login),
|
data.fullName.getOrElse(data.login),
|
||||||
data.email,
|
data.email,
|
||||||
data.isAdmin.getOrElse(false),
|
data.isAdmin.getOrElse(false),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ trait PullRequestComponent extends TemplateComponent { self: Profile =>
|
|||||||
val requestBranch = column[String]("REQUEST_BRANCH")
|
val requestBranch = column[String]("REQUEST_BRANCH")
|
||||||
val commitIdFrom = column[String]("COMMIT_ID_FROM")
|
val commitIdFrom = column[String]("COMMIT_ID_FROM")
|
||||||
val commitIdTo = column[String]("COMMIT_ID_TO")
|
val commitIdTo = column[String]("COMMIT_ID_TO")
|
||||||
|
val isDraft = column[Boolean]("IS_DRAFT")
|
||||||
def * =
|
def * =
|
||||||
(
|
(
|
||||||
userName,
|
userName,
|
||||||
@@ -22,7 +23,8 @@ trait PullRequestComponent extends TemplateComponent { self: Profile =>
|
|||||||
requestRepositoryName,
|
requestRepositoryName,
|
||||||
requestBranch,
|
requestBranch,
|
||||||
commitIdFrom,
|
commitIdFrom,
|
||||||
commitIdTo
|
commitIdTo,
|
||||||
|
isDraft
|
||||||
) <> (PullRequest.tupled, PullRequest.unapply)
|
) <> (PullRequest.tupled, PullRequest.unapply)
|
||||||
|
|
||||||
def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) =
|
def byPrimaryKey(userName: String, repositoryName: String, issueId: Int) =
|
||||||
@@ -41,5 +43,6 @@ case class PullRequest(
|
|||||||
requestRepositoryName: String,
|
requestRepositoryName: String,
|
||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
commitIdFrom: String,
|
commitIdFrom: String,
|
||||||
commitIdTo: String
|
commitIdTo: String,
|
||||||
|
isDraft: Boolean
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import gitbucket.core.controller.{Context, ControllerBase}
|
|||||||
import gitbucket.core.model.{Account, Issue}
|
import gitbucket.core.model.{Account, Issue}
|
||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
import io.github.gitbucket.solidbase.model.Version
|
import io.github.gitbucket.solidbase.model.Version
|
||||||
import org.apache.sshd.server.command.Command
|
import org.apache.sshd.server.command.Command
|
||||||
import play.twirl.api.Html
|
import play.twirl.api.Html
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait for define plugin interface.
|
* Trait for define plugin interface.
|
||||||
@@ -434,7 +434,7 @@ abstract class Plugin {
|
|||||||
* Helper method to get a resource from classpath.
|
* Helper method to get a resource from classpath.
|
||||||
*/
|
*/
|
||||||
protected def fromClassPath(path: String): Array[Byte] =
|
protected def fromClassPath(path: String): Array[Byte] =
|
||||||
using(getClass.getClassLoader.getResourceAsStream(path)) { in =>
|
Using.resource(getClass.getClassLoader.getResourceAsStream(path)) { in =>
|
||||||
val bytes = new Array[Byte](in.available)
|
val bytes = new Array[Byte](in.available)
|
||||||
in.read(bytes)
|
in.read(bytes)
|
||||||
bytes
|
bytes
|
||||||
|
|||||||
@@ -15,19 +15,17 @@ import gitbucket.core.service.ProtectedBranchService.ProtectedBranchReceiveHook
|
|||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.service.SystemSettingsService
|
import gitbucket.core.service.SystemSettingsService
|
||||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.util.DatabaseConfig
|
import gitbucket.core.util.{ConfigUtil, DatabaseConfig}
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.HttpClientUtil._
|
|
||||||
import io.github.gitbucket.solidbase.Solidbase
|
import io.github.gitbucket.solidbase.Solidbase
|
||||||
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
||||||
import io.github.gitbucket.solidbase.model.Module
|
import io.github.gitbucket.solidbase.model.Module
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.apache.http.client.methods.HttpGet
|
|
||||||
import org.apache.sshd.server.command.Command
|
import org.apache.sshd.server.command.Command
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import play.twirl.api.Html
|
import play.twirl.api.Html
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
class PluginRegistry {
|
class PluginRegistry {
|
||||||
|
|
||||||
@@ -227,40 +225,6 @@ object PluginRegistry {
|
|||||||
initialize(context, settings, conn)
|
initialize(context, settings, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Install a plugin from a specified jar file.
|
|
||||||
*/
|
|
||||||
def install(
|
|
||||||
pluginId: String,
|
|
||||||
url: java.net.URL,
|
|
||||||
context: ServletContext,
|
|
||||||
settings: SystemSettings,
|
|
||||||
conn: java.sql.Connection
|
|
||||||
): Unit =
|
|
||||||
synchronized {
|
|
||||||
shutdown(context, settings)
|
|
||||||
|
|
||||||
new File(PluginHome)
|
|
||||||
.listFiles((_: File, name: String) => {
|
|
||||||
name.startsWith(s"gitbucket-${pluginId}-plugin") && name.endsWith(".jar")
|
|
||||||
})
|
|
||||||
.foreach(_.delete())
|
|
||||||
|
|
||||||
withHttpClient(settings.pluginProxy) { httpClient =>
|
|
||||||
val httpGet = new HttpGet(url.toString)
|
|
||||||
try {
|
|
||||||
val response = httpClient.execute(httpGet)
|
|
||||||
val in = response.getEntity.getContent
|
|
||||||
FileUtils.copyToFile(in, new File(PluginHome, new File(url.getFile).getName))
|
|
||||||
} finally {
|
|
||||||
httpGet.releaseConnection()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
instance = new PluginRegistry()
|
|
||||||
initialize(context, settings, conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
private def listPluginJars(dir: File): Seq[File] = {
|
private def listPluginJars(dir: File): Seq[File] = {
|
||||||
dir
|
dir
|
||||||
.listFiles(new FilenameFilter {
|
.listFiles(new FilenameFilter {
|
||||||
@@ -271,7 +235,7 @@ object PluginRegistry {
|
|||||||
.reverse
|
.reverse
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val extraPluginDir: Option[String] = Option(System.getProperty("gitbucket.pluginDir"))
|
lazy val extraPluginDir: Option[String] = ConfigUtil.getConfigValue[String]("gitbucket.pluginDir")
|
||||||
|
|
||||||
def getGitBucketVersion(pluginJarFileName: String): Option[String] = {
|
def getGitBucketVersion(pluginJarFileName: String): Option[String] = {
|
||||||
val regex = ".+-gitbucket\\_(\\d+\\.\\d+\\.\\d+(-SNAPSHOT)?)-.+".r
|
val regex = ".+-gitbucket\\_(\\d+\\.\\d+\\.\\d+(-SNAPSHOT)?)-.+".r
|
||||||
@@ -449,7 +413,6 @@ case class PluginInfo(
|
|||||||
|
|
||||||
class PluginWatchThread(context: ServletContext, dir: String) extends Thread with SystemSettingsService {
|
class PluginWatchThread(context: ServletContext, dir: String) extends Thread with SystemSettingsService {
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread])
|
private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread])
|
||||||
|
|
||||||
@@ -479,7 +442,7 @@ class PluginWatchThread(context: ServletContext, dir: String) extends Thread wit
|
|||||||
}
|
}
|
||||||
if (events.nonEmpty) {
|
if (events.nonEmpty) {
|
||||||
events.foreach { event =>
|
events.foreach { event =>
|
||||||
logger.info(event.kind + ": " + event.context)
|
logger.info(s"${event.kind}: ${event.context}")
|
||||||
}
|
}
|
||||||
new Thread {
|
new Thread {
|
||||||
override def run(): Unit = {
|
override def run(): Unit = {
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
package gitbucket.core.plugin
|
|
||||||
|
|
||||||
import gitbucket.core.controller.Context
|
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import gitbucket.core.util.HttpClientUtil._
|
|
||||||
import org.json4s._
|
|
||||||
import org.apache.commons.io.IOUtils
|
|
||||||
|
|
||||||
import org.apache.http.client.methods.HttpGet
|
|
||||||
import org.slf4j.LoggerFactory
|
|
||||||
|
|
||||||
object PluginRepository {
|
|
||||||
private val logger = LoggerFactory.getLogger(getClass)
|
|
||||||
implicit val formats = DefaultFormats
|
|
||||||
|
|
||||||
def parsePluginJson(json: String): Seq[PluginMetadata] = {
|
|
||||||
org.json4s.jackson.JsonMethods.parse(json).extract[Seq[PluginMetadata]]
|
|
||||||
}
|
|
||||||
|
|
||||||
def getPlugins()(implicit context: Context): Seq[PluginMetadata] = {
|
|
||||||
try {
|
|
||||||
val url = new java.net.URL("https://plugins.gitbucket-community.org/releases/plugins.json")
|
|
||||||
|
|
||||||
withHttpClient(context.settings.pluginProxy) { httpClient =>
|
|
||||||
val httpGet = new HttpGet(url.toString)
|
|
||||||
try {
|
|
||||||
val response = httpClient.execute(httpGet)
|
|
||||||
using(response.getEntity.getContent) { in =>
|
|
||||||
val str = IOUtils.toString(in, "UTF-8")
|
|
||||||
parsePluginJson(str)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
httpGet.releaseConnection()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
case t: Throwable =>
|
|
||||||
logger.warn("Failed to access to the plugin repository: " + t.toString)
|
|
||||||
Nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mapped from plugins.json
|
|
||||||
case class PluginMetadata(
|
|
||||||
id: String,
|
|
||||||
name: String,
|
|
||||||
description: String,
|
|
||||||
versions: Seq[VersionDef],
|
|
||||||
default: Boolean = false
|
|
||||||
) {
|
|
||||||
lazy val latestVersion: VersionDef = versions.last
|
|
||||||
}
|
|
||||||
|
|
||||||
case class VersionDef(
|
|
||||||
version: String,
|
|
||||||
url: String,
|
|
||||||
gitbucketVersion: String
|
|
||||||
)
|
|
||||||
@@ -131,6 +131,23 @@ trait ActivityService {
|
|||||||
currentDate
|
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(
|
def recordCommentIssueActivity(
|
||||||
userName: String,
|
userName: String,
|
||||||
repositoryName: String,
|
repositoryName: String,
|
||||||
|
|||||||
@@ -94,7 +94,8 @@ trait CommitsService {
|
|||||||
repository,
|
repository,
|
||||||
issue,
|
issue,
|
||||||
pullRequest,
|
pullRequest,
|
||||||
loginAccount
|
loginAccount,
|
||||||
|
context.settings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
case None =>
|
case None =>
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ package gitbucket.core.service
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
import gitbucket.core.model.GpgKey
|
import gitbucket.core.model.GpgKey
|
||||||
|
|
||||||
import collection.JavaConverters._
|
|
||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import org.bouncycastle.bcpg.ArmoredInputStream
|
import org.bouncycastle.bcpg.ArmoredInputStream
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing
|
import org.bouncycastle.openpgp.PGPPublicKeyRing
|
||||||
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory
|
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory
|
||||||
|
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
trait GpgKeyService {
|
trait GpgKeyService {
|
||||||
def getGpgPublicKeys(userName: String)(implicit s: Session): List[GpgKey] =
|
def getGpgPublicKeys(userName: String)(implicit s: Session): List[GpgKey] =
|
||||||
GpgKeys.filter(_.userName === userName.bind).sortBy(_.gpgKeyId).list
|
GpgKeys.filter(_.userName === userName.bind).sortBy(_.gpgKeyId).list
|
||||||
|
|||||||
@@ -39,7 +39,10 @@ trait HandleCommentService {
|
|||||||
))
|
))
|
||||||
case "reopen" if (issue.closed) =>
|
case "reopen" if (issue.closed) =>
|
||||||
false ->
|
false ->
|
||||||
(Some("reopen") -> Some(recordReopenIssueActivity _))
|
(Some("reopen") -> Some(
|
||||||
|
if (issue.isPullRequest) recordReopenPullRequestActivity _
|
||||||
|
else recordReopenIssueActivity _
|
||||||
|
))
|
||||||
}
|
}
|
||||||
.map {
|
.map {
|
||||||
case (closed, t) =>
|
case (closed, t) =>
|
||||||
@@ -80,16 +83,17 @@ trait HandleCommentService {
|
|||||||
|
|
||||||
// call web hooks
|
// call web hooks
|
||||||
action match {
|
action match {
|
||||||
case None => commentId foreach (callIssueCommentWebHook(repository, issue, _, loginAccount))
|
case None =>
|
||||||
|
commentId foreach (callIssueCommentWebHook(repository, issue, _, loginAccount, context.settings))
|
||||||
case Some(act) =>
|
case Some(act) =>
|
||||||
val webHookAction = act match {
|
val webHookAction = act match {
|
||||||
case "close" => "closed"
|
case "close" => "closed"
|
||||||
case "reopen" => "reopened"
|
case "reopen" => "reopened"
|
||||||
}
|
}
|
||||||
if (issue.isPullRequest)
|
if (issue.isPullRequest)
|
||||||
callPullRequestWebHook(webHookAction, repository, issue.issueId, loginAccount)
|
callPullRequestWebHook(webHookAction, repository, issue.issueId, loginAccount, context.settings)
|
||||||
else
|
else
|
||||||
callIssuesWebHook(webHookAction, repository, issue, loginAccount)
|
callIssuesWebHook(webHookAction, repository, issue, loginAccount, context.settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
// call hooks
|
// call hooks
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ trait IssueCreationService {
|
|||||||
createReferComment(owner, name, issue, title + " " + body.getOrElse(""), loginAccount)
|
createReferComment(owner, name, issue, title + " " + body.getOrElse(""), loginAccount)
|
||||||
|
|
||||||
// call web hooks
|
// call web hooks
|
||||||
callIssuesWebHook("opened", repository, issue, loginAccount)
|
callIssuesWebHook("opened", repository, issue, loginAccount, context.settings)
|
||||||
|
|
||||||
// call hooks
|
// call hooks
|
||||||
PluginRegistry().getIssueHooks.foreach(_.created(issue, repository))
|
PluginRegistry().getIssueHooks.foreach(_.created(issue, repository))
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ trait IssuesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the search result against issues.
|
* Returns the search result against issues.
|
||||||
*
|
*
|
||||||
* @param condition the search condition
|
* @param condition the search condition
|
||||||
* @param pullRequest if true then returns only pull requests, false then returns only issues.
|
* @param pullRequest if true then returns only pull requests, false then returns only issues.
|
||||||
@@ -265,17 +265,19 @@ trait IssuesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** for api
|
/** for api
|
||||||
* @return (issue, issueUser, commentCount)
|
* @return (issue, issueUser, assignedUser)
|
||||||
*/
|
*/
|
||||||
def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(
|
def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(
|
||||||
implicit s: Session
|
implicit s: Session
|
||||||
): List[(Issue, Account)] = {
|
): List[(Issue, Account, Option[Account])] = {
|
||||||
// get issues and comment count and labels
|
// get issues and comment count and labels
|
||||||
searchIssueQueryBase(condition, false, offset, limit, repos)
|
searchIssueQueryBase(condition, false, offset, limit, repos)
|
||||||
.join(Accounts)
|
.join(Accounts)
|
||||||
.on { case t1 ~ t2 ~ i ~ t3 => t3.userName === t1.openedUserName }
|
.on { case t1 ~ t2 ~ i ~ t3 => t3.userName === t1.openedUserName }
|
||||||
.sortBy { case t1 ~ t2 ~ i ~ t3 => i asc }
|
.joinLeft(Accounts)
|
||||||
.map { case t1 ~ t2 ~ i ~ t3 => (t1, t3) }
|
.on { case t1 ~ t2 ~ i ~ t3 ~ t4 => t4.userName === t1.assignedUserName }
|
||||||
|
.sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 => i asc }
|
||||||
|
.map { case t1 ~ t2 ~ i ~ t3 ~ t4 => (t1, t3, t4) }
|
||||||
.list
|
.list
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,7 +754,7 @@ trait IssuesService {
|
|||||||
implicit s: Session
|
implicit s: Session
|
||||||
): Unit = {
|
): Unit = {
|
||||||
extractIssueId(message).foreach { issueId =>
|
extractIssueId(message).foreach { issueId =>
|
||||||
val content = fromIssue.issueId + ":" + fromIssue.title
|
val content = s"${fromIssue.issueId}:${fromIssue.title}"
|
||||||
if (getIssue(owner, repository, issueId).isDefined) {
|
if (getIssue(owner, repository, issueId).isDefined) {
|
||||||
// Not add if refer comment already exist.
|
// Not add if refer comment already exist.
|
||||||
if (!getComments(owner, repository, issueId.toInt).exists { x =>
|
if (!getComments(owner, repository, issueId.toInt).exists { x =>
|
||||||
@@ -908,13 +910,9 @@ object IssuesService {
|
|||||||
param(request, "groups").map(_.split(",").toSet).getOrElse(Set.empty)
|
param(request, "groups").map(_.split(",").toSet).getOrElse(Set.empty)
|
||||||
)
|
)
|
||||||
|
|
||||||
def page(request: HttpServletRequest) =
|
def page(request: HttpServletRequest) = {
|
||||||
try {
|
PaginationHelper.page(param(request, "page"))
|
||||||
val i = param(request, "page").getOrElse("1").toInt
|
}
|
||||||
if (i <= 0) 1 else i
|
|
||||||
} catch {
|
|
||||||
case e: NumberFormatException => 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case class CommitStatusInfo(
|
case class CommitStatusInfo(
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ import gitbucket.core.plugin.PluginRegistry
|
|||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.{JGitUtil, LockUtil}
|
import gitbucket.core.util.{JGitUtil, LockUtil}
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
import gitbucket.core.model.Profile._
|
|
||||||
import gitbucket.core.model.Profile.profile._
|
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import org.eclipse.jgit.merge.{MergeStrategy, Merger, RecursiveMerger}
|
import org.eclipse.jgit.merge.{MergeStrategy, Merger, RecursiveMerger}
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.transport.RefSpec
|
import org.eclipse.jgit.transport.RefSpec
|
||||||
@@ -18,7 +16,8 @@ import org.eclipse.jgit.errors.NoMergeBaseException
|
|||||||
import org.eclipse.jgit.lib.{CommitBuilder, ObjectId, PersonIdent, Repository}
|
import org.eclipse.jgit.lib.{CommitBuilder, ObjectId, PersonIdent, Repository}
|
||||||
import org.eclipse.jgit.revwalk.{RevCommit, RevWalk}
|
import org.eclipse.jgit.revwalk.{RevCommit, RevWalk}
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait MergeService {
|
trait MergeService {
|
||||||
self: AccountService
|
self: AccountService
|
||||||
@@ -35,7 +34,7 @@ trait MergeService {
|
|||||||
* Returns true if conflict will be caused.
|
* Returns true if conflict will be caused.
|
||||||
*/
|
*/
|
||||||
def checkConflict(userName: String, repositoryName: String, branch: String, issueId: Int): Option[String] = {
|
def checkConflict(userName: String, repositoryName: String, branch: String, issueId: Int): Option[String] = {
|
||||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||||
new MergeCacheInfo(git, branch, issueId).checkConflict()
|
new MergeCacheInfo(git, branch, issueId).checkConflict()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,7 +51,7 @@ trait MergeService {
|
|||||||
branch: String,
|
branch: String,
|
||||||
issueId: Int
|
issueId: Int
|
||||||
): Option[Option[String]] = {
|
): Option[Option[String]] = {
|
||||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||||
new MergeCacheInfo(git, branch, issueId).checkConflictCache()
|
new MergeCacheInfo(git, branch, issueId).checkConflictCache()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +98,7 @@ trait MergeService {
|
|||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
issueId: Int
|
issueId: Int
|
||||||
): Unit = {
|
): Unit = {
|
||||||
using(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(userName, repositoryName))) { git =>
|
||||||
git.fetch
|
git.fetch
|
||||||
.setRemote(getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString)
|
.setRemote(getRepositoryDir(requestUserName, requestRepositoryName).toURI.toString)
|
||||||
.setRefSpecs(new RefSpec(s"refs/heads/${requestBranch}:refs/pull/${issueId}/head"))
|
.setRefSpecs(new RefSpec(s"refs/heads/${requestBranch}:refs/pull/${issueId}/head"))
|
||||||
@@ -118,7 +117,7 @@ trait MergeService {
|
|||||||
remoteRepositoryName: String,
|
remoteRepositoryName: String,
|
||||||
remoteBranch: String
|
remoteBranch: String
|
||||||
): Either[String, (ObjectId, ObjectId, ObjectId)] = {
|
): Either[String, (ObjectId, ObjectId, ObjectId)] = {
|
||||||
using(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||||
val remoteRefName = s"refs/heads/${remoteBranch}"
|
val remoteRefName = s"refs/heads/${remoteBranch}"
|
||||||
val tmpRefName = s"refs/remote-temp/${remoteUserName}/${remoteRepositoryName}/${remoteBranch}"
|
val tmpRefName = s"refs/remote-temp/${remoteUserName}/${remoteRepositoryName}/${remoteBranch}"
|
||||||
val refSpec = new RefSpec(s"${remoteRefName}:${tmpRefName}").setForceUpdate(true)
|
val refSpec = new RefSpec(s"${remoteRefName}:${tmpRefName}").setForceUpdate(true)
|
||||||
@@ -169,7 +168,8 @@ trait MergeService {
|
|||||||
remoteBranch: String,
|
remoteBranch: String,
|
||||||
loginAccount: Account,
|
loginAccount: Account,
|
||||||
message: String,
|
message: String,
|
||||||
pullreq: Option[PullRequest]
|
pullreq: Option[PullRequest],
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Option[ObjectId] = {
|
)(implicit s: Session, c: JsonFormat.Context): Option[ObjectId] = {
|
||||||
val localUserName = localRepository.owner
|
val localUserName = localRepository.owner
|
||||||
val localRepositoryName = localRepository.name
|
val localRepositoryName = localRepository.name
|
||||||
@@ -177,7 +177,7 @@ trait MergeService {
|
|||||||
val remoteRepositoryName = remoteRepository.name
|
val remoteRepositoryName = remoteRepository.name
|
||||||
tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map {
|
tryMergeRemote(localUserName, localRepositoryName, localBranch, remoteUserName, remoteRepositoryName, remoteBranch).map {
|
||||||
case (newTreeId, oldBaseId, oldHeadId) =>
|
case (newTreeId, oldBaseId, oldHeadId) =>
|
||||||
using(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(localUserName, localRepositoryName))) { git =>
|
||||||
val existIds = JGitUtil.getAllCommitIds(git).toSet
|
val existIds = JGitUtil.getAllCommitIds(git).toSet
|
||||||
|
|
||||||
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
val committer = new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||||
@@ -214,7 +214,7 @@ trait MergeService {
|
|||||||
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
|
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, localUserName, localRepositoryName)
|
||||||
.foreach { issueId =>
|
.foreach { issueId =>
|
||||||
getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
|
getIssue(localRepository.owner, localRepository.name, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", localRepository, issue, loginAccount)
|
callIssuesWebHook("closed", localRepository, issue, loginAccount, settings)
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(
|
.foreach(
|
||||||
_.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
|
_.closedByCommitComment(issue, localRepository, commit.fullMessage, loginAccount)
|
||||||
@@ -225,7 +225,7 @@ trait MergeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pullreq.foreach { pullreq =>
|
pullreq.foreach { pullreq =>
|
||||||
callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push) {
|
callWebHookOf(localRepository.owner, localRepository.name, WebHook.Push, settings) {
|
||||||
for {
|
for {
|
||||||
ownerAccount <- getAccountByUserName(localRepository.owner)
|
ownerAccount <- getAccountByUserName(localRepository.owner)
|
||||||
} yield {
|
} yield {
|
||||||
@@ -252,135 +252,147 @@ trait MergeService {
|
|||||||
issueId: Int,
|
issueId: Int,
|
||||||
loginAccount: Account,
|
loginAccount: Account,
|
||||||
message: String,
|
message: String,
|
||||||
strategy: String
|
strategy: String,
|
||||||
|
isDraft: Boolean,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context, context: Context): Either[String, ObjectId] = {
|
)(implicit s: Session, c: JsonFormat.Context, context: Context): Either[String, ObjectId] = {
|
||||||
if (repository.repository.options.mergeOptions.split(",").contains(strategy)) {
|
if (!isDraft) {
|
||||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
if (repository.repository.options.mergeOptions.split(",").contains(strategy)) {
|
||||||
getPullRequest(repository.owner, repository.name, issueId)
|
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||||
.map {
|
getPullRequest(repository.owner, repository.name, issueId)
|
||||||
case (issue, pullreq) =>
|
.map {
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
case (issue, pullreq) =>
|
||||||
// mark issue as merged and close.
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val commentId =
|
// mark issue as merged and close.
|
||||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, message, "merge")
|
val commentId =
|
||||||
createComment(repository.owner, repository.name, loginAccount.userName, issueId, "Close", "close")
|
createComment(repository.owner, repository.name, loginAccount.userName, issueId, message, "merge")
|
||||||
updateClosed(repository.owner, repository.name, issueId, true)
|
createComment(repository.owner, repository.name, loginAccount.userName, issueId, "Close", "close")
|
||||||
|
updateClosed(repository.owner, repository.name, issueId, true)
|
||||||
|
|
||||||
// record activity
|
// record activity
|
||||||
recordMergeActivity(repository.owner, repository.name, loginAccount.userName, issueId, message)
|
recordMergeActivity(repository.owner, repository.name, loginAccount.userName, issueId, message)
|
||||||
|
|
||||||
val (commits, _) = getRequestCompareInfo(
|
val (commits, _) = getRequestCompareInfo(
|
||||||
repository.owner,
|
repository.owner,
|
||||||
repository.name,
|
repository.name,
|
||||||
pullreq.commitIdFrom,
|
pullreq.commitIdFrom,
|
||||||
pullreq.requestUserName,
|
pullreq.requestUserName,
|
||||||
pullreq.requestRepositoryName,
|
pullreq.requestRepositoryName,
|
||||||
pullreq.commitIdTo
|
pullreq.commitIdTo
|
||||||
)
|
)
|
||||||
|
|
||||||
val revCommits = using(new RevWalk(git.getRepository)) { revWalk =>
|
val revCommits = Using
|
||||||
commits.flatten.map { commit =>
|
.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
revWalk.parseCommit(git.getRepository.resolve(commit.id))
|
commits.flatten.map { commit =>
|
||||||
}
|
revWalk.parseCommit(git.getRepository.resolve(commit.id))
|
||||||
}.reverse
|
}
|
||||||
|
}
|
||||||
|
.reverse
|
||||||
|
|
||||||
// merge git repository
|
// merge git repository
|
||||||
(strategy match {
|
(strategy match {
|
||||||
case "merge-commit" =>
|
case "merge-commit" =>
|
||||||
Some(
|
Some(
|
||||||
mergePullRequest(
|
mergePullRequest(
|
||||||
git,
|
git,
|
||||||
pullreq.branch,
|
pullreq.branch,
|
||||||
issueId,
|
issueId,
|
||||||
s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestBranch}\n\n" + message,
|
s"Merge pull request #${issueId} from ${pullreq.requestUserName}/${pullreq.requestBranch}\n\n" + message,
|
||||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
case "rebase" =>
|
||||||
case "rebase" =>
|
Some(
|
||||||
Some(
|
rebasePullRequest(
|
||||||
rebasePullRequest(
|
git,
|
||||||
git,
|
pullreq.branch,
|
||||||
pullreq.branch,
|
issueId,
|
||||||
issueId,
|
revCommits,
|
||||||
revCommits,
|
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
)
|
||||||
)
|
)
|
||||||
)
|
case "squash" =>
|
||||||
case "squash" =>
|
Some(
|
||||||
Some(
|
squashPullRequest(
|
||||||
squashPullRequest(
|
git,
|
||||||
git,
|
pullreq.branch,
|
||||||
pullreq.branch,
|
issueId,
|
||||||
issueId,
|
s"${issue.title} (#${issueId})\n\n" + message,
|
||||||
s"${issue.title} (#${issueId})\n\n" + message,
|
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
||||||
new PersonIdent(loginAccount.fullName, loginAccount.mailAddress)
|
)
|
||||||
)
|
)
|
||||||
)
|
case _ =>
|
||||||
case _ =>
|
None
|
||||||
None
|
}) match {
|
||||||
}) match {
|
case Some(newCommitId) =>
|
||||||
case Some(newCommitId) =>
|
// close issue by content of pull request
|
||||||
// close issue by content of pull request
|
val defaultBranch = getRepository(repository.owner, repository.name).get.repository.defaultBranch
|
||||||
val defaultBranch = getRepository(repository.owner, repository.name).get.repository.defaultBranch
|
if (pullreq.branch == defaultBranch) {
|
||||||
if (pullreq.branch == defaultBranch) {
|
commits.flatten.foreach { commit =>
|
||||||
commits.flatten.foreach { commit =>
|
closeIssuesFromMessage(
|
||||||
|
commit.fullMessage,
|
||||||
|
loginAccount.userName,
|
||||||
|
repository.owner,
|
||||||
|
repository.name
|
||||||
|
).foreach { issueId =>
|
||||||
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
|
callIssuesWebHook("closed", repository, issue, loginAccount, context.settings)
|
||||||
|
PluginRegistry().getIssueHooks
|
||||||
|
.foreach(_.closedByCommitComment(issue, repository, commit.fullMessage, loginAccount))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val issueContent = issue.title + " " + issue.content.getOrElse("")
|
||||||
closeIssuesFromMessage(
|
closeIssuesFromMessage(
|
||||||
commit.fullMessage,
|
issueContent,
|
||||||
loginAccount.userName,
|
loginAccount.userName,
|
||||||
repository.owner,
|
repository.owner,
|
||||||
repository.name
|
repository.name
|
||||||
).foreach { issueId =>
|
).foreach { issueId =>
|
||||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
callIssuesWebHook("closed", repository, issue, loginAccount, context.settings)
|
||||||
PluginRegistry().getIssueHooks
|
|
||||||
.foreach(_.closedByCommitComment(issue, repository, commit.fullMessage, loginAccount))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val issueContent = issue.title + " " + issue.content.getOrElse("")
|
|
||||||
closeIssuesFromMessage(
|
|
||||||
issueContent,
|
|
||||||
loginAccount.userName,
|
|
||||||
repository.owner,
|
|
||||||
repository.name
|
|
||||||
).foreach { issueId =>
|
|
||||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
|
||||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
|
||||||
PluginRegistry().getIssueHooks
|
|
||||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name)
|
|
||||||
.foreach { issueId =>
|
|
||||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
|
||||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name)
|
||||||
|
.foreach { issueId =>
|
||||||
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
|
callIssuesWebHook("closed", repository, issue, loginAccount, context.settings)
|
||||||
|
PluginRegistry().getIssueHooks
|
||||||
|
.foreach(_.closedByCommitComment(issue, repository, issueContent, loginAccount))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
callPullRequestWebHook("closed", repository, issueId, context.loginAccount.get)
|
callPullRequestWebHook("closed", repository, issueId, context.loginAccount.get, context.settings)
|
||||||
|
|
||||||
updatePullRequests(repository.owner, repository.name, pullreq.branch, loginAccount, "closed")
|
updatePullRequests(
|
||||||
|
repository.owner,
|
||||||
|
repository.name,
|
||||||
|
pullreq.branch,
|
||||||
|
loginAccount,
|
||||||
|
"closed",
|
||||||
|
settings
|
||||||
|
)
|
||||||
|
|
||||||
// call hooks
|
// call hooks
|
||||||
PluginRegistry().getPullRequestHooks.foreach { h =>
|
PluginRegistry().getPullRequestHooks.foreach { h =>
|
||||||
h.addedComment(commentId, message, issue, repository)
|
h.addedComment(commentId, message, issue, repository)
|
||||||
h.merged(issue, repository)
|
h.merged(issue, repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
Right(newCommitId)
|
Right(newCommitId)
|
||||||
case None =>
|
case None =>
|
||||||
Left("Unknown strategy")
|
Left("Unknown strategy")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
case _ => Left("Unknown error")
|
||||||
case _ => Left("Unknown error")
|
}
|
||||||
}
|
.getOrElse(Left("Pull request not found"))
|
||||||
.getOrElse(Left("Pull request not found"))
|
}
|
||||||
}
|
} else Left("Strategy not allowed")
|
||||||
} else Left("Strategy not allowed")
|
} else Left("Draft pull requests cannot be merged")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +414,7 @@ object MergeService {
|
|||||||
mergeCommit.setCommitter(committer)
|
mergeCommit.setCommitter(committer)
|
||||||
mergeCommit.setMessage(message)
|
mergeCommit.setMessage(message)
|
||||||
// insertObject and got mergeCommit Object Id
|
// insertObject and got mergeCommit Object Id
|
||||||
using(repository.newObjectInserter) { inserter =>
|
Using.resource(repository.newObjectInserter) { inserter =>
|
||||||
val mergeCommitId = inserter.insert(mergeCommit)
|
val mergeCommitId = inserter.insert(mergeCommit)
|
||||||
inserter.flush()
|
inserter.flush()
|
||||||
mergeCommitId
|
mergeCommitId
|
||||||
@@ -470,7 +482,7 @@ object MergeService {
|
|||||||
} catch {
|
} catch {
|
||||||
case e: NoMergeBaseException => true
|
case e: NoMergeBaseException => true
|
||||||
}
|
}
|
||||||
val mergeTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeTip))
|
val mergeTipCommit = Using.resource(new RevWalk(repository))(_.parseCommit(mergeTip))
|
||||||
val committer = mergeTipCommit.getCommitterIdent
|
val committer = mergeTipCommit.getCommitterIdent
|
||||||
|
|
||||||
def _updateBranch(treeId: ObjectId, message: String, branchName: String): Unit = {
|
def _updateBranch(treeId: ObjectId, message: String, branchName: String): Unit = {
|
||||||
@@ -523,10 +535,10 @@ object MergeService {
|
|||||||
newCommit
|
newCommit
|
||||||
}
|
}
|
||||||
|
|
||||||
val mergeBaseTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
val mergeBaseTipCommit = Using.resource(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||||
var previousId = mergeBaseTipCommit.getId
|
var previousId = mergeBaseTipCommit.getId
|
||||||
|
|
||||||
using(repository.newObjectInserter) { inserter =>
|
Using.resource(repository.newObjectInserter) { inserter =>
|
||||||
commits.foreach { commit =>
|
commits.foreach { commit =>
|
||||||
val nextCommit = _cloneCommit(commit, previousId, mergeBaseTipCommit.getId)
|
val nextCommit = _cloneCommit(commit, previousId, mergeBaseTipCommit.getId)
|
||||||
previousId = inserter.insert(nextCommit)
|
previousId = inserter.insert(nextCommit)
|
||||||
@@ -542,8 +554,9 @@ object MergeService {
|
|||||||
throw new RuntimeException("This pull request can't merge automatically.")
|
throw new RuntimeException("This pull request can't merge automatically.")
|
||||||
}
|
}
|
||||||
|
|
||||||
val mergeBaseTipCommit = using(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
val mergeBaseTipCommit = Using.resource(new RevWalk(repository))(_.parseCommit(mergeBaseTip))
|
||||||
val mergeBranchHeadCommit = using(new RevWalk(repository))(_.parseCommit(repository.resolve(mergedBranchName)))
|
val mergeBranchHeadCommit =
|
||||||
|
Using.resource(new RevWalk(repository))(_.parseCommit(repository.resolve(mergedBranchName)))
|
||||||
|
|
||||||
// Create squash commit
|
// Create squash commit
|
||||||
val mergeCommit = new CommitBuilder()
|
val mergeCommit = new CommitBuilder()
|
||||||
@@ -554,7 +567,7 @@ object MergeService {
|
|||||||
mergeCommit.setMessage(message)
|
mergeCommit.setMessage(message)
|
||||||
|
|
||||||
// insertObject and got squash commit Object Id
|
// insertObject and got squash commit Object Id
|
||||||
val newCommitId = using(repository.newObjectInserter) { inserter =>
|
val newCommitId = Using.resource(repository.newObjectInserter) { inserter =>
|
||||||
val newCommitId = inserter.insert(mergeCommit)
|
val newCommitId = inserter.insert(mergeCommit)
|
||||||
inserter.flush()
|
inserter.flush()
|
||||||
newCommitId
|
newCommitId
|
||||||
@@ -577,7 +590,7 @@ object MergeService {
|
|||||||
private def createMergeCommit(treeId: ObjectId, committer: PersonIdent, message: String) =
|
private def createMergeCommit(treeId: ObjectId, committer: PersonIdent, message: String) =
|
||||||
Util.createMergeCommit(repository, treeId, committer, message, Seq[ObjectId](mergeBaseTip, mergeTip))
|
Util.createMergeCommit(repository, treeId, committer, message, Seq[ObjectId](mergeBaseTip, mergeTip))
|
||||||
|
|
||||||
private def parseCommit(id: ObjectId) = using(new RevWalk(repository))(_.parseCommit(id))
|
private def parseCommit(id: ObjectId) = Using.resource(new RevWalk(repository))(_.parseCommit(id))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import gitbucket.core.model.Account
|
|||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
import scala.collection.JavaConverters.{asScalaSet, mapAsJavaMap}
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service class for the OpenID Connect authentication.
|
* Service class for the OpenID Connect authentication.
|
||||||
@@ -101,7 +101,7 @@ trait OpenIDConnectService {
|
|||||||
redirectURI: URI
|
redirectURI: URI
|
||||||
): Option[AuthenticationSuccessResponse] =
|
): Option[AuthenticationSuccessResponse] =
|
||||||
try {
|
try {
|
||||||
AuthenticationResponseParser.parse(redirectURI, mapAsJavaMap(params)) match {
|
AuthenticationResponseParser.parse(redirectURI, params.asJava) match {
|
||||||
case response: AuthenticationSuccessResponse =>
|
case response: AuthenticationSuccessResponse =>
|
||||||
if (response.getState == state) {
|
if (response.getState == state) {
|
||||||
Some(response)
|
Some(response)
|
||||||
@@ -207,5 +207,5 @@ object OpenIDConnectService {
|
|||||||
"RSA" -> Family.RSA,
|
"RSA" -> Family.RSA,
|
||||||
"ECDSA" -> Family.EC,
|
"ECDSA" -> Family.EC,
|
||||||
"EdDSA" -> Family.ED
|
"EdDSA" -> Family.ED
|
||||||
).toMap.map { case (name, family) => (name, asScalaSet(family).toSet) }
|
).toMap.map { case (name, family) => (name, family.asScala.toSet) }
|
||||||
}
|
}
|
||||||
|
|||||||
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import gitbucket.core.service.RepositoryService.RepositoryInfo
|
|||||||
import gitbucket.core.api.JsonFormat
|
import gitbucket.core.api.JsonFormat
|
||||||
import gitbucket.core.controller.Context
|
import gitbucket.core.controller.Context
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.JGitUtil
|
import gitbucket.core.util.JGitUtil
|
||||||
@@ -19,7 +19,8 @@ import gitbucket.core.view.helpers
|
|||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.lib.ObjectId
|
import org.eclipse.jgit.lib.ObjectId
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait PullRequestService {
|
trait PullRequestService {
|
||||||
self: IssuesService
|
self: IssuesService
|
||||||
@@ -48,6 +49,14 @@ trait PullRequestService {
|
|||||||
.map(pr => pr.commitIdTo -> pr.commitIdFrom)
|
.map(pr => pr.commitIdTo -> pr.commitIdFrom)
|
||||||
.update((commitIdTo, commitIdFrom))
|
.update((commitIdTo, commitIdFrom))
|
||||||
|
|
||||||
|
def updateDraftToPullRequest(owner: String, repository: String, issueId: Int)(
|
||||||
|
implicit s: Session
|
||||||
|
): Unit =
|
||||||
|
PullRequests
|
||||||
|
.filter(_.byPrimaryKey(owner, repository, issueId))
|
||||||
|
.map(pr => pr.isDraft)
|
||||||
|
.update(false)
|
||||||
|
|
||||||
def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])(
|
def getPullRequestCountGroupByUser(closed: Boolean, owner: Option[String], repository: Option[String])(
|
||||||
implicit s: Session
|
implicit s: Session
|
||||||
): List[PullRequestCount] =
|
): List[PullRequestCount] =
|
||||||
@@ -97,7 +106,9 @@ trait PullRequestService {
|
|||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
commitIdFrom: String,
|
commitIdFrom: String,
|
||||||
commitIdTo: String,
|
commitIdTo: String,
|
||||||
loginAccount: Account
|
isDraft: Boolean,
|
||||||
|
loginAccount: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, context: Context): Unit = {
|
)(implicit s: Session, context: Context): Unit = {
|
||||||
getIssue(originRepository.owner, originRepository.name, issueId.toString).foreach { baseIssue =>
|
getIssue(originRepository.owner, originRepository.name, issueId.toString).foreach { baseIssue =>
|
||||||
PullRequests insert PullRequest(
|
PullRequests insert PullRequest(
|
||||||
@@ -109,7 +120,8 @@ trait PullRequestService {
|
|||||||
requestRepositoryName,
|
requestRepositoryName,
|
||||||
requestBranch,
|
requestBranch,
|
||||||
commitIdFrom,
|
commitIdFrom,
|
||||||
commitIdTo
|
commitIdTo,
|
||||||
|
isDraft
|
||||||
)
|
)
|
||||||
|
|
||||||
// fetch requested branch
|
// fetch requested branch
|
||||||
@@ -132,7 +144,7 @@ trait PullRequestService {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// call web hook
|
// call web hook
|
||||||
callPullRequestWebHook("opened", originRepository, issueId, loginAccount)
|
callPullRequestWebHook("opened", originRepository, issueId, loginAccount, settings)
|
||||||
|
|
||||||
getIssue(originRepository.owner, originRepository.name, issueId.toString) foreach { issue =>
|
getIssue(originRepository.owner, originRepository.name, issueId.toString) foreach { issue =>
|
||||||
// extract references and create refer comment
|
// extract references and create refer comment
|
||||||
@@ -216,7 +228,14 @@ trait PullRequestService {
|
|||||||
/**
|
/**
|
||||||
* Fetch pull request contents into refs/pull/${issueId}/head and update pull request table.
|
* Fetch pull request contents into refs/pull/${issueId}/head and update pull request table.
|
||||||
*/
|
*/
|
||||||
def updatePullRequests(owner: String, repository: String, branch: String, loginAccount: Account, action: String)(
|
def updatePullRequests(
|
||||||
|
owner: String,
|
||||||
|
repository: String,
|
||||||
|
branch: String,
|
||||||
|
loginAccount: Account,
|
||||||
|
action: String,
|
||||||
|
settings: SystemSettings
|
||||||
|
)(
|
||||||
implicit s: Session,
|
implicit s: Session,
|
||||||
c: JsonFormat.Context
|
c: JsonFormat.Context
|
||||||
): Unit = {
|
): Unit = {
|
||||||
@@ -265,7 +284,8 @@ trait PullRequestService {
|
|||||||
action,
|
action,
|
||||||
getRepository(owner, repository).get,
|
getRepository(owner, repository).get,
|
||||||
pullreq.requestBranch,
|
pullreq.requestBranch,
|
||||||
loginAccount
|
loginAccount,
|
||||||
|
settings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -377,7 +397,7 @@ trait PullRequestService {
|
|||||||
requestRepositoryName: String,
|
requestRepositoryName: String,
|
||||||
requestCommitId: String
|
requestCommitId: String
|
||||||
): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) =
|
): (Seq[Seq[CommitInfo]], Seq[DiffInfo]) =
|
||||||
using(
|
Using.resources(
|
||||||
Git.open(getRepositoryDir(userName, repositoryName)),
|
Git.open(getRepositoryDir(userName, repositoryName)),
|
||||||
Git.open(getRepositoryDir(requestUserName, requestRepositoryName))
|
Git.open(getRepositoryDir(requestUserName, requestRepositoryName))
|
||||||
) { (oldGit, newGit) =>
|
) { (oldGit, newGit) =>
|
||||||
@@ -468,7 +488,7 @@ trait PullRequestService {
|
|||||||
originId: String,
|
originId: String,
|
||||||
forkedId: String
|
forkedId: String
|
||||||
): (Option[ObjectId], Option[ObjectId]) = {
|
): (Option[ObjectId], Option[ObjectId]) = {
|
||||||
using(
|
Using.resources(
|
||||||
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
|
||||||
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
|
||||||
) {
|
) {
|
||||||
@@ -534,7 +554,7 @@ object PullRequestService {
|
|||||||
lazy val commitStateSummary: (CommitState, String) = {
|
lazy val commitStateSummary: (CommitState, String) = {
|
||||||
val stateMap = statuses.groupBy(_.state)
|
val stateMap = statuses.groupBy(_.state)
|
||||||
val state = CommitState.combine(stateMap.keySet)
|
val state = CommitState.combine(stateMap.keySet)
|
||||||
val summary = stateMap.map { case (keyState, states) => states.size + " " + keyState.name }.mkString(", ")
|
val summary = stateMap.map { case (keyState, states) => s"${states.size} ${keyState.name}" }.mkString(", ")
|
||||||
state -> summary
|
state -> summary
|
||||||
}
|
}
|
||||||
lazy val statusesAndRequired: List[(CommitStatus, Boolean)] = statuses.map { s =>
|
lazy val statusesAndRequired: List[(CommitStatus, Boolean)] = statuses.map { s =>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package gitbucket.core.service
|
package gitbucket.core.service
|
||||||
|
|
||||||
import gitbucket.core.controller.Context
|
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.profile.blockingApi._
|
||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
import gitbucket.core.model.Profile.dateColumnType
|
import gitbucket.core.model.Profile.dateColumnType
|
||||||
|
import gitbucket.core.util.JGitUtil
|
||||||
|
|
||||||
trait ReleaseService {
|
trait ReleaseService {
|
||||||
self: AccountService with RepositoryService =>
|
self: AccountService with RepositoryService =>
|
||||||
@@ -35,10 +36,9 @@ trait ReleaseService {
|
|||||||
ReleaseAssets.filter(x => x.byTag(owner, repository, tag)).list
|
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
|
implicit s: Session
|
||||||
): Map[ReleaseTag, Seq[ReleaseAsset]] = {
|
): Map[ReleaseTag, Seq[ReleaseAsset]] = {
|
||||||
val releases = getReleases(owner, repository)
|
|
||||||
releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.tag))).toMap
|
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
|
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] = {
|
def getReleases(owner: String, repository: String, tags: Seq[JGitUtil.TagInfo])(
|
||||||
//Releases filter (_.byPrimaryKey(owner, repository, releaseId)) firstOption
|
implicit s: Session
|
||||||
ReleaseTags filter (_.byTag(owner, repository, tag)) firstOption
|
): 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])(
|
def updateRelease(owner: String, repository: String, tag: String, title: String, content: Option[String])(
|
||||||
implicit s: Session
|
implicit s: Session
|
||||||
@@ -107,3 +105,9 @@ trait ReleaseService {
|
|||||||
ReleaseTags filter (_.byPrimaryKey(owner, repository, tag)) delete
|
ReleaseTags filter (_.byPrimaryKey(owner, repository, tag)) delete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object ReleaseService {
|
||||||
|
|
||||||
|
val ReleaseLimit = 10
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ import gitbucket.core.model.{Account, WebHook}
|
|||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import gitbucket.core.plugin.PluginRegistry
|
import gitbucket.core.plugin.PluginRegistry
|
||||||
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.service.WebHookService.WebHookPushPayload
|
import gitbucket.core.service.WebHookService.WebHookPushPayload
|
||||||
import gitbucket.core.util.Directory.getRepositoryDir
|
import gitbucket.core.util.Directory.getRepositoryDir
|
||||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
import gitbucket.core.util.{JGitUtil, LockUtil}
|
import gitbucket.core.util.{JGitUtil, LockUtil}
|
||||||
import gitbucket.core.util.SyntaxSugars.using
|
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||||
import org.eclipse.jgit.lib._
|
import org.eclipse.jgit.lib._
|
||||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||||
|
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait RepositoryCommitFileService {
|
trait RepositoryCommitFileService {
|
||||||
self: AccountService with ActivityService with IssuesService with PullRequestService with WebHookPullRequestService =>
|
self: AccountService with ActivityService with IssuesService with PullRequestService with WebHookPullRequestService =>
|
||||||
import RepositoryCommitFileService._
|
import RepositoryCommitFileService._
|
||||||
@@ -24,12 +26,13 @@ trait RepositoryCommitFileService {
|
|||||||
branch: String,
|
branch: String,
|
||||||
path: String,
|
path: String,
|
||||||
message: String,
|
message: String,
|
||||||
loginAccount: Account
|
loginAccount: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(
|
)(
|
||||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
||||||
)(implicit s: Session, c: JsonFormat.Context) = {
|
)(implicit s: Session, c: JsonFormat.Context) = {
|
||||||
// prepend path to the filename
|
// prepend path to the filename
|
||||||
_commitFile(repository, branch, message, loginAccount, loginAccount.fullName, loginAccount.mailAddress)(f)
|
_commitFile(repository, branch, message, loginAccount, loginAccount.fullName, loginAccount.mailAddress, settings)(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
def commitFile(
|
def commitFile(
|
||||||
@@ -42,7 +45,8 @@ trait RepositoryCommitFileService {
|
|||||||
charset: String,
|
charset: String,
|
||||||
message: String,
|
message: String,
|
||||||
commit: String,
|
commit: String,
|
||||||
loginAccount: Account
|
loginAccount: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
||||||
commitFile(
|
commitFile(
|
||||||
repository,
|
repository,
|
||||||
@@ -55,7 +59,8 @@ trait RepositoryCommitFileService {
|
|||||||
commit,
|
commit,
|
||||||
loginAccount,
|
loginAccount,
|
||||||
loginAccount.fullName,
|
loginAccount.fullName,
|
||||||
loginAccount.mailAddress
|
loginAccount.mailAddress,
|
||||||
|
settings
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +75,8 @@ trait RepositoryCommitFileService {
|
|||||||
commit: String,
|
commit: String,
|
||||||
loginAccount: Account,
|
loginAccount: Account,
|
||||||
fullName: String,
|
fullName: String,
|
||||||
mailAddress: String
|
mailAddress: String,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
||||||
|
|
||||||
val newPath = newFileName.map { newFileName =>
|
val newPath = newFileName.map { newFileName =>
|
||||||
@@ -80,7 +86,7 @@ trait RepositoryCommitFileService {
|
|||||||
if (path.length == 0) oldFileName else s"${path}/${oldFileName}"
|
if (path.length == 0) oldFileName else s"${path}/${oldFileName}"
|
||||||
}
|
}
|
||||||
|
|
||||||
_commitFile(repository, branch, message, loginAccount, fullName, mailAddress) {
|
_commitFile(repository, branch, message, loginAccount, fullName, mailAddress, settings) {
|
||||||
case (git, headTip, builder, inserter) =>
|
case (git, headTip, builder, inserter) =>
|
||||||
if (headTip.getName == commit) {
|
if (headTip.getName == commit) {
|
||||||
val permission = JGitUtil
|
val permission = JGitUtil
|
||||||
@@ -111,13 +117,14 @@ trait RepositoryCommitFileService {
|
|||||||
message: String,
|
message: String,
|
||||||
loginAccount: Account,
|
loginAccount: Account,
|
||||||
committerName: String,
|
committerName: String,
|
||||||
committerMailAddress: String
|
committerMailAddress: String,
|
||||||
|
settings: SystemSettings
|
||||||
)(
|
)(
|
||||||
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit
|
||||||
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
)(implicit s: Session, c: JsonFormat.Context): ObjectId = {
|
||||||
|
|
||||||
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
LockUtil.lock(s"${repository.owner}/${repository.name}") {
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
val builder = DirCache.newInCore.builder()
|
val builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
val headName = s"refs/heads/${branch}"
|
val headName = s"refs/heads/${branch}"
|
||||||
@@ -165,7 +172,7 @@ trait RepositoryCommitFileService {
|
|||||||
refUpdate.update()
|
refUpdate.update()
|
||||||
|
|
||||||
// update pull request
|
// update pull request
|
||||||
updatePullRequests(repository.owner, repository.name, branch, loginAccount, "synchronize")
|
updatePullRequests(repository.owner, repository.name, branch, loginAccount, "synchronize", settings)
|
||||||
|
|
||||||
// record activity
|
// record activity
|
||||||
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||||
@@ -178,7 +185,7 @@ trait RepositoryCommitFileService {
|
|||||||
if (branch == repository.repository.defaultBranch) {
|
if (branch == repository.repository.defaultBranch) {
|
||||||
closeIssuesFromMessage(message, committerName, repository.owner, repository.name).foreach { issueId =>
|
closeIssuesFromMessage(message, committerName, repository.owner, repository.name).foreach { issueId =>
|
||||||
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
getIssue(repository.owner, repository.name, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", repository, issue, loginAccount)
|
callIssuesWebHook("closed", repository, issue, loginAccount, settings)
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(_.closedByCommitComment(issue, repository, message, loginAccount))
|
.foreach(_.closedByCommitComment(issue, repository, message, loginAccount))
|
||||||
}
|
}
|
||||||
@@ -191,7 +198,7 @@ trait RepositoryCommitFileService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.Push) {
|
callWebHookOf(repository.owner, repository.name, WebHook.Push, settings) {
|
||||||
getAccountByUserName(repository.owner).map { ownerAccount =>
|
getAccountByUserName(repository.owner).map { ownerAccount =>
|
||||||
WebHookPushPayload(
|
WebHookPushPayload(
|
||||||
git,
|
git,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import java.nio.file.Files
|
|||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.{FileUtil, JGitUtil, LockUtil}
|
import gitbucket.core.util.{FileUtil, JGitUtil, LockUtil}
|
||||||
import gitbucket.core.model.{Account, Role}
|
import gitbucket.core.model.{Account, Role}
|
||||||
@@ -18,6 +17,7 @@ import org.eclipse.jgit.lib.{Constants, FileMode}
|
|||||||
|
|
||||||
import scala.concurrent.ExecutionContext.Implicits.global
|
import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
object RepositoryCreationService {
|
object RepositoryCreationService {
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ trait RepositoryCreationService {
|
|||||||
JGitUtil.initRepository(gitdir)
|
JGitUtil.initRepository(gitdir)
|
||||||
|
|
||||||
if (initOption == "README" || initOption == "EMPTY_COMMIT") {
|
if (initOption == "README" || initOption == "EMPTY_COMMIT") {
|
||||||
using(Git.open(gitdir)) { git =>
|
Using.resource(Git.open(gitdir)) { git =>
|
||||||
val builder = DirCache.newInCore.builder()
|
val builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
@@ -148,7 +148,7 @@ trait RepositoryCreationService {
|
|||||||
|
|
||||||
copyRepositoryDir.foreach { dir =>
|
copyRepositoryDir.foreach { dir =>
|
||||||
try {
|
try {
|
||||||
using(Git.open(dir)) { git =>
|
Using.resource(Git.open(dir)) { git =>
|
||||||
git.push().setRemote(gitdir.toURI.toString).setPushAll().setPushTags().call()
|
git.push().setRemote(gitdir.toURI.toString).setPushAll().setPushTags().call()
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import gitbucket.core.model.Issue
|
|||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
import gitbucket.core.util.StringUtil
|
import gitbucket.core.util.StringUtil
|
||||||
import Directory._
|
import Directory._
|
||||||
import SyntaxSugars._
|
|
||||||
import org.eclipse.jgit.revwalk.RevWalk
|
import org.eclipse.jgit.revwalk.RevWalk
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk
|
import org.eclipse.jgit.treewalk.TreeWalk
|
||||||
import org.eclipse.jgit.lib.FileMode
|
import org.eclipse.jgit.lib.FileMode
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait RepositorySearchService { self: IssuesService =>
|
trait RepositorySearchService { self: IssuesService =>
|
||||||
import RepositorySearchService._
|
import RepositorySearchService._
|
||||||
@@ -37,12 +37,12 @@ trait RepositorySearchService { self: IssuesService =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
def countFiles(owner: String, repository: String, query: String): Int =
|
def countFiles(owner: String, repository: String, query: String): Int =
|
||||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||||
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||||
}
|
}
|
||||||
|
|
||||||
def searchFiles(owner: String, repository: String, query: String): List[FileSearchResult] =
|
def searchFiles(owner: String, repository: String, query: String): List[FileSearchResult] =
|
||||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||||
if (JGitUtil.isEmpty(git)) {
|
if (JGitUtil.isEmpty(git)) {
|
||||||
Nil
|
Nil
|
||||||
} else {
|
} else {
|
||||||
@@ -57,12 +57,12 @@ trait RepositorySearchService { self: IssuesService =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
def countWikiPages(owner: String, repository: String, query: String): Int =
|
def countWikiPages(owner: String, repository: String, query: String): Int =
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||||
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
if (JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||||
}
|
}
|
||||||
|
|
||||||
def searchWikiPages(owner: String, repository: String, query: String): List[FileSearchResult] =
|
def searchWikiPages(owner: String, repository: String, query: String): List[FileSearchResult] =
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||||
if (JGitUtil.isEmpty(git)) {
|
if (JGitUtil.isEmpty(git)) {
|
||||||
Nil
|
Nil
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.eclipse.jgit.api.Git
|
|||||||
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
import org.eclipse.jgit.dircache.{DirCache, DirCacheBuilder}
|
||||||
import org.eclipse.jgit.lib.{Repository => _, _}
|
import org.eclipse.jgit.lib.{Repository => _, _}
|
||||||
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
import org.eclipse.jgit.transport.{ReceiveCommand, ReceivePack}
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait RepositoryService {
|
trait RepositoryService {
|
||||||
self: AccountService =>
|
self: AccountService =>
|
||||||
@@ -458,6 +459,7 @@ trait RepositoryService {
|
|||||||
/**
|
/**
|
||||||
* Returns the list of visible repositories for the specified user.
|
* Returns the list of visible repositories for the specified user.
|
||||||
* If repositoryUserName is given then filters results by repository owner.
|
* If repositoryUserName is given then filters results by repository owner.
|
||||||
|
* This function is for plugin compatibility.
|
||||||
*
|
*
|
||||||
* @param loginAccount the logged in account
|
* @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 repositoryUserName the repository owner (if None then returns all repositories which are visible for logged in user)
|
||||||
@@ -469,23 +471,42 @@ trait RepositoryService {
|
|||||||
loginAccount: Option[Account],
|
loginAccount: Option[Account],
|
||||||
repositoryUserName: Option[String] = None,
|
repositoryUserName: Option[String] = None,
|
||||||
withoutPhysicalInfo: Boolean = false
|
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] = {
|
)(implicit s: Session): List[RepositoryInfo] = {
|
||||||
(loginAccount match {
|
(loginAccount match {
|
||||||
// for Administrators
|
// for Administrators
|
||||||
case Some(x) if (x.isAdmin) =>
|
case Some(x) if (x.isAdmin && !limit) =>
|
||||||
Repositories
|
Repositories
|
||||||
.join(Accounts)
|
.join(Accounts)
|
||||||
.on(_.userName === _.userName)
|
.on(_.userName === _.userName)
|
||||||
.filter { case (t1, t2) => t2.removed === false.bind }
|
.filter { case (t1, t2) => t2.removed === false.bind }
|
||||||
.map { case (t1, t2) => t1 }
|
.map { case (t1, t2) => t1 }
|
||||||
// for Normal Users
|
// for Normal Users
|
||||||
case Some(x) if (!x.isAdmin) =>
|
case Some(x) if (!x.isAdmin || limit) =>
|
||||||
Repositories
|
Repositories
|
||||||
.join(Accounts)
|
.join(Accounts)
|
||||||
.on(_.userName === _.userName)
|
.on(_.userName === _.userName)
|
||||||
.filter {
|
.filter {
|
||||||
case (t1, t2) =>
|
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)) ||
|
(t1.userName in GroupMembers.filter(_.userName === x.userName.bind).map(_.groupName)) ||
|
||||||
(Collaborators.filter { t3 =>
|
(Collaborators.filter { t3 =>
|
||||||
t3.byRepository(t1.userName, t1.repositoryName) &&
|
t3.byRepository(t1.userName, t1.repositoryName) &&
|
||||||
@@ -763,7 +784,7 @@ trait RepositoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get template file from project root. When didn't find, will lookup default folder.
|
// Get template file from project root. When didn't find, will lookup default folder.
|
||||||
using(Git.open(Directory.getRepositoryDir(repository.owner, repository.name))) { git =>
|
Using.resource(Git.open(Directory.getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||||
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, "."))
|
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, "."))
|
||||||
.orElse {
|
.orElse {
|
||||||
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, ".gitbucket"))
|
choiceTemplate(JGitUtil.getFileList(git, repository.repository.defaultBranch, ".gitbucket"))
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import gitbucket.core.service.SystemSettingsService._
|
|||||||
import gitbucket.core.util.ConfigUtil._
|
import gitbucket.core.util.ConfigUtil._
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
trait SystemSettingsService {
|
trait SystemSettingsService {
|
||||||
|
|
||||||
@@ -20,9 +21,15 @@ trait SystemSettingsService {
|
|||||||
props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString)
|
props.setProperty(AllowAccountRegistration, settings.allowAccountRegistration.toString)
|
||||||
props.setProperty(AllowAnonymousAccess, settings.allowAnonymousAccess.toString)
|
props.setProperty(AllowAnonymousAccess, settings.allowAnonymousAccess.toString)
|
||||||
props.setProperty(IsCreateRepoOptionPublic, settings.isCreateRepoOptionPublic.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(Gravatar, settings.gravatar.toString)
|
||||||
props.setProperty(Notification, settings.notification.toString)
|
props.setProperty(Notification, settings.notification.toString)
|
||||||
settings.activityLogLimit.foreach(x => props.setProperty(ActivityLogLimit, x.toString))
|
settings.activityLogLimit.foreach(x => props.setProperty(ActivityLogLimit, x.toString))
|
||||||
|
props.setProperty(LimitVisibleRepositories, settings.limitVisibleRepositories.toString)
|
||||||
props.setProperty(SshEnabled, settings.ssh.enabled.toString)
|
props.setProperty(SshEnabled, settings.ssh.enabled.toString)
|
||||||
settings.ssh.sshHost.foreach(x => props.setProperty(SshHost, x.trim))
|
settings.ssh.sshHost.foreach(x => props.setProperty(SshHost, x.trim))
|
||||||
settings.ssh.sshPort.foreach(x => props.setProperty(SshPort, x.toString))
|
settings.ssh.sshPort.foreach(x => props.setProperty(SshPort, x.toString))
|
||||||
@@ -68,20 +75,16 @@ trait SystemSettingsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
props.setProperty(SkinName, settings.skinName.toString)
|
props.setProperty(SkinName, settings.skinName.toString)
|
||||||
|
settings.userDefinedCss.foreach(x => props.setProperty(UserDefinedCss, x))
|
||||||
props.setProperty(ShowMailAddress, settings.showMailAddress.toString)
|
props.setProperty(ShowMailAddress, settings.showMailAddress.toString)
|
||||||
props.setProperty(PluginNetworkInstall, settings.pluginNetworkInstall.toString)
|
props.setProperty(WebHookBlockPrivateAddress, settings.webHook.blockPrivateAddress.toString)
|
||||||
settings.pluginProxy.foreach { proxy =>
|
props.setProperty(WebHookWhitelist, settings.webHook.whitelist.mkString("\n"))
|
||||||
props.setProperty(PluginProxyHost, proxy.host)
|
props.setProperty(UploadMaxFileSize, settings.upload.maxFileSize.toString)
|
||||||
props.setProperty(PluginProxyPort, proxy.port.toString)
|
props.setProperty(UploadTimeout, settings.upload.timeout.toString)
|
||||||
proxy.user.foreach { user =>
|
props.setProperty(UploadLargeMaxFileSize, settings.upload.largeMaxFileSize.toString)
|
||||||
props.setProperty(PluginProxyUser, user)
|
props.setProperty(UploadLargeTimeout, settings.upload.largeTimeout.toString)
|
||||||
}
|
|
||||||
proxy.password.foreach { password =>
|
|
||||||
props.setProperty(PluginProxyPassword, password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using(new java.io.FileOutputStream(GitBucketConf)) { out =>
|
Using.resource(new java.io.FileOutputStream(GitBucketConf)) { out =>
|
||||||
props.store(out, null)
|
props.store(out, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +93,7 @@ trait SystemSettingsService {
|
|||||||
def loadSystemSettings(): SystemSettings = {
|
def loadSystemSettings(): SystemSettings = {
|
||||||
defining(new java.util.Properties()) { props =>
|
defining(new java.util.Properties()) { props =>
|
||||||
if (GitBucketConf.exists) {
|
if (GitBucketConf.exists) {
|
||||||
using(new java.io.FileInputStream(GitBucketConf)) { in =>
|
Using.resource(new java.io.FileInputStream(GitBucketConf)) { in =>
|
||||||
props.load(in)
|
props.load(in)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,15 +103,27 @@ trait SystemSettingsService {
|
|||||||
getValue(props, AllowAccountRegistration, false),
|
getValue(props, AllowAccountRegistration, false),
|
||||||
getValue(props, AllowAnonymousAccess, true),
|
getValue(props, AllowAnonymousAccess, true),
|
||||||
getValue(props, IsCreateRepoOptionPublic, 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, Gravatar, false),
|
||||||
getValue(props, Notification, false),
|
getValue(props, Notification, false),
|
||||||
getOptionValue[Int](props, ActivityLogLimit, None),
|
getOptionValue[Int](props, ActivityLogLimit, None),
|
||||||
|
getValue(props, LimitVisibleRepositories, false),
|
||||||
Ssh(
|
Ssh(
|
||||||
getValue(props, SshEnabled, false),
|
getValue(props, SshEnabled, false),
|
||||||
getOptionValue[String](props, SshHost, None).map(_.trim),
|
getOptionValue[String](props, SshHost, None).map(_.trim),
|
||||||
getOptionValue(props, SshPort, Some(DefaultSshPort))
|
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))) {
|
if (getValue(props, UseSMTP, getValue(props, Notification, false))) {
|
||||||
Some(
|
Some(
|
||||||
Smtp(
|
Smtp(
|
||||||
@@ -156,18 +171,15 @@ trait SystemSettingsService {
|
|||||||
None
|
None
|
||||||
},
|
},
|
||||||
getValue(props, SkinName, "skin-blue"),
|
getValue(props, SkinName, "skin-blue"),
|
||||||
|
getOptionValue(props, UserDefinedCss, None),
|
||||||
getValue(props, ShowMailAddress, false),
|
getValue(props, ShowMailAddress, false),
|
||||||
getValue(props, PluginNetworkInstall, false),
|
WebHook(getValue(props, WebHookBlockPrivateAddress, false), getSeqValue(props, WebHookWhitelist, "")),
|
||||||
if (getValue(props, PluginProxyHost, "").nonEmpty) {
|
Upload(
|
||||||
Some(
|
getValue(props, UploadMaxFileSize, 3 * 1024 * 1024),
|
||||||
Proxy(
|
getValue(props, UploadTimeout, 3 * 10000),
|
||||||
getValue(props, PluginProxyHost, ""),
|
getValue(props, UploadLargeMaxFileSize, 3 * 1024 * 1024),
|
||||||
getValue(props, PluginProxyPort, 8080),
|
getValue(props, UploadLargeTimeout, 3 * 10000)
|
||||||
getOptionValue(props, PluginProxyUser, None),
|
)
|
||||||
getOptionValue(props, PluginProxyPassword, None)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else None
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,9 +197,11 @@ object SystemSettingsService {
|
|||||||
allowAccountRegistration: Boolean,
|
allowAccountRegistration: Boolean,
|
||||||
allowAnonymousAccess: Boolean,
|
allowAnonymousAccess: Boolean,
|
||||||
isCreateRepoOptionPublic: Boolean,
|
isCreateRepoOptionPublic: Boolean,
|
||||||
|
repositoryOperation: RepositoryOperation,
|
||||||
gravatar: Boolean,
|
gravatar: Boolean,
|
||||||
notification: Boolean,
|
notification: Boolean,
|
||||||
activityLogLimit: Option[Int],
|
activityLogLimit: Option[Int],
|
||||||
|
limitVisibleRepositories: Boolean,
|
||||||
ssh: Ssh,
|
ssh: Ssh,
|
||||||
useSMTP: Boolean,
|
useSMTP: Boolean,
|
||||||
smtp: Option[Smtp],
|
smtp: Option[Smtp],
|
||||||
@@ -196,9 +210,10 @@ object SystemSettingsService {
|
|||||||
oidcAuthentication: Boolean,
|
oidcAuthentication: Boolean,
|
||||||
oidc: Option[OIDC],
|
oidc: Option[OIDC],
|
||||||
skinName: String,
|
skinName: String,
|
||||||
|
userDefinedCss: Option[String],
|
||||||
showMailAddress: Boolean,
|
showMailAddress: Boolean,
|
||||||
pluginNetworkInstall: Boolean,
|
webHook: WebHook,
|
||||||
pluginProxy: Option[Proxy]
|
upload: Upload
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def baseUrl(request: HttpServletRequest): String =
|
def baseUrl(request: HttpServletRequest): String =
|
||||||
@@ -217,12 +232,21 @@ object SystemSettingsService {
|
|||||||
.fold(base)(_ + base.dropWhile(_ != ':'))
|
.fold(base)(_ + base.dropWhile(_ != ':'))
|
||||||
}
|
}
|
||||||
|
|
||||||
def sshAddress: Option[SshAddress] = ssh.sshHost.collect {
|
def sshAddress: Option[SshAddress] =
|
||||||
case host if ssh.enabled =>
|
ssh.sshHost.collect {
|
||||||
SshAddress(host, ssh.sshPort.getOrElse(DefaultSshPort), "git")
|
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(
|
case class Ssh(
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
sshHost: Option[String],
|
sshHost: Option[String],
|
||||||
@@ -270,12 +294,14 @@ object SystemSettingsService {
|
|||||||
host: String,
|
host: String,
|
||||||
port: Int,
|
port: Int,
|
||||||
user: Option[String],
|
user: Option[String],
|
||||||
password: Option[String],
|
password: Option[String]
|
||||||
)
|
)
|
||||||
|
|
||||||
case class SshAddress(host: String, port: Int, genericUser: String)
|
case class SshAddress(host: String, port: Int, genericUser: String)
|
||||||
|
|
||||||
case class Lfs(serverUrl: Option[String])
|
case class WebHook(blockPrivateAddress: Boolean, whitelist: Seq[String])
|
||||||
|
|
||||||
|
case class Upload(maxFileSize: Long, timeout: Long, largeMaxFileSize: Long, largeTimeout: Long)
|
||||||
|
|
||||||
val DefaultSshPort = 29418
|
val DefaultSshPort = 29418
|
||||||
val DefaultSmtpPort = 25
|
val DefaultSmtpPort = 25
|
||||||
@@ -286,9 +312,15 @@ object SystemSettingsService {
|
|||||||
private val AllowAccountRegistration = "allow_account_registration"
|
private val AllowAccountRegistration = "allow_account_registration"
|
||||||
private val AllowAnonymousAccess = "allow_anonymous_access"
|
private val AllowAnonymousAccess = "allow_anonymous_access"
|
||||||
private val IsCreateRepoOptionPublic = "is_create_repository_option_public"
|
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 Gravatar = "gravatar"
|
||||||
private val Notification = "notification"
|
private val Notification = "notification"
|
||||||
private val ActivityLogLimit = "activity_log_limit"
|
private val ActivityLogLimit = "activity_log_limit"
|
||||||
|
private val LimitVisibleRepositories = "limitVisibleRepositories"
|
||||||
private val SshEnabled = "ssh"
|
private val SshEnabled = "ssh"
|
||||||
private val SshHost = "ssh.host"
|
private val SshHost = "ssh.host"
|
||||||
private val SshPort = "ssh.port"
|
private val SshPort = "ssh.port"
|
||||||
@@ -320,15 +352,17 @@ object SystemSettingsService {
|
|||||||
private val OidcClientSecret = "oidc.client_secret"
|
private val OidcClientSecret = "oidc.client_secret"
|
||||||
private val OidcJwsAlgorithm = "oidc.jws_algorithm"
|
private val OidcJwsAlgorithm = "oidc.jws_algorithm"
|
||||||
private val SkinName = "skinName"
|
private val SkinName = "skinName"
|
||||||
|
private val UserDefinedCss = "userDefinedCss"
|
||||||
private val ShowMailAddress = "showMailAddress"
|
private val ShowMailAddress = "showMailAddress"
|
||||||
private val PluginNetworkInstall = "plugin.networkInstall"
|
private val WebHookBlockPrivateAddress = "webhook.block_private_address"
|
||||||
private val PluginProxyHost = "plugin.proxy.host"
|
private val WebHookWhitelist = "webhook.whitelist"
|
||||||
private val PluginProxyPort = "plugin.proxy.port"
|
private val UploadMaxFileSize = "upload.maxFileSize"
|
||||||
private val PluginProxyUser = "plugin.proxy.user"
|
private val UploadTimeout = "upload.timeout"
|
||||||
private val PluginProxyPassword = "plugin.proxy.password"
|
private val UploadLargeMaxFileSize = "upload.largeMaxFileSize"
|
||||||
|
private val UploadLargeTimeout = "upload.largeTimeout"
|
||||||
|
|
||||||
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = {
|
private def getValue[A: ClassTag](props: java.util.Properties, key: String, default: A): A = {
|
||||||
getSystemProperty(key).getOrElse(getEnvironmentVariable(key).getOrElse {
|
getConfigValue(key).getOrElse {
|
||||||
defining(props.getProperty(key)) { value =>
|
defining(props.getProperty(key)) { value =>
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
default
|
default
|
||||||
@@ -336,11 +370,21 @@ object SystemSettingsService {
|
|||||||
convertType(value).asInstanceOf[A]
|
convertType(value).asInstanceOf[A]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def getSeqValue[A: ClassTag](props: java.util.Properties, key: String, default: A): Seq[A] = {
|
||||||
|
getValue[String](props, key, "").split("\n").toIndexedSeq.map { value =>
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
default
|
||||||
|
} else {
|
||||||
|
convertType(value).asInstanceOf[A]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def getOptionValue[A: ClassTag](props: java.util.Properties, key: String, default: Option[A]): Option[A] = {
|
private def getOptionValue[A: ClassTag](props: java.util.Properties, key: String, default: Option[A]): Option[A] = {
|
||||||
getSystemProperty(key).orElse(getEnvironmentVariable(key).orElse {
|
getConfigValue(key).orElse {
|
||||||
defining(props.getProperty(key)) { value =>
|
defining(props.getProperty(key)) { value =>
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
default
|
default
|
||||||
@@ -348,7 +392,7 @@ object SystemSettingsService {
|
|||||||
Some(convertType(value)).asInstanceOf[Option[A]]
|
Some(convertType(value)).asInstanceOf[Option[A]]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,24 +3,26 @@ package gitbucket.core.service
|
|||||||
import fr.brouillard.oss.security.xhub.XHub
|
import fr.brouillard.oss.security.xhub.XHub
|
||||||
import fr.brouillard.oss.security.xhub.XHub.{XHubConverter, XHubDigest}
|
import fr.brouillard.oss.security.xhub.XHub.{XHubConverter, XHubDigest}
|
||||||
import gitbucket.core.api._
|
import gitbucket.core.api._
|
||||||
|
import gitbucket.core.controller.Context
|
||||||
import gitbucket.core.model.{
|
import gitbucket.core.model.{
|
||||||
Account,
|
Account,
|
||||||
|
AccountWebHook,
|
||||||
|
AccountWebHookEvent,
|
||||||
CommitComment,
|
CommitComment,
|
||||||
Issue,
|
Issue,
|
||||||
IssueComment,
|
IssueComment,
|
||||||
Label,
|
Label,
|
||||||
PullRequest,
|
PullRequest,
|
||||||
WebHook,
|
|
||||||
RepositoryWebHook,
|
RepositoryWebHook,
|
||||||
RepositoryWebHookEvent,
|
RepositoryWebHookEvent,
|
||||||
AccountWebHook,
|
WebHook
|
||||||
AccountWebHookEvent
|
|
||||||
}
|
}
|
||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import org.apache.http.client.utils.URLEncodedUtils
|
import org.apache.http.client.utils.URLEncodedUtils
|
||||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||||
import gitbucket.core.util.{RepositoryName, StringUtil}
|
import gitbucket.core.util.Implicits._
|
||||||
|
import gitbucket.core.util.{HttpClientUtil, RepositoryName, StringUtil}
|
||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import org.apache.http.NameValuePair
|
import org.apache.http.NameValuePair
|
||||||
import org.apache.http.client.entity.UrlEncodedFormEntity
|
import org.apache.http.client.entity.UrlEncodedFormEntity
|
||||||
@@ -34,6 +36,7 @@ import scala.util.{Failure, Success}
|
|||||||
import org.apache.http.HttpRequest
|
import org.apache.http.HttpRequest
|
||||||
import org.apache.http.HttpResponse
|
import org.apache.http.HttpResponse
|
||||||
import gitbucket.core.model.WebHookContentType
|
import gitbucket.core.model.WebHookContentType
|
||||||
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import org.apache.http.client.entity.EntityBuilder
|
import org.apache.http.client.entity.EntityBuilder
|
||||||
import org.apache.http.entity.ContentType
|
import org.apache.http.entity.ContentType
|
||||||
|
|
||||||
@@ -55,6 +58,7 @@ trait WebHookService {
|
|||||||
.map { case (w, t) => w -> t.event }
|
.map { case (w, t) => w -> t.event }
|
||||||
.list
|
.list
|
||||||
.groupBy(_._1)
|
.groupBy(_._1)
|
||||||
|
.view
|
||||||
.mapValues(_.map(_._2).toSet)
|
.mapValues(_.map(_._2).toSet)
|
||||||
.toList
|
.toList
|
||||||
.sortBy(_._1.url)
|
.sortBy(_._1.url)
|
||||||
@@ -87,6 +91,7 @@ trait WebHookService {
|
|||||||
.map { case (w, t) => w -> t.event }
|
.map { case (w, t) => w -> t.event }
|
||||||
.list
|
.list
|
||||||
.groupBy(_._1)
|
.groupBy(_._1)
|
||||||
|
.view
|
||||||
.mapValues(_.map(_._2).toSet)
|
.mapValues(_.map(_._2).toSet)
|
||||||
.headOption
|
.headOption
|
||||||
|
|
||||||
@@ -136,6 +141,7 @@ trait WebHookService {
|
|||||||
.map { case (w, t) => w -> t.event }
|
.map { case (w, t) => w -> t.event }
|
||||||
.list
|
.list
|
||||||
.groupBy(_._1)
|
.groupBy(_._1)
|
||||||
|
.view
|
||||||
.mapValues(_.map(_._2).toSet)
|
.mapValues(_.map(_._2).toSet)
|
||||||
.toList
|
.toList
|
||||||
.sortBy(_._1.url)
|
.sortBy(_._1.url)
|
||||||
@@ -164,6 +170,7 @@ trait WebHookService {
|
|||||||
.map { case (w, t) => w -> t.event }
|
.map { case (w, t) => w -> t.event }
|
||||||
.list
|
.list
|
||||||
.groupBy(_._1)
|
.groupBy(_._1)
|
||||||
|
.view
|
||||||
.mapValues(_.map(_._2).toSet)
|
.mapValues(_.map(_._2).toSet)
|
||||||
.headOption
|
.headOption
|
||||||
|
|
||||||
@@ -197,20 +204,28 @@ trait WebHookService {
|
|||||||
def deleteAccountWebHook(owner: String, url: String)(implicit s: Session): Unit =
|
def deleteAccountWebHook(owner: String, url: String)(implicit s: Session): Unit =
|
||||||
AccountWebHooks.filter(_.byPrimaryKey(owner, url)).delete
|
AccountWebHooks.filter(_.byPrimaryKey(owner, url)).delete
|
||||||
|
|
||||||
def callWebHookOf(owner: String, repository: String, event: WebHook.Event)(
|
def callWebHookOf(owner: String, repository: String, event: WebHook.Event, settings: SystemSettings)(
|
||||||
makePayload: => Option[WebHookPayload]
|
makePayload: => Option[WebHookPayload]
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
val webHooks = getWebHooksByEvent(owner, repository, event)
|
val webHooks = getWebHooksByEvent(owner, repository, event)
|
||||||
if (webHooks.nonEmpty) {
|
if (webHooks.nonEmpty) {
|
||||||
makePayload.map(callWebHook(event, webHooks, _))
|
makePayload.map(callWebHook(event, webHooks, _, settings))
|
||||||
}
|
}
|
||||||
val accountWebHooks = getAccountWebHooksByEvent(owner, event)
|
val accountWebHooks = getAccountWebHooksByEvent(owner, event)
|
||||||
if (accountWebHooks.nonEmpty) {
|
if (accountWebHooks.nonEmpty) {
|
||||||
makePayload.map(callWebHook(event, accountWebHooks, _))
|
makePayload.map(callWebHook(event, accountWebHooks, _, settings))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def callWebHook(event: WebHook.Event, webHooks: List[WebHook], payload: WebHookPayload)(
|
private def validateTargetAddress(settings: SystemSettings, url: String): Boolean = {
|
||||||
|
val host = new java.net.URL(url).getHost
|
||||||
|
|
||||||
|
!settings.webHook.blockPrivateAddress ||
|
||||||
|
!HttpClientUtil.isPrivateAddress(host) ||
|
||||||
|
settings.webHook.whitelist.exists(range => HttpClientUtil.inIpRange(range, host))
|
||||||
|
}
|
||||||
|
|
||||||
|
def callWebHook(event: WebHook.Event, webHooks: List[WebHook], payload: WebHookPayload, settings: SystemSettings)(
|
||||||
implicit c: JsonFormat.Context
|
implicit c: JsonFormat.Context
|
||||||
): List[(WebHook, String, Future[HttpRequest], Future[HttpResponse])] = {
|
): List[(WebHook, String, Future[HttpRequest], Future[HttpResponse])] = {
|
||||||
import org.apache.http.impl.client.HttpClientBuilder
|
import org.apache.http.impl.client.HttpClientBuilder
|
||||||
@@ -230,6 +245,9 @@ trait WebHookService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
if (!validateTargetAddress(settings, webHook.url)) {
|
||||||
|
throw new IllegalArgumentException(s"Illegal address: ${webHook.url}")
|
||||||
|
}
|
||||||
val httpClient = HttpClientBuilder.create.useSystemProperties.addInterceptorLast(itcp).build
|
val httpClient = HttpClientBuilder.create.useSystemProperties.addInterceptorLast(itcp).build
|
||||||
logger.debug(s"start web hook invocation for ${webHook.url}")
|
logger.debug(s"start web hook invocation for ${webHook.url}")
|
||||||
val httpPost = new HttpPost(webHook.url)
|
val httpPost = new HttpPost(webHook.url)
|
||||||
@@ -298,7 +316,6 @@ trait WebHookService {
|
|||||||
} else {
|
} else {
|
||||||
Nil
|
Nil
|
||||||
}
|
}
|
||||||
// logger.debug("end callWebHook")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,10 +328,12 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
action: String,
|
action: String,
|
||||||
repository: RepositoryService.RepositoryInfo,
|
repository: RepositoryService.RepositoryInfo,
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, context: JsonFormat.Context): Unit = {
|
)(implicit s: Session, context: JsonFormat.Context): Unit = {
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.Issues) {
|
callWebHookOf(repository.owner, repository.name, WebHook.Issues, settings) {
|
||||||
val users = getAccountsByUserNames(Set(repository.owner, issue.openedUserName), Set(sender))
|
val users =
|
||||||
|
getAccountsByUserNames(Set(repository.owner, issue.openedUserName) ++ issue.assignedUserName, Set(sender))
|
||||||
for {
|
for {
|
||||||
repoOwner <- users.get(repository.owner)
|
repoOwner <- users.get(repository.owner)
|
||||||
issueUser <- users.get(issue.openedUserName)
|
issueUser <- users.get(issue.openedUserName)
|
||||||
@@ -327,6 +346,7 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
issue,
|
issue,
|
||||||
RepositoryName(repository),
|
RepositoryName(repository),
|
||||||
ApiUser(issueUser),
|
ApiUser(issueUser),
|
||||||
|
issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
|
||||||
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||||
.map(ApiLabel(_, RepositoryName(repository)))
|
.map(ApiLabel(_, RepositoryName(repository)))
|
||||||
),
|
),
|
||||||
@@ -340,10 +360,11 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
action: String,
|
action: String,
|
||||||
repository: RepositoryService.RepositoryInfo,
|
repository: RepositoryService.RepositoryInfo,
|
||||||
issueId: Int,
|
issueId: Int,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
import WebHookService._
|
import WebHookService._
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequest) {
|
callWebHookOf(repository.owner, repository.name, WebHook.PullRequest, settings) {
|
||||||
for {
|
for {
|
||||||
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
|
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
|
||||||
users = getAccountsByUserNames(
|
users = getAccountsByUserNames(
|
||||||
@@ -396,13 +417,14 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
if wht.event === WebHook.PullRequest.asInstanceOf[WebHook.Event].bind && wht.byRepositoryWebHook(wh)
|
if wht.event === WebHook.PullRequest.asInstanceOf[WebHook.Event].bind && wht.byRepositoryWebHook(wh)
|
||||||
} yield {
|
} yield {
|
||||||
((is, iu, pr, bu, ru), wh)
|
((is, iu, pr, bu, ru), wh)
|
||||||
}).list.groupBy(_._1).mapValues(_.map(_._2))
|
}).list.groupBy(_._1).map { case (k, v) => (k, v.map(_._2)) }
|
||||||
|
|
||||||
def callPullRequestWebHookByRequestBranch(
|
def callPullRequestWebHookByRequestBranch(
|
||||||
action: String,
|
action: String,
|
||||||
requestRepository: RepositoryService.RepositoryInfo,
|
requestRepository: RepositoryService.RepositoryInfo,
|
||||||
requestBranch: String,
|
requestBranch: String,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
import WebHookService._
|
import WebHookService._
|
||||||
for {
|
for {
|
||||||
@@ -433,7 +455,7 @@ trait WebHookPullRequestService extends WebHookService {
|
|||||||
mergedComment = getMergedComment(baseRepo.owner, baseRepo.name, issue.issueId)
|
mergedComment = getMergedComment(baseRepo.owner, baseRepo.name, issue.issueId)
|
||||||
)
|
)
|
||||||
|
|
||||||
callWebHook(WebHook.PullRequest, webHooks, payload)
|
callWebHook(WebHook.PullRequest, webHooks, payload, settings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,10 +469,11 @@ trait WebHookPullRequestReviewCommentService extends WebHookService {
|
|||||||
repository: RepositoryService.RepositoryInfo,
|
repository: RepositoryService.RepositoryInfo,
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
pullRequest: PullRequest,
|
pullRequest: PullRequest,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
import WebHookService._
|
import WebHookService._
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment) {
|
callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment, settings) {
|
||||||
val users =
|
val users =
|
||||||
getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
|
getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
|
||||||
for {
|
for {
|
||||||
@@ -492,18 +515,20 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
|
|||||||
repository: RepositoryService.RepositoryInfo,
|
repository: RepositoryService.RepositoryInfo,
|
||||||
issue: Issue,
|
issue: Issue,
|
||||||
issueCommentId: Int,
|
issueCommentId: Int,
|
||||||
sender: Account
|
sender: Account,
|
||||||
|
settings: SystemSettings
|
||||||
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
)(implicit s: Session, c: JsonFormat.Context): Unit = {
|
||||||
callWebHookOf(repository.owner, repository.name, WebHook.IssueComment) {
|
callWebHookOf(repository.owner, repository.name, WebHook.IssueComment, settings) {
|
||||||
for {
|
for {
|
||||||
issueComment <- getComment(repository.owner, repository.name, issueCommentId.toString())
|
issueComment <- getComment(repository.owner, repository.name, issueCommentId.toString())
|
||||||
users = getAccountsByUserNames(
|
users = getAccountsByUserNames(
|
||||||
Set(issue.openedUserName, repository.owner, issueComment.commentedUserName),
|
Set(issue.openedUserName, repository.owner, issueComment.commentedUserName) ++ issue.assignedUserName,
|
||||||
Set(sender)
|
Set(sender)
|
||||||
)
|
)
|
||||||
issueUser <- users.get(issue.openedUserName)
|
issueUser <- users.get(issue.openedUserName)
|
||||||
repoOwner <- users.get(repository.owner)
|
repoOwner <- users.get(repository.owner)
|
||||||
commenter <- users.get(issueComment.commentedUserName)
|
commenter <- users.get(issueComment.commentedUserName)
|
||||||
|
assignedUser = issue.assignedUserName.flatMap(users.get(_))
|
||||||
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
|
||||||
} yield {
|
} yield {
|
||||||
WebHookIssueCommentPayload(
|
WebHookIssueCommentPayload(
|
||||||
@@ -513,6 +538,7 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
|
|||||||
commentUser = commenter,
|
commentUser = commenter,
|
||||||
repository = repository,
|
repository = repository,
|
||||||
repositoryUser = repoOwner,
|
repositoryUser = repoOwner,
|
||||||
|
assignedUser = assignedUser,
|
||||||
sender = sender,
|
sender = sender,
|
||||||
labels = labels
|
labels = labels
|
||||||
)
|
)
|
||||||
@@ -686,6 +712,7 @@ object WebHookService {
|
|||||||
commentUser: Account,
|
commentUser: Account,
|
||||||
repository: RepositoryInfo,
|
repository: RepositoryInfo,
|
||||||
repositoryUser: Account,
|
repositoryUser: Account,
|
||||||
|
assignedUser: Option[Account],
|
||||||
sender: Account,
|
sender: Account,
|
||||||
labels: List[Label]
|
labels: List[Label]
|
||||||
): WebHookIssueCommentPayload =
|
): WebHookIssueCommentPayload =
|
||||||
@@ -696,6 +723,7 @@ object WebHookService {
|
|||||||
issue,
|
issue,
|
||||||
RepositoryName(repository),
|
RepositoryName(repository),
|
||||||
ApiUser(issueUser),
|
ApiUser(issueUser),
|
||||||
|
assignedUser.map(ApiUser(_)),
|
||||||
labels.map(ApiLabel(_, RepositoryName(repository)))
|
labels.map(ApiLabel(_, RepositoryName(repository)))
|
||||||
),
|
),
|
||||||
comment =
|
comment =
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import org.eclipse.jgit.diff.{DiffEntry, DiffFormatter}
|
|||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import org.eclipse.jgit.patch._
|
import org.eclipse.jgit.patch._
|
||||||
import org.eclipse.jgit.api.errors.PatchFormatException
|
import org.eclipse.jgit.api.errors.PatchFormatException
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
object WikiService {
|
object WikiService {
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ trait WikiService {
|
|||||||
* Returns the wiki page.
|
* Returns the wiki page.
|
||||||
*/
|
*/
|
||||||
def getWikiPage(owner: String, repository: String, pageName: String): Option[WikiPageInfo] = {
|
def getWikiPage(owner: String, repository: String, pageName: String): Option[WikiPageInfo] = {
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||||
if (!JGitUtil.isEmpty(git)) {
|
if (!JGitUtil.isEmpty(git)) {
|
||||||
JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file =>
|
JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file =>
|
||||||
WikiPageInfo(
|
WikiPageInfo(
|
||||||
@@ -92,7 +94,7 @@ trait WikiService {
|
|||||||
* Returns the list of wiki page names.
|
* Returns the list of wiki page names.
|
||||||
*/
|
*/
|
||||||
def getWikiPageList(owner: String, repository: String): List[String] = {
|
def getWikiPageList(owner: String, repository: String): List[String] = {
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||||
JGitUtil
|
JGitUtil
|
||||||
.getFileList(git, "master", ".")
|
.getFileList(git, "master", ".")
|
||||||
.filter(_.name.endsWith(".md"))
|
.filter(_.name.endsWith(".md"))
|
||||||
@@ -118,7 +120,7 @@ trait WikiService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||||
val reader = git.getRepository.newObjectReader
|
val reader = git.getRepository.newObjectReader
|
||||||
val oldTreeIter = new CanonicalTreeParser
|
val oldTreeIter = new CanonicalTreeParser
|
||||||
oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}"))
|
oldTreeIter.reset(reader, git.getRepository.resolve(from + "^{tree}"))
|
||||||
@@ -133,7 +135,7 @@ trait WikiService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val patch = using(new java.io.ByteArrayOutputStream()) { out =>
|
val patch = Using.resource(new java.io.ByteArrayOutputStream()) { out =>
|
||||||
val formatter = new DiffFormatter(out)
|
val formatter = new DiffFormatter(out)
|
||||||
formatter.setRepository(git.getRepository)
|
formatter.setRepository(git.getRepository)
|
||||||
formatter.format(diffs.asJava)
|
formatter.format(diffs.asJava)
|
||||||
@@ -237,7 +239,7 @@ trait WikiService {
|
|||||||
currentId: Option[String]
|
currentId: Option[String]
|
||||||
): Option[String] = {
|
): Option[String] = {
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||||
val builder = DirCache.newInCore.builder()
|
val builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
@@ -309,7 +311,7 @@ trait WikiService {
|
|||||||
message: String
|
message: String
|
||||||
): Unit = {
|
): Unit = {
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||||
val builder = DirCache.newInCore.builder()
|
val builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import gitbucket.core.model.Account
|
|||||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.service.{AccessTokenService, AccountService, SystemSettingsService}
|
import gitbucket.core.service.{AccessTokenService, AccountService, SystemSettingsService}
|
||||||
import gitbucket.core.util.{AuthUtil, Keys}
|
import gitbucket.core.util.{AuthUtil, Keys}
|
||||||
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
|
// Imported names have higher precedence than names, defined in other files.
|
||||||
|
// If Database is not bound by explicit import, then "Database" refers to the Database introduced by the wildcard import above.
|
||||||
|
import gitbucket.core.servlet.Database
|
||||||
|
|
||||||
class ApiAuthenticationFilter extends Filter with AccessTokenService with AccountService with SystemSettingsService {
|
class ApiAuthenticationFilter extends Filter with AccessTokenService with AccountService with SystemSettingsService {
|
||||||
|
|
||||||
@@ -20,7 +24,7 @@ class ApiAuthenticationFilter extends Filter with AccessTokenService with Accoun
|
|||||||
val response = res.asInstanceOf[HttpServletResponse]
|
val response = res.asInstanceOf[HttpServletResponse]
|
||||||
Option(request.getHeader("Authorization"))
|
Option(request.getHeader("Authorization"))
|
||||||
.map {
|
.map {
|
||||||
case auth if auth.startsWith("token ") =>
|
case auth if auth.toLowerCase().startsWith("token ") =>
|
||||||
AccessTokenService.getAccountByAccessToken(auth.substring(6).trim).toRight(())
|
AccessTokenService.getAccountByAccessToken(auth.substring(6).trim).toRight(())
|
||||||
case auth if auth.startsWith("Basic ") => doBasicAuth(auth, loadSystemSettings(), request).toRight(())
|
case auth if auth.startsWith("Basic ") => doBasicAuth(auth, loadSystemSettings(), request).toRight(())
|
||||||
case _ => Left(())
|
case _ => Left(())
|
||||||
@@ -33,7 +37,9 @@ class ApiAuthenticationFilter extends Filter with AccessTokenService with Accoun
|
|||||||
} match {
|
} match {
|
||||||
case Some(Right(account)) =>
|
case Some(Right(account)) =>
|
||||||
request.setAttribute(Keys.Session.LoginAccount, account)
|
request.setAttribute(Keys.Session.LoginAccount, account)
|
||||||
updateLastLoginDate(account.userName)
|
Database() withTransaction { implicit session =>
|
||||||
|
updateLastLoginDate(account.userName)
|
||||||
|
}
|
||||||
chain.doFilter(req, res)
|
chain.doFilter(req, res)
|
||||||
case None => chain.doFilter(req, res)
|
case None => chain.doFilter(req, res)
|
||||||
case Some(Left(_)) => {
|
case Some(Left(_)) => {
|
||||||
@@ -47,8 +53,9 @@ class ApiAuthenticationFilter extends Filter with AccessTokenService with Accoun
|
|||||||
}
|
}
|
||||||
|
|
||||||
def doBasicAuth(auth: String, settings: SystemSettings, request: HttpServletRequest): Option[Account] = {
|
def doBasicAuth(auth: String, settings: SystemSettings, request: HttpServletRequest): Option[Account] = {
|
||||||
implicit val session = request.getAttribute(Keys.Request.DBSession).asInstanceOf[slick.jdbc.JdbcBackend#Session]
|
|
||||||
val Array(username, password) = AuthUtil.decodeAuthHeader(auth).split(":", 2)
|
val Array(username, password) = AuthUtil.decodeAuthHeader(auth).split(":", 2)
|
||||||
authenticate(settings, username, password)
|
Database() withTransaction { implicit session =>
|
||||||
|
authenticate(settings, username, password)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,16 @@ import javax.servlet._
|
|||||||
import javax.servlet.http._
|
import javax.servlet.http._
|
||||||
|
|
||||||
import gitbucket.core.model.Account
|
import gitbucket.core.model.Account
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
|
||||||
import gitbucket.core.plugin.{GitRepositoryFilter, GitRepositoryRouting, PluginRegistry}
|
import gitbucket.core.plugin.{GitRepositoryFilter, GitRepositoryRouting, PluginRegistry}
|
||||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||||
import gitbucket.core.service.{AccessTokenService, AccountService, RepositoryService, SystemSettingsService}
|
import gitbucket.core.service.{AccessTokenService, AccountService, RepositoryService, SystemSettingsService}
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util.{AuthUtil, Implicits, Keys}
|
import gitbucket.core.util.{AuthUtil, Keys}
|
||||||
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
|
// Imported names have higher precedence than names, defined in other files.
|
||||||
|
// If Database is not bound by explicit import, then "Database" refers to the Database introduced by the wildcard import above.
|
||||||
|
import gitbucket.core.servlet.Database
|
||||||
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import gitbucket.core.util.{FileUtil, StringUtil}
|
|||||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||||
import org.json4s.jackson.Serialization._
|
import org.json4s.jackson.Serialization._
|
||||||
import org.apache.http.HttpStatus
|
import org.apache.http.HttpStatus
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides GitLFS Transfer API
|
* Provides GitLFS Transfer API
|
||||||
@@ -28,8 +29,8 @@ class GitLfsTransferServlet extends HttpServlet {
|
|||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
res.setStatus(HttpStatus.SC_OK)
|
res.setStatus(HttpStatus.SC_OK)
|
||||||
res.setContentType("application/octet-stream")
|
res.setContentType("application/octet-stream")
|
||||||
res.setContentLength(file.length.toInt)
|
res.setHeader("Content-Length", file.length.toString)
|
||||||
using(new FileInputStream(file), res.getOutputStream) { (in, out) =>
|
Using.resources(new FileInputStream(file), res.getOutputStream) { (in, out) =>
|
||||||
IOUtils.copy(in, out)
|
IOUtils.copy(in, out)
|
||||||
out.flush()
|
out.flush()
|
||||||
}
|
}
|
||||||
@@ -45,7 +46,7 @@ class GitLfsTransferServlet extends HttpServlet {
|
|||||||
} yield {
|
} yield {
|
||||||
val file = new File(FileUtil.getLfsFilePath(owner, repository, oid))
|
val file = new File(FileUtil.getLfsFilePath(owner, repository, oid))
|
||||||
FileUtils.forceMkdir(file.getParentFile)
|
FileUtils.forceMkdir(file.getParentFile)
|
||||||
using(req.getInputStream, new FileOutputStream(file)) { (in, out) =>
|
Using.resources(req.getInputStream, new FileOutputStream(file)) { (in, out) =>
|
||||||
IOUtils.copy(in, out)
|
IOUtils.copy(in, out)
|
||||||
}
|
}
|
||||||
res.setStatus(HttpStatus.SC_OK)
|
res.setStatus(HttpStatus.SC_OK)
|
||||||
@@ -71,7 +72,7 @@ class GitLfsTransferServlet extends HttpServlet {
|
|||||||
|
|
||||||
private def sendError(res: HttpServletResponse, status: Int, message: String): Unit = {
|
private def sendError(res: HttpServletResponse, status: Int, message: String): Unit = {
|
||||||
res.setStatus(status)
|
res.setStatus(status)
|
||||||
using(res.getWriter()) { out =>
|
Using.resource(res.getWriter()) { out =>
|
||||||
out.write(write(GitLfs.Error(message)))
|
out.write(write(GitLfs.Error(message)))
|
||||||
out.flush()
|
out.flush()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import java.io.File
|
|||||||
import java.util
|
import java.util
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
import gitbucket.core.api
|
import gitbucket.core.api
|
||||||
import gitbucket.core.model.WebHook
|
import gitbucket.core.model.WebHook
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
|
||||||
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
|
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
|
||||||
import gitbucket.core.service.IssuesService.IssueSearchCondition
|
import gitbucket.core.service.IssuesService.IssueSearchCondition
|
||||||
import gitbucket.core.service.WebHookService._
|
import gitbucket.core.service.WebHookService._
|
||||||
@@ -14,6 +15,11 @@ import gitbucket.core.service._
|
|||||||
import gitbucket.core.util.SyntaxSugars._
|
import gitbucket.core.util.SyntaxSugars._
|
||||||
import gitbucket.core.util.Implicits._
|
import gitbucket.core.util.Implicits._
|
||||||
import gitbucket.core.util._
|
import gitbucket.core.util._
|
||||||
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
|
// Imported names have higher precedence than names, defined in other files.
|
||||||
|
// If Database is not bound by explicit import, then "Database" refers to the Database introduced by the wildcard import above.
|
||||||
|
import gitbucket.core.servlet.Database
|
||||||
|
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.http.server.GitServlet
|
import org.eclipse.jgit.http.server.GitServlet
|
||||||
import org.eclipse.jgit.lib._
|
import org.eclipse.jgit.lib._
|
||||||
@@ -108,7 +114,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
GitLfs.Action(
|
GitLfs.Action(
|
||||||
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
||||||
header =
|
header =
|
||||||
Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
|
Map("Authorization" -> StringUtil.encodeBlowfish(s"$timeout ${requestObject.oid}")),
|
||||||
expires_at = new Date(timeout)
|
expires_at = new Date(timeout)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -129,7 +135,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
GitLfs.Action(
|
GitLfs.Action(
|
||||||
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
||||||
header =
|
header =
|
||||||
Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
|
Map("Authorization" -> StringUtil.encodeBlowfish(s"$timeout ${requestObject.oid}")),
|
||||||
expires_at = new Date(timeout)
|
expires_at = new Date(timeout)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -140,7 +146,7 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.setContentType("application/vnd.git-lfs+json")
|
res.setContentType("application/vnd.git-lfs+json")
|
||||||
using(res.getWriter) { out =>
|
Using.resource(res.getWriter) { out =>
|
||||||
out.print(write(batchResponse))
|
out.print(write(batchResponse))
|
||||||
out.flush()
|
out.flush()
|
||||||
}
|
}
|
||||||
@@ -216,7 +222,7 @@ class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest]
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String, sshUrl: Option[String])
|
class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl: String, sshUrl: Option[String])
|
||||||
extends PostReceiveHook
|
extends PostReceiveHook
|
||||||
@@ -233,7 +239,8 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
with MilestonesService
|
with MilestonesService
|
||||||
with WebHookPullRequestService
|
with WebHookPullRequestService
|
||||||
with WebHookPullRequestReviewCommentService
|
with WebHookPullRequestReviewCommentService
|
||||||
with CommitsService {
|
with CommitsService
|
||||||
|
with SystemSettingsService {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
private val logger = LoggerFactory.getLogger(classOf[CommitLogHook])
|
||||||
private var existIds: Seq[String] = Nil
|
private var existIds: Seq[String] = Nil
|
||||||
@@ -250,7 +257,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
command.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, error)
|
command.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||||
existIds = JGitUtil.getAllCommitIds(git)
|
existIds = JGitUtil.getAllCommitIds(git)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -263,9 +270,11 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def onPostReceive(receivePack: ReceivePack, commands: java.util.Collection[ReceiveCommand]): Unit = {
|
def onPostReceive(receivePack: ReceivePack, commands: java.util.Collection[ReceiveCommand]): Unit = {
|
||||||
|
val settings = loadSystemSettings()
|
||||||
|
|
||||||
Database() withTransaction { implicit session =>
|
Database() withTransaction { implicit session =>
|
||||||
try {
|
try {
|
||||||
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||||
JGitUtil.removeCache(git)
|
JGitUtil.removeCache(git)
|
||||||
|
|
||||||
val pushedIds = scala.collection.mutable.Set[String]()
|
val pushedIds = scala.collection.mutable.Set[String]()
|
||||||
@@ -289,7 +298,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
if (JGitUtil.isEmpty(git) && commits.nonEmpty && branchName != repositoryInfo.repository.defaultBranch) {
|
if (JGitUtil.isEmpty(git) && commits.nonEmpty && branchName != repositoryInfo.repository.defaultBranch) {
|
||||||
saveRepositoryDefaultBranch(owner, repository, branchName)
|
saveRepositoryDefaultBranch(owner, repository, branchName)
|
||||||
// Change repository HEAD
|
// Change repository HEAD
|
||||||
using(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(Directory.getRepositoryDir(owner, repository))) { git =>
|
||||||
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + branchName)
|
git.getRepository.updateRef(Constants.HEAD, true).link(Constants.R_HEADS + branchName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,7 +320,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||||
closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository).foreach { issueId =>
|
closeIssuesFromMessage(commit.fullMessage, pusher, owner, repository).foreach { issueId =>
|
||||||
getIssue(owner, repository, issueId.toString).foreach { issue =>
|
getIssue(owner, repository, issueId.toString).foreach { issue =>
|
||||||
callIssuesWebHook("closed", repositoryInfo, issue, pusherAccount)
|
callIssuesWebHook("closed", repositoryInfo, issue, pusherAccount, settings)
|
||||||
PluginRegistry().getIssueHooks
|
PluginRegistry().getIssueHooks
|
||||||
.foreach(_.closedByCommitComment(issue, repositoryInfo, commit.fullMessage, pusherAccount))
|
.foreach(_.closedByCommitComment(issue, repositoryInfo, commit.fullMessage, pusherAccount))
|
||||||
}
|
}
|
||||||
@@ -331,7 +340,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
}.isDefined) {
|
}.isDefined) {
|
||||||
markMergeAndClosePullRequest(pusher, owner, repository, pull)
|
markMergeAndClosePullRequest(pusher, owner, repository, pull)
|
||||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||||
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount)
|
callPullRequestWebHook("closed", repositoryInfo, pull.issueId, pusherAccount, settings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,14 +368,14 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
case ReceiveCommand.Type.CREATE | ReceiveCommand.Type.UPDATE |
|
case ReceiveCommand.Type.CREATE | ReceiveCommand.Type.UPDATE |
|
||||||
ReceiveCommand.Type.UPDATE_NONFASTFORWARD =>
|
ReceiveCommand.Type.UPDATE_NONFASTFORWARD =>
|
||||||
getAccountByUserName(pusher).foreach { pusherAccount =>
|
getAccountByUserName(pusher).foreach { pusherAccount =>
|
||||||
updatePullRequests(owner, repository, branchName, pusherAccount, "synchronize")
|
updatePullRequests(owner, repository, branchName, pusherAccount, "synchronize", settings)
|
||||||
}
|
}
|
||||||
case _ =>
|
case _ =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// call web hook
|
// call web hook
|
||||||
callWebHookOf(owner, repository, WebHook.Push) {
|
callWebHookOf(owner, repository, WebHook.Push, settings) {
|
||||||
for {
|
for {
|
||||||
pusherAccount <- getAccountByUserName(pusher)
|
pusherAccount <- getAccountByUserName(pusher)
|
||||||
ownerAccount <- getAccountByUserName(owner)
|
ownerAccount <- getAccountByUserName(owner)
|
||||||
@@ -384,7 +393,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (command.getType == ReceiveCommand.Type.CREATE) {
|
if (command.getType == ReceiveCommand.Type.CREATE) {
|
||||||
callWebHookOf(owner, repository, WebHook.Create) {
|
callWebHookOf(owner, repository, WebHook.Create, settings) {
|
||||||
for {
|
for {
|
||||||
pusherAccount <- getAccountByUserName(pusher)
|
pusherAccount <- getAccountByUserName(pusher)
|
||||||
ownerAccount <- getAccountByUserName(owner)
|
ownerAccount <- getAccountByUserName(owner)
|
||||||
@@ -422,11 +431,14 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
extends PostReceiveHook
|
extends PostReceiveHook
|
||||||
with WebHookService
|
with WebHookService
|
||||||
with AccountService
|
with AccountService
|
||||||
with RepositoryService {
|
with RepositoryService
|
||||||
|
with SystemSettingsService {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[WikiCommitHook])
|
private val logger = LoggerFactory.getLogger(classOf[WikiCommitHook])
|
||||||
|
|
||||||
override def onPostReceive(receivePack: ReceivePack, commands: util.Collection[ReceiveCommand]): Unit = {
|
override def onPostReceive(receivePack: ReceivePack, commands: util.Collection[ReceiveCommand]): Unit = {
|
||||||
|
val settings = loadSystemSettings()
|
||||||
|
|
||||||
Database() withTransaction { implicit session =>
|
Database() withTransaction { implicit session =>
|
||||||
try {
|
try {
|
||||||
commands.asScala.headOption.foreach { command =>
|
commands.asScala.headOption.foreach { command =>
|
||||||
@@ -443,7 +455,7 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
|
|
||||||
commitIds.foreach {
|
commitIds.foreach {
|
||||||
case (oldCommitId, newCommitId) =>
|
case (oldCommitId, newCommitId) =>
|
||||||
val commits = using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
val commits = Using.resource(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||||
JGitUtil.getCommitLog(git, oldCommitId, newCommitId).flatMap { commit =>
|
JGitUtil.getCommitLog(git, oldCommitId, newCommitId).flatMap { commit =>
|
||||||
val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
|
val diffs = JGitUtil.getDiffs(git, None, commit.id, false, false)
|
||||||
diffs.collect {
|
diffs.collect {
|
||||||
@@ -462,7 +474,7 @@ class WikiCommitHook(owner: String, repository: String, pusher: String, baseUrl:
|
|||||||
(commits.head._1, fileName, commits.last._3)
|
(commits.head._1, fileName, commits.last._3)
|
||||||
}
|
}
|
||||||
|
|
||||||
callWebHookOf(owner, repository, WebHook.Gollum) {
|
callWebHookOf(owner, repository, WebHook.Gollum, settings) {
|
||||||
for {
|
for {
|
||||||
pusherAccount <- getAccountByUserName(pusher)
|
pusherAccount <- getAccountByUserName(pusher)
|
||||||
repositoryUser <- getAccountByUserName(owner)
|
repositoryUser <- getAccountByUserName(owner)
|
||||||
|
|||||||
@@ -9,9 +9,12 @@ import gitbucket.core.plugin.PluginRegistry
|
|||||||
import gitbucket.core.service.{ActivityService, SystemSettingsService}
|
import gitbucket.core.service.{ActivityService, SystemSettingsService}
|
||||||
import gitbucket.core.util.DatabaseConfig
|
import gitbucket.core.util.DatabaseConfig
|
||||||
import gitbucket.core.util.Directory._
|
import gitbucket.core.util.Directory._
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
import gitbucket.core.util.JDBCUtil._
|
import gitbucket.core.util.JDBCUtil._
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
|
// Imported names have higher precedence than names, defined in other files.
|
||||||
|
// If Database is not bound by explicit import, then "Database" refers to the Database introduced by the wildcard import above.
|
||||||
|
import gitbucket.core.servlet.Database
|
||||||
|
|
||||||
import io.github.gitbucket.solidbase.Solidbase
|
import io.github.gitbucket.solidbase.Solidbase
|
||||||
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
import io.github.gitbucket.solidbase.manager.JDBCVersionManager
|
||||||
import javax.servlet.{ServletContextEvent, ServletContextListener}
|
import javax.servlet.{ServletContextEvent, ServletContextListener}
|
||||||
@@ -21,7 +24,8 @@ import org.slf4j.LoggerFactory
|
|||||||
import akka.actor.{Actor, ActorSystem, Props}
|
import akka.actor.{Actor, ActorSystem, Props}
|
||||||
import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension
|
import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize GitBucket system.
|
* Initialize GitBucket system.
|
||||||
@@ -84,7 +88,7 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Install bundled plugins
|
// Install bundled plugins
|
||||||
extractBundledPlugins(gitbucketVersion)
|
extractBundledPlugins()
|
||||||
|
|
||||||
// Load plugins
|
// Load plugins
|
||||||
logger.info("Initialize plugins")
|
logger.info("Initialize plugins")
|
||||||
@@ -134,11 +138,11 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def extractBundledPlugins(gitbucketVersion: String): Unit = {
|
private def extractBundledPlugins(): Unit = {
|
||||||
logger.info("Extract bundled plugins...")
|
logger.info("Extract bundled plugins...")
|
||||||
val cl = Thread.currentThread.getContextClassLoader
|
val cl = Thread.currentThread.getContextClassLoader
|
||||||
try {
|
try {
|
||||||
using(cl.getResourceAsStream("bundle-plugins.txt")) { pluginsFile =>
|
Using.resource(cl.getResourceAsStream("bundle-plugins.txt")) { pluginsFile =>
|
||||||
if (pluginsFile != null) {
|
if (pluginsFile != null) {
|
||||||
val plugins = IOUtils.readLines(pluginsFile, "UTF-8")
|
val plugins = IOUtils.readLines(pluginsFile, "UTF-8")
|
||||||
val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion
|
val gitbucketVersion = GitBucketCoreModule.getVersions.asScala.last.getVersion
|
||||||
@@ -146,14 +150,14 @@ class InitializeListener extends ServletContextListener with SystemSettingsServi
|
|||||||
plugins.asScala.foreach { plugin =>
|
plugins.asScala.foreach { plugin =>
|
||||||
plugin.trim.split(":") match {
|
plugin.trim.split(":") match {
|
||||||
case Array(pluginId, pluginVersion) =>
|
case Array(pluginId, pluginVersion) =>
|
||||||
val fileName = s"gitbucket-${pluginId}-plugin-gitbucket_${gitbucketVersion}-${pluginVersion}.jar"
|
val fileName = s"gitbucket-${pluginId}-plugin-${pluginVersion}.jar"
|
||||||
val in = cl.getResourceAsStream("plugins/" + fileName)
|
val in = cl.getResourceAsStream("plugins/" + fileName)
|
||||||
if (in != null) {
|
if (in != null) {
|
||||||
val file = new File(PluginHome, fileName)
|
val file = new File(PluginHome, fileName)
|
||||||
logger.info(s"Extract to ${file.getAbsolutePath}")
|
logger.info(s"Extract to ${file.getAbsolutePath}")
|
||||||
|
|
||||||
FileUtils.forceMkdirParent(file)
|
FileUtils.forceMkdirParent(file)
|
||||||
using(in, new FileOutputStream(file)) {
|
Using.resources(in, new FileOutputStream(file)) {
|
||||||
case (in, out) => IOUtils.copy(in, out)
|
case (in, out) => IOUtils.copy(in, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ import org.apache.sshd.server.session.ServerSession
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.io.{File, InputStream, OutputStream}
|
import java.io.{File, InputStream, OutputStream}
|
||||||
|
|
||||||
import SyntaxSugars._
|
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import Directory._
|
import Directory._
|
||||||
import gitbucket.core.ssh.PublicKeyAuthenticator.AuthType
|
import gitbucket.core.ssh.PublicKeyAuthenticator.AuthType
|
||||||
import org.eclipse.jgit.transport.{ReceivePack, UploadPack}
|
import org.eclipse.jgit.transport.{ReceivePack, UploadPack}
|
||||||
import org.apache.sshd.server.shell.UnknownCommand
|
import org.apache.sshd.server.shell.UnknownCommand
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException
|
import org.eclipse.jgit.errors.RepositoryNotFoundException
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
object GitCommand {
|
object GitCommand {
|
||||||
val DefaultCommandRegex = """\Agit-(upload|receive)-pack '/([a-zA-Z0-9\-_.]+)/([a-zA-Z0-9\-\+_.]+).git'\Z""".r
|
val DefaultCommandRegex = """\Agit-(upload|receive)-pack '/([a-zA-Z0-9\-_.]+)/([a-zA-Z0-9\-\+_.]+).git'\Z""".r
|
||||||
@@ -152,7 +152,7 @@ class DefaultGitUploadPack(owner: String, repoName: String)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (execute) {
|
if (execute) {
|
||||||
using(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
||||||
val repository = git.getRepository
|
val repository = git.getRepository
|
||||||
val upload = new UploadPack(repository)
|
val upload = new UploadPack(repository)
|
||||||
upload.upload(in, out, err)
|
upload.upload(in, out, err)
|
||||||
@@ -177,7 +177,7 @@ class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String, ss
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (execute) {
|
if (execute) {
|
||||||
using(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
||||||
val repository = git.getRepository
|
val repository = git.getRepository
|
||||||
val receive = new ReceivePack(repository)
|
val receive = new ReceivePack(repository)
|
||||||
if (!repoName.endsWith(".wiki")) {
|
if (!repoName.endsWith(".wiki")) {
|
||||||
@@ -202,7 +202,7 @@ class PluginGitUploadPack(repoName: String, routing: GitRepositoryRouting)
|
|||||||
|
|
||||||
if (execute) {
|
if (execute) {
|
||||||
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
||||||
using(Git.open(new File(Directory.GitBucketHome, path))) { git =>
|
Using.resource(Git.open(new File(Directory.GitBucketHome, path))) { git =>
|
||||||
val repository = git.getRepository
|
val repository = git.getRepository
|
||||||
val upload = new UploadPack(repository)
|
val upload = new UploadPack(repository)
|
||||||
upload.upload(in, out, err)
|
upload.upload(in, out, err)
|
||||||
@@ -222,7 +222,7 @@ class PluginGitReceivePack(repoName: String, routing: GitRepositoryRouting)
|
|||||||
|
|
||||||
if (execute) {
|
if (execute) {
|
||||||
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
||||||
using(Git.open(new File(Directory.GitBucketHome, path))) { git =>
|
Using.resource(Git.open(new File(Directory.GitBucketHome, path))) { git =>
|
||||||
val repository = git.getRepository
|
val repository = git.getRepository
|
||||||
val receive = new ReceivePack(repository)
|
val receive = new ReceivePack(repository)
|
||||||
receive.receive(in, out, err)
|
receive.receive(in, out, err)
|
||||||
|
|||||||
41
src/main/scala/gitbucket/core/util/ConfigUtil.scala
Normal file
41
src/main/scala/gitbucket/core/util/ConfigUtil.scala
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package gitbucket.core.util
|
||||||
|
|
||||||
|
import gitbucket.core.util.SyntaxSugars.defining
|
||||||
|
|
||||||
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
|
object ConfigUtil {
|
||||||
|
|
||||||
|
def getConfigValue[A: ClassTag](key: String): Option[A] = {
|
||||||
|
getSystemProperty(key).orElse(getEnvironmentVariable(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
def getEnvironmentVariable[A: ClassTag](key: String): Option[A] = {
|
||||||
|
val name = (if (key.startsWith("gitbucket.")) "" else "GITBUCKET_") + key.toUpperCase.replace('.', '_')
|
||||||
|
val value = System.getenv(name)
|
||||||
|
if (value != null && value.nonEmpty) {
|
||||||
|
Some(convertType(value))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def getSystemProperty[A: ClassTag](key: String): Option[A] = {
|
||||||
|
val name = if (key.startsWith("gitbucket.")) key else "gitbucket." + key
|
||||||
|
val value = System.getProperty(name)
|
||||||
|
if (value != null && value.nonEmpty) {
|
||||||
|
Some(convertType(value))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def convertType[A: ClassTag](value: String): A =
|
||||||
|
defining(implicitly[ClassTag[A]].runtimeClass) { c =>
|
||||||
|
if (c == classOf[Boolean]) value.toBoolean
|
||||||
|
else if (c == classOf[Long]) value.toLong
|
||||||
|
else if (c == classOf[Int]) value.toInt
|
||||||
|
else value
|
||||||
|
}.asInstanceOf[A]
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,9 +6,8 @@ import java.io.File
|
|||||||
import Directory._
|
import Directory._
|
||||||
import ConfigUtil._
|
import ConfigUtil._
|
||||||
import com.github.takezoe.slick.blocking.{BlockingH2Driver, BlockingJdbcProfile, BlockingMySQLDriver}
|
import com.github.takezoe.slick.blocking.{BlockingH2Driver, BlockingJdbcProfile, BlockingMySQLDriver}
|
||||||
import gitbucket.core.util.SyntaxSugars.defining
|
|
||||||
import liquibase.database.AbstractJdbcDatabase
|
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 org.apache.commons.io.FileUtils
|
||||||
|
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
@@ -54,16 +53,14 @@ object DatabaseConfig {
|
|||||||
lazy val minimumIdle: Option[Int] = getOptionValue("db.minimumIdle", config.getInt)
|
lazy val minimumIdle: Option[Int] = getOptionValue("db.minimumIdle", config.getInt)
|
||||||
lazy val maximumPoolSize: Option[Int] = getOptionValue("db.maximumPoolSize", config.getInt)
|
lazy val maximumPoolSize: Option[Int] = getOptionValue("db.maximumPoolSize", config.getInt)
|
||||||
|
|
||||||
private def getValue[T](path: String, f: String => T): T = {
|
private def getValue[T: ClassTag](path: String, f: String => T): T = {
|
||||||
getSystemProperty(path).getOrElse(getEnvironmentVariable(path).getOrElse {
|
getConfigValue(path).getOrElse(f(path))
|
||||||
f(path)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def getOptionValue[T](path: String, f: String => T): Option[T] = {
|
private def getOptionValue[T: ClassTag](path: String, f: String => T): Option[T] = {
|
||||||
getSystemProperty(path).orElse(getEnvironmentVariable(path).orElse {
|
getConfigValue(path).orElse {
|
||||||
if (config.hasPath(path)) Some(f(path)) else None
|
if (config.hasPath(path)) Some(f(path)) else None
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -81,6 +78,8 @@ object DatabaseType {
|
|||||||
H2
|
H2
|
||||||
} else if (url.startsWith("jdbc:mysql:")) {
|
} else if (url.startsWith("jdbc:mysql:")) {
|
||||||
MySQL
|
MySQL
|
||||||
|
} else if (url.startsWith("jdbc:mariadb:")) {
|
||||||
|
MariaDb
|
||||||
} else if (url.startsWith("jdbc:postgresql:")) {
|
} else if (url.startsWith("jdbc:postgresql:")) {
|
||||||
PostgreSQL
|
PostgreSQL
|
||||||
} else {
|
} else {
|
||||||
@@ -100,6 +99,12 @@ object DatabaseType {
|
|||||||
val liquiDriver = new MySQLDatabase()
|
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 {
|
object PostgreSQL extends DatabaseType {
|
||||||
val jdbcDriver = "org.postgresql.Driver2"
|
val jdbcDriver = "org.postgresql.Driver2"
|
||||||
val slickDriver = BlockingPostgresDriver
|
val slickDriver = BlockingPostgresDriver
|
||||||
@@ -114,33 +119,3 @@ object DatabaseType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object ConfigUtil {
|
|
||||||
|
|
||||||
def getEnvironmentVariable[A](key: String): Option[A] = {
|
|
||||||
val value = System.getenv("GITBUCKET_" + key.toUpperCase.replace('.', '_'))
|
|
||||||
if (value != null && value.nonEmpty) {
|
|
||||||
Some(convertType(value)).asInstanceOf[Option[A]]
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def getSystemProperty[A](key: String): Option[A] = {
|
|
||||||
val value = System.getProperty("gitbucket." + key)
|
|
||||||
if (value != null && value.nonEmpty) {
|
|
||||||
Some(convertType(value)).asInstanceOf[Option[A]]
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def convertType[A: ClassTag](value: String) =
|
|
||||||
defining(implicitly[ClassTag[A]].runtimeClass) { c =>
|
|
||||||
if (c == classOf[Boolean]) value.toBoolean
|
|
||||||
else if (c == classOf[Long]) value.toLong
|
|
||||||
else if (c == classOf[Int]) value.toInt
|
|
||||||
else value
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ object Directory {
|
|||||||
new File(getRepositoryFilesDir(owner, repository), "releases")
|
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 =
|
def getLfsDir(owner: String, repository: String): File =
|
||||||
new File(getRepositoryFilesDir(owner, repository), "lfs")
|
new File(getRepositoryFilesDir(owner, repository), "lfs")
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import org.eclipse.jgit.api.Git
|
|||||||
import org.eclipse.jgit.lib.{ObjectReader, Repository}
|
import org.eclipse.jgit.lib.{ObjectReader, Repository}
|
||||||
import org.eclipse.jgit.revwalk.{RevTree, RevWalk}
|
import org.eclipse.jgit.revwalk.{RevTree, RevWalk}
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk
|
import org.eclipse.jgit.treewalk.TreeWalk
|
||||||
import gitbucket.core.util.SyntaxSugars._
|
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
object EditorConfigUtil {
|
object EditorConfigUtil {
|
||||||
private class JGitResource(repo: Repository, revStr: String, path: Ec4jPath) extends Resource {
|
private class JGitResource(repo: Repository, revStr: String, path: Ec4jPath) extends Resource {
|
||||||
@@ -27,7 +28,7 @@ object EditorConfigUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def getRevTree: RevTree = {
|
private def getRevTree: RevTree = {
|
||||||
using(repo.newObjectReader()) { reader: ObjectReader =>
|
Using.resource(repo.newObjectReader()) { reader: ObjectReader =>
|
||||||
val revWalk = new RevWalk(reader)
|
val revWalk = new RevWalk(reader)
|
||||||
val id = repo.resolve(revStr)
|
val id = repo.resolve(revStr)
|
||||||
val commit = revWalk.parseCommit(id)
|
val commit = revWalk.parseCommit(id)
|
||||||
@@ -36,7 +37,7 @@ object EditorConfigUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def exists(): Boolean = {
|
override def exists(): Boolean = {
|
||||||
using(repo.newObjectReader()) { reader: ObjectReader =>
|
Using.resource(repo.newObjectReader()) { reader: ObjectReader =>
|
||||||
try {
|
try {
|
||||||
val treeWalk = Option(TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree))
|
val treeWalk = Option(TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree))
|
||||||
treeWalk.isDefined
|
treeWalk.isDefined
|
||||||
@@ -59,7 +60,7 @@ object EditorConfigUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override def openReader(): Reader = {
|
override def openReader(): Reader = {
|
||||||
using(repo.newObjectReader) { reader: ObjectReader =>
|
Using.resource(repo.newObjectReader) { reader: ObjectReader =>
|
||||||
val treeWalk = TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree)
|
val treeWalk = TreeWalk.forPath(reader, removeInitialSlash(path), getRevTree)
|
||||||
new InputStreamReader(reader.open(treeWalk.getObjectId(0)).openStream, StandardCharsets.UTF_8)
|
new InputStreamReader(reader.open(treeWalk.getObjectId(0)).openStream, StandardCharsets.UTF_8)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,16 +27,18 @@ object FileUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getSafeMimeType(name: String): String = {
|
def getSafeMimeType(name: String): String = {
|
||||||
getMimeType(name).replace("text/html", "text/plain")
|
getMimeType(name)
|
||||||
|
.replace("text/html", "text/plain")
|
||||||
|
.replace("image/svg+xml", "text/plain; charset=UTF-8")
|
||||||
}
|
}
|
||||||
|
|
||||||
def isImage(name: String): Boolean = getMimeType(name).startsWith("image/")
|
def isImage(name: String): Boolean = getSafeMimeType(name).startsWith("image/")
|
||||||
|
|
||||||
def isLarge(size: Long): Boolean = (size > 1024 * 1000)
|
def isLarge(size: Long): Boolean = (size > 1024 * 1000)
|
||||||
|
|
||||||
def isText(content: Array[Byte]): Boolean = !content.contains(0)
|
def isText(content: Array[Byte]): Boolean = !content.contains(0)
|
||||||
|
|
||||||
def generateFileId: String = System.currentTimeMillis + Random.alphanumeric.take(10).mkString
|
def generateFileId: String = s"${System.currentTimeMillis}${Random.alphanumeric.take(10).mkString}"
|
||||||
|
|
||||||
def getExtension(name: String): String =
|
def getExtension(name: String): String =
|
||||||
name.lastIndexOf('.') match {
|
name.lastIndexOf('.') match {
|
||||||
@@ -56,7 +58,7 @@ object FileUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def getLfsFilePath(owner: String, repository: String, oid: String): String =
|
def getLfsFilePath(owner: String, repository: String, oid: String): String =
|
||||||
Directory.getLfsDir(owner, repository) + "/" + checkFilename(oid)
|
s"${Directory.getLfsDir(owner, repository)}/${checkFilename(oid)}"
|
||||||
|
|
||||||
def readableSize(size: Long): String = FileUtils.byteCountToDisplaySize(size)
|
def readableSize(size: Long): String = FileUtils.byteCountToDisplaySize(size)
|
||||||
|
|
||||||
@@ -90,10 +92,4 @@ object FileUtil {
|
|||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val MaxFileSize =
|
|
||||||
if (System.getProperty("gitbucket.maxFileSize") != null)
|
|
||||||
System.getProperty("gitbucket.maxFileSize").toLong
|
|
||||||
else
|
|
||||||
3 * 1024 * 1024
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package gitbucket.core.util
|
package gitbucket.core.util
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
|
|
||||||
import collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
|
||||||
import gitbucket.core.model.Profile._
|
import gitbucket.core.model.Profile._
|
||||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||||
import org.bouncycastle.bcpg.ArmoredInputStream
|
import org.bouncycastle.bcpg.ArmoredInputStream
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package gitbucket.core.util
|
package gitbucket.core.util
|
||||||
|
|
||||||
|
import java.net.{InetAddress, URL}
|
||||||
|
|
||||||
import gitbucket.core.service.SystemSettingsService
|
import gitbucket.core.service.SystemSettingsService
|
||||||
|
import org.apache.commons.net.util.SubnetUtils
|
||||||
import org.apache.http.HttpHost
|
import org.apache.http.HttpHost
|
||||||
import org.apache.http.auth.{AuthScope, UsernamePasswordCredentials}
|
import org.apache.http.auth.{AuthScope, UsernamePasswordCredentials}
|
||||||
import org.apache.http.impl.client.{BasicCredentialsProvider, CloseableHttpClient, HttpClientBuilder}
|
import org.apache.http.impl.client.{BasicCredentialsProvider, CloseableHttpClient, HttpClientBuilder}
|
||||||
@@ -32,4 +35,19 @@ object HttpClientUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def isPrivateAddress(address: String): Boolean = {
|
||||||
|
val ipAddress = InetAddress.getByName(address)
|
||||||
|
ipAddress.isSiteLocalAddress || ipAddress.isLinkLocalAddress || ipAddress.isLoopbackAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
def inIpRange(ipRange: String, ipAddress: String): Boolean = {
|
||||||
|
if (ipRange.contains('/')) {
|
||||||
|
val utils = new SubnetUtils(ipRange)
|
||||||
|
utils.setInclusiveHostCount(true)
|
||||||
|
utils.getInfo.isInRange(ipAddress)
|
||||||
|
} else {
|
||||||
|
ipRange == ipAddress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package gitbucket.core.util
|
|||||||
import java.io._
|
import java.io._
|
||||||
import java.sql._
|
import java.sql._
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import SyntaxSugars._
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
|
import scala.util.Using
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides implicit class which extends java.sql.Connection.
|
* Provides implicit class which extends java.sql.Connection.
|
||||||
@@ -26,7 +26,7 @@ object JDBCUtil {
|
|||||||
|
|
||||||
def find[T](sql: String, params: Any*)(f: ResultSet => T): Option[T] = {
|
def find[T](sql: String, params: Any*)(f: ResultSet => T): Option[T] = {
|
||||||
execute(sql, params: _*) { stmt =>
|
execute(sql, params: _*) { stmt =>
|
||||||
using(stmt.executeQuery()) { rs =>
|
Using.resource(stmt.executeQuery()) { rs =>
|
||||||
if (rs.next) Some(f(rs)) else None
|
if (rs.next) Some(f(rs)) else None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ object JDBCUtil {
|
|||||||
|
|
||||||
def select[T](sql: String, params: Any*)(f: ResultSet => T): Seq[T] = {
|
def select[T](sql: String, params: Any*)(f: ResultSet => T): Seq[T] = {
|
||||||
execute(sql, params: _*) { stmt =>
|
execute(sql, params: _*) { stmt =>
|
||||||
using(stmt.executeQuery()) { rs =>
|
Using.resource(stmt.executeQuery()) { rs =>
|
||||||
val list = new ListBuffer[T]
|
val list = new ListBuffer[T]
|
||||||
while (rs.next) {
|
while (rs.next) {
|
||||||
list += f(rs)
|
list += f(rs)
|
||||||
@@ -46,14 +46,14 @@ object JDBCUtil {
|
|||||||
|
|
||||||
def selectInt(sql: String, params: Any*): Int = {
|
def selectInt(sql: String, params: Any*): Int = {
|
||||||
execute(sql, params: _*) { stmt =>
|
execute(sql, params: _*) { stmt =>
|
||||||
using(stmt.executeQuery()) { rs =>
|
Using.resource(stmt.executeQuery()) { rs =>
|
||||||
if (rs.next) rs.getInt(1) else 0
|
if (rs.next) rs.getInt(1) else 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def execute[T](sql: String, params: Any*)(f: (PreparedStatement) => T): T = {
|
private def execute[T](sql: String, params: Any*)(f: (PreparedStatement) => T): T = {
|
||||||
using(conn.prepareStatement(sql)) { stmt =>
|
Using.resource(conn.prepareStatement(sql)) { stmt =>
|
||||||
params.zipWithIndex.foreach {
|
params.zipWithIndex.foreach {
|
||||||
case (p, i) =>
|
case (p, i) =>
|
||||||
p match {
|
p match {
|
||||||
@@ -68,7 +68,7 @@ object JDBCUtil {
|
|||||||
def importAsSQL(in: InputStream): Unit = {
|
def importAsSQL(in: InputStream): Unit = {
|
||||||
conn.setAutoCommit(false)
|
conn.setAutoCommit(false)
|
||||||
try {
|
try {
|
||||||
using(in) { in =>
|
Using.resource(in) { in =>
|
||||||
var out = new ByteArrayOutputStream()
|
var out = new ByteArrayOutputStream()
|
||||||
|
|
||||||
var length = 0
|
var length = 0
|
||||||
@@ -111,7 +111,7 @@ object JDBCUtil {
|
|||||||
val dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
|
val dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
|
||||||
val file = File.createTempFile("gitbucket-export-", ".sql")
|
val file = File.createTempFile("gitbucket-export-", ".sql")
|
||||||
|
|
||||||
using(new FileOutputStream(file)) { out =>
|
Using.resource(new FileOutputStream(file)) { out =>
|
||||||
val dbMeta = conn.getMetaData
|
val dbMeta = conn.getMetaData
|
||||||
val allTablesInDatabase = allTablesOrderByDependencies(dbMeta)
|
val allTablesInDatabase = allTablesOrderByDependencies(dbMeta)
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ object JDBCUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def allTableNames(): Seq[String] = {
|
def allTableNames(): Seq[String] = {
|
||||||
using(conn.getMetaData.getTables(null, null, "%", Seq("TABLE").toArray)) { rs =>
|
Using.resource(conn.getMetaData.getTables(null, null, "%", Seq("TABLE").toArray)) { rs =>
|
||||||
val tableNames = new ListBuffer[String]
|
val tableNames = new ListBuffer[String]
|
||||||
while (rs.next) {
|
while (rs.next) {
|
||||||
val name = rs.getString("TABLE_NAME").toUpperCase
|
val name = rs.getString("TABLE_NAME").toUpperCase
|
||||||
@@ -188,7 +188,7 @@ object JDBCUtil {
|
|||||||
tableName
|
tableName
|
||||||
}
|
}
|
||||||
|
|
||||||
using(meta.getExportedKeys(null, null, normalizedTableName)) { rs =>
|
Using.resource(meta.getExportedKeys(null, null, normalizedTableName)) { rs =>
|
||||||
val children = new ListBuffer[String]
|
val children = new ListBuffer[String]
|
||||||
while (rs.next) {
|
while (rs.next) {
|
||||||
val childTableName = rs.getString("FKTABLE_NAME").toUpperCase
|
val childTableName = rs.getString("FKTABLE_NAME").toUpperCase
|
||||||
@@ -218,7 +218,7 @@ object JDBCUtil {
|
|||||||
ordered ++ orphans
|
ordered ++ orphans
|
||||||
}
|
}
|
||||||
|
|
||||||
def tsort[A](edges: Traversable[(A, A)]): Iterable[A] = {
|
def tsort[A](edges: Iterable[(A, A)]): Iterable[A] = {
|
||||||
@tailrec
|
@tailrec
|
||||||
def tsort(toPreds: Map[A, Set[A]], done: Iterable[A]): Iterable[A] = {
|
def tsort(toPreds: Map[A, Set[A]], done: Iterable[A]): Iterable[A] = {
|
||||||
val (noPreds, hasPreds) = toPreds.partition { _._2.isEmpty }
|
val (noPreds, hasPreds) = toPreds.partition { _._2.isEmpty }
|
||||||
@@ -226,7 +226,7 @@ object JDBCUtil {
|
|||||||
if (hasPreds.isEmpty) done else sys.error(hasPreds.toString)
|
if (hasPreds.isEmpty) done else sys.error(hasPreds.toString)
|
||||||
} else {
|
} else {
|
||||||
val found = noPreds.map { _._1 }
|
val found = noPreds.map { _._1 }
|
||||||
tsort(hasPreds.mapValues { _ -- found }, done ++ found)
|
tsort(hasPreds.map { case (k, v) => (k, v -- found) }, done ++ found)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import StringUtil._
|
|||||||
import SyntaxSugars._
|
import SyntaxSugars._
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.JavaConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
|
import scala.util.Using
|
||||||
import org.eclipse.jgit.lib._
|
import org.eclipse.jgit.lib._
|
||||||
import org.eclipse.jgit.revwalk._
|
import org.eclipse.jgit.revwalk._
|
||||||
import org.eclipse.jgit.revwalk.filter._
|
import org.eclipse.jgit.revwalk.filter._
|
||||||
@@ -20,7 +21,6 @@ import org.eclipse.jgit.errors.{ConfigInvalidException, IncorrectObjectTypeExcep
|
|||||||
import org.eclipse.jgit.transport.RefSpec
|
import org.eclipse.jgit.transport.RefSpec
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.function.Consumer
|
|
||||||
|
|
||||||
import org.cache2k.Cache2kBuilder
|
import org.cache2k.Cache2kBuilder
|
||||||
import org.eclipse.jgit.api.errors._
|
import org.eclipse.jgit.api.errors._
|
||||||
@@ -29,6 +29,8 @@ import org.eclipse.jgit.dircache.DirCacheEntry
|
|||||||
import org.eclipse.jgit.util.io.DisabledOutputStream
|
import org.eclipse.jgit.util.io.DisabledOutputStream
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
import scala.util.Using.Releasable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides complex JGit operations.
|
* Provides complex JGit operations.
|
||||||
*/
|
*/
|
||||||
@@ -36,6 +38,10 @@ object JGitUtil {
|
|||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(JGitUtil.getClass)
|
private val logger = LoggerFactory.getLogger(JGitUtil.getClass)
|
||||||
|
|
||||||
|
implicit val objectDatabaseReleasable = new Releasable[ObjectDatabase] {
|
||||||
|
override def release(resource: ObjectDatabase): Unit = resource.close()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The repository data.
|
* The repository data.
|
||||||
*
|
*
|
||||||
@@ -284,6 +290,11 @@ object JGitUtil {
|
|||||||
.entryCapacity(10000)
|
.entryCapacity(10000)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
private val objectCommitCache = new Cache2kBuilder[ObjectId, RevCommit]() {}
|
||||||
|
.name("object-commit")
|
||||||
|
.entryCapacity(10000)
|
||||||
|
.build()
|
||||||
|
|
||||||
def removeCache(git: Git): Unit = {
|
def removeCache(git: Git): Unit = {
|
||||||
val dir = git.getRepository.getDirectory
|
val dir = git.getRepository.getDirectory
|
||||||
val keyPrefix = dir.getAbsolutePath + "@"
|
val keyPrefix = dir.getAbsolutePath + "@"
|
||||||
@@ -318,7 +329,7 @@ object JGitUtil {
|
|||||||
* Returns the repository information. It contains branch names and tag names.
|
* Returns the repository information. It contains branch names and tag names.
|
||||||
*/
|
*/
|
||||||
def getRepositoryInfo(owner: String, repository: String): RepositoryInfo = {
|
def getRepositoryInfo(owner: String, repository: String): RepositoryInfo = {
|
||||||
using(Git.open(getRepositoryDir(owner, repository))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, repository))) { git =>
|
||||||
try {
|
try {
|
||||||
RepositoryInfo(
|
RepositoryInfo(
|
||||||
owner,
|
owner,
|
||||||
@@ -362,10 +373,17 @@ object JGitUtil {
|
|||||||
* @param revision the branch name or commit id
|
* @param revision the branch name or commit id
|
||||||
* @param path the directory path (optional)
|
* @param path the directory path (optional)
|
||||||
* @param baseUrl the base url of GitBucket instance. This parameter is used to generate links of submodules (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(
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
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)
|
val objectId = git.getRepository.resolve(revision)
|
||||||
if (objectId == null) return Nil
|
if (objectId == null) return Nil
|
||||||
val revCommit = revWalk.parseCommit(objectId)
|
val revCommit = revWalk.parseCommit(objectId)
|
||||||
@@ -374,112 +392,93 @@ object JGitUtil {
|
|||||||
if (path == ".") {
|
if (path == ".") {
|
||||||
val treeWalk = new TreeWalk(git.getRepository)
|
val treeWalk = new TreeWalk(git.getRepository)
|
||||||
treeWalk.addTree(rev.getTree)
|
treeWalk.addTree(rev.getTree)
|
||||||
using(treeWalk)(f)
|
Using.resource(treeWalk)(f)
|
||||||
} else {
|
} else {
|
||||||
val treeWalk = TreeWalk.forPath(git.getRepository, path, rev.getTree)
|
val treeWalk = TreeWalk.forPath(git.getRepository, path, rev.getTree)
|
||||||
if (treeWalk != null) {
|
if (treeWalk != null) {
|
||||||
treeWalk.enterSubtree
|
treeWalk.enterSubtree
|
||||||
using(treeWalk)(f)
|
Using.resource(treeWalk)(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@tailrec
|
@tailrec
|
||||||
def simplifyPath(
|
def simplifyPath(
|
||||||
tuple: (ObjectId, FileMode, String, String, Option[String], RevCommit)
|
tuple: (ObjectId, FileMode, String, String, Option[String], Option[RevCommit])
|
||||||
): (ObjectId, FileMode, String, String, Option[String], RevCommit) = tuple match {
|
): (ObjectId, FileMode, String, String, Option[String], Option[RevCommit]) =
|
||||||
case (oid, FileMode.TREE, name, path, _, commit) =>
|
tuple match {
|
||||||
(using(new TreeWalk(git.getRepository)) { walk =>
|
case (oid, FileMode.TREE, name, path, _, commit) =>
|
||||||
walk.addTree(oid)
|
(Using.resource(new TreeWalk(git.getRepository)) { walk =>
|
||||||
// single tree child, or None
|
walk.addTree(oid)
|
||||||
if (walk.next() && walk.getFileMode(0) == FileMode.TREE) {
|
// single tree child, or None
|
||||||
Some(
|
if (walk.next() && walk.getFileMode(0) == FileMode.TREE) {
|
||||||
(
|
Some(
|
||||||
walk.getObjectId(0),
|
(
|
||||||
walk.getFileMode(0),
|
walk.getObjectId(0),
|
||||||
name + "/" + walk.getNameString,
|
walk.getFileMode(0),
|
||||||
path + "/" + walk.getNameString,
|
name + "/" + walk.getNameString,
|
||||||
None,
|
path + "/" + walk.getNameString,
|
||||||
commit
|
None,
|
||||||
)
|
commit
|
||||||
).filterNot(_ => walk.next())
|
)
|
||||||
} else {
|
).filterNot(_ => walk.next())
|
||||||
None
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}) match {
|
||||||
|
case Some(child) => simplifyPath(child)
|
||||||
|
case _ => tuple
|
||||||
}
|
}
|
||||||
}) match {
|
case _ => tuple
|
||||||
case Some(child) => simplifyPath(child)
|
}
|
||||||
case _ => tuple
|
|
||||||
}
|
|
||||||
case _ => tuple
|
|
||||||
}
|
|
||||||
|
|
||||||
def tupleAdd(tuple: (ObjectId, FileMode, String, String, Option[String]), rev: RevCommit) = tuple match {
|
def appendLastCommits(
|
||||||
case (oid, fmode, name, path, opt) => (oid, fmode, name, path, opt, rev)
|
fileList: List[(ObjectId, FileMode, String, String, Option[String])]
|
||||||
}
|
): List[(ObjectId, FileMode, String, String, Option[String], Option[RevCommit])] = {
|
||||||
|
fileList.map {
|
||||||
@tailrec
|
case (id, mode, name, path, opt) =>
|
||||||
def findLastCommits(
|
// Don't attempt to get the last commit if the number of files is very large.
|
||||||
result: List[(ObjectId, FileMode, String, String, Option[String], RevCommit)],
|
if (fileList.size >= 100) {
|
||||||
restList: List[((ObjectId, FileMode, String, String, Option[String]), Map[RevCommit, RevCommit])],
|
(id, mode, name, path, opt, None)
|
||||||
revIterator: java.util.Iterator[RevCommit]
|
} else if (commitCount < 10000) {
|
||||||
): List[(ObjectId, FileMode, String, String, Option[String], RevCommit)] = {
|
val i = git
|
||||||
if (restList.isEmpty) {
|
(id, mode, name, path, opt, Some(getCommit(path)))
|
||||||
result
|
} else {
|
||||||
} else if (!revIterator.hasNext) { // maybe, revCommit has only 1 log. other case, restList be empty
|
val cached = objectCommitCache.getEntry(id)
|
||||||
result ++ restList.map { case (tuple, map) => tupleAdd(tuple, map.values.headOption.getOrElse(revCommit)) }
|
if (cached == null) {
|
||||||
} else {
|
val commit = getCommit(path)
|
||||||
val newCommit = revIterator.next
|
objectCommitCache.put(id, commit)
|
||||||
val (thisTimeChecks, skips) = restList.partition {
|
(id, mode, name, path, opt, Some(commit))
|
||||||
case (tuple, parentsMap) => parentsMap.contains(newCommit)
|
} else {
|
||||||
}
|
(id, mode, name, path, opt, Some(cached.getValue))
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
|
var fileList: List[(ObjectId, FileMode, String, String, Option[String])] = Nil
|
||||||
useTreeWalk(revCommit) { treeWalk =>
|
useTreeWalk(revCommit) { treeWalk =>
|
||||||
while (treeWalk.next()) {
|
while (treeWalk.next()) {
|
||||||
val linkUrl = if (treeWalk.getFileMode(0) == FileMode.GITLINK) {
|
val linkUrl = if (treeWalk.getFileMode(0) == FileMode.GITLINK) {
|
||||||
getSubmodules(git, revCommit.getTree, baseUrl).find(_.path == treeWalk.getPathString).map(_.viewerUrl)
|
getSubmodules(git, revCommit.getTree, baseUrl).find(_.path == treeWalk.getPathString).map(_.viewerUrl)
|
||||||
} else None
|
} 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
|
appendLastCommits(fileList)
|
||||||
val lastCommit = it.next
|
|
||||||
val nextParentsMap = Option(lastCommit).map(_.getParents.map(_ -> lastCommit).toMap).getOrElse(Map())
|
|
||||||
findLastCommits(List.empty, fileList.map(a => a -> nextParentsMap), it)
|
|
||||||
.map(simplifyPath)
|
.map(simplifyPath)
|
||||||
.map {
|
.map {
|
||||||
case (objectId, fileMode, name, path, linkUrl, commit) =>
|
case (objectId, fileMode, name, path, linkUrl, commit) =>
|
||||||
@@ -488,11 +487,14 @@ object JGitUtil {
|
|||||||
fileMode == FileMode.TREE || fileMode == FileMode.GITLINK,
|
fileMode == FileMode.TREE || fileMode == FileMode.GITLINK,
|
||||||
name,
|
name,
|
||||||
path,
|
path,
|
||||||
getSummaryMessage(commit.getFullMessage, commit.getShortMessage),
|
getSummaryMessage(
|
||||||
commit.getName,
|
commit.map(_.getFullMessage).getOrElse(""),
|
||||||
commit.getAuthorIdent.getWhen,
|
commit.map(_.getShortMessage).getOrElse("")
|
||||||
commit.getAuthorIdent.getName,
|
),
|
||||||
commit.getAuthorIdent.getEmailAddress,
|
commit.map(_.getName).getOrElse(""),
|
||||||
|
commit.map(_.getAuthorIdent.getWhen).orNull,
|
||||||
|
commit.map(_.getAuthorIdent.getName).getOrElse(""),
|
||||||
|
commit.map(_.getAuthorIdent.getEmailAddress).getOrElse(""),
|
||||||
linkUrl
|
linkUrl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -521,7 +523,7 @@ object JGitUtil {
|
|||||||
* get all file list by revision. only file.
|
* get all file list by revision. only file.
|
||||||
*/
|
*/
|
||||||
def getTreeId(git: Git, revision: String): Option[String] = {
|
def getTreeId(git: Git, revision: String): Option[String] = {
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
val objectId = git.getRepository.resolve(revision)
|
val objectId = git.getRepository.resolve(revision)
|
||||||
if (objectId == null) return None
|
if (objectId == null) return None
|
||||||
val revCommit = revWalk.parseCommit(objectId)
|
val revCommit = revWalk.parseCommit(objectId)
|
||||||
@@ -533,10 +535,10 @@ object JGitUtil {
|
|||||||
* get all file list by tree object id.
|
* get all file list by tree object id.
|
||||||
*/
|
*/
|
||||||
def getAllFileListByTreeId(git: Git, treeId: String): List[String] = {
|
def getAllFileListByTreeId(git: Git, treeId: String): List[String] = {
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
val objectId = git.getRepository.resolve(treeId + "^{tree}")
|
val objectId = git.getRepository.resolve(treeId + "^{tree}")
|
||||||
if (objectId == null) return Nil
|
if (objectId == null) return Nil
|
||||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||||
treeWalk.addTree(objectId)
|
treeWalk.addTree(objectId)
|
||||||
treeWalk.setRecursive(true)
|
treeWalk.setRecursive(true)
|
||||||
var ret: List[String] = Nil
|
var ret: List[String] = Nil
|
||||||
@@ -587,7 +589,7 @@ object JGitUtil {
|
|||||||
case _ => (logs, i.hasNext)
|
case _ => (logs, i.hasNext)
|
||||||
}
|
}
|
||||||
|
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
defining(git.getRepository.resolve(revision)) { objectId =>
|
defining(git.getRepository.resolve(revision)) { objectId =>
|
||||||
if (objectId == null) {
|
if (objectId == null) {
|
||||||
Left(s"${revision} can't be resolved.")
|
Left(s"${revision} can't be resolved.")
|
||||||
@@ -619,7 +621,7 @@ object JGitUtil {
|
|||||||
case false => logs
|
case false => logs
|
||||||
}
|
}
|
||||||
|
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
revWalk.markStart(revWalk.parseCommit(git.getRepository.resolve(begin)))
|
revWalk.markStart(revWalk.parseCommit(git.getRepository.resolve(begin)))
|
||||||
getCommitLog(revWalk.iterator, Nil).reverse
|
getCommitLog(revWalk.iterator, Nil).reverse
|
||||||
}
|
}
|
||||||
@@ -679,12 +681,12 @@ object JGitUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def getDiffEntries(git: Git, from: Option[String], to: String): Seq[DiffEntry] = {
|
private def getDiffEntries(git: Git, from: Option[String], to: String): Seq[DiffEntry] = {
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
val df = new DiffFormatter(DisabledOutputStream.INSTANCE)
|
val df = new DiffFormatter(DisabledOutputStream.INSTANCE)
|
||||||
df.setRepository(git.getRepository)
|
df.setRepository(git.getRepository)
|
||||||
|
|
||||||
val toCommit = revWalk.parseCommit(git.getRepository.resolve(to))
|
val toCommit = revWalk.parseCommit(git.getRepository.resolve(to))
|
||||||
from match {
|
(from match {
|
||||||
case None => {
|
case None => {
|
||||||
toCommit.getParentCount match {
|
toCommit.getParentCount match {
|
||||||
case 0 =>
|
case 0 =>
|
||||||
@@ -700,12 +702,12 @@ object JGitUtil {
|
|||||||
val fromCommit = revWalk.parseCommit(git.getRepository.resolve(from))
|
val fromCommit = revWalk.parseCommit(git.getRepository.resolve(from))
|
||||||
df.scan(fromCommit.getTree, toCommit.getTree).asScala
|
df.scan(fromCommit.getTree, toCommit.getTree).asScala
|
||||||
}
|
}
|
||||||
}
|
}).toSeq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getParentCommitId(git: Git, id: String): Option[String] = {
|
def getParentCommitId(git: Git, id: String): Option[String] = {
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
val commit = revWalk.parseCommit(git.getRepository.resolve(id))
|
val commit = revWalk.parseCommit(git.getRepository.resolve(id))
|
||||||
commit.getParentCount match {
|
commit.getParentCount match {
|
||||||
case 0 => None
|
case 0 => None
|
||||||
@@ -787,7 +789,7 @@ object JGitUtil {
|
|||||||
|
|
||||||
private def makePatchFromDiffEntry(git: Git, diff: DiffEntry): String = {
|
private def makePatchFromDiffEntry(git: Git, diff: DiffEntry): String = {
|
||||||
val out = new ByteArrayOutputStream()
|
val out = new ByteArrayOutputStream()
|
||||||
using(new DiffFormatter(out)) { formatter =>
|
Using.resource(new DiffFormatter(out)) { formatter =>
|
||||||
formatter.setRepository(git.getRepository)
|
formatter.setRepository(git.getRepository)
|
||||||
formatter.format(diff)
|
formatter.format(diff)
|
||||||
val patch = new String(out.toByteArray) // TODO charset???
|
val patch = new String(out.toByteArray) // TODO charset???
|
||||||
@@ -799,7 +801,7 @@ object JGitUtil {
|
|||||||
* Returns the list of branch names of the specified commit.
|
* Returns the list of branch names of the specified commit.
|
||||||
*/
|
*/
|
||||||
def getBranchesOfCommit(git: Git, commitId: String): List[String] =
|
def getBranchesOfCommit(git: Git, commitId: String): List[String] =
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
defining(revWalk.parseCommit(git.getRepository.resolve(commitId + "^0"))) { commit =>
|
defining(revWalk.parseCommit(git.getRepository.resolve(commitId + "^0"))) { commit =>
|
||||||
git.getRepository.getRefDatabase
|
git.getRepository.getRefDatabase
|
||||||
.getRefsByPrefix(Constants.R_HEADS)
|
.getRefsByPrefix(Constants.R_HEADS)
|
||||||
@@ -842,7 +844,7 @@ object JGitUtil {
|
|||||||
* Returns the list of tags which contains the specified commit.
|
* Returns the list of tags which contains the specified commit.
|
||||||
*/
|
*/
|
||||||
def getTagsOfCommit(git: Git, commitId: String): List[String] =
|
def getTagsOfCommit(git: Git, commitId: String): List[String] =
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
defining(revWalk.parseCommit(git.getRepository.resolve(commitId + "^0"))) { commit =>
|
defining(revWalk.parseCommit(git.getRepository.resolve(commitId + "^0"))) { commit =>
|
||||||
git.getRepository.getRefDatabase
|
git.getRepository.getRefDatabase
|
||||||
.getRefsByPrefix(Constants.R_TAGS)
|
.getRefsByPrefix(Constants.R_TAGS)
|
||||||
@@ -863,13 +865,13 @@ object JGitUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def initRepository(dir: java.io.File): Unit =
|
def initRepository(dir: java.io.File): Unit =
|
||||||
using(new RepositoryBuilder().setGitDir(dir).setBare.build) { repository =>
|
Using.resource(new RepositoryBuilder().setGitDir(dir).setBare.build) { repository =>
|
||||||
repository.create(true)
|
repository.create(true)
|
||||||
setReceivePack(repository)
|
setReceivePack(repository)
|
||||||
}
|
}
|
||||||
|
|
||||||
def cloneRepository(from: java.io.File, to: java.io.File): Unit =
|
def cloneRepository(from: java.io.File, to: java.io.File): Unit =
|
||||||
using(Git.cloneRepository.setURI(from.toURI.toString).setDirectory(to).setBare(true).call) { git =>
|
Using.resource(Git.cloneRepository.setURI(from.toURI.toString).setDirectory(to).setBare(true).call) { git =>
|
||||||
setReceivePack(git.getRepository)
|
setReceivePack(git.getRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -899,7 +901,7 @@ object JGitUtil {
|
|||||||
def createTag(git: Git, name: String, message: Option[String], commitId: String) = {
|
def createTag(git: Git, name: String, message: Option[String], commitId: String) = {
|
||||||
try {
|
try {
|
||||||
val objectId: ObjectId = git.getRepository.resolve(commitId)
|
val objectId: ObjectId = git.getRepository.resolve(commitId)
|
||||||
using(new RevWalk(git.getRepository)) { walk =>
|
Using.resource(new RevWalk(git.getRepository)) { walk =>
|
||||||
val tagCommand = git.tag().setName(name).setObjectId(walk.parseCommit(objectId))
|
val tagCommand = git.tag().setName(name).setObjectId(walk.parseCommit(objectId))
|
||||||
message.foreach { message =>
|
message.foreach { message =>
|
||||||
tagCommand.setMessage(message)
|
tagCommand.setMessage(message)
|
||||||
@@ -908,10 +910,10 @@ object JGitUtil {
|
|||||||
}
|
}
|
||||||
Right("Tag added.")
|
Right("Tag added.")
|
||||||
} catch {
|
} catch {
|
||||||
case e: GitAPIException => Left("Sorry, some Git operation error occurs.")
|
case e: ConcurrentRefUpdateException => Left("Sorry, some error occurs.")
|
||||||
case e: ConcurrentRefUpdateException => Left("Sorry some error occurs.")
|
|
||||||
case e: InvalidTagNameException => Left("Sorry, that name is invalid.")
|
case e: InvalidTagNameException => Left("Sorry, that name is invalid.")
|
||||||
case e: NoHeadException => Left("Sorry, this repo doesn't have HEAD reference")
|
case e: NoHeadException => Left("Sorry, this repo doesn't have HEAD reference")
|
||||||
|
case e: GitAPIException => Left("Sorry, some Git operation error occurs.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,13 +1000,14 @@ object JGitUtil {
|
|||||||
*/
|
*/
|
||||||
def getContentFromPath(git: Git, revTree: RevTree, path: String, fetchLargeFile: Boolean): Option[Array[Byte]] = {
|
def getContentFromPath(git: Git, revTree: RevTree, path: String, fetchLargeFile: Boolean): Option[Array[Byte]] = {
|
||||||
@scala.annotation.tailrec
|
@scala.annotation.tailrec
|
||||||
def getPathObjectId(path: String, walk: TreeWalk): Option[ObjectId] = walk.next match {
|
def getPathObjectId(path: String, walk: TreeWalk): Option[ObjectId] =
|
||||||
case true if (walk.getPathString == path) => Some(walk.getObjectId(0))
|
walk.next match {
|
||||||
case true => getPathObjectId(path, walk)
|
case true if (walk.getPathString == path) => Some(walk.getObjectId(0))
|
||||||
case false => None
|
case true => getPathObjectId(path, walk)
|
||||||
}
|
case false => None
|
||||||
|
}
|
||||||
|
|
||||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||||
treeWalk.addTree(revTree)
|
treeWalk.addTree(revTree)
|
||||||
treeWalk.setRecursive(true)
|
treeWalk.setRecursive(true)
|
||||||
getPathObjectId(path, treeWalk)
|
getPathObjectId(path, treeWalk)
|
||||||
@@ -1049,7 +1052,7 @@ object JGitUtil {
|
|||||||
|
|
||||||
def getContentInfo(git: Git, path: String, objectId: ObjectId): ContentInfo = {
|
def getContentInfo(git: Git, path: String, objectId: ObjectId): ContentInfo = {
|
||||||
// Viewer
|
// Viewer
|
||||||
using(git.getRepository.getObjectDatabase) { db =>
|
Using.resource(git.getRepository.getObjectDatabase) { db =>
|
||||||
val loader = db.open(objectId)
|
val loader = db.open(objectId)
|
||||||
val isLfs = isLfsPointer(loader)
|
val isLfs = isLfsPointer(loader)
|
||||||
val large = FileUtil.isLarge(loader.getSize)
|
val large = FileUtil.isLarge(loader.getSize)
|
||||||
@@ -1087,7 +1090,7 @@ object JGitUtil {
|
|||||||
*/
|
*/
|
||||||
def getContentFromId(git: Git, id: ObjectId, fetchLargeFile: Boolean): Option[Array[Byte]] =
|
def getContentFromId(git: Git, id: ObjectId, fetchLargeFile: Boolean): Option[Array[Byte]] =
|
||||||
try {
|
try {
|
||||||
using(git.getRepository.getObjectDatabase) { db =>
|
Using.resource(git.getRepository.getObjectDatabase) { db =>
|
||||||
val loader = db.open(id)
|
val loader = db.open(id)
|
||||||
if (loader.isLarge || (fetchLargeFile == false && FileUtil.isLarge(loader.getSize))) {
|
if (loader.isLarge || (fetchLargeFile == false && FileUtil.isLarge(loader.getSize))) {
|
||||||
None
|
None
|
||||||
@@ -1109,7 +1112,7 @@ object JGitUtil {
|
|||||||
*/
|
*/
|
||||||
def getObjectLoaderFromId[A](git: Git, id: ObjectId)(f: ObjectLoader => A): Option[A] =
|
def getObjectLoaderFromId[A](git: Git, id: ObjectId)(f: ObjectLoader => A): Option[A] =
|
||||||
try {
|
try {
|
||||||
using(git.getRepository.getObjectDatabase) { db =>
|
Using.resource(git.getRepository.getObjectDatabase) { db =>
|
||||||
Some(f(db.open(id)))
|
Some(f(db.open(id)))
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@@ -1132,8 +1135,8 @@ object JGitUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def processTree[T](git: Git, id: ObjectId)(f: (String, CanonicalTreeParser) => T): Seq[T] = {
|
def processTree[T](git: Git, id: ObjectId)(f: (String, CanonicalTreeParser) => T): Seq[T] = {
|
||||||
using(new RevWalk(git.getRepository)) { revWalk =>
|
Using.resource(new RevWalk(git.getRepository)) { revWalk =>
|
||||||
using(new TreeWalk(git.getRepository)) { treeWalk =>
|
Using.resource(new TreeWalk(git.getRepository)) { treeWalk =>
|
||||||
val index = treeWalk.addTree(revWalk.parseTree(id))
|
val index = treeWalk.addTree(revWalk.parseTree(id))
|
||||||
treeWalk.setRecursive(true)
|
treeWalk.setRecursive(true)
|
||||||
val result = new collection.mutable.ListBuffer[T]()
|
val result = new collection.mutable.ListBuffer[T]()
|
||||||
@@ -1177,7 +1180,7 @@ object JGitUtil {
|
|||||||
requestRepositoryName: String,
|
requestRepositoryName: String,
|
||||||
requestBranch: String
|
requestBranch: String
|
||||||
): (String, String) =
|
): (String, String) =
|
||||||
using(
|
Using.resources(
|
||||||
Git.open(Directory.getRepositoryDir(userName, repositoryName)),
|
Git.open(Directory.getRepositoryDir(userName, repositoryName)),
|
||||||
Git.open(Directory.getRepositoryDir(requestUserName, requestRepositoryName))
|
Git.open(Directory.getRepositoryDir(requestUserName, requestRepositoryName))
|
||||||
) { (oldGit, newGit) =>
|
) { (oldGit, newGit) =>
|
||||||
@@ -1247,7 +1250,7 @@ object JGitUtil {
|
|||||||
} finally {
|
} finally {
|
||||||
walk.dispose()
|
walk.dispose()
|
||||||
}
|
}
|
||||||
}
|
}.toSeq
|
||||||
}
|
}
|
||||||
|
|
||||||
def getBlame(git: Git, id: String, path: String): Iterable[BlameInfo] = {
|
def getBlame(git: Git, id: String, path: String): Iterable[BlameInfo] = {
|
||||||
@@ -1277,7 +1280,7 @@ object JGitUtil {
|
|||||||
}
|
}
|
||||||
idLine :+= (c.name, i)
|
idLine :+= (c.name, i)
|
||||||
}
|
}
|
||||||
val limeMap = idLine.groupBy(_._1).mapValues(_.map(_._2).toSet)
|
val limeMap = idLine.groupBy(_._1).view.mapValues(_.map(_._2).toSet)
|
||||||
blameMap.values.map { b =>
|
blameMap.values.map { b =>
|
||||||
b.copy(lines = limeMap(b.id))
|
b.copy(lines = limeMap(b.id))
|
||||||
}
|
}
|
||||||
@@ -1294,7 +1297,7 @@ object JGitUtil {
|
|||||||
* @return sha1
|
* @return sha1
|
||||||
*/
|
*/
|
||||||
def getShaByRef(owner: String, name: String, revstr: String): Option[String] = {
|
def getShaByRef(owner: String, name: String, revstr: String): Option[String] = {
|
||||||
using(Git.open(getRepositoryDir(owner, name))) { git =>
|
Using.resource(Git.open(getRepositoryDir(owner, name))) { git =>
|
||||||
Option(git.getRepository.resolve(revstr)).map(ObjectId.toString(_))
|
Option(git.getRepository.resolve(revstr)).map(ObjectId.toString(_))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1309,14 +1312,14 @@ object JGitUtil {
|
|||||||
if (lfsAttrs.nonEmpty) {
|
if (lfsAttrs.nonEmpty) {
|
||||||
val oid = lfsAttrs("oid").split(":")(1)
|
val oid = lfsAttrs("oid").split(":")(1)
|
||||||
|
|
||||||
using(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))) { in =>
|
Using.resource(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))) { in =>
|
||||||
f(in)
|
f(in)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new NoSuchElementException("LFS attribute is empty.")
|
throw new NoSuchElementException("LFS attribute is empty.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
using(loader.openStream()) { in =>
|
Using.resource(loader.openStream()) { in =>
|
||||||
f(in)
|
f(in)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1325,7 +1328,7 @@ object JGitUtil {
|
|||||||
def openFile[T](git: Git, repository: RepositoryService.RepositoryInfo, tree: RevTree, path: String)(
|
def openFile[T](git: Git, repository: RepositoryService.RepositoryInfo, tree: RevTree, path: String)(
|
||||||
f: InputStream => T
|
f: InputStream => T
|
||||||
): T = {
|
): T = {
|
||||||
using(TreeWalk.forPath(git.getRepository, path, tree)) { treeWalk =>
|
Using.resource(TreeWalk.forPath(git.getRepository, path, tree)) { treeWalk =>
|
||||||
openFile(git, repository, treeWalk)(f)
|
openFile(git, repository, treeWalk)(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,8 +127,14 @@ object LDAPUtil {
|
|||||||
private def getSslProvider(): Provider = {
|
private def getSslProvider(): Provider = {
|
||||||
val cachedInstance = provider.get()
|
val cachedInstance = provider.get()
|
||||||
if (cachedInstance == null) {
|
if (cachedInstance == null) {
|
||||||
val newInstance = Class
|
val cls = try {
|
||||||
.forName("com.sun.net.ssl.internal.ssl.Provider")
|
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()
|
.getDeclaredConstructor()
|
||||||
.newInstance()
|
.newInstance()
|
||||||
.asInstanceOf[Provider]
|
.asInstanceOf[Provider]
|
||||||
@@ -149,7 +155,7 @@ object LDAPUtil {
|
|||||||
keystore: String,
|
keystore: String,
|
||||||
error: String
|
error: String
|
||||||
)(f: LDAPConnection => Either[String, A]): Either[String, A] = {
|
)(f: LDAPConnection => Either[String, A]): Either[String, A] = {
|
||||||
if (tls) {
|
if (tls || ssl) {
|
||||||
// Dynamically set Sun as the security provider
|
// Dynamically set Sun as the security provider
|
||||||
Security.addProvider(getSslProvider())
|
Security.addProvider(getSslProvider())
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ object SyntaxSugars {
|
|||||||
|
|
||||||
def defining[A, B](value: A)(f: A => B): B = f(value)
|
def defining[A, B](value: A)(f: A => B): B = f(value)
|
||||||
|
|
||||||
|
@deprecated("Use scala.util.Using.resource instead", "4.32.0")
|
||||||
def using[A <: { def close(): Unit }, B](resource: A)(f: A => B): B =
|
def using[A <: { def close(): Unit }, B](resource: A)(f: A => B): B =
|
||||||
try f(resource)
|
try f(resource)
|
||||||
finally {
|
finally {
|
||||||
@@ -21,6 +22,7 @@ object SyntaxSugars {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@deprecated("Use scala.util.Using.resources instead", "4.32.0")
|
||||||
def using[A <: { def close(): Unit }, B <: { def close(): Unit }, C](resource1: A, resource2: B)(f: (A, B) => C): C =
|
def using[A <: { def close(): Unit }, B <: { def close(): Unit }, C](resource1: A, resource2: B)(f: (A, B) => C): C =
|
||||||
try f(resource1, resource2)
|
try f(resource1, resource2)
|
||||||
finally {
|
finally {
|
||||||
@@ -36,10 +38,12 @@ object SyntaxSugars {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@deprecated("Use scala.util.Using.resource instead", "4.32.0")
|
||||||
def using[T](git: Git)(f: Git => T): T =
|
def using[T](git: Git)(f: Git => T): T =
|
||||||
try f(git)
|
try f(git)
|
||||||
finally git.getRepository.close()
|
finally git.getRepository.close()
|
||||||
|
|
||||||
|
@deprecated("Use scala.util.Using.resources instead", "4.32.0")
|
||||||
def using[T](git1: Git, git2: Git)(f: (Git, Git) => T): T =
|
def using[T](git1: Git, git2: Git)(f: (Git, Git) => T): T =
|
||||||
try f(git1, git2)
|
try f(git1, git2)
|
||||||
finally {
|
finally {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ trait Validations {
|
|||||||
/**
|
/**
|
||||||
* Constraint for the identifier such as user name or page name.
|
* Constraint for the identifier such as user name or page name.
|
||||||
*/
|
*/
|
||||||
def identifier: Constraint = new Constraint() {
|
val identifier: Constraint = new Constraint() {
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||||
if (!value.matches("[a-zA-Z0-9\\-_.]+")) {
|
if (!value.matches("[a-zA-Z0-9\\-_.]+")) {
|
||||||
Some(s"${name} contains invalid character.")
|
Some(s"${name} contains invalid character.")
|
||||||
@@ -19,22 +19,10 @@ trait Validations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constraint for the password.
|
|
||||||
*/
|
|
||||||
def password: Constraint = new Constraint() {
|
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
|
||||||
if (System.getProperty("gitbucket.validate.password") != "false" && !value.matches("[a-zA-Z0-9\\-_.]+")) {
|
|
||||||
Some(s"${name} contains invalid character.")
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constraint for the repository identifier.
|
* Constraint for the repository identifier.
|
||||||
*/
|
*/
|
||||||
def repository: Constraint = new Constraint() {
|
val repository: Constraint = new Constraint() {
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||||
if (!value.matches("[a-zA-Z0-9\\-\\+_.]+")) {
|
if (!value.matches("[a-zA-Z0-9\\-\\+_.]+")) {
|
||||||
Some(s"${name} contains invalid character.")
|
Some(s"${name} contains invalid character.")
|
||||||
@@ -48,7 +36,7 @@ trait Validations {
|
|||||||
/**
|
/**
|
||||||
* Constraint for the color pattern.
|
* Constraint for the color pattern.
|
||||||
*/
|
*/
|
||||||
def color = pattern("#[0-9a-fA-F]{6}")
|
val color = pattern("#[0-9a-fA-F]{6}")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ValueType for the java.util.Date property.
|
* ValueType for the java.util.Date property.
|
||||||
|
|||||||
@@ -169,22 +169,31 @@ object Markdown {
|
|||||||
private def fixUrl(url: String, branch: String, isImage: Boolean = false): String = {
|
private def fixUrl(url: String, branch: String, isImage: Boolean = false): String = {
|
||||||
lazy val urlWithRawParam: String = url + (if (isImage && !url.endsWith("?raw=true")) "?raw=true" else "")
|
lazy val urlWithRawParam: String = url + (if (isImage && !url.endsWith("?raw=true")) "?raw=true" else "")
|
||||||
|
|
||||||
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("mailto:") || url.startsWith("/")) {
|
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("mailto:")) {
|
||||||
url
|
url
|
||||||
|
} else if (url.startsWith("/")) {
|
||||||
|
context.baseUrl + url
|
||||||
} else if (url.startsWith("#")) {
|
} else if (url.startsWith("#")) {
|
||||||
("#" + generateAnchorName(url.substring(1)))
|
("#" + generateAnchorName(url.substring(1)))
|
||||||
} else if (!enableWikiLink) {
|
|
||||||
if (context.currentPath.contains("/blob/")) {
|
|
||||||
urlWithRawParam
|
|
||||||
} else if (context.currentPath.contains("/tree/")) {
|
|
||||||
val paths = context.currentPath.split("/")
|
|
||||||
val path = if (paths.length > 3) paths.drop(4).mkString("/") else branch
|
|
||||||
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + path + "/" + urlWithRawParam
|
|
||||||
} else {
|
|
||||||
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + branch + "/" + urlWithRawParam
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/_blob/" + url
|
// Relative path
|
||||||
|
if (!enableWikiLink) {
|
||||||
|
if (context.currentPath.contains("/blob/")) {
|
||||||
|
val paths = context.currentPath.split("/").dropRight(1)
|
||||||
|
val path = if (paths.length > 3) paths.drop(4).mkString("/") else branch
|
||||||
|
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + path + "/" + urlWithRawParam
|
||||||
|
} else if (context.currentPath.contains("/tree/")) {
|
||||||
|
val paths = context.currentPath.split("/")
|
||||||
|
val path = if (paths.length > 3) paths.drop(4).mkString("/") else branch
|
||||||
|
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/blob/" + path + "/" + urlWithRawParam
|
||||||
|
} else {
|
||||||
|
repository.httpUrl
|
||||||
|
.replaceFirst("/git/", "/")
|
||||||
|
.stripSuffix(".git") + "/blob/" + branch + "/" + urlWithRawParam
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repository.httpUrl.replaceFirst("/git/", "/").stripSuffix(".git") + "/wiki/_blob/" + url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.util.{Date, Locale, TimeZone}
|
|||||||
import com.nimbusds.jose.util.JSONObjectUtils
|
import com.nimbusds.jose.util.JSONObjectUtils
|
||||||
import gitbucket.core.controller.Context
|
import gitbucket.core.controller.Context
|
||||||
import gitbucket.core.model.CommitState
|
import gitbucket.core.model.CommitState
|
||||||
|
import gitbucket.core.model.PullRequest
|
||||||
import gitbucket.core.plugin.{PluginRegistry, RenderRequest}
|
import gitbucket.core.plugin.{PluginRegistry, RenderRequest}
|
||||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||||
import gitbucket.core.service.{RepositoryService, RequestCache}
|
import gitbucket.core.service.{RepositoryService, RequestCache}
|
||||||
@@ -218,14 +219,18 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
|||||||
.replaceAll(
|
.replaceAll(
|
||||||
"\\[branch:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]",
|
"\\[branch:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]",
|
||||||
(m: Match) =>
|
(m: Match) =>
|
||||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${m
|
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${StringUtil
|
||||||
.group(3)}</a>"""
|
.escapeHtml(
|
||||||
|
m.group(3)
|
||||||
|
)}</a>"""
|
||||||
)
|
)
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
"\\[tag:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]",
|
"\\[tag:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]",
|
||||||
(m: Match) =>
|
(m: Match) =>
|
||||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${m
|
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${StringUtil
|
||||||
.group(3)}</a>"""
|
.escapeHtml(
|
||||||
|
m.group(3)
|
||||||
|
)}</a>"""
|
||||||
)
|
)
|
||||||
.replaceAll("\\[user:([^\\s]+?)\\]", (m: Match) => user(m.group(1)).body)
|
.replaceAll("\\[user:([^\\s]+?)\\]", (m: Match) => user(m.group(1)).body)
|
||||||
.replaceAll(
|
.replaceAll(
|
||||||
@@ -237,8 +242,10 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
|||||||
.replaceAll(
|
.replaceAll(
|
||||||
"\\[release:([^\\s]+?)/([^\\s]+?)/([^\\s]+?):(.+)\\]",
|
"\\[release:([^\\s]+?)/([^\\s]+?)/([^\\s]+?):(.+)\\]",
|
||||||
(m: Match) =>
|
(m: Match) =>
|
||||||
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/releases/${encodeRefName(m.group(3))}">${m
|
s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/releases/${encodeRefName(m.group(3))}">${StringUtil
|
||||||
.group(4)}</a>"""
|
.escapeHtml(
|
||||||
|
m.group(4)
|
||||||
|
)}</a>"""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -267,6 +274,18 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
|||||||
*/
|
*/
|
||||||
def url(userName: String)(implicit context: Context): String = s"${context.path}/${encodeRefName(userName)}"
|
def url(userName: String)(implicit context: Context): String = s"${context.path}/${encodeRefName(userName)}"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the url to the pull request base branch.
|
||||||
|
*/
|
||||||
|
def basePRBranchUrl(pullreq: PullRequest)(implicit context: Context): String =
|
||||||
|
s"${context.path}/${encodeRefName(pullreq.userName)}/${encodeRefName(pullreq.repositoryName)}/tree/${encodeRefName(pullreq.branch)}"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the url to the pull request branch.
|
||||||
|
*/
|
||||||
|
def requestPRBranchUrl(pullreq: PullRequest)(implicit context: Context): String =
|
||||||
|
s"${context.path}/${encodeRefName(pullreq.requestUserName)}/${encodeRefName(pullreq.repositoryName)}/tree/${encodeRefName(pullreq.requestBranch)}"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the url to the root of assets.
|
* Returns the url to the root of assets.
|
||||||
*/
|
*/
|
||||||
@@ -475,4 +494,8 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
|||||||
|
|
||||||
case class CommentDiffLine(newLine: Option[String], oldLine: Option[String], `type`: String, text: String)
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,21 +12,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding-left: 10px; padding-right: 10px;">
|
<div style="padding-left: 10px; padding-right: 10px;">
|
||||||
@account.description.map{ description =>
|
@account.description.map { description =>
|
||||||
<p style="color: #999">@description</p>
|
<p style="color: #999">@description</p>
|
||||||
}
|
}
|
||||||
@if(account.url.isDefined){
|
@account.url.map { url =>
|
||||||
<p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
<p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||||
<i class="octicon octicon-home"></i> <a href="@account.url">@account.url</a>
|
<i class="octicon octicon-home"></i>
|
||||||
|
@if(url.startsWith("http://") || url.startsWith("https://")){
|
||||||
|
<a href="@url">@url</a>
|
||||||
|
} else {
|
||||||
|
<span style="color: #999">@url</span>
|
||||||
|
}
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
@if(context.settings.showMailAddress){
|
@if(context.settings.showMailAddress){
|
||||||
<p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
<p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||||
<i class="octicon octicon-mail"></i> <a href="mailto: @account.mailAddress">@account.mailAddress</a>
|
<i class="octicon octicon-mail"></i> <a href="mailto:@account.mailAddress">@account.mailAddress</a>
|
||||||
</p>
|
</p>
|
||||||
@extraMailAddresses.map{ mail =>
|
@extraMailAddresses.map { mail =>
|
||||||
<p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
<p style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||||
<i class="octicon octicon-mail"></i> <a href="mailto: @mail">@mail</a>
|
<i class="octicon octicon-mail"></i> <a href="mailto:@mail">@mail</a>
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,10 +84,10 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
|
|||||||
<span class="strong">Copy existing git repository</span>
|
<span class="strong">Copy existing git repository</span>
|
||||||
<div class="normal muted">
|
<div class="normal muted">
|
||||||
Create new repository from existing git repository.
|
Create new repository from existing git repository.
|
||||||
<input type="text" class="form-control" name="sourceUrl" id="sourceUrl" disabled placeholder="Source git repository URL..."/>
|
|
||||||
<span id="error-sourceUrl" class="error"></span>
|
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
<input type="text" class="form-control" name="sourceUrl" id="sourceUrl" disabled placeholder="Source git repository URL..."/>
|
||||||
|
<span id="error-sourceUrl" class="error"></span>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="border-top form-actions">
|
<fieldset class="border-top form-actions">
|
||||||
<input type="submit" class="btn btn-success" value="Create repository"/>
|
<input type="submit" class="btn btn-success" value="Create repository"/>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<form method="POST" action="@context.path/@account.userName/_ssh" validate="true" autocomplete="off">
|
<form method="POST" action="@context.path/@account.userName/_ssh" validate="true" autocomplete="off">
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading strong">Add a SSH Key</div>
|
<div class="panel-heading strong">Add a public SSH Key</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<fieldset class="form-group">
|
<fieldset class="form-group">
|
||||||
<label for="title" class="strong">Title</label>
|
<label for="title" class="strong">Title</label>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@(plugins: List[(gitbucket.core.plugin.PluginInfoBase, Boolean, String)], info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
@(plugins: List[gitbucket.core.plugin.PluginInfoBase], info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
||||||
@gitbucket.core.html.main("Plugins"){
|
@gitbucket.core.html.main("Plugins"){
|
||||||
@gitbucket.core.admin.html.menu("plugins") {
|
@gitbucket.core.admin.html.menu("plugins") {
|
||||||
@gitbucket.core.helper.html.information(info)
|
@gitbucket.core.helper.html.information(info)
|
||||||
@@ -8,26 +8,17 @@
|
|||||||
<h1 class="system-settings-title">Plugins</h1>
|
<h1 class="system-settings-title">Plugins</h1>
|
||||||
@if(plugins.size > 0) {
|
@if(plugins.size > 0) {
|
||||||
<ul>
|
<ul>
|
||||||
@plugins.map { case (plugin, enabled, updatableVersion) =>
|
@plugins.map { plugin =>
|
||||||
<li><a href="#@plugin.pluginId">@plugin.pluginId:@plugin.pluginVersion</a></li>
|
<li><a href="#@plugin.pluginId">@plugin.pluginId:@plugin.pluginVersion</a></li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@plugins.map { case (plugin, enabled, updatableVersion) =>
|
@plugins.map { plugin =>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading strong" id="@plugin.pluginId">
|
<div class="panel-heading strong" id="@plugin.pluginId">
|
||||||
<form method="POST" class="pull-right">
|
<form method="POST" class="pull-right">
|
||||||
@if(enabled){
|
<input type="submit" value="Uninstall" class="btn btn-danger btn-sm uninstall-plugin" style="position: relative; top: -5px; left: 10px;"
|
||||||
@if(updatableVersion.nonEmpty){
|
data-name="@plugin.pluginName" formaction="@{context.path}/admin/plugins/@{plugin.pluginId}/_uninstall">
|
||||||
<input type="submit" value="Update" class="btn btn-success btn-sm update-plugin" style="position: relative; top: -5px; left: 10px;"
|
|
||||||
data-name="@plugin.pluginName" formaction="@{context.path}/admin/plugins/@{plugin.pluginId}/@{updatableVersion}/_install">
|
|
||||||
}
|
|
||||||
<input type="submit" value="Uninstall" class="btn btn-danger btn-sm uninstall-plugin" style="position: relative; top: -5px; left: 10px;"
|
|
||||||
data-name="@plugin.pluginName" formaction="@{context.path}/admin/plugins/@{plugin.pluginId}/_uninstall">
|
|
||||||
} else {
|
|
||||||
<input type="submit" value="Install" class="btn btn-success btn-sm install-plugin" style="position: relative; top: -5px; left: 10px;"
|
|
||||||
data-name="@plugin.pluginName" formaction="@{context.path}/admin/plugins/@{plugin.pluginId}/@{plugin.pluginVersion}/_install">
|
|
||||||
}
|
|
||||||
</form>
|
</form>
|
||||||
@plugin.pluginName
|
@plugin.pluginName
|
||||||
</div>
|
</div>
|
||||||
@@ -58,15 +49,5 @@
|
|||||||
var name = $(e.target).data('name');
|
var name = $(e.target).data('name');
|
||||||
return confirm('Uninstall ' + name + '. Are you sure?');
|
return confirm('Uninstall ' + name + '. Are you sure?');
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.install-plugin').click(function(e){
|
|
||||||
var name = $(e.target).data('name');
|
|
||||||
return confirm('Install ' + name + '. Are you sure?');
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.update-plugin').click(function(e){
|
|
||||||
var name = $(e.target).data('name');
|
|
||||||
return confirm('Update ' + name + '. Are you sure?');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -7,19 +7,19 @@
|
|||||||
<form action="@context.path/admin/system" method="POST" validate="true" class="form-horizontal" autocomplete="off">
|
<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">
|
<ul class="nav nav-tabs fill-width" id="pullreq-tab">
|
||||||
<li><a href="#system">System settings</a></li>
|
<li><a href="#system">System settings</a></li>
|
||||||
|
<li><a href="#integrations">Integrations</a></li>
|
||||||
<li><a href="#authentication">Authentication</a></li>
|
<li><a href="#authentication">Authentication</a></li>
|
||||||
<li><a href="#plugins">Plugins</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<div class="tab-content fill-width" style="padding-top: 20px;">
|
<div class="tab-content fill-width" style="padding-top: 20px;">
|
||||||
<div class="tab-pane" id="system">
|
<div class="tab-pane" id="system">
|
||||||
@settings_system(info)
|
@settings_system(info)
|
||||||
</div>
|
</div>
|
||||||
|
<div class="tab-pane" id="integrations">
|
||||||
|
@settings_integrations(info)
|
||||||
|
</div>
|
||||||
<div class="tab-pane" id="authentication">
|
<div class="tab-pane" id="authentication">
|
||||||
@settings_authentication(info)
|
@settings_authentication(info)
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="plugins">
|
|
||||||
@settings_plugins(info)
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="align-right" style="margin-top: 20px;">
|
<div class="align-right" style="margin-top: 20px;">
|
||||||
@@ -34,9 +34,9 @@ $(function(){
|
|||||||
if(location.hash == '#authentication'){
|
if(location.hash == '#authentication'){
|
||||||
$('li:has(a[href="#authentication"])').addClass('active');
|
$('li:has(a[href="#authentication"])').addClass('active');
|
||||||
$('div#authentication').addClass('active');
|
$('div#authentication').addClass('active');
|
||||||
} else if(location.hash == '#plugins'){
|
} else if(location.hash == '#integrations'){
|
||||||
$('li:has(a[href="#plugins"])').addClass('active');
|
$('li:has(a[href="#integrations"])').addClass('active');
|
||||||
$('div#plugins').addClass('active');
|
$('div#integrations').addClass('active');
|
||||||
} else {
|
} else {
|
||||||
$('li:has(a[href="#system"])').addClass('active');
|
$('li:has(a[href="#system"])').addClass('active');
|
||||||
$('div#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>
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
@(info: Option[Any])(implicit context: gitbucket.core.controller.Context)
|
|
||||||
<label class="strong">Plugin repositories</label>
|
|
||||||
<fieldset>
|
|
||||||
<label class="checkbox">
|
|
||||||
<input type="checkbox" id="pluginNetworkInstall" name="pluginNetworkInstall"@if(context.settings.pluginNetworkInstall){ checked} />
|
|
||||||
<a href="https://plugins.gitbucket-community.org" target="_blank">plugins.gitbucket-community.org</a>
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
<hr>
|
|
||||||
<fieldset>
|
|
||||||
<label class="checkbox">
|
|
||||||
<input type="checkbox" id="useProxy" name="useProxy"@if(context.settings.pluginProxy.isDefined){ checked} />
|
|
||||||
Use proxy
|
|
||||||
</label>
|
|
||||||
</fieldset>
|
|
||||||
<div class="proxy">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-2" for="proxyHost">Proxy host</label>
|
|
||||||
<div class="col-md-10">
|
|
||||||
<input type="text" id="proxyHost" name="proxy.host" class="form-control" value="@context.settings.pluginProxy.map(_.host)"/>
|
|
||||||
<span id="error-proxy_host" class="error"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-2" for="proxyPort">Proxy port</label>
|
|
||||||
<div class="col-md-10">
|
|
||||||
<input type="text" id="proxyPort" name="proxy.port" class="form-control input-mini" value="@context.settings.pluginProxy.map(_.port)"/>
|
|
||||||
<span id="error-proxy_port" class="error"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-2" for="proxyUser">Proxy user</label>
|
|
||||||
<div class="col-md-10">
|
|
||||||
<input type="text" id="proxyUser" name="proxy.user" class="form-control" value="@context.settings.pluginProxy.map(_.user)"/>
|
|
||||||
<span id="error-proxy_user" class="error"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="control-label col-md-2" for="proxyPassword">Proxy password</label>
|
|
||||||
<div class="col-md-10">
|
|
||||||
<input type="password" id="proxyPassword" name="proxy.password" class="form-control" value="@context.settings.pluginProxy.map(_.password)"/>
|
|
||||||
<span id="error-proxy_password" class="error"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
$(function(){
|
|
||||||
$('#useProxy').change(function(){
|
|
||||||
$('.proxy input').prop('disabled', !$(this).prop('checked'));
|
|
||||||
}).change();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -94,6 +94,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 -->
|
<!-- Account registration -->
|
||||||
<!--====================================================================-->
|
<!--====================================================================-->
|
||||||
<hr>
|
<hr>
|
||||||
@@ -108,8 +116,80 @@
|
|||||||
<span class="strong">Deny</span> <span class="normal">- Only administrators can create accounts.</span>
|
<span class="strong">Deny</span> <span class="normal">- Only administrators can create accounts.</span>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<!--====================================================================-->
|
||||||
|
<!-- Repository operations -->
|
||||||
|
<!--====================================================================-->
|
||||||
<hr>
|
<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>
|
<fieldset>
|
||||||
<label class="radio">
|
<label class="radio">
|
||||||
<input type="radio" name="isCreateRepoOptionPublic" value="true"@if(context.settings.isCreateRepoOptionPublic){ checked}>
|
<input type="radio" name="isCreateRepoOptionPublic" value="true"@if(context.settings.isCreateRepoOptionPublic){ checked}>
|
||||||
@@ -151,6 +231,41 @@
|
|||||||
</label>
|
</label>
|
||||||
</fieldset>
|
</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 -->
|
<!-- Activity -->
|
||||||
<!--====================================================================-->
|
<!--====================================================================-->
|
||||||
<hr>
|
<hr>
|
||||||
@@ -165,122 +280,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<!--====================================================================-->
|
<!--====================================================================-->
|
||||||
<!-- Services -->
|
<!-- Sidebar -->
|
||||||
<!--====================================================================-->
|
<!--====================================================================-->
|
||||||
<hr>
|
<hr>
|
||||||
<label class="strong">Services</label>
|
<label><span class="strong">Show Repositories in Sidebar</span></label>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label class="checkbox">
|
<label class="radio">
|
||||||
<input type="checkbox" name="gravatar"@if(context.settings.gravatar){ checked}/>
|
<input type="radio" name="limitVisibleRepositories" value="false"@if(!context.settings.limitVisibleRepositories){ checked}>
|
||||||
Use Gravatar for profile images
|
<span class="strong">All</span> <span class="normal">- Show all repositories in sidebar.</span>
|
||||||
</label>
|
</label>
|
||||||
</fieldset>
|
<label class="radio">
|
||||||
<!--====================================================================-->
|
<input type="radio" name="limitVisibleRepositories" value="true"@if(context.settings.limitVisibleRepositories){ checked}>
|
||||||
<!-- SSH access -->
|
<span class="strong">Limited</span> <span class="normal">- Limit the visible repositories in sidebar.</span>
|
||||||
<!--====================================================================-->
|
|
||||||
<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>
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<script>
|
<script>
|
||||||
@@ -292,57 +303,5 @@ $(function(){
|
|||||||
themeCss.attr('href', themeCss.attr('href').replace(oldVal, that.val()));
|
themeCss.attr('href', themeCss.attr('href').replace(oldVal, that.val()));
|
||||||
$(document.body).removeClass(oldVal).addClass(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();
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) =>
|
@issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) => {
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding-top: 12px; padding-bottom: 12px;">
|
<td style="padding-top: 12px; padding-bottom: 12px;">
|
||||||
<a href="@context.path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a> ・
|
<a href="@context.path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a> ・
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}}
|
||||||
@if(issues.isEmpty){
|
@if(issues.isEmpty){
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 20px; background-color: #eee; text-align: center;">
|
<td style="padding: 20px; background-color: #eee; text-align: center;">
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ $(function(){
|
|||||||
}
|
}
|
||||||
@dropzone(clickable: Boolean, textareaId: Option[String]) = {
|
@dropzone(clickable: Boolean, textareaId: Option[String]) = {
|
||||||
url: '@context.path/upload/file/@repository.owner/@repository.name',
|
url: '@context.path/upload/file/@repository.owner/@repository.name',
|
||||||
maxFilesize: @{FileUtil.MaxFileSize / 1024 / 1024},
|
maxFilesize: @{context.settings.upload.maxFileSize / 1024 / 1024},
|
||||||
|
timeout: @{context.settings.upload.timeout},
|
||||||
clickable: @clickable,
|
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>",
|
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) {
|
success: function(file, id) {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
@(latestUpdatedDate: java.util.Date,
|
@(latestUpdatedDate: java.util.Date,
|
||||||
recentOnly: Boolean = true)
|
recentOnly: Boolean = true)
|
||||||
@import gitbucket.core.view.helpers
|
@import gitbucket.core.view.helpers
|
||||||
<span data-toggle="tooltip" title="@helpers.datetime(latestUpdatedDate)">
|
@if(latestUpdatedDate != null){
|
||||||
@if(recentOnly){
|
<span data-toggle="tooltip" title="@helpers.datetime(latestUpdatedDate)">
|
||||||
@helpers.datetimeAgoRecentOnly(latestUpdatedDate)
|
@if(recentOnly){
|
||||||
} else {
|
@helpers.datetimeAgoRecentOnly(latestUpdatedDate)
|
||||||
@helpers.datetimeAgo(latestUpdatedDate)
|
} else {
|
||||||
}
|
@helpers.datetimeAgo(latestUpdatedDate)
|
||||||
</span>
|
}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
<table class="table table-bordered diff-outside" commitId="@newCommitId" fileName="@diff.newPath" data-diff-id="@i">
|
<table class="table table-bordered diff-outside" commitId="@newCommitId" fileName="@diff.newPath" data-diff-id="@i">
|
||||||
<tr>
|
<tr>
|
||||||
<th style="font-weight: normal;" class="box-header">
|
<th style="font-weight: normal;" class="box-header">
|
||||||
|
<i class="fa fa-chevron-down rotate" data-toggle="collapse" data-target=".diff-collapse-@i" aria-hidden="true" style="cursor: pointer; font-size: 12px"></i>
|
||||||
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
||||||
@if(newCommitId.isDefined){
|
@if(newCommitId.isDefined){
|
||||||
<div class="pull-right align-right">
|
<div class="pull-right align-right">
|
||||||
@@ -92,7 +93,7 @@
|
|||||||
}
|
}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr class="diff-collapse-@i collapse in">
|
||||||
<td style="padding: 0;">
|
<td style="padding: 0;">
|
||||||
@if(diff.oldObjectId == diff.newObjectId){
|
@if(diff.oldObjectId == diff.newObjectId){
|
||||||
@if(diff.oldPath != diff.newPath){
|
@if(diff.oldPath != diff.newPath){
|
||||||
@@ -103,8 +104,8 @@
|
|||||||
} else {
|
} else {
|
||||||
@if(diff.newContent != None || diff.oldContent != None){
|
@if(diff.newContent != None || diff.oldContent != None){
|
||||||
<div id="diffText-@i" class="diffText"></div>
|
<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="newText-@i" data-file-name="@diff.oldPath" data-val="@diff.newContent">
|
||||||
<input type="hidden" id="oldText-@i" data-file-name="@diff.newPath" value="@diff.oldContent">
|
<input type="hidden" id="oldText-@i" data-file-name="@diff.newPath" data-val="@diff.oldContent">
|
||||||
} else {
|
} else {
|
||||||
@if(diff.newIsImage || diff.oldIsImage){
|
@if(diff.newIsImage || diff.oldIsImage){
|
||||||
<div class="diff-image-render diff2up">
|
<div class="diff-image-render diff2up">
|
||||||
@@ -139,6 +140,17 @@
|
|||||||
<script type="text/javascript" src="@helpers.assets("/vendors/jsdifflib/difflib.js")"></script>
|
<script type="text/javascript" src="@helpers.assets("/vendors/jsdifflib/difflib.js")"></script>
|
||||||
<link href="@helpers.assets("/vendors/jsdifflib/diffview.css")" type="text/css" rel="stylesheet" />
|
<link href="@helpers.assets("/vendors/jsdifflib/diffview.css")" type="text/css" rel="stylesheet" />
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
$(".rotate").click(function(){
|
||||||
|
if($(this).hasClass('fa-chevron-right')) {
|
||||||
|
$(this).removeClass('fa-chevron-right');
|
||||||
|
$(this).addClass('fa-chevron-down');
|
||||||
|
} else {
|
||||||
|
$(this).removeClass('fa-chevron-down');
|
||||||
|
$(this).addClass('fa-chevron-right');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$(function(){
|
$(function(){
|
||||||
@if(showIndex){
|
@if(showIndex){
|
||||||
$('#toggle-file-list').click(function(){
|
$('#toggle-file-list').click(function(){
|
||||||
|
|||||||
@@ -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 =>
|
@defining(gitbucket.core.view.Pagination(page, count, limit, width)){ p =>
|
||||||
@if(p.count > p.limit){
|
@if(p.count > p.limit){
|
||||||
<div>
|
<div>
|
||||||
@@ -6,7 +7,7 @@
|
|||||||
@if(page == 1){
|
@if(page == 1){
|
||||||
<li class="disabled"><span>◀</span></li>
|
<li class="disabled"><span>◀</span></li>
|
||||||
} else {
|
} 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){
|
@for(i <- 1 to p.max){
|
||||||
@if(i == p.max && p.omitRight){
|
@if(i == p.max && p.omitRight){
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
<li class="active"><span>@i</span></li>
|
<li class="active"><span>@i</span></li>
|
||||||
} else {
|
} else {
|
||||||
@if(p.visibleFor(i)){
|
@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){
|
@if(i == 1 && p.omitLeft){
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
@if(page == p.max){
|
@if(page == p.max){
|
||||||
<li class="disabled"><span>▶</span></li>
|
<li class="disabled"><span>▶</span></li>
|
||||||
} else {
|
} else {
|
||||||
<li><a href="@baseURL&page=@(page + 1)">▶</a></li>
|
<li><a href="@helpers.appendQueryString(baseURL, s"page=${page + 1}")">▶</a></li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -114,9 +114,9 @@
|
|||||||
@gitbucket.core.helper.html.datetimeago(comment.registeredDate)
|
@gitbucket.core.helper.html.datetimeago(comment.registeredDate)
|
||||||
</div>
|
</div>
|
||||||
<div style="discussion-item-content">
|
<div style="discussion-item-content">
|
||||||
@defining(comment.content.split(":")){ case Array(issueId, rest @ _*) =>
|
@defining(comment.content.split(":")){ case Array(issueId, rest @ _*) => {
|
||||||
@helpers.issueLink(repository, issueId.toInt, rest.mkString(":"))
|
@helpers.issueLink(repository, issueId.toInt, rest.mkString(":"))
|
||||||
}
|
}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user