Compare commits

...

86 Commits
3.11 ... 3.12

Author SHA1 Message Date
Naoki Takezoe
977f856854 Update README.md 2016-02-27 04:52:36 +09:00
Naoki Takezoe
da2a7bf77d Fix comment editing in pull request diff view 2016-02-27 04:48:46 +09:00
Naoki Takezoe
3da3a048f0 Fix width of previewable editing forms 2016-02-27 02:56:22 +09:00
Naoki Takezoe
7b5b453e56 (refs #1123)Fix page list style 2016-02-26 21:48:49 +09:00
Naoki Takezoe
c18f95edf8 Merge pull request #1120 from lidice/adjust-image-width
Adjust img width in content box to max-width:100%
2016-02-25 13:23:24 +09:00
Naoki Takezoe
71cf043f56 Merge pull request #1119 from lidice/fix-unmatched-tag
(fixes #1118)Remove duplicated <script>
2016-02-25 13:22:17 +09:00
lidice
a31e4b5897 Adjust img width in content box to max-width:100% 2016-02-23 19:52:44 +09:00
lidice
1679da4abe (fixes #1118)Remove duplicated <script> 2016-02-22 11:55:31 +09:00
Naoki Takezoe
505bc71f9a Fix width of wiki page editing form 2016-02-22 08:57:23 +09:00
Naoki Takezoe
4bc057c653 Fix broken presentation 2016-02-22 02:17:41 +09:00
Naoki Takezoe
8eee13d7aa Merge some controllers because a large amount mapping causes performance issue 2016-02-22 01:33:38 +09:00
Naoki Takezoe
8981e339b4 Disable new pull request button if user does not signed-in 2016-02-21 16:00:14 +09:00
Naoki Takezoe
e1dd5dd057 Merged branch master into master 2016-02-21 15:58:46 +09:00
Naoki Takezoe
cb64f8eab8 Replace new issue button with new pull request button 2016-02-21 15:56:12 +09:00
Naoki Takezoe
c47d50d0df Fix pull request guide 2016-02-21 15:36:41 +09:00
Naoki Takezoe
1f46da2273 Merge pull request #1116 from McFoggy/templates
addition of issues & PR templates
2016-02-21 12:28:40 +09:00
Matthieu Brouillard
06fc26cd06 addition of issues & PR templates 2016-02-20 21:45:18 +01:00
Naoki Takezoe
3a4f9b9027 Merge pull request #1112 from oohira/fix/margin-after-octicon
Add margin after octicon
2016-02-21 03:51:11 +09:00
Naoki Takezoe
f98c849c7c Merge pull request #1115 from lidice/fix-label-color-format
(fixes #1114)Add Colorpicker options that to force hex format
2016-02-21 03:49:42 +09:00
Naoki Takezoe
aa0bd5b34a Update version to 3.12 2016-02-20 23:02:00 +09:00
Naoki Takezoe
b52e904ed1 Update CONTRIBUTING.md 2016-02-20 20:52:56 +09:00
lidice
70e0dcf99d (fixes #1114)Add Colorpicker options that to force hex format 2016-02-19 20:20:26 +09:00
Naoki Takezoe
56bb20dfd2 (refs #1113)Improve printing styles 2016-02-19 15:48:14 +09:00
oohira
7d7d2f488d Add margin after octicon 2016-02-17 23:42:51 +09:00
Naoki Takezoe
72affd67b9 Merge pull request #1108 from gitbucket/new-ui
New GitHub UI and Mobile support
2016-02-17 03:04:55 +09:00
Naoki Takezoe
0cf1f43deb Adjust issue / comment form 2016-02-16 17:34:21 +09:00
Naoki Takezoe
8494c682a7 Fix search box style for mobile 2016-02-16 15:02:34 +09:00
Naoki Takezoe
1af5611159 Mobile view improvement 2016-02-16 02:51:09 +09:00
Naoki Takezoe
4d39f63ef7 Tweak header buttons 2016-02-16 02:36:45 +09:00
Naoki Takezoe
120d1c2fff Implement repository url selector 2016-02-16 01:41:48 +09:00
Naoki Takezoe
62e9c0358a Adjust new file, new pull request button and others 2016-02-15 23:26:56 +09:00
Naoki Takezoe
5a90848c75 Remove unused code 2016-02-15 09:20:36 +09:00
Naoki Takezoe
760d443f74 Tweak top margin of contents 2016-02-15 09:11:57 +09:00
Naoki Takezoe
5ee0e75dfe Implementing new header parts 2016-02-15 02:21:00 +09:00
Naoki Takezoe
3b4d2d6f91 Fix header style 2016-02-15 01:02:35 +09:00
Naoki Takezoe
dfaabeb41d Move sidemenu to header 2016-02-14 23:41:07 +09:00
Naoki Takezoe
0fae2dac35 Merge pull request #1097 from ritschwumm/patch-3
Java 8 is a new requirement
2016-02-13 10:23:47 +09:00
Naoki Takezoe
4db4fe28b4 Merge pull request #1102 from ritschwumm/wip/name
rename file to the name of the type within
2016-02-13 10:19:08 +09:00
Naoki Takezoe
5b87efa032 (refs #1084)Remove RepositoryUrls 2016-02-13 10:16:11 +09:00
Herr Ritschwumm
3ad609bad7 rename file to the name of the type within 2016-02-13 01:24:04 +01:00
Naoki Takezoe
8145cba111 Merge branch 'wip/baseurl' of https://github.com/ritschwumm/gitbucket into ritschwumm-wip/baseurl 2016-02-12 23:15:59 +09:00
Herr Ritschwumm
24b9a9a12c remove RepoBase by moving RepositoryUrls construction into Context 2016-02-11 23:24:09 +01:00
Herr Ritschwumm
ee7220ebd2 move SshAddress into the SystemSettingsService object 2016-02-11 22:55:22 +01:00
Naoki Takezoe
8fb72fd55e (refs #982)Provide fixed url for pull request tabs 2016-02-09 18:14:03 +09:00
Naoki Takezoe
a1eded2d9a Merge pull request #1091 from oohira/fix/review-comment-box-border
Fix bug that border of review comment box is not shown
2016-02-09 13:58:18 +09:00
Naoki Takezoe
7f184e1126 Merge pull request #1100 from oohira/fix/label-duplicate-error
Fix bug that label duplicate check is wrong
2016-02-09 13:43:47 +09:00
oohira
09aafbcce1 Fix wrong query to find the specified label 2016-02-08 23:40:17 +09:00
Naoki Takezoe
7f5024a746 Change javac option to require Java8 2016-02-08 01:11:54 +09:00
Naoki Takezoe
8fec0870a8 Merge pull request #1098 from nus/fix-hidden-pull-requests
Fix some hidden pull requests
2016-02-08 01:03:41 +09:00
Naoki Takezoe
a8d2afaff7 Merge pull request #1099 from gitbucket/scalatest
Move to ScalaTest from Specs2
2016-02-07 00:02:46 +09:00
Naoki Takezoe
8fd92f1c2f Fix compilation error in testcase 2016-02-06 23:39:57 +09:00
Naoki Takezoe
419ea16ead Remove Specs2 dependency 2016-02-06 22:27:23 +09:00
Naoki Takezoe
e72d808a3c Migrating testcase to ScalaTest 2016-02-06 22:10:20 +09:00
Naoki Takezoe
44e8c0a9be Migrating testcase to ScalaTest 2016-02-05 23:00:35 +09:00
Naoki Takezoe
e2c39d7815 Merged branch master into scalatest 2016-02-04 01:51:29 +09:00
Yota Ichino
687cd54f9a Fix some hidden pull requests
IssuesService.IssueLimit value is equalized with
PullRequestService.PullRequestLimit value by this commit.

If without this, some pull requests are hidden.
For example, inspite of 30 pull requests are exists,
pull request page shows 25.
2016-02-03 16:43:49 +00:00
Naoki Takezoe
911754e1dc Migrating testcase to ScalaTest 2016-02-04 00:40:58 +09:00
Naoki Takezoe
0067cbce6f Fix failed test 2016-02-04 00:37:50 +09:00
Naoki Takezoe
f40f8427aa Replace === with == 2016-02-04 00:27:00 +09:00
oohira
98ceff2391 Fix bug that border of review comment box is not shown 2016-02-04 00:04:07 +09:00
Naoki Takezoe
642a51a208 Merge pull request #1096 from nus/fix-invisible-closed-label
Fix invisible closed label
2016-02-03 23:41:37 +09:00
Naoki Takezoe
9ec7c321d8 Merge pull request #1095 from oohira/link-avatar-image-to-profile-page
Link avatar image to the user's profile page
2016-02-03 23:33:57 +09:00
Naoki Takezoe
a3c419b6f5 Merge pull request #1086 from oohira/fix/invalid-repos-owner-url
Fix repository owner link URL
2016-02-03 23:08:55 +09:00
Naoki Takezoe
15c28cffa4 Merge branch 'ritschwumm-patch-1' 2016-02-03 22:37:40 +09:00
Naoki Takezoe
f4d0f16481 (refs #1083)Bump sbt launcher 2016-02-03 22:37:09 +09:00
Naoki Takezoe
45535e4fdf Merge branch 'patch-1' of https://github.com/ritschwumm/gitbucket into ritschwumm-patch-1 2016-02-03 22:34:30 +09:00
ritschwumm
64635c5dc6 Java 8 is a new requirement 2016-02-03 01:19:29 +01:00
Yota Ichino
2fd95c7f1a Fix invisible closed label
Closed label has label-important attribute which
was not defined in gitbucket.css, so that define
the attribute.
2016-02-02 15:35:45 +00:00
oohira
eb6da85183 Link avatar image to the user's profile page 2016-02-02 23:24:05 +09:00
Naoki Takezoe
bcc05f021c Migrating testcases to ScalaTest from Specs2 2016-02-02 00:36:08 +09:00
Naoki Takezoe
d58ed55c3a Merged branch ritschwumm-wip/escape into master 2016-01-31 12:26:37 +09:00
Naoki Takezoe
057f029c80 (refs #1085)Remove var by replacing for expression with foldLeft 2016-01-31 12:26:09 +09:00
Naoki Takezoe
c9a12ff913 Fix version extraction 2016-01-31 10:33:07 +09:00
oohira
66bf00b5d3 Fix repository owner link URL 2016-01-31 08:32:59 +09:00
Herr Ritschwumm
7ba3ca6f15 properly escape html characters in a description 2016-01-30 16:25:50 +01:00
Herr Ritschwumm
a1bacccc09 add tests, failing right now 2016-01-30 16:25:44 +01:00
ritschwumm
333eeb4bad update sbt to newest version 2016-01-30 11:24:23 +01:00
Herr Ritschwumm
3f2935612d feature: add settings for the ssh host 2016-01-30 11:15:07 +01:00
Herr Ritschwumm
4c87bdd959 refactor: make the settings alone responsible for ssh server location 2016-01-30 11:14:13 +01:00
Herr Ritschwumm
3543073150 cleanup: wiki urls could have been simpler 2016-01-30 11:12:11 +01:00
Herr Ritschwumm
e50fe604c2 cleanup: ... which in turns lets us avoid passing around the base url where we shouldn't need to know about it at all 2016-01-30 11:12:11 +01:00
Herr Ritschwumm
63369258bd preparation: move url generation from RepositoryInfo towards the gui 2016-01-30 11:12:07 +01:00
Herr Ritschwumm
e7c3376303 cleanup: baseUrl is not used here at all 2016-01-30 07:31:40 +01:00
Herr Ritschwumm
86163f66ce cleanup: jgit repo info does not have to know about urls in the gui 2016-01-30 07:31:26 +01:00
Herr Ritschwumm
518f0bfc28 cleanup: derive baseUrl from http request outside the SettingsService 2016-01-30 07:29:57 +01:00
Herr Ritschwumm
0a759f6127 cleanup: don't repeat yourself, calculate effective ssh port in one place only 2016-01-30 07:29:21 +01:00
100 changed files with 1503 additions and 1699 deletions

7
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,7 @@
# 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.
- 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).
- Write an issue in English. At least, write subject in English.
- First priority of GitBucket is easy installation and reproduce GitHub behavior, so we might reject if your request is against it.

19
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,19 @@
### Before submitting an issue to Gitbucket I have first:
- [] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/CONTRIBUTING.md)
- [] searched for similar already existing issue
- [] 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)*
## Issue
**Impacted version**: xxxx
**Deployment mode**: *explain here how you use gitbucket : standalone app, under webcontainer (which one), with an http frontend (nginx, httpd, ...)*
**Problem description**:
- *be as explicit has you can*
- *describe the problem and its symptoms*
- *explain how to reproduce*
- *attach whatever information that can help understanding the context (screen capture, log files)*
- *do your best to use a correct english (re-read yourself)*

8
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,8 @@
### Before submitting a pull-request to Gitbucket I have first:
- [] read the [contribution guidelines](https://github.com/gitbucket/gitbucket/blob/master/CONTRIBUTING.md)
- [] rebased my branch over master
- [] verified that project is compiling
- [] verified that tests are passing
- [] squashed my commits as appropriate *(keep several commits if it is relevant to understand the PR)*
- [] [marked as closed](https://help.github.com/articles/closing-issues-via-commit-messages/) all issue ID that this PR should correct

View File

@@ -1,7 +0,0 @@
# Guideline for Issues
- If you have any question about GitBucket, send it to [gitter room](https://gitter.im/gitbucket/gitbucket) before raise an issue.
- Make sure check whether there is a same question or request in the past.
- When raise a new issue, write subject in **English** at least.
- We can also support in Japaneses other than English at [gitter room for Japanese](https://gitter.im/gitbucket/gitbucket_ja).
- First priority of GitBucket is easy installation and reproduce GitHub behavior, so we might reject if your request is against it.

View File

@@ -60,6 +60,13 @@ Support
Release Notes
--------
### 3.12 - 27 Feb 2016
- New GitHub UI
- Improve mobile view
- Improve printing style
- Individual URL for pull request tabs
- SSH host configuration is separated from HTTP base URL
### 3.11 - 30 Jan 2016
- Upgrade Scalatra to 2.4
- Sidebar and Footer for Wiki
@@ -67,6 +74,7 @@ Release Notes
- Limit recent updated repositories list
- Issue actions look-alike GitHub
- Web API for labels
- Requires Java 8
### 3.10 - 30 Dec 2015
- Move to Bootstrap3

View File

@@ -1,6 +1,6 @@
val Organization = "gitbucket"
val Name = "gitbucket"
val GitBucketVersion = "3.11.0"
val GitBucketVersion = "3.12.0"
val ScalatraVersion = "2.4.0"
val JettyVersion = "9.3.6.v20151106"
@@ -10,7 +10,7 @@ sourcesInBase := false
organization := Organization
name := Name
version := GitBucketVersion
scalaVersion := "2.11.6"
scalaVersion := "2.11.7"
// dependency settings
resolvers ++= Seq(
@@ -43,8 +43,8 @@ libraryDependencies ++= Seq(
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
"junit" % "junit" % "4.12" % "test",
"org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test",
"org.specs2" %% "specs2-junit" % "3.6.6" % "test"
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
"org.scalaz" %% "scalaz-core" % "7.2.0" % "test"
)
// Twirl settings
@@ -52,7 +52,7 @@ play.twirl.sbt.Import.TwirlKeys.templateImports += "gitbucket.core._"
// Compiler settings
scalacOptions := Seq("-deprecation", "-language:postfixOps")
javacOptions in compile ++= Seq("-target", "7", "-source", "7")
javacOptions in compile ++= Seq("-target", "8", "-source", "8")
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"
testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "junitxml", "console")
javaOptions in Test += "-Dgitbucket.home=target/gitbucket_home_for_test"

View File

@@ -1 +1 @@
sbt.version=0.13.8
sbt.version=0.13.9

View File

@@ -1,3 +1,3 @@
#!/bin/sh
export GITBUCKET_VERSION=`cat ../project/build.scala | grep 'val Version' | cut -d \" -f 2`
export GITBUCKET_VERSION=`cat ../build.sbt | grep 'val GitBucketVersion' | cut -d \" -f 2`
echo "GITBUCKET_VERSION: $GITBUCKET_VERSION"

View File

@@ -1,2 +1,2 @@
set SCRIPT_DIR=%~dp0
java %JAVA_OPTS% -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar "%SCRIPT_DIR%\sbt-launch-0.13.8.jar" %*
java %JAVA_OPTS% -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar "%SCRIPT_DIR%\sbt-launch-0.13.9.jar" %*

2
sbt.sh
View File

@@ -1,2 +1,2 @@
#!/bin/sh
java $JAVA_OPTS -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar `dirname $0`/sbt-launch-0.13.8.jar "$@"
java $JAVA_OPTS -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar `dirname $0`/sbt-launch-0.13.9.jar "$@"

View File

@@ -27,12 +27,10 @@ class ScalatraBootstrap extends LifeCycle {
}
context.mount(new IndexController, "/")
context.mount(new SearchController, "/")
context.mount(new FileUploadController, "/upload")
context.mount(new DashboardController, "/*")
context.mount(new UserManagementController, "/*")
context.mount(new SystemSettingsController, "/*")
context.mount(new PluginsController, "/*")
context.mount(new AccountController, "/*")
context.mount(new RepositoryViewerController, "/*")
context.mount(new WikiController, "/*")

View File

@@ -133,7 +133,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
val members = getGroupMembers(account.userName)
gitbucket.core.account.html.repositories(account,
if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
getVisibleRepositories(context.loginAccount, context.baseUrl, Some(userName)),
getVisibleRepositories(context.loginAccount, Some(userName)),
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager }))
}
}
@@ -366,7 +366,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
*/
post("/new", newRepositoryForm)(usersOnly { form =>
LockUtil.lock(s"${form.owner}/${form.name}"){
if(getRepository(form.owner, form.name, context.baseUrl).isEmpty){
if(getRepository(form.owner, form.name).isEmpty){
createRepository(form.owner, form.name, form.description, form.isPrivate, form.createReadme)
}
@@ -385,9 +385,9 @@ trait AccountControllerBase extends AccountManagementControllerBase {
data <- extractFromJsonBody[CreateARepository] if data.isValid
} yield {
LockUtil.lock(s"${owner}/${data.name}") {
if(getRepository(owner, data.name, context.baseUrl).isEmpty){
if(getRepository(owner, data.name).isEmpty){
createRepository(owner, data.name, data.description, data.`private`, data.auto_init)
val repository = getRepository(owner, data.name, context.baseUrl).get
val repository = getRepository(owner, data.name).get
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(owner).get)))
} else {
ApiError(
@@ -409,9 +409,9 @@ trait AccountControllerBase extends AccountManagementControllerBase {
data <- extractFromJsonBody[CreateARepository] if data.isValid
} yield {
LockUtil.lock(s"${groupName}/${data.name}") {
if(getRepository(groupName, data.name, context.baseUrl).isEmpty){
if(getRepository(groupName, data.name).isEmpty){
createRepository(groupName, data.name, data.description, data.`private`, data.auto_init)
val repository = getRepository(groupName, data.name, context.baseUrl).get
val repository = getRepository(groupName, data.name).get
JsonFormat(ApiRepository(repository, ApiUser(getAccountByUserName(groupName).get)))
} else {
ApiError(
@@ -447,7 +447,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
val accountName = form.accountName
LockUtil.lock(s"${accountName}/${repository.name}"){
if(getRepository(accountName, repository.name, baseUrl).isDefined ||
if(getRepository(accountName, repository.name).isDefined ||
(accountName != loginUserName && !getGroupsByUserName(loginUserName).contains(accountName))){
// redirect to the repository if repository already exists
redirect(s"/${accountName}/${repository.name}")

View File

@@ -180,7 +180,6 @@ abstract class ControllerBase extends ScalatraFilter
* Context object for the current request.
*/
case class Context(settings: SystemSettingsService.SystemSettings, loginAccount: Option[Account], request: HttpServletRequest){
val path = settings.baseUrl.getOrElse(request.getContextPath)
val currentPath = request.getRequestURI.substring(request.getContextPath.length)
val baseUrl = settings.baseUrl(request)

View File

@@ -94,7 +94,7 @@ trait DashboardControllerBase extends ControllerBase {
val userName = context.loginAccount.get.userName
val condition = getOrCreateCondition(Keys.Session.DashboardIssues, filter, userName)
val userRepos = getUserRepositories(userName, context.baseUrl, true).map(repo => repo.owner -> repo.name)
val userRepos = getUserRepositories(userName, true).map(repo => repo.owner -> repo.name)
val page = IssueSearchCondition.page(request)
html.issues(

View File

@@ -2,35 +2,46 @@ package gitbucket.core.controller
import gitbucket.core.api._
import gitbucket.core.helper.xml
import gitbucket.core.html
import gitbucket.core.model.Account
import gitbucket.core.service.{RepositoryService, ActivityService, AccountService}
import gitbucket.core.service.{RepositoryService, ActivityService, AccountService, RepositorySearchService, IssuesService}
import gitbucket.core.util.Implicits._
import gitbucket.core.util.{LDAPUtil, Keys, UsersAuthenticator}
import gitbucket.core.util.ControlUtil._
import gitbucket.core.util.{LDAPUtil, Keys, UsersAuthenticator, ReferrerAuthenticator, StringUtil}
import io.github.gitbucket.scalatra.forms._
class IndexController extends IndexControllerBase
with RepositoryService with ActivityService with AccountService with UsersAuthenticator
with RepositoryService with ActivityService with AccountService with RepositorySearchService with IssuesService
with UsersAuthenticator with ReferrerAuthenticator
trait IndexControllerBase extends ControllerBase {
self: RepositoryService with ActivityService with AccountService with UsersAuthenticator =>
self: RepositoryService with ActivityService with AccountService with RepositorySearchService
with UsersAuthenticator with ReferrerAuthenticator =>
case class SignInForm(userName: String, password: String)
val form = mapping(
val signinForm = mapping(
"userName" -> trim(label("Username", text(required))),
"password" -> trim(label("Password", text(required)))
)(SignInForm.apply)
val searchForm = mapping(
"query" -> trim(text(required)),
"owner" -> trim(text(required)),
"repository" -> trim(text(required))
)(SearchForm.apply)
case class SearchForm(query: String, owner: String, repository: String)
get("/"){
val loginAccount = context.loginAccount
if(loginAccount.isEmpty) {
html.index(getRecentActivities(),
getVisibleRepositories(loginAccount, context.baseUrl, withoutPhysicalInfo = true),
loginAccount.map{ account => getUserRepositories(account.userName, context.baseUrl, withoutPhysicalInfo = true) }.getOrElse(Nil)
gitbucket.core.html.index(getRecentActivities(),
getVisibleRepositories(loginAccount, withoutPhysicalInfo = true),
loginAccount.map{ account => getUserRepositories(account.userName, withoutPhysicalInfo = true) }.getOrElse(Nil)
)
} else {
val loginUserName = loginAccount.get.userName
@@ -39,9 +50,9 @@ trait IndexControllerBase extends ControllerBase {
visibleOwnerSet ++= loginUserGroups
html.index(getRecentActivitiesByOwners(visibleOwnerSet),
getVisibleRepositories(loginAccount, context.baseUrl, withoutPhysicalInfo = true),
loginAccount.map{ account => getUserRepositories(account.userName, context.baseUrl, withoutPhysicalInfo = true) }.getOrElse(Nil)
gitbucket.core.html.index(getRecentActivitiesByOwners(visibleOwnerSet),
getVisibleRepositories(loginAccount, withoutPhysicalInfo = true),
loginAccount.map{ account => getUserRepositories(account.userName, withoutPhysicalInfo = true) }.getOrElse(Nil)
)
}
}
@@ -51,10 +62,10 @@ trait IndexControllerBase extends ControllerBase {
if(redirect.isDefined && redirect.get.startsWith("/")){
flash += Keys.Flash.Redirect -> redirect.get
}
html.signin()
gitbucket.core.html.signin()
}
post("/signin", form){ form =>
post("/signin", signinForm){ form =>
authenticate(context.settings, form.userName, form.password) match {
case Some(account) => signin(account)
case None => redirect("/signin")
@@ -119,4 +130,33 @@ trait IndexControllerBase extends ControllerBase {
// this message is same as github enterprise...
org.scalatra.NotFound(ApiError("Rate limiting is not enabled."))
}
// TODO Move to RepositoryViwerController?
post("/search", searchForm){ form =>
redirect(s"/${form.owner}/${form.repository}/search?q=${StringUtil.urlEncode(form.query)}")
}
// TODO Move to RepositoryViwerController?
get("/:owner/:repository/search")(referrersOnly { repository =>
defining(params("q").trim, params.getOrElse("type", "code")){ case (query, target) =>
val page = try {
val i = params.getOrElse("page", "1").toInt
if(i <= 0) 1 else i
} catch {
case e: NumberFormatException => 1
}
target.toLowerCase match {
case "issue" => gitbucket.core.search.html.issues(
searchIssues(repository.owner, repository.name, query),
countFiles(repository.owner, repository.name, query),
query, page, repository)
case _ => gitbucket.core.search.html.code(
searchFiles(repository.owner, repository.name, query),
countIssues(repository.owner, repository.name, query),
query, page, repository)
}
}
})
}

View File

@@ -1,11 +0,0 @@
package gitbucket.core.controller
import gitbucket.core.admin.plugins.html
import gitbucket.core.plugin.PluginRegistry
import gitbucket.core.util.AdminAuthenticator
class PluginsController extends ControllerBase with AdminAuthenticator {
get("/admin/plugins")(adminOnly {
html.plugins(PluginRegistry().getPlugins())
})
}

View File

@@ -137,7 +137,7 @@ trait PullRequestsControllerBase extends ControllerBase {
baseOwner <- users.get(repository.owner)
headOwner <- users.get(pullRequest.requestUserName)
issueUser <- users.get(issue.openedUserName)
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName, baseUrl)
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
} yield {
JsonFormat(ApiPullRequest(
issue,
@@ -196,7 +196,7 @@ trait PullRequestsControllerBase extends ControllerBase {
issue,
pullreq,
repository,
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName, context.baseUrl).get)
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName).get)
}
} getOrElse NotFound
})
@@ -229,7 +229,7 @@ trait PullRequestsControllerBase extends ControllerBase {
if(branchProtection.needStatusCheck(loginAccount.userName)){
flash += "error" -> s"branch ${pullreq.requestBranch} is protected need status check."
} else {
val repository = getRepository(owner, name, context.baseUrl).get
val repository = getRepository(owner, name).get
LockUtil.lock(s"${owner}/${name}"){
val alias = if(pullreq.repositoryName == pullreq.requestRepositoryName && pullreq.userName == pullreq.requestUserName){
pullreq.branch
@@ -310,7 +310,7 @@ trait PullRequestsControllerBase extends ControllerBase {
pullreq.requestUserName, pullreq.requestRepositoryName, pullreq.commitIdTo)
// close issue by content of pull request
val defaultBranch = getRepository(owner, name, context.baseUrl).get.repository.defaultBranch
val defaultBranch = getRepository(owner, name).get.repository.defaultBranch
if(pullreq.branch == defaultBranch){
commits.flatten.foreach { commit =>
closeIssuesFromMessage(commit.fullMessage, loginAccount.userName, owner, name)
@@ -343,7 +343,7 @@ trait PullRequestsControllerBase extends ControllerBase {
val headBranch:Option[String] = params.get("head")
(forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match {
case (Some(originUserName), Some(originRepositoryName)) => {
getRepository(originUserName, originRepositoryName, context.baseUrl).map { originRepository =>
getRepository(originUserName, originRepositoryName).map { originRepository =>
using(
Git.open(getRepositoryDir(originUserName, originRepositoryName)),
Git.open(getRepositoryDir(forkedRepository.owner, forkedRepository.name))
@@ -384,12 +384,12 @@ trait PullRequestsControllerBase extends ControllerBase {
forkedRepository.repository.originRepositoryName
} else {
// Sibling repository
getUserRepositories(originOwner, context.baseUrl).find { x =>
getUserRepositories(originOwner).find { x =>
x.repository.originUserName == forkedRepository.repository.originUserName &&
x.repository.originRepositoryName == forkedRepository.repository.originRepositoryName
}.map(_.repository.repositoryName)
};
originRepository <- getRepository(originOwner, originRepositoryName, context.baseUrl)
originRepository <- getRepository(originOwner, originRepositoryName)
) yield {
using(
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),
@@ -457,7 +457,7 @@ trait PullRequestsControllerBase extends ControllerBase {
getForkedRepositories(forkedRepository.owner, forkedRepository.name).find(_._1 == originOwner).map(_._2)
}
};
originRepository <- getRepository(originOwner, originRepositoryName, context.baseUrl)
originRepository <- getRepository(originOwner, originRepositoryName)
) yield {
using(
Git.open(getRepositoryDir(originRepository.owner, originRepository.name)),

View File

@@ -560,8 +560,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
html.forked(
getRepository(
repository.repository.originUserName.getOrElse(repository.owner),
repository.repository.originRepositoryName.getOrElse(repository.name),
context.baseUrl),
repository.repository.originRepositoryName.getOrElse(repository.name)),
getForkedRepositories(
repository.repository.originUserName.getOrElse(repository.owner),
repository.repository.originRepositoryName.getOrElse(repository.name)),
@@ -759,8 +758,6 @@ trait RepositoryViewerControllerBase extends ControllerBase {
.setTree(revCommit.getTree)
.setOutputStream(response.getOutputStream)
.call()
Unit
}
}

View File

@@ -1,51 +0,0 @@
package gitbucket.core.controller
import gitbucket.core.search.html
import gitbucket.core.service._
import gitbucket.core.util.{StringUtil, ControlUtil, ReferrerAuthenticator, Implicits}
import ControlUtil._
import Implicits._
import io.github.gitbucket.scalatra.forms._
class SearchController extends SearchControllerBase
with RepositoryService with AccountService with ActivityService with RepositorySearchService with IssuesService with ReferrerAuthenticator
trait SearchControllerBase extends ControllerBase { self: RepositoryService
with ActivityService with RepositorySearchService with ReferrerAuthenticator =>
val searchForm = mapping(
"query" -> trim(text(required)),
"owner" -> trim(text(required)),
"repository" -> trim(text(required))
)(SearchForm.apply)
case class SearchForm(query: String, owner: String, repository: String)
post("/search", searchForm){ form =>
redirect(s"/${form.owner}/${form.repository}/search?q=${StringUtil.urlEncode(form.query)}")
}
get("/:owner/:repository/search")(referrersOnly { repository =>
defining(params("q").trim, params.getOrElse("type", "code")){ case (query, target) =>
val page = try {
val i = params.getOrElse("page", "1").toInt
if(i <= 0) 1 else i
} catch {
case e: NumberFormatException => 1
}
target.toLowerCase match {
case "issue" => html.issues(
searchIssues(repository.owner, repository.name, query),
countFiles(repository.owner, repository.name, query),
query, page, repository)
case _ => html.code(
searchFiles(repository.owner, repository.name, query),
countIssues(repository.owner, repository.name, query),
query, page, repository)
}
}
})
}

View File

@@ -4,6 +4,7 @@ import gitbucket.core.admin.html
import gitbucket.core.service.{AccountService, SystemSettingsService}
import gitbucket.core.util.AdminAuthenticator
import gitbucket.core.ssh.SshServer
import gitbucket.core.plugin.PluginRegistry
import SystemSettingsService._
import io.github.gitbucket.scalatra.forms._
@@ -23,6 +24,7 @@ trait SystemSettingsControllerBase extends ControllerBase {
"notification" -> trim(label("Notification", boolean())),
"activityLogLimit" -> trim(label("Limit of activity logs", optional(number()))),
"ssh" -> trim(label("SSH access", boolean())),
"sshHost" -> trim(label("SSH host", optional(text()))),
"sshPort" -> trim(label("SSH port", optional(number()))),
"useSMTP" -> trim(label("SMTP", boolean())),
"smtp" -> optionalIfNotChecked("useSMTP", mapping(
@@ -50,9 +52,14 @@ trait SystemSettingsControllerBase extends ControllerBase {
"keystore" -> trim(label("Keystore", optional(text())))
)(Ldap.apply))
)(SystemSettings.apply).verifying { settings =>
if(settings.ssh && settings.baseUrl.isEmpty){
Seq("baseUrl" -> "Base URL is required if SSH access is enabled.")
} else Nil
Vector(
if(settings.ssh && settings.baseUrl.isEmpty){
Some("baseUrl" -> "Base URL is required if SSH access is enabled.")
} else None,
if(settings.ssh && settings.sshHost.isEmpty){
Some("sshHost" -> "SSH host is required if SSH access is enabled.")
} else None
).flatten
}
private val pluginForm = mapping(
@@ -68,20 +75,21 @@ trait SystemSettingsControllerBase extends ControllerBase {
post("/admin/system", form)(adminOnly { form =>
saveSystemSettings(form)
if(form.ssh && SshServer.isActive && context.settings.sshPort != form.sshPort){
SshServer.stop()
}
if(form.ssh && !SshServer.isActive && form.baseUrl.isDefined){
SshServer.start(
form.sshPort.getOrElse(SystemSettingsService.DefaultSshPort),
form.baseUrl.get)
} else if(!form.ssh && SshServer.isActive){
if (form.sshAddress != context.settings.sshAddress) {
SshServer.stop()
for {
sshAddress <- form.sshAddress
baseUrl <- form.baseUrl
}
SshServer.start(sshAddress, baseUrl)
}
flash += "info" -> "System settings has been updated."
redirect("/admin/system")
})
get("/admin/plugins")(adminOnly {
html.plugins(PluginRegistry().getPlugins())
})
}

View File

@@ -35,7 +35,7 @@ protected[model] trait TemplateComponent { self: Profile =>
byRepository(userName, repositoryName) && (this.labelId === labelId)
def byLabel(owner: String, repository: String, labelName: String) =
byRepository(userName, repositoryName) && (this.labelName === labelName.bind)
byRepository(owner, repository) && (this.labelName === labelName.bind)
}
trait MilestoneTemplate extends BasicTemplate { self: Table[_] =>

View File

@@ -399,7 +399,7 @@ trait IssuesService {
object IssuesService {
import javax.servlet.http.HttpServletRequest
val IssueLimit = 30
val IssueLimit = 25
case class IssueSearchCondition(
labels: Set[String] = Set.empty,

View File

@@ -1,5 +1,6 @@
package gitbucket.core.service
import gitbucket.core.controller.Context
import gitbucket.core.model.{Collaborator, Repository, Account}
import gitbucket.core.model.Profile._
import gitbucket.core.util.JGitUtil
@@ -194,10 +195,9 @@ trait RepositoryService { self: AccountService =>
*
* @param userName the user name of the repository owner
* @param repositoryName the repository name
* @param baseUrl the base url of this application
* @return the repository information
*/
def getRepository(userName: String, repositoryName: String, baseUrl: String)(implicit s: Session): Option[RepositoryInfo] = {
def getRepository(userName: String, repositoryName: String)(implicit s: Session): Option[RepositoryInfo] = {
(Repositories filter { t => t.byRepository(userName, repositoryName) } firstOption) map { repository =>
// for getting issue count and pull request count
val issues = Issues.filter { t =>
@@ -205,7 +205,7 @@ trait RepositoryService { self: AccountService =>
}.map(_.pullRequest).list
new RepositoryInfo(
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl),
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName),
repository,
issues.count(_ == false),
issues.count(_ == true),
@@ -234,7 +234,7 @@ trait RepositoryService { self: AccountService =>
}.list
}
def getUserRepositories(userName: String, baseUrl: String, withoutPhysicalInfo: Boolean = false)
def getUserRepositories(userName: String, withoutPhysicalInfo: Boolean = false)
(implicit s: Session): List[RepositoryInfo] = {
Repositories.filter { t1 =>
(t1.userName === userName.bind) ||
@@ -242,9 +242,9 @@ trait RepositoryService { self: AccountService =>
}.sortBy(_.lastActivityDate desc).list.map{ repository =>
new RepositoryInfo(
if(withoutPhysicalInfo){
new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName, baseUrl)
new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName)
} else {
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl)
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName)
},
repository,
getForkedCount(
@@ -260,13 +260,12 @@ trait RepositoryService { self: AccountService =>
* If repositoryUserName is given then filters results by repository owner.
*
* @param loginAccount the logged in account
* @param baseUrl the base url of this application
* @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
* @return the repository information which is sorted in descending order of lastActivityDate.
*/
def getVisibleRepositories(loginAccount: Option[Account], baseUrl: String, repositoryUserName: Option[String] = None,
def getVisibleRepositories(loginAccount: Option[Account], repositoryUserName: Option[String] = None,
withoutPhysicalInfo: Boolean = false)
(implicit s: Session): List[RepositoryInfo] = {
(loginAccount match {
@@ -284,9 +283,9 @@ trait RepositoryService { self: AccountService =>
}.sortBy(_.lastActivityDate desc).list.map{ repository =>
new RepositoryInfo(
if(withoutPhysicalInfo){
new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName, baseUrl)
new JGitUtil.RepositoryInfo(repository.userName, repository.repositoryName)
} else {
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl)
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName)
},
repository,
getForkedCount(
@@ -389,32 +388,39 @@ trait RepositoryService { self: AccountService =>
object RepositoryService {
case class RepositoryInfo(owner: String, name: String, httpUrl: String, repository: Repository,
case class RepositoryInfo(owner: String, name: String, repository: Repository,
issueCount: Int, pullCount: Int, commitCount: Int, forkedCount: Int,
branchList: Seq[String], tags: Seq[JGitUtil.TagInfo], managers: Seq[String]){
lazy val host = """^https?://(.+?)(:\d+)?/""".r.findFirstMatchIn(httpUrl).get.group(1)
def sshUrl(port: Int, userName: String) = s"ssh://${userName}@${host}:${port}/${owner}/${name}.git"
def sshOpenRepoUrl(platform: String, port: Int, userName: String) = openRepoUrl(platform, sshUrl(port, userName))
def httpOpenRepoUrl(platform: String) = openRepoUrl(platform, httpUrl)
def openRepoUrl(platform: String, openUrl: String) = s"github-${platform}://openRepo/${openUrl}"
branchList: Seq[String], tags: Seq[JGitUtil.TagInfo], managers: Seq[String]) {
/**
* Creates instance with issue count and pull request count.
*/
def this(repo: JGitUtil.RepositoryInfo, model: Repository, issueCount: Int, pullCount: Int, forkedCount: Int, managers: Seq[String]) =
this(repo.owner, repo.name, repo.url, model, issueCount, pullCount, repo.commitCount, forkedCount, repo.branchList, repo.tags, managers)
this(
repo.owner, repo.name, model,
issueCount, pullCount, repo.commitCount, forkedCount,
repo.branchList, repo.tags, managers)
/**
* Creates instance without issue count and pull request count.
*/
def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int, managers: Seq[String]) =
this(repo.owner, repo.name, repo.url, model, 0, 0, repo.commitCount, forkedCount, repo.branchList, repo.tags, managers)
def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int, managers: Seq[String]) =
this(
repo.owner, repo.name, model,
0, 0, repo.commitCount, forkedCount,
repo.branchList, repo.tags, managers)
def httpUrl(implicit context: Context): String = RepositoryService.httpUrl(owner, name)
def sshUrl(implicit context: Context): Option[String] = RepositoryService.sshUrl(owner, name)
}
case class RepositoryTreeNode(owner: String, name: String, children: List[RepositoryTreeNode])
def httpUrl(owner: String, name: String)(implicit context: Context): String = s"${context.baseUrl}/git/${owner}/${name}.git"
def sshUrl(owner: String, name: String)(implicit context: Context): Option[String] =
if(context.settings.ssh){
context.loginAccount.flatMap { loginAccount =>
context.settings.sshAddress.map { x => s"ssh://${loginAccount.userName}@${x.host}:${x.port}/${owner}/${name}.git" }
}
} else None
def openRepoUrl(openUrl: String)(implicit context: Context): String = s"github-${context.platform}://openRepo/${openUrl}"
}

View File

@@ -1,6 +1,7 @@
package gitbucket.core.service
import gitbucket.core.util.{Directory, ControlUtil}
import gitbucket.core.util.Implicits._
import Directory._
import ControlUtil._
import SystemSettingsService._
@@ -21,6 +22,7 @@ trait SystemSettingsService {
props.setProperty(Notification, settings.notification.toString)
settings.activityLogLimit.foreach(x => props.setProperty(ActivityLogLimit, x.toString))
props.setProperty(Ssh, settings.ssh.toString)
settings.sshHost.foreach(x => props.setProperty(SshHost, x.trim))
settings.sshPort.foreach(x => props.setProperty(SshPort, x.toString))
props.setProperty(UseSMTP, settings.useSMTP.toString)
if(settings.useSMTP) {
@@ -75,6 +77,7 @@ trait SystemSettingsService {
getValue(props, Notification, false),
getOptionValue[Int](props, ActivityLogLimit, None),
getValue(props, Ssh, false),
getOptionValue[String](props, SshHost, None).map(_.trim),
getOptionValue(props, SshPort, Some(DefaultSshPort)),
getValue(props, UseSMTP, getValue(props, Notification, false)), // handle migration scenario from only notification to useSMTP
if(getValue(props, UseSMTP, getValue(props, Notification, false))){
@@ -126,16 +129,19 @@ object SystemSettingsService {
notification: Boolean,
activityLogLimit: Option[Int],
ssh: Boolean,
sshHost: Option[String],
sshPort: Option[Int],
useSMTP: Boolean,
smtp: Option[Smtp],
ldapAuthentication: Boolean,
ldap: Option[Ldap]){
def baseUrl(request: HttpServletRequest): String = baseUrl.getOrElse {
defining(request.getRequestURL.toString){ url =>
url.substring(0, url.length - (request.getRequestURI.length - request.getContextPath.length))
def baseUrl(request: HttpServletRequest): String = baseUrl.fold(request.baseUrl)(_.stripSuffix("/"))
def sshAddress:Option[SshAddress] =
for {
host <- sshHost if ssh
}
}.stripSuffix("/")
yield SshAddress(host, sshPort.getOrElse(DefaultSshPort))
}
case class Ldap(
@@ -161,6 +167,10 @@ object SystemSettingsService {
fromAddress: Option[String],
fromName: Option[String])
case class SshAddress(
host:String,
port:Int)
val DefaultSshPort = 29418
val DefaultSmtpPort = 25
val DefaultLdapPort = 389
@@ -174,6 +184,7 @@ object SystemSettingsService {
private val Notification = "notification"
private val ActivityLogLimit = "activity_log_limit"
private val Ssh = "ssh"
private val SshHost = "ssh.host"
private val SshPort = "ssh.port"
private val UseSMTP = "useSMTP"
private val SmtpHost = "smtp.host"
@@ -216,7 +227,4 @@ object SystemSettingsService {
else value
}
// // TODO temporary flag
// val enablePluginSystem = Option(System.getProperty("enable.plugin")).getOrElse("false").toBoolean
}

View File

@@ -161,7 +161,7 @@ trait WebHookPullRequestService extends WebHookService {
baseOwner <- users.get(repository.owner)
headOwner <- users.get(pullRequest.requestUserName)
issueUser <- users.get(issue.openedUserName)
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName, baseUrl)
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
} yield {
WebHookPullRequestPayload(
action = action,
@@ -200,7 +200,7 @@ trait WebHookPullRequestService extends WebHookService {
import WebHookService._
for{
((issue, issueUser, pullRequest, baseOwner, headOwner), webHooks) <- getPullRequestsByRequestForWebhook(requestRepository.owner, requestRepository.name, requestBranch)
baseRepo <- getRepository(pullRequest.userName, pullRequest.repositoryName, baseUrl)
baseRepo <- getRepository(pullRequest.userName, pullRequest.repositoryName)
} yield {
val payload = WebHookPullRequestPayload(
action = action,
@@ -229,7 +229,7 @@ trait WebHookPullRequestReviewCommentService extends WebHookService {
baseOwner <- users.get(repository.owner)
headOwner <- users.get(pullRequest.requestUserName)
issueUser <- users.get(issue.openedUserName)
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName, baseUrl)
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
} yield {
WebHookPullRequestReviewCommentPayload(
action = action,

View File

@@ -1,7 +1,9 @@
package gitbucket.core.service
import java.util.Date
import gitbucket.core.controller.Context
import gitbucket.core.model.Account
import gitbucket.core.service.RepositoryService.RepositoryInfo
import gitbucket.core.util._
import gitbucket.core.util.ControlUtil._
import org.eclipse.jgit.api.Git
@@ -13,7 +15,6 @@ import java.io.ByteArrayInputStream
import org.eclipse.jgit.patch._
import org.eclipse.jgit.api.errors.PatchFormatException
import scala.collection.JavaConverters._
import RepositoryService.RepositoryInfo
object WikiService {
@@ -38,10 +39,13 @@ object WikiService {
*/
case class WikiPageHistoryInfo(name: String, committer: String, message: String, date: Date)
def httpUrl(repository: RepositoryInfo) = repository.httpUrl.replaceFirst("\\.git\\Z", ".wiki.git")
def sshUrl(repository: RepositoryInfo, settings: SystemSettingsService.SystemSettings, userName: String) =
repository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), userName).replaceFirst("\\.git\\Z", ".wiki.git")
def wikiHttpUrl(repositoryInfo: RepositoryInfo)(implicit context: Context): String
= RepositoryService.httpUrl(repositoryInfo.owner, repositoryInfo.name + ".wiki")
def wikiSshUrl(repositoryInfo: RepositoryInfo)(implicit context: Context): Option[String]
= RepositoryService.sshUrl(repositoryInfo.owner, repositoryInfo.name + ".wiki")
}
trait WikiService {

View File

@@ -21,6 +21,7 @@ object AutoUpdate {
* The history of versions. A head of this sequence is the current GitBucket version.
*/
val versions = Seq(
new Version(3, 12),
new Version(3, 11),
new Version(3, 10),
new Version(3, 9),

View File

@@ -74,7 +74,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
request.paths match {
case Array(_, repositoryOwner, repositoryName, _*) =>
getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match {
getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", "")) match {
case Some(repository) => {
if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){
chain.doFilter(request, response)

View File

@@ -160,7 +160,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
countIssue(IssueSearchCondition(state = "open"), false, owner -> repository) +
countIssue(IssueSearchCondition(state = "closed"), false, owner -> repository)
val repositoryInfo = getRepository(owner, repository, baseUrl).get
val repositoryInfo = getRepository(owner, repository).get
// Extract new commit and apply issue comment
val defaultBranch = repositoryInfo.repository.defaultBranch

View File

@@ -87,11 +87,11 @@ abstract class DefaultGitCommand(val owner: String, val repoName: String) extend
}
class DefaultGitUploadPack(owner: String, repoName: String, baseUrl: String) extends DefaultGitCommand(owner, repoName)
class DefaultGitUploadPack(owner: String, repoName: String) extends DefaultGitCommand(owner, repoName)
with RepositoryService with AccountService {
override protected def runTask(user: String)(implicit session: Session): Unit = {
getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", ""), baseUrl).foreach { repositoryInfo =>
getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", "")).foreach { repositoryInfo =>
if(!repositoryInfo.repository.isPrivate || isWritableUser(user, repositoryInfo)){
using(Git.open(getRepositoryDir(owner, repoName))) { git =>
val repository = git.getRepository
@@ -107,7 +107,7 @@ class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String) ex
with RepositoryService with AccountService {
override protected def runTask(user: String)(implicit session: Session): Unit = {
getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", ""), baseUrl).foreach { repositoryInfo =>
getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", "")).foreach { repositoryInfo =>
if(isWritableUser(user, repositoryInfo)){
using(Git.open(getRepositoryDir(owner, repoName))) { git =>
val repository = git.getRepository
@@ -124,7 +124,7 @@ class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String) ex
}
}
class PluginGitUploadPack(repoName: String, baseUrl: String, routing: GitRepositoryRouting) extends GitCommand
class PluginGitUploadPack(repoName: String, routing: GitRepositoryRouting) extends GitCommand
with SystemSettingsService {
override protected def runTask(user: String)(implicit session: Session): Unit = {
@@ -139,7 +139,7 @@ class PluginGitUploadPack(repoName: String, baseUrl: String, routing: GitReposit
}
}
class PluginGitReceivePack(repoName: String, baseUrl: String, routing: GitRepositoryRouting) extends GitCommand
class PluginGitReceivePack(repoName: String, routing: GitRepositoryRouting) extends GitCommand
with SystemSettingsService {
override protected def runTask(user: String)(implicit session: Session): Unit = {
@@ -163,9 +163,9 @@ class GitCommandFactory(baseUrl: String) extends CommandFactory {
logger.debug(s"command: $command")
command match {
case SimpleCommandRegex ("upload" , repoName) if(pluginRepository(repoName)) => new PluginGitUploadPack (repoName, baseUrl, routing(repoName))
case SimpleCommandRegex ("receive", repoName) if(pluginRepository(repoName)) => new PluginGitReceivePack(repoName, baseUrl, routing(repoName))
case DefaultCommandRegex("upload" , owner, repoName) => new DefaultGitUploadPack (owner, repoName, baseUrl)
case SimpleCommandRegex ("upload" , repoName) if(pluginRepository(repoName)) => new PluginGitUploadPack (repoName, routing(repoName))
case SimpleCommandRegex ("receive", repoName) if(pluginRepository(repoName)) => new PluginGitReceivePack(repoName, routing(repoName))
case DefaultCommandRegex("upload" , owner, repoName) => new DefaultGitUploadPack (owner, repoName)
case DefaultCommandRegex("receive", owner, repoName) => new DefaultGitReceivePack(owner, repoName, baseUrl)
case _ => new UnknownCommand(command)
}

View File

@@ -1,12 +1,13 @@
package gitbucket.core.ssh
import gitbucket.core.service.SystemSettingsService
import gitbucket.core.service.SystemSettingsService.SshAddress
import org.apache.sshd.common.Factory
import org.apache.sshd.server.{Environment, ExitCallback, Command}
import java.io.{OutputStream, InputStream}
import org.eclipse.jgit.lib.Constants
class NoShell extends Factory[Command] with SystemSettingsService {
class NoShell(sshAddress:SshAddress) extends Factory[Command] {
override def create(): Command = new Command() {
private var in: InputStream = null
private var out: OutputStream = null
@@ -15,7 +16,6 @@ class NoShell extends Factory[Command] with SystemSettingsService {
override def start(env: Environment): Unit = {
val user = env.getEnv.get("USER")
val port = loadSystemSettings().sshPort.getOrElse(SystemSettingsService.DefaultSshPort)
val message =
"""
| Welcome to
@@ -31,8 +31,8 @@ class NoShell extends Factory[Command] with SystemSettingsService {
|
| Please use:
|
| git clone ssh://%s@GITBUCKET_HOST:%d/OWNER/REPOSITORY_NAME.git
""".stripMargin.format(user, port).replace("\n", "\r\n") + "\r\n"
| git clone ssh://%s@%s:%d/OWNER/REPOSITORY_NAME.git
""".stripMargin.format(user, sshAddress.host, sshAddress.port).replace("\n", "\r\n") + "\r\n"
err.write(Constants.encode(message))
err.flush()
in.close()

View File

@@ -5,7 +5,8 @@ import java.util.concurrent.atomic.AtomicBoolean
import javax.servlet.{ServletContextEvent, ServletContextListener}
import gitbucket.core.service.SystemSettingsService
import gitbucket.core.util.Directory
import gitbucket.core.service.SystemSettingsService.SshAddress
import gitbucket.core.util.{Directory}
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider
import org.slf4j.LoggerFactory
@@ -14,20 +15,20 @@ object SshServer {
private val server = org.apache.sshd.server.SshServer.setUpDefaultServer()
private val active = new AtomicBoolean(false)
private def configure(port: Int, baseUrl: String) = {
server.setPort(port)
private def configure(sshAddress: SshAddress, baseUrl: String) = {
server.setPort(sshAddress.port)
val provider = new SimpleGeneratorHostKeyProvider(new File(s"${Directory.GitBucketHome}/gitbucket.ser"))
provider.setAlgorithm("RSA")
provider.setOverwriteAllowed(false)
server.setKeyPairProvider(provider)
server.setPublickeyAuthenticator(new PublicKeyAuthenticator)
server.setCommandFactory(new GitCommandFactory(baseUrl))
server.setShellFactory(new NoShell)
server.setShellFactory(new NoShell(sshAddress))
}
def start(port: Int, baseUrl: String) = {
def start(sshAddress: SshAddress, baseUrl: String) = {
if(active.compareAndSet(false, true)){
configure(port, baseUrl)
configure(sshAddress, baseUrl)
server.start()
logger.info(s"Start SSH Server Listen on ${server.getPort}")
}
@@ -55,20 +56,18 @@ class SshServerListener extends ServletContextListener with SystemSettingsServic
override def contextInitialized(sce: ServletContextEvent): Unit = {
val settings = loadSystemSettings()
if(settings.ssh){
settings.baseUrl match {
case None =>
logger.error("Could not start SshServer because the baseUrl is not configured.")
case Some(baseUrl) =>
SshServer.start(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), baseUrl)
}
if (settings.sshAddress.isDefined && settings.baseUrl.isEmpty) {
logger.error("Could not start SshServer because the baseUrl is not configured.")
}
for {
sshAddress <- settings.sshAddress
baseUrl <- settings.baseUrl
}
SshServer.start(sshAddress, baseUrl)
}
override def contextDestroyed(sce: ServletContextEvent): Unit = {
if(loadSystemSettings().ssh){
SshServer.stop()
}
SshServer.stop()
}
}

View File

@@ -36,7 +36,7 @@ trait OwnerAuthenticator { self: ControllerBase with RepositoryService with Acco
private def authenticate(action: (RepositoryInfo) => Any) = {
{
defining(request.paths){ paths =>
getRepository(paths(0), paths(1), baseUrl).map { repository =>
getRepository(paths(0), paths(1)).map { repository =>
context.loginAccount match {
case Some(x) if(x.isAdmin) => action(repository)
case Some(x) if(repository.owner == x.userName) => action(repository)
@@ -95,7 +95,7 @@ trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService =
private def authenticate(action: (RepositoryInfo) => Any) = {
{
defining(request.paths){ paths =>
getRepository(paths(0), paths(1), baseUrl).map { repository =>
getRepository(paths(0), paths(1)).map { repository =>
context.loginAccount match {
case Some(x) if(x.isAdmin) => action(repository)
case Some(x) if(paths(0) == x.userName) => action(repository)
@@ -118,7 +118,7 @@ trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
private def authenticate(action: (RepositoryInfo) => Any) = {
{
defining(request.paths){ paths =>
getRepository(paths(0), paths(1), baseUrl).map { repository =>
getRepository(paths(0), paths(1)).map { repository =>
if(!repository.repository.isPrivate){
action(repository)
} else {
@@ -145,7 +145,7 @@ trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService =
private def authenticate(action: (RepositoryInfo) => Any) = {
{
defining(request.paths){ paths =>
getRepository(paths(0), paths(1), baseUrl).map { repository =>
getRepository(paths(0), paths(1)).map { repository =>
context.loginAccount match {
case Some(x) if(x.isAdmin) => action(repository)
case Some(x) if(!repository.repository.isPrivate) => action(repository)

View File

@@ -75,6 +75,11 @@ object Implicits {
def gitRepositoryPath: String = request.getRequestURI.replaceFirst("^/git/", "/")
def baseUrl:String = {
val url = request.getRequestURL.toString
val len = url.length - (request.getRequestURI.length - request.getContextPath.length)
url.substring(0, len).stripSuffix("/")
}
}
implicit class RichSession(session: HttpSession){

View File

@@ -32,14 +32,13 @@ object JGitUtil {
*
* @param owner the user name of the repository owner
* @param name the repository name
* @param url the repository URL
* @param commitCount the commit count. If the repository has over 1000 commits then this property is 1001.
* @param branchList the list of branch names
* @param tags the list of tags
*/
case class RepositoryInfo(owner: String, name: String, url: String, commitCount: Int, branchList: List[String], tags: List[TagInfo]){
def this(owner: String, name: String, baseUrl: String) = {
this(owner, name, s"${baseUrl}/git/${owner}/${name}.git", 0, Nil, Nil)
case class RepositoryInfo(owner: String, name: String, commitCount: Int, branchList: List[String], tags: List[TagInfo]){
def this(owner: String, name: String) = {
this(owner, name, 0, Nil, Nil)
}
}
@@ -174,14 +173,14 @@ object JGitUtil {
/**
* Returns the repository information. It contains branch names and tag names.
*/
def getRepositoryInfo(owner: String, repository: String, baseUrl: String): RepositoryInfo = {
def getRepositoryInfo(owner: String, repository: String): RepositoryInfo = {
using(Git.open(getRepositoryDir(owner, repository))){ git =>
try {
// get commit count
val commitCount = git.log.all.call.iterator.asScala.map(_ => 1).take(10001).sum
RepositoryInfo(
owner, repository, s"${baseUrl}/git/${owner}/${repository}.git",
owner, repository,
// commit count
commitCount,
// branches
@@ -197,7 +196,7 @@ object JGitUtil {
} catch {
// not initialized
case e: NoHeadException => RepositoryInfo(
owner, repository, s"${baseUrl}/git/${owner}/${repository}.git", 0, Nil, Nil)
owner, repository, 0, Nil, Nil)
}
}

View File

@@ -9,7 +9,7 @@ import gitbucket.core.plugin.{RenderRequest, PluginRegistry}
import gitbucket.core.service.{RepositoryService, RequestCache}
import gitbucket.core.util.{FileUtil, JGitUtil, StringUtil}
import play.twirl.api.Html
import play.twirl.api.{Html, HtmlFormat}
/**
* Provides helper methods for Twirl templates.
@@ -225,6 +225,13 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
def avatarLink(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false)(implicit context: Context): Html =
userWithContent(userName, mailAddress)(avatar(userName, size, tooltip, mailAddress))
/**
* Generates the avatar link to the account page.
* If user does not exist or disabled, this method returns avatar image without link.
*/
def avatarLink(commit: JGitUtil.CommitInfo, size: Int)(implicit context: Context): Html =
userWithContent(commit.authorName, commit.authorEmailAddress)(avatar(commit, size))
private def userWithContent(userName: String, mailAddress: String = "", styleClass: String = "")(content: Html)(implicit context: Context): Html =
(if(mailAddress.isEmpty){
getAccountByUserName(userName)
@@ -306,6 +313,19 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
private[this] val detectAndRenderLinksRegex = """(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,13}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))""".r
def detectAndRenderLinks(text: String): Html = {
Html(detectAndRenderLinksRegex.replaceAllIn(text, m => s"""<a href="${m.group(0)}">${m.group(0)}</a>"""))
val matches = detectAndRenderLinksRegex.findAllMatchIn(text).toSeq
val (x, pos) = matches.foldLeft((collection.immutable.Seq.empty[Html], 0)){ case ((x, pos), m) =>
val url = m.group(0)
val href = url.replace("\"", "&quot;")
(x ++ (Seq(
if(pos < m.start) Some(HtmlFormat.escape(text.substring(pos, m.start))) else None,
Some(Html(s"""<a href="${href}">${url}</a>"""))
).flatten), m.end)
}
// append rest fragment
val out = if (pos < text.length) x :+ HtmlFormat.escape(text.substring(pos)) else x
HtmlFormat.fill(out)
}
}

View File

@@ -4,7 +4,7 @@
@import context._
@import gitbucket.core.view.helpers._
@html.main("Applications"){
<div class="container">
<div class="container body">
<div class="row">
<div class="col-md-3">
@menu("application", settings.ssh)
@@ -16,24 +16,27 @@
@if(personalTokens.isEmpty && gneratedToken.isEmpty){
No tokens.
} else {
Tokens you have generated that can be used to access the GitBucket API.<hr>
Tokens you have generated that can be used to access the GitBucket API.
<hr style="margin-top: 10px;">
}
@gneratedToken.map{ case (token, tokenString) =>
<div class="alert alert-info">
Make sure to copy your new personal access token now. You won't be able to see it again!
</div>
@helper.html.copy("generated-token-copy", tokenString){
<input type="text" value="@tokenString" style="width:21em" readonly>
}
<a href="@path/@account.userName/_personalToken/delete/@token.accessTokenId" class="btn btn-mini btn-danger pull-right">Delete</a>
<hr>
<a href="@path/@account.userName/_personalToken/delete/@token.accessTokenId" class="btn btn-sm btn-danger pull-right">Delete</a>
<div style="width: 50%;">
@helper.html.copy("generated-token-copy", tokenString){
<input type="text" value="@tokenString" class="form-control input-sm" readonly>
}
</div>
<hr style="margin-top: 10px;">
}
@personalTokens.zipWithIndex.map { case (token, i) =>
@if(i != 0){
<hr>
<hr style="margin-top: 10px;">
}
<strong>@token.note</strong>
<a href="@path/@account.userName/_personalToken/delete/@token.accessTokenId" class="btn btn-mini btn-danger pull-right">Delete</a>
<a href="@path/@account.userName/_personalToken/delete/@token.accessTokenId" class="btn btn-sm btn-danger pull-right">Delete</a>
}
</div>
</div>
@@ -44,7 +47,7 @@
<fieldset>
<label for="note" class="strong">Token description</label>
<div><span id="error-note" class="error"></span></div>
<input type="text" name="note" id="note" class="form-control" style="width: 400px;"/>
<input type="text" name="note" id="note" class="form-control"/>
<p class="muted">What's this token for?</p>
</fieldset>
<input type="submit" class="btn btn-success" value="Generate token"/>

View File

@@ -3,7 +3,7 @@
@import context._
@import gitbucket.core.view.helpers._
@html.main("Edit your profile"){
<div class="container">
<div class="container body">
<div class="row">
<div class="col-md-3">
@menu("profile", settings.ssh)

View File

@@ -2,7 +2,7 @@
@import context._
@import gitbucket.core.view.helpers._
@html.main(if(account.isEmpty) "Create group" else "Edit group"){
<div class="container">
<div class="container body">
<form id="form" method="post" action="@if(account.isEmpty){@path/groups/new} else {@path/@account.get.userName/_editgroup}" validate="true">
<div class="row">
<div class="col-md-5">

View File

@@ -3,7 +3,7 @@
@import context._
@import gitbucket.core.view.helpers._
@html.main(account.userName){
<div class="container">
<div class="container body">
<div class="container-fluid">
<div class="row">
<div class="col-md-4">
@@ -22,7 +22,7 @@
<div>
<div>Groups</div>
@groupNames.map { groupName =>
<a href="@url(groupName)">@avatar(groupName, 36, tooltip = true)</a>
@avatarLink(groupName, 36, tooltip = true)
}
</div>
}

View File

@@ -3,7 +3,7 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
@import context._
@import gitbucket.core.view.helpers._
@html.main("Create a New Repository"){
<div style="width: 600px; margin: 10px auto;">
<div class="body" style="width: 600px; margin: 10px auto;">
<h2>Create a new repository</h2>
<p class="muted">
A repository contains all the files for your project, including the revision history.

View File

@@ -2,7 +2,7 @@
@import context._
@import gitbucket.core.view.helpers._
@html.main("Create your account"){
<div class="container">
<div class="container body">
<h3>Create your account</h3>
<form action="@path/register" method="POST" validate="true">
<div class="row">

View File

@@ -3,7 +3,7 @@
@import context._
@import gitbucket.core.view.helpers._
@html.main("SSH Keys"){
<div class="container">
<div class="container body">
<div class="row">
<div class="col-md-3">
@menu("ssh", settings.ssh)
@@ -17,10 +17,10 @@
}
@sshKeys.zipWithIndex.map { case (key, i) =>
@if(i != 0){
<hr>
<hr style="margin-top: 10px;">
}
<strong>@key.title</strong> (@SshUtil.fingerPrint(key.publicKey).getOrElse("Key is invalid."))
<a href="@path/@account.userName/_ssh/delete/@key.sshKeyId" class="btn btn-mini btn-danger pull-right">Delete</a>
<a href="@path/@account.userName/_ssh/delete/@key.sshKeyId" class="btn btn-sm btn-danger pull-right">Delete</a>
}
</div>
</div>
@@ -31,12 +31,12 @@
<fieldset class="form-group">
<label for="title" class="strong">Title</label>
<div><span id="error-title" class="error"></span></div>
<input type="text" name="title" id="title" class="form-control" style="width: 400px;"/>
<input type="text" name="title" id="title" class="form-control"/>
</fieldset>
<fieldset class="form-group">
<label for="publicKey" class="strong">Key</label>
<div><span id="error-publicKey" class="error"></span></div>
<textarea name="publicKey" id="publicKey" class="form-control" style="width: 600px; height: 250px;"></textarea>
<textarea name="publicKey" id="publicKey" class="form-control" style="height: 250px;"></textarea>
</fieldset>
<input type="submit" class="btn btn-success" value="Add"/>
</div>

View File

@@ -11,7 +11,7 @@
<a href="@path/admin/system">System Settings</a>
</li>
<li@if(active=="plugins"){ class="active"}>
<a href="@path/admin/plugins">Plugins</a>
<a href="@path/admin/plugins">Plugins</a>
</li>
<li>
<a href="@path/console/login.jsp">H2 Console</a>

View File

@@ -111,15 +111,24 @@
Enable SSH access to git repository
</label>
</fieldset>
<div class="form-group ssh">
<label class="control-label col-md-3" for="sshPort">SSH Port</label>
<div class="col-md-9">
<input type="text" id="sshPort" name="sshPort" class="form-control" value="@settings.sshPort"/>
<span id="error-sshPort" class="error"></span>
<div class="ssh">
<div class="form-group">
<label class="control-label col-md-3" for="sshHost">SSH Host</label>
<div class="col-md-9">
<input type="text" id="sshHost" name="sshHost" class="form-control" value="@settings.sshHost"/>
<span id="error-sshHost" class="error"></span>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3" for="sshPort">SSH Port</label>
<div class="col-md-9">
<input type="text" id="sshPort" name="sshPort" class="form-control" value="@settings.sshPort"/>
<span id="error-sshPort" class="error"></span>
</div>
</div>
</div>
<p class="muted">
Base URL is required if SSH access is enabled.
Both of SSH host and Base URL are required if SSH access is enabled.
</p>
<!--====================================================================-->
<!-- Authentication -->

View File

@@ -8,9 +8,11 @@
@import context._
@import gitbucket.core.view.helpers._
@html.main("Issues"){
@dashboard.html.tab("issues")
<div class="container">
@issuesnavi(filter, "issues", condition)
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
<div class="body">
@dashboard.html.tab("issues")
<div class="container">
@issuesnavi(filter, "issues", condition)
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
</div>
</div>
}

View File

@@ -8,9 +8,11 @@
@import context._
@import gitbucket.core.view.helpers._
@html.main("Pull Requests"){
@dashboard.html.tab("pulls")
<div class="container">
@issuesnavi(filter, "pulls", condition)
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
<div class="body">
@dashboard.html.tab("pulls")
<div class="container">
@issuesnavi(filter, "pulls", condition)
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
</div>
</div>
}

View File

@@ -27,12 +27,6 @@ $(function(){
throw e;
}
}
// Adjust clickable area width
$('#@textareaId').next('div.clickable').css({
'width': ($('#@textareaId').width() + 18) + 'px',
'font-size': '13px'
});
});
</script>
}

View File

@@ -10,8 +10,8 @@
@if(comment.fileName.isDefined){filename="@comment.fileName.get"}
@if(comment.newLine.isDefined){newline="@comment.newLine.get"}
@if(comment.oldLine.isDefined){oldline="@comment.oldLine.get"}>
<div class="issue-avatar-image">@avatar(comment.commentedUserName, 48)</div>
<div class="panel- panel-default commit-comment-box commit-comment-@comment.commentId">
<div class="issue-avatar-image">@avatarLink(comment.commentedUserName, 48)</div>
<div class="panel panel-default commit-comment-box commit-comment-@comment.commentId">
<div class="panel-heading">
@user(comment.commentedUserName, styleClass="username strong")
<span class="muted">

View File

@@ -1,8 +1,16 @@
@(id: String, value: String, prepend: Boolean = false)(html: Html)
<div class="input-group @if(prepend){input-prepend}" style="margin-bottom: 0px;">
@html
<span class="input-group-btn"><span id="@id" class="btn btn-sm btn-default" data-clipboard-text="@value" data-placement="bottom" title="copy to clipboard"><i class="octicon octicon-clippy"></i></span></span>
</div>
@(id: String, value: String, style: String = "")(html: Html = Html(""))
@if(html.body.nonEmpty){
<div class="input-group" style="margin-bottom: 0px;">
@html
<span class="input-group-btn">
<span id="@id" class="btn btn-sm btn-default" @if(style.nonEmpty){style="@style"}
data-clipboard-text="@value" data-placement="bottom" title="copy to clipboard"><i class="octicon octicon-clippy"></i></span>
</span>
</div>
} else {
<span id="@id" class="btn btn-sm btn-default" @if(style.nonEmpty){style="@style"}
data-clipboard-text="@value" data-placement="bottom" title="copy to clipboard"><i class="octicon octicon-clippy"></i></span>
}
<script>
// copy to clipboard
(function() {

View File

@@ -4,79 +4,81 @@
@import context._
@import gitbucket.core.view.helpers._
@main("GitBucket"){
@dashboard.html.tab()
<div class="container">
<div class="row">
<div class="col-md-8">
<div class="pull-right">
<a href="@path/activities.atom"><img src="@assets/common/images/feed.png" alt="activities"></a>
</div>
@helper.html.activities(activities)
</div>
<div class="col-md-4">
@settings.information.map { information =>
<div class="alert alert-info" style="background-color: white; color: #555; border-color: #4183c4; font-size: small; line-height: 120%;">
<button type="button" class="close" data-dismiss="alert">&times;</button>
@Html(information)
<div class="body">
@dashboard.html.tab()
<div class="container">
<div class="row">
<div class="col-md-8">
<div class="pull-right">
<a href="@path/activities.atom"><img src="@assets/common/images/feed.png" alt="activities"></a>
</div>
}
@if(loginAccount.isEmpty){
@signinform(settings)
} else {
<div class="panel panel-default">
<div class="panel-heading strong">
<div class="pull-right">
<a href="@path/new" class="btn btn-success btn-sm">New repository</a>
</div>
Your repositories <span class="badge">@userRepositories.size</span>
@helper.html.activities(activities)
</div>
<div class="col-md-4">
@settings.information.map { information =>
<div class="alert alert-info" style="background-color: white; color: #555; border-color: #4183c4; font-size: small; line-height: 120%;">
<button type="button" class="close" data-dismiss="alert">&times;</button>
@Html(information)
</div>
<ul class="list-group list-group-flush">
@if(userRepositories.isEmpty){
<li class="list-group-item">No repositories</li>
} else {
@defining(20){ max =>
@userRepositories.zipWithIndex.map { case (repository, i) =>
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
@helper.html.repositoryicon(repository, false)
@if(repository.owner == loginAccount.get.userName){
<a href="@url(repository)"><span class="strong">@repository.name</span></a>
} else {
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
}
</li>
}
@if(userRepositories.size > max){
<li class="list-group-item show-more">
<a href="javascript:void(0);" id="show-more-repos">Show @{userRepositories.size - max} more repositories...</a>
</li>
}
@if(loginAccount.isEmpty){
<div id="dashboard-signin-form">@signinform(settings)</div>
} else {
<div class="panel panel-default">
<div class="panel-heading strong">
<div class="pull-right">
<a href="@path/new" class="btn btn-success btn-sm">New repository</a>
</div>
Your repositories <span class="badge">@userRepositories.size</span>
</div>
<ul class="list-group list-group-flush">
@if(userRepositories.isEmpty){
<li class="list-group-item">No repositories</li>
} else {
@defining(20){ max =>
@userRepositories.zipWithIndex.map { case (repository, i) =>
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
@helper.html.repositoryicon(repository, false)
@if(repository.owner == loginAccount.get.userName){
<a href="@url(repository)"><span class="strong">@repository.name</span></a>
} else {
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
}
</li>
}
@if(userRepositories.size > max){
<li class="list-group-item show-more">
<a href="javascript:void(0);" id="show-more-repos">Show @{userRepositories.size - max} more repositories...</a>
</li>
}
}
}
}
</ul>
</div>
}
<div class="panel panel-default">
<div class="panel-heading strong">Recent updated repositories</div>
<ul class="list-group list-group-flush">
@if(recentRepositories.isEmpty){
<li class="list-group-item">No repositories</li>
} else {
@defining(20){ max =>
@recentRepositories.zipWithIndex.map { case (repository, i) =>
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
@helper.html.repositoryicon(repository, false)
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
</li>
}
@if(recentRepositories.size > max){
<li class="list-group-item show-more">
<a href="javascript:void(0);" id="show-more-recent-repos">Show @{recentRepositories.size - max} more repositories...</a>
</li>
</ul>
</div>
}
<div class="panel panel-default">
<div class="panel-heading strong">Recent updated repositories</div>
<ul class="list-group list-group-flush">
@if(recentRepositories.isEmpty){
<li class="list-group-item">No repositories</li>
} else {
@defining(20){ max =>
@recentRepositories.zipWithIndex.map { case (repository, i) =>
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
@helper.html.repositoryicon(repository, false)
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
</li>
}
@if(recentRepositories.size > max){
<li class="list-group-item show-more">
<a href="javascript:void(0);" id="show-more-recent-repos">Show @{recentRepositories.size - max} more repositories...</a>
</li>
}
}
}
}
</ul>
</ul>
</div>
</div>
</div>
</div>

View File

@@ -7,7 +7,7 @@
@if(loginAccount.isDefined){
<hr/><br/>
<form method="POST" validate="true">
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="issue-avatar-image">@avatarLink(loginAccount.get.userName, 48)</div>
<div class="panel panel-default issue-comment-box">
<div class="panel-body">
@helper.html.preview(

View File

@@ -7,7 +7,7 @@
@import gitbucket.core.view.helpers._
@import gitbucket.core.model.CommitComment
@if(issue.isDefined){
<div class="issue-avatar-image">@avatar(issue.get.openedUserName, 48)</div>
<div class="issue-avatar-image">@avatarLink(issue.get.openedUserName, 48)</div>
<div class="panel panel-default issue-comment-box">
<div class="panel-heading">
@user(issue.get.openedUserName, styleClass="username strong") <span class="muted">commented @helper.html.datetimeago(issue.get.registeredDate)</span>
@@ -36,7 +36,7 @@
case comment: gitbucket.core.model.IssueComment => {
@if(comment.action != "close" && comment.action != "reopen" && comment.action != "delete_branch"
&& comment.action != "commit" && comment.action != "refer"){
<div class="issue-avatar-image">@avatar(comment.commentedUserName, 48)</div>
<div class="issue-avatar-image">@avatarLink(comment.commentedUserName, 48)</div>
<div class="panel panel-default issue-comment-box" id="comment-@comment.commentId">
<div class="panel-heading">
@user(comment.commentedUserName, styleClass="username strong")
@@ -210,7 +210,7 @@ $(function(){
$(document).on('click', '.commit-comment-box i.octicon-pencil', function(){
var id = $(this).closest('a').data('comment-id');
var url = '@url(repository)/commit_comments/_data/' + id;
var $content = $('.commit-commentContent-' + id, $(this).closest('.box'));
var $content = $('.commit-commentContent-' + id, $(this).closest('.commit-comment-box'));
$.get(url,
{

View File

@@ -10,11 +10,11 @@
<form action="@url(repository)/issues/new" method="POST" validate="true" class="form-group">
<div class="row">
<div class="col-md-10">
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="issue-avatar-image">@avatarLink(loginAccount.get.userName, 48)</div>
<div class="panel panel-default issue-box">
<div class="panel-body">
<span id="error-title" class="error"></span>
<input type="text" name="title" class="form-control input-lg" value="" placeholder="Title" style="width: 680px;" autofocus/>
<input type="text" id="issue-title" name="title" class="form-control input-lg" value="" placeholder="Title" autofocus/>
@helper.html.preview(
repository = repository,
content = "",
@@ -23,7 +23,7 @@
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission,
style = "width: 680px; height: 200px; max-height: 250px;",
style = "height: 200px; max-height: 250px;",
elastic = true
)
<div class="align-right">

View File

@@ -49,7 +49,8 @@
<div class="small" style="padding-left: 20px;">
@milestone.dueDate.map { dueDate =>
@if(isPast(dueDate)){
<i class="octicon octicon-alert" style="color:#BD2C00;"></i><span class="milestone-alert">Due by @date(dueDate)</span>
<i class="octicon octicon-alert" style="color:#BD2C00;"></i>
<span class="milestone-alert">Due by @date(dueDate)</span>
} else {
<span class="muted">Due by @date(dueDate)</span>
}

View File

@@ -11,7 +11,7 @@
<span class="input-group-addon"><i style="background-color: #@label.map(_.color).getOrElse("888888");"></i></span>
</div>
<script>
$('div#label-color-@labelId').colorpicker();
$('div#label-color-@labelId').colorpicker({format: "hex"});
</script>
<span class="pull-right">
<span id="label-error-@labelId" class="error"></span>

View File

@@ -41,7 +41,8 @@
} else {
@milestone.dueDate.map { dueDate =>
@if(isPast(dueDate)){
<i class="octicon octicon-alert" style="color:#BD2C00;"></i><span class="muted milestone-alert">Due by @date(dueDate)</span>
<i class="octicon octicon-alert" style="color:#BD2C00;"></i>
<span class="muted milestone-alert">Due by @date(dueDate)</span>
} else {
<span class="muted">Due by @date(dueDate)</span>
}

View File

@@ -1,30 +1,28 @@
@(active: String,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
id: Option[String] = None,
expand: Boolean = false,
isRepoTop: Boolean = false,
isNoGroup: Boolean = true,
info: Option[Any] = None,
error: Option[Any] = None)(body: Html)(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.service.SystemSettingsService
@import context._
@import gitbucket.core.view.helpers._
@sidemenu(path: String, name: String, icon: String, label: String, count: Int = 0) = {
<li @if(active == name){class="active"} @if(!expand){data-toggle="tooltip" data-placement="left" data-original-title="@label"}>
@menuitem(path: String, name: String, icon: String, label: String, count: Int = 0) = {
<li @if(active == name){class="active"}>
<a href="@url(repository)@path">
<i class="menu-icon @if(active == name){menu-icon-active} octicon octicon-@{icon} "></i>
@if(expand){ @label}
@if(expand && count > 0){
<div class="pull-right"><span class="badge">@count</span></div>
<i class="menu-icon @if(active == name){menu-icon-active} octicon octicon-@{icon} "></i> <span class="pc">@label</span>
@if(count > 0){
<span class="badge">@count</span>
}
</a>
</li>
}
<div class="container">
@helper.html.information(info)
@helper.html.error(error)
<div class="row">
<div class="headbar">
<div class="container">
@helper.html.information(info)
@helper.html.error(error)
<div class="head">
@if(repository.commitCount > 0){
<div class="input-group pull-right">
@@ -60,87 +58,52 @@
}
}
</div>
<ul class="headmenu">
@menuitem("" , "code" , "code" , "Code")
@menuitem("/issues", "issues" , "issue-opened" , "Issues", repository.issueCount)
@menuitem("/pulls" , "pulls" , "git-pull-request" , "Pull Requests", repository.pullCount)
@menuitem("/wiki" , "wiki" , "book" , "Wiki")
@if(loginAccount.isDefined && (loginAccount.get.isAdmin || repository.managers.contains(loginAccount.get.userName))){
@menuitem("/settings" , "settings" , "tools", "Settings")
}
</ul>
</div>
</div>
<hr style="margin-bottom: 20px;"/>
<div class="container body">
<div style="width: @if(expand){170px} else {40px};" class="pull-right">
<ul class="sidemenu">
<li style="height: 12px"><div class="gradient pull-left" style="height: 12px"></div></li>
@sidemenu("" , "code" , "code" , "Code")
@sidemenu("/issues", "issues" , "issue-opened" , "Issues", repository.issueCount)
@sidemenu("/pulls" , "pulls" , "git-pull-request" , "Pull Requests", repository.pullCount)
@sidemenu("/wiki" , "wiki" , "book" , "Wiki")
@if(loginAccount.isDefined && (loginAccount.get.isAdmin || repository.managers.contains(loginAccount.get.userName))){
@sidemenu("/settings" , "settings" , "tools", "Settings")
}
<li style="height: 12px"><div class="gradient pull-left" style="height: 12px"></div></li>
</ul>
@if(expand){
<div class="small">
<strong id="repository-url-proto">HTTP</strong> <span class="mute">clone URL</span>
</div>
@helper.html.copy("repository-url-copy", repository.httpUrl){
<input type="text" value="@repository.httpUrl" id="repository-url" class="form-control input-sm" readonly>
}
@if(settings.ssh && loginAccount.isDefined){
<div class="small">
<span class="mute">You can clone <a href="javascript:void(0);" id="repository-url-http">HTTP</a> or <a href="javascript:void(0);" id="repository-url-ssh">SSH</a>.</span>
</div>
}
@id.map { id =>
@if(context.platform != "linux" && context.platform != null){
<div style="margin-top: 10px;">
<a href="@repository.httpOpenRepoUrl(context.platform)" id="repository-clone-url" class="btn btn-sm btn-default btn-block"><i class="octicon octicon-desktop-download"></i>&nbsp;&nbsp;Clone in Desktop</a>
</div>
}
<div style="margin-top: 10px;">
<a href="@{url(repository)}/archive/@{encodeRefName(id)}.zip" class="btn btn-sm btn-default btn-block"><i class="octicon octicon-cloud-download"></i>Download ZIP</a>
</div>
@*
<div style="margin-top: 10px;">
<a href="@{url(repository)}/archive/@{encodeRefName(id)}.tar.gz" class="btn btn-sm btn-default btn-block "><i class="octicon octicon-cloud-download"></i>Download TAR.GZ</a>
</div>
*@
}
@if(isRepoTop){
@repository.repository.description.map { description =>
<p class="description">@detectAndRenderLinks(description)</p>
}
</div>
<div class="pull-left" style="width: @if(expand){770px} else {895px};">
@if(expand){
@repository.repository.description.map { description =>
<p class="description">@detectAndRenderLinks(description)</p>
}
<div style="margin-bottom: 10px; padding: 4px;" class="panel panel-default">
<table class="fill-width">
<tr>
<td style="width: 33%; text-align: center;">
<a href="@url(repository)/commits/@encodeRefName(id.getOrElse(""))" class="header-link">
<i class="octicon octicon-history"></i>
@if(repository.commitCount > 10000){
<strong>10000+</strong> commits
} else {
<strong>@repository.commitCount</strong> commits
}
</a>
</td>
<td style="width: 33%; text-align: center;">
<a href="@url(repository)/branches" class="header-link" class="header-link">
<i class="octicon octicon-git-branch"></i>
<strong>@repository.branchList.length</strong> branches
</a>
</td>
<td style="width: 33%; text-align: center;">
<a href="@url(repository)/tags" class="header-link" class="header-link">
<i class="octicon octicon-tag"></i>
<strong>@repository.tags.length</strong> releases
</a>
</td>
</tr>
</table>
</div>
}
@body
</div>
<div style="margin-bottom: 10px; padding: 4px;" class="panel panel-default">
<table class="fill-width">
<tr>
<td style="width: 33%; text-align: center;">
<a href="@url(repository)/commits/@encodeRefName(id.getOrElse(""))" class="header-link">
<i class="octicon octicon-history"></i>
@if(repository.commitCount > 10000){
<strong>10000+</strong> commits
} else {
<strong>@repository.commitCount</strong> commits
}
</a>
</td>
<td style="width: 33%; text-align: center;">
<a href="@url(repository)/branches" class="header-link" class="header-link">
<i class="octicon octicon-git-branch"></i>
<strong>@repository.branchList.length</strong> branches
</a>
</td>
<td style="width: 33%; text-align: center;">
<a href="@url(repository)/tags" class="header-link" class="header-link">
<i class="octicon octicon-tag"></i>
<strong>@repository.tags.length</strong> releases
</a>
</td>
</tr>
</table>
</div>
}
@body
</div>
<script>
$(function(){
@@ -150,7 +113,7 @@ $(function(){
if(e.target.tagName == "I"){
target = e.target.parentElement;
}
$(target).prev ('div.gradient' ).css('border-left', '1px solid silver');
$(target).prev('div.gradient').css('border-left', '1px solid silver');
});
$('ul.sidemenu a').mouseout(function(e){
@@ -158,7 +121,7 @@ $(function(){
if(e.target.tagName == "I"){
target = e.target.parentElement;
}
$(target).prev ('div.gradient' ).css('border-left', '1px solid #eee');
$(target).prev('div.gradient').css('border-left', '1px solid #eee');
});
$('a[rel*=facebox]').facebox({
@@ -180,21 +143,5 @@ $(function(){
$('#fork-form').submit();
});
}
@if(settings.ssh && loginAccount.isDefined){
$('#repository-url-http').click(function(){
$('#repository-url-proto').text('HTTP');
$('#repository-url').val('@repository.httpUrl');
$('#repository-clone-url').attr('href', '@repository.httpOpenRepoUrl(context.platform)')
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
});
$('#repository-url-ssh').click(function(){
$('#repository-url-proto').text('SSH');
$('#repository-url').val('@repository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)');
$('#repository-clone-url').attr('href', '@repository.sshOpenRepoUrl(context.platform, settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)');
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
});
}
});
</script>

View File

@@ -17,7 +17,7 @@
<li><a href="@url(repository)/tree/@commit.id" style="line-height: 16px;"><i class="octicon octicon-code link"></i></a></li>
</ul>
<div>
<div class="commit-avatar-image">@avatar(commit, 40)</div>
<div class="commit-avatar-image">@avatarLink(commit, 40)</div>
<div>
<a href="@url(repository)/commit/@commit.id" class="commit-message" style="font-weight: bold;">@link(commit.summary, repository)</a>
@if(commit.description.isDefined){

View File

@@ -55,11 +55,11 @@
<form method="POST" action="@path/@originRepository.owner/@originRepository.name/pulls/new" validate="true">
<div class="row">
<div class="col-md-10">
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="issue-avatar-image">@avatarLink(loginAccount.get.userName, 48)</div>
<div class="panel panel-default issue-box">
<div class="panel-body">
<span class="error" id="error-title"></span>
<input type="text" name="title" class="form-control input-lg" style="width: 680px" placeholder="Title"/>
<input type="text" name="title" class="form-control input-lg" placeholder="Title"/>
@helper.html.preview(
repository = repository,
content = "",
@@ -68,7 +68,7 @@
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true,
style = "width: 680px; height: 200px;"
style = "height: 200px;"
)
<input type="hidden" name="targetUserName" value="@originRepository.owner"/>
<input type="hidden" name="targetBranch" value="@originId"/>

View File

@@ -83,8 +83,8 @@
</div>
@if(status.hasMergePermission){
<div style="padding:15px;border-top:solid 1px #e5e5e5;background:#fafafa">
<input type="button" class="btn @if(!status.hasProblem){ btn-success }" id="merge-pull-request-button" value="Merge pull request"@if(!status.canMerge){ disabled="true"}/>
You can also merge branches on the <a href="#" class="show-command-line">command line</a>.
<input type="button" class="btn btn-lg @if(!status.hasProblem){btn-success} else {btn-default}" id="merge-pull-request-button" value="Merge pull request"@if(!status.canMerge){ disabled="true"}/>
&nbsp;&nbsp;You can also merge branches on the <a href="#" class="show-command-line">command line</a>.
<div id="command-line" style="display: none;margin-top: 15px;">
<hr />
@if(status.hasConflict){
@@ -100,24 +100,23 @@
you can perform a manual merge on the command line.
</p>
}
@helper.html.copy("repository-url-copy", forkedRepository.httpUrl, true){
<div class="btn-group" data-toggle="buttons-radio">
<button class="btn btn-small active" type="button" id="repository-url-http">HTTP</button>
@helper.html.copy("repository-url-copy", forkedRepository.httpUrl){
<div class="input-group-btn" data-toggle="buttons">
<label class="btn btn-sm btn-default active" id="repository-url-http"><input type="radio" checked>HTTP</label>
@if(settings.ssh && loginAccount.isDefined){
<button class="btn btn-small" type="button" id="repository-url-ssh" style="border-radius: 0px;">SSH</button>
<label class="btn btn-sm btn-default" id="repository-url-ssh"><input type="radio">SSH</label>
}
</div>
<input type="text" style="width: 500px;" value="@forkedRepository.httpUrl" id="repository-url" readonly />
<input type="text" class="form-control input-sm" value="@forkedRepository.httpUrl" id="repository-url" readonly />
}
<div>
<div style="margin-top: 10px;">
<p>
<span class="strong">Step 1:</span> From your project repository, check out a new branch and test the changes.
</p>
@defining(s"git checkout -b ${pullreq.requestUserName}-${pullreq.requestBranch} ${pullreq.branch}\n" +
s"git pull ${forkedRepository.httpUrl} ${pullreq.requestBranch}"){ command =>
@helper.html.copy("merge-command-copy-1", command){
<pre style="width: 600px; float: left; font-size: 12px; border-radius: 3px 0px 3px 3px;" id="merge-command">@Html(command)</pre>
}
@helper.html.copy("merge-command-copy-1", command, "position: absolute; right: 31px;")()
<pre style="font-size: 12px; border-radius: 3px;" id="merge-command">@Html(command)</pre>
}
</div>
<div>
@@ -126,9 +125,8 @@
</p>
@defining(s"git checkout ${pullreq.branch}\ngit merge --no-ff ${pullreq.requestUserName}-${pullreq.requestBranch}\n" +
s"git push origin ${pullreq.branch}"){ command =>
@helper.html.copy("merge-command-copy-2", command){
<pre style="width: 600px; float: left; font-size: 12px; border-radius: 3px 0px 3px 3px;">@command</pre>
}
@helper.html.copy("merge-command-copy-2", command, "position: absolute; right: 31px;")()
<pre style="font-size: 12px; border-radius: 3px;">@command</pre>
}
</div>
</div>
@@ -141,7 +139,7 @@
Merge pull request #@issue.issueId from @{pullreq.requestUserName}/@{pullreq.requestBranch}
</div>
<span id="error-message" class="error"></span>
<textarea name="message" style="width: 635px; height: 80px;">@issue.title</textarea>
<textarea name="message" style="height: 80px;" class="form-control">@issue.title</textarea>
<div>
<input type="button" class="btn" value="Cancel" id="cancel-merge-pull-request"/>
<input type="submit" class="btn btn-success" value="Confirm merge"/>
@@ -171,27 +169,25 @@ $(function(){
$('#confirm-merge-form').show();
});
@if(settings.ssh && loginAccount.isDefined){
$('#repository-url-http').click(function(){
@forkedRepository.sshUrl.map { sshUrl =>
$('#repository-url-http').click(function(e){
// Update URL box
$('#repository-url').val('@forkedRepository.httpUrl');
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
// Update command guidance
$('#merge-command').text($('#merge-command').text().replace(
'@forkedRepository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)',
'@forkedRepository.httpUrl'
'@sshUrl', '@forkedRepository.httpUrl'
));
$('#merge-command-copy-1').attr('data-clipboard-text', $('#merge-command').text());
});
$('#repository-url-ssh').click(function(){
$('#repository-url-ssh').click(function(e){
// Update URL box
$('#repository-url').val('@forkedRepository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)');
$('#repository-url').val('@sshUrl');
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
// Update command guidance
$('#merge-command').text($('#merge-command').text().replace(
'@forkedRepository.httpUrl',
'@forkedRepository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)'
'@forkedRepository.httpUrl', '@sshUrl'
));
$('#merge-command-copy-1').attr('data-clipboard-text', $('#merge-command').text());
});

View File

@@ -21,7 +21,9 @@
@if(hasWritePermission || loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
<a class="btn" href="#" id="edit">Edit</a>
}
<a class="btn btn-success" href="@url(repository)/issues/new">New issue</a>
@if(loginAccount.isDefined){
<a class="btn btn-success" href="@url(repository)/compare">New pull request</a>
}
</div>
<div class="edit-title pull-right" style="display: none;">
<a class="btn" href="#" id="update">Save</a> <a href="#" id="cancel">Cancel</a>
@@ -65,7 +67,7 @@
}
</div>
<ul class="nav nav-tabs fill-width pull-left" id="pullreq-tab">
<li class="active"><a href="#conversation">Conversation <span class="badge">@comments.flatMap @{
<li><a href="#conversation">Conversation <span class="badge">@comments.flatMap @{
case comment: IssueComment => Some(comment)
case _: CommitComment => None
}.size</span></a></li>
@@ -73,7 +75,7 @@
<li><a href="#files">Files Changed <span class="badge">@diffs.size</span></a></li>
</ul>
<div class="tab-content fill-width pull-left">
<div class="tab-pane active" id="conversation">
<div class="tab-pane" id="conversation">
@flash.get("error").map{ error =>
<div class="alert alert-error">@error</div>
}
@@ -94,6 +96,22 @@
}
<script>
$(function(){
// Determine active tab from hash
if(location.hash == '#commits'){
$('li:has(a[href=#commits])').addClass('active');
$('div#commits').addClass('active');
} else if(location.hash == '#files'){
$('li:has(a[href=#files])').addClass('active');
$('div#files').addClass('active');
} else {
$('li:has(a[href=#conversation])').addClass('active');
$('div#conversation').addClass('active');
}
// Set hash when tab is clicked
$('ul.nav-tabs li a').click(function(e){
location.href = $(e.delegateTarget).attr("href");
});
$('#pullreq-tab a').click(function (e) {
e.preventDefault();
$(this).tab('show');

View File

@@ -11,7 +11,7 @@
@html.menu("code", repository){
<div class="head">
<div class="pull-right hide-if-blame"><div class="btn-group">
<a href="@url(repository)/find/@encodeRefName(branch)" class="btn btn-sm btn-default" data-toggle="tooltip" data-placement="bottom" data-hotkey="t" title="Quickly jump between files"><i class="octicon octicon-list-unordered"></i></a>
<a href="@url(repository)/find/@encodeRefName(branch)" class="btn btn-sm btn-default" data-hotkey="t">Find file</a>
</div></div>
<div class="line-age-legend">
<span>Newer</span>

View File

@@ -11,7 +11,7 @@
@if(!fileName.isDefined){<hr/><br/>}
<form method="POST" validate="true" style="max-width: 874px;">
@if(!fileName.isDefined){
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="issue-avatar-image">@avatarLink(loginAccount.get.userName, 48)</div>
}
<div class="panel panel-default issue-comment-box">
<div class="panel-body">
@@ -23,7 +23,7 @@
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission,
style = "width: 635px; height: 100px; max-height: 150px;",
style = "height: 100px; max-height: 150px;",
elastic = true
)
</div>

View File

@@ -46,7 +46,7 @@
<li><a href="@url(repository)/tree/@commit.id" style="line-height: 16px;"><i class="octicon octicon-code link"></i></a></li>
</ul>
<div>
<div class="commit-avatar-image">@avatar(commit, 40)</div>
<div class="commit-avatar-image">@avatarLink(commit, 40)</div>
<div>
<a href="@url(repository)/commit/@commit.id" class="commit-message" style="font-weight: bold;">@link(commit.summary, repository)</a>
@if(commit.description.isDefined){

View File

@@ -33,7 +33,7 @@
</td>
</tr>
</table>
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="issue-avatar-image">@avatarLink(loginAccount.get.userName, 48)</div>
<div class="box issue-comment-box">
<div class="box-content">
<div>

View File

@@ -2,11 +2,11 @@
@import context._
<span class="error-edit-content-@commentId error"></span>
@helper.html.attached(owner, repository){
<textarea style="width: 635px; height: 100px;" id="edit-content-@commentId">@content</textarea>
<textarea style="height: 100px;" id="edit-content-@commentId" class="form-control">@content</textarea>
}
<div>
<input type="button" class="cancel-comment-@commentId btn btn-small btn-danger" value="Cancel"/>
<input type="button" class="update-comment-@commentId btn btn-small pull-right" value="Update comment"/>
<input type="button" class="update-comment-@commentId btn btn-small btn-default pull-right" value="Update comment"/>
</div>
<script>
$(function(){
@@ -19,7 +19,7 @@ $(function(){
}
$(document).on('click', '.update-comment-@commentId', function(){
$box = $(this).closest('.box');
$box = $(this).closest('.commit-comment-box');
$('.update-comment-@commentId, .cancel-comment-@commentId', $box).attr('disabled', 'disabled');
$.ajax({
url: '@path/@owner/@repository/commit_comments/edit/@commentId',

View File

@@ -47,7 +47,7 @@
</td>
</tr>
</table>
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="issue-avatar-image">@avatarLink(loginAccount.get.userName, 48)</div>
<div class="panel panel-default issue-comment-box">
<div class="panel-body">
<div>

View File

@@ -11,6 +11,7 @@
error: Option[Any] = None)(implicit context: gitbucket.core.controller.Context)
@import context._
@import gitbucket.core.view.helpers._
@import gitbucket.core.service.RepositoryService._
@html.main(
if(pathList.isEmpty){
if(branch == repository.repository.defaultBranch){
@@ -23,32 +24,68 @@
}, Some(repository)) {
@html.menu("code", repository, Some(branch), pathList.isEmpty, groupNames.isEmpty, info, error){
<div class="head">
<div class="pull-right"><div class="btn-group">
<a href="@url(repository)/find/@encodeRefName(branch)" class="btn btn-sm btn-default" data-toggle="tooltip" data-placement="bottom" data-hotkey="t" title="Quickly jump between files"><i class="octicon octicon-list-unordered"></i></a>
@if(pathList.nonEmpty){
<a href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default" data-toggle="tooltip" data-placement="bottom" title="Browse commits for this branch"><i class="octicon octicon-clock"></i></a>
}
</div></div>
@branchPullRequest.map{ case (pullRequest, issue) =>
<a href="@url(repository)/pull/@pullRequest.issueId" class="btn btn-sm btn-pullrequest-branch" title="@issue.title" data-toggle="tooltip">#@pullRequest.issueId</a>
}.getOrElse{
<a href="@url(repository)/compare?head=@urlEncode(encodeRefName(branch))" class="btn btn-sm btn-success"><i class="octicon octicon-git-compare" data-toggle="tooltip" title="Compare, review, create a pull request"></i></a>
@if(pathList.isEmpty){
<div class="pull-right pc">
@if(platform != "linux" && platform != null){
<a href="@openRepoUrl(repository.httpUrl)" id="repository-clone-url" class="btn btn-sm btn-default"><i class="octicon octicon-desktop-download"></i></a>
}
<a href="@{url(repository)}/archive/@{encodeRefName(branch)}.zip" class="btn btn-sm btn-default">Download ZIP</a>
</div>
<div class="pull-right pc">
<div style="width: 240px; margin-top: 2px; margin-right: 5px; margin-left: 5px;">
@helper.html.copy("repository-url-copy", repository.httpUrl){
@if(repository.sshUrl.isDefined){
<div class="btn-group input-group-btn">
<button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span id="repository-url-proto">HTTP</span> <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li>
<a href="javascript:void(0);" id="repository-url-http">
<strong>HTTP (recommended)</strong><br>
Clone with Git using the repository's web address.
</a>
</li>
<li>
<a href="javascript:void(0);" id="repository-url-ssh">
<strong>SSH</strong><br>
Clone with an SSH key and passphrase from your GitBucket settings.
</a>
</li>
</ul>
</div>
}
<input type="text" value="@repository.httpUrl" id="repository-url" class="form-control input-sm" readonly>
}
</div>
</div>
}
@helper.html.branchcontrol(
branch,
repository,
hasWritePermission
){
<div class="pull-right">
<div class="btn-group">
<a href="@url(repository)/new/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default pc" title="Create a new file here" @if(!hasWritePermission){disabled}>New file</i></a>
<a href="@url(repository)/find/@encodeRefName(branch)" class="btn btn-sm btn-default pc" data-hotkey="t">Find file</a>
@if(pathList.nonEmpty){
<a href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default" data-toggle="tooltip" data-placement="bottom" title="Browse commits for this branch">History</a>
}
</div>
</div>
@helper.html.branchcontrol(branch, repository, hasWritePermission){
@repository.branchList.map { x =>
<li><a href="@url(repository)/tree/@encodeRefName(x)">@helper.html.checkicon(x == branch) @x</a></li>
}
}
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
@pathList.zipWithIndex.map { case (section, i) =>
<a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
}
@if(hasWritePermission){
<a href="@url(repository)/new/@encodeRefName(branch)/@pathList.mkString("/")" title="Create a new file here" style="text-decoration: none;">+</i></a>
@if(pathList.isEmpty){
@branchPullRequest.map{ case (pullRequest, issue) =>
<a href="@url(repository)/pull/@pullRequest.issueId" class="btn btn-sm btn-pullrequest-branch" title="@issue.title" data-toggle="tooltip">View #@pullRequest.issueId</a>
}.getOrElse {
<a href="@url(repository)/compare?head=@urlEncode(encodeRefName(branch))" class="btn btn-sm btn-success" @if(loginAccount.isEmpty){disabled}>New pull request</a>
}
} else {
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
@pathList.zipWithIndex.map { case (section, i) =>
<a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
}
}
</div>
<table class="table table-file-list">
@@ -141,3 +178,20 @@
}
}
}
<script>
@repository.sshUrl.map { sshUrl =>
$('#repository-url-http').click(function(){
$('#repository-url-proto').text('HTTP');
$('#repository-url').val('@repository.httpUrl');
$('#repository-clone-url').attr('href', '@openRepoUrl(repository.httpUrl)')
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
});
$('#repository-url-ssh').click(function(){
$('#repository-url-proto').text('SSH');
$('#repository-url').val('@sshUrl');
$('#repository-clone-url').attr('href', '@openRepoUrl(sshUrl)');
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
});
}
</script>

View File

@@ -13,7 +13,7 @@
@if(originRepository.isDefined){
@avatar(originRepository.get.owner, 20)
<span@if(repository.owner == originRepository.get.owner){ class="highlight"}>
<a href="@url(originRepository.get)">@originRepository.get.owner</a> / <a href="@path/@originRepository.get.owner/@originRepository.get.name">@originRepository.get.name</a>
<a href="@url(originRepository.get.owner)">@originRepository.get.owner</a> / <a href="@path/@originRepository.get.owner/@originRepository.get.name">@originRepository.get.name</a>
</span>
} else {
@avatar(repository.repository.originUserName.get, 20)

View File

@@ -11,9 +11,9 @@
<h3><strong>Quick setup</strong> — if you've done this kind of thing before</h3>
<div class="empty-repo-options">
via <a href="@repository.httpUrl" class="git-protocol-selector">HTTP</a>
@if(settings.ssh && loginAccount.isDefined){
@repository.sshUrl.map { sshUrl =>
or
<a href="@repository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)" class="git-protocol-selector">SSH</a>
<a href="@sshUrl" class="git-protocol-selector">SSH</a>
}
</div>
<h3 style="margin-top: 30px;">Create a new repository on the command line</h3>

View File

@@ -24,7 +24,8 @@
Opened by <a href="@url(issue.openedUserName)" class="username">@issue.openedUserName</a>
@helper.html.datetimeago(issue.registeredDate)
@if(issue.commentCount > 0){
&nbsp;&nbsp;<i class="octicon octicon-comment"></i><span class="strong">@issue.commentCount</span> @plural(issue.commentCount, "comment")
&nbsp;&nbsp;<i class="octicon octicon-comment"></i>
<span class="strong">@issue.commentCount</span> @plural(issue.commentCount, "comment")
}
</div>
</div>

View File

@@ -21,7 +21,7 @@
</ul>
<form action="@url(repository)/wiki/@if(page.isEmpty){_new} else {_edit}" method="POST" validate="true">
<span id="error-pageName" class="error"></span>
<input type="text" name="pageName" value="@pageName" class="form-control input-lg" style="width: 900px; font-weight: bold;" placeholder="Input a page name."/>
<input type="text" name="pageName" value="@pageName" class="form-control input-lg" style="font-weight: bold;" placeholder="Input a page name."/>
@helper.html.preview(
repository = repository,
content = page.map(_.content).getOrElse(""),
@@ -30,13 +30,13 @@
enableLineBreaks = false,
enableTaskList = false,
hasWritePermission = false,
style = "width: 900px; height: 400px;",
style = "height: 400px;",
styleClass = "monospace",
placeholder = ""
)
<div class="form-group">
<label for="message">Edit Message</label>
<input type="text" id="message" name="message" value="" class="form-control" style="width: 900px;" placeholder="Write a small message here explaining this change. (Optional)"/>
<input type="text" id="message" name="message" value="" class="form-control" placeholder="Write a small message here explaining this change. (Optional)"/>
</div>
<div class="form-group pull-right">
<input type="hidden" name="currentPageName" value="@pageName"/>

View File

@@ -7,7 +7,7 @@
footer: Option[gitbucket.core.service.WikiService.WikiPageInfo])(implicit context: gitbucket.core.controller.Context)
@import context._
@import gitbucket.core.view.helpers._
@import gitbucket.core.service.WikiService._
@import gitbucket.core.service.WikiService.{wikiHttpUrl, wikiSshUrl}
@html.main(s"${pageName} - ${repository.owner}/${repository.name}", Some(repository)){
@html.menu("wiki", repository){
<ul class="nav nav-tabs fill-width">
@@ -26,7 +26,7 @@
}
</li>
</ul>
<div style="width: 200px; margin-top: 20px;" class="pull-right">
<div style="width: 200px; margin-top: 20px;" class="pull-right pc">
@defining(15){ max =>
<div class="panel panel-default">
<div class="panel-heading strong">
@@ -67,10 +67,10 @@
<div class="small">
<strong>Clone this wiki locally</strong>
</div>
@helper.html.copy("repository-url-copy", httpUrl(repository)){
<input type="text" value="@httpUrl(repository)" id="repository-url" class="form-control input-sm" readonly>
@helper.html.copy("repository-url-copy", wikiHttpUrl(repository)){
<input type="text" value="@wikiHttpUrl(repository)" id="repository-url" class="form-control input-sm" readonly>
}
@if(settings.ssh && loginAccount.isDefined){
@if(wikiSshUrl(repository).isDefined) {
<div class="small">
<span class="mute">You can clone <a href="javascript:void(0);" id="repository-url-http">HTTP</a> or <a href="javascript:void(0);" id="repository-url-ssh">SSH</a>.</span>
</div>
@@ -131,13 +131,13 @@ $(function(){
$('#triangle-right').show();
}
@if(settings.ssh && loginAccount.isDefined){
@wikiSshUrl(repository).map { sshUrl =>
$('#repository-url-http').click(function(){
$('#repository-url').val('@httpUrl(repository)');
$('#repository-url').val('@wikiHttpUrl(repository)');
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
});
$('#repository-url-ssh').click(function(){
$('#repository-url').val('@sshUrl(repository, settings, loginAccount.get.userName)');
$('#repository-url').val('@sshUrl');
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
});
}

View File

@@ -58,7 +58,7 @@ h6 {
margin-right: 5px;
}
.head .octicon,.head .mega-octicon{
.head .octicon, .head .mega-octicon{
color : #BBB;
}
@@ -70,57 +70,6 @@ blockquote p {
font-size: 15px;
}
/*
.nav {
margin-bottom: 12px;
}
.table-bordered {
border-collapse: inherit;
border: none;
}
.table-bordered > thead > tr > th,
.table-bordered > tbody > tr > th,
.table-bordered > tbody > tr > td {
border-bottom: none;
}
.table-bordered > thead > tr:first-child > th:nth-of-type(1),
.table-bordered > tbody > tr:first-child > th:nth-of-type(1),
.table-bordered > tbody > tr:first-child > td:nth-of-type(1) {
border-top-left-radius: 4px;
}
.table-bordered > thead > tr:first-child > th:nth-last-of-type(1),
.table-bordered > tbody > tr:first-child > th:nth-last-of-type(1),
.table-bordered > tbody > tr:first-child > td:nth-last-of-type(1) {
border-top-right-radius: 4px;
}
.table-bordered > tbody > tr:last-child > td:nth-of-type(1) {
border-bottom-left-radius: 4px;
}
.table-bordered > tbody > tr:last-child > td:nth-last-of-type(1) {
border-bottom-right-radius: 4px;
}
.table-bordered > tbody > tr:last-child > td {
border-bottom: 1px solid #dddddd;
}
*/
/*
.table-bordered > thead > tr > th,
.table-bordered > thead > tr > td {
border-bottom-width: 1px;
}
*/
/*
.tab-content {
margin-top: 20px;
}
*/
.danger {
color: #900;
}
@@ -167,43 +116,6 @@ pre.reset {
/* ======================================================================== */
/* Global Header */
/* ======================================================================== */
/*
div.navbar-inner {
border-radius: 0px;
-webkit-border-radius: 0px;
-moz-border-radius: 0px;
border-top: none;
border-left: none;
border-right: none;
border-bottom: 1px solid #d4d4d4;
padding-right: 0px;
}
div.header-menu {
line-height: 40px;
}
div.header-menu .octicon{
color: #333;
}
div.header-menu input,
div.header-menu a.btn {
margin-top: 0px;
margin-bottom: 0px;
}
/*
div.nav-collapse a.menu {
margin-right: 12px;
}
div.nav-collapse a.btn-last,
div.nav-collapse a.menu-last {
margin-right: 30px;
}
*/
.navbar-brand {
height: unset;
padding: 8px;
@@ -211,6 +123,7 @@ div.nav-collapse a.menu-last {
.navbar {
min-height: unset;
margin-bottom: 0px;
}
span.header-version {
@@ -277,6 +190,7 @@ div.pagination {
*/
div.body {
margin-top: 20px;
margin-bottom: 40px;
}
@@ -412,27 +326,13 @@ div.box-content {
padding: 4px;
border-radius: 3px;
}
/*
div > div.box-content-row:nth-of-type(1) {
border: none;
}
div.box-content-row {
border-top: 1px solid #d8d8d8;
padding: 4px;
}
*/
/*
div.repo-link {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
*/
li.repo-link, li.page-link {
padding-top: 4px;
padding-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
div.box-content-bottom {
@@ -444,6 +344,10 @@ div.box-content-bottom {
margin-bottom: 20px;
}
div.box-content-bottom img {
max-width: 100%;
}
table.table th.box-header {
background-color: #f5f5f5;
}
@@ -533,61 +437,56 @@ a.btn-danger:hover .octicon {
}
/****************************************************************************/
/* Side Menu */
/* Head Menu */
/****************************************************************************/
ul.sidemenu {
div.headbar {
background-color: #fafafa;
padding-top: 19px;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
ul.headmenu {
margin-top: 20px;
margin-left: 0px;
padding-left: 0px;
margin-bottom: -1px;
}
ul.sidemenu a {
display: block;
padding: 8px 10px;
}
ul.sidemenu a:hover {
ul.headmenu a:hover {
text-decoration: none;
color: black;
}
ul.sidemenu li.active {
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
border-right: 3px solid #bb4444;
border-left: 1px solid white;
ul.headmenu li {
display: inline-block;
list-style-type: none;
font-size: 13px;
}
ul.sidemenu li.active a {
ul.headmenu li a {
color: #666;
padding: 8px 10px;
display: block;
}
ul.headmenu li.active a {
color: black;
}
ul.headmenu li.active {
border-top: 3px solid #bb4444;
border-left: 1px solid #eee;
border-right: 1px solid #eee;
border-bottom: none;
background-color: white;
}
ul.headmenu li.active a {
background-color: #fff;
}
ul.sidemenu {
background-image: -webkit-linear-gradient(left, #f6f6f6 0%, #fff 8px);
background-image: linear-gradient(to right, #f6f6f6 0%, #fff 8px);
box-shadow: inset 1px 0 0 #eee;
}
ul.sidemenu div.margin {
width: 5px;
height: 30px;
margin-right: 4px;
border-left: 1px solid white;
}
ul.sidemenu li {
border-left: 1px solid #eee;
margin-left:0px;
border-right: 2px solid white;
list-style-type: none;
font-size: 90%;
}
ul.sidemenu span.badge {
}
ul.sidemenu a:hover i.menu-icon, ul.sidemenu i.menu-icon-active {
color: #333;
}
/****************************************************************************/
/* Create Repository */
/****************************************************************************/
@@ -636,18 +535,6 @@ a#show-pages-index {
text-decoration: none;
}
/*
ul.nav-stacked.side-menu li span.header {
border-top-right-radius: 3px;
border-top-left-radius: 3px;
border: 1px solid #d8d8d8;
display: block;
padding: 8px 15px 9px;
margin-right: 2px;
background-color: #f5f5f5;
}
*/
ul.nav-stacked.side-menu li a:hover {
background-color: transparent;
}
@@ -750,7 +637,6 @@ div.repository-content {
padding: 0 3px;
}
/****************************************************************************/
/* Activity */
/****************************************************************************/
@@ -978,73 +864,6 @@ span.simplified-path {
color: #0088cc;
}
/****************************************************************************/
/* nav pulls group */
/****************************************************************************/
/*
.nav-pills-group:after {
display: table;
line-height: 0;
content: "";
}
.nav-pills-group:after {
clear: both;
}
.nav-pills-group > li {
float: left;
}
*/
/*
.nav-pills > li + li {
margin-left: 0px;
}
.nav-pills > li > a {
padding-right: 12px;
padding-left: 12px;
line-height: 14px;
color: #666;
font-weight: bold;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.nav-pills > li > a {
padding-top: 10px;
padding-bottom: 10px;
border-left : 1px solid #e5e5e5;
border-top : 1px solid #e5e5e5;
border-bottom : 1px solid #e5e5e5;
}
.nav-pills > li:nth-of-type(1) > a {
-webkit-border-radius: 4px 0 0 4px;
-moz-border-radius: 4px 0 0 4px;
border-radius: 4px 0 0 4px;
}
.nav-pills > li:nth-last-of-type(1) > a {
-webkit-border-radius: 0 4px 4px 0;
-moz-border-radius: 0 4px 4px 0;
border-radius: 0 4px 4px 0;
border-right : 1px solid #e5e5e5;
}
*/
/*
.nav-pills-group > .active > a,
.nav-pills-group > .active > a:hover,
.nav-pills-group > .active > a:focus {
color: #ffffff;
background-color: #0088cc;
border-color: #0088cc;
}
*/
/****************************************************************************/
/* Issues */
/****************************************************************************/
@@ -1081,11 +900,11 @@ table.table-issues {
margin-top: 12px;
}
table.table-issues td .octicon-issue-opened,table.table-issues td .octicon-git-pull-request .open {
table.table-issues td .octicon-issue-opened, table.table-issues td .octicon-git-pull-request .open {
color: #6CC644;
}
table.table-issues td .octicon-issue-closed,table.table-issues td .octicon-git-pull-request .closed{
table.table-issues td .octicon-issue-closed, table.table-issues td .octicon-git-pull-request .closed{
color : #BD2C00;;
}
@@ -1099,6 +918,10 @@ a.issue-title {
background-color: #6cc644;
}
.label-important {
background-color: #bd2c00;
}
ul.label-list {
list-style-type: none;
padding-left: 0px;
@@ -1153,47 +976,6 @@ div.milestone-menu a.delete {
color: #b00;
}
/*
div#milestone-progress-area {
display: inline-block;
}
div#milestone-progress-area div.milestone-progress {
width: 130px;
margin-bottom: -6px;
}
div.milestone-progress {
position: relative;
height: 10px;
color: white;
margin-bottom: 4px;
font-weight: bold;
font-size: 12px;
text-shadow: 0px 0px 5px #444;
background-color: silver;
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
}
span.milestone-progress {
position: absolute;
height: 100%;
background-color: green;
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
}
div.issue-header {
padding-left: 8px;
padding-right: 8px;
padding-top: 12px;
padding-bottom: 12px;
}
*/
div.issue-info {
border-top: 1px solid #e5e5e5;
border-bottom: 1px solid #e5e5e5;
@@ -1203,21 +985,6 @@ div.issue-info {
margin-right: 0px;
}
/*
div.issue-content {
padding: 13px;
background-color: #fff;
}
div.issue-content p:first-child {
margin-top: 0;
}
div.issue-content p:last-child {
margin-bottom: 0;
}
*/
h4#issueTitle {
font-size: large;
font-weight: bold;
@@ -1258,7 +1025,6 @@ div.commit-comment-box > div.panel-body {
div.issue-comment-box textarea {
width: 650px;
height: 100px;
max-height: 300px;
}
@@ -1425,18 +1191,6 @@ a.absent {
color: #c00;
}
/*
div.wiki-index-header {
background-color: #f5f5f5;
color: #333333;
margin: 0;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
border: 1px solid #d8d8d8;
padding: 8px 8px 8px 8px;
}
*/
div.wiki-sidebar {
background-color: white;
border: 1px solid #d8d8d8;
@@ -1466,18 +1220,6 @@ div.wiki-footer {
color: gray;
}
/*
div.wiki-index-content {
background-color: white;
border: 1px solid #d8d8d8;
padding: 0px;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
margin-bottom: 20px;
border-top: none;
}
*/
/****************************************************************************/
/* Commit */
/****************************************************************************/
@@ -2216,94 +1958,96 @@ div.container.blame-container{
/* Mobile */
/****************************************************************************/
@media (max-width: 767px) {
body>form#search {
margin: 0 -20px 20px -20px;
}
/* Hide header search box */
input[name=query] {
display: none;
}
#dashboard-signin-form {
display: none;
}
.container {
width: auto !important;
}
.body>div.pull-left {
width: auto !important;
}
.pc {
display: none;
}
body>div.dashboard-nav {
margin: 0 -20px 20px -20px;
padding: 0 10px;
}
/* Adjust issue / comment form */
#issue-title {
width: 100% !important;
}
div.attachable>textarea,
div.attachable>div.clickable {
width: 100% !important;
}
.container {
width: auto !important;
}
/* Adjust issue search box size and position */
#search-filter-box {
width: 90% !important;
position: absolute;
left: 14px;
right: 20px;
margin-top: 42px;
}
form#search-filter-form {
float: none !important;
margin-bottom: 80px !important;
}
.table-issues a.button-link {
width: 42px;
height: 16px;
overflow: hidden;
display: inline-block;
}
.nav-tabs a.btn[href$="/_edit"] {
width: 24px;
white-space: nowrap;
overflow: hidden;
padding: 4px 6px;
margin: 3px 4px 0 0;
}
body>div.container.body {
margin: 0 -12px 40px -12px;
}
.container.body>div[style="width: 170px;"]{
width: 32px !important;
margin-right: -5px;
overflow: hidden;
white-space: nowrap;
}
.container.body>div[style="margin-right: 180px;"]{
margin-right: 32px !important;
}
.container.body>div[style="width: 170px;"] .sidemenu i, .container.body>div[style="width: 170px;"] .sidemenu img {
padding-right: 5px;
}
/* Hide repository url box */
.container.body>div[style="width: 170px;"] .small,.container.body>div[style="width: 170px;"] .input-group {
display: none;
}
.container.body>div[style="width: 170px;"] div[style="margin-top: 10px;"] a.btn{
width: 26px !important;
padding: 2px;
}
.container.body>div[style="width: 170px;"] div[style="margin-top: 10px;"] a.btn i {
margin: 5px 10px 5px 6px;
}
/* Hide fork button */
div.col-md-1>div.input-group.pull-right {
display: none;
}
body>.container>#fork-form{
display: inline;
}
/* Adjust issue search box size and position */
#search-filter-box {
width: 100% !important;
}
form#search-filter-form {
float: none !important;
margin-bottom: 10px;
}
form#search-filter-form>div.form-group {
width: 100% !important;
margin-bottom: 10px;
}
.table-issues a.button-link {
width: 42px;
height: 16px;
overflow: hidden;
display: inline-block;
}
/*
.nav-tabs a.btn[href$="/_edit"] {
width: 24px;
white-space: nowrap;
overflow: hidden;
padding: 4px 6px;
margin: 3px 4px 0 0;
}
*/
body>div.container.body {
margin: 0 -12px 40px -12px;
}
/* Adjust sidemenu */
.container.body>div[style="width: 170px;"]{
width: 32px !important;
margin-right: -5px;
overflow: hidden;
white-space: nowrap;
}
/* Hide badge of sidemenu */
.container.body>div[style="width: 170px;"] span.badge {
display: none;
}
/* Hide download button */
.container.body>div[style="width: 170px;"] a.btn-sm {
display: none;
}
/* Hide repository url box */
.container.body>div[style="width: 170px;"] .small,
.container.body>div[style="width: 170px;"] .input-group {
display: none;
}
/* Hide fork button */
div.input-group>span.fork {
display: none;
}
}
/****************************************************************************/
/* Print */
/****************************************************************************/
a[href]:after {
display: none;
@media print {
div.headbar {
display: none;
}
a[href]:after {
display: none;
}
}

View File

@@ -2,16 +2,14 @@ package gitbucket.core.api
import gitbucket.core.util.RepositoryName
import org.specs2.mutable.Specification
import org.json4s.jackson.JsonMethods.{pretty, parse}
import org.json4s.jackson.JsonMethods.parse
import org.json4s._
import org.specs2.matcher._
import org.scalatest.FunSuite
import java.util.{Calendar, TimeZone, Date}
class JsonFormatSpec extends Specification {
class JsonFormatSpec extends FunSuite {
val date1 = {
val d = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
d.set(2011,3,14,16,0,49)
@@ -374,67 +372,58 @@ class JsonFormatSpec extends Specification {
}
}"""
def beFormatted(json2Arg:String) = new Matcher[String] {
def apply[S <: String](e: Expectable[S]) = {
import java.util.regex.Pattern
val json2 = Pattern.compile("""^\s*//.*$""", Pattern.MULTILINE).matcher(json2Arg).replaceAll("")
val js2 = try{
parse(json2)
}catch{
case e:com.fasterxml.jackson.core.JsonParseException => {
val p = java.lang.Math.max(e.getLocation.getCharOffset()-10,0).toInt
val message = json2.substring(p,java.lang.Math.min(p+100,json2.length))
throw new com.fasterxml.jackson.core.JsonParseException(message + e.getMessage , e.getLocation)
}
def assertJson(resultJson: String, expectJson: String) = {
import java.util.regex.Pattern
val json2 = Pattern.compile("""^\s*//.*$""", Pattern.MULTILINE).matcher(expectJson).replaceAll("")
val js2 = try {
parse(json2)
} catch {
case e: com.fasterxml.jackson.core.JsonParseException => {
val p = java.lang.Math.max(e.getLocation.getCharOffset() - 10, 0).toInt
val message = json2.substring(p, java.lang.Math.min(p + 100, json2.length))
throw new com.fasterxml.jackson.core.JsonParseException(message + e.getMessage, e.getLocation)
}
val js1 = parse(e.value)
result(js1 == js2,
"expected",
{
val diff = js2 diff js1
s"${pretty(js1)} is not ${pretty(js2)} \n\n ${pretty(Extraction.decompose(diff)(org.json4s.DefaultFormats))}"
},
e)
}
val js1 = parse(resultJson)
assert(js1 === js2)
}
"JsonFormat" should {
"apiUser" in {
JsonFormat(apiUser) must beFormatted(apiUserJson)
}
"repository" in {
JsonFormat(repository) must beFormatted(repositoryJson)
}
"apiPushCommit" in {
JsonFormat(apiPushCommit) must beFormatted(apiPushCommitJson)
}
"apiComment" in {
JsonFormat(apiComment) must beFormatted(apiCommentJson)
JsonFormat(apiCommentPR) must beFormatted(apiCommentPRJson)
}
"apiCommitListItem" in {
JsonFormat(apiCommitListItem) must beFormatted(apiCommitListItemJson)
}
"apiCommitStatus" in {
JsonFormat(apiCommitStatus) must beFormatted(apiCommitStatusJson)
}
"apiCombinedCommitStatus" in {
JsonFormat(apiCombinedCommitStatus) must beFormatted(apiCombinedCommitStatusJson)
}
"apiLabel" in {
JsonFormat(apiLabel) must beFormatted(apiLabelJson)
}
"apiIssue" in {
JsonFormat(apiIssue) must beFormatted(apiIssueJson)
JsonFormat(apiIssuePR) must beFormatted(apiIssuePRJson)
}
"apiPullRequest" in {
JsonFormat(apiPullRequest) must beFormatted(apiPullRequestJson)
}
"apiPullRequestReviewComment" in {
JsonFormat(apiPullRequestReviewComment) must beFormatted(apiPullRequestReviewCommentJson)
}
"apiBranchProtection" in {
JsonFormat(apiBranchProtection) must beFormatted(apiBranchProtectionJson)
}
test("apiUser") {
assertJson(JsonFormat(apiUser), apiUserJson)
}
test("repository") {
assertJson(JsonFormat(repository), repositoryJson)
}
test("apiPushCommit") {
assertJson(JsonFormat(apiPushCommit), apiPushCommitJson)
}
test("apiComment") {
assertJson(JsonFormat(apiComment), apiCommentJson)
assertJson(JsonFormat(apiCommentPR), apiCommentPRJson)
}
test("apiCommitListItem") {
assertJson(JsonFormat(apiCommitListItem), apiCommitListItemJson)
}
test("apiCommitStatus") {
assertJson(JsonFormat(apiCommitStatus), apiCommitStatusJson)
}
test("apiCombinedCommitStatus") {
assertJson(JsonFormat(apiCombinedCommitStatus), apiCombinedCommitStatusJson)
}
test("apiLabel") {
assertJson(JsonFormat(apiLabel), apiLabelJson)
}
test("apiIssue") {
assertJson(JsonFormat(apiIssue), apiIssueJson)
assertJson(JsonFormat(apiIssuePR), apiIssuePRJson)
}
test("apiPullRequest") {
assertJson(JsonFormat(apiPullRequest), apiPullRequestJson)
}
test("apiPullRequestReviewComment") {
assertJson(JsonFormat(apiPullRequestReviewComment), apiPullRequestReviewCommentJson)
}
test("apiBranchProtection") {
assertJson(JsonFormat(apiBranchProtection), apiBranchProtectionJson)
}
}

View File

@@ -1,26 +1,25 @@
package gitbucket.core.model
import gitbucket.core.model.CommitState._
import org.specs2.mutable.Specification
import org.scalatest.FunSpec
class CommitStateSpec extends Specification {
"CommitState" should {
"combine empty must eq PENDING" in {
combine(Set()) must_== PENDING
class CommitStateSpec extends FunSpec {
describe("CommitState") {
it("should combine empty must eq PENDING") {
assert(combine(Set()) == PENDING)
}
"combine includes ERROR must eq FAILURE" in {
combine(Set(ERROR, SUCCESS, PENDING)) must_== FAILURE
it("should combine includes ERROR must eq FAILURE") {
assert(combine(Set(ERROR, SUCCESS, PENDING)) == FAILURE)
}
"combine includes FAILURE must eq peinding" in {
combine(Set(FAILURE, SUCCESS, PENDING)) must_== FAILURE
it("should combine includes FAILURE must eq peinding") {
assert(combine(Set(FAILURE, SUCCESS, PENDING)) == FAILURE)
}
"combine includes PENDING must eq peinding" in {
combine(Set(PENDING, SUCCESS)) must_== PENDING
it("should combine includes PENDING must eq peinding") {
assert(combine(Set(PENDING, SUCCESS)) == PENDING)
}
"combine only SUCCESS must eq SUCCESS" in {
combine(Set(SUCCESS)) must_== SUCCESS
it("should combine only SUCCESS must eq SUCCESS") {
assert(combine(Set(SUCCESS)) == SUCCESS)
}
}
}

View File

@@ -1,91 +1,78 @@
package gitbucket.core.service
import gitbucket.core.model._
import org.specs2.mutable.Specification
import java.util.Date
import org.scalatest.FunSuite
class AccessTokenServiceSpec extends Specification with ServiceSpecBase {
class AccessTokenServiceSpec extends FunSuite with ServiceSpecBase {
"AccessTokenService" should {
"generateAccessToken" in { withTestDB { implicit session =>
AccessTokenService.generateAccessToken("root", "note") must be like{
case (id, token) if id != 0 => ok
}
}}
test("generateAccessToken") { withTestDB { implicit session =>
assert(AccessTokenService.generateAccessToken("root", "note") match {
case (id, token) => id != 0
})
}}
"getAccessTokens" in { withTestDB { implicit session =>
val (id, token) = AccessTokenService.generateAccessToken("root", "note")
val tokenHash = AccessTokenService.tokenToHash(token)
test("getAccessTokens") { withTestDB { implicit session =>
val (id, token) = AccessTokenService.generateAccessToken("root", "note")
val tokenHash = AccessTokenService.tokenToHash(token)
AccessTokenService.getAccessTokens("root") must be like{
case List(AccessToken(`id`, "root", `tokenHash`, "note")) => ok
}
}}
assert(AccessTokenService.getAccessTokens("root") == List(AccessToken(`id`, "root", `tokenHash`, "note")))
}}
"getAccessTokens(root) get root's tokens" in { withTestDB { implicit session =>
val (id, token) = AccessTokenService.generateAccessToken("root", "note")
val tokenHash = AccessTokenService.tokenToHash(token)
val user2 = generateNewAccount("user2")
AccessTokenService.generateAccessToken("user2", "note2")
test("getAccessTokens(root) get root's tokens") { withTestDB { implicit session =>
val (id, token) = AccessTokenService.generateAccessToken("root", "note")
val tokenHash = AccessTokenService.tokenToHash(token)
val user2 = generateNewAccount("user2")
AccessTokenService.generateAccessToken("user2", "note2")
AccessTokenService.getAccessTokens("root") must be like{
case List(AccessToken(`id`, "root", `tokenHash`, "note")) => ok
}
}}
assert(AccessTokenService.getAccessTokens("root") == List(AccessToken(`id`, "root", `tokenHash`, "note")))
}}
"deleteAccessToken" in { withTestDB { implicit session =>
val (id, token) = AccessTokenService.generateAccessToken("root", "note")
val user2 = generateNewAccount("user2")
AccessTokenService.generateAccessToken("user2", "note2")
test("deleteAccessToken") { withTestDB { implicit session =>
val (id, token) = AccessTokenService.generateAccessToken("root", "note")
val user2 = generateNewAccount("user2")
AccessTokenService.generateAccessToken("user2", "note2")
AccessTokenService.deleteAccessToken("root", id)
AccessTokenService.deleteAccessToken("root", id)
AccessTokenService.getAccessTokens("root") must beEmpty
}}
assert(AccessTokenService.getAccessTokens("root").isEmpty)
}}
"getAccountByAccessToken" in { withTestDB { implicit session =>
val (id, token) = AccessTokenService.generateAccessToken("root", "note")
AccessTokenService.getAccountByAccessToken(token) must beSome.like {
case user => user.userName must_== "root"
}
}}
test("getAccountByAccessToken") { withTestDB { implicit session =>
val (id, token) = AccessTokenService.generateAccessToken("root", "note")
assert(AccessTokenService.getAccountByAccessToken(token) match {
case Some(user) => user.userName == "root"
})
}}
"getAccountByAccessToken don't get removed account" in { withTestDB { implicit session =>
val user2 = generateNewAccount("user2")
val (id, token) = AccessTokenService.generateAccessToken("user2", "note")
AccountService.updateAccount(user2.copy(isRemoved=true))
test("getAccountByAccessToken don't get removed account") { withTestDB { implicit session =>
val user2 = generateNewAccount("user2")
val (id, token) = AccessTokenService.generateAccessToken("user2", "note")
AccountService.updateAccount(user2.copy(isRemoved=true))
AccessTokenService.getAccountByAccessToken(token) must beEmpty
}}
assert(AccessTokenService.getAccountByAccessToken(token).isEmpty)
}}
"generateAccessToken create uniq token" in { withTestDB { implicit session =>
val tokenIt = List("token1","token1","token1","token2").iterator
val service = new AccessTokenService{
override def makeAccessTokenString:String = tokenIt.next
}
test("generateAccessToken create uniq token") { withTestDB { implicit session =>
val tokenIt = List("token1","token1","token1","token2").iterator
val service = new AccessTokenService{
override def makeAccessTokenString:String = tokenIt.next
}
service.generateAccessToken("root", "note1") must like{
case (_, "token1") => ok
}
service.generateAccessToken("root", "note2") must like{
case (_, "token2") => ok
}
}}
assert(service.generateAccessToken("root", "note1")._2 == "token1")
assert(service.generateAccessToken("root", "note2")._2 == "token2")
}}
"when update Account.userName then AccessToken.userName changed" in { withTestDB { implicit session =>
val user2 = generateNewAccount("user2")
val (id, token) = AccessTokenService.generateAccessToken("user2", "note")
import gitbucket.core.model.Profile._
import profile.simple._
Accounts.filter(_.userName === "user2".bind).map(_.userName).update("user3")
test("when update Account.userName then AccessToken.userName changed") { withTestDB { implicit session =>
val user2 = generateNewAccount("user2")
val (id, token) = AccessTokenService.generateAccessToken("user2", "note")
import gitbucket.core.model.Profile._
import profile.simple._
Accounts.filter(_.userName === "user2".bind).map(_.userName).update("user3")
AccessTokenService.getAccountByAccessToken(token) must beSome.like {
case user => user.userName must_== "user3"
}
}}
}
assert(AccessTokenService.getAccountByAccessToken(token) match {
case Some(user) => user.userName == "user3"
})
}}
}

View File

@@ -1,79 +1,71 @@
package gitbucket.core.service
import gitbucket.core.model.{Account, GroupMember}
import org.specs2.mutable.Specification
import java.util.Date
import org.scalatest.FunSuite
class AccountServiceSpec extends Specification with ServiceSpecBase {
class AccountServiceSpec extends FunSuite with ServiceSpecBase {
"AccountService" should {
val RootMailAddress = "root@localhost"
val RootMailAddress = "root@localhost"
"getAllUsers" in { withTestDB { implicit session =>
AccountService.getAllUsers() must be like{
case List(Account("root", "root", RootMailAddress, _, true, _, _, _, None, None, false, false)) => ok
}
}}
test("getAllUsers") { withTestDB { implicit session =>
assert(AccountService.getAllUsers() match {
case List(Account("root", "root", RootMailAddress, _, true, _, _, _, None, None, false, false)) => true
case _ => false
})
}}
"getAccountByUserName" in { withTestDB { implicit session =>
AccountService.getAccountByUserName("root") must beSome.like {
case user => user.userName must_== "root"
}
test("getAccountByUserName") { withTestDB { implicit session =>
assert(AccountService.getAccountByUserName("root").get.userName == "root")
assert(AccountService.getAccountByUserName("invalid user name").isEmpty)
}}
AccountService.getAccountByUserName("invalid user name") must beNone
}}
test("getAccountByMailAddress") { withTestDB { implicit session =>
assert(AccountService.getAccountByMailAddress(RootMailAddress).isDefined)
}}
"getAccountByMailAddress" in { withTestDB { implicit session =>
AccountService.getAccountByMailAddress(RootMailAddress) must beSome
}}
test("updateLastLoginDate") { withTestDB { implicit session =>
val root = "root"
def user() = AccountService.getAccountByUserName(root).getOrElse(sys.error(s"user $root does not exists"))
"updateLastLoginDate" in { withTestDB { implicit session =>
val root = "root"
def user() =
AccountService.getAccountByUserName(root).getOrElse(sys.error(s"user $root does not exists"))
assert(user().lastLoginDate.isEmpty)
user().lastLoginDate must beNone
val date1 = new Date
AccountService.updateLastLoginDate(root)
user().lastLoginDate must beSome.like{ case date =>
date must be_>(date1)
}
val date2 = new Date
Thread.sleep(1000)
AccountService.updateLastLoginDate(root)
user().lastLoginDate must beSome.like{ case date =>
date must be_>(date2)
}
}}
val date1 = new Date
AccountService.updateLastLoginDate(root)
assert(user().lastLoginDate.get.compareTo(date1) > 0)
"updateAccount" in { withTestDB { implicit session =>
val root = "root"
def user() =
AccountService.getAccountByUserName(root).getOrElse(sys.error(s"user $root does not exists"))
val date2 = new Date
Thread.sleep(1000)
AccountService.updateLastLoginDate(root)
assert(user().lastLoginDate.get.compareTo(date2) > 0)
}}
val newAddress = "new mail address"
AccountService.updateAccount(user().copy(mailAddress = newAddress))
user().mailAddress must_== newAddress
}}
test("updateAccount") { withTestDB { implicit session =>
val root = "root"
def user() = AccountService.getAccountByUserName(root).getOrElse(sys.error(s"user $root does not exists"))
"group" in { withTestDB { implicit session =>
val group1 = "group1"
val user1 = "root"
AccountService.createGroup(group1, None)
val newAddress = "new mail address"
AccountService.updateAccount(user().copy(mailAddress = newAddress))
assert(user().mailAddress == newAddress)
}}
AccountService.getGroupMembers(group1) must_== Nil
AccountService.getGroupsByUserName(user1) must_== Nil
test("group") { withTestDB { implicit session =>
val group1 = "group1"
val user1 = "root"
AccountService.createGroup(group1, None)
AccountService.updateGroupMembers(group1, List((user1, true)))
assert(AccountService.getGroupMembers(group1) == Nil)
assert(AccountService.getGroupsByUserName(user1) == Nil)
AccountService.getGroupMembers(group1) must_== List(GroupMember(group1, user1, true))
AccountService.getGroupsByUserName(user1) must_== List(group1)
AccountService.updateGroupMembers(group1, List((user1, true)))
AccountService.updateGroupMembers(group1, Nil)
assert(AccountService.getGroupMembers(group1) == List(GroupMember(group1, user1, true)))
assert(AccountService.getGroupsByUserName(user1) == List(group1))
AccountService.getGroupMembers(group1) must_== Nil
AccountService.getGroupsByUserName(user1) must_== Nil
}}
}
AccountService.updateGroupMembers(group1, Nil)
assert(AccountService.getGroupMembers(group1) == Nil)
assert(AccountService.getGroupsByUserName(user1) == Nil)
}}
}

View File

@@ -1,78 +0,0 @@
package gitbucket.core.service
import gitbucket.core.model._
import gitbucket.core.model.Profile._
import profile.simple._
import org.specs2.mutable.Specification
import java.util.Date
class CommitStatusServiceSpec extends Specification with ServiceSpecBase with CommitStatusService
with RepositoryService with AccountService{
val now = new java.util.Date()
val fixture1 = CommitStatus(
userName = "root",
repositoryName = "repo",
commitId = "0e97b8f59f7cdd709418bb59de53f741fd1c1bd7",
context = "jenkins/test",
creator = "tester",
state = CommitState.PENDING,
targetUrl = Some("http://example.com/target"),
description = Some("description"),
updatedDate = now,
registeredDate = now)
def findById(id: Int)(implicit s:Session) = CommitStatuses.filter(_.byPrimaryKey(id)).firstOption
def generateFixture1(tester:Account)(implicit s:Session) = createCommitStatus(
userName = fixture1.userName,
repositoryName = fixture1.repositoryName,
sha = fixture1.commitId,
context = fixture1.context,
state = fixture1.state,
targetUrl = fixture1.targetUrl,
description = fixture1.description,
creator = tester,
now = fixture1.registeredDate)
"CommitStatusService" should {
"createCommitState can insert and update" in { withTestDB { implicit session =>
val tester = generateNewAccount(fixture1.creator)
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
val id = generateFixture1(tester:Account)
getCommitStatus(fixture1.userName, fixture1.repositoryName, id) must_==
Some(fixture1.copy(commitStatusId=id))
// other one can update
val tester2 = generateNewAccount("tester2")
val time2 = new java.util.Date();
val id2 = createCommitStatus(
userName = fixture1.userName,
repositoryName = fixture1.repositoryName,
sha = fixture1.commitId,
context = fixture1.context,
state = CommitState.SUCCESS,
targetUrl = Some("http://example.com/target2"),
description = Some("description2"),
creator = tester2,
now = time2)
getCommitStatus(fixture1.userName, fixture1.repositoryName, id2) must_== Some(fixture1.copy(
commitStatusId = id,
creator = "tester2",
state = CommitState.SUCCESS,
targetUrl = Some("http://example.com/target2"),
description = Some("description2"),
updatedDate = time2))
}}
"getCommitStatus can find by commitId and context" in { withTestDB { implicit session =>
val tester = generateNewAccount(fixture1.creator)
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
val id = generateFixture1(tester:Account)
getCommitStatus(fixture1.userName, fixture1.repositoryName, fixture1.commitId, fixture1.context) must_== Some(fixture1.copy(commitStatusId=id))
}}
"getCommitStatus can find by commitStatusId" in { withTestDB { implicit session =>
val tester = generateNewAccount(fixture1.creator)
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
val id = generateFixture1(tester:Account)
getCommitStatus(fixture1.userName, fixture1.repositoryName, id) must_== Some(fixture1.copy(commitStatusId=id))
}}
}
}

View File

@@ -0,0 +1,74 @@
package gitbucket.core.service
import gitbucket.core.model._
import gitbucket.core.model.Profile._
import profile.simple._
import org.scalatest.FunSuite
class CommitStatusServiceSpec extends FunSuite with ServiceSpecBase with CommitStatusService
with RepositoryService with AccountService{
val now = new java.util.Date()
val fixture1 = CommitStatus(
userName = "root",
repositoryName = "repo",
commitId = "0e97b8f59f7cdd709418bb59de53f741fd1c1bd7",
context = "jenkins/test",
creator = "tester",
state = CommitState.PENDING,
targetUrl = Some("http://example.com/target"),
description = Some("description"),
updatedDate = now,
registeredDate = now)
def findById(id: Int)(implicit s:Session) = CommitStatuses.filter(_.byPrimaryKey(id)).firstOption
def generateFixture1(tester:Account)(implicit s:Session) = createCommitStatus(
userName = fixture1.userName,
repositoryName = fixture1.repositoryName,
sha = fixture1.commitId,
context = fixture1.context,
state = fixture1.state,
targetUrl = fixture1.targetUrl,
description = fixture1.description,
creator = tester,
now = fixture1.registeredDate)
test("createCommitState can insert and update") { withTestDB { implicit session =>
val tester = generateNewAccount(fixture1.creator)
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
val id = generateFixture1(tester:Account)
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, id) == Some(fixture1.copy(commitStatusId=id)))
// other one can update
val tester2 = generateNewAccount("tester2")
val time2 = new java.util.Date();
val id2 = createCommitStatus(
userName = fixture1.userName,
repositoryName = fixture1.repositoryName,
sha = fixture1.commitId,
context = fixture1.context,
state = CommitState.SUCCESS,
targetUrl = Some("http://example.com/target2"),
description = Some("description2"),
creator = tester2,
now = time2)
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, id2) == Some(fixture1.copy(
commitStatusId = id,
creator = "tester2",
state = CommitState.SUCCESS,
targetUrl = Some("http://example.com/target2"),
description = Some("description2"),
updatedDate = time2)))
}}
test("getCommitStatus can find by commitId and context") { withTestDB { implicit session =>
val tester = generateNewAccount(fixture1.creator)
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
val id = generateFixture1(tester:Account)
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, fixture1.commitId, fixture1.context) == Some(fixture1.copy(commitStatusId=id)))
}}
test("getCommitStatus can find by commitStatusId") { withTestDB { implicit session =>
val tester = generateNewAccount(fixture1.creator)
createRepository(fixture1.repositoryName,fixture1.userName,None,false)
val id = generateFixture1(tester:Account)
assert(getCommitStatus(fixture1.userName, fixture1.repositoryName, id) == Some(fixture1.copy(commitStatusId=id)))
}}
}

View File

@@ -2,49 +2,44 @@ package gitbucket.core.service
import gitbucket.core.model._
import gitbucket.core.service.IssuesService._
import org.specs2.mutable.Specification
import java.util.Date
import org.scalatest.FunSuite
class IssuesServiceSpec extends Specification with ServiceSpecBase {
"IssuesService" should {
"getCommitStatues" in { withTestDB { implicit session =>
val user1 = generateNewUserWithDBRepository("user1","repo1")
class IssuesServiceSpec extends FunSuite with ServiceSpecBase {
test("getCommitStatues") { withTestDB { implicit session =>
val user1 = generateNewUserWithDBRepository("user1","repo1")
def getCommitStatues = dummyService.getCommitStatues(List(("user1","repo1",1),("user1","repo1",2)))
def getCommitStatues = dummyService.getCommitStatues(List(("user1","repo1",1),("user1","repo1",2)))
getCommitStatues must_== Map.empty
assert(getCommitStatues == Map.empty)
val now = new java.util.Date()
val issueId = generateNewIssue("user1","repo1")
issueId must_== 1
val now = new java.util.Date()
val issueId = generateNewIssue("user1","repo1")
assert(issueId == 1)
getCommitStatues must_== Map.empty
assert(getCommitStatues == Map.empty)
val cs = dummyService.createCommitStatus("user1","repo1","shasha", "default", CommitState.SUCCESS, Some("http://exmple.com/ci"), Some("exampleService"), now, user1)
val cs = dummyService.createCommitStatus("user1","repo1","shasha", "default", CommitState.SUCCESS, Some("http://exmple.com/ci"), Some("exampleService"), now, user1)
getCommitStatues must_== Map.empty
assert(getCommitStatues == Map.empty)
val (is2, pr2) = generateNewPullRequest("user1/repo1/master","user1/repo1/feature1")
pr2.issueId must_== 2
val (is2, pr2) = generateNewPullRequest("user1/repo1/master","user1/repo1/feature1")
assert(pr2.issueId == 2)
// if there are no statuses, state is none
getCommitStatues must_== Map.empty
// if there are no statuses, state is none
assert(getCommitStatues == Map.empty)
// if there is a status, state is that
val cs2 = dummyService.createCommitStatus("user1","repo1","feature1", "default", CommitState.SUCCESS, Some("http://exmple.com/ci"), Some("exampleService"), now, user1)
getCommitStatues must_== Map(("user1","repo1",2) -> CommitStatusInfo(1,1,Some("default"),Some(CommitState.SUCCESS),Some("http://exmple.com/ci"),Some("exampleService")))
// if there is a status, state is that
val cs2 = dummyService.createCommitStatus("user1","repo1","feature1", "default", CommitState.SUCCESS, Some("http://exmple.com/ci"), Some("exampleService"), now, user1)
assert(getCommitStatues == Map(("user1","repo1",2) -> CommitStatusInfo(1,1,Some("default"),Some(CommitState.SUCCESS),Some("http://exmple.com/ci"),Some("exampleService"))))
// if there are two statuses, state is none
val cs3 = dummyService.createCommitStatus("user1","repo1","feature1", "pend", CommitState.PENDING, Some("http://exmple.com/ci"), Some("exampleService"), now, user1)
getCommitStatues must_== Map(("user1","repo1",2) -> CommitStatusInfo(2,1,None,None,None,None))
// if there are two statuses, state is none
val cs3 = dummyService.createCommitStatus("user1","repo1","feature1", "pend", CommitState.PENDING, Some("http://exmple.com/ci"), Some("exampleService"), now, user1)
assert(getCommitStatues == Map(("user1","repo1",2) -> CommitStatusInfo(2,1,None,None,None,None)))
// get only statuses in query issues
val (is3, pr3) = generateNewPullRequest("user1/repo1/master","user1/repo1/feature3")
val cs4 = dummyService.createCommitStatus("user1","repo1","feature3", "none", CommitState.PENDING, None, None, now, user1)
getCommitStatues must_== Map(("user1","repo1",2) -> CommitStatusInfo(2,1,None,None,None,None))
} }
}
// get only statuses in query issues
val (is3, pr3) = generateNewPullRequest("user1/repo1/master","user1/repo1/feature3")
val cs4 = dummyService.createCommitStatus("user1","repo1","feature3", "none", CommitState.PENDING, None, None, now, user1)
assert(getCommitStatues == Map(("user1","repo1",2) -> CommitStatusInfo(2,1,None,None,None,None)))
} }
}

View File

@@ -1,12 +1,11 @@
package gitbucket.core.service
import gitbucket.core.model._
import org.scalatest.FunSpec
import org.specs2.mutable.Specification
class LabelsServiceSpec extends Specification with ServiceSpecBase {
"getLabels" should {
"be empty when not have any labels" in { withTestDB { implicit session =>
class LabelsServiceSpec extends FunSpec with ServiceSpecBase {
describe("getLabels") {
it("should be empty when not have any labels") { withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
generateNewUserWithDBRepository("user1", "repo2")
@@ -15,9 +14,9 @@ class LabelsServiceSpec extends Specification with ServiceSpecBase {
generateNewUserWithDBRepository("user2", "repo1")
dummyService.createLabel("user2", "repo1", "label1", "000000")
dummyService.getLabels("user1", "repo1") must haveSize(0)
assert(dummyService.getLabels("user1", "repo1").isEmpty)
}}
"return contained labels" in { withTestDB { implicit session =>
it("should return contained labels") { withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
val labelId1 = dummyService.createLabel("user1", "repo1", "label1", "000000")
val labelId2 = dummyService.createLabel("user1", "repo1", "label2", "ffffff")
@@ -30,20 +29,22 @@ class LabelsServiceSpec extends Specification with ServiceSpecBase {
def getLabels = dummyService.getLabels("user1", "repo1")
getLabels must haveSize(2)
getLabels must_== List(
assert(getLabels.length == 2)
assert(getLabels == List(
Label("user1", "repo1", labelId1, "label1", "000000"),
Label("user1", "repo1", labelId2, "label2", "ffffff"))
)
}}
}
"getLabel" should {
"return None when the label not exist" in { withTestDB { implicit session =>
describe("getLabel") {
it("should return None when the label not exist") { withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
dummyService.getLabel("user1", "repo1", 1) must beNone
dummyService.getLabel("user1", "repo1", "label1") must beNone
assert(dummyService.getLabel("user1", "repo1", 1) == None)
assert(dummyService.getLabel("user1", "repo1", "label1") == None)
}}
"return a label fetched by label id" in { withTestDB { implicit session =>
it("should return a label fetched by label id") { withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
val labelId1 = dummyService.createLabel("user1", "repo1", "label1", "000000")
dummyService.createLabel("user1", "repo1", "label2", "ffffff")
@@ -55,9 +56,9 @@ class LabelsServiceSpec extends Specification with ServiceSpecBase {
dummyService.createLabel("user2", "repo1", "label1", "000000")
def getLabel = dummyService.getLabel("user1", "repo1", labelId1)
getLabel must_== Some(Label("user1", "repo1", labelId1, "label1", "000000"))
assert(getLabel == Some(Label("user1", "repo1", labelId1, "label1", "000000")))
}}
"return a label fetched by label name" in { withTestDB { implicit session =>
it("should return a label fetched by label name") { withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
val labelId1 = dummyService.createLabel("user1", "repo1", "label1", "000000")
dummyService.createLabel("user1", "repo1", "label2", "ffffff")
@@ -69,11 +70,11 @@ class LabelsServiceSpec extends Specification with ServiceSpecBase {
dummyService.createLabel("user2", "repo1", "label1", "000000")
def getLabel = dummyService.getLabel("user1", "repo1", "label1")
getLabel must_== Some(Label("user1", "repo1", labelId1, "label1", "000000"))
getLabel == Some(Label("user1", "repo1", labelId1, "label1", "000000"))
}}
}
"createLabel" should {
"return accurate label id" in { withTestDB { implicit session =>
describe("createLabel") {
it("should return accurate label id") { withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
generateNewUserWithDBRepository("user1", "repo2")
generateNewUserWithDBRepository("user2", "repo1")
@@ -81,13 +82,13 @@ class LabelsServiceSpec extends Specification with ServiceSpecBase {
dummyService.createLabel("user1", "repo2", "label1", "000000")
dummyService.createLabel("user2", "repo1", "label1", "000000")
val labelId = dummyService.createLabel("user1", "repo1", "label2", "000000")
labelId must_== 4
assert(labelId == 4)
def getLabel = dummyService.getLabel("user1", "repo1", labelId)
getLabel must_== Some(Label("user1", "repo1", labelId, "label2", "000000"))
assert(getLabel == Some(Label("user1", "repo1", labelId, "label2", "000000")))
}}
}
"updateLabel" should {
"change target label" in { withTestDB { implicit session =>
describe("updateLabel") {
it("should change target label") { withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
generateNewUserWithDBRepository("user1", "repo2")
generateNewUserWithDBRepository("user2", "repo1")
@@ -96,11 +97,11 @@ class LabelsServiceSpec extends Specification with ServiceSpecBase {
dummyService.createLabel("user2", "repo1", "label1", "000000")
dummyService.updateLabel("user1", "repo1", labelId, "updated-label", "ffffff")
def getLabel = dummyService.getLabel("user1", "repo1", labelId)
getLabel must_== Some(Label("user1", "repo1", labelId, "updated-label", "ffffff"))
assert(getLabel == Some(Label("user1", "repo1", labelId, "updated-label", "ffffff")))
}}
}
"deleteLabel" should {
"remove target label" in { withTestDB { implicit session =>
describe("deleteLabel") {
it("should remove target label") { withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
generateNewUserWithDBRepository("user1", "repo2")
generateNewUserWithDBRepository("user2", "repo1")
@@ -108,7 +109,7 @@ class LabelsServiceSpec extends Specification with ServiceSpecBase {
dummyService.createLabel("user1", "repo2", "label1", "000000")
dummyService.createLabel("user2", "repo1", "label1", "000000")
dummyService.deleteLabel("user1", "repo1", labelId)
dummyService.getLabel("user1", "repo1", labelId) must beNone
assert(dummyService.getLabel("user1", "repo1", labelId) == None)
}}
}
}

View File

@@ -1,26 +1,17 @@
package gitbucket.core.service
import gitbucket.core.model._
import gitbucket.core.util.JGitUtil
import gitbucket.core.util.Directory._
import gitbucket.core.util.Implicits._
import gitbucket.core.util.ControlUtil._
import gitbucket.core.util.GitSpecUtil._
import org.apache.commons.io.FileUtils
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.dircache.DirCache
import org.eclipse.jgit.lib._
import org.eclipse.jgit.revwalk._
import org.eclipse.jgit.treewalk._
import org.specs2.mutable.Specification
import org.scalatest.FunSpec
import java.io.File
import java.nio.file._
import java.util.Date
class MergeServiceSpec extends Specification {
sequential
class MergeServiceSpec extends FunSpec {
val service = new MergeService{}
val branch = "master"
val issueId = 10
@@ -36,95 +27,95 @@ class MergeServiceSpec extends Specification {
createFile(git, s"refs/heads/${branch}", "test.txt", "hoge2" )
createFile(git, s"refs/pull/${issueId}/head", "test.txt", "hoge4" )
}
"checkConflict, checkConflictCache" should {
"checkConflict false if not conflicted, and create cache" in {
describe("checkConflict, checkConflictCache") {
it("checkConflict false if not conflicted, and create cache") {
val repo1Dir = initRepository("user1","repo1")
service.checkConflictCache("user1", "repo1", branch, issueId) mustEqual None
assert(service.checkConflictCache("user1", "repo1", branch, issueId) == None)
val conflicted = service.checkConflict("user1", "repo1", branch, issueId)
service.checkConflictCache("user1", "repo1", branch, issueId) mustEqual Some(false)
conflicted mustEqual false
assert(service.checkConflictCache("user1", "repo1", branch, issueId) == Some(false))
assert(conflicted == false)
}
"checkConflict true if not conflicted, and create cache" in {
it("checkConflict true if not conflicted, and create cache") {
val repo2Dir = initRepository("user1","repo2")
using(Git.open(repo2Dir)){ git =>
createConfrict(git)
}
service.checkConflictCache("user1", "repo2", branch, issueId) mustEqual None
assert(service.checkConflictCache("user1", "repo2", branch, issueId) == None)
val conflicted = service.checkConflict("user1", "repo2", branch, issueId)
conflicted mustEqual true
service.checkConflictCache("user1", "repo2", branch, issueId) mustEqual Some(true)
assert(conflicted == true)
assert(service.checkConflictCache("user1", "repo2", branch, issueId) == Some(true))
}
}
"checkConflictCache" should {
"merged cache invalid if origin branch moved" in {
describe("checkConflictCache") {
it("merged cache invalid if origin branch moved") {
val repo3Dir = initRepository("user1","repo3")
service.checkConflict("user1", "repo3", branch, issueId) mustEqual false
service.checkConflictCache("user1", "repo3", branch, issueId) mustEqual Some(false)
assert(service.checkConflict("user1", "repo3", branch, issueId) == false)
assert(service.checkConflictCache("user1", "repo3", branch, issueId) == Some(false))
using(Git.open(repo3Dir)){ git =>
createFile(git, s"refs/heads/${branch}", "test.txt", "hoge2" )
}
service.checkConflictCache("user1", "repo3", branch, issueId) mustEqual None
assert(service.checkConflictCache("user1", "repo3", branch, issueId) == None)
}
"merged cache invalid if request branch moved" in {
it("merged cache invalid if request branch moved") {
val repo4Dir = initRepository("user1","repo4")
service.checkConflict("user1", "repo4", branch, issueId) mustEqual false
service.checkConflictCache("user1", "repo4", branch, issueId) mustEqual Some(false)
assert(service.checkConflict("user1", "repo4", branch, issueId) == false)
assert(service.checkConflictCache("user1", "repo4", branch, issueId) == Some(false))
using(Git.open(repo4Dir)){ git =>
createFile(git, s"refs/pull/${issueId}/head", "test.txt", "hoge4" )
}
service.checkConflictCache("user1", "repo4", branch, issueId) mustEqual None
assert(service.checkConflictCache("user1", "repo4", branch, issueId) == None)
}
"merged cache invalid if origin branch moved" in {
it("should merged cache invalid if origin branch moved") {
val repo5Dir = initRepository("user1","repo5")
service.checkConflict("user1", "repo5", branch, issueId) mustEqual false
service.checkConflictCache("user1", "repo5", branch, issueId) mustEqual Some(false)
assert(service.checkConflict("user1", "repo5", branch, issueId) == false)
assert(service.checkConflictCache("user1", "repo5", branch, issueId) == Some(false))
using(Git.open(repo5Dir)){ git =>
createFile(git, s"refs/heads/${branch}", "test.txt", "hoge2" )
}
service.checkConflictCache("user1", "repo5", branch, issueId) mustEqual None
assert(service.checkConflictCache("user1", "repo5", branch, issueId) == None)
}
"conflicted cache invalid if request branch moved" in {
it("conflicted cache invalid if request branch moved") {
val repo6Dir = initRepository("user1","repo6")
using(Git.open(repo6Dir)){ git =>
createConfrict(git)
}
service.checkConflict("user1", "repo6", branch, issueId) mustEqual true
service.checkConflictCache("user1", "repo6", branch, issueId) mustEqual Some(true)
assert(service.checkConflict("user1", "repo6", branch, issueId) == true)
assert(service.checkConflictCache("user1", "repo6", branch, issueId) == Some(true))
using(Git.open(repo6Dir)){ git =>
createFile(git, s"refs/pull/${issueId}/head", "test.txt", "hoge4" )
}
service.checkConflictCache("user1", "repo6", branch, issueId) mustEqual None
assert(service.checkConflictCache("user1", "repo6", branch, issueId) == None)
}
"conflicted cache invalid if origin branch moved" in {
it("conflicted cache invalid if origin branch moved") {
val repo7Dir = initRepository("user1","repo7")
using(Git.open(repo7Dir)){ git =>
createConfrict(git)
}
service.checkConflict("user1", "repo7", branch, issueId) mustEqual true
service.checkConflictCache("user1", "repo7", branch, issueId) mustEqual Some(true)
assert(service.checkConflict("user1", "repo7", branch, issueId) == true)
assert(service.checkConflictCache("user1", "repo7", branch, issueId) == Some(true))
using(Git.open(repo7Dir)){ git =>
createFile(git, s"refs/heads/${branch}", "test.txt", "hoge4" )
}
service.checkConflictCache("user1", "repo7", branch, issueId) mustEqual None
assert(service.checkConflictCache("user1", "repo7", branch, issueId) == None)
}
}
"mergePullRequest" should {
"can merge" in {
describe("mergePullRequest") {
it("can merge") {
val repo8Dir = initRepository("user1","repo8")
using(Git.open(repo8Dir)){ git =>
createFile(git, s"refs/pull/${issueId}/head", "test.txt", "hoge2" )
val committer = new PersonIdent("dummy2", "dummy2@example.com")
getFile(git, branch, "test.txt").content.get mustEqual "hoge"
assert(getFile(git, branch, "test.txt").content.get == "hoge")
val requestBranchId = git.getRepository.resolve(s"refs/pull/${issueId}/head")
val masterId = git.getRepository.resolve(branch)
service.mergePullRequest(git, branch, issueId, "merged", committer)
val lastCommitId = git.getRepository.resolve(branch);
val lastCommitId = git.getRepository.resolve(branch)
val commit = using(new RevWalk(git.getRepository))(_.parseCommit(lastCommitId))
commit.getCommitterIdent() mustEqual committer
commit.getAuthorIdent() mustEqual committer
commit.getFullMessage() mustEqual "merged"
commit.getParents.toSet mustEqual Set( requestBranchId, masterId )
getFile(git, branch, "test.txt").content.get mustEqual "hoge2"
assert(commit.getCommitterIdent() == committer)
assert(commit.getAuthorIdent() == committer)
assert(commit.getFullMessage() == "merged")
assert(commit.getParents.toSet == Set( requestBranchId, masterId ))
assert(getFile(git, branch, "test.txt").content.get == "hoge2")
}
}
}

View File

@@ -1,134 +1,134 @@
package gitbucket.core.service
import gitbucket.core.util.GitSpecUtil._
import org.specs2.mutable.Specification
import org.eclipse.jgit.transport.{ReceivePack, ReceiveCommand}
import org.eclipse.jgit.lib.ObjectId
import gitbucket.core.model.CommitState
import gitbucket.core.service.ProtectedBranchService.{ProtectedBranchReceiveHook, ProtectedBranchInfo}
import scalaz._, Scalaz._
import org.scalatest.FunSpec
class ProtectedBranchServiceSpec extends Specification with ServiceSpecBase with ProtectedBranchService with CommitStatusService {
class ProtectedBranchServiceSpec extends FunSpec with ServiceSpecBase with ProtectedBranchService with CommitStatusService {
val receiveHook = new ProtectedBranchReceiveHook()
val now = new java.util.Date()
val sha = "0c77148632618b59b6f70004e3084002be2b8804"
val sha2 = "0c77148632618b59b6f70004e3084002be2b8805"
"getProtectedBranchInfo" should {
"empty is disabled" in {
describe("getProtectedBranchInfo") {
it("should empty is disabled") {
withTestDB { implicit session =>
getProtectedBranchInfo("user1", "repo1", "branch") must_== ProtectedBranchInfo.disabled("user1", "repo1")
assert(getProtectedBranchInfo("user1", "repo1", "branch") == ProtectedBranchInfo.disabled("user1", "repo1"))
}
}
"enable and update and disable" in {
it("should enable and update and disable") {
withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
enableBranchProtection("user1", "repo1", "branch", false, Nil)
getProtectedBranchInfo("user1", "repo1", "branch") must_== ProtectedBranchInfo("user1", "repo1", true, Nil, false)
assert(getProtectedBranchInfo("user1", "repo1", "branch") == ProtectedBranchInfo("user1", "repo1", true, Nil, false))
enableBranchProtection("user1", "repo1", "branch", true, Seq("hoge","huge"))
getProtectedBranchInfo("user1", "repo1", "branch") must_== ProtectedBranchInfo("user1", "repo1", true, Seq("hoge","huge"), true)
assert(getProtectedBranchInfo("user1", "repo1", "branch") == ProtectedBranchInfo("user1", "repo1", true, Seq("hoge","huge"), true))
disableBranchProtection("user1", "repo1", "branch")
getProtectedBranchInfo("user1", "repo1", "branch") must_== ProtectedBranchInfo.disabled("user1", "repo1")
assert(getProtectedBranchInfo("user1", "repo1", "branch") == ProtectedBranchInfo.disabled("user1", "repo1"))
}
}
"empty contexts is no-include-administrators" in {
it("should empty contexts is no-include-administrators") {
withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
enableBranchProtection("user1", "repo1", "branch", false, Nil)
getProtectedBranchInfo("user1", "repo1", "branch").includeAdministrators must_== false
assert(getProtectedBranchInfo("user1", "repo1", "branch").includeAdministrators == false)
enableBranchProtection("user1", "repo1", "branch", true, Nil)
getProtectedBranchInfo("user1", "repo1", "branch").includeAdministrators must_== false
assert(getProtectedBranchInfo("user1", "repo1", "branch").includeAdministrators == false)
}
}
"getProtectedBranchList" in {
it("getProtectedBranchList") {
withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
enableBranchProtection("user1", "repo1", "branch", false, Nil)
enableBranchProtection("user1", "repo1", "branch2", false, Seq("fuga"))
enableBranchProtection("user1", "repo1", "branch3", true, Seq("hoge"))
getProtectedBranchList("user1", "repo1").toSet must_== Set("branch", "branch2", "branch3")
assert(getProtectedBranchList("user1", "repo1").toSet == Set("branch", "branch2", "branch3"))
}
}
"getBranchProtectedReason on force push from admin" in {
it("getBranchProtectedReason on force push from admin") {
withTestDB { implicit session =>
withTestRepository { git =>
val rp = new ReceivePack(git.getRepository) <| { _.setAllowNonFastForwards(true) }
val rc = new ReceiveCommand(ObjectId.fromString(sha), ObjectId.fromString(sha2), "refs/heads/branch", ReceiveCommand.Type.UPDATE_NONFASTFORWARD)
generateNewUserWithDBRepository("user1", "repo1")
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== None
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == None)
enableBranchProtection("user1", "repo1", "branch", false, Nil)
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== Some("Cannot force-push to a protected branch")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == Some("Cannot force-push to a protected branch"))
}
}
}
"getBranchProtectedReason on force push from othre" in {
it("getBranchProtectedReason on force push from other") {
withTestDB { implicit session =>
withTestRepository { git =>
val rp = new ReceivePack(git.getRepository) <| { _.setAllowNonFastForwards(true) }
val rc = new ReceiveCommand(ObjectId.fromString(sha), ObjectId.fromString(sha2), "refs/heads/branch", ReceiveCommand.Type.UPDATE_NONFASTFORWARD)
generateNewUserWithDBRepository("user1", "repo1")
receiveHook.preReceive("user1", "repo1", rp, rc, "user2") must_== None
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2") == None)
enableBranchProtection("user1", "repo1", "branch", false, Nil)
receiveHook.preReceive("user1", "repo1", rp, rc, "user2") must_== Some("Cannot force-push to a protected branch")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2") == Some("Cannot force-push to a protected branch"))
}
}
}
"getBranchProtectedReason check status on push from othre" in {
it("getBranchProtectedReason check status on push from other") {
withTestDB { implicit session =>
withTestRepository { git =>
val rp = new ReceivePack(git.getRepository) <| { _.setAllowNonFastForwards(false) }
val rc = new ReceiveCommand(ObjectId.fromString(sha), ObjectId.fromString(sha2), "refs/heads/branch", ReceiveCommand.Type.UPDATE)
val user1 = generateNewUserWithDBRepository("user1", "repo1")
receiveHook.preReceive("user1", "repo1", rp, rc, "user2") must_== None
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2") == None)
enableBranchProtection("user1", "repo1", "branch", false, Seq("must"))
receiveHook.preReceive("user1", "repo1", rp, rc, "user2") must_== Some("Required status check \"must\" is expected")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2") == Some("Required status check \"must\" is expected"))
enableBranchProtection("user1", "repo1", "branch", false, Seq("must", "must2"))
receiveHook.preReceive("user1", "repo1", rp, rc, "user2") must_== Some("2 of 2 required status checks are expected")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2") == Some("2 of 2 required status checks are expected"))
createCommitStatus("user1", "repo1", sha2, "context", CommitState.SUCCESS, None, None, now, user1)
receiveHook.preReceive("user1", "repo1", rp, rc, "user2") must_== Some("2 of 2 required status checks are expected")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2") == Some("2 of 2 required status checks are expected"))
createCommitStatus("user1", "repo1", sha2, "must", CommitState.SUCCESS, None, None, now, user1)
receiveHook.preReceive("user1", "repo1", rp, rc, "user2") must_== Some("Required status check \"must2\" is expected")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2") == Some("Required status check \"must2\" is expected"))
createCommitStatus("user1", "repo1", sha2, "must2", CommitState.SUCCESS, None, None, now, user1)
receiveHook.preReceive("user1", "repo1", rp, rc, "user2") must_== None
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user2") == None)
}
}
}
"getBranchProtectedReason check status on push from admin" in {
it("getBranchProtectedReason check status on push from admin") {
withTestDB { implicit session =>
withTestRepository { git =>
val rp = new ReceivePack(git.getRepository) <| { _.setAllowNonFastForwards(false) }
val rc = new ReceiveCommand(ObjectId.fromString(sha), ObjectId.fromString(sha2), "refs/heads/branch", ReceiveCommand.Type.UPDATE)
val user1 = generateNewUserWithDBRepository("user1", "repo1")
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== None
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == None)
enableBranchProtection("user1", "repo1", "branch", false, Seq("must"))
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== None
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == None)
enableBranchProtection("user1", "repo1", "branch", true, Seq("must"))
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== Some("Required status check \"must\" is expected")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == Some("Required status check \"must\" is expected"))
enableBranchProtection("user1", "repo1", "branch", false, Seq("must", "must2"))
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== None
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == None)
enableBranchProtection("user1", "repo1", "branch", true, Seq("must", "must2"))
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== Some("2 of 2 required status checks are expected")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == Some("2 of 2 required status checks are expected"))
createCommitStatus("user1", "repo1", sha2, "context", CommitState.SUCCESS, None, None, now, user1)
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== Some("2 of 2 required status checks are expected")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == Some("2 of 2 required status checks are expected"))
createCommitStatus("user1", "repo1", sha2, "must", CommitState.SUCCESS, None, None, now, user1)
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== Some("Required status check \"must2\" is expected")
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == Some("Required status check \"must2\" is expected"))
createCommitStatus("user1", "repo1", sha2, "must2", CommitState.SUCCESS, None, None, now, user1)
receiveHook.preReceive("user1", "repo1", rp, rc, "user1") must_== None
assert(receiveHook.preReceive("user1", "repo1", rp, rc, "user1") == None)
}
}
}
}
"ProtectedBranchInfo" should {
"administrator is owner" in {
describe("ProtectedBranchInfo") {
it("administrator is owner") {
withTestDB { implicit session =>
generateNewUserWithDBRepository("user1", "repo1")
val x = ProtectedBranchInfo("user1", "repo1", true, Nil, false)
x.isAdministrator("user1") must_== true
x.isAdministrator("user2") must_== false
assert(x.isAdministrator("user1") == true)
assert(x.isAdministrator("user2") == false)
}
}
"administrator is manager" in {
it("administrator is manager") {
withTestDB { implicit session =>
val x = ProtectedBranchInfo("grp1", "repo1", true, Nil, false)
x.createGroup("grp1", None)
@@ -137,49 +137,49 @@ class ProtectedBranchServiceSpec extends Specification with ServiceSpecBase with
generateNewAccount("user3")
x.updateGroupMembers("grp1", List("user1"->true, "user2"->false))
x.isAdministrator("user1") must_== true
x.isAdministrator("user2") must_== false
x.isAdministrator("user3") must_== false
assert(x.isAdministrator("user1") == true)
assert(x.isAdministrator("user2") == false)
assert(x.isAdministrator("user3") == false)
}
}
"unSuccessedContexts" in {
it("unSuccessedContexts") {
withTestDB { implicit session =>
val user1 = generateNewUserWithDBRepository("user1", "repo1")
val x = ProtectedBranchInfo("user1", "repo1", true, List("must"), false)
x.unSuccessedContexts(sha) must_== Set("must")
assert(x.unSuccessedContexts(sha) == Set("must"))
createCommitStatus("user1", "repo1", sha, "context", CommitState.SUCCESS, None, None, now, user1)
x.unSuccessedContexts(sha) must_== Set("must")
assert(x.unSuccessedContexts(sha) == Set("must"))
createCommitStatus("user1", "repo1", sha, "must", CommitState.ERROR, None, None, now, user1)
x.unSuccessedContexts(sha) must_== Set("must")
assert(x.unSuccessedContexts(sha) == Set("must"))
createCommitStatus("user1", "repo1", sha, "must", CommitState.PENDING, None, None, now, user1)
x.unSuccessedContexts(sha) must_== Set("must")
assert(x.unSuccessedContexts(sha) == Set("must"))
createCommitStatus("user1", "repo1", sha, "must", CommitState.FAILURE, None, None, now, user1)
x.unSuccessedContexts(sha) must_== Set("must")
assert(x.unSuccessedContexts(sha) == Set("must"))
createCommitStatus("user1", "repo1", sha, "must", CommitState.SUCCESS, None, None, now, user1)
x.unSuccessedContexts(sha) must_== Set()
assert(x.unSuccessedContexts(sha) == Set())
}
}
"unSuccessedContexts when empty" in {
it("unSuccessedContexts when empty") {
withTestDB { implicit session =>
val user1 = generateNewUserWithDBRepository("user1", "repo1")
val x = ProtectedBranchInfo("user1", "repo1", true, Nil, false)
val sha = "0c77148632618b59b6f70004e3084002be2b8804"
x.unSuccessedContexts(sha) must_== Nil
assert(x.unSuccessedContexts(sha) == Set())
createCommitStatus("user1", "repo1", sha, "context", CommitState.SUCCESS, None, None, now, user1)
x.unSuccessedContexts(sha) must_== Nil
assert(x.unSuccessedContexts(sha) == Set())
}
}
"if disabled, needStatusCheck is false" in {
it("if disabled, needStatusCheck is false") {
withTestDB { implicit session =>
ProtectedBranchInfo("user1", "repo1", false, Seq("must"), true).needStatusCheck("user1") must_== false
assert(ProtectedBranchInfo("user1", "repo1", false, Seq("must"), true).needStatusCheck("user1") == false)
}
}
"needStatusCheck includeAdministrators" in {
it("needStatusCheck includeAdministrators") {
withTestDB { implicit session =>
ProtectedBranchInfo("user1", "repo1", true, Seq("must"), false).needStatusCheck("user2") must_== true
ProtectedBranchInfo("user1", "repo1", true, Seq("must"), false).needStatusCheck("user1") must_== false
ProtectedBranchInfo("user1", "repo1", true, Seq("must"), true ).needStatusCheck("user2") must_== true
ProtectedBranchInfo("user1", "repo1", true, Seq("must"), true ).needStatusCheck("user1") must_== true
assert(ProtectedBranchInfo("user1", "repo1", true, Seq("must"), false).needStatusCheck("user2") == true)
assert(ProtectedBranchInfo("user1", "repo1", true, Seq("must"), false).needStatusCheck("user1") == false)
assert(ProtectedBranchInfo("user1", "repo1", true, Seq("must"), true ).needStatusCheck("user2") == true)
assert(ProtectedBranchInfo("user1", "repo1", true, Seq("must"), true ).needStatusCheck("user1") == true)
}
}
}

View File

@@ -1,17 +1,17 @@
package gitbucket.core.service
import gitbucket.core.model._
import gitbucket.core.model.Profile._
import org.scalatest.FunSpec
import org.specs2.mutable.Specification
class PullRequestServiceSpec extends FunSpec with ServiceSpecBase with PullRequestService with IssuesService {
class PullRequestServiceSpec extends Specification with ServiceSpecBase with PullRequestService with IssuesService {
def swap(r: (Issue, PullRequest)) = (r._2 -> r._1)
"PullRequestService.getPullRequestFromBranch" should {
"""
describe("PullRequestService.getPullRequestFromBranch") {
it("""should
|return pull request if exists pull request from `branch` to `defaultBranch` and not closed.
|return pull request if exists pull request from `branch` to othre branch and not closed.
|return None if all pull request is closed""".stripMargin.trim in { withTestDB { implicit se =>
|return None if all pull request is closed""".stripMargin.trim) { withTestDB { implicit se =>
generateNewUserWithDBRepository("user1", "repo1")
generateNewUserWithDBRepository("user1", "repo2")
generateNewUserWithDBRepository("user2", "repo1")
@@ -22,12 +22,12 @@ class PullRequestServiceSpec extends Specification with ServiceSpecBase with Pul
val r1 = swap(generateNewPullRequest("user1/repo1/master2", "user1/repo1/head1"))
val r2 = swap(generateNewPullRequest("user1/repo1/master", "user1/repo1/head1"))
val r3 = swap(generateNewPullRequest("user1/repo1/master4", "user1/repo1/head1"))
getPullRequestFromBranch("user1", "repo1", "head1", "master") must_== Some(r2)
assert(getPullRequestFromBranch("user1", "repo1", "head1", "master") == Some(r2))
updateClosed("user1", "repo1", r2._1.issueId, true)
getPullRequestFromBranch("user1", "repo1", "head1", "master").get must beOneOf(r1, r2)
assert(Seq(r1, r2).contains(getPullRequestFromBranch("user1", "repo1", "head1", "master").get))
updateClosed("user1", "repo1", r1._1.issueId, true)
updateClosed("user1", "repo1", r3._1.issueId, true)
getPullRequestFromBranch("user1", "repo1", "head1", "master") must beNone
assert(getPullRequestFromBranch("user1", "repo1", "head1", "master") == None)
} }
}
}

View File

@@ -1,39 +1,33 @@
package gitbucket.core.service
import gitbucket.core.model._
import org.specs2.mutable.Specification
import org.scalatest.FunSuite
class RepositoryServiceSpec extends FunSuite with ServiceSpecBase with RepositoryService with AccountService{
test("renameRepository can rename CommitState, ProtectedBranches") { withTestDB { implicit session =>
val tester = generateNewAccount("tester")
createRepository("repo", "root", None, false)
val service = new CommitStatusService with ProtectedBranchService {}
val id = service.createCommitStatus(
userName = "root",
repositoryName = "repo",
sha = "0e97b8f59f7cdd709418bb59de53f741fd1c1bd7",
context = "jenkins/test",
state = CommitState.PENDING,
targetUrl = Some("http://example.com/target"),
description = Some("description"),
creator = tester,
now = new java.util.Date)
class RepositoryServiceSpec extends Specification with ServiceSpecBase with RepositoryService with AccountService{
"RepositoryService" should {
"renameRepository can rename CommitState, ProtectedBranches" in { withTestDB { implicit session =>
val tester = generateNewAccount("tester")
createRepository("repo","root",None,false)
val service = new CommitStatusService with ProtectedBranchService {}
val id = service.createCommitStatus(
userName = "root",
repositoryName = "repo",
sha = "0e97b8f59f7cdd709418bb59de53f741fd1c1bd7",
context = "jenkins/test",
state = CommitState.PENDING,
targetUrl = Some("http://example.com/target"),
description = Some("description"),
creator = tester,
now = new java.util.Date)
service.enableBranchProtection("root", "repo", "branch", true, Seq("must1", "must2"))
var orgPbi = service.getProtectedBranchInfo("root", "repo", "branch")
val org = service.getCommitStatus("root","repo", id).get
service.enableBranchProtection("root", "repo", "branch", true, Seq("must1", "must2"))
renameRepository("root","repo","tester","repo2")
val orgPbi = service.getProtectedBranchInfo("root", "repo", "branch")
val org = service.getCommitStatus("root","repo", id).get
val neo = service.getCommitStatus("tester","repo2", org.commitId, org.context).get
neo must_==
org.copy(
commitStatusId=neo.commitStatusId,
repositoryName="repo2",
userName="tester")
service.getProtectedBranchInfo("tester", "repo2", "branch") must_==
orgPbi.copy(owner="tester", repository="repo2")
}}
}
renameRepository("root","repo","tester","repo2")
val neo = service.getCommitStatus("tester","repo2", org.commitId, org.context).get
assert(neo == org.copy(commitStatusId = neo.commitStatusId, repositoryName = "repo2", userName = "tester"))
assert(service.getProtectedBranchInfo("tester", "repo2", "branch") == orgPbi.copy(owner = "tester", repository = "repo2"))
}}
}

View File

@@ -1,76 +1,75 @@
package gitbucket.core.service
import org.specs2.mutable.Specification
import gitbucket.core.model.WebHook
import org.scalatest.FunSuite
class WebHookServiceSpec extends Specification with ServiceSpecBase {
class WebHookServiceSpec extends FunSuite with ServiceSpecBase {
lazy val service = new WebHookPullRequestService with AccountService with RepositoryService with PullRequestService with IssuesService
"WebHookPullRequestService.getPullRequestsByRequestForWebhook" should {
"find from request branch" in { withTestDB { implicit session =>
val user1 = generateNewUserWithDBRepository("user1","repo1")
val user2 = generateNewUserWithDBRepository("user2","repo2")
val user3 = generateNewUserWithDBRepository("user3","repo3")
val issueUser = user("root")
val (issue1, pullreq1) = generateNewPullRequest("user1/repo1/master1", "user2/repo2/master2", loginUser="root")
val (issue3, pullreq3) = generateNewPullRequest("user3/repo3/master3", "user2/repo2/master2", loginUser="root")
val (issue32, pullreq32) = generateNewPullRequest("user3/repo3/master32", "user2/repo2/master2", loginUser="root")
generateNewPullRequest("user2/repo2/master2", "user1/repo1/master2")
service.addWebHook("user1", "repo1", "webhook1-1", Set(WebHook.PullRequest))
service.addWebHook("user1", "repo1", "webhook1-2", Set(WebHook.PullRequest))
service.addWebHook("user2", "repo2", "webhook2-1", Set(WebHook.PullRequest))
service.addWebHook("user2", "repo2", "webhook2-2", Set(WebHook.PullRequest))
service.addWebHook("user3", "repo3", "webhook3-1", Set(WebHook.PullRequest))
service.addWebHook("user3", "repo3", "webhook3-2", Set(WebHook.PullRequest))
test("WebHookPullRequestService.getPullRequestsByRequestForWebhook") { withTestDB { implicit session =>
val user1 = generateNewUserWithDBRepository("user1","repo1")
val user2 = generateNewUserWithDBRepository("user2","repo2")
val user3 = generateNewUserWithDBRepository("user3","repo3")
val issueUser = user("root")
val (issue1, pullreq1) = generateNewPullRequest("user1/repo1/master1", "user2/repo2/master2", loginUser="root")
val (issue3, pullreq3) = generateNewPullRequest("user3/repo3/master3", "user2/repo2/master2", loginUser="root")
val (issue32, pullreq32) = generateNewPullRequest("user3/repo3/master32", "user2/repo2/master2", loginUser="root")
generateNewPullRequest("user2/repo2/master2", "user1/repo1/master2")
service.addWebHook("user1", "repo1", "webhook1-1", Set(WebHook.PullRequest))
service.addWebHook("user1", "repo1", "webhook1-2", Set(WebHook.PullRequest))
service.addWebHook("user2", "repo2", "webhook2-1", Set(WebHook.PullRequest))
service.addWebHook("user2", "repo2", "webhook2-2", Set(WebHook.PullRequest))
service.addWebHook("user3", "repo3", "webhook3-1", Set(WebHook.PullRequest))
service.addWebHook("user3", "repo3", "webhook3-2", Set(WebHook.PullRequest))
service.getPullRequestsByRequestForWebhook("user1","repo1","master1") must_== Map.empty
assert(service.getPullRequestsByRequestForWebhook("user1","repo1","master1") == Map.empty)
var r = service.getPullRequestsByRequestForWebhook("user2","repo2","master2").mapValues(_.map(_.url).toSet)
val r = service.getPullRequestsByRequestForWebhook("user2","repo2","master2").mapValues(_.map(_.url).toSet)
r.size must_== 3
r((issue1, issueUser, pullreq1, user1, user2)) must_== Set("webhook1-1","webhook1-2")
r((issue3, issueUser, pullreq3, user3, user2)) must_== Set("webhook3-1","webhook3-2")
r((issue32, issueUser, pullreq32, user3, user2)) must_== Set("webhook3-1","webhook3-2")
assert(r.size == 3)
assert(r((issue1, issueUser, pullreq1, user1, user2)) == Set("webhook1-1","webhook1-2"))
assert(r((issue3, issueUser, pullreq3, user3, user2)) == Set("webhook3-1","webhook3-2"))
assert(r((issue32, issueUser, pullreq32, user3, user2)) == Set("webhook3-1","webhook3-2"))
// when closed, it not founds.
service.updateClosed("user1","repo1",issue1.issueId, true)
// when closed, it not founds.
service.updateClosed("user1","repo1",issue1.issueId, true)
var r2 = service.getPullRequestsByRequestForWebhook("user2","repo2","master2").mapValues(_.map(_.url).toSet)
r2.size must_== 2
r2((issue3, issueUser, pullreq3, user3, user2)) must_== Set("webhook3-1","webhook3-2")
r2((issue32, issueUser, pullreq32, user3, user2)) must_== Set("webhook3-1","webhook3-2")
} }
}
val r2 = service.getPullRequestsByRequestForWebhook("user2","repo2","master2").mapValues(_.map(_.url).toSet)
assert(r2.size == 2)
assert(r2((issue3, issueUser, pullreq3, user3, user2)) == Set("webhook3-1","webhook3-2"))
assert(r2((issue32, issueUser, pullreq32, user3, user2)) == Set("webhook3-1","webhook3-2"))
} }
"add and get and update and delete" in { withTestDB { implicit session =>
test("add and get and update and delete") { withTestDB { implicit session =>
val user1 = generateNewUserWithDBRepository("user1","repo1")
service.addWebHook("user1", "repo1", "http://example.com", Set(WebHook.PullRequest))
service.getWebHooks("user1", "repo1") must_== List((WebHook("user1","repo1","http://example.com"),Set(WebHook.PullRequest)))
service.getWebHook("user1", "repo1", "http://example.com") must_== Some((WebHook("user1","repo1","http://example.com"),Set(WebHook.PullRequest)))
service.getWebHooksByEvent("user1", "repo1", WebHook.PullRequest) must_== List((WebHook("user1","repo1","http://example.com")))
service.getWebHooksByEvent("user1", "repo1", WebHook.Push) must_== Nil
service.getWebHook("user1", "repo1", "http://example.com2") must_== None
service.getWebHook("user2", "repo1", "http://example.com") must_== None
service.getWebHook("user1", "repo2", "http://example.com") must_== None
assert(service.getWebHooks("user1", "repo1") == List((WebHook("user1","repo1","http://example.com"),Set(WebHook.PullRequest))))
assert(service.getWebHook("user1", "repo1", "http://example.com") == Some((WebHook("user1","repo1","http://example.com"),Set(WebHook.PullRequest))))
assert(service.getWebHooksByEvent("user1", "repo1", WebHook.PullRequest) == List((WebHook("user1","repo1","http://example.com"))))
assert(service.getWebHooksByEvent("user1", "repo1", WebHook.Push) == Nil)
assert(service.getWebHook("user1", "repo1", "http://example.com2") == None)
assert(service.getWebHook("user2", "repo1", "http://example.com") == None)
assert(service.getWebHook("user1", "repo2", "http://example.com") == None)
service.updateWebHook("user1", "repo1", "http://example.com", Set(WebHook.Push, WebHook.Issues))
service.getWebHook("user1", "repo1", "http://example.com") must_== Some((WebHook("user1","repo1","http://example.com"),Set(WebHook.Push, WebHook.Issues)))
service.getWebHooksByEvent("user1", "repo1", WebHook.PullRequest) must_== Nil
service.getWebHooksByEvent("user1", "repo1", WebHook.Push) must_== List((WebHook("user1","repo1","http://example.com")))
assert(service.getWebHook("user1", "repo1", "http://example.com") == Some((WebHook("user1","repo1","http://example.com"),Set(WebHook.Push, WebHook.Issues))))
assert(service.getWebHooksByEvent("user1", "repo1", WebHook.PullRequest) == Nil)
assert(service.getWebHooksByEvent("user1", "repo1", WebHook.Push) == List((WebHook("user1","repo1","http://example.com"))))
service.deleteWebHook("user1", "repo1", "http://example.com")
service.getWebHook("user1", "repo1", "http://example.com") must_== None
assert(service.getWebHook("user1", "repo1", "http://example.com") == None)
} }
"getWebHooks, getWebHooksByEvent" in { withTestDB { implicit session =>
test("getWebHooks, getWebHooksByEvent") { withTestDB { implicit session =>
val user1 = generateNewUserWithDBRepository("user1","repo1")
service.addWebHook("user1", "repo1", "http://example.com/1", Set(WebHook.PullRequest))
service.addWebHook("user1", "repo1", "http://example.com/2", Set(WebHook.Push))
service.addWebHook("user1", "repo1", "http://example.com/3", Set(WebHook.PullRequest,WebHook.Push))
service.getWebHooks("user1", "repo1") must_== List(
assert(service.getWebHooks("user1", "repo1") == List(
WebHook("user1","repo1","http://example.com/1")->Set(WebHook.PullRequest),
WebHook("user1","repo1","http://example.com/2")->Set(WebHook.Push),
WebHook("user1","repo1","http://example.com/3")->Set(WebHook.PullRequest,WebHook.Push))
service.getWebHooksByEvent("user1", "repo1", WebHook.PullRequest) must_== List(
WebHook("user1","repo1","http://example.com/3")->Set(WebHook.PullRequest,WebHook.Push)))
assert(service.getWebHooksByEvent("user1", "repo1", WebHook.PullRequest) == List(
WebHook("user1","repo1","http://example.com/1"),
WebHook("user1","repo1","http://example.com/3"))
WebHook("user1","repo1","http://example.com/3")))
} }
}

View File

@@ -1,39 +1,35 @@
package gitbucket.core.ssh
import org.specs2.mutable._
import org.specs2.mock.Mockito
import org.apache.sshd.server.command.UnknownCommand
import javax.servlet.ServletContext
import org.scalatest.FunSpec
class GitCommandFactorySpec extends Specification with Mockito {
class GitCommandFactorySpec extends FunSpec {
val factory = new GitCommandFactory("http://localhost:8080")
"createCommand" should {
"returns GitReceivePack when command is git-receive-pack" in {
factory.createCommand("git-receive-pack '/owner/repo.git'").isInstanceOf[DefaultGitReceivePack] must beTrue
factory.createCommand("git-receive-pack '/owner/repo.wiki.git'").isInstanceOf[DefaultGitReceivePack] must beTrue
describe("createCommand") {
it("should return GitReceivePack when command is git-receive-pack"){
assert(factory.createCommand("git-receive-pack '/owner/repo.git'").isInstanceOf[DefaultGitReceivePack] == true)
assert(factory.createCommand("git-receive-pack '/owner/repo.wiki.git'").isInstanceOf[DefaultGitReceivePack] == true)
}
"returns GitUploadPack when command is git-upload-pack" in {
factory.createCommand("git-upload-pack '/owner/repo.git'").isInstanceOf[DefaultGitUploadPack] must beTrue
factory.createCommand("git-upload-pack '/owner/repo.wiki.git'").isInstanceOf[DefaultGitUploadPack] must beTrue
it("should return GitUploadPack when command is git-upload-pack"){
assert(factory.createCommand("git-upload-pack '/owner/repo.git'").isInstanceOf[DefaultGitUploadPack] == true)
assert(factory.createCommand("git-upload-pack '/owner/repo.wiki.git'").isInstanceOf[DefaultGitUploadPack] == true)
}
"returns UnknownCommand when command is not git-(upload|receive)-pack" in {
factory.createCommand("git- '/owner/repo.git'").isInstanceOf[UnknownCommand] must beTrue
factory.createCommand("git-pack '/owner/repo.git'").isInstanceOf[UnknownCommand] must beTrue
factory.createCommand("git-a-pack '/owner/repo.git'").isInstanceOf[UnknownCommand] must beTrue
factory.createCommand("git-up-pack '/owner/repo.git'").isInstanceOf[UnknownCommand] must beTrue
factory.createCommand("\ngit-upload-pack '/owner/repo.git'").isInstanceOf[UnknownCommand] must beTrue
it("should return UnknownCommand when command is not git-(upload|receive)-pack"){
assert(factory.createCommand("git- '/owner/repo.git'").isInstanceOf[UnknownCommand] == true)
assert(factory.createCommand("git-pack '/owner/repo.git'").isInstanceOf[UnknownCommand] == true)
assert(factory.createCommand("git-a-pack '/owner/repo.git'").isInstanceOf[UnknownCommand] == true)
assert(factory.createCommand("git-up-pack '/owner/repo.git'").isInstanceOf[UnknownCommand] == true)
assert(factory.createCommand("\ngit-upload-pack '/owner/repo.git'").isInstanceOf[UnknownCommand] == true)
}
"returns UnknownCommand when git command has no valid arguments" in {
it("should return UnknownCommand when git command has no valid arguments"){
// must be: git-upload-pack '/owner/repository_name.git'
factory.createCommand("git-upload-pack").isInstanceOf[UnknownCommand] must beTrue
factory.createCommand("git-upload-pack /owner/repo.git").isInstanceOf[UnknownCommand] must beTrue
factory.createCommand("git-upload-pack 'owner/repo.git'").isInstanceOf[UnknownCommand] must beTrue
factory.createCommand("git-upload-pack '/ownerrepo.git'").isInstanceOf[UnknownCommand] must beTrue
factory.createCommand("git-upload-pack '/owner/repo.wiki'").isInstanceOf[UnknownCommand] must beTrue
assert(factory.createCommand("git-upload-pack").isInstanceOf[UnknownCommand] == true)
assert(factory.createCommand("git-upload-pack /owner/repo.git").isInstanceOf[UnknownCommand] == true)
assert(factory.createCommand("git-upload-pack 'owner/repo.git'").isInstanceOf[UnknownCommand] == true)
assert(factory.createCommand("git-upload-pack '/ownerrepo.git'").isInstanceOf[UnknownCommand] == true)
assert(factory.createCommand("git-upload-pack '/owner/repo.wiki'").isInstanceOf[UnknownCommand] == true)
}
}

View File

@@ -1,15 +1,16 @@
package gitbucket.core.util
import org.specs2.mutable._
import org.scalatest.FunSpec
class DirectorySpec extends FunSpec {
class DirectorySpec extends Specification {
"GitBucketHome" should {
"set under target in test scope" in {
Directory.GitBucketHome mustEqual new java.io.File("target/gitbucket_home_for_test").getAbsolutePath
}
"exists" in {
new java.io.File(Directory.GitBucketHome).exists
describe("GitBucketHome"){
it("should set under target in test scope"){
assert(Directory.GitBucketHome == new java.io.File("target/gitbucket_home_for_test").getAbsolutePath)
}
}
// test("GitBucketHome should exists"){
// new java.io.File(Directory.GitBucketHome).exists
// }
}

View File

@@ -1,103 +1,104 @@
package gitbucket.core.util
import org.specs2.mutable._
import GitSpecUtil._
import org.scalatest.FunSuite
class JGitUtilSpec extends Specification {
class JGitUtilSpec extends FunSuite {
"getFileList(git: Git, revision: String, path)" should {
test("getFileList(git: Git, revision: String, path)"){
withTestRepository { git =>
def list(branch: String, path: String) =
JGitUtil.getFileList(git, branch, path).map(finfo => (finfo.name, finfo.message, finfo.isDirectory))
list("master", ".") mustEqual Nil
list("master", "dir/subdir") mustEqual Nil
list("branch", ".") mustEqual Nil
list("branch", "dir/subdir") mustEqual Nil
assert(list("master", ".") == Nil)
assert(list("master", "dir/subdir") == Nil)
assert(list("branch", ".") == Nil)
assert(list("branch", "dir/subdir") == Nil)
createFile(git, "master", "README.md", "body1", message = "commit1")
list("master", ".") mustEqual List(("README.md", "commit1", false))
list("master", "dir/subdir") mustEqual Nil
list("branch", ".") mustEqual Nil
list("branch", "dir/subdir") mustEqual Nil
assert(list("master", ".") == List(("README.md", "commit1", false)))
assert(list("master", "dir/subdir") == Nil)
assert(list("branch", ".") == Nil)
assert(list("branch", "dir/subdir") == Nil)
createFile(git, "master", "README.md", "body2", message = "commit2")
list("master", ".") mustEqual List(("README.md", "commit2", false))
list("master", "dir/subdir") mustEqual Nil
list("branch", ".") mustEqual Nil
list("branch", "dir/subdir") mustEqual Nil
assert(list("master", ".") == List(("README.md", "commit2", false)))
assert(list("master", "dir/subdir") == Nil)
assert(list("branch", ".") == Nil)
assert(list("branch", "dir/subdir") == Nil)
createFile(git, "master", "dir/subdir/File3.md", "body3", message = "commit3")
list("master", ".") mustEqual List(("dir/subdir", "commit3", true), ("README.md", "commit2", false))
list("master", "dir/subdir") mustEqual List(("File3.md", "commit3", false))
list("branch", ".") mustEqual Nil
list("branch", "dir/subdir") mustEqual Nil
assert(list("master", ".") == List(("dir/subdir", "commit3", true), ("README.md", "commit2", false)))
assert(list("master", "dir/subdir") == List(("File3.md", "commit3", false)))
assert(list("branch", ".") == Nil)
assert(list("branch", "dir/subdir") == Nil)
createFile(git, "master", "dir/subdir/File4.md", "body4", message = "commit4")
list("master", ".") mustEqual List(("dir/subdir", "commit4", true), ("README.md", "commit2", false))
list("master", "dir/subdir") mustEqual List(("File3.md", "commit3", false), ("File4.md", "commit4", false))
list("branch", ".") mustEqual Nil
list("branch", "dir/subdir") mustEqual Nil
assert(list("master", ".") == List(("dir/subdir", "commit4", true), ("README.md", "commit2", false)))
assert(list("master", "dir/subdir") == List(("File3.md", "commit3", false), ("File4.md", "commit4", false)))
assert(list("branch", ".") == Nil)
assert(list("branch", "dir/subdir") == Nil)
createFile(git, "master", "README5.md", "body5", message = "commit5")
list("master", ".") mustEqual List(("dir/subdir", "commit4", true), ("README.md", "commit2", false), ("README5.md", "commit5", false))
list("master", "dir/subdir") mustEqual List(("File3.md", "commit3", false), ("File4.md", "commit4", false))
list("branch", ".") mustEqual Nil
list("branch", "dir/subdir") mustEqual Nil
assert(list("master", ".") == List(("dir/subdir", "commit4", true), ("README.md", "commit2", false), ("README5.md", "commit5", false)))
assert(list("master", "dir/subdir") == List(("File3.md", "commit3", false), ("File4.md", "commit4", false)))
assert(list("branch", ".") == Nil)
assert(list("branch", "dir/subdir") == Nil)
createFile(git, "master", "README.md", "body6", message = "commit6")
list("master", ".") mustEqual List(("dir/subdir", "commit4", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("master", "dir/subdir") mustEqual List(("File3.md", "commit3", false), ("File4.md", "commit4", false))
list("branch", ".") mustEqual Nil
list("branch", "dir/subdir") mustEqual Nil
assert(list("master", ".") == List(("dir/subdir", "commit4", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("master", "dir/subdir") == List(("File3.md", "commit3", false), ("File4.md", "commit4", false)))
assert(list("branch", ".") == Nil)
assert(list("branch", "dir/subdir") == Nil)
git.branchCreate().setName("branch").setStartPoint("master").call()
list("master", ".") mustEqual List(("dir/subdir", "commit4", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("master", "dir/subdir") mustEqual List(("File3.md", "commit3", false), ("File4.md", "commit4", false))
list("branch", ".") mustEqual List(("dir/subdir", "commit4", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("branch", "dir/subdir") mustEqual List(("File3.md", "commit3", false), ("File4.md", "commit4", false))
assert(list("master", ".") == List(("dir/subdir", "commit4", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("master", "dir/subdir") == List(("File3.md", "commit3", false), ("File4.md", "commit4", false)))
assert(list("branch", ".") == List(("dir/subdir", "commit4", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("branch", "dir/subdir") == List(("File3.md", "commit3", false), ("File4.md", "commit4", false)))
createFile(git, "branch", "dir/subdir/File3.md", "body7", message = "commit7")
list("master", ".") mustEqual List(("dir/subdir", "commit4", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("master", "dir/subdir") mustEqual List(("File3.md", "commit3", false), ("File4.md", "commit4", false))
list("branch", ".") mustEqual List(("dir/subdir", "commit7", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("branch", "dir/subdir") mustEqual List(("File3.md", "commit7", false), ("File4.md", "commit4", false))
assert(list("master", ".") == List(("dir/subdir", "commit4", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("master", "dir/subdir") == List(("File3.md", "commit3", false), ("File4.md", "commit4", false)))
assert(list("branch", ".") == List(("dir/subdir", "commit7", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("branch", "dir/subdir") == List(("File3.md", "commit7", false), ("File4.md", "commit4", false)))
createFile(git, "master", "dir8/File8.md", "body8", message = "commit8")
list("master", ".") mustEqual List(("dir/subdir", "commit4", true), ("dir8", "commit8", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("master", "dir/subdir") mustEqual List(("File3.md", "commit3", false), ("File4.md", "commit4", false))
list("branch", ".") mustEqual List(("dir/subdir", "commit7", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("branch", "dir/subdir") mustEqual List(("File3.md", "commit7", false), ("File4.md", "commit4", false))
assert(list("master", ".") == List(("dir/subdir", "commit4", true), ("dir8", "commit8", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("master", "dir/subdir") == List(("File3.md", "commit3", false), ("File4.md", "commit4", false)))
assert(list("branch", ".") == List(("dir/subdir", "commit7", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("branch", "dir/subdir") == List(("File3.md", "commit7", false), ("File4.md", "commit4", false)))
createFile(git, "branch", "dir/subdir9/File9.md", "body9", message = "commit9")
list("master", ".") mustEqual List(("dir/subdir", "commit4", true), ("dir8", "commit8", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("master", "dir/subdir") mustEqual List(("File3.md", "commit3", false), ("File4.md", "commit4", false))
list("branch", ".") mustEqual List(("dir", "commit9", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("branch", "dir/subdir") mustEqual List(("File3.md", "commit7", false), ("File4.md", "commit4", false))
assert(list("master", ".") == List(("dir/subdir", "commit4", true), ("dir8", "commit8", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("master", "dir/subdir") == List(("File3.md", "commit3", false), ("File4.md", "commit4", false)))
assert(list("branch", ".") == List(("dir", "commit9", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("branch", "dir/subdir") == List(("File3.md", "commit7", false), ("File4.md", "commit4", false)))
mergeAndCommit(git, "master", "branch", message = "merge10")
list("master", ".") mustEqual List(("dir", "commit9", true), ("dir8", "commit8", true), ("README.md", "commit6", false), ("README5.md", "commit5", false))
list("master", "dir/subdir") mustEqual List(("File3.md", "commit7", false), ("File4.md", "commit4", false))
assert(list("master", ".") == List(("dir", "commit9", true), ("dir8", "commit8", true), ("README.md", "commit6", false), ("README5.md", "commit5", false)))
assert(list("master", "dir/subdir") == List(("File3.md", "commit7", false), ("File4.md", "commit4", false)))
}
}
"getFileList subfolder multi-origin (issue #721)" should {
test("getFileList subfolder multi-origin (issue #721)") {
withTestRepository { git =>
def list(branch: String, path: String) =
JGitUtil.getFileList(git, branch, path).map(finfo => (finfo.name, finfo.message, finfo.isDirectory))
createFile(git, "master", "README.md", "body1", message = "commit1")
createFile(git, "branch", "test/text2.txt", "body2", message = "commit2")
mergeAndCommit(git, "master", "branch", message = "merge3")
list("master", "test") mustEqual List(("text2.txt", "commit2", false))
assert(list("master", "test") == List(("text2.txt", "commit2", false)))
}
}
}

View File

@@ -1,66 +1,66 @@
package gitbucket.core.util
import org.specs2.mutable._
import org.scalatest.FunSpec
class StringUtilSpec extends Specification {
class StringUtilSpec extends FunSpec {
"urlEncode" should {
"encode whitespace to %20" in {
describe("urlEncode") {
it("should encode whitespace to %20") {
val encoded = StringUtil.urlEncode("aa bb")
encoded mustEqual "aa%20bb"
assert(encoded == "aa%20bb")
}
}
"urlDecode" should {
"decode encoded string to original string" in {
describe("urlDecode") {
it("should decode encoded string to original string") {
val encoded = StringUtil.urlEncode("あいうえお")
StringUtil.urlDecode(encoded) mustEqual "あいうえお"
assert(StringUtil.urlDecode(encoded) == "あいうえお")
}
"decode en%20 to whitespace" in {
StringUtil.urlDecode("aa%20bb") mustEqual "aa bb"
it("should decode en%20 to whitespace") {
assert(StringUtil.urlDecode("aa%20bb") == "aa bb")
}
}
"splitWords" should {
"split string by whitespaces" in {
describe("splitWords") {
it("should split string by whitespaces") {
val split = StringUtil.splitWords("aa bb\tcc dd \t ee")
split mustEqual Array("aa", "bb", "cc", "dd", "ee")
assert(split === Array("aa", "bb", "cc", "dd", "ee"))
}
}
"escapeHtml" should {
"escape &, <, > and \"" in {
StringUtil.escapeHtml("<a href=\"/test\">a & b</a>") mustEqual "&lt;a href=&quot;/test&quot;&gt;a &amp; b&lt;/a&gt;"
describe("escapeHtml") {
it("should escape &, <, > and \"") {
assert(StringUtil.escapeHtml("<a href=\"/test\">a & b</a>") == "&lt;a href=&quot;/test&quot;&gt;a &amp; b&lt;/a&gt;")
}
}
"md5" should {
"generate MD5 hash" in {
StringUtil.md5("abc") mustEqual "900150983cd24fb0d6963f7d28e17f72"
describe("md5") {
it("should generate MD5 hash") {
assert(StringUtil.md5("abc") == "900150983cd24fb0d6963f7d28e17f72")
}
}
"sha1" should {
"generate SHA1 hash" in {
StringUtil.sha1("abc") mustEqual "a9993e364706816aba3e25717850c26c9cd0d89d"
describe("sha1") {
it("should generate SHA1 hash") {
assert(StringUtil.sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d")
}
}
"extractIssueId" should {
"extract '#xxx' and return extracted id" in {
StringUtil.extractIssueId("(refs #123)").toSeq mustEqual Seq("123")
describe("extractIssueId") {
it("should extract '#xxx' and return extracted id") {
assert(StringUtil.extractIssueId("(refs #123)").toSeq == Seq("123"))
}
"returns Nil from message which does not contain #xxx" in {
StringUtil.extractIssueId("this is test!").toSeq mustEqual Nil
it("should return Nil from message which does not contain #xxx") {
assert(StringUtil.extractIssueId("this is test!").toSeq == Nil)
}
}
"extractCloseId" should {
"extract 'close #xxx' and return extracted id" in {
StringUtil.extractCloseId("(close #123)").toSeq mustEqual Seq("123")
describe("extractCloseId") {
it("should extract 'close #xxx' and return extracted id") {
assert(StringUtil.extractCloseId("(close #123)").toSeq == Seq("123"))
}
"returns Nil from message which does not contain close command" in {
StringUtil.extractCloseId("(refs #123)").toSeq mustEqual Nil
it("should returns Nil from message which does not contain close command") {
assert(StringUtil.extractCloseId("(refs #123)").toSeq == Nil)
}
}
}

View File

@@ -1,35 +1,35 @@
package gitbucket.core.util
import org.specs2.mutable._
import org.scalatra.i18n.Messages
import org.scalatest.FunSpec
class ValidationsSpec extends Specification with Validations {
class ValidationsSpec extends FunSpec with Validations {
"identifier" should {
"validate id string " in {
identifier.validate("id", "aa_ZZ-00.01", null) mustEqual None
identifier.validate("id", "_aaaa", null) mustEqual Some("id starts with invalid character.")
identifier.validate("id", "-aaaa", null) mustEqual Some("id starts with invalid character.")
identifier.validate("id", "aa_ZZ#01", null) mustEqual Some("id contains invalid character.")
describe("identifier") {
it("should validate id string ") {
assert(identifier.validate("id", "aa_ZZ-00.01", null) == None)
assert(identifier.validate("id", "_aaaa", null) == Some("id starts with invalid character."))
assert(identifier.validate("id", "-aaaa", null) == Some("id starts with invalid character."))
assert(identifier.validate("id", "aa_ZZ#01", null) == Some("id contains invalid character."))
}
}
"color" should {
"validate color string " in {
describe("color") {
it("should validate color string ") {
val messages = Messages()
color.validate("color", "#88aaff", messages) mustEqual None
color.validate("color", "#gghhii", messages) mustEqual Some("color must be '#[0-9a-fA-F]{6}'.")
assert(color.validate("color", "#88aaff", messages) == None)
assert(color.validate("color", "#gghhii", messages) == Some("color must be '#[0-9a-fA-F]{6}'."))
}
}
"date" should {
describe("date") {
// "validate date string " in {
// date().validate("date", "2013-10-05", Map[String, String]()) mustEqual Nil
// date().validate("date", "2013-10-5" , Map[String, String]()) mustEqual List(("date", "date must be '\\d{4}-\\d{2}-\\d{2}'."))
// }
"convert date string " in {
it("should convert date string ") {
val result = date().convert("2013-10-05", null)
new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(result) mustEqual "2013-10-05 00:00:00"
assert(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(result) == "2013-10-05 00:00:00")
}
}

View File

@@ -5,74 +5,76 @@ import java.util.Date
import gitbucket.core.model.Account
import gitbucket.core.service.{SystemSettingsService, RequestCache}
import gitbucket.core.controller.Context
import org.specs2.mutable._
import org.specs2.mock.Mockito
import SystemSettingsService.SystemSettings
import javax.servlet.http.HttpServletRequest
import play.twirl.api.Html
import org.scalatest.FunSpec
import org.scalatest.mock.MockitoSugar
import org.mockito.Mockito._
class AvatarImageProviderSpec extends Specification with Mockito {
class AvatarImageProviderSpec extends FunSpec with MockitoSugar {
val request = mock[HttpServletRequest]
request.getRequestURL returns new StringBuffer("http://localhost:8080/path.html")
request.getRequestURI returns "/path.html"
request.getContextPath returns ""
when(request.getRequestURL).thenReturn(new StringBuffer("http://localhost:8080/path.html"))
when(request.getRequestURI).thenReturn("/path.html")
when(request.getContextPath).thenReturn("")
"getAvatarImageHtml" should {
"show Gravatar image for no image account if gravatar integration is enabled" in {
describe("getAvatarImageHtml") {
it("should show Gravatar image for no image account if gravatar integration is enabled") {
implicit val context = Context(createSystemSettings(true), None, request)
val provider = new AvatarImageProviderImpl(Some(createAccount(None)))
provider.toHtml("user", 32).toString mustEqual
"<img src=\"https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s=32&d=retro&r=g\" class=\"avatar\" style=\"width: 32px; height: 32px;\" />"
assert(provider.toHtml("user", 32).toString ==
"<img src=\"https://www.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s=32&d=retro&r=g\" class=\"avatar\" style=\"width: 32px; height: 32px;\" />")
}
"show uploaded image even if gravatar integration is enabled" in {
it("should show uploaded image even if gravatar integration is enabled") {
implicit val context = Context(createSystemSettings(true), None, request)
val provider = new AvatarImageProviderImpl(Some(createAccount(Some("icon.png"))))
provider.toHtml("user", 32).toString mustEqual
"<img src=\"/user/_avatar\" class=\"avatar\" style=\"width: 32px; height: 32px;\" />"
assert(provider.toHtml("user", 32).toString ==
"<img src=\"/user/_avatar\" class=\"avatar\" style=\"width: 32px; height: 32px;\" />")
}
"show local image for no image account if gravatar integration is disabled" in {
it("should show local image for no image account if gravatar integration is disabled") {
implicit val context = Context(createSystemSettings(false), None, request)
val provider = new AvatarImageProviderImpl(Some(createAccount(None)))
provider.toHtml("user", 32).toString mustEqual
"<img src=\"/user/_avatar\" class=\"avatar\" style=\"width: 32px; height: 32px;\" />"
assert(provider.toHtml("user", 32).toString ==
"<img src=\"/user/_avatar\" class=\"avatar\" style=\"width: 32px; height: 32px;\" />")
}
"show Gravatar image for specified mail address if gravatar integration is enabled" in {
it("should show Gravatar image for specified mail address if gravatar integration is enabled") {
implicit val context = Context(createSystemSettings(true), None, request)
val provider = new AvatarImageProviderImpl(None)
provider.toHtml("user", 20, "hoge@hoge.com").toString mustEqual
"<img src=\"https://www.gravatar.com/avatar/4712f9b0e63f56ad952ad387eaa23b9c?s=20&d=retro&r=g\" class=\"avatar-mini\" style=\"width: 20px; height: 20px;\" />"
assert(provider.toHtml("user", 20, "hoge@hoge.com").toString ==
"<img src=\"https://www.gravatar.com/avatar/4712f9b0e63f56ad952ad387eaa23b9c?s=20&d=retro&r=g\" class=\"avatar-mini\" style=\"width: 20px; height: 20px;\" />")
}
"show unknown image for unknown user if gravatar integration is enabled" in {
it("should show unknown image for unknown user if gravatar integration is enabled") {
implicit val context = Context(createSystemSettings(true), None, request)
val provider = new AvatarImageProviderImpl(None)
provider.toHtml("user", 20).toString mustEqual
"<img src=\"/_unknown/_avatar\" class=\"avatar-mini\" style=\"width: 20px; height: 20px;\" />"
assert(provider.toHtml("user", 20).toString ==
"<img src=\"/_unknown/_avatar\" class=\"avatar-mini\" style=\"width: 20px; height: 20px;\" />")
}
"show unknown image for specified mail address if gravatar integration is disabled" in {
it("should show unknown image for specified mail address if gravatar integration is disabled") {
implicit val context = Context(createSystemSettings(false), None, request)
val provider = new AvatarImageProviderImpl(None)
provider.toHtml("user", 20, "hoge@hoge.com").toString mustEqual
"<img src=\"/_unknown/_avatar\" class=\"avatar-mini\" style=\"width: 20px; height: 20px;\" />"
assert(provider.toHtml("user", 20, "hoge@hoge.com").toString ==
"<img src=\"/_unknown/_avatar\" class=\"avatar-mini\" style=\"width: 20px; height: 20px;\" />")
}
"add tooltip if it's enabled" in {
it("should add tooltip if it's enabled") {
implicit val context = Context(createSystemSettings(false), None, request)
val provider = new AvatarImageProviderImpl(None)
provider.toHtml("user", 20, "hoge@hoge.com", true).toString mustEqual
"<img src=\"/_unknown/_avatar\" class=\"avatar-mini\" style=\"width: 20px; height: 20px;\" data-toggle=\"tooltip\" title=\"user\"/>"
assert(provider.toHtml("user", 20, "hoge@hoge.com", true).toString ==
"<img src=\"/_unknown/_avatar\" class=\"avatar-mini\" style=\"width: 20px; height: 20px;\" data-toggle=\"tooltip\" title=\"user\"/>")
}
}
@@ -102,6 +104,7 @@ class AvatarImageProviderSpec extends Specification with Mockito {
notification = false,
activityLogLimit = None,
ssh = false,
sshHost = None,
sshPort = None,
useSMTP = false,
smtp = None,

View File

@@ -1,38 +1,59 @@
package gitbucket.core.view
import org.specs2.mutable._
import org.scalatest.FunSpec
class HelpersSpec extends Specification {
class HelpersSpec extends FunSpec {
import helpers._
"detect and render links" should {
describe("detect and render links") {
"pass identical string when no link is present" in {
it("should pass identical string when no link is present") {
val before = "Description"
val after = detectAndRenderLinks(before).toString()
after mustEqual before
assert(after == before)
}
"convert a single link" in {
it("should convert a single link") {
val before = "http://example.com"
val after = detectAndRenderLinks(before).toString()
after mustEqual """<a href="http://example.com">http://example.com</a>"""
assert(after == """<a href="http://example.com">http://example.com</a>""")
}
"convert a single link within trailing text" in {
it("should convert a single link within trailing text") {
val before = "Example Project. http://example.com"
val after = detectAndRenderLinks(before).toString()
after mustEqual """Example Project. <a href="http://example.com">http://example.com</a>"""
assert(after == """Example Project. <a href="http://example.com">http://example.com</a>""")
}
"convert a mulitple links within text" in {
it("should convert a mulitple links within text") {
val before = "Example Project. http://example.com. (See also https://github.com/)"
val after = detectAndRenderLinks(before).toString()
after mustEqual """Example Project. <a href="http://example.com">http://example.com</a>. (See also <a href="https://github.com/">https://github.com/</a>)"""
assert(after == """Example Project. <a href="http://example.com">http://example.com</a>. (See also <a href="https://github.com/">https://github.com/</a>)""")
}
it("should properly escape html metacharacters") {
val before = "<>&"
val after = detectAndRenderLinks(before).toString()
assert(after == """&lt;&gt;&amp;""")
}
it("should escape html metacharacters adjacent to a link") {
val before = "<http://example.com>"
val after = detectAndRenderLinks(before).toString()
assert(after == """&lt;<a href="http://example.com">http://example.com</a>&gt;""")
}
it("should stop link recognition at a metacharacter") {
val before = "http://exa<mple.com"
val after = detectAndRenderLinks(before).toString()
assert(after == """<a href="http://exa">http://exa</a>&lt;mple.com""")
}
it("should make sure there are no double quotes in the href attribute") {
val before = "http://exa\"mple.com"
val after = detectAndRenderLinks(before).toString()
assert(after == """<a href="http://exa&quot;mple.com">http://exa"mple.com</a>""")
}
}
}

View File

@@ -1,92 +1,92 @@
package gitbucket.core.view
import org.specs2.mutable._
import org.scalatest.FunSpec
class MarkdownSpec extends Specification {
class MarkdownSpec extends FunSpec {
import Markdown._
"generateAnchorName" should {
"convert whitespace characters to hyphens" in {
describe("generateAnchorName") {
it("should convert whitespace characters to hyphens") {
val before = "foo bar baz"
val after = generateAnchorName(before)
after mustEqual "foo-bar-baz"
assert(after == "foo-bar-baz")
}
"normalize characters with diacritics" in {
it("should normalize characters with diacritics") {
val before = "Dónde estará mi vida"
val after = generateAnchorName(before)
after mustEqual "do%cc%81nde-estara%cc%81-mi-vida"
assert(after == "do%cc%81nde-estara%cc%81-mi-vida")
}
"omit special characters" in {
it("should omit special characters") {
val before = "foo!bar@baz>9000"
val after = generateAnchorName(before)
after mustEqual "foo%21bar%40baz%3e9000"
assert(after == "foo%21bar%40baz%3e9000")
}
}
"escapeTaskList" should {
"convert '- [ ] ' to '* task: :'" in {
describe("escapeTaskList") {
it("should convert '- [ ] ' to '* task: :'") {
val before = "- [ ] aaaa"
val after = escapeTaskList(before)
after mustEqual "* task: : aaaa"
assert(after == "* task: : aaaa")
}
"convert ' - [ ] ' to ' * task: :'" in {
it("should convert ' - [ ] ' to ' * task: :'") {
val before = " - [ ] aaaa"
val after = escapeTaskList(before)
after mustEqual " * task: : aaaa"
assert(after == " * task: : aaaa")
}
"convert only first '- [ ] '" in {
it("should convert only first '- [ ] '") {
val before = " - [ ] aaaa - [ ] bbb"
val after = escapeTaskList(before)
after mustEqual " * task: : aaaa - [ ] bbb"
assert(after == " * task: : aaaa - [ ] bbb")
}
"convert '- [x] ' to '* task:x:'" in {
it("should convert '- [x] ' to '* task:x:'") {
val before = " - [x] aaaa"
val after = escapeTaskList(before)
after mustEqual " * task:x: aaaa"
assert(after == " * task:x: aaaa")
}
"convert multi lines" in {
it("should convert multi lines") {
val before = """
tasks
- [x] aaaa
- [ ] bbb
"""
val after = escapeTaskList(before)
after mustEqual """
assert(after == """
tasks
* task:x: aaaa
* task: : bbb
"""
""")
}
"no convert if inserted before '- [ ] '" in {
it("should not convert if inserted before '- [ ] '") {
val before = " a - [ ] aaaa"
val after = escapeTaskList(before)
after mustEqual " a - [ ] aaaa"
assert(after == " a - [ ] aaaa")
}
"no convert '- [] '" in {
it("should not convert '- [] '") {
val before = " - [] aaaa"
val after = escapeTaskList(before)
after mustEqual " - [] aaaa"
assert(after == " - [] aaaa")
}
"no convert '- [ ]a'" in {
it("should not convert '- [ ]a'") {
val before = " - [ ]a aaaa"
val after = escapeTaskList(before)
after mustEqual " - [ ]a aaaa"
assert(after == " - [ ]a aaaa")
}
"no convert '-[ ] '" in {
it("should not convert '-[ ] '") {
val before = " -[ ] aaaa"
val after = escapeTaskList(before)
after mustEqual " -[ ] aaaa"
assert(after == " -[ ] aaaa")
}
}
}

View File

@@ -1,68 +1,68 @@
package gitbucket.core.view
import gitbucket.core.util.ControlUtil
import org.specs2.mutable._
import ControlUtil._
import org.scalatest.FunSpec
class PaginationSpec extends Specification {
class PaginationSpec extends FunSpec {
"max" should {
"return max page number" in {
describe("max") {
it("should return max page number") {
val pagination = Pagination(1, 100, 10, 6)
pagination.max mustEqual 10
assert(pagination.max == 10)
}
}
"omitLeft and omitRight" should {
"return true if pagination links at their side will be omitted" in {
describe("omitLeft and omitRight") {
it("should return true if pagination links at their side will be omitted") {
defining(Pagination(1, 100, 10, 6)){ pagination =>
pagination.omitLeft mustEqual false
pagination.omitRight mustEqual true
assert(pagination.omitLeft == false)
assert(pagination.omitRight == true)
}
defining(Pagination(9, 100, 10, 6)){ pagination =>
pagination.omitLeft mustEqual true
pagination.omitRight mustEqual false
assert(pagination.omitLeft == true)
assert(pagination.omitRight == false)
}
}
}
"visibleFor" should {
"return true for visible pagination links" in {
describe("visibleFor") {
it("should return true for visible pagination links") {
defining(Pagination(1, 100, 10, 6)){ pagination =>
pagination.visibleFor(1) mustEqual true
pagination.visibleFor(2) mustEqual true
pagination.visibleFor(3) mustEqual true
pagination.visibleFor(4) mustEqual true
pagination.visibleFor(5) mustEqual true
pagination.visibleFor(6) mustEqual false
pagination.visibleFor(7) mustEqual false
pagination.visibleFor(8) mustEqual false
pagination.visibleFor(9) mustEqual false
pagination.visibleFor(10) mustEqual true
assert(pagination.visibleFor(1) == true)
assert(pagination.visibleFor(2) == true)
assert(pagination.visibleFor(3) == true)
assert(pagination.visibleFor(4) == true)
assert(pagination.visibleFor(5) == true)
assert(pagination.visibleFor(6) == false)
assert(pagination.visibleFor(7) == false)
assert(pagination.visibleFor(8) == false)
assert(pagination.visibleFor(9) == false)
assert(pagination.visibleFor(10) == true)
}
defining(Pagination(5, 100, 10, 6)){ pagination =>
pagination.visibleFor(1) mustEqual true
pagination.visibleFor(2) mustEqual false
pagination.visibleFor(3) mustEqual false
pagination.visibleFor(4) mustEqual true
pagination.visibleFor(5) mustEqual true
pagination.visibleFor(6) mustEqual true
pagination.visibleFor(7) mustEqual false
pagination.visibleFor(8) mustEqual false
pagination.visibleFor(9) mustEqual false
pagination.visibleFor(10) mustEqual true
assert(pagination.visibleFor(1) == true)
assert(pagination.visibleFor(2) == false)
assert(pagination.visibleFor(3) == false)
assert(pagination.visibleFor(4) == true)
assert(pagination.visibleFor(5) == true)
assert(pagination.visibleFor(6) == true)
assert(pagination.visibleFor(7) == false)
assert(pagination.visibleFor(8) == false)
assert(pagination.visibleFor(9) == false)
assert(pagination.visibleFor(10) == true)
}
defining(Pagination(8, 100, 10, 6)){ pagination =>
pagination.visibleFor(1) mustEqual true
pagination.visibleFor(2) mustEqual false
pagination.visibleFor(3) mustEqual false
pagination.visibleFor(4) mustEqual false
pagination.visibleFor(5) mustEqual false
pagination.visibleFor(6) mustEqual true
pagination.visibleFor(7) mustEqual true
pagination.visibleFor(8) mustEqual true
pagination.visibleFor(9) mustEqual true
pagination.visibleFor(10) mustEqual true
assert(pagination.visibleFor(1) == true)
assert(pagination.visibleFor(2) == false)
assert(pagination.visibleFor(3) == false)
assert(pagination.visibleFor(4) == false)
assert(pagination.visibleFor(5) == false)
assert(pagination.visibleFor(6) == true)
assert(pagination.visibleFor(7) == true)
assert(pagination.visibleFor(8) == true)
assert(pagination.visibleFor(9) == true)
assert(pagination.visibleFor(10) == true)
}
}
}