Compare commits

...

72 Commits
4.11 ... 4.12.1

Author SHA1 Message Date
Naoki Takezoe
014bdb9d5c Bump to 4.12.1 2017-05-04 10:29:48 +09:00
Damian Bronecki
bef65870d8 Fix redirect issue by partially reverting to 956af54 - fixes #1567 2017-05-04 10:28:52 +09:00
Naoki Takezoe
df4b1d6f01 Merge remote-tracking branch 'origin/master' 2017-04-29 09:25:42 +09:00
Naoki Takezoe
c5a53f0719 Release 4.12 2017-04-29 09:25:14 +09:00
Naoki Takezoe
961e21e5a7 Merge pull request #1562 from tkgdsg/filter_branch
dropdown menu filter method modified
2017-04-29 02:15:53 +09:00
Yasuhiro Takagi
e33e304644 dropdown menu filter method modified
intermediate match & case insensitive not using regexp
2017-04-28 21:05:44 +09:00
Naoki Takezoe
46896da46e Fix to avoid regular expression error in the filter box 2017-04-28 08:26:50 +09:00
Naoki Takezoe
207aa8b8c1 Merge pull request #1555 from tkgdsg/dropdown_menu_filter_in_compare
dropdown menu filter in branch compare page
2017-04-27 10:06:19 +09:00
Yasuhiro Takagi
8b4017a082 dropdown menu filter in compare page
filter applied "base fork:" "base:" "head fork:" "compare:" menus.
2017-04-26 21:22:46 +09:00
Naoki Takezoe
f46f5909f1 (refs #1554)Enable DB session for plugin git repo authentication 2017-04-25 13:53:50 +09:00
Naoki Takezoe
5337b29532 Disable dock icon 2017-04-25 09:41:42 +09:00
Naoki Takezoe
7010b316fd (refs #1551)Fix validation for repository name 2017-04-25 09:32:13 +09:00
Naoki Takezoe
6128258cfb Bump to 4.12.0 2017-04-23 23:32:24 +09:00
Naoki Takezoe
83e619ecd4 Merge pull request #1550 from tkgdsg/branch_filter_improve
modify branch filter method
2017-04-21 10:07:46 +09:00
YT
3af89a7897 modify branch filter method 2017-04-20 20:26:45 +09:00
Naoki Takezoe
1e94b69a68 Merge pull request #1548 from kounoike/pr-fix-1525
Fix #1525 move attached directory when rename/transfer repo.
2017-04-20 11:21:33 +09:00
Naoki Takezoe
95b1945bc1 Merge pull request #1547 from tkgdsg/archive_root_dir
root dir in archive file
2017-04-20 11:20:59 +09:00
Naoki Takezoe
8aaba606bc Merge pull request #1545 from motohacy/master
dropdown-menu become scrollable
2017-04-20 11:18:59 +09:00
Naoki Takezoe
f40657a7ff Merge pull request #1546 from xuwei-k/Scala-2.12.2
Scala 2.12.2
2017-04-20 11:15:45 +09:00
KOUNOIKE Yuusuke
788e56d926 Fix #1525 move attached directory when rename/transfer repo. 2017-04-19 20:46:52 +09:00
YT
37303a8c5a make dir in archive 2017-04-19 20:19:03 +09:00
xuwei-k
c6449d4c10 Scala 2.12.2 2017-04-19 10:30:11 +02:00
Naoki Takezoe
9c078971ab Don't retrieve unused repository information when withoutPhysicalInfo is true 2017-04-18 18:42:56 +09:00
motohacy
0f0c3c1b1d dropdown-menu become scrollable
dropdown-menu become scrollable.
2017-04-18 17:24:07 +09:00
Naoki Takezoe
835f35393e Don't retrieve unused repository information when withoutPhysicalInfo is true 2017-04-18 11:31:03 +09:00
Naoki Takezoe
e576e14460 Update database configuration warning message 2017-04-16 15:30:29 +09:00
Naoki Takezoe
a839e9eab5 Merge pull request #1518 from kounoike/pr-caution-for-h2
Add caution message when using H2 database
2017-04-16 14:54:29 +09:00
Naoki Takezoe
e7b368ced2 Merge pull request #1537 from tkgdsg/archivefile_naming_rule_when_sha1
To have compatibility with GitHub/GitHubEnterprise
2017-04-16 14:53:22 +09:00
Naoki Takezoe
d662df5a7d Merge pull request #1542 from dbronecki/fix/1539
Word wrap commit descriptions
2017-04-16 14:36:38 +09:00
Damian Bronecki
7c4a286937 Fixes #1539 2017-04-15 23:20:57 +02:00
Naoki Takezoe
2164b8ce31 Merge pull request #1534 from aadrian/patch-1
improve workding
2017-04-14 10:16:31 +09:00
Naoki Takezoe
71737eb018 Merge pull request #1540 from dbronecki/fix-1481
Fixes #1481
2017-04-14 08:28:54 +09:00
Damian Bronecki
ed543847a8 Fixes #1481 2017-04-13 20:43:20 +02:00
Yasuhiro Takagi
c0320d3139 I refactor some code. 2017-04-11 22:41:55 +09:00
Yasuhiro Takagi
b218c2284e To have compatibility with GitHub/GitHubEnterprise
when archive downloading with sha1, even if supplied part of sha1,
GH/GHE gives a filename with full(long) sha1.

This patch make GitBucket to be compatible with GH/GHE behaviour.

You can check that difference by following url.
I upload same repo as in GitHuB to GitBucket demo site.
 
GitHub:  
https://github.com/MunGell/awesome-for-beginners/archive/fc7d067.tar.gz
GitBucket:
http://gitbucket.herokuapp.com/root/awesome-for-beginners/archive/fc7d067.tar.gz

GH returns,
awesome-for-beginners-fc7d067cb13559f248bb362253ff2fa3b2617aba.tar.gz
Otherwise GitBucket returns,
awesome-for-beginners-fc7d067.tar.gz
2017-04-10 21:21:47 +09:00
aadrian
fce8cbdaef improve workding 2017-04-09 18:55:02 +02:00
Naoki Takezoe
f40bdb6494 Update issues guideline 2017-04-10 01:17:43 +09:00
Naoki Takezoe
74e940ea3c Update support policy 2017-04-10 01:13:28 +09:00
Naoki Takezoe
650aff5649 (refs #1383)Add demo site information 2017-04-10 01:08:58 +09:00
Naoki Takezoe
9793bfc074 Remove plugins outside the gitbucket organization 2017-04-10 00:54:43 +09:00
Naoki Takezoe
0cba10994b Merge pull request #1532 from aadrian/wording
Wording improvements.
2017-04-10 00:19:05 +09:00
adrian
9df2c221df improve wording.
(cherry picked from commit a184767d9c)
2017-04-09 15:19:29 +02:00
adrian
e3c6621398 improve wording.
(cherry picked from commit ce7d7340f7)
2017-04-09 15:18:54 +02:00
Naoki Takezoe
b736f904e9 (refs #1333)Bump to JGit 4.7.0 2017-04-09 21:43:58 +09:00
Naoki Takezoe
00093728ee Merge pull request #1530 from tkgdsg/modify_slash_escape_to_hyphen
archive naming rule modification to have comatibility with GitHub/GitHubEnerprise.
2017-04-09 20:11:26 +09:00
Yasuhiro Takagi
34c1fce8a2 To have comatibility with GitHub/GitHubEnerprise.
When downloding archive file,
GH/GHE use "-" as escape character for "/" in brach name.
2017-04-09 16:57:52 +09:00
kenji yoshida
6a520061cf s/2.11/2.12 2017-04-06 07:10:35 +09:00
Naoki Takezoe
c581d9e6b9 Remove unused import statement 2017-04-05 01:48:36 +09:00
Naoki Takezoe
956af54d4f Remove outdated TODO 2017-04-05 01:48:24 +09:00
Naoki Takezoe
d5a0ade5d9 Merge remote-tracking branch 'origin/master' 2017-04-05 01:47:07 +09:00
Naoki Takezoe
68c2f832ac (refs #1523)Eliminate CR and LF in public key 2017-04-05 01:46:57 +09:00
Naoki Takezoe
614bba4a0f Merge pull request #1520 from xuwei-k/refactoring
some refactoring
2017-04-02 16:36:07 +09:00
Naoki Takezoe
fde075f067 Merge pull request #1521 from xuwei-k/test-MySQL-version
update MySQL v5_7_latest in test
2017-04-02 16:35:15 +09:00
Naoki Takezoe
ea6bc9ccf0 Merge pull request #1522 from xuwei-k/Class-newInstance-deprecated
s/newInstance()/getDeclaredConstructor().newInstance()
2017-04-02 16:35:04 +09:00
Naoki Takezoe
b99c1c3f6e Merge pull request #1517 from kounoike/pr-fix-1516
Fix #1516 send noimage.png if user is removed.
2017-04-02 16:34:42 +09:00
xuwei-k
1e715d8b06 s/newInstance()/getDeclaredConstructor().newInstance()
java.lang.Class#newInstance deprecated since Java 9

http://download.java.net/java/jdk9/docs/api/java/lang/Class.html#newInstance--
2017-04-02 16:07:23 +09:00
xuwei-k
2a3e4af082 update MySQL v5_7_latest in test
v5_7_10 does not work Sierra

https://github.com/wix/wix-embedded-mysql/blob/wix-embedded-mysql-2.1.4/src/main/java/com/wix/mysql/distribution/Version.java#L86
2017-04-02 15:58:43 +09:00
xuwei-k
968c45c77d use "exists" instead of "find().isEmpty" 2017-04-02 15:49:10 +09:00
xuwei-k
ee8be37f55 use "contains" instead of "find().isEmpty" 2017-04-02 15:48:46 +09:00
xuwei-k
2d9727a695 use "contains" instead of "find().isDefined" 2017-04-02 15:43:24 +09:00
xuwei-k
0aa6e674e9 use exists instead of "filter().nonEmpty" 2017-04-02 15:42:22 +09:00
xuwei-k
445bf1cc34 remove return 2017-04-02 15:42:22 +09:00
xuwei-k
cfae6d76b2 use count instead of "filter().length" 2017-04-02 15:42:22 +09:00
KOUNOIKE Yuusuke
83a27809ef remove .get 2017-04-02 15:42:21 +09:00
xuwei-k
5eb41d5b25 use "Map#getOrElse" instead of "get().getOrElse" 2017-04-02 15:28:44 +09:00
xuwei-k
fe5961c59e use switch 2017-04-02 15:28:44 +09:00
xuwei-k
114c50a354 use "contains(key)" instead of "exists(_ == key)" 2017-04-02 15:28:44 +09:00
xuwei-k
56a39775e8 use "forall" instead of "filterNot().isEmpty" 2017-04-02 15:17:21 +09:00
KOUNOIKE Yuusuke
d0fa4da45a Remove message on sign-in dialog. and change message in System settings. 2017-04-02 15:11:38 +09:00
KOUNOIKE Yuusuke
dcd4c55fb9 Add caution message in System settings. 2017-04-01 21:42:30 +09:00
KOUNOIKE Yuusuke
a7920a7dd7 When using H2, caution message shows at sign-in. 2017-04-01 21:42:30 +09:00
KOUNOIKE Yuusuke
3bb32f11d7 Fix #1516 send noimage.png when user is not exist or removed. 2017-04-01 21:26:32 +09:00
34 changed files with 245 additions and 151 deletions

View File

@@ -1,6 +1,7 @@
# Guideline for Issues # Guideline for Issues
- At first, See [FAQ](https://github.com/gitbucket/gitbucket/wiki/FAQ) and check issues whether there is a same question or request in the past. - At first, see [Wiki](https://github.com/gitbucket/gitbucket/wiki) and check issues whether there is a same question or request in the past.
- 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.
- We can also support in Japaneses other than English at [gitter room for Japanese](https://gitter.im/gitbucket/gitbucket_ja). - We can also support in Japanese other than English at [gitter room for Japanese](https://gitter.im/gitbucket/gitbucket_ja).
- Write an issue in English. At least, write subject in English. - Write an issue in English. At least, write subject in English.
- 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.

View File

@@ -1,31 +1,35 @@
GitBucket [![Gitter chat](https://badges.gitter.im/gitbucket/gitbucket.png)](https://gitter.im/gitbucket/gitbucket) [![Build Status](https://travis-ci.org/gitbucket/gitbucket.svg?branch=master)](https://travis-ci.org/gitbucket/gitbucket) 0;95;0cGitBucket [![Gitter chat](https://badges.gitter.im/gitbucket/gitbucket.png)](https://gitter.im/gitbucket/gitbucket) [![Build Status](https://travis-ci.org/gitbucket/gitbucket.svg?branch=master)](https://travis-ci.org/gitbucket/gitbucket)
========= =========
GitBucket is a Git platform powered by Scala offering: GitBucket is a Git web platform powered by Scala offering:
- Easy installation - Easy installation
- Intuitive UI
- High extensibility by plugins - High extensibility by plugins
- API compatibility with GitHub - API compatibility with GitHub
You can try an [online demo](https://gitbucket.herokuapp.com/) *(ID: root / Pass: root)* of GitBucket, and also get the latest information at [GitBucket News](https://gitbucket.github.io/gitbucket-news/).
Features Features
-------- --------
The current version of GitBucket provides a basic features below: The current version of GitBucket provides many features such as:
- Public / Private Git repository (http and ssh access) - Public / Private Git repositories (with http/https and ssh access)
- GitLFS support - GitLFS support
- Repository viewer includes online file editor - Repository viewer including an online file editor
- Issues, Pull request and Wiki for repositories - Issues, Pull Requests and Wiki for repositories
- Activity timeline and email notification - Activity timeline and email notifications
- Account and group management with LDAP integration - Account and group management with LDAP integration
- 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/how_to_run.md).
Installation Installation
-------- --------
GitBucket requires **Java8**. You have to install it if it is not already installed. GitBucket requires **Java8**. You have to install it, if it is not already installed.
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 **root** / **root**. 2. Go to `http://[hostname]:8080/` and log in with ID: **root** / Pass: **root**.
You can specify following options: You can specify following options:
@@ -45,24 +49,30 @@ To upgrade GitBucket, replace `gitbucket.war` with the new version, after stoppi
Plugins Plugins
-------- --------
GitBucket has a plug-in system to allow extensions to GitBucket. We provide some official plug-ins: GitBucket has a plug-in system that allows extra functionality. Officially the following plug-ins are provided:
- [gitbucket-gist-plugin](https://github.com/gitbucket/gitbucket-gist-plugin) - [gitbucket-gist-plugin](https://github.com/gitbucket/gitbucket-gist-plugin)
- [gitbucket-emoji-plugin](https://github.com/gitbucket/gitbucket-emoji-plugin) - [gitbucket-emoji-plugin](https://github.com/gitbucket/gitbucket-emoji-plugin)
- [gitbucket-pages-plugin](https://github.com/gitbucket/gitbucket-pages-plugin)
You can find more plugins made by the community at [GitBucket community plugins](http://gitbucket-plugins.github.io/). You can find more plugins made by the community at [GitBucket community plugins](http://gitbucket-plugins.github.io/).
Support Support
-------- --------
- If you have any questions about GitBucket, send it to the [gitter room](https://gitter.im/gitbucket/gitbucket) before opening an issue. - If you have any questions about GitBucket, see [Wiki](https://github.com/gitbucket/gitbucket/wiki) and check issues whether there is a same question or request in the past.
- Make sure check whether there is the same question or request in the past. - If you can't find same question and report, send it to [gitter room](https://gitter.im/gitbucket/gitbucket) before raising an issue.
- When raise a new issue, write at least the subject in **English**. - We can also provide support in Japanese other than English at [gitter room for Japanese](https://gitter.im/gitbucket/gitbucket_ja).
- We can also provide support in Japanese at [gitter room for Japanese](https://gitter.im/gitbucket/gitbucket_ja). - Write an issue in English. At least, write subject in English.
- The first priority of GitBucket is easy installation and API compatibility with GitHub, so we might reject if your request is against it. - 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.
Release Notes Release Notes
------------- -------------
### 4.12 - 30 Apr 2017
- Gist plug-in provides JavaScript to embed snippet
- Dropdown menu filter in the branch comparing page
- Caution for the embedded H2 database
### 4.11 - 1 Apr 2017 ### 4.11 - 1 Apr 2017
- Deploy keys support - Deploy keys support
- Auto generate avatar images - Auto generate avatar images

View File

@@ -1,6 +1,6 @@
val Organization = "io.github.gitbucket" val Organization = "io.github.gitbucket"
val Name = "gitbucket" val Name = "gitbucket"
val GitBucketVersion = "4.11.0" val GitBucketVersion = "4.12.1"
val ScalatraVersion = "2.5.0" val ScalatraVersion = "2.5.0"
val JettyVersion = "9.3.9.v20160517" val JettyVersion = "9.3.9.v20160517"
@@ -10,7 +10,7 @@ sourcesInBase := false
organization := Organization organization := Organization
name := Name name := Name
version := GitBucketVersion version := GitBucketVersion
scalaVersion := "2.12.1" scalaVersion := "2.12.2"
// dependency settings // dependency settings
resolvers ++= Seq( resolvers ++= Seq(
@@ -21,8 +21,8 @@ resolvers ++= Seq(
"amateras-snapshot" at "http://amateras.sourceforge.jp/mvn-snapshot/" "amateras-snapshot" at "http://amateras.sourceforge.jp/mvn-snapshot/"
) )
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.eclipse.jgit" % "org.eclipse.jgit.http.server" % "4.6.1.201703071140-r", "org.eclipse.jgit" % "org.eclipse.jgit.http.server" % "4.7.0.201704051617-r",
"org.eclipse.jgit" % "org.eclipse.jgit.archive" % "4.6.1.201703071140-r", "org.eclipse.jgit" % "org.eclipse.jgit.archive" % "4.7.0.201704051617-r",
"org.scalatra" %% "scalatra" % ScalatraVersion, "org.scalatra" %% "scalatra" % ScalatraVersion,
"org.scalatra" %% "scalatra-json" % ScalatraVersion, "org.scalatra" %% "scalatra-json" % ScalatraVersion,
"org.json4s" %% "json4s-jackson" % "3.5.0", "org.json4s" %% "json4s-jackson" % "3.5.0",

View File

@@ -4,15 +4,15 @@ How to run from the source tree
Run for Development Run for Development
-------- --------
If you want to test GitBucket, input following command at the root directory of the source tree. If you want to test GitBucket, type the following command in the root directory of the source tree.
``` ```
$ sbt ~jetty:start $ sbt ~jetty:start
``` ```
Then access to `http://localhost:8080/` by your browser. The default administrator account is `root` and password is `root`. Then access `http://localhost:8080/` in your browser. The default administrator account is `root` and password is `root`.
Source code modification is detected and reloaded automatically. You can modify logging configuration by editing `src/main/resources/logback-dev.xml`. Source code modifications are detected and a reloaded happens automatically. You can modify the logging configuration by editing `src/main/resources/logback-dev.xml`.
Build war file Build war file
-------- --------
@@ -23,9 +23,9 @@ To build war file, run the following command:
$ sbt package $ sbt package
``` ```
`gitbucket_2.11-x.x.x.war` is generated into `target/scala-2.11`. `gitbucket_2.12-x.x.x.war` is generated into `target/scala-2.12`.
To build executable war file, run To build an executable war file, run
``` ```
$ sbt executable $ sbt executable
@@ -35,8 +35,8 @@ at the top of the source tree. It generates executable `gitbucket.war` into `tar
Run tests spec Run tests spec
--------- ---------
To run the full serie of tests, run the following command: To run the full series of tests, run the following command:
``` ```
sbt test $ sbt test
``` ```

View File

@@ -1,7 +1,7 @@
Notification Email Notification Email
======== ========
GitBucket sends email to target users by enabling the notification email by an administrator. GitBucket can send email notification to users if this feature is enabled by an administrator.
The timing of the notification are as follows: The timing of the notification are as follows:
@@ -20,4 +20,4 @@ Notified users are as follows:
* collaborators * collaborators
* participants * participants
However, the operation in person is excluded from the target. However, the person performing the operation is excluded from the notification.

View File

@@ -8,6 +8,8 @@ import java.security.ProtectionDomain;
public class JettyLauncher { public class JettyLauncher {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
System.setProperty("java.awt.headless", "true");
String host = null; String host = null;
int port = 8080; int port = 8080;
InetSocketAddress address = null; InetSocketAddress address = null;
@@ -19,19 +21,25 @@ public class JettyLauncher {
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) {
if(dim[0].equals("--host")) { switch (dim[0]) {
host = dim[1]; case "--host":
} else if(dim[0].equals("--port")) { host = dim[1];
port = Integer.parseInt(dim[1]); break;
} else if(dim[0].equals("--prefix")) { case "--port":
contextPath = dim[1]; port = Integer.parseInt(dim[1]);
if(!contextPath.startsWith("/")){ break;
contextPath = "/" + contextPath; case "--prefix":
} contextPath = dim[1];
} else if(dim[0].equals("--gitbucket.home")){ if (!contextPath.startsWith("/")) {
System.setProperty("gitbucket.home", dim[1]); contextPath = "/" + contextPath;
} else if(dim[0].equals("--temp_dir")){ }
tmpDirPath = dim[1]; break;
case "--gitbucket.home":
System.setProperty("gitbucket.home", dim[1]);
break;
case "--temp_dir":
tmpDirPath = dim[1];
break;
} }
} }
} }

View File

@@ -31,5 +31,7 @@ object GitBucketCoreModule extends Module("gitbucket-core",
new Version("4.10.0"), new Version("4.10.0"),
new Version("4.11.0", new Version("4.11.0",
new LiquibaseMigration("update/gitbucket-core_4.11.xml") new LiquibaseMigration("update/gitbucket-core_4.11.xml")
) ),
new Version("4.12.0"),
new Version("4.12.1")
) )

View File

@@ -19,8 +19,8 @@ case class CreateAStatus(
def isValid: Boolean = { def isValid: Boolean = {
CommitState.valueOf(state).isDefined && CommitState.valueOf(state).isDefined &&
// only http // only http
target_url.filterNot(f => "\\Ahttps?://".r.findPrefixOf(f).isDefined && f.length<255).isEmpty && target_url.forall(f => "\\Ahttps?://".r.findPrefixOf(f).isDefined && f.length < 255) &&
context.filterNot(f => f.length<255).isEmpty && context.forall(f => f.length < 255) &&
description.filterNot(f => f.length<1000).isEmpty description.forall(f => f.length < 1000)
} }
} }

View File

@@ -15,7 +15,6 @@ import io.github.gitbucket.scalatra.forms._
import org.apache.commons.io.FileUtils import org.apache.commons.io.FileUtils
import org.scalatra.i18n.Messages import org.scalatra.i18n.Messages
import org.scalatra.BadRequest import org.scalatra.BadRequest
import java.util.Date
class AccountController extends AccountControllerBase class AccountController extends AccountControllerBase
@@ -61,31 +60,31 @@ trait AccountControllerBase extends AccountManagementControllerBase {
val sshKeyForm = mapping( val sshKeyForm = mapping(
"title" -> trim(label("Title", text(required, maxlength(100)))), "title" -> trim(label("Title", text(required, maxlength(100)))),
"publicKey" -> trim(label("Key" , text(required, validPublicKey))) "publicKey" -> trim2(label("Key" , text(required, validPublicKey)))
)(SshKeyForm.apply) )(SshKeyForm.apply)
val personalTokenForm = mapping( val personalTokenForm = mapping(
"note" -> trim(label("Token", text(required, maxlength(100)))) "note" -> trim(label("Token", text(required, maxlength(100))))
)(PersonalTokenForm.apply) )(PersonalTokenForm.apply)
case class NewGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String) case class NewGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String)
case class EditGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String, clearImage: Boolean) case class EditGroupForm(groupName: String, description: Option[String], url: Option[String], fileId: Option[String], members: String, clearImage: Boolean)
val newGroupForm = mapping( val newGroupForm = mapping(
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier, uniqueUserName, reservedNames))), "groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier, uniqueUserName, reservedNames))),
"description" -> trim(label("Group description", optional(text()))), "description" -> trim(label("Group description", optional(text()))),
"url" -> trim(label("URL" ,optional(text(maxlength(200))))), "url" -> trim(label("URL" ,optional(text(maxlength(200))))),
"fileId" -> trim(label("File ID" ,optional(text()))), "fileId" -> trim(label("File ID" ,optional(text()))),
"members" -> trim(label("Members" ,text(required, members))) "members" -> trim(label("Members" ,text(required, members)))
)(NewGroupForm.apply) )(NewGroupForm.apply)
val editGroupForm = mapping( val editGroupForm = mapping(
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier))), "groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier))),
"description" -> trim(label("Group description", optional(text()))), "description" -> trim(label("Group description", optional(text()))),
"url" -> trim(label("URL" ,optional(text(maxlength(200))))), "url" -> trim(label("URL" ,optional(text(maxlength(200))))),
"fileId" -> trim(label("File ID" ,optional(text()))), "fileId" -> trim(label("File ID" ,optional(text()))),
"members" -> trim(label("Members" ,text(required, members))), "members" -> trim(label("Members" ,text(required, members))),
"clearImage" -> trim(label("Clear image" ,boolean())) "clearImage" -> trim(label("Clear image" ,boolean()))
)(EditGroupForm.apply) )(EditGroupForm.apply)
case class RepositoryCreationForm(owner: String, name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean) case class RepositoryCreationForm(owner: String, name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean)
@@ -150,20 +149,21 @@ trait AccountControllerBase extends AccountManagementControllerBase {
get("/:userName/_avatar"){ get("/:userName/_avatar"){
val userName = params("userName") val userName = params("userName")
getAccountByUserName(userName).map{ account => contentType = "image/png"
getAccountByUserName(userName).flatMap{ account =>
response.setDateHeader("Last-Modified", account.updatedDate.getTime) response.setDateHeader("Last-Modified", account.updatedDate.getTime)
account.image.map{ image => account.image.map{ image =>
RawData(FileUtil.getMimeType(image), new java.io.File(getUserUploadDir(userName), image)) Some(RawData(FileUtil.getMimeType(image), new java.io.File(getUserUploadDir(userName), image)))
}.getOrElse{ }.getOrElse{
contentType = "image/png" if (account.isGroupAccount) {
(if (account.isGroupAccount) {
TextAvatarUtil.textGroupAvatar(account.fullName) TextAvatarUtil.textGroupAvatar(account.fullName)
} else { } else {
TextAvatarUtil.textAvatar(account.fullName) TextAvatarUtil.textAvatar(account.fullName)
}).getOrElse(Thread.currentThread.getContextClassLoader.getResourceAsStream("noimage.png")) }
} }
}.getOrElse{ }.getOrElse{
NotFound() response.setHeader("Cache-Control", "max-age=3600")
Thread.currentThread.getContextClassLoader.getResourceAsStream("noimage.png")
} }
} }

View File

@@ -125,7 +125,7 @@ trait ApiControllerBase extends ControllerBase {
get ("/api/v3/repos/:owner/:repo/branches/:branch")(referrersOnly { repository => get ("/api/v3/repos/:owner/:repo/branches/:branch")(referrersOnly { repository =>
//import gitbucket.core.api._ //import gitbucket.core.api._
(for{ (for{
branch <- params.get("branch") if repository.branchList.find(_ == branch).isDefined branch <- params.get("branch") if repository.branchList.contains(branch)
br <- getBranches(repository.owner, repository.name, repository.repository.defaultBranch, repository.repository.originUserName.isEmpty).find(_.name == branch) br <- getBranches(repository.owner, repository.name, repository.repository.defaultBranch, repository.repository.originUserName.isEmpty).find(_.name == branch)
} yield { } yield {
val protection = getProtectedBranchInfo(repository.owner, repository.name, branch) val protection = getProtectedBranchInfo(repository.owner, repository.name, branch)
@@ -287,7 +287,7 @@ trait ApiControllerBase extends ControllerBase {
patch("/api/v3/repos/:owner/:repo/branches/:branch")(ownerOnly { repository => patch("/api/v3/repos/:owner/:repo/branches/:branch")(ownerOnly { repository =>
import gitbucket.core.api._ import gitbucket.core.api._
(for{ (for{
branch <- params.get("branch") if repository.branchList.find(_ == branch).isDefined branch <- params.get("branch") if repository.branchList.contains(branch)
protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection) protection <- extractFromJsonBody[ApiBranchProtection.EnablingAndDisabling].map(_.protection)
br <- getBranches(repository.owner, repository.name, repository.repository.defaultBranch, repository.repository.originUserName.isEmpty).find(_.name == branch) br <- getBranches(repository.owner, repository.name, repository.repository.defaultBranch, repository.repository.originUserName.isEmpty).find(_.name == branch)
} yield { } yield {

View File

@@ -40,10 +40,6 @@ abstract class ControllerBase extends ScalatraFilter
contentType = formats("json") contentType = formats("json")
} }
// TODO Scala 2.11
// // Don't set content type via Accept header.
// override def format(implicit request: HttpServletRequest) = ""
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = try { override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = try {
val httpRequest = request.asInstanceOf[HttpServletRequest] val httpRequest = request.asInstanceOf[HttpServletRequest]
val httpResponse = response.asInstanceOf[HttpServletResponse] val httpResponse = response.asInstanceOf[HttpServletResponse]
@@ -151,7 +147,6 @@ abstract class ControllerBase extends ScalatraFilter
} }
} }
// TODO Scala 2.11
override def url(path: String, params: Iterable[(String, Any)] = Iterable.empty, override def url(path: String, params: Iterable[(String, Any)] = Iterable.empty,
includeContextPath: Boolean = true, includeServletPath: Boolean = true, includeContextPath: Boolean = true, includeServletPath: Boolean = true,
absolutize: Boolean = true, withSessionId: Boolean = true) absolutize: Boolean = true, withSessionId: Boolean = true)
@@ -159,6 +154,18 @@ abstract class ControllerBase extends ScalatraFilter
if (path.startsWith("http")) path if (path.startsWith("http")) path
else baseUrl + super.url(path, params, false, false, false) else baseUrl + super.url(path, params, false, false, false)
/**
* Extends scalatra-form's trim rule to eliminate CR and LF.
*/
protected def trim2[T](valueType: SingleValueType[T]): SingleValueType[T] = new SingleValueType[T](){
def convert(value: String, messages: Messages): T = valueType.convert(trim(value), messages)
override def validate(name: String, value: String, params: Map[String, String], messages: Messages): Seq[(String, String)] =
valueType.validate(name, trim(value), params, messages)
private def trim(value: String): String = if(value == null) null else value.replaceAll("\r\n", "").trim
}
/** /**
* Use this method to response the raw data against XSS. * Use this method to response the raw data against XSS.
*/ */

View File

@@ -26,13 +26,13 @@ trait IndexControllerBase extends ControllerBase {
"password" -> trim(label("Password", text(required))) "password" -> trim(label("Password", text(required)))
)(SignInForm.apply) )(SignInForm.apply)
val searchForm = mapping( // val searchForm = mapping(
"query" -> trim(text(required)), // "query" -> trim(text(required)),
"owner" -> trim(text(required)), // "owner" -> trim(text(required)),
"repository" -> trim(text(required)) // "repository" -> trim(text(required))
)(SearchForm.apply) // )(SearchForm.apply)
//
case class SearchForm(query: String, owner: String, repository: String) // case class SearchForm(query: String, owner: String, repository: String)
get("/"){ get("/"){
@@ -163,7 +163,7 @@ trait IndexControllerBase extends ControllerBase {
get("/search"){ get("/search"){
val query = params.getOrElse("query", "").trim.toLowerCase val query = params.getOrElse("query", "").trim.toLowerCase
val visibleRepositories = getVisibleRepositories(context.loginAccount, None) val visibleRepositories = getVisibleRepositories(context.loginAccount, repositoryUserName = None, withoutPhysicalInfo = true)
val repositories = visibleRepositories.filter { repository => val repositories = 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
} }

View File

@@ -40,7 +40,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
) )
val optionsForm = mapping( val optionsForm = mapping(
"repositoryName" -> trim(label("Repository Name" , text(required, maxlength(100), identifier, renameRepositoryName))), "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))),
@@ -63,7 +63,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
val deployKeyForm = mapping( val deployKeyForm = mapping(
"title" -> trim(label("Title", text(required, maxlength(100)))), "title" -> trim(label("Title", text(required, maxlength(100)))),
"publicKey" -> trim(label("Key" , text(required))), // TODO duplication check in the repository? "publicKey" -> trim2(label("Key" , text(required))), // TODO duplication check in the repository?
"allowWrite" -> trim(label("Key" , boolean())) "allowWrite" -> trim(label("Key" , boolean()))
)(DeployKeyForm.apply) )(DeployKeyForm.apply)
@@ -139,6 +139,12 @@ trait RepositorySettingsControllerBase extends ControllerBase {
FileUtils.moveDirectory(dir, getLfsDir(repository.owner, form.repositoryName)) FileUtils.moveDirectory(dir, getLfsDir(repository.owner, form.repositoryName))
} }
} }
// Move attached directory
defining(getAttachedDir(repository.owner, repository.name)){ dir =>
if(dir.isDirectory) {
FileUtils.moveDirectory(dir, getAttachedDir(repository.owner, form.repositoryName))
}
}
// Delete parent directory // Delete parent directory
FileUtil.deleteDirectoryIfEmpty(getRepositoryFilesDir(repository.owner, repository.name)) FileUtil.deleteDirectoryIfEmpty(getRepositoryFilesDir(repository.owner, repository.name))
@@ -157,7 +163,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
/** Update default branch */ /** Update default branch */
post("/:owner/:repository/settings/update_default_branch", defaultBranchForm)(ownerOnly { (form, repository) => post("/:owner/:repository/settings/update_default_branch", defaultBranchForm)(ownerOnly { (form, repository) =>
if(repository.branchList.find(_ == form.defaultBranch).isEmpty){ if(!repository.branchList.contains(form.defaultBranch)){
redirect(s"/${repository.owner}/${repository.name}/settings/options") redirect(s"/${repository.owner}/${repository.name}/settings/options")
} else { } else {
saveRepositoryDefaultBranch(repository.owner, repository.name, form.defaultBranch) saveRepositoryDefaultBranch(repository.owner, repository.name, form.defaultBranch)
@@ -174,7 +180,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
get("/:owner/:repository/settings/branches/:branch")(ownerOnly { repository => get("/:owner/:repository/settings/branches/:branch")(ownerOnly { repository =>
import gitbucket.core.api._ import gitbucket.core.api._
val branch = params("branch") val branch = params("branch")
if(repository.branchList.find(_ == branch).isEmpty){ if(!repository.branchList.contains(branch)){
redirect(s"/${repository.owner}/${repository.name}/settings/branches") redirect(s"/${repository.owner}/${repository.name}/settings/branches")
} else { } else {
val protection = ApiBranchProtection(getProtectedBranchInfo(repository.owner, repository.name, branch)) val protection = ApiBranchProtection(getProtectedBranchInfo(repository.owner, repository.name, branch))
@@ -352,6 +358,12 @@ trait RepositorySettingsControllerBase extends ControllerBase {
FileUtils.moveDirectory(dir, getLfsDir(form.newOwner, repository.name)) FileUtils.moveDirectory(dir, getLfsDir(form.newOwner, repository.name))
} }
} }
// Move attached directory
defining(getAttachedDir(repository.owner, repository.name)){ dir =>
if(dir.isDirectory) {
FileUtils.moveDirectory(dir, getAttachedDir(form.newOwner, repository.name))
}
}
// Delere parent directory // Delere parent directory
FileUtil.deleteDirectoryIfEmpty(getRepositoryFilesDir(repository.owner, repository.name)) FileUtil.deleteDirectoryIfEmpty(getRepositoryFilesDir(repository.owner, repository.name))

View File

@@ -232,7 +232,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
oldFileName = form.oldFileName, oldFileName = form.oldFileName,
content = appendNewLine(convertLineSeparator(form.content, form.lineSeparator), form.lineSeparator), content = appendNewLine(convertLineSeparator(form.content, form.lineSeparator), form.lineSeparator),
charset = form.charset, charset = form.charset,
message = if(form.oldFileName.exists(_ == form.newFileName)){ message = if(form.oldFileName.contains(form.newFileName)){
form.message.getOrElse(s"Update ${form.newFileName}") form.message.getOrElse(s"Update ${form.newFileName}")
} else { } else {
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}") form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
@@ -614,7 +614,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
val permission = JGitUtil.processTree(git, headTip){ (path, tree) => val permission = JGitUtil.processTree(git, headTip){ (path, tree) =>
// Add all entries except the editing file // Add all entries except the editing file
if(!newPath.exists(_ == path) && !oldPath.exists(_ == path)){ if(!newPath.contains(path) && !oldPath.contains(path)){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId)) builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
} }
// Retrieve permission if file exists to keep it // Retrieve permission if file exists to keep it
@@ -670,12 +670,13 @@ trait RepositoryViewerControllerBase extends ControllerBase {
private def archiveRepository(name: String, suffix: String, repository: RepositoryService.RepositoryInfo): Unit = { private def archiveRepository(name: String, suffix: String, repository: RepositoryService.RepositoryInfo): Unit = {
val revision = name.stripSuffix(suffix) val revision = name.stripSuffix(suffix)
val filename = repository.name + "-" +
(if(revision.length == 40) revision.substring(0, 10) else revision).replace('/', '_') + suffix
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(revision)) val oid = git.getRepository.resolve(revision)
val revCommit = JGitUtil.getRevCommitFromId(git, oid)
val sha1 = oid.getName()
val repositorySuffix = (if(sha1.startsWith(revision)) sha1 else revision).replace('/','-')
val filename = repository.name + "-" + repositorySuffix + suffix
contentType = "application/octet-stream" contentType = "application/octet-stream"
response.setHeader("Content-Disposition", s"attachment; filename=${filename}") response.setHeader("Content-Disposition", s"attachment; filename=${filename}")
@@ -683,6 +684,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
git.archive git.archive
.setFormat(suffix.tail) .setFormat(suffix.tail)
.setPrefix(repository.name + "-" + repositorySuffix + "/")
.setTree(revCommit) .setTree(revCommit)
.setOutputStream(response.getOutputStream) .setOutputStream(response.getOutputStream)
.call() .call()

View File

@@ -9,10 +9,10 @@ trait MilestoneComponent extends TemplateComponent { self: Profile =>
class Milestones(tag: Tag) extends Table[Milestone](tag, "MILESTONE") with MilestoneTemplate { class Milestones(tag: Tag) extends Table[Milestone](tag, "MILESTONE") with MilestoneTemplate {
override val milestoneId = column[Int]("MILESTONE_ID", O AutoInc) override val milestoneId = column[Int]("MILESTONE_ID", O AutoInc)
val title = column[String]("TITLE") val title = column[String]("TITLE")
val description = column[String]("DESCRIPTION") val description = column[Option[String]]("DESCRIPTION")
val dueDate = column[java.util.Date]("DUE_DATE") val dueDate = column[Option[java.util.Date]]("DUE_DATE")
val closedDate = column[java.util.Date]("CLOSED_DATE") val closedDate = column[Option[java.util.Date]]("CLOSED_DATE")
def * = (userName, repositoryName, milestoneId, title, description.?, dueDate.?, closedDate.?) <> (Milestone.tupled, Milestone.unapply) def * = (userName, repositoryName, milestoneId, title, description, dueDate, closedDate) <> (Milestone.tupled, Milestone.unapply)
def byPrimaryKey(owner: String, repository: String, milestoneId: Int) = byMilestone(owner, repository, milestoneId) def byPrimaryKey(owner: String, repository: String, milestoneId: Int) = byMilestone(owner, repository, milestoneId)
def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], milestoneId: Rep[Int]) = byMilestone(userName, repositoryName, milestoneId) def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], milestoneId: Rep[Int]) = byMilestone(userName, repositoryName, milestoneId)

View File

@@ -83,7 +83,7 @@ class PluginRegistry {
def addRenderer(extension: String, renderer: Renderer): Unit = renderers += ((extension, renderer)) def addRenderer(extension: String, renderer: Renderer): Unit = renderers += ((extension, renderer))
def getRenderer(extension: String): Renderer = renderers.get(extension).getOrElse(DefaultRenderer) def getRenderer(extension: String): Renderer = renderers.getOrElse(extension, DefaultRenderer)
def renderableExtensions: Seq[String] = renderers.keys.toSeq def renderableExtensions: Seq[String] = renderers.keys.toSeq
@@ -175,7 +175,7 @@ object PluginRegistry {
}).foreach { pluginJar => }).foreach { pluginJar =>
val classLoader = new URLClassLoader(Array(pluginJar.toURI.toURL), Thread.currentThread.getContextClassLoader) val classLoader = new URLClassLoader(Array(pluginJar.toURI.toURL), Thread.currentThread.getContextClassLoader)
try { try {
val plugin = classLoader.loadClass("Plugin").newInstance().asInstanceOf[Plugin] val plugin = classLoader.loadClass("Plugin").getDeclaredConstructor().newInstance().asInstanceOf[Plugin]
// Migration // Migration
val solidbase = new Solidbase() val solidbase = new Solidbase()

View File

@@ -111,7 +111,7 @@ trait IssuesService {
val (_, cs) = status.head val (_, cs) = status.head
Some(CommitStatusInfo( Some(CommitStatusInfo(
count = status.length, count = status.length,
successCount = status.filter(_._2.state == CommitState.SUCCESS).length, successCount = status.count(_._2.state == CommitState.SUCCESS),
context = (if(status.length == 1) Some(cs.context) else None), context = (if(status.length == 1) Some(cs.context) else None),
state = (if(status.length == 1) Some(cs.state) else None), state = (if(status.length == 1) Some(cs.state) else None),
targetUrl = (if(status.length == 1) cs.targetUrl else None), targetUrl = (if(status.length == 1) cs.targetUrl else None),

View File

@@ -21,7 +21,7 @@ trait MilestonesService {
def updateMilestone(milestone: Milestone)(implicit s: Session): Unit = def updateMilestone(milestone: Milestone)(implicit s: Session): Unit =
Milestones Milestones
.filter (t => t.byPrimaryKey(milestone.userName, milestone.repositoryName, milestone.milestoneId)) .filter (t => t.byPrimaryKey(milestone.userName, milestone.repositoryName, milestone.milestoneId))
.map (t => (t.title, t.description.?, t.dueDate.?, t.closedDate.?)) .map (t => (t.title, t.description, t.dueDate, t.closedDate))
.update (milestone.title, milestone.description, milestone.dueDate, milestone.closedDate) .update (milestone.title, milestone.description, milestone.dueDate, milestone.closedDate)
def openMilestone(milestone: Milestone)(implicit s: Session): Unit = def openMilestone(milestone: Milestone)(implicit s: Session): Unit =

View File

@@ -76,7 +76,7 @@ object ProtectedBranchService {
includeAdministrators: Boolean) extends AccountService with CommitStatusService { includeAdministrators: Boolean) extends AccountService with CommitStatusService {
def isAdministrator(pusher: String)(implicit session: Session): Boolean = def isAdministrator(pusher: String)(implicit session: Session): Boolean =
pusher == owner || getGroupMembers(owner).filter(gm => gm.userName == pusher && gm.isManager).nonEmpty pusher == owner || getGroupMembers(owner).exists(gm => gm.userName == pusher && gm.isManager)
/** /**
* Can't be force pushed * Can't be force pushed

View File

@@ -265,7 +265,7 @@ object PullRequestService {
val summary = stateMap.map{ case (keyState, states) => states.size+" "+keyState.name }.mkString(", ") val summary = stateMap.map{ case (keyState, states) => states.size+" "+keyState.name }.mkString(", ")
state -> summary state -> summary
} }
lazy val statusesAndRequired:List[(CommitStatus, Boolean)] = statuses.map{ s => s -> branchProtection.contexts.exists(_==s.context) } lazy val statusesAndRequired:List[(CommitStatus, Boolean)] = statuses.map{ s => s -> branchProtection.contexts.contains(s.context) }
lazy val isAllSuccess = commitStateSummary._1==CommitState.SUCCESS lazy val isAllSuccess = commitStateSummary._1==CommitState.SUCCESS
} }
} }

View File

@@ -262,11 +262,19 @@ trait RepositoryService { self: AccountService =>
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName) JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName)
}, },
repository, repository,
getForkedCount( if(withoutPhysicalInfo){
repository.originUserName.getOrElse(repository.userName), -1
repository.originRepositoryName.getOrElse(repository.repositoryName) } else {
), getForkedCount(
getRepositoryManagers(repository.userName)) repository.originUserName.getOrElse(repository.userName),
repository.originRepositoryName.getOrElse(repository.repositoryName)
)
},
if(withoutPhysicalInfo){
Nil
} else {
getRepositoryManagers(repository.userName)
})
} }
} }
@@ -300,7 +308,7 @@ trait RepositoryService { self: AccountService =>
case None => Repositories filter(_.isPrivate === false.bind) case None => Repositories filter(_.isPrivate === false.bind)
}).filter { t => }).filter { t =>
repositoryUserName.map { userName => t.userName === userName.bind } getOrElse LiteralColumn(true) repositoryUserName.map { userName => t.userName === userName.bind } getOrElse LiteralColumn(true)
}.sortBy(_.lastActivityDate desc).list.map{ repository => }.sortBy(_.lastActivityDate desc).list.map { repository =>
new RepositoryInfo( new RepositoryInfo(
if(withoutPhysicalInfo){ if(withoutPhysicalInfo){
new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName) new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName)
@@ -308,11 +316,19 @@ trait RepositoryService { self: AccountService =>
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName) JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName)
}, },
repository, repository,
getForkedCount( if(withoutPhysicalInfo){
repository.originUserName.getOrElse(repository.userName), -1
repository.originRepositoryName.getOrElse(repository.repositoryName) } else {
), getForkedCount(
getRepositoryManagers(repository.userName)) repository.originUserName.getOrElse(repository.userName),
repository.originRepositoryName.getOrElse(repository.repositoryName)
)
},
if(withoutPhysicalInfo) {
Nil
} else {
getRepositoryManagers(repository.userName)
})
} }
} }

View File

@@ -177,7 +177,7 @@ trait WikiService {
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}") val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
JGitUtil.processTree(git, headId){ (path, tree) => JGitUtil.processTree(git, headId){ (path, tree) =>
if(revertInfo.find(x => x.filePath == path).isEmpty){ if(!revertInfo.exists(x => x.filePath == path)){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId)) builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
} }
} }

View File

@@ -51,21 +51,21 @@ class GitAuthenticationFilter extends Filter with RepositoryService with Account
private def pluginRepository(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain, private def pluginRepository(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain,
settings: SystemSettings, isUpdating: Boolean, filter: GitRepositoryFilter): Unit = { settings: SystemSettings, isUpdating: Boolean, filter: GitRepositoryFilter): Unit = {
implicit val r = request Database() withSession { implicit session =>
val account = for {
auth <- Option(request.getHeader("Authorization"))
Array(username, password) = AuthUtil.decodeAuthHeader(auth).split(":", 2)
account <- authenticate(settings, username, password)
} yield {
request.setAttribute(Keys.Request.UserName, account.userName)
account
}
val account = for { if (filter.filter(request.gitRepositoryPath, account.map(_.userName), settings, isUpdating)) {
auth <- Option(request.getHeader("Authorization")) chain.doFilter(request, response)
Array(username, password) = AuthUtil.decodeAuthHeader(auth).split(":", 2) } else {
account <- authenticate(settings, username, password) AuthUtil.requireAuth(response)
} yield { }
request.setAttribute(Keys.Request.UserName, account.userName)
account
}
if(filter.filter(request.gitRepositoryPath, account.map(_.userName), settings, isUpdating)){
chain.doFilter(request, response)
} else {
AuthUtil.requireAuth(response)
} }
} }

View File

@@ -105,7 +105,7 @@ abstract class DefaultGitCommand(val owner: String, val repoName: String) extend
} }
} }
case AuthType.DeployKeyType(key) => { case AuthType.DeployKeyType(key) => {
getDeployKeys(owner, repoName).filter(sshKey => SshUtil.str2PublicKey(sshKey.publicKey).exists(_ == key)) match { getDeployKeys(owner, repoName).filter(sshKey => SshUtil.str2PublicKey(sshKey.publicKey).contains(key)) match {
case List(_) => true case List(_) => true
case _ => false case _ => false
} }
@@ -123,7 +123,7 @@ abstract class DefaultGitCommand(val owner: String, val repoName: String) extend
} }
} }
case AuthType.DeployKeyType(key) => { case AuthType.DeployKeyType(key) => {
getDeployKeys(owner, repoName).filter(sshKey => SshUtil.str2PublicKey(sshKey.publicKey).exists(_ == key)) match { getDeployKeys(owner, repoName).filter(sshKey => SshUtil.str2PublicKey(sshKey.publicKey).contains(key)) match {
case List(x) if x.allowWrite => true case List(x) if x.allowWrite => true
case _ => false case _ => false
} }

View File

@@ -68,7 +68,7 @@ class PublicKeyAuthenticator(genericUser: String) extends PublickeyAuthenticator
private def authenticateGenericUser(userName: String, key: PublicKey, session: ServerSession, genericUser: String)(implicit s: Session): Boolean = { private def authenticateGenericUser(userName: String, key: PublicKey, session: ServerSession, genericUser: String)(implicit s: Session): Boolean = {
// find all users having the key we got from ssh // find all users having the key we got from ssh
val possibleUserNames = getAllKeys().filter { sshKey => val possibleUserNames = getAllKeys().filter { sshKey =>
SshUtil.str2PublicKey(sshKey.publicKey).exists(_ == key) SshUtil.str2PublicKey(sshKey.publicKey).contains(key)
}.map(_.userName).distinct }.map(_.userName).distinct
// determine the user - if different accounts share the same key, tough luck // determine the user - if different accounts share the same key, tough luck
@@ -85,7 +85,7 @@ class PublicKeyAuthenticator(genericUser: String) extends PublickeyAuthenticator
}.getOrElse { }.getOrElse {
// search deploy keys // search deploy keys
val existsDeployKey = getAllDeployKeys().exists { sshKey => val existsDeployKey = getAllDeployKeys().exists { sshKey =>
SshUtil.str2PublicKey(sshKey.publicKey).exists(_ == key) SshUtil.str2PublicKey(sshKey.publicKey).contains(key)
} }
if(existsDeployKey){ if(existsDeployKey){
// found deploy key for repository // found deploy key for repository

View File

@@ -18,16 +18,17 @@ object SshUtil {
val parts = key.split(" ") val parts = key.split(" ")
if (parts.size < 2) { if (parts.size < 2) {
logger.debug(s"Invalid PublicKey Format: ${key}") logger.debug(s"Invalid PublicKey Format: ${key}")
return None None
} } else {
try { try {
val encodedKey = parts(1) val encodedKey = parts(1)
val decode = Base64.getDecoder.decode(Constants.encodeASCII(encodedKey)) val decode = Base64.getDecoder.decode(Constants.encodeASCII(encodedKey))
Some(new ByteArrayBuffer(decode).getRawPublicKey) Some(new ByteArrayBuffer(decode).getRawPublicKey)
} catch { } catch {
case e: Throwable => case e: Throwable =>
logger.debug(e.getMessage, e) logger.debug(e.getMessage, e)
None None
}
} }
} }

View File

@@ -952,7 +952,7 @@ object JGitUtil {
* @return the last modified commit of specified path * @return the last modified commit of specified path
*/ */
def getLastModifiedCommit(git: Git, startCommit: RevCommit, path: String): RevCommit = { def getLastModifiedCommit(git: Git, startCommit: RevCommit, path: String): RevCommit = {
return git.log.add(startCommit).addPath(path).setMaxCount(1).call.iterator.next git.log.add(startCommit).addPath(path).setMaxCount(1).call.iterator.next
} }
def getBranches(owner: String, name: String, defaultBranch: String, origin: Boolean): Seq[BranchInfo] = { def getBranches(owner: String, name: String, defaultBranch: String, origin: Boolean): Seq[BranchInfo] = {

View File

@@ -1,4 +1,5 @@
@(info: Option[Any])(implicit context: gitbucket.core.controller.Context) @(info: Option[Any])(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.util.DatabaseConfig
@gitbucket.core.html.main("System settings"){ @gitbucket.core.html.main("System settings"){
@gitbucket.core.admin.html.menu("system"){ @gitbucket.core.admin.html.menu("system"){
@gitbucket.core.helper.html.information(info) @gitbucket.core.helper.html.information(info)
@@ -20,7 +21,17 @@
</tr> </tr>
<tr> <tr>
<td>DATABASE_URL</td> <td>DATABASE_URL</td>
<td>@gitbucket.core.util.DatabaseConfig.url</td> @if(DatabaseConfig.url.startsWith("jdbc:h2:")) {
<td class="danger">
<p>@gitbucket.core.util.DatabaseConfig.url</p>
<p>
Your GitBucket is running on embedded H2 database.
Recommend to <a href="https://github.com/gitbucket/gitbucket/wiki/External-database-configuration">configure to use external database</a> if you would like to use GitBucket for important purpose.
</p>
</td>
}else{
<td>@gitbucket.core.util.DatabaseConfig.url</td>
}
</tr> </tr>
</table> </table>
<!--====================================================================--> <!--====================================================================-->

View File

@@ -49,10 +49,7 @@ $(function(){
$('#filter-box').keyup(function(){ $('#filter-box').keyup(function(){
var inputVal = $('#filter-box').val(); var inputVal = $('#filter-box').val();
$.each($('li.repo-link a'), function(index, elem) { $.each($('li.repo-link a'), function(index, elem) {
console.log(inputVal); if ( !inputVal || !elem.text.trim() || elem.text.trim().toLowerCase().indexOf(inputVal.toLowerCase()) >= 0 ) {
console.log(elem.text.trim());
console.log(elem.text.trim().lastIndexOf(inputVal, 0));
if (!inputVal || !elem.text.trim() || elem.text.trim().indexOf(inputVal) >= 0) {
$(elem).parent().show(); $(elem).parent().show();
} else { } else {
$(elem).parent().hide(); $(elem).parent().hide();

View File

@@ -31,7 +31,7 @@
$('#branch-control-input').keyup(function() { $('#branch-control-input').keyup(function() {
var inputVal = $('#branch-control-input').val(); var inputVal = $('#branch-control-input').val();
$.each($('#branch-control-input').parent().parent().find('a'), function(index, elem) { $.each($('#branch-control-input').parent().parent().find('a'), function(index, elem) {
if (!inputVal || !elem.text.trim() || elem.text.trim().lastIndexOf(inputVal, 0) >= 0) { if (!inputVal || !elem.text.trim() || elem.text.trim().toLowerCase().indexOf(inputVal.toLowerCase()) >= 0) {
$(elem).parent().show(); $(elem).parent().show();
} else { } else {
$(elem).parent().hide(); $(elem).parent().hide();

View File

@@ -32,7 +32,7 @@ $(function(){
$('#@{filter}-input').keyup(function() { $('#@{filter}-input').keyup(function() {
var inputVal = $('#@{filter}-input').val(); var inputVal = $('#@{filter}-input').val();
$.each($('#@{filter}-input').parent().parent().find('a'), function(index, elem) { $.each($('#@{filter}-input').parent().parent().find('a'), function(index, elem) {
if (!inputVal || !elem.text.trim() || elem.text.trim().lastIndexOf(inputVal, 0) >= 0) { if ( !inputVal || !elem.text.trim() || elem.text.trim().toLowerCase().indexOf(inputVal.toLowerCase()) >=0 ) {
$(elem).parent().show(); $(elem).parent().show();
} else { } else {
$(elem).parent().hide(); $(elem).parent().hide();

View File

@@ -221,3 +221,27 @@ $(function(){
} }
}); });
</script> </script>
<script>
function dropdownFilter(dropdownItem,placeHolder,id) {
$('<li><input id="' + id + '" type="text" class="form-control input-sm dropdown-filter-input" placeholder="' + placeHolder + '"/></li>')
.insertBefore($("li:has(a" + dropdownItem + "):first"));
$('#'+id).keyup(function() {
var inputVal = $('#'+id).val();
$.each($('#'+id).parent().parent().find('a'), function(index, elem) {
if (!inputVal || !elem.text.trim() || elem.text.trim().toLowerCase().indexOf(inputVal.toLowerCase()) >= 0) {
$(elem).parent().show();
} else {
$(elem).parent().hide();
}
});
});
}
$(function(){
dropdownFilter('.origin-owner', 'Find Repository...', 'origin-owner-control-input');
dropdownFilter('.origin-branch','Find Branch...', 'origin-branch-control-input');
dropdownFilter('.forked-owner', 'Find Repository...', 'forked-owner-control-input');
dropdownFilter('.forked-branch','Find Branch...', 'forked-branch-control-input');
});
</script>

View File

@@ -342,6 +342,8 @@ div.account-image {
ul.dropdown-menu { ul.dropdown-menu {
padding: 2px 0; padding: 2px 0;
overflow: auto;
max-height: 100vh;
} }
ul.dropdown-menu li { ul.dropdown-menu li {
@@ -556,6 +558,7 @@ pre.commit-description {
background-color: transparent; background-color: transparent;
padding: 2px; padding: 2px;
margin: 0px; margin: 0px;
white-space: pre-wrap;
} }
#repository-url { #repository-url {

View File

@@ -29,7 +29,7 @@ class GitBucketCoreModuleSpec extends FunSuite {
} }
test("Migration MySQL", ExternalDBTest){ test("Migration MySQL", ExternalDBTest){
val config = aMysqldConfig(v5_7_10) val config = aMysqldConfig(v5_7_latest)
.withPort(3306) .withPort(3306)
.withUser("sa", "sa") .withUser("sa", "sa")
.withCharset(Charset.UTF8) .withCharset(Charset.UTF8)