Compare commits

..

134 Commits
3.8 ... 3.10

Author SHA1 Message Date
Naoki Takezoe
990d082422 3.10 release 2015-12-30 01:18:13 +09:00
Naoki Takezoe
dfe3fbc02f Bump up H2 to 1.4.190 2015-12-29 01:37:52 +09:00
Naoki Takezoe
c077dd0046 Rename logback-test.xml to logback-dev.xml 2015-12-27 14:40:54 +09:00
Naoki Takezoe
9cc6beead7 (refs #990)Provides path for raw contents instead of ?raw=true 2015-12-26 22:48:28 +09:00
Naoki Takezoe
92f6792ad9 Adjust format 2015-12-26 21:20:09 +09:00
Naoki Takezoe
25031eadfb Merge pull request #1017 from swaldman/minor-c3p0-tweaks
Upgrade c3p0 and tweak default config to avoid Tomcat issues
2015-12-26 17:00:33 +09:00
Naoki Takezoe
a7024615e1 Merge pull request #1020 from P1tt187/sshd-RSA-keyprovider
update sshd dependency, rsa keyprovider as default
2015-12-26 16:59:30 +09:00
Naoki Takezoe
8191a4bedb (refs #1004)Fix edit webhook link url 2015-12-26 03:25:49 +09:00
Naoki Takezoe
7e1f5aa353 Fix branch control dropdown 2015-12-25 10:26:30 +09:00
Fabian Markert
44e161f6ec update version 2015-12-24 17:16:20 +01:00
Fabian Markert
7bd17ed038 update sshd dependency
migrate to new sshd api
change the default key provider algorithm to RSA
2015-12-24 17:11:40 +01:00
Naoki Takezoe
a47404130b Don't break lines in Wiki or markdown files on the repository viewer 2015-12-24 02:58:39 +09:00
Naoki Takezoe
669949aa8c Update docs 2015-12-23 23:30:10 +09:00
Naoki Takezoe
c773bb90f6 Update docs 2015-12-23 23:26:59 +09:00
Naoki Takezoe
18f525523b Update docs 2015-12-23 23:06:22 +09:00
Naoki Takezoe
3bfd228df1 Update docs 2015-12-23 23:03:32 +09:00
Naoki Takezoe
c072ab2dc5 Update docs 2015-12-23 22:44:20 +09:00
Naoki Takezoe
53e06c5e86 (refs #1018)Move to xsbt-web-plugin from scalatra-sbt 2015-12-23 21:37:25 +09:00
Steve Waldman
b0a749bdc1 Upgrade c3p0 and tweak default config to avoid Tomcat issues
Adds a standard typesafe-config reference.conf file that sets c3p0
config to prevents Tomcat memory leaks on hot-redeploy/shutdown.

See
   http://www.mchange.com/projects/c3p0/#configuring_to_avoid_memory_leaks_on_redeploy
   http://www.mchange.com/projects/c3p0/#tomcat-specific

This new config can be overridden in an application.conf or
c3p0.properties file.

( See http://www.mchange.com/projects/c3p0/#configuration_precedence )

Updates c3p0 version to c3p0-0.9.5.2, which fixes that leads to
unnecessary String allocation when logging via slf4j.
2015-12-21 22:51:12 -08:00
Naoki Takezoe
809443a46f Zip logback.xml into the root of gitbucket.war 2015-12-22 07:59:47 +09:00
Naoki Takezoe
59ef44dd71 Update logging configuration 2015-12-22 07:59:15 +09:00
Naoki Takezoe
d77973a29e Remove IDE plugins and add logback-classic 2015-12-22 07:58:30 +09:00
Naoki Takezoe
be6bc16d3d Fix global header style 2015-12-21 15:14:51 +09:00
Naoki Takezoe
407e926abe Fix styles in file editing form in the repository viewer 2015-12-21 15:14:21 +09:00
Naoki Takezoe
117655b011 (refs #1002)Upgrade markedj to 1.0.6-SNAPSHOT to solve IndexOutOfBoundsException in the table 2015-12-19 12:56:34 +09:00
Naoki Takezoe
2ea4a5a7ef (refs #1007)Fix max length constraint for repository owner and name at the repository creation form 2015-12-18 01:46:25 +09:00
Naoki Takezoe
30b0279efe (refs #1005)Fix typo 2015-12-18 01:40:32 +09:00
Naoki Takezoe
7511701bb2 Merge pull request #1009 from team-lab/fix/webhook-push-pusher
(fixes #1008) pusher of webhook push event is not same as github's it
2015-12-18 01:32:18 +09:00
Naoki Takezoe
a919ae3430 Merge pull request #1010 from team-lab/suppot-legacy-statuses-api
support legacy route of List Statuses for a specific Ref api
2015-12-18 01:31:28 +09:00
nazoking
c30ee24f8d support legacy route of List Statuses for a specific Ref api 2015-12-16 21:54:47 +09:00
nazoking
10b5de571e (fixes #1008) pusher of webhook push event is not same as github's it 2015-12-16 21:41:14 +09:00
Naoki Takezoe
c6f4ec7250 Fix header-link style 2015-12-15 19:51:16 +09:00
Naoki Takezoe
b229058726 Fix fork button group style 2015-12-15 19:39:18 +09:00
Naoki Takezoe
21363794b6 Merge pull request #999 from gitbucket/css_bootstrap3
Move to Bootstrap3 with GitHub theme
2015-12-15 15:58:54 +09:00
Naoki Takezoe
1754d37ac0 Fix style of circle icons 2015-12-15 15:13:54 +09:00
Naoki Takezoe
1c36b185ec Fix width and font of the dropzone 2015-12-15 14:56:24 +09:00
Naoki Takezoe
1a4b94e5df Fix modal dialog 2015-12-15 11:46:47 +09:00
Naoki Takezoe
31bd8e1741 Fix button size 2015-12-14 18:48:22 +09:00
Naoki Takezoe
e8824ad1ac Imporve styles for mobile 2015-12-14 01:56:15 +09:00
Naoki Takezoe
17636a5ecf Fixup 2015-12-14 01:11:42 +09:00
Naoki Takezoe
8f36ff60de Use bootstrap panel instead of custom box styles 2015-12-14 00:16:18 +09:00
Naoki Takezoe
7ad605afba Fix style of issue comment form and more 2015-12-13 23:59:42 +09:00
Naoki Takezoe
f080e8693e Fix right margin of account search box 2015-12-12 13:20:05 +09:00
Naoki Takezoe
e27bf7868f Tweak pagination padding 2015-12-10 17:17:18 +09:00
Naoki Takezoe
db55fe9ff3 Fixup 2015-12-10 16:02:58 +09:00
Naoki Takezoe
de4db0764c Use Bootstrap panel instead of custom CSS 2015-12-10 14:53:19 +09:00
Naoki Takezoe
9d644b4625 Fix buttons of webhook list 2015-12-10 14:04:44 +09:00
Naoki Takezoe
354dc27e84 Use Bootstrap panel instead of custom CSS 2015-12-10 12:13:22 +09:00
Naoki Takezoe
1c639474d3 Fix milestone list and progress bar 2015-12-10 12:03:22 +09:00
Naoki Takezoe
4d8b47732e Remove unused bootstrap files 2015-12-10 02:03:46 +09:00
Naoki Takezoe
8650abf679 Fix milestone creation / editing form 2015-12-10 02:00:42 +09:00
Naoki Takezoe
27dcc2ef48 Fix table vertical border 2015-12-10 01:16:17 +09:00
Naoki Takezoe
94a12cd28c Fix label editing form 2015-12-09 19:16:34 +09:00
Naoki Takezoe
a982b64fd4 Use Bootstrap panels instead of GitBucket original styles 2015-12-09 18:29:41 +09:00
Naoki Takezoe
ee5b4bbf2e Fix styles 2015-12-09 02:48:10 +09:00
Naoki Takezoe
d09ddd8d07 Fix tables and some pages 2015-12-08 19:21:08 +09:00
Naoki Takezoe
c7bf47820c Use customized Bootstrap3 theme 2015-12-08 09:33:22 +09:00
Naoki Takezoe
4dfb01d59e Fix typo 2015-12-08 02:35:52 +09:00
Naoki Takezoe
54bef6505f Fix issue search box 2015-12-08 02:26:11 +09:00
Naoki Takezoe
2aa71ed217 Fix borderd table style 2015-12-08 01:59:31 +09:00
Naoki Takezoe
d90938421f Fix ZeroClipboard button 2015-12-07 23:19:30 +09:00
Naoki Takezoe
e4992bbd17 Fix styles of repository setting pages 2015-12-07 16:56:38 +09:00
Naoki Takezoe
7853b22522 Fix sidebar style 2015-12-07 16:01:03 +09:00
Naoki Takezoe
9475215fa6 Fix styles of plugins page 2015-12-07 15:20:06 +09:00
Naoki Takezoe
8622bbd2ac Fix styles of account and group creation page 2015-12-07 14:38:18 +09:00
Naoki Takezoe
0cdcc252e0 Merge branch 'master' into css_bootstrap3
Conflicts:
	src/main/twirl/gitbucket/core/account/edit.scala.html
2015-12-07 08:33:22 +09:00
Naoki Takezoe
9477cca8e8 Fix box style 2015-12-05 19:54:32 +09:00
Naoki Takezoe
176e427058 Update README.md for 3.9 release 2015-12-05 19:48:44 +09:00
Naoki Takezoe
1bf26cacb9 Update version to 3.9.0 2015-12-05 19:46:29 +09:00
Naoki Takezoe
38e8247483 Fix styles of system administration page 2015-12-05 19:45:07 +09:00
Naoki Takezoe
44aa12108c Fix styles of the repository creation page and more 2015-12-05 14:54:50 +09:00
Naoki Takezoe
dd73405aa9 Fix styles 2015-12-05 12:41:55 +09:00
Naoki Takezoe
6710393a53 Merge branch 'master' into css_bootstrap3 2015-12-05 11:53:15 +09:00
Naoki Takezoe
711aee1c8b Fix markedj version as 1.0.5 2015-12-05 03:54:51 +09:00
Naoki Takezoe
51be1048d5 (refs #987)Remove user related data when users delete themselves 2015-12-05 03:45:26 +09:00
Naoki Takezoe
50166f04d8 Disable auto completion in the select user field 2015-12-05 03:43:57 +09:00
Naoki Takezoe
7eb6fea08b Fix typo in comment 2015-12-05 03:12:21 +09:00
Naoki Takezoe
8e3c054da4 Merge pull request #983 from team-lab/feature/webhook-review-comment
Send webhook on create pull request review comment
2015-12-05 01:14:54 +09:00
nazoking
5249b67df1 Merge branch 'master' into feature/webhook-review-comment 2015-12-03 20:09:46 +09:00
Naoki Takezoe
583830c1c2 Fix styles 2015-12-01 02:47:09 +09:00
Naoki Takezoe
bb75f1c034 Merge branch 'css_bootstrap3' of https://github.com/takezoe/gitbucket into css_bootstrap3 2015-12-01 02:12:08 +09:00
Naoki Takezoe
b7a9236283 Merge branch 'master' of https://github.com/takezoe/gitbucket into css_bootstrap3 2015-12-01 01:37:14 +09:00
Naoki Takezoe
7e0e172d37 Fix fork button 2015-11-30 23:22:16 +09:00
Naoki Takezoe
ea37a34476 Fix styles of issues and pull requests 2015-11-30 01:16:27 +09:00
Naoki Takezoe
fb5564de07 Fix diff styles 2015-11-27 12:53:10 +09:00
Naoki Takezoe
cead7e9e4e Fix commit list style 2015-11-27 12:37:26 +09:00
Naoki Takezoe
7cf23b6c56 Fix pull request creation form style 2015-11-27 12:37:16 +09:00
Naoki Takezoe
1080821862 Fix issue styles 2015-11-27 10:16:17 +09:00
Naoki Takezoe
0bf843ce2a Fix styles 2015-11-27 10:10:15 +09:00
Naoki Takezoe
b8bd8f5512 Fix branch list styles 2015-11-27 09:43:17 +09:00
Naoki Takezoe
12fba30ef8 Fix styles 2015-11-25 10:44:55 +09:00
Naoki Takezoe
dc5cb74f4d Merge branch 'master' of https://github.com/takezoe/gitbucket 2015-11-18 01:48:47 +09:00
Naoki Takezoe
446a524b5a Upgrade scalatra-forms to 0.2.0 to solve performance issue 2015-11-18 01:48:40 +09:00
Naoki Takezoe
121110aede Fix markdown header style 2015-11-17 23:19:08 +09:00
Naoki Takezoe
c76a8562ea Merge pull request #995 from uli-heller/patch-2
extensibility is probably the right word?
2015-11-17 19:28:00 +09:00
uli-heller
495d8069f5 extensibility is probably the right word? 2015-11-17 09:51:50 +01:00
Naoki Takezoe
70af6d0c35 Update README.md 2015-11-17 16:04:58 +09:00
Naoki Takezoe
abfc6eea1e Fix repository setting pages presentation 2015-11-17 01:52:14 +09:00
Naoki Takezoe
fd2f3e252c Fix webhook editing presentation and performance issue 2015-11-17 01:51:55 +09:00
Naoki Takezoe
3b6d0065a2 Once delete editHooks.scala.html to rename 2015-11-17 01:48:13 +09:00
Naoki Takezoe
7ffe2ab864 Fix global header style 2015-11-15 12:54:34 +09:00
Naoki Takezoe
2f6febb748 Adjust styles for Bootstrap3 2015-11-15 02:27:55 +09:00
Naoki Takezoe
092c897150 Fix sidebar style 2015-11-15 02:05:24 +09:00
Naoki Takezoe
30f072f925 Merge pull request #992 from team-lab/fix/lost-webhook-events-on-rename-repository
fix lost web hook events on repository renamed.
2015-11-14 15:02:41 +09:00
nazoking
72d0282613 Merge branch 'master' into feature/webhook-review-comment 2015-11-14 13:27:40 +09:00
nazoking
a56f766497 fix lost web hook events on repository renamed. 2015-11-14 13:16:13 +09:00
Naoki Takezoe
31b5583896 Fix header styles 2015-11-13 02:32:04 +09:00
Naoki Takezoe
7c95bfc77e Merge branch 'master' into css_bootstrap3 2015-11-12 03:45:00 +09:00
Naoki Takezoe
c6804fe589 Add bootstrap-theme-github 2015-11-12 03:44:37 +09:00
Naoki Takezoe
fc41e282ce Merge pull request #973 from gitbucket/commit_comment_count
Count COMMIT_COMMENT as issue comment
2015-11-10 11:48:20 +09:00
Naoki Takezoe
d3b21a4e39 Drop PULL_REQUEST column from COMMIT_COMMENT table 2015-11-10 11:35:59 +09:00
Naoki Takezoe
7b6d9850a2 Moving to Bootstrap3 2015-11-10 11:06:11 +09:00
nazoking
84d97817e3 add comment 2015-11-09 13:57:37 +09:00
nazoking
2c6d2176bb fix space 2015-11-09 13:57:26 +09:00
nazoking
7840d28ed2 add webhook pull-request-review-comment 2015-11-09 12:20:07 +09:00
Naoki Takezoe
d4fd2e6cd6 Remove bottom margin of inline comment in diff view 2015-11-09 01:20:44 +09:00
Naoki Takezoe
70e8385246 (refs #974)Not hide add-comment icon when adding comment form is active. 2015-11-09 01:15:04 +09:00
Naoki Takezoe
3fd23f1749 Fix markdown preview style 2015-11-08 22:26:43 +09:00
Naoki Takezoe
a3e3aea4e0 Fix styles 2015-11-08 19:08:05 +09:00
Naoki Takezoe
dcfb8ad8eb (refs #976)Enable GFM line breaks 2015-11-08 18:03:44 +09:00
Naoki Takezoe
35f82e91bc (refs #976)Upgrade markedj to 1.0.5-SNAPSHOT 2015-11-08 17:49:25 +09:00
Naoki Takezoe
f1533cb168 Count COMMIT_COMMENT as issue comment 2015-11-08 04:06:14 +09:00
Naoki Takezoe
763fbec0ca Update README.md 2015-11-08 02:58:54 +09:00
Naoki Takezoe
111c893d94 Merge pull request #968 from justinpitts/master
Correct typo on system administration page.
2015-11-07 03:35:39 +09:00
Naoki Takezoe
80c38a45c5 Merge pull request #941 from team-lab/feature/webhook-scope
feature/can select event trigger of webhook.
2015-11-07 03:35:09 +09:00
nazoking
548860607b update migration version to 3.9 from 3.8 2015-11-06 14:52:36 +09:00
nazoking
66b5fe7337 Merge branch 'master' into feature/webhook-scope 2015-11-06 14:48:03 +09:00
Justin Pitts
2b2a117912 Correct typo on system administration page. 2015-11-04 08:51:48 -05:00
Naoki Takezoe
39a10fd167 Update README.md 2015-11-04 02:00:14 +09:00
Naoki Takezoe
93f8dbd42a Fix markedj version 2015-11-02 08:58:29 +09:00
Naoki Takezoe
4f280d0df8 Fix blockquote font size 2015-11-02 02:33:06 +09:00
Naoki Takezoe
5e9ff3278a Fix the default number of displayed repositories 2015-11-02 00:32:46 +09:00
Naoki Takezoe
b4b68f0e17 Update README.md 2015-10-31 11:55:16 +09:00
nazoking
a08a212fdc feature/can select webhook events 2015-10-13 18:48:02 +09:00
142 changed files with 23949 additions and 12353 deletions

View File

@@ -1,8 +1,7 @@
GitBucket [![Gitter chat](https://badges.gitter.im/gitbucket/gitbucket.png)](https://gitter.im/gitbucket/gitbucket) [![Build Status](https://travis-ci.org/gitbucket/gitbucket.svg?branch=master)](https://travis-ci.org/gitbucket/gitbucket)
=========
GitBucket is the easily installable GitHub clone powered by Scala.
GitBucket is a GitHub clone powered by Scala which has easy installation and high extensibility.
Features
--------
@@ -40,7 +39,7 @@ or you can start GitBucket by `java -jar gitbucket.war` without servlet containe
- --host=[HOSTNAME]
- --gitbucket.home=[DATA_DIR]
To upgrade GitBucket, only replace gitbucket.war. All GitBucket data is stored in HOME/.gitbucket. So if you want to back up GitBucket data, copy this directory to the other disk.
To upgrade GitBucket, only replace gitbucket.war after stop GitBucket. All GitBucket data is stored in HOME/.gitbucket. So if you want to back up GitBucket data, copy this directory to the other disk.
For Installation on Windows Server with IIS see [this wiki page](https://github.com/gitbucket/gitbucket/wiki/Installation-on-IIS-and-Helicontech-Zoo)
@@ -81,6 +80,7 @@ GitBucket has the plug-in system to extend GitBucket from outside of GitBucket.
- [gitbucket-h2-backup-plugin](https://github.com/gitbucket-plugins/gitbucket-h2-backup-plugin)
- [gitbucket-desktopnotify-plugin](https://github.com/yoshiyoshifujii/gitbucket-desktopnotify-plugin)
- [gitbucket-commitgraphs-plugin](https://github.com/yoshiyoshifujii/gitbucket-commitgraphs-plugin)
- [gitbucket-asciidoctor-plugin](https://github.com/lefou/gitbucket-asciidoctor-plugin)
You can find community plugins other than them at [gitbucket community plugins](http://gitbucket-plugins.github.io/).
@@ -95,8 +95,19 @@ Support
Release Notes
--------
### 3.10 - 30 Dec 2015
- Move to Bootstrap3
- New URL for raw contents (`raw/master/doc/activity.md` instead of `blob/master/doc/activity.md?raw=true`)
- Update xsbt-web-pligin
- Update H2 database
### 3.9 - 5 Dec 2015
- GFM inline breaks support in Markdown
- WebHook on create review comment is available
- WebHook event trigger is selectable
### 3.8 - 31 Oct 2015
- Moved to organization
- Moved to GitHub organization
- Omit diff view for large differences
- Repository creation API
- Render url as link in repository description

60
doc/authenticator.md Normal file
View File

@@ -0,0 +1,60 @@
Authentication in Controller
========
GitBucket provides many [authenticators](https://github.com/gitbucket/gitbucket/blob/master/src/main/scala/gitbucket/core/util/Authenticator.scala) to access controlling in the controller.
For example, in the case of `RepositoryViwerController`,
it references three authenticators: `ReadableUsersAuthenticator`, `ReferrerAuthenticator` and `CollaboratorsAuthenticator`.
```scala
class RepositoryViewerController extends RepositoryViewerControllerBase
with RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with CommitStatusService
with WebHookPullRequestService with WebHookPullRequestReviewCommentService
trait RepositoryViewerControllerBase extends ControllerBase {
self: RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with CommitStatusService
with WebHookPullRequestService with WebHookPullRequestReviewCommentService =>
...
```
Authenticators provides a method to add guard to actions in the controller:
- `ReadableUsersAuthenticator` provides `readableUsersOnly` method
- `ReferrerAuthenticator` provides `referrersOnly` method
- `CollaboratorsAuthenticator` provides `collaboratorsOnly` method
These methods are available in each actions as below:
```scala
// Allows only the repository owner (or manager for group repository) and administrators.
get("/:owner/:repository/tree/*")(referrersOnly { repository =>
...
})
// Allows only collaborators and administrators.
get("/:owner/:repository/new/*")(collaboratorsOnly { repository =>
...
})
// Allows only signed in users which can access the repository.
post("/:owner/:repository/commit/:id/comment/new", commentForm)(readableUsersOnly { (form, repository) =>
...
})
```
Currently, GitBucket provides below authenticators:
|Trait | Method | Description |
|--------------------------|-----------------|--------------------------------------------------------------------------------------|
|OneselfAuthenticator |oneselfOnly |Allows only oneself and administrators. |
|OwnerAuthenticator |ownerOnly |Allows only the repository owner and administrators. |
|UsersAuthenticator |usersOnly |Allows only signed in users. |
|AdminAuthenticator |adminOnly |Allows only administrators. |
|CollaboratorsAuthenticator|collaboratorsOnly|Allows only collaborators and administrators. |
|ReferrerAuthenticator |referrersOnly |Allows only the repository owner (or manager for group repository) and administrators.|
|ReadableUsersAuthenticator|readableUsersOnly|Allows only signed in users which can access the repository. |
|GroupManagerAuthenticator |managersOnly |Allows only the group managers. |
Of course, if you make a new plugin, you can define a your own authenticator according to requirement in your plugin.

View File

@@ -1,48 +1,56 @@
About Action in Issue Comment
========
After the issue creation at GitBucket, users can add comments or close it.
The details are saved at ```ISSUE_COMMENT``` table.
The details are saved at `ISSUE_COMMENT` table.
To determine if it was any operation, you see the ```ACTION``` column.
To determine if it was any operation, you see the `ACTION` column.
And in the case of some actions, `CONTENT` column value contains additional information.
|ACTION|
|--------|
|comment|
|close_comment|
|reopen_comment|
|close|
|reopen|
|commit|
|merge|
|delete_branch|
|refer|
|ACTION |CONTENT |
|---------------|-----------------|
|comment |comment |
|close_comment |comment |
|reopen_comment |comment |
|close |"Close" |
|reopen |"Reopen" |
|commit |comment commitId |
|merge |comment |
|delete_branch |branchName |
|refer |issueId:title |
### comment
#####comment
This value is saved when users have made a normal comment.
#####close_comment, reopen_comment
### close_comment, reopen_comment
These values are saved when users have reopened or closed the issue with comments.
#####close, reopen
### close, reopen
These values are saved when users have reopened or closed the issue.
At the same time, store the fixed value(i.e. "Close" or "Reopen") to the ```CONTENT``` column.
At the same time, store the fixed value(i.e. "Close" or "Reopen") to the `CONTENT` column.
Therefore, this comment is not displayed, and not counted as a comment.
#####commit
This value is saved when users have pushed including the ```#issueId``` to the commit message.
At the same time, store it to the ```CONTENT``` column with its commit id.
### commit
This value is saved when users have pushed including the `#issueId` to the commit message.
At the same time, store it to the `CONTENT` column with its commit id.
This comment is displayed. But it can not be edited by all users, and also not counted as a comment.
#####merge
### merge
This value is saved when users have merged the pull request.
At the same time, store the message to the ```CONTENT``` column.
At the same time, store the message to the `CONTENT` column.
This comment is displayed. But it can not be edited by all users, and also not counted as a comment.
#####delete_branch
### delete_branch
This value is saved when users have deleted the branch. Users can delete branch after merging pull request which is requested from the same repository.
At the same time, store it to the ```CONTENT``` column with the deleted branch name.
At the same time, store it to the `CONTENT` column with the deleted branch name.
Therefore, this comment is not displayed, and not counted as a comment.
#####refer
This value is saved when other issue or issue comment contains reference to the issue like ```#issueId```.
At the same time, store id and title of the referrer issue as ```id:title```.
### refer
This value is saved when other issue or issue comment contains reference to the issue like `#issueId`.
At the same time, store id and title of the referrer issue as `id:title`.

View File

@@ -1,56 +1,26 @@
How to run from the source tree
========
for Testers
Run for Development
--------
If you want to test GitBucket, input following command at the root directory of the source tree.
```
C:\gitbucket> sbt ~container:start
$ sbt ~jetty:start
```
Then access to `http://localhost:8080/` by your browser. The default administrator account is `root` and password is `root`.
for Developers
--------
If you want to modify source code and confirm it, you can run GitBucket in auto reloading mode as following:
Windows:
```
C:\gitbucket> sbt
...
> container:start
...
> ~ ;copy-resources;aux-compile
```
Linux:
```
~/gitbucket$ ./sbt.sh
...
> container:start
...
> ~ ;copy-resources;aux-compile
```
Source code modification is detected and reloaded automatically. You can modify logging configuration by editing `src/main/resources/logback-dev.xml`.
Build war file
--------
To build war file, run the following command:
Windows:
```
C:\gitbucket> sbt package
```
Linux:
```
~/gitbucket$ ./sbt.sh package
$ sbt package
```
`gitbucket_2.11-x.x.x.war` is generated into `target/scala-2.11`.

View File

@@ -3,7 +3,7 @@ Developer's Guide
* [How to run from source tree](how_to_run.md)
* [Directory Structure](directory.md)
* [Mapping and Validation](validation.md)
* Authentication in Controller (not yet)
* [Authentication in Controller](authenticator.md)
* [About Action in Issue Comment](comment_action.md)
* [Activity Types](activity.md)
* [Notification Email](notification.md)

View File

@@ -1,16 +1,15 @@
import sbt._
import Keys._
import org.scalatra.sbt._
import com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys
import com.earldouglas.xwp.JettyPlugin
import play.twirl.sbt.SbtTwirl
import play.twirl.sbt.Import.TwirlKeys._
import sbtassembly._
import sbt.Keys._
import sbt._
import sbtassembly.AssemblyKeys._
import sbtassembly._
import JettyPlugin.autoImport._
object MyBuild extends Build {
val Organization = "gitbucket"
val Name = "gitbucket"
val Version = "3.8.0"
val Version = "3.10.0"
val ScalaVersion = "2.11.6"
val ScalatraVersion = "2.3.1"
@@ -18,7 +17,7 @@ object MyBuild extends Build {
"gitbucket",
file(".")
)
.settings(ScalatraPlugin.scalatraWithJRebel: _*)
// .settings(ScalatraPlugin.scalatraWithJRebel: _*)
.settings(
test in assembly := {},
assemblyMergeStrategy in assembly := {
@@ -49,33 +48,33 @@ object MyBuild extends Build {
"org.scalatra" %% "scalatra-specs2" % ScalatraVersion % "test",
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
"org.json4s" %% "json4s-jackson" % "3.2.11",
"jp.sf.amateras" %% "scalatra-forms" % "0.1.0",
"jp.sf.amateras" %% "scalatra-forms" % "0.2.0",
"commons-io" % "commons-io" % "2.4",
"io.github.gitbucket" % "markedj" % "1.0.4-SNAPSHOT",
"io.github.gitbucket" % "markedj" % "1.0.6-SNAPSHOT",
"org.apache.commons" % "commons-compress" % "1.9",
"org.apache.commons" % "commons-email" % "1.3.3",
"org.apache.httpcomponents" % "httpclient" % "4.3.6",
"org.apache.sshd" % "apache-sshd" % "0.11.0",
"org.apache.sshd" % "apache-sshd" % "1.0.0",
"org.apache.tika" % "tika-core" % "1.10",
"com.typesafe.slick" %% "slick" % "2.1.0",
"com.novell.ldap" % "jldap" % "2009-10-07",
"com.h2database" % "h2" % "1.4.180",
// "ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
"org.eclipse.jetty" % "jetty-webapp" % "8.1.16.v20140903" % "container;provided",
"org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container;provided;test" artifacts Artifact("javax.servlet", "jar", "jar"),
"com.h2database" % "h2" % "1.4.190",
"ch.qos.logback" % "logback-classic" % "1.1.1",
"org.eclipse.jetty" % "jetty-webapp" % "8.1.16.v20140903" % "provided",
"javax.servlet" % "javax.servlet-api" % "3.0.1" % "provided",
"junit" % "junit" % "4.12" % "test",
"com.mchange" % "c3p0" % "0.9.5",
"com.mchange" % "c3p0" % "0.9.5.2",
"com.typesafe" % "config" % "1.2.1",
"com.typesafe.akka" %% "akka-actor" % "2.3.10",
"com.enragedginger" %% "akka-quartz-scheduler" % "1.3.0-akka-2.3.x" exclude("c3p0","c3p0")
),
play.twirl.sbt.Import.TwirlKeys.templateImports += "gitbucket.core._",
EclipseKeys.withSource := true,
javacOptions in compile ++= Seq("-target", "7", "-source", "7"),
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",
testOptions in Test += Tests.Setup( () => new java.io.File("target/gitbucket_home_for_test").mkdir() ),
fork in Test := true,
packageOptions += Package.MainClass("JettyLauncher")
).enablePlugins(SbtTwirl)
).enablePlugins(SbtTwirl, JettyPlugin)
}

View File

@@ -1,8 +1,5 @@
scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature")
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.4.0")
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0")
addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.3.5")
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.0.4")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.8")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.0.4")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")
addSbtPlugin("com.earldouglas" % "xsbt-web-plugin" % "2.1.0")

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>gitbucket.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
<!--
<logger name="service.WebHookService" level="DEBUG" />
<logger name="servlet" level="DEBUG" />
-->
<logger name="scala.slick.jdbc.JdbcBackend.statement" level="DEBUG" />
</configuration>

View File

@@ -6,12 +6,21 @@
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>gitbucket.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
<!--
<logger name="service.WebHookService" level="DEBUG" />
<logger name="servlet" level="DEBUG" />
<logger name="scala.slick.jdbc.JdbcBackend.statement" level="DEBUG" />
-->
</configuration>

View File

@@ -0,0 +1,5 @@
c3p0 {
privilegeSpawnedThreads=true
contextClassLoaderSource=library
}

View File

@@ -0,0 +1,55 @@
DROP TABLE IF EXISTS WEB_HOOK_EVENT;
CREATE TABLE WEB_HOOK_EVENT(
USER_NAME VARCHAR(100) NOT NULL,
REPOSITORY_NAME VARCHAR(100) NOT NULL,
URL VARCHAR(200) NOT NULL,
EVENT VARCHAR(30) NOT NULL
);
ALTER TABLE WEB_HOOK_EVENT ADD CONSTRAINT IDX_WEB_HOOK_EVENT_PK PRIMARY KEY (USER_NAME, REPOSITORY_NAME, URL, EVENT);
ALTER TABLE WEB_HOOK_EVENT ADD CONSTRAINT IDX_WEB_HOOK_EVENT_FK0 FOREIGN KEY (USER_NAME, REPOSITORY_NAME, URL) REFERENCES WEB_HOOK (USER_NAME, REPOSITORY_NAME, URL)
ON DELETE CASCADE ON UPDATE CASCADE;
CREATE TEMPORARY TABLE TMP_EVENTS (EVENT VARCHAR(30));
INSERT INTO TMP_EVENTS VALUES ('push'),('issue_comment'),('issues'),('pull_request');
INSERT INTO WEB_HOOK_EVENT (USER_NAME, REPOSITORY_NAME, URL, EVENT)
SELECT USER_NAME, REPOSITORY_NAME, URL, EVENT
FROM WEB_HOOK, TMP_EVENTS;
DROP TABLE TMP_EVENTS;
ALTER TABLE COMMIT_COMMENT ADD COLUMN ISSUE_ID INT;
CREATE OR REPLACE VIEW ISSUE_OUTLINE_VIEW AS
SELECT
A.USER_NAME,
A.REPOSITORY_NAME,
A.ISSUE_ID,
NVL(B.COMMENT_COUNT, 0) + NVL(C.COMMENT_COUNT, 0) AS COMMENT_COUNT
FROM ISSUE A
LEFT OUTER JOIN (
SELECT USER_NAME, REPOSITORY_NAME, ISSUE_ID, COUNT(COMMENT_ID) AS COMMENT_COUNT FROM ISSUE_COMMENT
WHERE ACTION IN ('comment', 'close_comment', 'reopen_comment')
GROUP BY USER_NAME, REPOSITORY_NAME, ISSUE_ID
) B
ON (A.USER_NAME = B.USER_NAME AND A.REPOSITORY_NAME = B.REPOSITORY_NAME AND A.ISSUE_ID = B.ISSUE_ID)
LEFT OUTER JOIN (
SELECT USER_NAME, REPOSITORY_NAME, ISSUE_ID, COUNT(COMMENT_ID) AS COMMENT_COUNT FROM COMMIT_COMMENT
GROUP BY USER_NAME, REPOSITORY_NAME, ISSUE_ID
) C
ON (A.USER_NAME = C.USER_NAME AND A.REPOSITORY_NAME = C.REPOSITORY_NAME AND A.ISSUE_ID = C.ISSUE_ID);
UPDATE COMMIT_COMMENT C SET (ISSUE_ID) = (
SELECT MAX(P.ISSUE_ID)
FROM PULL_REQUEST P
WHERE
C.USER_NAME = P.USER_NAME AND
C.REPOSITORY_NAME = P.REPOSITORY_NAME AND
C.COMMIT_ID = P.COMMIT_ID_TO
);
ALTER TABLE COMMIT_COMMENT DROP COLUMN PULL_REQUEST;

View File

@@ -0,0 +1,61 @@
package gitbucket.core.api
import gitbucket.core.util.RepositoryName
import gitbucket.core.model.CommitComment
import java.util.Date
/**
* https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent
*/
case class ApiPullRequestReviewComment(
id: Int, // 29724692
// "diff_hunk": "@@ -1 +1 @@\n-# public-repo",
path: String, // "README.md",
// "position": 1,
// "original_position": 1,
commit_id: String, // "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c",
// "original_commit_id": "0d1a26e67d8f5eaf1f6ba5c57fc3c7d91ac0fd1c",
user: ApiUser,
body: String, // "Maybe you should use more emojji on this line.",
created_at: Date, // "2015-05-05T23:40:27Z",
updated_at: Date // "2015-05-05T23:40:27Z",
)(repositoryName:RepositoryName, issueId: Int) extends FieldSerializable {
// "url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments/29724692",
val url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/pulls/comments/${id}")
// "html_url": "https://github.com/baxterthehacker/public-repo/pull/1#discussion_r29724692",
val html_url = ApiPath(s"/${repositoryName.fullName}/pull/${issueId}#discussion_r${id}")
// "pull_request_url": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1",
val pull_request_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/pulls/${issueId}")
/*
"_links": {
"self": {
"href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/comments/29724692"
},
"html": {
"href": "https://github.com/baxterthehacker/public-repo/pull/1#discussion_r29724692"
},
"pull_request": {
"href": "https://api.github.com/repos/baxterthehacker/public-repo/pulls/1"
}
}
*/
val _links = Map(
"self" -> Map("href" -> url),
"html" -> Map("href" -> html_url),
"pull_request" -> Map("href" -> pull_request_url))
}
object ApiPullRequestReviewComment{
def apply(comment: CommitComment, commentedUser: ApiUser, repositoryName: RepositoryName, issueId: Int): ApiPullRequestReviewComment =
new ApiPullRequestReviewComment(
id = comment.commentId,
path = comment.fileName.getOrElse(""),
commit_id = comment.commitId,
user = commentedUser,
body = comment.content,
created_at = comment.registeredDate,
updated_at = comment.updatedDate
)(repositoryName, issueId)
}

View File

@@ -0,0 +1,11 @@
package gitbucket.core.api
import gitbucket.core.model.Account
case class ApiPusher(name: String, email: String)
object ApiPusher {
def apply(user: Account): ApiPusher = ApiPusher(
name = user.userName,
email = user.mailAddress)
}

View File

@@ -5,40 +5,48 @@ import org.joda.time.DateTimeZone
import org.joda.time.format._
import org.json4s._
import org.json4s.jackson.Serialization
import java.util.Date
import scala.util.Try
object JsonFormat {
case class Context(baseUrl:String)
case class Context(baseUrl: String)
val parserISO = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'")
val jsonFormats = Serialization.formats(NoTypeHints) + new CustomSerializer[Date](format =>
(
{ case JString(s) => Try(parserISO.parseDateTime(s)).toOption.map(_.toDate)
.getOrElse(throw new MappingException("Can't convert " + s + " to Date")) },
{ case JString(s) => Try(parserISO.parseDateTime(s)).toOption.map(_.toDate).getOrElse(throw new MappingException("Can't convert " + s + " to Date")) },
{ case x: Date => JString(parserISO.print(new DateTime(x).withZone(DateTimeZone.UTC))) }
)
) + FieldSerializer[ApiUser]() + FieldSerializer[ApiPullRequest]() + FieldSerializer[ApiRepository]() +
FieldSerializer[ApiCommitListItem.Parent]() + FieldSerializer[ApiCommitListItem]() + FieldSerializer[ApiCommitListItem.Commit]() +
FieldSerializer[ApiCommitStatus]() + FieldSerializer[FieldSerializable]() + FieldSerializer[ApiCombinedCommitStatus]() +
FieldSerializer[ApiPullRequest.Commit]() + FieldSerializer[ApiIssue]() + FieldSerializer[ApiComment]()
) + FieldSerializer[ApiUser]() +
FieldSerializer[ApiPullRequest]() +
FieldSerializer[ApiRepository]() +
FieldSerializer[ApiCommitListItem.Parent]() +
FieldSerializer[ApiCommitListItem]() +
FieldSerializer[ApiCommitListItem.Commit]() +
FieldSerializer[ApiCommitStatus]() +
FieldSerializer[FieldSerializable]() +
FieldSerializer[ApiCombinedCommitStatus]() +
FieldSerializer[ApiPullRequest.Commit]() +
FieldSerializer[ApiIssue]() +
FieldSerializer[ApiComment]()
def apiPathSerializer(c: Context) = new CustomSerializer[ApiPath](format =>
(
{
case JString(s) if s.startsWith(c.baseUrl) => ApiPath(s.substring(c.baseUrl.length))
case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath")
},
{
case ApiPath(path) => JString(c.baseUrl+path)
}
)
(
{
case JString(s) if s.startsWith(c.baseUrl) => ApiPath(s.substring(c.baseUrl.length))
case JString(s) => throw new MappingException("Can't convert " + s + " to ApiPath")
},
{
case ApiPath(path) => JString(c.baseUrl + path)
}
)
)
/**
* convert object to json string
*/
def apply(obj: AnyRef)(implicit c: Context): String = Serialization.write(obj)(jsonFormats + apiPathSerializer(c))
}

View File

@@ -90,8 +90,8 @@ trait AccountControllerBase extends AccountManagementControllerBase {
case class ForkRepositoryForm(owner: String, name: String)
val newRepositoryForm = mapping(
"owner" -> trim(label("Owner" , text(required, maxlength(40), identifier, existsAccount))),
"name" -> trim(label("Repository name", text(required, maxlength(40), repository, uniqueRepository))),
"owner" -> trim(label("Owner" , text(required, maxlength(100), identifier, existsAccount))),
"name" -> trim(label("Repository name", text(required, maxlength(100), repository, uniqueRepository))),
"description" -> trim(label("Description" , optional(text()))),
"isPrivate" -> trim(label("Repository Type", boolean())),
"createReadme" -> trim(label("Create README" , boolean()))
@@ -212,6 +212,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
// // Remove from GROUP_MEMBER, COLLABORATOR and REPOSITORY
// removeUserRelatedData(userName)
removeUserRelatedData(userName)
updateAccount(account.copy(isRemoved = true))
}

View File

@@ -104,7 +104,7 @@ trait IndexControllerBase extends ControllerBase {
})
/**
* JSON APU for checking user existence.
* JSON API for checking user existence.
*/
post("/_user/existence")(usersOnly {
getAccountByUserName(params("userName")).isDefined

View File

@@ -231,10 +231,20 @@ trait IssuesControllerBase extends ControllerBase {
} getOrElse {
contentType = formats("json")
org.json4s.jackson.Serialization.write(
Map("title" -> x.title,
"content" -> Markdown.toHtml(x.content getOrElse "No description given.",
repository, false, true, true, true, isEditable(x.userName, x.repositoryName, x.openedUserName))
))
Map(
"title" -> x.title,
"content" -> Markdown.toHtml(
markdown = x.content getOrElse "No description given.",
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = isEditable(x.userName, x.repositoryName, x.openedUserName)
)
)
)
}
} else Unauthorized
} getOrElse NotFound
@@ -249,9 +259,19 @@ trait IssuesControllerBase extends ControllerBase {
} getOrElse {
contentType = formats("json")
org.json4s.jackson.Serialization.write(
Map("content" -> view.Markdown.toHtml(x.content,
repository, false, true, true, isEditable(x.userName, x.repositoryName, x.commentedUserName))
))
Map(
"content" -> view.Markdown.toHtml(
markdown = x.content,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = isEditable(x.userName, x.repositoryName, x.commentedUserName)
)
)
)
}
} else Unauthorized
} getOrElse NotFound

View File

@@ -29,7 +29,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
case class OptionsForm(repositoryName: String, description: Option[String], defaultBranch: String, isPrivate: Boolean)
val optionsForm = mapping(
"repositoryName" -> trim(label("Description" , text(required, maxlength(40), identifier, renameRepositoryName))),
"repositoryName" -> trim(label("Repository Name", text(required, maxlength(40), identifier, renameRepositoryName))),
"description" -> trim(label("Description" , optional(text()))),
"defaultBranch" -> trim(label("Default Branch" , text(required, maxlength(100)))),
"isPrivate" -> trim(label("Repository Type", boolean()))
@@ -43,10 +43,11 @@ trait RepositorySettingsControllerBase extends ControllerBase {
)(CollaboratorForm.apply)
// for web hook url addition
case class WebHookForm(url: String)
case class WebHookForm(url: String, events: Set[WebHook.Event])
val webHookForm = mapping(
"url" -> trim(label("url", text(required, webHook)))
def webHookForm(update:Boolean) = mapping(
"url" -> trim(label("url", text(required, webHook(update)))),
"events" -> webhookEvents
)(WebHookForm.apply)
// for transfer ownership
@@ -139,14 +140,23 @@ trait RepositorySettingsControllerBase extends ControllerBase {
* Display the web hook page.
*/
get("/:owner/:repository/settings/hooks")(ownerOnly { repository =>
html.hooks(getWebHookURLs(repository.owner, repository.name), flash.get("url"), repository, flash.get("info"))
html.hooks(getWebHooks(repository.owner, repository.name), repository, flash.get("info"))
})
/**
* Display the web hook edit page.
*/
get("/:owner/:repository/settings/hooks/new")(ownerOnly { repository =>
val webhook = WebHook(repository.owner, repository.name, "")
html.edithooks(webhook, Set(WebHook.Push), repository, flash.get("info"), true)
})
/**
* Add the web hook URL.
*/
post("/:owner/:repository/settings/hooks/add", webHookForm)(ownerOnly { (form, repository) =>
addWebHookURL(repository.owner, repository.name, form.url)
post("/:owner/:repository/settings/hooks/new", webHookForm(false))(ownerOnly { (form, repository) =>
addWebHook(repository.owner, repository.name, form.url, form.events)
flash += "info" -> s"Webhook ${form.url} created"
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
})
@@ -154,32 +164,87 @@ trait RepositorySettingsControllerBase extends ControllerBase {
* Delete the web hook URL.
*/
get("/:owner/:repository/settings/hooks/delete")(ownerOnly { repository =>
deleteWebHookURL(repository.owner, repository.name, params("url"))
deleteWebHook(repository.owner, repository.name, params("url"))
flash += "info" -> s"Webhook ${params("url")} deleted"
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
})
/**
* Send the test request to registered web hook URLs.
*/
post("/:owner/:repository/settings/hooks/test", webHookForm)(ownerOnly { (form, repository) =>
ajaxPost("/:owner/:repository/settings/hooks/test")(ownerOnly { repository =>
def _headers(h: Array[org.apache.http.Header]): Array[Array[String]] = h.map { h => Array(h.getName, h.getValue) }
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
import scala.collection.JavaConverters._
val commits = if(repository.commitCount == 0) List.empty else git.log
.add(git.getRepository.resolve(repository.repository.defaultBranch))
.setMaxCount(4)
.call.iterator.asScala.map(new CommitInfo(_)).toList
import scala.concurrent.duration._
import scala.concurrent._
import scala.util.control.NonFatal
import org.apache.http.util.EntityUtils
import scala.concurrent.ExecutionContext.Implicits.global
getAccountByUserName(repository.owner).foreach { ownerAccount =>
callWebHook("push",
List(WebHook(repository.owner, repository.name, form.url)),
WebHookPushPayload(git, ownerAccount, "refs/heads/" + repository.repository.defaultBranch, repository, (if(commits.isEmpty){Nil}else{commits.tail}), ownerAccount,
oldId = commits.lastOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId()),
newId = commits.headOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId()))
val url = params("url")
val dummyWebHookInfo = WebHook(repository.owner, repository.name, url)
val dummyPayload = {
val ownerAccount = getAccountByUserName(repository.owner).get
val commits = if(repository.commitCount == 0) List.empty else git.log
.add(git.getRepository.resolve(repository.repository.defaultBranch))
.setMaxCount(4)
.call.iterator.asScala.map(new CommitInfo(_)).toList
val pushedCommit = commits.drop(1)
WebHookPushPayload(
git = git,
sender = ownerAccount,
refName = "refs/heads/" + repository.repository.defaultBranch,
repositoryInfo = repository,
commits = pushedCommit,
repositoryOwner = ownerAccount,
oldId = commits.lastOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId()),
newId = commits.headOption.map(_.id).map(ObjectId.fromString).getOrElse(ObjectId.zeroId())
)
}
flash += "url" -> form.url
flash += "info" -> "Test payload deployed!"
val (webHook, json, reqFuture, resFuture) = callWebHook(WebHook.Push, List(dummyWebHookInfo), dummyPayload).head
val toErrorMap: PartialFunction[Throwable, Map[String,String]] = {
case e: java.net.UnknownHostException => Map("error"-> ("Unknown host " + e.getMessage))
case e: java.lang.IllegalArgumentException => Map("error"-> ("invalid url"))
case e: org.apache.http.client.ClientProtocolException => Map("error"-> ("invalid url"))
case NonFatal(e) => Map("error"-> (e.getClass + " "+ e.getMessage))
}
contentType = formats("json")
org.json4s.jackson.Serialization.write(Map(
"url" -> url,
"request" -> Await.result(reqFuture.map(req => Map(
"headers" -> _headers(req.getAllHeaders),
"payload" -> json
)).recover(toErrorMap), 20 seconds),
"responce" -> Await.result(resFuture.map(res => Map(
"status" -> res.getStatusLine(),
"body" -> EntityUtils.toString(res.getEntity()),
"headers" -> _headers(res.getAllHeaders())
)).recover(toErrorMap), 20 seconds)
))
}
})
/**
* Display the web hook edit page.
*/
get("/:owner/:repository/settings/hooks/edit")(ownerOnly { repository =>
getWebHook(repository.owner, repository.name, params("url")).map{ case (webhook, events) =>
html.edithooks(webhook, events, repository, flash.get("info"), false)
} getOrElse NotFound
})
/**
* Update web hook settings.
*/
post("/:owner/:repository/settings/hooks/edit", webHookForm(true))(ownerOnly { (form, repository) =>
updateWebHook(repository.owner, repository.name, form.url, form.events)
flash += "info" -> s"webhook ${form.url} updated"
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
})
@@ -229,9 +294,30 @@ trait RepositorySettingsControllerBase extends ControllerBase {
/**
* Provides duplication check for web hook url.
*/
private def webHook: Constraint = new Constraint(){
private def webHook(needExists: Boolean): Constraint = new Constraint(){
override def validate(name: String, value: String, messages: Messages): Option[String] =
getWebHookURLs(params("owner"), params("repository")).map(_.url).find(_ == value).map(_ => "URL had been registered already.")
if(getWebHook(params("owner"), params("repository"), value).isDefined != needExists){
Some(if(needExists){
"URL had not been registered yet."
} else {
"URL had been registered already."
})
} else {
None
}
}
private def webhookEvents = new ValueType[Set[WebHook.Event]]{
def convert(name: String, params: Map[String, String], messages: Messages): Set[WebHook.Event] = {
WebHook.Event.values.flatMap { t =>
params.get(name + "." + t.name).map(_ => t)
}.toSet
}
def validate(name: String, params: Map[String, String], messages: Messages): Seq[(String, String)] = if(convert(name,params,messages).isEmpty){
Seq(name -> messages("error.required").format(name))
} else {
Nil
}
}
/**

View File

@@ -1,5 +1,7 @@
package gitbucket.core.controller
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
import gitbucket.core.api._
import gitbucket.core.plugin.PluginRegistry
import gitbucket.core.repo.html
@@ -11,7 +13,7 @@ import gitbucket.core.util.StringUtil._
import gitbucket.core.util.ControlUtil._
import gitbucket.core.util.Implicits._
import gitbucket.core.util.Directory._
import gitbucket.core.model.{Account, CommitState}
import gitbucket.core.model.{Account, CommitState, WebHook}
import gitbucket.core.service.CommitStatusService
import gitbucket.core.service.WebHookService._
import gitbucket.core.view
@@ -32,7 +34,7 @@ import org.scalatra._
class RepositoryViewerController extends RepositoryViewerControllerBase
with RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with CommitStatusService
with WebHookPullRequestService
with WebHookPullRequestService with WebHookPullRequestReviewCommentService
/**
* The repository viewer.
@@ -40,7 +42,7 @@ class RepositoryViewerController extends RepositoryViewerControllerBase
trait RepositoryViewerControllerBase extends ControllerBase {
self: RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator with PullRequestService with CommitStatusService
with WebHookPullRequestService =>
with WebHookPullRequestService with WebHookPullRequestReviewCommentService =>
ArchiveCommand.registerFormat("zip", new ZipFormat)
ArchiveCommand.registerFormat("tar.gz", new TgzFormat)
@@ -102,11 +104,15 @@ trait RepositoryViewerControllerBase extends ControllerBase {
*/
post("/:owner/:repository/_preview")(referrersOnly { repository =>
contentType = "text/html"
helpers.markdown(params("content"), repository,
params("enableWikiLink").toBoolean,
params("enableRefsLink").toBoolean,
params("enableTaskList").toBoolean,
hasWritePermission(repository.owner, repository.name, context.loginAccount))
helpers.markdown(
markdown = params("content"),
repository = repository,
enableWikiLink = params("enableWikiLink").toBoolean,
enableRefsLink = params("enableRefsLink").toBoolean,
enableLineBreaks = params("enableLineBreaks").toBoolean,
enableTaskList = params("enableTaskList").toBoolean,
hasWritePermission = hasWritePermission(repository.owner, repository.name, context.loginAccount)
)
})
/**
@@ -177,7 +183,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
*
* ref is Ref to list the statuses from. It can be a SHA, a branch name, or a tag name.
*/
get("/api/v3/repos/:owner/:repo/commits/:ref/statuses")(referrersOnly { repository =>
val listStatusesRoute = get("/api/v3/repos/:owner/:repo/commits/:ref/statuses")(referrersOnly { repository =>
(for{
ref <- params.get("ref")
sha <- JGitUtil.getShaByRef(repository.owner, repository.name, ref)
@@ -188,6 +194,15 @@ trait RepositoryViewerControllerBase extends ControllerBase {
}) getOrElse NotFound
})
/**
* https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref
*
* legacy route
*/
get("/api/v3/repos/:owner/:repo/statuses/:ref"){
listStatusesRoute.action()
}
/**
* https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
*
@@ -282,6 +297,21 @@ trait RepositoryViewerControllerBase extends ControllerBase {
redirect(s"/${repository.owner}/${repository.name}/tree/${form.branch}${if(form.path.length == 0) "" else form.path}")
})
get("/:owner/:repository/raw/*")(referrersOnly { repository =>
val (id, path) = splitPath(repository, multiParams("splat").head)
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
getPathObjectId(git, path, revCommit).flatMap { objectId =>
JGitUtil.getObjectLoaderFromId(git, objectId){ loader =>
contentType = "application/octet-stream"
response.setContentLength(loader.getSize.toInt)
loader.copyTo(response.outputStream)
()
}
} getOrElse NotFound
}
})
/**
* Displays the file content of the specified branch or commit.
*/
@@ -292,12 +322,11 @@ trait RepositoryViewerControllerBase extends ControllerBase {
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
getPathObjectId(git, path, revCommit).map { objectId =>
if(raw){
// Download
// Download (This route is left for backword compatibility)
JGitUtil.getObjectLoaderFromId(git, objectId){ loader =>
//RawData("application/octet-stream", bytes)
contentType = "application/octet-stream"
response.setContentLength(loader.getSize.toInt)
loader.copyTo(response.getOutputStream)
loader.copyTo(response.outputStream)
()
} getOrElse NotFound
} else {
@@ -370,7 +399,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
post("/:owner/:repository/commit/:id/comment/new", commentForm)(readableUsersOnly { (form, repository) =>
val id = params("id")
createCommitComment(repository.owner, repository.name, id, context.loginAccount.get.userName, form.content,
form.fileName, form.oldLineNumber, form.newLineNumber, form.issueId.isDefined)
form.fileName, form.oldLineNumber, form.newLineNumber, form.issueId)
form.issueId match {
case Some(issueId) => recordCommentPullRequestActivity(repository.owner, repository.name, context.loginAccount.get.userName, issueId, form.content)
case None => recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content)
@@ -395,13 +424,15 @@ trait RepositoryViewerControllerBase extends ControllerBase {
ajaxPost("/:owner/:repository/commit/:id/comment/_data/new", commentForm)(readableUsersOnly { (form, repository) =>
val id = params("id")
val commentId = createCommitComment(repository.owner, repository.name, id, context.loginAccount.get.userName,
form.content, form.fileName, form.oldLineNumber, form.newLineNumber, form.issueId.isDefined)
form.content, form.fileName, form.oldLineNumber, form.newLineNumber, form.issueId)
val comment = getCommitComment(repository.owner, repository.name, commentId.toString).get
form.issueId match {
case Some(issueId) => recordCommentPullRequestActivity(repository.owner, repository.name, context.loginAccount.get.userName, issueId, form.content)
case Some(issueId) =>
recordCommentPullRequestActivity(repository.owner, repository.name, context.loginAccount.get.userName, issueId, form.content)
callPullRequestReviewCommentWebHook("create", comment, repository, issueId, context.baseUrl, context.loginAccount.get)
case None => recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content)
}
helper.html.commitcomment(getCommitComment(repository.owner, repository.name, commentId.toString).get,
hasWritePermission(repository.owner, repository.name, context.loginAccount), repository)
helper.html.commitcomment(comment, hasWritePermission(repository.owner, repository.name, context.loginAccount), repository)
})
ajaxGet("/:owner/:repository/commit_comments/_data/:id")(readableUsersOnly { repository =>
@@ -413,8 +444,16 @@ trait RepositoryViewerControllerBase extends ControllerBase {
} getOrElse {
contentType = formats("json")
org.json4s.jackson.Serialization.write(
Map("content" -> view.Markdown.toHtml(x.content,
repository, false, true, true, isEditable(x.userName, x.repositoryName, x.commentedUserName))
Map(
"content" -> view.Markdown.toHtml(
markdown = x.content,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
hasWritePermission = isEditable(x.userName, x.repositoryName, x.commentedUserName)
)
))
}
} else Unauthorized
@@ -667,7 +706,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
// call web hook
callPullRequestWebHookByRequestBranch("synchronize", repository, branch, context.baseUrl, loginAccount)
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
callWebHookOf(repository.owner, repository.name, "push") {
callWebHookOf(repository.owner, repository.name, WebHook.Push) {
getAccountByUserName(repository.owner).map{ ownerAccount =>
WebHookPushPayload(git, loginAccount, headName, repository, List(commit), ownerAccount,
oldId = headTip, newId = commitId)
@@ -722,4 +761,11 @@ trait RepositoryViewerControllerBase extends ControllerBase {
private def isEditable(owner: String, repository: String, author: String)(implicit context: Context): Boolean =
hasWritePermission(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
override protected def renderUncaughtException(e: Throwable)(implicit request: HttpServletRequest, response: HttpServletResponse): Unit = {
e.printStackTrace()
}
}

View File

@@ -100,12 +100,12 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
if(form.isRemoved){
// Remove repositories
getRepositoryNamesOfUser(userName).foreach { repositoryName =>
deleteRepository(userName, repositoryName)
FileUtils.deleteDirectory(getRepositoryDir(userName, repositoryName))
FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
}
// getRepositoryNamesOfUser(userName).foreach { repositoryName =>
// deleteRepository(userName, repositoryName)
// FileUtils.deleteDirectory(getRepositoryDir(userName, repositoryName))
// FileUtils.deleteDirectory(getWikiRepositoryDir(userName, repositoryName))
// FileUtils.deleteDirectory(getTemporaryDir(userName, repositoryName))
// }
// Remove from GROUP_MEMBER, COLLABORATOR and REPOSITORY
removeUserRelatedData(userName)
}

View File

@@ -55,8 +55,8 @@ trait CommitCommentComponent extends TemplateComponent { self: Profile =>
val newLine = column[Option[Int]]("NEW_LINE_NUMBER")
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
val updatedDate = column[java.util.Date]("UPDATED_DATE")
val pullRequest = column[Boolean]("PULL_REQUEST")
def * = (userName, repositoryName, commitId, commentId, commentedUserName, content, fileName, oldLine, newLine, registeredDate, updatedDate, pullRequest) <> (CommitComment.tupled, CommitComment.unapply)
val issueId = column[Option[Int]]("ISSUE_ID")
def * = (userName, repositoryName, commitId, commentId, commentedUserName, content, fileName, oldLine, newLine, registeredDate, updatedDate, issueId) <> (CommitComment.tupled, CommitComment.unapply)
def byPrimaryKey(commentId: Int) = this.commentId === commentId.bind
}
@@ -74,5 +74,5 @@ case class CommitComment(
newLine: Option[Int],
registeredDate: java.util.Date,
updatedDate: java.util.Date,
pullRequest: Boolean
issueId: Option[Int]
) extends Comment

View File

@@ -48,6 +48,7 @@ trait CoreProfile extends ProfileProvider with Profile
with RepositoryComponent
with SshKeyComponent
with WebHookComponent
with WebHookEventComponent
with PluginComponent
object Profile extends CoreProfile

View File

@@ -7,7 +7,7 @@ trait WebHookComponent extends TemplateComponent { self: Profile =>
class WebHooks(tag: Tag) extends Table[WebHook](tag, "WEB_HOOK") with BasicTemplate {
val url = column[String]("URL")
def * = (userName, repositoryName, url) <> (WebHook.tupled, WebHook.unapply)
def * = (userName, repositoryName, url) <> ((WebHook.apply _).tupled, WebHook.unapply)
def byPrimaryKey(owner: String, repository: String, url: String) = byRepository(owner, repository) && (this.url === url.bind)
}
@@ -18,3 +18,32 @@ case class WebHook(
repositoryName: String,
url: String
)
object WebHook {
sealed class Event(var name: String)
case object CommitComment extends Event("commit_comment")
case object Create extends Event("create")
case object Delete extends Event("delete")
case object Deployment extends Event("deployment")
case object DeploymentStatus extends Event("deployment_status")
case object Fork extends Event("fork")
case object Gollum extends Event("gollum")
case object IssueComment extends Event("issue_comment")
case object Issues extends Event("issues")
case object Member extends Event("member")
case object PageBuild extends Event("page_build")
case object Public extends Event("public")
case object PullRequest extends Event("pull_request")
case object PullRequestReviewComment extends Event("pull_request_review_comment")
case object Push extends Event("push")
case object Release extends Event("release")
case object Status extends Event("status")
case object TeamAdd extends Event("team_add")
case object Watch extends Event("watch")
object Event{
val values = List(CommitComment,Create,Delete,Deployment,DeploymentStatus,Fork,Gollum,IssueComment,Issues,Member,PageBuild,Public,PullRequest,PullRequestReviewComment,Push,Release,Status,TeamAdd,Watch)
private val map:Map[String,Event] = values.map(e => e.name -> e).toMap
def valueOf(name: String): Event = map(name)
def valueOpt(name: String): Option[Event] = map.get(name)
}
}

View File

@@ -0,0 +1,30 @@
package gitbucket.core.model
trait WebHookEventComponent extends TemplateComponent { self: Profile =>
import profile.simple._
import gitbucket.core.model.Profile.WebHooks
lazy val WebHookEvents = TableQuery[WebHookEvents]
implicit val typedType = MappedColumnType.base[WebHook.Event, String](_.name, WebHook.Event.valueOf(_))
class WebHookEvents(tag: Tag) extends Table[WebHookEvent](tag, "WEB_HOOK_EVENT") with BasicTemplate {
val url = column[String]("URL")
val event = column[WebHook.Event]("EVENT")
def * = (userName, repositoryName, url, event) <> ((WebHookEvent.apply _).tupled, WebHookEvent.unapply)
def byWebHook(owner: String, repository: String, url: String) = byRepository(owner, repository) && (this.url === url.bind)
def byWebHook(owner: Column[String], repository: Column[String], url: Column[String]) =
byRepository(userName, repositoryName) && (this.url === url)
def byWebHook(webhook: WebHooks) =
byRepository(webhook.userName, webhook.repositoryName) && (this.url === webhook.url)
def byPrimaryKey(owner: String, repository: String, url: String, event: WebHook.Event) = byWebHook(owner, repository, url) && (this.event === event.bind)
}
}
case class WebHookEvent(
userName: String,
repositoryName: String,
url: String,
event: WebHook.Event
)

View File

@@ -20,7 +20,14 @@ trait Renderer {
object MarkdownRenderer extends Renderer {
override def render(request: RenderRequest): Html = {
import request._
Html(Markdown.toHtml(fileContent, repository, enableWikiLink, enableRefsLink, enableAnchor)(context))
Html(Markdown.toHtml(
markdown = fileContent,
repository = repository,
enableWikiLink = enableWikiLink,
enableRefsLink = enableRefsLink,
enableAnchor = enableAnchor,
enableLineBreaks = false
)(context))
}
}
@@ -35,11 +42,13 @@ object DefaultRenderer extends Renderer {
}
}
case class RenderRequest(filePath: List[String],
fileContent: String,
branch: String,
repository: RepositoryService.RepositoryInfo,
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableAnchor: Boolean,
context: Context)
case class RenderRequest(
filePath: List[String],
fileContent: String,
branch: String,
repository: RepositoryService.RepositoryInfo,
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableAnchor: Boolean,
context: Context
)

View File

@@ -13,9 +13,9 @@ import StringUtil._
trait CommitsService {
def getCommitComments(owner: String, repository: String, commitId: String, pullRequest: Boolean)(implicit s: Session) =
def getCommitComments(owner: String, repository: String, commitId: String, includePullRequest: Boolean)(implicit s: Session) =
CommitComments filter {
t => t.byCommit(owner, repository, commitId) && (t.pullRequest === pullRequest || pullRequest)
t => t.byCommit(owner, repository, commitId) && (t.issueId.isEmpty || includePullRequest)
} list
def getCommitComment(owner: String, repository: String, commentId: String)(implicit s: Session) =
@@ -27,7 +27,8 @@ trait CommitsService {
None
def createCommitComment(owner: String, repository: String, commitId: String, loginUser: String,
content: String, fileName: Option[String], oldLine: Option[Int], newLine: Option[Int], pullRequest: Boolean)(implicit s: Session): Int =
content: String, fileName: Option[String], oldLine: Option[Int], newLine: Option[Int],
issueId: Option[Int])(implicit s: Session): Int =
CommitComments.autoInc insert CommitComment(
userName = owner,
repositoryName = repository,
@@ -39,7 +40,7 @@ trait CommitsService {
newLine = newLine,
registeredDate = currentDate,
updatedDate = currentDate,
pullRequest = pullRequest)
issueId = issueId)
def updateCommitComment(commentId: Int, content: String)(implicit s: Session) =
CommitComments

View File

@@ -47,6 +47,7 @@ trait RepositoryService { self: AccountService =>
Repositories insert repository.copy(userName = newUserName, repositoryName = newRepositoryName)
val webHooks = WebHooks .filter(_.byRepository(oldUserName, oldRepositoryName)).list
val webHookEvents = WebHookEvents .filter(_.byRepository(oldUserName, oldRepositoryName)).list
val milestones = Milestones .filter(_.byRepository(oldUserName, oldRepositoryName)).list
val issueId = IssueId .filter(_.byRepository(oldUserName, oldRepositoryName)).list
val issues = Issues .filter(_.byRepository(oldUserName, oldRepositoryName)).list
@@ -75,9 +76,10 @@ trait RepositoryService { self: AccountService =>
deleteRepository(oldUserName, oldRepositoryName)
WebHooks .insertAll(webHooks .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
Milestones.insertAll(milestones .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
IssueId .insertAll(issueId .map(_.copy(_1 = newUserName, _2 = newRepositoryName)) :_*)
WebHooks .insertAll(webHooks .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
WebHookEvents.insertAll(webHookEvents .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
Milestones .insertAll(milestones .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
IssueId .insertAll(issueId .map(_.copy(_1 = newUserName, _2 = newRepositoryName)) :_*)
val newMilestones = Milestones.filter(_.byRepository(newUserName, newRepositoryName)).list
Issues.insertAll(issues.map { x => x.copy(
@@ -146,6 +148,7 @@ trait RepositoryService { self: AccountService =>
IssueId .filter(_.byRepository(userName, repositoryName)).delete
Milestones .filter(_.byRepository(userName, repositoryName)).delete
WebHooks .filter(_.byRepository(userName, repositoryName)).delete
WebHookEvents .filter(_.byRepository(userName, repositoryName)).delete
Repositories .filter(_.byRepository(userName, repositoryName)).delete
// Update ORIGIN_USER_NAME and ORIGIN_REPOSITORY_NAME

View File

@@ -1,7 +1,7 @@
package gitbucket.core.service
import gitbucket.core.api._
import gitbucket.core.model.{WebHook, Account, Issue, PullRequest, IssueComment}
import gitbucket.core.model.{WebHook, Account, Issue, PullRequest, IssueComment, WebHookEvent, CommitComment}
import gitbucket.core.model.Profile._
import profile.simple._
import gitbucket.core.util.JGitUtil.CommitInfo
@@ -14,6 +14,9 @@ import org.apache.http.message.BasicNameValuePair
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.ObjectId
import org.slf4j.LoggerFactory
import scala.concurrent._
import org.apache.http.HttpRequest
import org.apache.http.HttpResponse
trait WebHookService {
@@ -21,46 +24,93 @@ trait WebHookService {
private val logger = LoggerFactory.getLogger(classOf[WebHookService])
def getWebHookURLs(owner: String, repository: String)(implicit s: Session): List[WebHook] =
WebHooks.filter(_.byRepository(owner, repository)).sortBy(_.url).list
/** get All WebHook informations of repository */
def getWebHooks(owner: String, repository: String)(implicit s: Session): List[(WebHook, Set[WebHook.Event])] =
WebHooks.filter(_.byRepository(owner, repository))
.innerJoin(WebHookEvents).on { (w, t) => t.byWebHook(w) }
.map{ case (w,t) => w -> t.event }
.list.groupBy(_._1).mapValues(_.map(_._2).toSet).toList.sortBy(_._1.url)
def addWebHookURL(owner: String, repository: String, url :String)(implicit s: Session): Unit =
/** get All WebHook informations of repository event */
def getWebHooksByEvent(owner: String, repository: String, event: WebHook.Event)(implicit s: Session): List[WebHook] =
WebHookEvents.filter(t => t.byRepository(owner, repository) && t.event === event.bind)
.list.map(t => WebHook(t.userName, t.repositoryName, t.url))
/** get All WebHook information from repository to url */
def getWebHook(owner: String, repository: String, url: String)(implicit s: Session): Option[(WebHook, Set[WebHook.Event])] =
WebHooks
.filter(_.byPrimaryKey(owner, repository, url))
.innerJoin(WebHookEvents).on { (w, t) => t.byWebHook(w) }
.map{ case (w,t) => w -> t.event }
.list.groupBy(_._1).mapValues(_.map(_._2).toSet).headOption
def addWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event])(implicit s: Session): Unit = {
WebHooks insert WebHook(owner, repository, url)
def deleteWebHookURL(owner: String, repository: String, url :String)(implicit s: Session): Unit =
WebHooks.filter(_.byPrimaryKey(owner, repository, url)).delete
def callWebHookOf(owner: String, repository: String, eventName: String)(makePayload: => Option[WebHookPayload])(implicit s: Session, c: JsonFormat.Context): Unit = {
val webHookURLs = getWebHookURLs(owner, repository)
if(webHookURLs.nonEmpty){
makePayload.map(callWebHook(eventName, webHookURLs, _))
events.toSet.map{ event: WebHook.Event =>
WebHookEvents insert WebHookEvent(owner, repository, url, event)
}
}
def callWebHook(eventName: String, webHookURLs: List[WebHook], payload: WebHookPayload)(implicit c: JsonFormat.Context): Unit = {
import org.apache.http.client.methods.HttpPost
def updateWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event])(implicit s: Session): Unit = {
WebHookEvents.filter(_.byWebHook(owner, repository, url)).delete
events.toSet.map{ event: WebHook.Event =>
WebHookEvents insert WebHookEvent(owner, repository, url, event)
}
}
def deleteWebHook(owner: String, repository: String, url :String)(implicit s: Session): Unit =
WebHooks.filter(_.byPrimaryKey(owner, repository, url)).delete
def callWebHookOf(owner: String, repository: String, event: WebHook.Event)(makePayload: => Option[WebHookPayload])
(implicit s: Session, c: JsonFormat.Context): Unit = {
val webHooks = getWebHooksByEvent(owner, repository, event)
if(webHooks.nonEmpty){
makePayload.map(callWebHook(event, webHooks, _))
}
}
def callWebHook(event: WebHook.Event, webHookURLs: List[WebHook], payload: WebHookPayload)
(implicit c: JsonFormat.Context): List[(WebHook, String, Future[HttpRequest], Future[HttpResponse])] = {
import org.apache.http.impl.client.HttpClientBuilder
import scala.concurrent._
import ExecutionContext.Implicits.global
import org.apache.http.protocol.HttpContext
import org.apache.http.client.methods.HttpPost
if(webHookURLs.nonEmpty){
val json = JsonFormat(payload)
val httpClient = HttpClientBuilder.create.build
webHookURLs.foreach { webHookUrl =>
webHookURLs.map { webHookUrl =>
val reqPromise = Promise[HttpRequest]
val f = Future {
logger.debug(s"start web hook invocation for ${webHookUrl}")
val httpPost = new HttpPost(webHookUrl.url)
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded")
httpPost.addHeader("X-Github-Event", eventName)
val itcp = new org.apache.http.HttpRequestInterceptor{
def process(res: HttpRequest, ctx: HttpContext): Unit = {
reqPromise.success(res)
}
}
try{
val httpClient = HttpClientBuilder.create.addInterceptorLast(itcp).build
logger.debug(s"start web hook invocation for ${webHookUrl.url}")
val httpPost = new HttpPost(webHookUrl.url)
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded")
httpPost.addHeader("X-Github-Event", event.name)
httpPost.addHeader("X-Github-Delivery", java.util.UUID.randomUUID().toString)
val params: java.util.List[NameValuePair] = new java.util.ArrayList()
params.add(new BasicNameValuePair("payload", json))
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"))
val params: java.util.List[NameValuePair] = new java.util.ArrayList()
params.add(new BasicNameValuePair("payload", json))
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"))
httpClient.execute(httpPost)
httpPost.releaseConnection()
logger.debug(s"end web hook invocation for ${webHookUrl}")
val res = httpClient.execute(httpPost)
httpPost.releaseConnection()
logger.debug(s"end web hook invocation for ${webHookUrl}")
res
}catch{
case e:Throwable => {
if(!reqPromise.isCompleted){
reqPromise.failure(e)
}
throw e
}
}
}
f.onSuccess {
case s => logger.debug(s"Success: web hook request to ${webHookUrl.url}")
@@ -68,9 +118,12 @@ trait WebHookService {
f.onFailure {
case t => logger.error(s"Failed: web hook request to ${webHookUrl.url}", t)
}
(webHookUrl, json, reqPromise.future, f)
}
} else {
Nil
}
logger.debug("end callWebHook")
// logger.debug("end callWebHook")
}
}
@@ -80,8 +133,9 @@ trait WebHookPullRequestService extends WebHookService {
import WebHookService._
// https://developer.github.com/v3/activity/events/types/#issuesevent
def callIssuesWebHook(action: String, repository: RepositoryService.RepositoryInfo, issue: Issue, baseUrl: String, sender: Account)(implicit s: Session, context:JsonFormat.Context): Unit = {
callWebHookOf(repository.owner, repository.name, "issues"){
def callIssuesWebHook(action: String, repository: RepositoryService.RepositoryInfo, issue: Issue, baseUrl: String, sender: Account)
(implicit s: Session, context:JsonFormat.Context): Unit = {
callWebHookOf(repository.owner, repository.name, WebHook.Issues){
val users = getAccountsByUserNames(Set(repository.owner, issue.openedUserName), Set(sender))
for{
repoOwner <- users.get(repository.owner)
@@ -97,9 +151,10 @@ trait WebHookPullRequestService extends WebHookService {
}
}
def callPullRequestWebHook(action: String, repository: RepositoryService.RepositoryInfo, issueId: Int, baseUrl: String, sender: Account)(implicit s: Session, context:JsonFormat.Context): Unit = {
def callPullRequestWebHook(action: String, repository: RepositoryService.RepositoryInfo, issueId: Int, baseUrl: String, sender: Account)
(implicit s: Session, context:JsonFormat.Context): Unit = {
import WebHookService._
callWebHookOf(repository.owner, repository.name, "pull_request"){
callWebHookOf(repository.owner, repository.name, WebHook.PullRequest){
for{
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
users = getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
@@ -135,11 +190,13 @@ trait WebHookPullRequestService extends WebHookService {
ru <- Accounts if ru.userName === pr.requestUserName
iu <- Accounts if iu.userName === is.openedUserName
wh <- WebHooks if wh.byRepository(is.userName , is.repositoryName)
wht <- WebHookEvents if wht.event === WebHook.PullRequest.asInstanceOf[WebHook.Event].bind && wht.byWebHook(wh)
} yield {
((is, iu, pr, bu, ru), wh)
}).list.groupBy(_._1).mapValues(_.map(_._2))
def callPullRequestWebHookByRequestBranch(action: String, requestRepository: RepositoryService.RepositoryInfo, requestBranch: String, baseUrl: String, sender: Account)(implicit s: Session, context:JsonFormat.Context): Unit = {
def callPullRequestWebHookByRequestBranch(action: String, requestRepository: RepositoryService.RepositoryInfo, requestBranch: String, baseUrl: String, sender: Account)
(implicit s: Session, context:JsonFormat.Context): Unit = {
import WebHookService._
for{
((issue, issueUser, pullRequest, baseOwner, headOwner), webHooks) <- getPullRequestsByRequestForWebhook(requestRepository.owner, requestRepository.name, requestBranch)
@@ -155,7 +212,37 @@ trait WebHookPullRequestService extends WebHookService {
baseRepository = baseRepo,
baseOwner = baseOwner,
sender = sender)
callWebHook("pull_request", webHooks, payload)
callWebHook(WebHook.PullRequest, webHooks, payload)
}
}
}
trait WebHookPullRequestReviewCommentService extends WebHookService {
self: AccountService with RepositoryService with PullRequestService with IssuesService with CommitsService =>
def callPullRequestReviewCommentWebHook(action: String, comment: CommitComment, repository: RepositoryService.RepositoryInfo, issueId: Int, baseUrl: String, sender: Account)
(implicit s: Session, context:JsonFormat.Context): Unit = {
import WebHookService._
callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment){
for{
(issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
users = getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
baseOwner <- users.get(repository.owner)
headOwner <- users.get(pullRequest.requestUserName)
issueUser <- users.get(issue.openedUserName)
headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName, baseUrl)
} yield {
WebHookPullRequestReviewCommentPayload(
action = action,
comment = comment,
issue = issue,
issueUser = issueUser,
pullRequest = pullRequest,
headRepository = headRepo,
headOwner = headOwner,
baseRepository = repository,
baseOwner = baseOwner,
sender = sender)
}
}
}
}
@@ -164,8 +251,9 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
self: AccountService with RepositoryService with PullRequestService with IssuesService =>
import WebHookService._
def callIssueCommentWebHook(repository: RepositoryService.RepositoryInfo, issue: Issue, issueCommentId: Int, sender: Account)(implicit s: Session, context:JsonFormat.Context): Unit = {
callWebHookOf(repository.owner, repository.name, "issue_comment"){
def callIssueCommentWebHook(repository: RepositoryService.RepositoryInfo, issue: Issue, issueCommentId: Int, sender: Account)
(implicit s: Session, context:JsonFormat.Context): Unit = {
callWebHookOf(repository.owner, repository.name, WebHook.IssueComment){
for{
issueComment <- getComment(repository.owner, repository.name, issueCommentId.toString())
users = getAccountsByUserNames(Set(issue.openedUserName, repository.owner, issueComment.commentedUserName), Set(sender))
@@ -191,7 +279,8 @@ object WebHookService {
// https://developer.github.com/v3/activity/events/types/#pushevent
case class WebHookPushPayload(
pusher: ApiUser,
pusher: ApiPusher,
sender: ApiUser,
ref: String,
before: String,
after: String,
@@ -208,11 +297,12 @@ object WebHookService {
}
object WebHookPushPayload {
def apply(git: Git, pusher: Account, refName: String, repositoryInfo: RepositoryInfo,
def apply(git: Git, sender: Account, refName: String, repositoryInfo: RepositoryInfo,
commits: List[CommitInfo], repositoryOwner: Account,
newId: ObjectId, oldId: ObjectId): WebHookPushPayload =
WebHookPushPayload(
pusher = ApiUser(pusher),
pusher = ApiPusher(sender),
sender = ApiUser(sender),
ref = refName,
before = ObjectId.toString(oldId),
after = ObjectId.toString(newId),
@@ -289,4 +379,38 @@ object WebHookService {
comment = ApiComment(comment, RepositoryName(repository), issue.issueId, ApiUser(commentUser), issue.isPullRequest),
sender = ApiUser(sender))
}
// https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent
case class WebHookPullRequestReviewCommentPayload(
action: String,
comment: ApiPullRequestReviewComment,
pull_request: ApiPullRequest,
repository: ApiRepository,
sender: ApiUser
) extends WebHookPayload
object WebHookPullRequestReviewCommentPayload{
def apply(
action: String,
comment: CommitComment,
issue: Issue,
issueUser: Account,
pullRequest: PullRequest,
headRepository: RepositoryInfo,
headOwner: Account,
baseRepository: RepositoryInfo,
baseOwner: Account,
sender: Account
) : WebHookPullRequestReviewCommentPayload = {
val headRepoPayload = ApiRepository(headRepository, headOwner)
val baseRepoPayload = ApiRepository(baseRepository, baseOwner)
val senderPayload = ApiUser(sender)
WebHookPullRequestReviewCommentPayload(
action = action,
comment = ApiPullRequestReviewComment(comment, senderPayload, RepositoryName(baseRepository), issue.issueId),
pull_request = ApiPullRequest(issue, pullRequest, headRepoPayload, baseRepoPayload, ApiUser(issueUser)),
repository = baseRepoPayload,
sender = senderPayload)
}
}
}

View File

@@ -21,6 +21,8 @@ object AutoUpdate {
* The history of versions. A head of this sequence is the current BitBucket version.
*/
val versions = Seq(
new Version(3, 10),
new Version(3, 9),
new Version(3, 8),
new Version(3, 7) with SystemSettingsService {
override def update(conn: Connection, cl: ClassLoader): Unit = {

View File

@@ -3,7 +3,7 @@ package gitbucket.core.servlet
import java.io.File
import gitbucket.core.api
import gitbucket.core.model.Session
import gitbucket.core.model.{Session, WebHook}
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
import gitbucket.core.service.IssuesService.IssueSearchCondition
import gitbucket.core.service.WebHookService._
@@ -200,7 +200,7 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
}
// call web hook
callWebHookOf(owner, repository, "push"){
callWebHookOf(owner, repository, WebHook.Push){
for(pusherAccount <- getAccountByUserName(pusher);
ownerAccount <- getAccountByUserName(owner)) yield {
WebHookPushPayload(git, pusherAccount, command.getRefName, repositoryInfo, newCommits, ownerAccount,

View File

@@ -1,10 +1,11 @@
package gitbucket.core.ssh
import java.security.PublicKey
import gitbucket.core.service.SshKeyService
import gitbucket.core.servlet.Database
import org.apache.sshd.server.PublickeyAuthenticator
import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator
import org.apache.sshd.server.session.ServerSession
import java.security.PublicKey
class PublicKeyAuthenticator extends PublickeyAuthenticator with SshKeyService {

View File

@@ -1,20 +1,25 @@
package gitbucket.core.ssh
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
import javax.servlet.{ServletContextEvent, ServletContextListener}
import gitbucket.core.service.SystemSettingsService
import gitbucket.core.util.Directory
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider
import org.slf4j.LoggerFactory
import java.util.concurrent.atomic.AtomicBoolean
object SshServer {
private val logger = LoggerFactory.getLogger(SshServer.getClass)
private val server = org.apache.sshd.SshServer.setUpDefaultServer()
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)
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(s"${Directory.GitBucketHome}/gitbucket.ser", "RSA"))
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)

View File

@@ -1,10 +1,13 @@
package gitbucket.core.ssh
import java.security.PublicKey
import org.slf4j.LoggerFactory
import org.apache.commons.codec.binary.Base64
import org.apache.sshd.common.config.keys.KeyUtils
import org.apache.sshd.common.util.buffer.ByteArrayBuffer
import org.eclipse.jgit.lib.Constants
import org.apache.sshd.common.util.{KeyUtils, Buffer}
import org.slf4j.LoggerFactory
object SshUtil {
@@ -20,7 +23,7 @@ object SshUtil {
try {
val encodedKey = parts(1)
val decode = Base64.decodeBase64(Constants.encodeASCII(encodedKey))
Some(new Buffer(decode).getRawPublicKey)
Some(new ByteArrayBuffer(decode).getRawPublicKey)
} catch {
case e: Throwable =>
logger.debug(e.getMessage, e)

View File

@@ -75,7 +75,14 @@ class Mailer(private val smtp: Smtp) extends Notifier {
database withSession { implicit session =>
defining(
s"[${r.name}] ${issue.title} (#${issue.issueId})" ->
msg(Markdown.toHtml(content, r, false, true, false))) { case (subject, msg) =>
msg(Markdown.toHtml(
markdown = content,
repository = r,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = false,
enableLineBreaks = false
))) { case (subject, msg) =>
recipients(issue) { to =>
val email = new HtmlEmail
email.setHostName(smtp.host)

View File

@@ -19,6 +19,7 @@ object Markdown {
* @param enableWikiLink if true then wiki style link is available in markdown
* @param enableRefsLink if true then issue reference (e.g. #123) is rendered as link
* @param enableAnchor if true then anchor for headline is generated
* @param enableLineBreaks if true then render line breaks as &lt;br&gt;
* @param enableTaskList if true then task list syntax is available
* @param hasWritePermission true if user has writable to ths given repository
* @param pages the list of existing Wiki pages
@@ -28,6 +29,7 @@ object Markdown {
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableAnchor: Boolean,
enableLineBreaks: Boolean,
enableTaskList: Boolean = false,
hasWritePermission: Boolean = false,
pages: List[String] = Nil)(implicit context: Context): String = {
@@ -43,6 +45,7 @@ object Markdown {
val options = new Options()
options.setSanitize(true)
options.setBreaks(enableLineBreaks)
val renderer = new GitBucketMarkedRenderer(options, repository, enableWikiLink, enableRefsLink, enableAnchor, enableTaskList, hasWritePermission, pages)
Marked.marked(source, options, renderer)
}

View File

@@ -84,14 +84,25 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
/**
* Converts Markdown of Wiki pages to HTML.
*/
def markdown(value: String,
def markdown(markdown: String,
repository: RepositoryService.RepositoryInfo,
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableLineBreaks: Boolean,
enableTaskList: Boolean = false,
hasWritePermission: Boolean = false,
pages: List[String] = Nil)(implicit context: Context): Html =
Html(Markdown.toHtml(value, repository, enableWikiLink, enableRefsLink, true, enableTaskList, hasWritePermission, pages))
Html(Markdown.toHtml(
markdown = markdown,
repository = repository,
enableWikiLink = enableWikiLink,
enableRefsLink = enableRefsLink,
enableAnchor = true,
enableLineBreaks = enableLineBreaks,
enableTaskList = enableTaskList,
hasWritePermission = hasWritePermission,
pages = pages
))
/**
* Render the given source (only markdown is supported in default) as HTML.

View File

@@ -5,17 +5,17 @@
@import gitbucket.core.view.helpers._
@html.main("Applications"){
<div class="container">
<div class="row-fluid">
<div class="span3">
<div class="row">
<div class="col-md-3">
@menu("application", settings.ssh)
</div>
<div class="span9">
<div class="box">
<div class="box-header">Personal access tokens</div>
<div class="box-content">
<div class="col-md-9">
<div class="panel panel-default">
<div class="panel-heading strong">Personal access tokens</div>
<div class="panel-body">
@if(personalTokens.isEmpty && gneratedToken.isEmpty){
No tokens.
}else{
} else {
Tokens you have generated that can be used to access the GitBucket API.<hr>
}
@gneratedToken.map{ case (token, tokenString) =>
@@ -38,13 +38,13 @@
</div>
</div>
<form method="POST" action="@path/@account.userName/_personalToken" validate="true">
<div class="box">
<div class="box-header">Generate new token</div>
<div class="box-content">
<div class="panel panel-default">
<div class="panel-heading strong">Generate new token</div>
<div class="panel-body">
<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" style="width: 400px;"/>
<input type="text" name="note" id="note" class="form-control" style="width: 400px;"/>
<p class="muted">What's this token for?</p>
</fieldset>
<input type="submit" class="btn btn-success" value="Generate token"/>

View File

@@ -4,46 +4,46 @@
@import gitbucket.core.view.helpers._
@html.main("Edit your profile"){
<div class="container">
<div class="row-fluid">
<div class="span3">
<div class="row">
<div class="col-md-3">
@menu("profile", settings.ssh)
</div>
<div class="span9">
<div class="col-md-9">
@helper.html.information(info)
@if(LDAPUtil.isDummyMailAddress(account)){<div class="alert alert-danger">Please register your mail address.</div>}
<form action="@url(account.userName)/_edit" method="POST" validate="true">
<div class="box">
<div class="box-header">Profile</div>
<div class="box-content">
<div class="row-fluid">
<div class="span6">
<div class="panel panel-default">
<div class="panel-heading strong">Profile</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
@if(account.password.nonEmpty){
<fieldset>
<fieldset class="form-group">
<label for="password" class="strong">
Password (input to change password):
</label>
<input type="password" name="password" id="password" value="" autocomplete="off"/>
<input type="password" name="password" id="password" class="form-control" value="" autocomplete="off"/>
<span id="error-password" class="error"></span>
</fieldset>
}
<fieldset>
<fieldset class="form-group">
<label for="fullName" class="strong">Full Name:</label>
<input type="text" name="fullName" id="fullName" value="@account.fullName"/>
<input type="text" name="fullName" id="fullName" class="form-control" value="@account.fullName"/>
<span id="error-fullName" class="error"></span>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label for="mailAddress" class="strong">Mail Address:</label>
<input type="text" name="mailAddress" id="mailAddress" value="@if(!LDAPUtil.isDummyMailAddress(account)){@account.mailAddress}"/>
<input type="text" name="mailAddress" id="mailAddress" class="form-control" value="@if(!LDAPUtil.isDummyMailAddress(account)){@account.mailAddress}"/>
<span id="error-mailAddress" class="error"></span>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label for="url" class="strong">URL (optional):</label>
<input type="text" name="url" id="url" style="width: 300px;" value="@account.url"/>
<input type="text" name="url" id="url" class="form-control" value="@account.url"/>
<span id="error-url" class="error"></span>
</fieldset>
</div>
<div class="span6">
<fieldset>
<div class="col-md-6">
<fieldset class="form-group">
<label for="avatar" class="strong">Image (optional):</label>
@helper.html.uploadavatar(Some(account))
</fieldset>
@@ -54,7 +54,7 @@
<a href="@path/@account.userName/_delete" class="btn btn-danger" id="delete">Delete account</a>
</div>
<input type="submit" class="btn btn-success" value="Save"/>
@if(!LDAPUtil.isDummyMailAddress(account)){<a href="@url(account.userName)" class="btn">Cancel</a>}
@if(!LDAPUtil.isDummyMailAddress(account)){<a href="@url(account.userName)" class="btn btn-default">Cancel</a>}
</div>
</div>
</div>

View File

@@ -4,34 +4,34 @@
@html.main(if(account.isEmpty) "Create group" else "Edit group"){
<div class="container">
<form id="form" method="post" action="@if(account.isEmpty){@path/groups/new} else {@path/@account.get.userName/_editgroup}" validate="true">
<div class="row-fluid">
<div class="span5">
<fieldset>
<div class="row">
<div class="col-md-5">
<fieldset class="form-group">
<label for="groupName" class="strong">Group name</label>
<div>
<span id="error-groupName" class="error"></span>
</div>
<input type="text" name="groupName" id="groupName" value="@account.map(_.userName)"@if(account.isDefined){ readonly}/>
<input type="text" name="groupName" id="groupName" class="form-control" value="@account.map(_.userName)"@if(account.isDefined){ readonly}/>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label class="strong">URL (Optional)</label>
<div>
<span id="error-url" class="error"></span>
</div>
<input type="text" name="url" id="url" style="width: 300px;" value="@account.map(_.url)"/>
<input type="text" name="url" id="url" class="form-control" value="@account.map(_.url)"/>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label for="avatar" class="strong">Image (Optional)</label>
@helper.html.uploadavatar(account)
</fieldset>
</div>
<div class="span7">
<fieldset>
<div class="col-md-7">
<fieldset class="form-group">
<label class="strong">Members</label>
<ul id="member-list" class="collaborator">
</ul>
@helper.html.account("memberName", 200)
<input type="button" class="btn" value="Add" id="addMember"/>
<input type="button" class="btn btn-default" value="Add" id="addMember"/>
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.isManager).mkString(",")"/>
<div>
<span class="error" id="error-members"></span>
@@ -42,12 +42,12 @@
<fieldset class="margin">
@if(account.isDefined){
<div class="pull-right">
<a href="@url(account.get.userName)/_deletegroup" id="delete" class="btn btn-danger">Delete Group</a>
<a href="@url(account.get.userName)/_deletegroup" id="delete" class="btn btn-danger btn-lg">Delete Group</a>
</div>
}
<input type="submit" class="btn btn-success" value="@if(account.isEmpty){Create Group} else {Update Group}"/>
<input type="submit" class="btn btn-success btn-lg" value="@if(account.isEmpty){Create Group} else {Update Group}"/>
@if(account.isDefined){
<a href="@url(account.get.userName)" class="btn">Cancel</a>
<a href="@url(account.get.userName)" class="btn btn-default btn-lg">Cancel</a>
}
</fieldset>
</form>

View File

@@ -5,8 +5,8 @@
@html.main(account.userName){
<div class="container">
<div class="container-fluid">
<div class="row-fluid">
<div class="span4">
<div class="row">
<div class="col-md-4">
<div class="block">
<div class="account-image">@avatar(account.userName, 270)</div>
<div class="account-fullname">@account.fullName</div>
@@ -28,7 +28,7 @@
}
</div>
<div class="span8">
<div class="col-md-8">
<ul class="nav nav-tabs" style="margin-bottom: 5px;">
<li@if(active == "repositories"){ class="active"}><a href="@url(account.userName)?tab=repositories">Repositories</a></li>
@if(account.isGroupAccount){
@@ -39,14 +39,14 @@
@if(loginAccount.isDefined && loginAccount.get.userName == account.userName){
<li class="pull-right">
<div class="button-group">
<a href="@url(account.userName)/_edit" class="btn">Edit Your Profile</a>
<a href="@url(account.userName)/_edit" class="btn btn-default">Edit Your Profile</a>
</div>
</li>
}
@if(loginAccount.isDefined && account.isGroupAccount && isGroupManager){
<li class="pull-right">
<div class="button-group">
<a href="@url(account.userName)/_editgroup" class="btn">Edit Group</a>
<a href="@url(account.userName)/_editgroup" class="btn btn-default">Edit Group</a>
</div>
</li>
}

View File

@@ -4,45 +4,56 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
@import gitbucket.core.view.helpers._
@html.main("Create a New Repository"){
<div 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.
</p>
<form id="form" method="post" action="@path/new" validate="true">
<fieldset>
<label for="name" class="strong">Repository name:</label>
<div class="btn-group" style="margin-bottom: 10px;" id="owner-dropdown">
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="strong">@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="javascript:void(0);" data-name="@loginAccount.get.userName"><i class="octicon octicon-check"></i> <span>@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</span></a></li>
@groupNames.map { groupName =>
<li><a href="javascript:void(0);" data-name="@groupName"><i class="icon-white"></i> <span>@avatar(groupName, 20) @groupName</span></a></li>
}
</ul>
<input type="hidden" name="owner" id="owner" value="@loginAccount.get.userName"/>
</div>
<span class="slash">/</span>
<input type="text" name="name" id="name" autofocus />
<span id="error-name" class="error"></span>
<fieldset class="margin form-group">
<dl style="float: left;">
<dt>Owner</dt>
<dd style="margin-left: 0px;">
<div class="btn-group" id="owner-dropdown">
<button class="btn dropdown-toggle btn-default" data-toggle="dropdown">
<span class="strong">@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="javascript:void(0);" data-name="@loginAccount.get.userName"><i class="octicon octicon-check"></i> <span>@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</span></a></li>
@groupNames.map { groupName =>
<li><a href="javascript:void(0);" data-name="@groupName"><i class="icon-white"></i> <span>@avatar(groupName, 20) @groupName</span></a></li>
}
</ul>
<input type="hidden" name="owner" id="owner" value="@loginAccount.get.userName"/>
</div>
</dd>
</dl>
<span class="slash" style="float: left; margin-left: 10px; margin-right: 10px; margin-top: 15px;">/</span>
<dl>
<dt>Repository name</dt>
<dd style="margin-left: 0px;">
<input type="text" name="name" id="name" class="form-control" style="width: 200px;" autofocus />
<span id="error-name" class="error"></span>
</dd>
</dl>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label for="description" class="strong">Description (optional):</label>
<input type="text" name="description" id="description" style="width: 95%;"/>
<input type="text" name="description" id="description" class="form-control" style="width: 95%;"/>
</fieldset>
<fieldset class="margin">
<label class="radio">
<input type="radio" name="isPrivate" value="false" @if(isCreateRepoOptionPublic){checked}>
<span class="strong"><i class="octicon octicon-repo"></i>&nbsp;</i>&nbsp;Public</span><br>
<div>
<span>All users and guests can read this repository.</span>
<span class="strong"><i class="octicon octicon-repo"></i>&nbsp;</i>&nbsp;Public</span>
<div class="normal muted">
Anyone can see this repository. You choose who can commit.
</div>
</label>
</fieldset>
<fieldset>
<label class="radio">
<input type="radio" name="isPrivate" value="true" @if(!isCreateRepoOptionPublic){checked}>
<span class="strong"><i class="octicon octicon-lock"></i>&nbsp;</i>&nbsp;Private</span><br>
<div>
<span>Only collaborators can read this repository.</span>
<span class="strong"><i class="octicon octicon-lock"></i>&nbsp;</i>&nbsp;Private</span>
<div class="normal muted">
You choose who can see and commit to this repository.
</div>
</label>
</fieldset>
@@ -50,13 +61,13 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
<label for="createReadme" class="checkbox">
<input type="checkbox" name="createReadme" id="createReadme"/>
<span class="strong">Initialize this repository with a README</span>
<div>
<span>This will allow you to <code>git clone</code> the repository immediately.</span>
<div class="normal muted">
This will let you immediately clone the repository to your computer. Skip this step if youre importing an existing repository.
</div>
</label>
</fieldset>
<fieldset class="margin">
<input type="submit" class="btn btn-success" value="Create repository"/>
<fieldset class="margin form-actions">
<input type="submit" class="btn btn-success btn-lg" value="Create repository"/>
</fieldset>
</form>
</div>

View File

@@ -5,8 +5,8 @@
<div class="container">
<h3>Create your account</h3>
<form action="@path/register" method="POST" validate="true">
<div class="row-fluid">
<div class="span6">
<div class="row">
<div class="col-md-6">
<fieldset>
<label for="userName" class="strong">Username:</label>
<input type="text" name="userName" id="userName" value="" autofocus/>
@@ -35,7 +35,7 @@
<span id="error-url" class="error"></span>
</fieldset>
</div>
<div class="span6">
<div class="col-md-6">
<fieldset>
<label for="avatar" class="strong">Image (optional):</label>
@helper.html.uploadavatar(None)

View File

@@ -4,14 +4,14 @@
@import gitbucket.core.view.helpers._
@html.main("SSH Keys"){
<div class="container">
<div class="row-fluid">
<div class="span3">
<div class="row">
<div class="col-md-3">
@menu("ssh", settings.ssh)
</div>
<div class="span9">
<div class="box">
<div class="box-header">SSH Keys</div>
<div class="box-content">
<div class="col-md-9">
<div class="panel panel-default">
<div class="panel-heading strong">SSH Keys</div>
<div class="panel-body">
@if(sshKeys.isEmpty){
No keys
}
@@ -25,18 +25,18 @@
</div>
</div>
<form method="POST" action="@path/@account.userName/_ssh" validate="true">
<div class="box">
<div class="box-header">Add an SSH Key</div>
<div class="box-content">
<fieldset>
<div class="panel panel-default">
<div class="panel-heading strong">Add an SSH Key</div>
<div class="panel-body">
<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" style="width: 400px;"/>
<input type="text" name="title" id="title" class="form-control" style="width: 400px;"/>
</fieldset>
<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" style="width: 600px; height: 250px;"></textarea>
<textarea name="publicKey" id="publicKey" class="form-control" style="width: 600px; height: 250px;"></textarea>
</fieldset>
<input type="submit" class="btn btn-success" value="Add"/>
</div>

View File

@@ -1,8 +1,8 @@
@(active: String)(body: Html)(implicit context: gitbucket.core.controller.Context)
@import context._
<div class="container">
<div class="row-fluid">
<div class="span3">
<div class="container body">
<div class="row">
<div class="col-md-3">
<ul class="nav nav-tabs nav-stacked side-menu" id="system-admin-menu-container">
<li@if(active=="users"){ class="active"}>
<a href="@path/admin/users">User Management</a>
@@ -18,7 +18,7 @@
</li>
</ul>
</div>
<div class="span9">
<div class="col-md-9">
@body
</div>
</div>

View File

@@ -2,29 +2,41 @@
@import context._
@import gitbucket.core.view.helpers._
@html.main("Plugins"){
@admin.html.menu("plugins") {
<h1>Installed plugins</h1>
@admin.html.menu("plugins") {
<h1>Installed plugins</h1>
@if(plugins.size > 0) {
<ul>
@plugins.map {plugin =>
<li><a href="#@plugin.pluginId">@plugin.pluginId:@plugin.version</a></li>
}
</ul>
@if(plugins.size > 0) {
<ul>
@plugins.map {plugin =>
<li><a href="#@plugin.pluginId">@plugin.pluginId:@plugin.version</a></li>
}
</ul>
@plugins.map {plugin =>
<div class="box">
<div class="box-header">@plugin.pluginName</div>
<div class="box-content">
<p><span class="strong">Id:&nbsp;</span>@plugin.pluginId</p>
<p><span class="strong">Version:&nbsp;</span>@plugin.version</p>
<p><span class="strong">Name:&nbsp;</span>@plugin.pluginName</p>
<p class="muted">@plugin.description</p>
</div>
</div>
}
} else {
<p>No plugin detected on your gitbucket installation.</p>
}
@plugins.map {plugin =>
<div class="panel panel-default">
<div class="panel-heading strong">@plugin.pluginName</div>
<div class="panel-body">
<div class="row">
<label class="col-md-2">Id</label>
<span class="col-md-8">@plugin.pluginId</span>
</div>
<div class="row">
<label class="col-md-2">Version</label>
<span class="col-md-8">@plugin.version</span>
</div>
<div class="row">
<label class="col-md-2">Name</label>
<span class="col-md-8">@plugin.pluginName</span>
</div>
<div class="row">
<label class="col-md-2">Description</label>
<span class="col-md-8 muted">@plugin.description</span>
</div>
</div>
</div>
}
} else {
<p>No plugin detected on your gitbucket installation.</p>
}
}
}

View File

@@ -5,10 +5,10 @@
@html.main("System Settings"){
@menu("system"){
@helper.html.information(info)
<form action="@path/admin/system" method="POST" validate="true">
<div class="box">
<div class="box-header">System Settings</div>
<div class="box-content">
<form action="@path/admin/system" method="POST" validate="true" class="form-horizontal">
<div class="panel panel-default">
<div class="panel-heading strong">System Settings</div>
<div class="panel-body">
<!--====================================================================-->
<!-- GITBUCKET_HOME -->
<!--====================================================================-->
@@ -21,7 +21,7 @@
<label><span class="strong">Base URL</span> (e.g. <code>http://example.com/gitbucket</code>)</label>
<fieldset>
<div class="controls">
<input type="text" name="baseUrl" id="baseUrl" style="width: 400px" value="@settings.baseUrl"/>
<input type="text" name="baseUrl" id="baseUrl" class="form-control" value="@settings.baseUrl"/>
<span id="error-baseUrl" class="error"></span>
</div>
</fieldset>
@@ -36,7 +36,7 @@
<hr>
<label><span class="strong">Information</span> (HTML is available)</label>
<fieldset>
<textarea name="information" style="width: 600px; height: 100px;">@settings.information</textarea>
<textarea name="information" class="form-control" style="height: 100px;">@settings.information</textarea>
</fieldset>
<!--====================================================================-->
<!-- Account registration -->
@@ -46,11 +46,11 @@
<fieldset>
<label class="radio">
<input type="radio" name="allowAccountRegistration" value="true"@if(settings.allowAccountRegistration){ checked}>
<span class="strong">Allow</span> - Users can create accounts by themselves.
<span class="strong">Allow</span> <span class="normal">- Users can create accounts by themselves.</span>
</label>
<label class="radio">
<input type="radio" name="allowAccountRegistration" value="false"@if(!settings.allowAccountRegistration){ checked}>
<span class="strong">Deny</span> - Only administrators can create accounts.
<span class="strong">Deny</span> - <span class="normal">Only administrators can create accounts.</span>
</label>
</fieldset>
<hr>
@@ -58,11 +58,11 @@
<fieldset>
<label class="radio">
<input type="radio" name="isCreateRepoOptionPublic" value="true"@if(settings.isCreateRepoOptionPublic){ checked}>
<span class="strong">Public</span> - All users and guests can read that repository.
<span class="strong">Public</span> <span class="normal">- All users and guests can read that repository.</span>
</label>
<label class="radio">
<input type="radio" name="isCreateRepoOptionPublic" value="false"@if(!settings.isCreateRepoOptionPublic){ checked}>
<span class="strong">Private</span> - Only collaborators can read that repository.
<span class="strong">Private</span> <span class="normal">- Only collaborators can read that repository.</span>
</label>
</fieldset>
<!--====================================================================-->
@@ -73,11 +73,11 @@
<fieldset>
<label class="radio">
<input type="radio" name="allowAnonymousAccess" value="true"@if(settings.allowAnonymousAccess){ checked}>
<span class="strong">Allow</span> - Anyone can view public repositories, user/group profiles.
<span class="strong">Allow</span> <span class="normal">- Anyone can view public repositories, user/group profiles.</span>
</label>
<label class="radio">
<input type="radio" name="allowAnonymousAccess" value="false"@if(!settings.allowAnonymousAccess){ checked}>
<span class="strong">Deny</span> - Users must authenticate before viewing any information
<span class="strong">Deny</span> <span class="normal">- Users must authenticate before viewing any information.</span>
</label>
</fieldset>
<!--====================================================================-->
@@ -86,7 +86,7 @@
<hr>
<label><span class="strong">Limit of activity logs</span> (Unlimited if it's not specified or zero)</label>
<div class="controls">
<input type="text" id="activityLogLimit" name="activityLogLimit" class="input-mini" value="@settings.activityLogLimit"/>
<input type="text" id="activityLogLimit" name="activityLogLimit" class="form-control input-mini" value="@settings.activityLogLimit"/>
<span id="error-activityLogLimit" class="error"></span>
</div>
<!--====================================================================-->
@@ -111,13 +111,11 @@
Enable SSH access to git repository
</label>
</fieldset>
<div class="form-horizontal ssh">
<div class="control-group">
<label class="control-label" for="sshPort">SSH Port</label>
<div class="controls">
<input type="text" id="sshPort" name="sshPort" value="@settings.sshPort"/>
<span id="error-sshPort" class="error"></span>
</div>
<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>
</div>
<p class="muted">
@@ -130,92 +128,90 @@
<label class="strong">Authentication</label>
<fieldset>
<label class="checkbox">
<input type="checkbox" id="ldapAuthentication" name="ldapAuthentication"@if(settings.ldap){ checked}/>
<input type="checkbox" id="ldapAuthentication" name="ldapAuthentication"@if(settings.ldap){ checked} />
LDAP
</label>
</fieldset>
<div class="form-horizontal ldap">
<div class="control-group">
<label class="control-label" for="ldapHost">LDAP Host</label>
<div class="controls">
<input type="text" id="ldapHost" name="ldap.host" value="@settings.ldap.map(_.host)"/>
<div class="ldap">
<div class="form-group">
<label class="control-label col-md-3" for="ldapHost">LDAP Host</label>
<div class="col-md-9">
<input type="text" id="ldapHost" name="ldap.host" class="form-control" value="@settings.ldap.map(_.host)"/>
<span id="error-ldap_host" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapPort">LDAP Port</label>
<div class="controls">
<input type="text" id="ldapPort" name="ldap.port" class="input-mini" value="@settings.ldap.map(_.port)"/>
<div class="form-group">
<label class="control-label col-md-3" for="ldapPort">LDAP Port</label>
<div class="col-md-9">
<input type="text" id="ldapPort" name="ldap.port" class="form-control input-mini" value="@settings.ldap.map(_.port)"/>
<span id="error-ldap_port" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapBindDN">Bind DN</label>
<div class="controls">
<input type="text" id="ldapBindDN" name="ldap.bindDN" value="@settings.ldap.map(_.bindDN)"/>
<div class="form-group">
<label class="control-label col-md-3" for="ldapBindDN">Bind DN</label>
<div class="col-md-9">
<input type="text" id="ldapBindDN" name="ldap.bindDN" class="form-control" value="@settings.ldap.map(_.bindDN)"/>
<span id="error-ldap_bindDN" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapBindPassword">Bind Password</label>
<div class="controls">
<input type="password" id="ldapBindPassword" name="ldap.bindPassword" value="@settings.ldap.map(_.bindPassword)"/>
<div class="form-group">
<label class="control-label col-md-3" for="ldapBindPassword">Bind Password</label>
<div class="col-md-9">
<input type="password" id="ldapBindPassword" name="ldap.bindPassword" class="form-control" value="@settings.ldap.map(_.bindPassword)"/>
<span id="error-ldap_bindPassword" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapBaseDN">Base DN</label>
<div class="controls">
<input type="text" id="ldapBaseDN" name="ldap.baseDN" value="@settings.ldap.map(_.baseDN)"/>
<div class="form-group">
<label class="control-label col-md-3" for="ldapBaseDN">Base DN</label>
<div class="col-md-9">
<input type="text" id="ldapBaseDN" name="ldap.baseDN" class="form-control" value="@settings.ldap.map(_.baseDN)"/>
<span id="error-ldap_baseDN" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapUserNameAttribute">User name attribute</label>
<div class="controls">
<input type="text" id="ldapUserNameAttribute" name="ldap.userNameAttribute" value="@settings.ldap.map(_.userNameAttribute)"/>
<div class="form-group">
<label class="control-label col-md-3" for="ldapUserNameAttribute">User name attribute</label>
<div class="col-md-9">
<input type="text" id="ldapUserNameAttribute" name="ldap.userNameAttribute" class="form-control" value="@settings.ldap.map(_.userNameAttribute)"/>
<span id="error-ldap_userNameAttribute" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapAdditionalFilterCondition">Additional filter condition</label>
<div class="controls">
<input type="text" id="ldapAdditionalFilterCondition" name="ldap.additionalFilterCondition" value="@settings.ldap.map(_.additionalFilterCondition)"/>
<div class="form-group">
<label class="control-label col-md-3" for="ldapAdditionalFilterCondition">Additional filter condition</label>
<div class="col-md-9">
<input type="text" id="ldapAdditionalFilterCondition" name="ldap.additionalFilterCondition" class="form-control" value="@settings.ldap.map(_.additionalFilterCondition)"/>
<span id="error-ldap_additionalFilterCondition" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapFullNameAttribute">Full name attribute</label>
<div class="controls">
<input type="text" id="ldapFullNameAttribute" name="ldap.fullNameAttribute" value="@settings.ldap.map(_.fullNameAttribute)"/>
<div class="form-group">
<label class="control-label col-md-3" for="ldapFullNameAttribute">Full name attribute</label>
<div class="col-md-9">
<input type="text" id="ldapFullNameAttribute" name="ldap.fullNameAttribute" class="form-control" value="@settings.ldap.map(_.fullNameAttribute)"/>
<span id="error-ldap_fullNameAttribute" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapMailAttribute">Mail address attribute</label>
<div class="controls">
<input type="text" id="ldapMailAttribute" name="ldap.mailAttribute" value="@settings.ldap.map(_.mailAttribute)"/>
<div class="form-group">
<label class="control-label col-md-3" for="ldapMailAttribute">Mail address attribute</label>
<div class="col-md-9">
<input type="text" id="ldapMailAttribute" name="ldap.mailAttribute" class="form-control" value="@settings.ldap.map(_.mailAttribute)"/>
<span id="error-ldap_mailAttribute" class="error"></span>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" name="ldap.tls"@if(settings.ldap.flatMap(_.tls).getOrElse(false)){ checked}/> Enable TLS
</label>
<div class="form-group">
<label class="control-label col-md-3">Enable TLS</label>
<div class="col-md-9">
<input type="checkbox" name="ldap.tls"@if(settings.ldap.flatMap(_.tls).getOrElse(false)){ checked}/>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" name="ldap.ssl"@if(settings.ldap.flatMap(_.ssl).getOrElse(false)){ checked}/> Enable SSL
</label>
<div class="form-group">
<label class="control-label col-md-3">Enable SSL</label>
<div class="col-md-9">
<input type="checkbox" name="ldap.ssl"@if(settings.ldap.flatMap(_.ssl).getOrElse(false)){ checked}/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ldapBindDN">Keystore</label>
<div class="controls">
<input type="text" id="ldapKeystore" name="ldap.keystore" value="@settings.ldap.map(_.keystore)"/>
<div class="form-group">
<label class="control-label col-md-3" for="ldapBindDN">Keystore</label>
<div class="col-md-9">
<input type="text" id="ldapKeystore" name="ldap.keystore" class="form-control" value="@settings.ldap.map(_.keystore)"/>
<span id="error-ldap_keystore" class="error"></span>
</div>
</div>
@@ -242,54 +238,53 @@
SMTP
</label>
</fieldset>
<div class="form-horizontal useSMTP">
<div class="control-group">
<label class="control-label" for="smtpHost">SMTP Host</label>
<div class="controls">
<input type="text" id="smtpHost" name="smtp.host" value="@settings.smtp.map(_.host)"/>
<div class="useSMTP">
<div class="form-group">
<label class="control-label col-md-3" for="smtpHost">SMTP Host</label>
<div class="col-md-9">
<input type="text" id="smtpHost" name="smtp.host" class="form-control" value="@settings.smtp.map(_.host)"/>
<span id="error-smtp_host" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="smtpPort">SMTP Port</label>
<div class="controls">
<input type="text" id="smtpPort" name="smtp.port" class="input-mini" value="@settings.smtp.map(_.port)"/>
<div class="form-group">
<label class="control-label col-md-3" for="smtpPort">SMTP Port</label>
<div class="col-md-9">
<input type="text" id="smtpPort" name="smtp.port" class="form-control input-mini" value="@settings.smtp.map(_.port)"/>
<span id="error-smtp_port" class="error"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="smtpUser">SMTP User</label>
<div class="controls">
<input type="text" id="smtpUser" name="smtp.user" value="@settings.smtp.map(_.user)"/>
<div class="form-group">
<label class="control-label col-md-3" for="smtpUser">SMTP User</label>
<div class="col-md-9">
<input type="text" id="smtpUser" name="smtp.user" class="form-control" value="@settings.smtp.map(_.user)"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="smtpPassword">SMTP Password</label>
<div class="controls">
<input type="password" id="smtpPassword" name="smtp.password" value="@settings.smtp.map(_.password)"/>
<div class="form-group">
<label class="control-label col-md-3" for="smtpPassword">SMTP Password</label>
<div class="col-md-9">
<input type="password" id="smtpPassword" name="smtp.password" class="form-control" value="@settings.smtp.map(_.password)"/>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input type="checkbox" name="smtp.ssl"@if(settings.smtp.flatMap(_.ssl).getOrElse(false)){ checked}/> Enable SSL
</label>
<div class="form-group">
<label class="control-label col-md-3" for="smtpPassword">Enable SSL</label>
<div class="col-md-9">
<input type="checkbox" name="smtp.ssl"@if(settings.smtp.flatMap(_.ssl).getOrElse(false)){ checked}/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="fromAddress">FROM Address</label>
<div class="controls">
<input type="text" id="fromAddress" name="smtp.fromAddress" value="@settings.smtp.map(_.fromAddress)"/>
<div class="form-group">
<label class="control-label col-md-3" for="fromAddress">FROM Address</label>
<div class="col-md-9">
<input type="text" id="fromAddress" name="smtp.fromAddress" class="form-control" value="@settings.smtp.map(_.fromAddress)"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="fromName">FROM Name</label>
<div class="controls">
<input type="text" id="fromName" name="smtp.fromName" value="@settings.smtp.map(_.fromName)"/>
<div class="form-group">
<label class="control-label col-md-3" for="fromName">FROM Name</label>
<div class="col-md-9">
<input type="text" id="fromName" name="smtp.fromName" class="form-control" value="@settings.smtp.map(_.fromName)"/>
</div>
</div>
<p class="muted">
Enable notification not only SMTP configuration if you want to send nitification email.
Enable notification not only SMTP configuration if you want to send notification email.
</p>
</div>
</div>
@@ -322,4 +317,4 @@ $(function(){
$('.ldap input').prop('disabled', !$(this).prop('checked'));
}).change();
});
</script>
</script>

View File

@@ -4,14 +4,14 @@
@html.main(if(account.isEmpty) "New Group" else "Update Group"){
@admin.html.menu("users"){
<form method="POST" action="@if(account.isEmpty){@path/admin/users/_newgroup} else {@path/admin/users/@account.get.userName/_editgroup}" validate="true">
<div class="row-fluid">
<div class="span5">
<fieldset>
<div class="row">
<div class="col-md-5">
<fieldset class="form-group">
<label for="groupName" class="strong">Group name</label>
<div>
<span id="error-groupName" class="error"></span>
</div>
<input type="text" name="groupName" id="groupName" value="@account.map(_.userName)"@if(account.isDefined){ readonly}/>
<input type="text" name="groupName" id="groupName" class="form-control" value="@account.map(_.userName)"@if(account.isDefined){ readonly}/>
@if(account.isDefined){
<label for="removed">
<input type="checkbox" name="removed" id="removed" value="true" @if(account.get.isRemoved){checked}/>
@@ -19,25 +19,25 @@
</label>
}
</fieldset>
<fieldset>
<fieldset class="form-group">
<label class="strong">URL (Optional)</label>
<div>
<span id="error-url" class="error"></span>
</div>
<input type="text" name="url" id="url" value="@account.map(_.url)"/>
<input type="text" name="url" id="url" class="form-control" value="@account.map(_.url)"/>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label for="avatar" class="strong">Image (Optional)</label>
@helper.html.uploadavatar(account)
</fieldset>
</div>
<div class="span7">
<fieldset>
<div class="col-md-7">
<fieldset class="form-group">
<label class="strong">Members</label>
<ul id="member-list" class="collaborator">
</ul>
@helper.html.account("memberName", 200)
<input type="button" class="btn" value="Add" id="addMember"/>
<input type="button" class="btn btn-default" value="Add" id="addMember"/>
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.isManager).mkString(",")"/>
<div>
<span class="error" id="error-members"></span>
@@ -47,7 +47,7 @@
</div>
<fieldset class="margin">
<input type="submit" class="btn btn-success" value="@if(account.isEmpty){Create Group} else {Update Group}"/>
<a href="@path/admin/users" class="btn">Cancel</a>
<a href="@path/admin/users" class="btn btn-default">Cancel</a>
</fieldset>
</form>
}

View File

@@ -4,8 +4,8 @@
@html.main("Manage Users"){
@admin.html.menu("users"){
<div class="pull-right" style="margin-bottom: 4px;">
<a href="@path/admin/users/_newuser" class="btn">New User</a>
<a href="@path/admin/users/_newgroup" class="btn">New Group</a>
<a href="@path/admin/users/_newuser" class="btn btn-default">New User</a>
<a href="@path/admin/users/_newgroup" class="btn btn-default">New Group</a>
</div>
<label for="includeRemoved">
<input type="checkbox" id="includeRemoved" name="includeRemoved" @if(includeRemoved){checked}/>

View File

@@ -3,14 +3,14 @@
@html.main(if(account.isEmpty) "New User" else "Update User"){
@admin.html.menu("users"){
<form method="POST" action="@if(account.isEmpty){@path/admin/users/_newuser} else {@path/admin/users/@account.get.userName/_edituser}" validate="true">
<div class="row-fluid">
<div class="span6">
<fieldset>
<div class="row">
<div class="col-md-6">
<fieldset class="form-group">
<label for="userName" class="strong">Username:</label>
<div>
<span id="error-userName" class="error"></span>
</div>
<input type="text" name="userName" id="userName" value="@account.map(_.userName)"@if(account.isDefined){ readonly}/>
<input type="text" name="userName" id="userName" class="form-control" value="@account.map(_.userName)"@if(account.isDefined){ readonly}/>
@if(account.isDefined){
<label for="removed">
<input type="checkbox" name="removed" id="removed" value="true" @if(account.get.isRemoved){checked}/>
@@ -22,7 +22,7 @@
}
</fieldset>
@if(account.map(_.password.nonEmpty).getOrElse(true)){
<fieldset>
<fieldset class="form-group">
<label for="password" class="strong">
Password
@if(account.isDefined){
@@ -33,24 +33,24 @@
<div>
<span id="error-password" class="error"></span>
</div>
<input type="password" name="password" id="password" value="" autocomplete="off"/>
<input type="password" name="password" id="password" class="form-control" value="" autocomplete="off"/>
</fieldset>
}
<fieldset>
<fieldset class="form-group">
<label for="fullName" class="strong">Full Name:</label>
<div>
<span id="error-fullName" class="error"></span>
</div>
<input type="text" name="fullName" id="fullName" value="@account.map(_.fullName)"/>
<input type="text" name="fullName" id="fullName" class="form-control" value="@account.map(_.fullName)"/>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label for="mailAddress" class="strong">Mail Address:</label>
<div>
<span id="error-mailAddress" class="error"></span>
</div>
<input type="text" name="mailAddress" id="mailAddress" value="@account.map(_.mailAddress)"/>
<input type="text" name="mailAddress" id="mailAddress" class="form-control" value="@account.map(_.mailAddress)"/>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label class="strong">User Type:</label>
<label class="radio" for="userType_Normal">
<input type="radio" name="isAdmin" id="userType_Normal" value="false"@if(account.isEmpty || !account.get.isAdmin){ checked}/> Normal
@@ -59,16 +59,16 @@
<input type="radio" name="isAdmin" id="userType_Admin" value="true"@if(account.isDefined && account.get.isAdmin){ checked}/> Administrator
</label>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label class="strong">URL (Optional):</label>
<div>
<span id="error-url" class="error"></span>
</div>
<input type="text" name="url" id="url" style="width: 400px;" value="@account.map(_.url)"/>
<input type="text" name="url" id="url" class="form-control" value="@account.map(_.url)"/>
</fieldset>
</div>
<div class="span6">
<fieldset>
<div class="col-md-6">
<fieldset class="form-group">
<label for="avatar" class="strong">Image (Optional)</label>
@helper.html.uploadavatar(account)
</fieldset>
@@ -76,7 +76,7 @@
</div>
<fieldset class="margin">
<input type="submit" class="btn btn-success" value="@if(account.isEmpty){Create User} else {Update User}"/>
<a href="@path/admin/users" class="btn">Cancel</a>
<a href="@path/admin/users" class="btn btn-default">Cancel</a>
</fieldset>
</form>
}

View File

@@ -10,53 +10,63 @@
@import gitbucket.core.service.IssuesService
@import gitbucket.core.service.IssuesService.IssueInfo
<table class="table table-bordered table-hover table-issues">
<tr>
<th style="background-color: #eee;">
@dashboard.html.header(openCount, closedCount, condition, groups)
</th>
</tr>
@issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
<tr>
<td style="padding-top: 15px; padding-bottom: 15px;">
@if(issue.isPullRequest){
<i class="octicon octicon-git-pull-request @(if(issue.closed) "closed" else "open")"></i>
} else {
<i class="octicon octicon-issue-@(if(issue.closed) "closed" else "opened")"></i>
}
<a href="@path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a>&nbsp;&#xFF65;
@if(issue.isPullRequest){
<a href="@path/@issue.userName/@issue.repositoryName/pull/@issue.issueId" class="issue-title">@issue.title</a>
} else {
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-title">@issue.title</a>
}
@gitbucket.core.issues.html.commitstatus(issue, commitStatus)
@labels.map { label =>
<span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span>
}
<span class="pull-right muted">
@issue.assignedUserName.map { userName =>
@avatar(userName, 20, tooltip = true)
}
@if(commentCount > 0){
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">
<i class="octicon octicon-comment active"></i> @commentCount
</a>
} else {
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count" style="color: silver;">
<i class="octicon octicon-comment"></i> @commentCount
</a>
}
</span>
<div class="small muted" style="margin-left: 20px; margin-top: 5px;">
#@issue.issueId opened by @user(issue.openedUserName, styleClass="username") @datetime(issue.registeredDate)
@milestone.map { milestone =>
<span style="margin: 20px;"><a href="@condition.copy(milestone = Some(Some(milestone))).toURL" class="username"><i class="octicon octicon-milestone"></i>
@milestone</a></span>
}
</div>
</td>
</tr>
}
<thead>
<tr>
<th style="background-color: #eee;">
@dashboard.html.header(openCount, closedCount, condition, groups)
</th>
</tr>
</thead>
<tbody>
@issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
<tr>
<td style="padding-top: 12px; padding-bottom: 12px;">
@if(issue.isPullRequest){
<i class="octicon octicon-git-pull-request @(if(issue.closed) "closed" else "open")"></i>
} else {
<i class="octicon octicon-issue-@(if(issue.closed) "closed" else "opened")"></i>
}
<a href="@path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a>&nbsp;&#xFF65;
@if(issue.isPullRequest){
<a href="@path/@issue.userName/@issue.repositoryName/pull/@issue.issueId" class="issue-title">@issue.title</a>
} else {
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-title">@issue.title</a>
}
@gitbucket.core.issues.html.commitstatus(issue, commitStatus)
@labels.map { label =>
<span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span>
}
<span class="pull-right muted">
@issue.assignedUserName.map { userName =>
@avatar(userName, 20, tooltip = true)
}
@if(commentCount > 0){
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">
<i class="octicon octicon-comment active"></i> @commentCount
</a>
} else {
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count" style="color: silver;">
<i class="octicon octicon-comment"></i> @commentCount
</a>
}
</span>
<div class="small muted" style="margin-left: 20px; margin-top: 2px;">
#@issue.issueId opened by @user(issue.openedUserName, styleClass="username") @datetime(issue.registeredDate)
@milestone.map { milestone =>
<span style="margin: 20px;"><a href="@condition.copy(milestone = Some(Some(milestone))).toURL" class="username"><i class="octicon octicon-milestone"></i> @milestone</a></span>
}
</div>
</td>
</tr>
}
@if(issues.isEmpty){
<tr>
<td style="padding: 20px; background-color: #eee; text-align: center;">
No results matched your search.
</td>
</tr>
}
</tbody>
</table>
<div class="pull-right">
@helper.html.paginator(page, (if(condition.state == "open") openCount else closedCount), IssuesService.IssueLimit, 10, condition.toURL)

View File

@@ -3,20 +3,18 @@
condition: gitbucket.core.service.IssuesService.IssueSearchCondition)(implicit context: gitbucket.core.controller.Context)
@import context._
@import gitbucket.core.view.helpers._
<ul class="nav nav-pills-group pull-left fill-width">
<li class="@if(filter == "created_by"){active} first">
<ul class="nav nav-pills pull-left" style="line-height: 14px;">
<li class="@if(filter == "created_by"){active}">
<a href="@path/dashboard/@active/created_by@condition.copy(author = None, assigned = None).toURL">Created</a>
</li>
<li class="@if(filter == "assigned"){active}">
<a href="@path/dashboard/@active/assigned@condition.copy(author = None, assigned = None).toURL">Assigned</a>
</li>
<li class="@if(filter == "mentioned"){active} last">
<li class="@if(filter == "mentioned"){active}">
<a href="@path/dashboard/@active/mentioned@condition.copy(author = None, assigned = None).toURL">Mentioned</a>
</li>
<li class="pull-right">
<form method="GET" id="search-filter-form" action="@path/dashboard/@active" style="margin-bottom: 0px;">
<input type="text" id="search-filter-box" class="input-xlarge" name="q" style="height: 24px; width: 400px;"
value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
</form>
</li>
</ul>
<form method="GET" id="search-filter-form" action="@path/dashboard/@active" class="pull-right">
<input type="text" id="search-filter-box" class="form-control input-lg" name="q" style="width: 400px;"
value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
</form>

View File

@@ -4,17 +4,14 @@
<div class="dashboard-nav">
<div class="container">
<a href="@path/" @if(active == ""){ class="active"}>
<i class="octicon octicon-rss"></i>
News Feed
<i class="octicon octicon-rss"></i> News Feed
</a>
@if(loginAccount.isDefined){
<a href="@path/dashboard/pulls" @if(active == "pulls" ){ class="active"}>
<i class="octicon octicon-git-pull-request"></i>
Pull Requests
<i class="octicon octicon-git-pull-request"></i> Pull Requests
</a>
<a href="@path/dashboard/issues" @if(active == "issues"){ class="active"}>
<i class="octicon octicon-issue-opened"></i>
Issues
<i class="octicon octicon-issue-opened"></i> Issues
</a>
}
</div>
@@ -27,8 +24,6 @@ div.dashboard-nav {
margin-bottom: 20px;
}
div.dashboard-nav a {
line-height: 10px;
margin-left: 20px;
@@ -38,7 +33,6 @@ div.dashboard-nav a {
color: #888;
}
div.dashboard-nav .octicon{
width: 16px;
height: 16px;

View File

@@ -1,6 +1,8 @@
@(id: String, width: Int)(implicit context: gitbucket.core.controller.Context)
@import context._
<input type="text" name="@id" id="@id" style="width: @{width}px; margin-bottom: 0px;"/>
<span style="margin-right: 0px;">
<input type="text" name="@id" id="@id" class="form-control" autocomplete="off" style="width: @{width}px; margin-bottom: 0px; display: inline; vertical-align: middle;"/>
</span>
<script>
$(function(){
$('#@id').typeahead({

View File

@@ -29,7 +29,10 @@ $(function(){
}
// Adjust clickable area width
$('#@textareaId').next('div.clickable').css('width', ($('#@textareaId').width() + 8) + 'px');
$('#@textareaId').next('div.clickable').css({
'width': ($('#@textareaId').width() + 18) + 'px',
'font-size': '13px'
});
});
</script>
}

View File

@@ -7,11 +7,10 @@
@import gitbucket.core.view.helpers._
@helper.html.dropdown(
value = if(branch.length == 40) branch.substring(0, 10) else branch,
prefix = if(branch.length == 40) "tree" else if(repository.branchList.contains(branch)) "branch" else "tree",
mini = false
prefix = if(branch.length == 40) "tree" else if(repository.branchList.contains(branch)) "branch" else "tree"
) {
<li><div id="branch-control-title">Switch branches<button id="branch-control-close" class="pull-right">&times</button></div></li>
<li><input id="branch-control-input" type="text" placeholder="Find or create branch ..."/></li>
<li><input id="branch-control-input" type="text" class="form-control input-sm" placeholder="Find or create branch ..."/></li>
@body
@if(hasWritePermission) {
<li id="create-branch" style="display: none;">

View File

@@ -6,18 +6,19 @@
@import gitbucket.core._
@import gitbucket.core.view.helpers._
<div class="@if(comment.fileName.isDefined && (!latestCommitId.isDefined || latestCommitId.get == comment.commitId)){inline-comment}"
id="discussion_r@comment.commentId"
@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="box commit-comment-box commit-comment-@comment.commentId">
<div class="box-header">
<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">
commented
@if(comment.pullRequest){
@if(comment.issueId.isDefined){
on this Pull Request
}else{
} else {
@if(comment.fileName.isDefined){
on @comment.fileName.get
}
@@ -32,8 +33,16 @@
}
</span>
</div>
<div class="box-content-bottom commit-commentContent-@comment.commentId markdown-body">
@markdown(comment.content, repository, false, true, true, hasWritePermission)
<div class="panel-body issue-content commit-commentContent-@comment.commentId markdown-body">
@markdown(
markdown = comment.content,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = false,
hasWritePermission = hasWritePermission
)
</div>
</div>
</div>

View File

@@ -1,7 +1,7 @@
@(id: String, value: String, prepend: Boolean = false)(html: Html)
<div class="input-append @if(prepend){input-prepend}" style="margin-bottom: 0px;">
<div class="input-group @if(prepend){input-prepend}" style="margin-bottom: 0px;">
@html
<span id="@id" class="add-on btn" data-clipboard-text="@value" data-placement="bottom" title="copy to clipboard"><i class="octicon octicon-clippy"></i></span>
<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>
<script>
// copy to clipboard

View File

@@ -1,11 +1,13 @@
@(name: String, value: Option[java.util.Date])
@import gitbucket.core.view.helpers
<div id="@name" class="input-append date" data-date-format="yyyy-mm-dd" data-date="@value.map(helpers.date)">
<input class="span2" name="@name" type="text" readonly="" value="@value.map(helpers.date)" size="16"/>
<span class="add-on"><i class="octicon octicon-calendar"></i></span>
<div id="@name" class="input-group date" data-date="@value.map(helpers.date)" style="width: 180px;">
<input class="form-control" name="@name" type="text" value="@value.map(helpers.date)"/>
<span class="input-group-addon"><i class="octicon octicon-calendar"></i></span>
</div>
<script>
$(function(){
$('#@name').datepicker();
$('#@name').datetimepicker({
format: 'YYYY-MM-DD'
});
});
</script>

View File

@@ -45,13 +45,13 @@
<a name="diff-@i"></a>
<table class="table table-bordered diff-outside" commitId="@newCommitId" fileName="@diff.newPath" data-diff-id="@i">
<tr>
<th style="font-weight: normal; line-height: 27px;" class="box-header">
<th style="font-weight: normal;" class="box-header">
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
@if(newCommitId.isDefined){
<div class="pull-right align-right">
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="ignore-whitespace" value="1"/>Ignore Space</label>
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-small" title="View the whole file at version @newCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="ignore-whitespace" value="1"/>Ignore Space</label>
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-default btn-sm" title="View the whole file at version @newCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
</div>
}
<span class="diffstat">
@@ -61,9 +61,9 @@
@if(diff.changeType == ChangeType.ADD || diff.changeType == ChangeType.MODIFY){
@if(newCommitId.isDefined){
<div class="pull-right align-right">
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="ignore-whitespace" value="1"/>Ignore Space</label>
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-small" title="View the whole file at version @newCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="ignore-whitespace" value="1"/>Ignore Space</label>
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-default btn-sm" title="View the whole file at version @newCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
</div>
}
<span class="diffstat">
@@ -78,8 +78,8 @@
@if(diff.changeType == ChangeType.DELETE){
@if(oldCommitId.isDefined){
<div class="pull-right align-right">
<label class="checkbox" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
<a href="@url(repository)/blob/@oldCommitId.get/@diff.oldPath" class="btn btn-small" title="View the whole file at version @oldCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
<a href="@url(repository)/blob/@oldCommitId.get/@diff.oldPath" class="btn btn-default btn-sm" title="View the whole file at version @oldCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
</div>
}
<span class="diffstat">
@@ -101,14 +101,14 @@
@if(diff.newIsImage || diff.oldIsImage){
<div class="diff-image-render diff2up">@diff.oldIsImage @diff.newIsImage
@if(oldCommitId.isDefined && diff.oldIsImage){
<div class="diff-image-frame diff-old"><img src="@url(repository)/blob/@oldCommitId.get/@diff.oldPath?raw=true" class="diff-image" onload="onLoadedDiffImages(this)" style="display:none" /></div>
<div class="diff-image-frame diff-old"><img src="@url(repository)/raw/@oldCommitId.get/@diff.oldPath" class="diff-image" onload="onLoadedDiffImages(this)" style="display:none" /></div>
} else {
@if(diff.changeType != ChangeType.ADD){
<div style="padding: 12px;">Not supported</div>
}
}
@if(newCommitId.isDefined && diff.newIsImage){
<div class="diff-image-frame diff-new"><img src="@url(repository)/blob/@newCommitId.get/@diff.newPath?raw=true" class="diff-image" onload="onLoadedDiffImages(this)" style="display:none" /></div>
<div class="diff-image-frame diff-new"><img src="@url(repository)/raw/@newCommitId.get/@diff.newPath" class="diff-image" onload="onLoadedDiffImages(this)" style="display:none" /></div>
} else {
@if(diff.changeType != ChangeType.DELETE){
<div style="padding: 12px;">Not supported</div>
@@ -220,7 +220,6 @@ $(function(){
dataType : 'html'
},
function(responseContent) {
$this.hide();
var tmp;
if (!isNaN(oldLineNumber) && oldLineNumber) {
if (!isNaN(newLineNumber) && newLineNumber) {

View File

@@ -1,13 +1,12 @@
@(value : String = "",
prefix: String = "",
mini : Boolean = true,
style : String = "",
right : Boolean = false,
flat : Boolean = false)(body: Html)
<div class="btn-group" @if(style.nonEmpty){style="@style"}>
<button
@if(flat){style="border: none; background-color: #eee;"}
class="dropdown-toggle @if(!flat){btn} else {flat} @if(mini){btn-mini} else {btn-small}" data-toggle="dropdown">
class="dropdown-toggle @if(!flat){btn btn-default} else {flat} btn-sm" data-toggle="dropdown">
@if(value.isEmpty){
<i class="octicon octicon-gear"></i>
} else {

View File

@@ -2,17 +2,18 @@
groupAndPerm: Map[String, Boolean])(implicit context: gitbucket.core.controller.Context)
@import context._
@import gitbucket.core.view.helpers._
<h2 class="facebox-header">Where should we fork this repository?</h2>
<form action="@url(repository)/fork" id="fork" method="post">
<div class="owner-select-grid">
<div class="owner-select-target js-fork-owner-select-target enabled">@avatar(loginAccount.get.userName, 100)<span class="owner css-truncate" title="@@@loginAccount.get.userName">@@@loginAccount.get.userName</span></div>
<h2 class="facebox-header">Where should we fork this repository?</h2>
<form action="@url(repository)/fork" id="fork" method="post">
<div class="owner-select-grid">
<div class="owner-select-target js-fork-owner-select-target enabled">@avatar(loginAccount.get.userName, 100)<span class="owner css-truncate" title="@@@loginAccount.get.userName">@@@loginAccount.get.userName</span></div>
@for((groupName, isManager) <- groupAndPerm) {
@if(isManager) {
<div class="owner-select-target js-fork-owner-select-target enabled">@avatar(groupName, 100)<span class="owner css-truncate" title="@@@groupName">@@@groupName</span></div>
<div class="owner-select-target js-fork-owner-select-target enabled">@avatar(groupName, 100)<span class="owner css-truncate" title="@@@groupName">@@@groupName</span></div>
} else {
<div title="You don't have permission to fork here." class="owner-select-target js-fork-owner-select-target disabled">@avatar(groupName, 100)<span class="owner css-truncate" title="@@@groupName">@@@groupName</span></div>
<div title="You don't have permission to fork here." class="owner-select-target js-fork-owner-select-target disabled">@avatar(groupName, 100)<span class="owner css-truncate" title="@@@groupName">@@@groupName</span></div>
}
}
</div>
<input id="account" name="account" type="hidden" />
</div>
<input id="account" name="account" type="hidden" />
</form>

View File

@@ -2,6 +2,7 @@
content: String,
enableWikiLink: Boolean,
enableRefsLink: Boolean,
enableLineBreaks: Boolean,
enableTaskList: Boolean,
hasWritePermission: Boolean,
style: String = "",
@@ -14,15 +15,15 @@
@import gitbucket.core._
@import gitbucket.core.view.helpers._
<div class="tabbable">
<ul class="nav nav-tabs" style="height: 37px;">
<ul class="nav nav-tabs fill-width pull-left" style="@*margin-top: 12px; *@margin-bottom: 10px;">
<li class="active"><a href="#tab@uid" data-toggle="tab">Write</a></li>
<li><a href="#tab@(uid+1)" data-toggle="tab" id="preview@uid">Preview</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab@uid">
<div class="tab-pane active" style="margin-top: 4px;" id="tab@uid">
<span id="error-content" class="error"></span>
@textarea = {
<textarea id="content@uid" name="content" placeholder="@placeholder"
<textarea id="content@uid" class="form-control" name="content" placeholder="@placeholder"
@if(tabIndex > -2){ tabindex="@tabIndex"}
@if(style.nonEmpty){ style="@style"}
@if(styleClass.nonEmpty){ class="@styleClass" }>@content</textarea>
@@ -50,10 +51,11 @@ $(function(){
$('#preview@uid').click(function(){
$('#preview-area@uid').html('<img src="@assets/common/images/indicator.gif"> Previewing...');
$.post('@url(repository)/_preview', {
content : $('#content@uid').val(),
enableWikiLink : @enableWikiLink,
enableRefsLink : @enableRefsLink,
enableTaskList : @enableTaskList
content : $('#content@uid').val(),
enableWikiLink : @enableWikiLink,
enableRefsLink : @enableRefsLink,
enableLineBreaks : @enableLineBreaks,
enableTaskList : @enableTaskList
}, function(data){
$('#preview-area@uid').html(data);
prettyPrint();

View File

@@ -6,15 +6,15 @@
@main("GitBucket"){
@dashboard.html.tab()
<div class="container">
<div class="row-fluid">
<div class="span8">
<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="span4">
<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>
@@ -24,55 +24,53 @@
@if(loginAccount.isEmpty){
@signinform(settings)
} else {
<div class="box-header">
<div class="pull-right">
<a href="@path/new" class="btn btn-success btn-mini">New repository</a>
</div>
<span class="strong">Your repositories</span> <span class="label">@userRepositories.size</span>
</div>
@if(userRepositories.isEmpty){
<div class="box-content-bottom">
No repositories
<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>
} else {
<div class="box-content-bottom" style="padding: 0px;">
@defining(3){ max =>
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) =>
<div class="box-content-row repo-link" style="@if(i > max - 1){display:none;}">
<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>
}
</div>
</li>
}
@if(userRepositories.size > max){
<div class="box-content-row show-more">
<li class="list-group-item show-more">
<a href="javascript:void(0);" id="show-more-repos">Show more @{userRepositories.size - max} pages...</a>
</div>
</li>
}
}
</div>
}
}
</ul>
</div>
}
<div class="box-header">
<span class="strong">Recent updated repositories</span>
<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 {
@recentRepositories.map { repository =>
<li class="list-group-item repo-link">
@helper.html.repositoryicon(repository, false)
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
</li>
}
}
</ul>
</div>
@if(recentRepositories.isEmpty){
<div class="box-content-bottom">
No repositories
</div>
} else {
<div class="box-content-bottom" style="padding: 0px;">
@recentRepositories.map { repository =>
<div class="box-content-row repo-link">
@helper.html.repositoryicon(repository, false)
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
</div>
}
</div>
}
</div>
</div>
</div>
@@ -80,8 +78,8 @@
<script>
$(function(){
$('#show-more-repos').click(function(e){
$(e.target).parents('div.box-content-bottom').find('div.repo-link').show();
$(e.target).parents('div.show-more').remove();
$(e.target).parents('ul.list-group').find('li.repo-link').show();
$(e.target).parents('li.show-more').remove();
});
});
</script>
</script>

View File

@@ -8,25 +8,26 @@
<hr/><br/>
<form method="POST" validate="true">
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="issue-comment-box">
<div class="box-content">
<div class="panel panel-default issue-comment-box">
<div class="panel-body">
@helper.html.preview(
repository = repository,
content = "",
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission,
style = "",
elastic = true,
tabIndex = 1
)
<div style="text-align: right;">
<div class="text-right">
<input type="hidden" name="issueId" value="@issue.issueId"/>
@if((reopenable || !issue.closed) && (hasWritePermission || issue.openedUserName == loginAccount.get.userName)){
<input type="submit" class="btn" tabindex="3" formaction="@url(repository)/issue_comments/state" value="@{if(issue.closed) "Reopen" else "Close"}" id="action"/>
<input type="submit" class="btn btn-lg btn-default" tabindex="3" formaction="@url(repository)/issue_comments/state" value="@{if(issue.closed) "Reopen" else "Close"}" id="action"/>
}
<input type="submit" class="btn btn-success" tabindex="2" formaction="@url(repository)/issue_comments/new" value="Comment"/>
<input type="submit" class="btn btn-lg btn-success" tabindex="2" formaction="@url(repository)/issue_comments/new" value="Comment"/>
</div>
</div>
</div>

View File

@@ -8,8 +8,8 @@
@import gitbucket.core.model.CommitComment
@if(issue.isDefined){
<div class="issue-avatar-image">@avatar(issue.get.openedUserName, 48)</div>
<div class="issue-comment-box">
<div class="box-header">
<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>
<span class="pull-right">
@if(hasWritePermission || loginAccount.map(_.userName == issue.get.openedUserName).getOrElse(false)){
@@ -17,8 +17,16 @@
}
</span>
</div>
<div class="box-content-bottom issue-content markdown-body" id="issueContent">
@markdown(issue.get.content getOrElse "No description provided.", repository, false, true, true, hasWritePermission)
<div class="panel-body issue-content markdown-body" id="issueContent">
@markdown(
markdown = issue.get.content getOrElse "No description provided.",
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
</div>
</div>
}
@@ -27,8 +35,8 @@
case comment: gitbucket.core.model.IssueComment => {
@if(comment.action != "close" && comment.action != "reopen" && comment.action != "delete_branch"){
<div class="issue-avatar-image">@avatar(comment.commentedUserName, 48)</div>
<div class="box issue-comment-box" id="comment-@comment.commentId">
<div class="box-header">
<div class="panel panel-default issue-comment-box" id="comment-@comment.commentId">
<div class="panel-heading">
@user(comment.commentedUserName, styleClass="username strong")
<span class="muted">
@if(comment.action == "comment"){
@@ -38,27 +46,45 @@
}
@helper.html.datetimeago(comment.registeredDate)
</span>
<span class="pull-right">
@if(comment.action != "commit" && comment.action != "merge" && comment.action != "refer"
&& (hasWritePermission || loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false))){
@if(comment.action != "commit" && comment.action != "merge" && comment.action != "refer"
&& (hasWritePermission || loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false))){
<span class="pull-right">
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>&nbsp;
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-x" aria-label="Remove"></i></a>
}
</span>
</div>
<div class="box-content-bottom issue-content" id="commentContent-@comment.commentId">
</span>
}
@if(comment.action == "commit" && comment.content.split(" ").last.matches("[a-f0-9]{40}")){
@defining(comment.content.substring(comment.content.length - 40)){ id =>
<div class="pull-right"><a href="@path/@repository.owner/@repository.name/commit/@id" class="monospace">@id.substring(0, 7)</a></div>
<div class="markdown-body">@markdown(comment.content.substring(0, comment.content.length - 41), repository, false, true, true, hasWritePermission)</div>
<span class="pull-right"><a href="@path/@repository.owner/@repository.name/commit/@id" class="monospace">@id.substring(0, 7)</a></span>
}
}
</div>
<div class="panel-body issue-content markdown-body" id="commentContent-@comment.commentId">
@if(comment.action == "commit" && comment.content.split(" ").last.matches("[a-f0-9]{40}")){
@markdown(
markdown = comment.content.substring(0, comment.content.length - 41),
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
} else {
@if(comment.action == "refer"){
@defining(comment.content.split(":")){ case Array(issueId, rest @ _*) =>
<strong>@issueLink(repository, issueId.toInt): @rest.mkString(":")</strong>
}
} else {
<div class="markdown-body">@markdown(comment.content, repository, false, true, true, hasWritePermission)</div>
@markdown(
markdown = comment.content,
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
}
}
</div>

View File

@@ -7,33 +7,32 @@
@import gitbucket.core.view.helpers._
@html.main(s"New Issue - ${repository.owner}/${repository.name}", Some(repository)){
@html.menu("issues", repository){
@navigation("issues", false, repository)
<br/><br/><hr style="margin-bottom: 10px;">
<form action="@url(repository)/issues/new" method="POST" validate="true">
<div class="row-fluid">
<div class="span10">
<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="box issue-box">
<div class="box-content">
<div class="panel panel-default issue-box">
<div class="panel-body">
<span id="error-title" class="error"></span>
<input type="text" name="title" value="" placeholder="Title" style="width: 690px;" autofocus/>
<input type="text" name="title" class="form-control input-lg" value="" placeholder="Title" style="width: 680px;" autofocus/>
@helper.html.preview(
repository = repository,
content = "",
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission,
style = "width: 690px; height: 200px; max-height: 250px;",
style = "width: 680px; height: 200px; max-height: 250px;",
elastic = true
)
<div class="align-right">
<input type="submit" class="btn btn-success" value="Submit new issue"/>
<input type="submit" class="btn btn-lg btn-success" value="Submit new issue"/>
</div>
</div>
</div>
</div>
<div class="span2">
<div class="col-md-2">
@issueinfo(None, Nil, Nil, collaborators, milestones.map(x => (x, 0, 0)), labels, hasWritePermission, repository)
</div>
</div>

View File

@@ -2,11 +2,11 @@
@import context._
<span id="error-edit-content-@commentId" class="error"></span>
@helper.html.attached(owner, repository){
<textarea id="edit-content-@commentId">@content</textarea>
<textarea id="edit-content-@commentId" class="form-control">@content</textarea>
}
<div>
<input type="button" id="cancel-comment-@commentId" class="btn btn-small btn-danger" value="Cancel"/>
<input type="button" id="update-comment-@commentId" class="btn btn-small pull-right" value="Update comment"/>
<input type="button" id="cancel-comment-@commentId" class="btn btn-lg btn-danger" value="Cancel"/>
<input type="button" id="update-comment-@commentId" class="btn btn-lg btn-default pull-right" value="Update comment"/>
</div>
<script>
$(function(){

View File

@@ -1,11 +1,11 @@
@(content: Option[String], issueId: Int, owner: String, repository: String)(implicit context: gitbucket.core.controller.Context)
@import context._
@helper.html.attached(owner, repository){
<textarea id="edit-content">@content.getOrElse("")</textarea>
<textarea id="edit-content" class="form-control">@content.getOrElse("")</textarea>
}
<div>
<input type="button" id="cancel-issue" class="btn btn-small btn-danger" value="Cancel"/>
<input type="button" id="update-issue" class="btn btn-small pull-right" value="Update comment"/>
<input type="button" id="cancel-issue" class="btn btn-lg btn-danger" value="Cancel"/>
<input type="button" id="update-issue" class="btn btn-lg btn-default pull-right" value="Update comment"/>
</div>
<script>
$(function(){

View File

@@ -13,12 +13,12 @@
<div>
<div class="show-title pull-right">
@if(hasWritePermission || loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
<a class="btn" href="#" id="edit">Edit</a>
<a class="btn btn-default" href="#" id="edit">Edit</a>
}
<a class="btn btn-success" href="@url(repository)/issues/new">New issue</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>
<a class="btn btn-default" href="#" id="update">Save</a> <a href="#" id="cancel">Cancel</a>
</div>
<h1>
<span class="show-title">
@@ -27,31 +27,31 @@
</span>
<span class="edit-title" style="display: none;">
<span id="error-edit-title" class="error"></span>
<input type="text" style="width: 700px;" id="edit-title" value="@issue.title"/>
<input type="text" class="form-control" style="width: 700px;" id="edit-title" value="@issue.title"/>
</span>
</h1>
</div>
@if(issue.closed) {
<span class="label label-important issue-status">Closed</span>
} else {
<span class="label label-success issue-status">Open</span>
}
<span class="muted">
@user(issue.openedUserName, styleClass="username strong") opened this issue @helper.html.datetimeago(issue.registeredDate) - @defining(
comments.count( _.action.contains("comment") )
){ count =>
@count @plural(count, "comment")
<div style="margin-bottom: 15px">
@if(issue.closed) {
<span class="label label-important issue-status">Closed</span>
} else {
<span class="label label-success issue-status">Open</span>
}
</span>
<br/><br/>
<span class="muted">
@user(issue.openedUserName, styleClass="username strong") opened this issue @helper.html.datetimeago(issue.registeredDate) - @defining(
comments.count( _.action.contains("comment") )
){ count =>
@count @plural(count, "comment")
}
</span>
</div>
<hr>
<br/>
<div class="row-fluid">
<div class="span10">
<div class="row" style="margin-top: 15px;">
<div class="col-md-10">
@commentlist(Some(issue), comments, hasWritePermission, repository)
@commentform(issue, true, hasWritePermission, repository)
</div>
<div class="span2">
<div class="col-md-2">
@issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
</div>
</div>

View File

@@ -116,11 +116,13 @@
<div style="margin-bottom: 8px;">
@defining((issue.openedUserName :: comments.map(_.commentedUserName)).distinct){ participants =>
<div class="muted small strong">@participants.size @plural(participants.size, "participant")</div>
@participants.map { participant =>
@avatarLink(participant, 20, tooltip = true)
}
}
</div>
@defining((issue.openedUserName :: comments.map(_.commentedUserName)).distinct){ participants =>
@participants.map { participant =>
@avatarLink(participant, 20, tooltip = true)
}
}
}
<script>
$(function(){

View File

@@ -4,18 +4,18 @@
@import gitbucket.core.view.helpers._
@defining(label.map(_.labelId).getOrElse("new")){ labelId =>
<div id="edit-label-area-@labelId">
<form style="margin-bottom: 0px;">
<input type="text" id="labelName-@labelId" style="width: 300px; margin-bottom: 0px;" value="@label.map(_.labelName)"@if(labelId == "new"){ placeholder="New label name"}/>
<div id="label-color-@labelId" class="input-append color bscp" data-color="#@label.map(_.color).getOrElse("888888")" data-color-format="hex" style="width: 100px; margin-bottom: 0px;">
<input type="text" class="span3" id="labelColor-@labelId" value="#@label.map(_.color).getOrElse("888888")" readonly style="width: 100px;">
<span class="add-on"><i style="background-color: #@label.map(_.color).getOrElse("888888");"></i></span>
<form class="form-inline">
<input type="text" id="labelName-@labelId" style="width: 300px; float: left; margin-right: 4px;" class="form-control" value="@label.map(_.labelName)"@if(labelId == "new"){ placeholder="New label name"}/>
<div id="label-color-@labelId" class="input-group color bscp" data-color="#@label.map(_.color).getOrElse("888888")" data-color-format="hex" style="width: 100px; float: left;">
<input type="text" class="form-control" id="labelColor-@labelId" value="#@label.map(_.color).getOrElse("888888")" readonly style="width: 100px;">
<span class="input-group-addon"><i style="background-color: #@label.map(_.color).getOrElse("888888");"></i></span>
</div>
<script>
$('div#label-color-@labelId').colorpicker();
</script>
<span class="pull-right">
<span id="label-error-@labelId" class="error"></span>
<input type="button" id="cancel-@labelId" class="btn label-edit-cancel" value="Cancel">
<input type="button" id="cancel-@labelId" class="btn btn-default label-edit-cancel" value="Cancel">
<input type="button" id="submit-@labelId" class="btn btn-success" style="margin-bottom: 0px;" value="@(if(labelId == "new") "Create label" else "Save changes")"/>
</span>
</form>

View File

@@ -6,8 +6,8 @@
@import gitbucket.core.view.helpers._
<tr id="label-row-@label.labelId">
<td style="padding-top: 15px; padding-bottom: 15px;">
<div class="milestone row-fluid" id="label-@label.labelId">
<div class="span8">
<div class="milestone row" id="label-@label.labelId">
<div class="col-md-8">
<div style="margin-top: 6px">
<a href="@url(repository)/issues?labels=@urlEncode(label.labelName)" id="label-row-content-@label.labelId">
<span style="background-color: #@label.color; color: #@label.fontColor; padding: 8px; font-size: 120%; border-radius: 4px;">
@@ -17,13 +17,13 @@
</a>
</div>
</div>
<div class="@if(hasWritePermission){span2} else {span4}">
<div class="@if(hasWritePermission){col-md-2} else {col-md-4}">
<div class="pull-right">
<span class="muted">@counts.get(label.labelName).getOrElse(0) open issues</span>
</div>
</div>
@if(hasWritePermission){
<div class="span2">
<div class="col-md-2">
<div class="pull-right">
<a href="javascript:void(0);" onclick="editLabel(@label.labelId)">Edit</a>
&nbsp;&nbsp;

View File

@@ -12,24 +12,28 @@
<tr><td></td></tr>
</table>
<table class="table table-bordered table-hover table-issues">
<tr id="label-row-header">
<th style="background-color: #eee;">
<span class="small">@labels.size labels</span>
</th>
</tr>
@labels.map { label =>
@gitbucket.core.issues.labels.html.label(label, counts, repository, hasWritePermission)
}
@if(labels.isEmpty){
<tr>
<td style="padding: 20px; background-color: #eee; text-align: center;">
No labels to show.
@if(hasWritePermission){
<a href="@url(repository)/issues/labels/new">Create a new label.</a>
}
</td>
<thead>
<tr id="label-row-header">
<th style="background-color: #eee;">
<span class="small">@labels.size labels</span>
</th>
</tr>
}
</thead>
<tbody>
@labels.map { label =>
@gitbucket.core.issues.labels.html.label(label, counts, repository, hasWritePermission)
}
@if(labels.isEmpty){
<tr>
<td style="padding: 20px; background-color: #eee; text-align: center;">
No labels to show.
@if(hasWritePermission){
<a href="@url(repository)/issues/labels/new">Create a new label.</a>
}
</td>
</tr>
}
</tbody>
</table>
}
}

View File

@@ -12,9 +12,8 @@
@import context._
@import gitbucket.core.view.helpers._
@import gitbucket.core.service.IssuesService.IssueInfo
<br>
@if(condition.nonEmpty){
<div>
<div style="width: 100%; display: inline-block;">
<a href="@gitbucket.core.service.IssuesService.IssueSearchCondition().toURL" class="header-link">
<i class="octicon octicon-x" ></i>
<span class="strong">Clear current search query, filters, and sorts</span>
@@ -22,196 +21,199 @@
</div>
}
<table class="table table-bordered table-hover table-issues">
<tr>
<th style="background-color: #eee;">
<input type="checkbox"/>
<span class="small">
<a class="button-link@if(condition.state == "open"){ selected}" href="@condition.copy(state = "open").toURL">
<i class="octicon octicon-issue-opened @(if(condition.state == "open"){"active"})"></i>
@openCount Open
</a>&nbsp;&nbsp;
<a class="button-link@if(condition.state == "closed"){ selected}" href="@condition.copy(state = "closed").toURL">
<i class="octicon octicon-check @(if(condition.state == "closed"){"active"})"></i>
@closedCount Closed
</a>
</span>
<div class="pull-right" id="table-issues-control">
@helper.html.dropdown("Author", flat = true) {
@collaborators.map { collaborator =>
<li>
<a href="@condition.copy(author = (if(condition.author == Some(collaborator)) None else Some(collaborator))).toURL">
@helper.html.checkicon(condition.author == Some(collaborator))
@avatar(collaborator, 20) @collaborator
</a>
</li>
}
}
@helper.html.dropdown("Label", flat = true) {
@labels.map { label =>
<li>
<a href="@condition.copy(labels = (if(condition.labels.contains(label.labelName)) condition.labels - label.labelName else condition.labels + label.labelName)).toURL">
@helper.html.checkicon(condition.labels.contains(label.labelName))
<span style="background-color: #@label.color;" class="label-color">&nbsp;&nbsp;</span>
@label.labelName
</a>
</li>
}
}
@helper.html.dropdown("Milestone", flat = true) {
<li>
<a href="@condition.copy(milestone = Some(None)).toURL">
@helper.html.checkicon(condition.milestone == Some(None)) Issues with no milestone
</a>
</li>
@milestones.filter(_.closedDate.isEmpty).map { milestone =>
<li>
<a href="@condition.copy(milestone = Some(Some(milestone.title))).toURL">
@helper.html.checkicon(condition.milestone == Some(Some(milestone.title))) @milestone.title
</a>
</li>
}
}
@helper.html.dropdown("Assignee", flat = true) {
@collaborators.map { collaborator =>
<li>
<a href="@condition.copy(assigned = Some(collaborator)).toURL">
@helper.html.checkicon(condition.assigned == Some(collaborator))
@avatar(collaborator, 20) @collaborator
</a>
</li>
}
}
@helper.html.dropdown("Sort", flat = true){
<li>
<a href="@condition.copy(sort="created", direction="desc").toURL">
@helper.html.checkicon(condition.sort == "created" && condition.direction == "desc") Newest
</a>
</li>
<li>
<a href="@condition.copy(sort="created", direction="asc" ).toURL">
@helper.html.checkicon(condition.sort == "created" && condition.direction == "asc") Oldest
</a>
</li>
<li>
<a href="@condition.copy(sort="comments", direction="desc").toURL">
@helper.html.checkicon(condition.sort == "comments" && condition.direction == "desc") Most commented
</a>
</li>
<li>
<a href="@condition.copy(sort="comments", direction="asc" ).toURL">
@helper.html.checkicon(condition.sort == "comments" && condition.direction == "asc") Least commented
</a>
</li>
<li>
<a href="@condition.copy(sort="updated", direction="desc").toURL">
@helper.html.checkicon(condition.sort == "updated" && condition.direction == "desc") Recently updated
</a>
</li>
<li>
<a href="@condition.copy(sort="updated", direction="asc" ).toURL">
@helper.html.checkicon(condition.sort == "updated" && condition.direction == "asc") Least recently updated
</a>
</li>
}
</div>
@if(hasWritePermission){
<div class="pull-right" id="table-issues-batchedit">
@helper.html.dropdown("Mark as", flat = true) {
<li><a href="javascript:void(0);" class="toggle-state" data-id="open">Open</a></li>
<li><a href="javascript:void(0);" class="toggle-state" data-id="close">Close</a></li>
<thead>
<tr>
<th style="background-color: #eee;">
<input type="checkbox"/>
<span class="small">
<a class="button-link@if(condition.state == "open"){ selected}" href="@condition.copy(state = "open").toURL">
<i class="octicon octicon-issue-opened @(if(condition.state == "open"){"active"})"></i>
@openCount Open
</a>&nbsp;&nbsp;
<a class="button-link@if(condition.state == "closed"){ selected}" href="@condition.copy(state = "closed").toURL">
<i class="octicon octicon-check @(if(condition.state == "closed"){"active"})"></i>
@closedCount Closed
</a>
</span>
<div class="pull-right" id="table-issues-control">
@helper.html.dropdown("Author", flat = true) {
@collaborators.map { collaborator =>
<li>
<a href="@condition.copy(author = (if(condition.author == Some(collaborator)) None else Some(collaborator))).toURL">
@helper.html.checkicon(condition.author == Some(collaborator))
@avatar(collaborator, 20) @collaborator
</a>
</li>
}
}
@helper.html.dropdown("Label", flat = true) {
@labels.map { label =>
<li>
<a href="javascript:void(0);" class="toggle-label" data-id="@label.labelId">
<i class="icon-white"></i>
<span class="label" style="background-color: #@label.color;">&nbsp;</span>
<a href="@condition.copy(labels = (if(condition.labels.contains(label.labelName)) condition.labels - label.labelName else condition.labels + label.labelName)).toURL">
@helper.html.checkicon(condition.labels.contains(label.labelName))
<span style="background-color: #@label.color;" class="label-color">&nbsp;&nbsp;</span>
@label.labelName
</a>
</li>
}
}
@helper.html.dropdown("Milestone", flat = true) {
<li><a href="javascript:void(0);" class="toggle-milestone" data-id="">No milestone</a></li>
<li>
<a href="@condition.copy(milestone = Some(None)).toURL">
@helper.html.checkicon(condition.milestone == Some(None)) Issues with no milestone
</a>
</li>
@milestones.filter(_.closedDate.isEmpty).map { milestone =>
<li><a href="javascript:void(0);" class="toggle-milestone" data-id="@milestone.milestoneId">@milestone.title</a></li>
<li>
<a href="@condition.copy(milestone = Some(Some(milestone.title))).toURL">
@helper.html.checkicon(condition.milestone == Some(Some(milestone.title))) @milestone.title
</a>
</li>
}
}
@helper.html.dropdown("Assignee", flat = true) {
<li><a href="javascript:void(0);" class="toggle-assign" data-name=""><i class="octicon octicon-x"></i> Clear assignee</a></li>
@collaborators.map { collaborator =>
<li><a href="javascript:void(0);" class="toggle-assign" data-name="@collaborator"><i class="icon-white"></i>@avatar(collaborator, 20) @collaborator</a></li>
<li>
<a href="@condition.copy(assigned = Some(collaborator)).toURL">
@helper.html.checkicon(condition.assigned == Some(collaborator))
@avatar(collaborator, 20) @collaborator
</a>
</li>
}
}
@helper.html.dropdown("Sort", flat = true){
<li>
<a href="@condition.copy(sort="created", direction="desc").toURL">
@helper.html.checkicon(condition.sort == "created" && condition.direction == "desc") Newest
</a>
</li>
<li>
<a href="@condition.copy(sort="created", direction="asc" ).toURL">
@helper.html.checkicon(condition.sort == "created" && condition.direction == "asc") Oldest
</a>
</li>
<li>
<a href="@condition.copy(sort="comments", direction="desc").toURL">
@helper.html.checkicon(condition.sort == "comments" && condition.direction == "desc") Most commented
</a>
</li>
<li>
<a href="@condition.copy(sort="comments", direction="asc" ).toURL">
@helper.html.checkicon(condition.sort == "comments" && condition.direction == "asc") Least commented
</a>
</li>
<li>
<a href="@condition.copy(sort="updated", direction="desc").toURL">
@helper.html.checkicon(condition.sort == "updated" && condition.direction == "desc") Recently updated
</a>
</li>
<li>
<a href="@condition.copy(sort="updated", direction="asc" ).toURL">
@helper.html.checkicon(condition.sort == "updated" && condition.direction == "asc") Least recently updated
</a>
</li>
}
</div>
}
</th>
</tr>
@if(issues.isEmpty){
<tr>
<td style="padding: 20px; background-color: #eee; text-align: center;">
@if(target == "issues"){
No issues to show.
} else {
No pull requests to show.
}
@if(condition.labels.nonEmpty || condition.milestone.isDefined){
<a href="@condition.copy(labels = Set.empty, milestone = None).toURL">Clear active filters.</a>
} else {
@if(repository.isDefined){
@if(target == "issues"){
<a href="@url(repository.get)/issues/new">Create a new issue.</a>
} else {
<a href="@url(repository.get)/compare">Create a new pull request.</a>
}
}
}
</td>
</tr>
}
@issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
<tr>
<td style="padding-top: 15px; padding-bottom: 15px;">
@if(hasWritePermission){
<input type="checkbox" value="@issue.issueId"/>
<div class="pull-right" id="table-issues-batchedit">
@helper.html.dropdown("Mark as", flat = true) {
<li><a href="javascript:void(0);" class="toggle-state" data-id="open">Open</a></li>
<li><a href="javascript:void(0);" class="toggle-state" data-id="close">Close</a></li>
}
@helper.html.dropdown("Label", flat = true) {
@labels.map { label =>
<li>
<a href="javascript:void(0);" class="toggle-label" data-id="@label.labelId">
<i class="icon-white"></i>
<span class="label" style="background-color: #@label.color;">&nbsp;</span>
@label.labelName
</a>
</li>
}
}
@helper.html.dropdown("Milestone", flat = true) {
<li><a href="javascript:void(0);" class="toggle-milestone" data-id="">No milestone</a></li>
@milestones.filter(_.closedDate.isEmpty).map { milestone =>
<li><a href="javascript:void(0);" class="toggle-milestone" data-id="@milestone.milestoneId">@milestone.title</a></li>
}
}
@helper.html.dropdown("Assignee", flat = true) {
<li><a href="javascript:void(0);" class="toggle-assign" data-name=""><i class="octicon octicon-x"></i> Clear assignee</a></li>
@collaborators.map { collaborator =>
<li><a href="javascript:void(0);" class="toggle-assign" data-name="@collaborator"><i class="icon-white"></i>@avatar(collaborator, 20) @collaborator</a></li>
}
}
</div>
}
<i class="octicon octicon-issue-@(if(issue.closed) "closed" else "opened")" style="margin-right: 3px;"></i>
@if(repository.isEmpty){
<a href="@path/@issue.userName/@issue.repositoryName">@issue.repositoryName</a>&nbsp;&#xFF65;
}
@if(target == "issues"){
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-title">@issue.title</a>
} else {
<a href="@path/@issue.userName/@issue.repositoryName/pull/@issue.issueId" class="issue-title">@issue.title</a>
}
@commitstatus(issue, commitStatus)
@labels.map { label =>
<span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span>
}
<span class="pull-right small">
@issue.assignedUserName.map { userName =>
@avatar(userName, 20, tooltip = true)
}
@if(commentCount > 0){
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">
<i class="octicon octicon-comment active"></i> @commentCount
</a>
} else {
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count" style="color: silver;">
<i class="octicon octicon-comment"></i> @commentCount
</a>
}
</span>
<div class="small muted" style="margin-left: 40px; margin-top: 5px;">
#@issue.issueId opened @helper.html.datetimeago(issue.registeredDate) by @user(issue.openedUserName, styleClass="username")
@milestone.map { milestone =>
<span style="margin: 20px;"><a href="@condition.copy(milestone = Some(Some(milestone))).toURL" class="username"><i class="octicon octicon-milestone"></i>
@milestone</a></span>
}
</div>
</td>
</th>
</tr>
}
</thead>
<tbody>
@if(issues.isEmpty){
<tr>
<td style="padding: 20px; background-color: #eee; text-align: center;">
@if(target == "issues"){
No issues to show.
} else {
No pull requests to show.
}
@if(condition.labels.nonEmpty || condition.milestone.isDefined){
<a href="@condition.copy(labels = Set.empty, milestone = None).toURL">Clear active filters.</a>
} else {
@if(repository.isDefined){
@if(target == "issues"){
<a href="@url(repository.get)/issues/new">Create a new issue.</a>
} else {
<a href="@url(repository.get)/compare">Create a new pull request.</a>
}
}
}
</td>
</tr>
}
@issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
<tr>
<td style="padding-top: 12px; padding-bottom: 12px;">
@if(hasWritePermission){
<input type="checkbox" value="@issue.issueId"/>
}
<i class="octicon octicon-issue-@(if(issue.closed) "closed" else "opened")" style="margin-right: 3px;"></i>
@if(repository.isEmpty){
<a href="@path/@issue.userName/@issue.repositoryName">@issue.repositoryName</a>&nbsp;&#xFF65;
}
@if(target == "issues"){
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-title">@issue.title</a>
} else {
<a href="@path/@issue.userName/@issue.repositoryName/pull/@issue.issueId" class="issue-title">@issue.title</a>
}
@commitstatus(issue, commitStatus)
@labels.map { label =>
<span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span>
}
<span class="pull-right small">
@issue.assignedUserName.map { userName =>
@avatar(userName, 20, tooltip = true)
}
@if(commentCount > 0){
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">
<i class="octicon octicon-comment active"></i> @commentCount
</a>
} else {
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count" style="color: silver;">
<i class="octicon octicon-comment"></i> @commentCount
</a>
}
</span>
<div class="small muted" style="margin-left: 40px; margin-top: 2px;">
#@issue.issueId opened @helper.html.datetimeago(issue.registeredDate) by @user(issue.openedUserName, styleClass="username")
@milestone.map { milestone =>
<span style="margin: 20px;"><a href="@condition.copy(milestone = Some(Some(milestone))).toURL" class="username"><i class="octicon octicon-milestone"></i> @milestone</a></span>
}
</div>
</td>
</tr>
}
</tbody>
</table>
<div class="pull-right">
@helper.html.paginator(page, (if(condition.state == "open") openCount else closedCount), gitbucket.core.service.IssuesService.IssueLimit, 10, condition.toURL)

View File

@@ -13,16 +13,16 @@
}
<hr style="margin-top: 12px; margin-bottom: 18px;" class="fill-width"/>
<form method="POST" action="@url(repository)/issues/milestones/@if(milestone.isEmpty){new}else{@milestone.get.milestoneId/edit}" validate="true">
<fieldset>
<input type="text" id="title" name="title" style="width: 500px;" value="@milestone.map(_.title)" placeholder="Title"/>
<fieldset class="form-group">
<input type="text" id="title" name="title" class="form-control" style="width: 500px;" value="@milestone.map(_.title)" placeholder="Title"/>
<span id="error-title" class="error"></span>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label for="description" class="strong">Description</label>
<textarea id="description" name="description" style="width: 500px; height: 150px;">@milestone.map(_.description)</textarea>
<textarea id="description" name="description" class="form-control" style="width: 500px; height: 150px;">@milestone.map(_.description)</textarea>
<span id="error-description" class="error"></span>
</fieldset>
<fieldset>
<fieldset class="form-group">
<label for="dueDate" class="strong">Due Date</label>
@helper.html.datepicker("dueDate", milestone.flatMap(_.dueDate))
<span id="error-dueDate" class="error"></span>
@@ -30,16 +30,16 @@
<hr>
<div class="pull-right">
@if(milestone.isEmpty){
<input type="submit" class="btn" value="Create milestone"/>
<input type="submit" class="btn btn-default" value="Create milestone"/>
} else {
@if(milestone.get.closedDate.isDefined){
<input type="button" class="btn" value="Open" id="open"
<input type="button" class="btn btn-default" value="Open" id="open"
onclick="location.href='@url(repository)/issues/milestones/@milestone.get.milestoneId/close';"/>
} else {
<input type="button" class="btn" value="Close" id="close"
<input type="button" class="btn btn-default" value="Close" id="close"
onclick="location.href='@url(repository)/issues/milestones/@milestone.get.milestoneId/open';"/>
}
<input type="submit" class="btn" value="Update milestone"/>
<input type="submit" class="btn btn-default" value="Update milestone"/>
}
</div>
</form>

View File

@@ -9,89 +9,99 @@
@issues.html.navigation("milestones", hasWritePermission, repository)
<br>
<table class="table table-bordered table-hover table-issues">
<tr>
<th style="background-color: #eee;">
<span class="small">
<a class="button-link@if(state == "open"){ selected}" href="?state=open">
<i class="octicon octicon-milestone @(if(state == "open"){"active"})"></i>
@milestones.count(_._1.closedDate.isEmpty) Open
</a>&nbsp;&nbsp;
<a class="button-link@if(state == "closed"){ selected}" href="?state=closed">
<i class="octicon octicon-milestone @(if(state == "closed"){"active"})"></i>
@milestones.count(_._1.closedDate.isDefined) Closed
</a>
</span>
</th>
</tr>
@defining(milestones.filter { case (milestone, _, _) =>
milestone.closedDate.map(_ => state == "closed").getOrElse(state == "open")
}){ milestones =>
@milestones.map { case (milestone, openCount, closedCount) =>
<thead>
<tr>
<td style="padding-top: 15px; padding-bottom: 15px;">
<div class="milestone row-fluid">
<div class="span4">
<a href="@url(repository)/issues?milestone=@milestone.title&state=open" class="milestone-title">@milestone.title</a>
<div style="margin-top: 6px">
@if(milestone.closedDate.isDefined){
<span class="muted">Closed @helper.html.datetimeago(milestone.closedDate.get)</span>
} 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>
} else {
<span class="muted">Due by @date(dueDate)</span>
}
}.getOrElse {
<span class="muted">No due date</span>
}
}
</div>
</div>
<div class="span8">
@progress(openCount + closedCount, closedCount)
<div>
<th style="background-color: #eee;">
<span class="small">
<a class="button-link@if(state == "open"){ selected}" href="?state=open">
<i class="octicon octicon-milestone @(if(state == "open"){"active"})"></i>
@milestones.count(_._1.closedDate.isEmpty) Open
</a>&nbsp;&nbsp;
<a class="button-link@if(state == "closed"){ selected}" href="?state=closed">
<i class="octicon octicon-milestone @(if(state == "closed"){"active"})"></i>
@milestones.count(_._1.closedDate.isDefined) Closed
</a>
</span>
</th>
</tr>
</thead>
<tbody>
@defining(milestones.filter { case (milestone, _, _) =>
milestone.closedDate.map(_ => state == "closed").getOrElse(state == "open")
}){ milestones =>
@milestones.map { case (milestone, openCount, closedCount) =>
<tr>
<td style="padding-top: 15px; padding-bottom: 15px;">
<div class="milestone row">
<div class="col-md-4">
<a href="@url(repository)/issues?milestone=@milestone.title&state=open" class="milestone-title">@milestone.title</a>
<div>
@if(closedCount == 0){
0%
@if(milestone.closedDate.isDefined){
<span class="muted">Closed @helper.html.datetimeago(milestone.closedDate.get)</span>
} else {
@((closedCount.toDouble / (openCount + closedCount).toDouble * 100).toInt)%
} <span class="muted">complete</span> &nbsp;&nbsp;
@openCount <span class="muted">open</span> &nbsp;&nbsp;
@closedCount <span class="muted">closed</span>
</div>
<div class="milestone-menu">
@if(hasWritePermission){
<a href="@url(repository)/issues/milestones/@milestone.milestoneId/edit">Edit</a> &nbsp;&nbsp;
@if(milestone.closedDate.isDefined){
<a href="@url(repository)/issues/milestones/@milestone.milestoneId/open">Open</a> &nbsp;&nbsp;
} else {
<a href="@url(repository)/issues/milestones/@milestone.milestoneId/close">Close</a> &nbsp;&nbsp;
@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>
} else {
<span class="muted">Due by @date(dueDate)</span>
}
}.getOrElse {
<span class="muted">No due date</span>
}
<a href="@url(repository)/issues/milestones/@milestone.milestoneId/delete" class="delete">Delete</a>
}
</div>
</div>
<div class="col-md-8">
@progress(openCount + closedCount, closedCount)
<div>
<div>
@if(closedCount == 0){
0%
} else {
@((closedCount.toDouble / (openCount + closedCount).toDouble * 100).toInt)%
} <span class="muted">complete</span> &nbsp;&nbsp;
@openCount <span class="muted">open</span> &nbsp;&nbsp;
@closedCount <span class="muted">closed</span>
</div>
<div class="milestone-menu">
@if(hasWritePermission){
<a href="@url(repository)/issues/milestones/@milestone.milestoneId/edit">Edit</a> &nbsp;&nbsp;
@if(milestone.closedDate.isDefined){
<a href="@url(repository)/issues/milestones/@milestone.milestoneId/open">Open</a> &nbsp;&nbsp;
} else {
<a href="@url(repository)/issues/milestones/@milestone.milestoneId/close">Close</a> &nbsp;&nbsp;
}
<a href="@url(repository)/issues/milestones/@milestone.milestoneId/delete" class="delete">Delete</a>
}
</div>
</div>
</div>
</div>
</div>
@if(milestone.description.isDefined){
<div class="milestone-description markdown-body">
@markdown(milestone.description.get, repository, false, false)
</div>
}
</td>
</tr>
}
@if(milestones.isEmpty){
<tr>
<td style="padding: 20px; background-color: #eee; text-align: center;">
No milestones to show.
@if(hasWritePermission){
<a href="@url(repository)/issues/milestones/new">Create a new milestone.</a>
}
</td>
</tr>
}
@if(milestone.description.isDefined){
<div class="milestone-description markdown-body">
@markdown(
markdown = milestone.description.get,
repository = repository,
enableWikiLink = false,
enableRefsLink = false,
enableLineBreaks = true
)
</div>
}
</td>
</tr>
}
@if(milestones.isEmpty){
<tr>
<td style="padding: 20px; background-color: #eee; text-align: center;">
No milestones to show.
@if(hasWritePermission){
<a href="@url(repository)/issues/milestones/new">Create a new milestone.</a>
}
</td>
</tr>
}
</tbody>
}
</table>
}

View File

@@ -1,6 +1,5 @@
@(total: Int, progress: Int)
<div class="milestone-progress">
@if(progress > 0){
<span class="milestone-progress" style="width: @((progress.toDouble / total.toDouble * 100).toInt)%;"></span>
}
<div class="progress" style="height: 12px; margin-bottom: 8px;">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="@((progress.toDouble / total.toDouble * 100).toInt)" aria-valuemin="0" aria-valuemax="100" style="width: @((progress.toDouble / total.toDouble * 100).toInt)%">
</div>
</div>

View File

@@ -4,55 +4,53 @@
condition: Option[gitbucket.core.service.IssuesService.IssueSearchCondition] = None)(implicit context: gitbucket.core.controller.Context)
@import context._
@import gitbucket.core.view.helpers._
<ul class="nav nav-pills-group pull-left fill-width">
<li class="@if(active == "issues" ){active} first"><a href="@url(repository)/issues">Issues</a></li>
<ul class="nav nav-pills pull-left" style="line-height: 14px;">
<li class="@if(active == "issues" ){active}"><a href="@url(repository)/issues">Issues</a></li>
<li class="@if(active == "pulls" ){active}"><a href="@url(repository)/pulls">Pull requests</a></li>
<li class="@if(active == "labels" ){active}"><a href="@url(repository)/issues/labels">Labels</a></li>
<li class="@if(active == "milestones"){active} last"><a href="@url(repository)/issues/milestones">Milestones</a></li>
<li class="pull-right">
<form method="GET" id="search-filter-form" style="margin-bottom: 0px;">
@condition.map { condition =>
@if(loginAccount.isDefined){
<div class="input-prepend" style="margin-bottom: 0px;">
<div class="btn-group">
<button type="button" class="btn dropdown-toggle" data-toggle="dropdown" style="height: 34px;">
Filter
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="?q=is:open">Open issues and pull requests</a></li>
<li><a href="?q=is:open+is:issue+author:@urlEncode(loginAccount.get.userName)">Your issues</a></li>
<li><a href="?q=is:open+is:pr+author:@urlEncode(loginAccount.get.userName)">Your pull requests</a></li>
<li><a href="?q=is:open+assignee:@urlEncode(loginAccount.get.userName)">Everything assigned to you</a></li>
@*
<li><a href="?q=is:open+mentions:@urlEncode(loginAccount.get.userName)">Everything mentioning you</a></li>
*@
</ul>
</div>
<input type="text" id="search-filter-box" class="input-xlarge" name="q" style="height: 24px;" value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
</div>
} else {
<input type="text" id="search-filter-box" class="input-xlarge" name="q" style="height: 24px;" value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
}
}
@if(loginAccount.isDefined){
<div class="btn-group">
@if(newButton){
@if(active == "issues"){
<a class="btn btn-success" href="@url(repository)/issues/new" style="height: 24px;">New issue</a>
}
@if(active == "pulls"){
<a class="btn btn-success" href="@url(repository)/compare" style="height: 24px;">New pull request</a>
}
@if(active == "labels"){
<a class="btn btn-success" href="javascript:void(0);" id="new-label-button" style="height: 24px;">New label</a>
}
@if(active == "milestones"){
<a class="btn btn-success" href="@url(repository)/issues/milestones/new" style="height: 24px;">New milestone</a>
}
}
</div>
}
</form>
</li>
<li class="@if(active == "milestones"){active}"><a href="@url(repository)/issues/milestones">Milestones</a></li>
</ul>
<form method="GET" id="search-filter-form" style="margin-bottom: 0px;" class="form-inline pull-right">
@condition.map { condition =>
@if(loginAccount.isDefined){
<div class="form-group" style="width: 300px">
<div class="input-group" style="margin-bottom: 0px;">
<div class="input-group-btn">
<button type="button" class="btn btn-default btn-lg dropdown-toggle" data-toggle="dropdown">
Filter
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="?q=is:open">Open issues and pull requests</a></li>
<li><a href="?q=is:open+is:issue+author:@urlEncode(loginAccount.get.userName)">Your issues</a></li>
<li><a href="?q=is:open+is:pr+author:@urlEncode(loginAccount.get.userName)">Your pull requests</a></li>
<li><a href="?q=is:open+assignee:@urlEncode(loginAccount.get.userName)">Everything assigned to you</a></li>
@*
<li><a href="?q=is:open+mentions:@urlEncode(loginAccount.get.userName)">Everything mentioning you</a></li>
*@
</ul>
</div>
<input type="text" id="search-filter-box" class="form-control input-lg" size="40" name="q" value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
</div>
</div>
} else {
<input type="text" id="search-filter-box" class="form-control input-lg" size="40" name="q" value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
}
}
@if(loginAccount.isDefined){
@if(newButton){
@if(active == "issues"){
<a class="btn btn-success btn-lg" href="@url(repository)/issues/new">New issue</a>
}
@if(active == "pulls"){
<a class="btn btn-success btn-lg" href="@url(repository)/compare">New pull request</a>
}
@if(active == "labels"){
<a class="btn btn-success btn-lg" href="javascript:void(0);" id="new-label-button">New label</a>
}
@if(active == "milestones"){
<a class="btn btn-success btn-lg" href="@url(repository)/issues/milestones/new">New milestone</a>
}
}
}
</form>

View File

@@ -11,10 +11,12 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="icon" href="@assets/common/images/gitbucket.png" type="image/vnd.microsoft.icon" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="@assets/vendors/bootstrap/css/bootstrap.css" rel="stylesheet">
<link href="@assets/vendors/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">
<link href="@assets/vendors/bootstrap-theme-github-3.1.1/css/bootstrap.css" rel="stylesheet">
@*
<link href="@assets/vendors/bootstrap-theme-github-3.1.1/css/bootstrap-responsive.css" rel="stylesheet">
*@
<link href="@assets/vendors/octicons/octicons.css" rel="stylesheet">
<link href="@assets/vendors/datepicker/css/datepicker.css" rel="stylesheet">
<link href="@assets/vendors/datepicker/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
<link href="@assets/vendors/colorpicker/css/bootstrap-colorpicker.css" rel="stylesheet">
<link href="@assets/vendors/google-code-prettify/prettify.css" type="text/css" rel="stylesheet"/>
<link href="@assets/vendors/facebox/facebox.css" rel="stylesheet"/>
@@ -23,8 +25,10 @@
<script src="@assets/vendors/dropzone/dropzone.js"></script>
<script src="@assets/common/js/validation.js"></script>
<script src="@assets/common/js/gitbucket.js"></script>
<script src="@assets/vendors/bootstrap/js/bootstrap.js"></script>
<script src="@assets/vendors/datepicker/js/bootstrap-datepicker.js"></script>
<script src="@assets/vendors/bootstrap-theme-github-3.1.1/js/bootstrap.js"></script>
<script src="@assets/vendors/bootstrap3-typeahead/bootstrap3-typeahead.js"></script>
<script src="@assets/vendors/datepicker/js/moment.js"></script>
<script src="@assets/vendors/datepicker/js/bootstrap-datetimepicker.min.js"></script>
<script src="@assets/vendors/colorpicker/js/bootstrap-colorpicker.js"></script>
<script src="@assets/vendors/google-code-prettify/prettify.js"></script>
<script src="@assets/vendors/zclip/ZeroClipboard.min.js"></script>
@@ -38,58 +42,69 @@
}
</head>
<body>
<form id="search" action="@path/search" method="POST">
<div class="navbar">
<div class="navbar-inner">
<div class="container" style="width: 980px;">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="brand" href="@path/">
<img src="@assets/common/images/gitbucket.png" style="width: 24px; height: 24px;"/>GitBucket
@defining(AutoUpdate.getCurrentVersion){ version =>
<span class="header-version">@version.majorVersion.@version.minorVersion</span>
}
</a>
<form id="search" action="@path/search" method="POST" class="form-inline">
<nav class="navbar navbar-default">
<div class="container" style="width: 980px;">
@* TODO: for plugi-ins?
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
*@
<a class="navbar-brand" href="@path/">
<img src="@assets/common/images/gitbucket.png" style="width: 24px; height: 24px;"/>GitBucket
@defining(AutoUpdate.getCurrentVersion){ version =>
<span class="header-version">@version.majorVersion.@version.minorVersion</span>
}
</a>
@if(loginAccount.isDefined){
@repository.map { repository =>
<input type="text" name="query" style="width: 200px; margin-top: 5px; margin-bottom: 0px;" placeholder="Search this repository"/>
<input type="text" name="query" class="form-control" style="width: 200px; margin-bottom: 0px;" placeholder="Search this repository"/>
<input type="hidden" name="owner" value="@repository.owner"/>
<input type="hidden" name="repository" value="@repository.name"/>
}
@if(loginAccount.isDefined){
<a href="@path/dashboard/pulls" class="global-header-menu">Pull requests</a>
<a href="@path/dashboard/issues" class="global-header-menu">Issues</a>
<a href="@path/dashboard/pulls" class="global-header-menu">Pull requests</a>
<a href="@path/dashboard/issues" class="global-header-menu">Issues</a>
} else {
@* TODO: merge with below *@
@repository.map { repository =>
<input type="text" name="query" class="form-control" style="width: 200px; margin-top: 6px; margin-bottom: 0px;" placeholder="Search this repository"/>
<input type="hidden" name="owner" value="@repository.owner"/>
<input type="hidden" name="repository" value="@repository.name"/>
}
<div class="nav-collapse collapse pull-right header-menu">
@if(loginAccount.isDefined){
<div class="btn-group" style="margin-top: 0px;">
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#"><i class="octicon octicon-plus" style="font-size: 20px; vertical-align: middle;height:20px !important;"></i><span class="caret" style="vertical-align: middle;"></span></a>
<ul class="dropdown-menu pull-right">
<li><a href="@path/new">New repository</a></li>
<li><a href="@path/groups/new">New group</a></li>
</ul>
</div>
<div class="btn-group" style="margin-top: 0px;">
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#" data-toggle="tooltip" data-placement="bottom" title="Signed is as @loginAccount.get.userName">@avatar(loginAccount.get.userName, 16)<span class="caret" style="vertical-align: middle;"></span></a>
<ul class="dropdown-menu pull-right">
<li><a href="@url(loginAccount.get.userName)">Your profile</a></li>
<li><a href="@url(loginAccount.get.userName)/_edit">Account settings</a></li>
@if(loginAccount.get.isAdmin){
<li><a href="@path/admin/users">System administration</a></li>
}
<li><a href="@path/signout">Sign out</a></li>
</ul>
</div>
} else {
<a href="@path/signin?redirect=@urlEncode(currentPath)" class="btn" id="signin">Sign in</a>
}
</div><!--/.nav-collapse -->
</div>
}
@if(loginAccount.isDefined){
<div class="pull-right" style="margin-top: 6px;">
<div class="btn-group" style="margin-right: 8px;">
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#">
<i class="octicon octicon-plus" style="color: black; font-size: 20px; vertical-align: middle;height:20px !important;"></i>
<span class="caret" style="color: black; vertical-align: middle;"></span>
</a>
<ul class="dropdown-menu pull-right">
<li><a href="@path/new">New repository</a></li>
<li><a href="@path/groups/new">New group</a></li>
</ul>
</div>
<div class="btn-group">
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#" data-toggle="tooltip" data-placement="bottom" title="Signed is as @loginAccount.get.userName">@avatar(loginAccount.get.userName, 16)
<span class="caret" style="color: black; vertical-align: middle;"></span>
</a>
<ul class="dropdown-menu pull-right">
<li><a href="@url(loginAccount.get.userName)">Your profile</a></li>
<li><a href="@url(loginAccount.get.userName)/_edit">Account settings</a></li>
@if(loginAccount.get.isAdmin){
<li><a href="@path/admin/users">System administration</a></li>
}
<li><a href="@path/signout">Sign out</a></li>
</ul>
</div>
</div>
} else {
<a href="@path/signin?redirect=@urlEncode(currentPath)" class="btn btn-default pull-right" style="margin-top: 6px;" id="signin">Sign in</a>
}
</div>
</div>
</nav>
</form>
@body
<script>

View File

@@ -24,38 +24,42 @@
<div class="container">
@helper.html.information(info)
@helper.html.error(error)
@if(repository.commitCount > 0){
<div class="pull-right">
<div class="input-prepend">
@if(loginAccount.isEmpty){
<a title="You must be signed in to fork a repository" href="@path/signin?redirect=@urlEncode(s"${path}/${repository.owner}/${repository.name}")" class="btn btn-small" style="margin-bottom: 10px;">Fork</a>
} else {
@if(isNoGroup) {
<a id="fork-link" href="javascript:void(0);" class="btn btn-small" style="margin-bottom: 10px;">Fork</a>
} else {
<a href="@path/@repository.owner/@repository.name/fork" class="btn btn-small" rel="facebox" style="margin-bottom: 10px;">Fork</a>
}
<div class="row">
<div class="head">
@if(repository.commitCount > 0){
<div class="input-group pull-right">
<span class="fork">
<span class="input-group-btn">
@if(loginAccount.isEmpty){
<a title="You must be signed in to fork a repository" href="@path/signin?redirect=@urlEncode(s"${path}/${repository.owner}/${repository.name}")" class="btn btn-default">Fork</a>
} else {
@if(isNoGroup) {
<a id="fork-link" href="javascript:void(0);" class="btn btn-default">Fork</a>
} else {
<a href="@path/@repository.owner/@repository.name/fork" class="btn btn-default" rel="facebox">Fork</a>
}
}
</span>
<span class="count"><a href="@url(repository)/network/members">@repository.forkedCount</a></span>
</span>
</div>
@if(loginAccount.isDefined && isNoGroup){
<form id="fork-form" method="post" action="@path/@repository.owner/@repository.name/fork" style="display: none;">
<input type="hidden" name="account" value="@loginAccount.get.userName"/>
</form>
}
}
@helper.html.repositoryicon(repository, true)
<a href="@url(repository.owner)">@repository.owner</a> / <a href="@url(repository)" class="strong">@repository.name</a>
@defining(repository.repository){ x =>
@if(repository.repository.originRepositoryName.isDefined){
<div class="forked">
forked from <a href="@path/@x.parentUserName/@x.parentRepositoryName">@x.parentUserName/@x.parentRepositoryName</a>
</div>
}
<span class="add-on count"><a href="@url(repository)/network/members">@repository.forkedCount</a></span>
</div>
@if(loginAccount.isDefined && isNoGroup){
<form id="fork-form" method="post" action="@path/@repository.owner/@repository.name/fork" style="display: none;">
<input type="hidden" name="account" value="@loginAccount.get.userName"/>
</form>
}
</div>
}
<div class="head">
@helper.html.repositoryicon(repository, true)
<a href="@url(repository.owner)">@repository.owner</a> / <a href="@url(repository)" class="strong">@repository.name</a>
@defining(repository.repository){ x =>
@if(repository.repository.originRepositoryName.isDefined){
<div class="forked">
forked from <a href="@path/@x.parentUserName/@x.parentRepositoryName">@x.parentUserName/@x.parentRepositoryName</a>
</div>
}
}
</div>
</div>
<hr style="margin-bottom: 20px;"/>
@@ -77,7 +81,7 @@
<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" readonly>
<input type="text" value="@repository.httpUrl" id="repository-url" class="form-control input-sm" readonly>
}
@if(settings.ssh && loginAccount.isDefined){
<div class="small">
@@ -87,26 +91,26 @@
@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-small" style="width: 147px;font-weight: bold;"><i class="octicon octicon-desktop-download"></i>&nbsp;&nbsp;Clone in Desktop</a>
<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-small" style="width: 147px;font-weight: bold;"><i class="octicon octicon-cloud-download"></i>Download ZIP</a>
<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-small" style="width: 147px;font-weight: bold;"><i class="octicon octicon-cloud-download"></i>Download TAR.GZ</a>
<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>
*@
}
}
</div>
<div style="margin-right: @if(expand){180px} else {50px};">
<div style="margin-right: @if(expand){180px} else {55px};">
@if(expand){
@repository.repository.description.map { description =>
<p class="description">@detectAndRenderLinks(description)</p>
}
<div style="margin-bottom: 10px;" class="box-content">
<div style="margin-bottom: 10px; padding: 4px;" class="panel panel-default">
<table class="fill-width">
<tr>
<td style="width: 33%; text-align: center;">

View File

@@ -9,37 +9,37 @@
<div class="muted" style="background-color: white;">
<i class="octicon octicon-git-commit"></i> Commits on @date(day.head.commitTime)
</div>
<div class="box-commits">
@day.map { commit =>
<div class="box-content-row" style="padding: 8px;">
<ul class="nav nav-pills-group pull-right" style="margin-top: 2px; margin-bottom: 0px; margin-right: 4px;">
<li class="first"><a href="@url(repository)/commit/@commit.id" class="link monospace">@commit.id.substring(0, 7)</a></li>
<li class="last"><a href="@url(repository)/tree/@commit.id" style="padding-top: 9px; padding-bottom: 10px;"><i class="octicon octicon-code link"></i></a></li>
</ul>
<div>
<div class="commit-avatar-image">@avatar(commit, 40)</div>
<div class="commit-message-box">
<a href="@url(repository)/commit/@commit.id" class="commit-message" style="font-weight: bold;">@link(commit.summary, repository)</a>
@if(commit.description.isDefined){
<a href="javascript:void(0)" onclick="$('#description-@commit.id').toggle();" class="omit">...</a>
}
<br>
@if(commit.description.isDefined){
<pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre>
}
<div class="small">
@user(commit.authorName, commit.authorEmailAddress, "username")
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
@if(commit.isDifferentFromAuthor) {
<span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span>
@user(commit.committerName, commit.committerEmailAddress, "username")
<span class="muted">committed @helper.html.datetimeago(commit.authorTime)</span>
<div class="list-group box-commits">
@day.map { commit =>
<div class="list-group-item">
<ul class="nav nav-pills pull-right">
<li><a href="@url(repository)/commit/@commit.id" class="link monospace" style="line-height: 16px;">@commit.id.substring(0, 7)</a></li>
<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>
<a href="@url(repository)/commit/@commit.id" class="commit-message" style="font-weight: bold;">@link(commit.summary, repository)</a>
@if(commit.description.isDefined){
<a href="javascript:void(0)" onclick="$('#description-@commit.id').toggle();" class="omit">...</a>
}
<br>
@if(commit.description.isDefined){
<pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre>
}
<div style="margin-top: 2px;">
@user(commit.authorName, commit.authorEmailAddress, "username")
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
@if(commit.isDifferentFromAuthor) {
<span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span>
@user(commit.committerName, commit.committerEmailAddress, "username")
<span class="muted">committed @helper.html.datetimeago(commit.authorTime)</span>
}
</div>
</div>
</div>
</div>
</div>
}
}
</div>
}
</div>

View File

@@ -53,21 +53,22 @@
</div>
<div id="pull-request-form" @*class="box"*@ style="display: none; margin-bottom: 20px;">
<form method="POST" action="@path/@originRepository.owner/@originRepository.name/pulls/new" validate="true">
<div class="row-fluid">
<div class="span10">
<div class="row">
<div class="col-md-10">
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="box issue-box">
<div class="box-content">
<div class="panel panel-default issue-box">
<div class="panel-body">
<span class="error" id="error-title"></span>
<input type="text" name="title" style="width: 690px" placeholder="Title"/>
<input type="text" name="title" class="form-control input-lg" style="width: 680px" placeholder="Title"/>
@helper.html.preview(
repository = repository,
content = "",
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true,
style = "width: 690px; height: 200px;"
style = "width: 680px; height: 200px;"
)
<input type="hidden" name="targetUserName" value="@originRepository.owner"/>
<input type="hidden" name="targetBranch" value="@originId"/>
@@ -77,12 +78,12 @@
<input type="hidden" name="commitIdFrom" value="@sourceId"/>
<input type="hidden" name="commitIdTo" value="@commitId"/>
<div class="align-right">
<input type="submit" class="btn btn-success" value="Create pull request"/>
<input type="submit" class="btn btn-lg btn-success" value="Create pull request"/>
</div>
</div>
</div>
</div>
<div class="span2">
<div class="col-md-2">
@issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map((_, 0, 0)), labels, hasOriginWritePermission, repository)
</div>
</div>
@@ -90,6 +91,11 @@
</div>
}
@if(commits.isEmpty){
<div class="panel panel-default" style="padding: 20px; background-color: #eee; text-align: center;">
<h4>There isn't anything to compare.</h4>
<span class="strong">@originRepository.owner:@originId</span> and <span class="strong">@forkedRepository.owner:@forkedId</span> are identical.
</div>
@*
<table class="table table-bordered table-hover table-issues">
<tr>
<td style="padding: 20px; background-color: #eee; text-align: center;">
@@ -98,8 +104,9 @@
</td>
</tr>
</table>
*@
} else {
<div style="margin-bottom: 10px;" class="box-content">
<div style="margin-bottom: 10px; padding: 4px;" class="panel panel-default">
<table class="fill-width">
<tr>
<td style="width: 25%; text-align: center;">

View File

@@ -11,8 +11,8 @@
@import gitbucket.core.view.helpers._
@import gitbucket.core.model._
<div class="row-fluid">
<div class="span10">
<div class="row">
<div class="col-md-10">
<div id="comment-list">
@issues.html.commentlist(Some(issue), comments, hasWritePermission, repository, Some(pullreq))
</div>
@@ -44,7 +44,7 @@
@issues.html.commentform(issue, !merged, hasWritePermission, repository)
}
</div>
<div class="span2">
<div class="col-md-2">
@issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
</div>
</div>

View File

@@ -10,7 +10,7 @@
@import gitbucket.core.view.helpers._
@import model.CommitState
<div class="box issue-comment-box" style="background-color: @if(hasProblem){ #fbeed5 }else{ #d8f5cd };">
<div class="box-content"class="issue-content" style="border: 1px solid @if(hasProblem){ #c09853 }else{ #95c97e }; padding: 10px;">
<div class="box-content issue-content" style="border: 1px solid @if(hasProblem){ #c09853 }else{ #95c97e }; padding: 10px;">
<div id="merge-pull-request">
@if(!statuses.isEmpty){
<div class="build-statuses">
@@ -48,7 +48,7 @@
</div>
}
<div class="pull-right">
<input type="button" class="btn @if(!hasProblem){ btn-success }" id="merge-pull-request-button" value="Merge pull request"@if(hasConflict){ disabled="true"}/>
<input type="button" class="btn @if(!hasProblem){ btn-success }else{ btn-default }" id="merge-pull-request-button" value="Merge pull request"@if(hasConflict){ disabled="true"}/>
</div>
<div>
@if(hasConflict){

View File

@@ -36,32 +36,33 @@
</span>
</h1>
</div>
@if(issue.closed) {
@comments.flatMap @{
case comment: IssueComment => Some(comment)
case _ => None
}.find(_.action == "merge").map{ comment =>
<span class="label label-info issue-status">Merged</span>
<span class="muted">
@user(comment.commentedUserName, styleClass="username strong") merged @commits.size @plural(commits.size, "commit")
into <code>@pullreq.userName:@pullreq.branch</code> from <code>@pullreq.requestUserName:@pullreq.requestBranch</code>
@helper.html.datetimeago(comment.registeredDate)
</span>
}.getOrElse {
<span class="label label-important issue-status">Closed</span>
<div style="margin-bottom: 15px">
@if(issue.closed) {
@comments.flatMap @{
case comment: IssueComment => Some(comment)
case _ => None
}.find(_.action == "merge").map{ comment =>
<span class="label label-info issue-status">Merged</span>
<span class="muted">
@user(comment.commentedUserName, styleClass="username strong") merged @commits.size @plural(commits.size, "commit")
into <code>@pullreq.userName:@pullreq.branch</code> from <code>@pullreq.requestUserName:@pullreq.requestBranch</code>
@helper.html.datetimeago(comment.registeredDate)
</span>
}.getOrElse {
<span class="label label-important issue-status">Closed</span>
<span class="muted">
@user(issue.openedUserName, styleClass="username strong") wants to merge @commits.size @plural(commits.size, "commit")
into <code>@pullreq.userName:@pullreq.branch</code> from <code>@pullreq.requestUserName:@pullreq.requestBranch</code>
</span>
}
} else {
<span class="label label-success issue-status">Open</span>
<span class="muted">
@user(issue.openedUserName, styleClass="username strong") wants to merge @commits.size @plural(commits.size, "commit")
into <code>@pullreq.userName:@pullreq.branch</code> from <code>@pullreq.requestUserName:@pullreq.requestBranch</code>
</span>
}
} else {
<span class="label label-success issue-status">Open</span>
<span class="muted">
@user(issue.openedUserName, styleClass="username strong") wants to merge @commits.size @plural(commits.size, "commit")
into <code>@pullreq.userName:@pullreq.branch</code> from <code>@pullreq.requestUserName:@pullreq.requestBranch</code>
</span>
}
<br/><br/>
</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 @{
case comment: IssueComment => Some(comment)

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-mini" 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-toggle="tooltip" data-placement="bottom" data-hotkey="t" title="Quickly jump between files"><i class="octicon octicon-list-unordered"></i></a>
</div></div>
<div class="line-age-legend">
<span>Newer</span>
@@ -54,22 +54,22 @@
<a href="@url(repository)/commit/@latestCommit.id" class="commit-message">@link(latestCommit.summary, repository)</a>
<div class="btn-group pull-right">
@if(hasWritePermission && content.viewType == "text" && repository.branchList.contains(branch)){
<a class="btn btn-mini" href="@url(repository)/edit/@encodeRefName(branch)/@pathList.mkString("/")">Edit</a>
<a class="btn btn-sm btn-default" href="@url(repository)/edit/@encodeRefName(branch)/@pathList.mkString("/")">Edit</a>
}
<a class="btn btn-mini" href="?raw=true">Raw</a>
<a class="btn btn-sm btn-default" href="@url(repository)/raw/@latestCommit.id/@pathList.mkString("/")">Raw</a>
@if(content.viewType == "text"){
<a class="btn btn-mini blame-action" href="@url(repository)/blame/@latestCommit.id/@pathList.mkString("/")" data-url="@url(repository)/get-blame/@latestCommit.id/@pathList.mkString("/")" data-repository="@url(repository)">Blame</a>
<a class="btn btn-sm btn-default blame-action" href="@url(repository)/blame/@latestCommit.id/@pathList.mkString("/")" data-url="@url(repository)/get-blame/@latestCommit.id/@pathList.mkString("/")" data-repository="@url(repository)">Blame</a>
}
<a class="btn btn-mini" href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")">History</a>
<a class="btn btn-sm btn-default" href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")">History</a>
@if(hasWritePermission && repository.branchList.contains(branch)){
<a class="btn btn-mini btn-danger" href="@url(repository)/remove/@encodeRefName(branch)/@pathList.mkString("/")">Delete</a>
<a class="btn btn-sm btn-danger" href="@url(repository)/remove/@encodeRefName(branch)/@pathList.mkString("/")">Delete</a>
}
</div>
</div>
@if(content.viewType == "text"){
@defining(isRenderable(pathList.reverse.head)){ isRrenderable =>
@if(!isBlame && isRrenderable) {
<div class="box-content-bottom markdown-body" style="padding-left: 16px; padding-right: 16px;">
<div class="box-content-bottom markdown-body" style="padding-left: 20px; padding-right: 20px;">
@renderMarkup(pathList, content.content.get, branch, repository, false, false, true)
</div>
} else {
@@ -81,12 +81,12 @@
}
@if(content.viewType == "image"){
<div class="box-content-bottom">
<img src="?raw=true"/>
<img src="@url(repository)/raw/@encodeRefName(branch)/@pathList.mkString("/")"/>
</div>
}
@if(content.viewType == "large" || content.viewType == "binary"){
<div class="box-content-bottom" style="text-align: center; padding-top: 20px; padding-bottom: 20px;">
<a href="?raw=true">View Raw</a><br>
<a href="@url(repository)/raw/@encodeRefName(branch)/@pathList.mkString("/")">View Raw</a><br>
<br>
(Sorry about that, but we can't show files that are this big right now)
</div>

View File

@@ -16,41 +16,43 @@
@branchInfo.map { case (branch, prs) =>
<tr><td style="padding: 12px;">
<div class="branch-action">
@branch.mergeInfo.map{ info =>
@prs.map{ case (pull, issue) =>
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title">#@issue.issueId</a>
@if(issue.closed) {
@if(info.isMerged){
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-info">Merged</a>
}else{
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-important">Closed</a>
}
} else {
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-success">Open</a>
}
}.getOrElse{
@if(context.loginAccount.isDefined){
<a href="@url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
urlEncode(parent) + ":" + encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
encodeRefName(repository.repository.defaultBranch)
}}...@{encodeRefName(branch.name)}?expand=1" class="btn btn-small">New Pull Request</a>
} else {
<a href="@url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
urlEncode(parent) + ":" + encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
encodeRefName(repository.repository.defaultBranch)
}}...@{encodeRefName(branch.name)}" class="btn btn-small">Compare</a>
}
}
@if(hasWritePermission){
@if(prs.map(!_._2.closed).getOrElse(false)){
<a class="btn disabled btn-mini" data-toggle="tooltip" title="You cant delete this branch because it has an open pull request"><i class="octicon octicon-trashcan"></i></a>
@branch.mergeInfo.map{ info =>
@prs.map{ case (pull, issue) =>
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title">#@issue.issueId</a>
@if(issue.closed) {
@if(info.isMerged){
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-info">Merged</a>
}else{
<a href="@url(repository)/delete/@encodeRefName(branch.name)" class="btn @if(info.isMerged){ btn-warning }else{ btn-danger } delete-branch btn-mini" data-name="@branch.name" @if(info.isMerged){ data-toggle="tooltip" title="this branch is merged" }><i class="octicon octicon-trashcan"></i></a>
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-important">Closed</a>
}
} else {
<a href="@url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-success">Open</a>
}
}.getOrElse{
@if(context.loginAccount.isDefined){
<a href="@url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
urlEncode(parent) + ":" + encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
encodeRefName(repository.repository.defaultBranch)
}}...@{encodeRefName(branch.name)}?expand=1" class="btn btn-default">New Pull Request</a>
} else {
<a href="@url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
urlEncode(parent) + ":" + encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
encodeRefName(repository.repository.defaultBranch)
}}...@{encodeRefName(branch.name)}" class="btn btn-default">Compare</a>
}
}
@if(hasWritePermission){
<span style="margin-left: 8px;">
@if(prs.map(!_._2.closed).getOrElse(false)){
<a class="disabled" data-toggle="tooltip" title="You cant delete this branch because it has an open pull request"><i class="octicon octicon-trashcan"></i></a>
} else {
<a href="@url(repository)/delete/@encodeRefName(branch.name)" class="delete-branch" data-name="@branch.name" @if(info.isMerged){ data-toggle="tooltip" title="this branch is merged" }><i class="octicon octicon-trashcan @if(info.isMerged){warning} else {danger}"></i></a>
}
</span>
}
}
</div>
<div class="branch-details">
<a href="@url(repository)/tree/@encodeRefName(branch.name)" class="branch-name">@branch.name</a>

View File

@@ -13,13 +13,14 @@
@if(!fileName.isDefined){
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
}
<div class="box issue-comment-box">
<div class="box-content">
<div class="panel panel-default issue-comment-box">
<div class="panel-body">
@helper.html.preview(
repository = repository,
content = "",
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission,
style = "width: 635px; height: 100px; max-height: 150px;",

View File

@@ -38,16 +38,16 @@
<div class="muted" style="background-color: white;">
<i class="octicon octicon-git-commit"></i> Commits on @date(day.head.commitTime)
</div>
<div class="box-commits">
<div class="list-group box-commits">
@day.map { commit =>
<div class="box-content-row" style="padding: 8px;">
<ul class="nav nav-pills-group pull-right" style="margin-top: 2px; margin-bottom: 0px; margin-right: 4px;">
<li class="first"><a href="@url(repository)/commit/@commit.id" class="link monospace">@commit.id.substring(0, 7)</a></li>
<li class="last"><a href="@url(repository)/tree/@commit.id" style="padding-top: 9px; padding-bottom: 10px;"><i class="octicon octicon-code link"></i></a></li>
<div class="list-group-item">
<ul class="nav nav-pills pull-right">
<li><a href="@url(repository)/commit/@commit.id" class="link monospace" style="line-height: 16px;">@commit.id.substring(0, 7)</a></li>
<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-message-box">
<div>
<a href="@url(repository)/commit/@commit.id" class="commit-message" style="font-weight: bold;">@link(commit.summary, repository)</a>
@if(commit.description.isDefined){
<a href="javascript:void(0)" onclick="$('#description-@commit.id').toggle();" class="omit">...</a>
@@ -56,7 +56,7 @@
@if(commit.description.isDefined){
<pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre>
}
<div class="small">
<div style="margin-top: 2px;">
@user(commit.authorName, commit.authorEmailAddress, "username")
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
@if(commit.isDifferentFromAuthor) {
@@ -72,8 +72,9 @@
</div>
}
</div>
<div class="pagination" style="text-align: center; margin-top: 30px">
<ul>
<nav style="text-align: center; margin-top: 30px">
<ul class="pagination">
@if(page <= 1){
<li class="disabled"><span>Newer</span></li>
} else {
@@ -85,6 +86,6 @@
<li><a href="?page=@{page + 1}">Older</a></li>
}
</ul>
</div>
</nav>
}
}

View File

@@ -23,7 +23,7 @@
<tr>
<th>
<div class="pull-right">
<select id="wrap" class="input-medium" style="margin-bottom: 0px; height: 26px; padding: 0px;">
<select id="wrap" class="form-control" style="margin-bottom: 0px; height: 26px; padding: 0px;">
<optgroup label="Line Wrap Mode">
<option value="false">No wrap</option>
<option value="true">Soft wrap</option>
@@ -44,21 +44,21 @@
</tr>
</table>
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="box issue-comment-box">
<div class="box-content">
<div class="panel panel-default issue-comment-box">
<div class="panel-body">
<div>
<strong>Commit changes</strong>
</div>
<div>
<input type="text" name="message" style="width: 98%;"/>
<div class="form-group">
<input type="text" name="message" class="form-control"/>
</div>
<div style="text-align: right;">
@if(fileName.isEmpty){
<a href="@url(repository)/tree/@encodeRefName(branch)/@{pathList.mkString("/")}" class="btn btn-danger">Cancel</a>
<a href="@url(repository)/tree/@encodeRefName(branch)/@{pathList.mkString("/")}" class="btn btn-lg btn-danger">Cancel</a>
} else {
<a href="@url(repository)/blob/@encodeRefName(branch)/@{(pathList ++ Seq(fileName.get)).mkString("/")}" class="btn btn-danger">Cancel</a>
<a href="@url(repository)/blob/@encodeRefName(branch)/@{(pathList ++ Seq(fileName.get)).mkString("/")}" class="btn btn-lg btn-danger">Cancel</a>
}
<input type="submit" id="commit" class="btn btn-success" value="Commit changes" disabled="true"/>
<input type="submit" id="commit" class="btn btn-lg btn-success" value="Commit changes" disabled="true"/>
<input type="hidden" id="charset" name="charset" value="@content.charset"/>
<input type="hidden" id="lineSeparator" name="lineSeparator" value="@content.lineSeparator"/>
<input type="hidden" id="content" name="content" value=""/>
@@ -114,11 +114,15 @@ $(function(){
$('#btn-code').click(function(){
$('#editor').show();
$('#preview').hide();
$('#btn-code').appendClass('active');
$('#btn-preview').removeClass('active');
});
$('#btn-preview').click(function(){
$('#editor').hide();
$('#preview').show()
$('#preview').show();
$('#btn-code').removeClass('active');
$('#btn-preview').appendClass('active');
@if(fileName.map(isRenderable _).getOrElse(false)) {
// update preview
@@ -130,7 +134,7 @@ $(function(){
enableTaskList : false
}, function(data){
$('#preview').empty().append(
$('<div class="markdown-body" style="padding-left: 16px; padding-right: 16px;">').html(data));
$('<div class="markdown-body" style="padding-left: 20px; padding-right: 20px;">').html(data));
prettyPrint();
});
} else {

View File

@@ -15,15 +15,15 @@
@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-small" 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-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-small" data-toggle="tooltip" data-placement="bottom" title="Browse commits for this branch"><i class="octicon octicon-clock"></i></a>
<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-small btn-pullrequest-branch" title="@issue.title" data-toggle="tooltip">#@pullRequest.issueId</a>
<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-small btn-success"><i class="octicon octicon-git-compare" data-toggle="tooltip" title="Compare, review, create a pull request"></i></a>
<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>
}
@helper.html.branchcontrol(
branch,
@@ -125,9 +125,9 @@
}
</table>
@readme.map { case(filePath, content) =>
<div id="readme">
<div class="box-header">@filePath.reverse.head</div>
<div class="box-content-bottom markdown-body" style="padding-left: 16px; padding-right: 16px;">@renderMarkup(filePath, content, branch, repository, false, false, true)</div>
<div id="readme" class="panel panel-default">
<div class="panel-heading strong">@filePath.reverse.head</div>
<div class="panel-body markdown-body" style="padding-left: 20px; padding-right: 20px;">@renderMarkup(filePath, content, branch, repository, false, false, true)</div>
</div>
}
}

View File

@@ -5,23 +5,27 @@
@html.menu("code", repository){
<h1>Tags</h1>
<table class="table table-bordered">
<tr>
<th width="40%">Tag</th>
<th width="20%">Date</th>
<th width="20%">Commit</th>
<th width="20%">Download</th>
</tr>
@repository.tags.reverse.map { tag =>
<thead>
<tr>
<td><a href="@url(repository)/tree/@encodeRefName(tag.name)">@tag.name</a></td>
<td>@helper.html.datetimeago(tag.time, false)</td>
<td class="monospace"><a href="@url(repository)/commit/@tag.id">@tag.id.substring(0, 10)</a></td>
<td>
<a href="@url(repository)/archive/@{encodeRefName(tag.name)}.zip">ZIP</a>
<a href="@url(repository)/archive/@{encodeRefName(tag.name)}.tar.gz">TAR.GZ</a>
</td>
<th width="40%">Tag</th>
<th width="20%">Date</th>
<th width="20%">Commit</th>
<th width="20%">Download</th>
</tr>
}
</thead>
<tbody>
@repository.tags.reverse.map { tag =>
<tr>
<td><a href="@url(repository)/tree/@encodeRefName(tag.name)">@tag.name</a></td>
<td>@helper.html.datetimeago(tag.time, false)</td>
<td class="monospace"><a href="@url(repository)/commit/@tag.id">@tag.id.substring(0, 10)</a></td>
<td>
<a href="@url(repository)/archive/@{encodeRefName(tag.name)}.zip">ZIP</a>
<a href="@url(repository)/archive/@{encodeRefName(tag.name)}.tar.gz">TAR.GZ</a>
</td>
</tr>
}
</tbody>
</table>
}
}
}

View File

@@ -3,8 +3,8 @@
@import context._
@import gitbucket.core.view.helpers._
@html.menu("", repository){
<div class="row-fluid">
<div class="span3">
<div class="row">
<div class="col-md-3">
<div class="box">
<ul class="nav nav-tabs nav-stacked side-menu">
<li@if(active=="code"){ class="active"}>
@@ -26,10 +26,10 @@
</ul>
</div>
</div>
<div class="span9">
<form action="@url(repository)/search" method="GET">
<input type="text" name="q" value="@query" style="width: 80%; margin-bottom: 0px;"/>
<input type="submit" value="Search" class="btn" style="width: 15%;"/>
<div class="col-md-9">
<form action="@url(repository)/search" method="GET" class="form-inline">
<input type="text" name="q" value="@query" class="form-control" style="width: 80%; margin-bottom: 0px;"/>
<input type="submit" value="Search" class="btn btn-default" style="width: 15%;"/>
<input type="hidden" name="type" value="@active"/>
</form>
@body

View File

@@ -27,7 +27,7 @@
<span class="error" id="error-userName"></span>
</div>
@helper.html.account("userName", 300)
<input type="submit" class="btn" value="Add"/>
<input type="submit" class="btn btn-default" value="Add"/>
</form>
}
}

View File

@@ -4,16 +4,16 @@
@html.main("Danger Zone", Some(repository)){
@html.menu("settings", repository){
@menu("danger", repository){
<div class="box">
<div class="box-header">Danger Zone</div>
<div class="box-content">
<div class="panel panel-default">
<div class="panel-heading strong">Danger Zone</div>
<div class="panel-body">
<form id="transfer-form" method="post" action="@url(repository)/settings/transfer" validate="true" autocomplete="off">
<fieldset>
<fieldset class="form-group">
<label class="strong">Transfer Ownership</label>
<div>
Transfer this repo to another user or to group.
<div class="pull-right">
@helper.html.account("newOwner", 150)
@helper.html.account("newOwner", 200)
<input type="submit" class="btn btn-danger" value="Transfer"/>
<div>
<span id="error-newOwner" class="error"></span>
@@ -23,7 +23,7 @@
</fieldset>
</form>
<form id="delete-form" method="post" action="@url(repository)/settings/delete">
<fieldset class="margin">
<fieldset class="margin form-group">
<label class="strong">Delete repository</label>
<div>
Once you delete a repository, there is no going back.
@@ -42,4 +42,4 @@ $(function(){
return confirm('Once you delete a repository, there is no going back.\nAre you sure?');
});
});
</script>
</script>

View File

@@ -0,0 +1,167 @@
@(webHook: gitbucket.core.model.WebHook,
events: Set[gitbucket.core.model.WebHook.Event],
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
info: Option[Any],
create: Boolean)(implicit context: gitbucket.core.controller.Context)
@import context._
@import gitbucket.core.view.helpers._
@import gitbucket.core.model.WebHook._
@check(name: String, event: Event)={
name="@(name).@event.name" value="on" @if(events(event)){checked}
}
@html.main("Settings", Some(repository)){
@html.menu("settings", repository){
@menu("hooks", repository){
@helper.html.information(info)
<div class="panel panel-default">
<div class="panel-heading strong">Webhook / Manage webhook</div>
<div class="panel-body">
<form method="POST" validate="true">
<fieldset class="form-group">
<label class="strong">Payload URL</label>
<div>
<span class="error" id="error-url"></span>
</div>
@if(create){
<input type="text" name="url" id="url" value="@webHook.url" class="form-control" style="display: inline; width: 500px; vertical-align: middle;" required />
} else {
<input type="text" value="@webHook.url" class="form-control" style="display: inline; width: 500px; vertical-align: middle;" disabled />
<input type="hidden" value="@webHook.url" name="url" />
}
<button class="btn btn-default" id="test">Test Hook</button>
</fieldset>
<hr />
<label class="strong">Which events would you like to trigger this webhook?</label>
<div>
<span class="error" id="error-events"></span>
</div>
<!--
<label class="checkbox"><input type="checkbox" @check("events",CommitComment) />Commit comment <small class="help-block">Commit or diff commented on. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",Create) />Create <small class="help-block">Branch, or tag created. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",Delete) />Delete <small class="help-block">Branch, or tag deleted. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",Deployment) />Deployment <small class="help-block">Repository deployed. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",DeploymentStatus) />Deployment status <small class="help-block">Deployment status updated from the API. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",Fork) />Fork <small class="help-block">Repository forked. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",Gollum) />Gollum <small class="help-block">Wiki page updated. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",Member) />Member <small class="help-block">Collaborator added to a non-organization repository. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",PageBuild) />Page build <small class="help-block">Pages site built. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",Public) />Public <small class="help-block">Repository changes from private to public. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",Release) />Release <small class="help-block">Release published in a repository. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",TeamAdd) />Team add <small class="help-block">Team added or modified on a repository. </small> </label>
<label class="checkbox"><input type="checkbox" @check("events",Watch) />Watch <small class="help-block">User stars a repository.</small></label>
<label class="checkbox"><input type="checkbox" @check("events",Status) />Status <small class="help-block">Commit status updated from the API. </small> </label>
-->
<label class="checkbox"><input type="checkbox" @check("events",IssueComment) />Issue comment <span class="help-block normal">Issue commented on. </span> </label>
<label class="checkbox"><input type="checkbox" @check("events",Issues) />Issues <span class="help-block normal">Issue opened, closed<!-- , assigned, or labeled -->. </span> </label>
<label class="checkbox"><input type="checkbox" @check("events",PullRequest) />Pull Request <span class="help-block normal">Pull Request opened, closed<!-- , assigned, labeled -->, or synchronized. </span> </label>
<label class="checkbox"><input type="checkbox" @check("events",PullRequestReviewComment) />Pull Request review comment <span class="help-block normal">Pull Request diff commented on. </span> </label>
<label class="checkbox"><input type="checkbox" @check("events",Push) />Push <span class="help-block normal">Git push to a repository. </span> </label>
<div class="text-right">
@if(!create){
<input type="submit" class="btn btn-success" value="Update webhook" formaction="@url(repository)/settings/hooks/edit" />
<a href="@url(repository)/settings/hooks/delete?url=@urlEncode(webHook.url)" class="btn btn-danger" onclick="return confirm('delete webhook for @webHook.url ?')">
Delete webhook
</a>
} else {
<input type="submit" class="btn btn-success" value="Add webhook" formaction="@url(repository)/settings/hooks/new" />
}
</div>
</form>
</div>
</div>
<div class="modal" id="test-report-modal" role="dialog" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>WebHook Test</h3>
</div>
<div class="modal-body" style="max-height: 300px; overflow: auto;">
<p>request to <span id="test-modal-url" style="word-break: break-all; word-wrap: break-word; white-space: pre; white-space: pre-wrap;"></span></p>
<div id="test-report" style="display:none">
<ul class="nav nav-tabs" id="test-report-tab">
<li class="active"><a href="#request">Request</a></li>
<li><a href="#response">Response <span class="badge badge-success" id="res-status"></span></a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="request">
<div id="req-errors" class="alert alert-error">
ERROR<span id="req-errors-body"></span>
</div>
<div id="req-success" style="display:none">
Headers
<pre id="req-headers"></pre>
Payload
<pre id="req-payload"></pre>
</div>
</div>
<div class="tab-pane" id="response">
<div id="res-errors" class="alert alert-error">
ERROR<span id="res-errors-body"></span>
</div>
<div id="res-success" style="display:none">
Headers
<pre id="res-headers"></pre>
Body
<pre id="res-body"></pre>
</div>
</div>
</div>
</div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<script>
$(function(){
$('#test-report-tab a').click(function (e) {
e.preventDefault();
$(this).tab('show');
});
$('#test').click(function(e){
e.stopPropagation();
e.stopImmediatePropagation();
e.preventDefault();
var url = this.form.url.value;
if(!/^https?:\/\/.+/.test(url)){
alert("invalid url");
return;
}
$("#test-modal-url").text(url)
$("#test-report-modal").modal('show')
$("#test-report").hide();
$.ajax({
method:'POST',
url:'@url(repository)/settings/hooks/test?url=' + encodeURIComponent(url),
success: function(e){
//console.log(e);
$('#test-report-tab a:first').tab('show');
$("#test-report").show();
$("#req-success").toggle(e.request&&!e.request.error);
$("#req-errors").toggle(e.request&&!!e.request.error);
$("#req-errors-body").text(e.request.error);
function headers(h){
h = h["headers"];
return h ? $.map(h, function(h){
return $("<div>").append($('<b>').text(h[0] + ":"),$('<span>').text(" " + h[1]))
}):"";
}
$("#req-headers").html(headers(e.request));
$("#req-payload").text(e.request && e.request.payload ? JSON.stringify(JSON.parse(e.request.payload),undefined,4) : "");
$("#res-success").toggle(e.responce && !e.responce.error);
$("#res-errors").toggle(e.responce && !!e.responce.error);
$("#res-errors-body").text(e.responce.error);
var success = !!(e.responce && e.responce.status && /^2\d\d$/.test(e.responce.status.statusCode));
$("#res-status").text((e.responce && e.responce.status && e.responce.status.statusCode) || "ERROR");
$("#res-status").toggleClass("badge-success", success).toggleClass("badge-important", !success);
$("#res-headers").html(headers(e.responce));
$("#res-body").text(e.responce && e.responce.body ? e.responce.body : "");
},
});
return false;
});
})
</script>
}
}
}

View File

@@ -1,5 +1,4 @@
@(webHooks: List[gitbucket.core.model.WebHook],
enteredUrl: Option[Any],
@(webHooks: List[(gitbucket.core.model.WebHook, Set[gitbucket.core.model.WebHook.Event])],
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
info: Option[Any])(implicit context: gitbucket.core.controller.Context)
@import context._
@@ -8,20 +7,39 @@
@html.menu("settings", repository){
@menu("hooks", repository){
@helper.html.information(info)
<h3>WebHook URLs</h3>
<ul>
@webHooks.map { webHook =>
<li>@webHook.url <a href="@url(repository)/settings/hooks/delete?url=@urlEncode(webHook.url)" class="remove">(remove)</a></li>
}
</ul>
<form method="POST" validate="true">
<div>
<span class="error" id="error-url"></span>
<div class="panel panel-default">
<div class="panel-heading strong">
<a href="@url(repository)/settings/hooks/new" class="btn btn-success btn-sm pull-right">Add webhook</a>
Webhooks
</div>
<input type="text" name="url" id="url" value="@enteredUrl" style="width: 300px; margin-bottom: 0px;"/>
<input type="submit" class="btn" formaction="@url(repository)/settings/hooks/add" value="Add"/>
<input type="submit" class="btn" formaction="@url(repository)/settings/hooks/test" value="Test Hook"/>
</form>
<div class="panel-body">
<p>
Webhooks allow external services to be notified when certain events happen within your repository.
When the specified events happen, well send a POST request to each of the URLs you provide.
Learn more in <a href="https://github.com/takezoe/gitbucket/wiki/API-WebHook" target="_blank">GitBucket Wiki Webhook Page</a>.
</p>
<table class="table table-condensed" style="margin-bottom:0">
@webHooks.map { case (webHook, events) =>
<tr><td style="vertical-align: middle;">
<a href="@url(repository)/settings/hooks/edit?url=@urlEncode(webHook.url)" class="css-truncate" style="max-width:360px">
<span class="css-truncate-target">@webHook.url</span>
</a>
<em class="css-truncate" style="max-width: 225px;">(<span class="css-truncate-target">@events.map(_.name).mkString(", ")</span>)</em>
</td><td>
<div class="btn-group pull-right">
<a href="@url(repository)/settings/hooks/edit?url=@urlEncode(webHook.url)" class="btn btn-default">
<span class="octicon octicon-pencil"></span>
</a>
<a href="@url(repository)/settings/hooks/delete?url=@urlEncode(webHook.url)" class="btn btn-danger" onclick="return confirm('delete webhook for @webHook.url ?')">
<span class="octicon octicon-x"></span>
</a>
</div>
</td></tr>
}
</table>
</div>
</div>
}
}
}

View File

@@ -1,8 +1,8 @@
@(active: String, repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(body: Html)(implicit context: gitbucket.core.controller.Context)
@import context._
@import gitbucket.core.view.helpers._
<div class="row-fluid">
<div class="span3">
<div class="row">
<div class="col-md-3">
<div class="box">
<ul class="nav nav-tabs nav-stacked side-menu">
<li@if(active=="options"){ class="active"}>
@@ -20,7 +20,7 @@
</ul>
</div>
</div>
<div class="span9">
<div class="col-md-9">
@body
</div>
</div>

View File

@@ -6,21 +6,21 @@
@menu("options", repository){
@helper.html.information(info)
<form id="form" method="post" action="@url(repository)/settings/options" validate="true">
<div class="box">
<div class="box-header">Settings</div>
<div class="box-content">
<fieldset>
<div class="panel panel-default">
<div class="panel-heading strong">Settings</div>
<div class="panel-body">
<fieldset class="form-group">
<label for="repositoryName" class="strong">Repository Name:</label>
<input type="text" name="repositoryName" id="repositoryName" value="@repository.name"/>
<input type="text" name="repositoryName" id="repositoryName" class="form-control" value="@repository.name"/>
<span id="error-repositoryName" class="error"></span>
</fieldset>
<fieldset class="margin">
<fieldset class="margin form-group">
<label for="description" class="strong">Description:</label>
<input type="text" name="description" id="description" style="width: 600px;" value="@repository.repository.description"/>
<input type="text" name="description" id="description" class="form-control" value="@repository.repository.description"/>
</fieldset>
<fieldset class="margin">
<fieldset class="margin form-group">
<label for="defaultBranch" class="strong">Default Branch:</label>
<select name="defaultBranch" id="defaultBranch"@if(repository.branchList.isEmpty){ disabled}>
<select name="defaultBranch" id="defaultBranch"@if(repository.branchList.isEmpty){ disabled} class="form-control">
@if(repository.branchList.isEmpty){
<option value="none" selected>No Branch</option>
} else {
@@ -40,57 +40,24 @@
@if(!repository.repository.isPrivate ){ checked }
@if(repository.repository.parentUserName.isDefined){ disabled }
>
<span class="strong">Public</span><br>
<div>
<span>All users and guests can read this repository.</span>
<span class="strong"><i class="octicon octicon-repo"></i> Public</span><br>
<div class="normal muted">
Anyone can see this repository. You choose who can commit.
</div>
</label>
</fieldset>
<fieldset>
<label class="radio">
<input type="radio" name="isPrivate" value="true"
@if(repository.repository.isPrivate ){ checked }
@if(repository.repository.parentUserName.isDefined){ disabled }
>
<span class="strong">Private</span><br>
<div>
<span>Only collaborators can read this repository.</span>
<span class="strong"><i class="octicon octicon-lock"></i> Private</span><br>
<div class="normal muted">
You choose who can see and commit to this repository.
</div>
</label>
</fieldset>
</div>
</div>
@*
<div class="box">
<div class="box-header">Features:</div>
<div class="box-content">
<dl>
<dt>
<label class="checkbox strong">
<input type="checkbox" name="wiki" id="wiki"/> Wiki
</label>
</dt>
<dd>
Adds lightweight Wiki system to this repository.
This is the simplest way to provide documentation or examples.
Only collaborators can edit Wiki pages.
</dd>
</dl>
<hr>
<dl>
<dt>
<label class="checkbox strong">
<input type="checkbox" name="issue" id="issue"/> Issue
</label>
</dt>
<dd>
Adds lightweight issue tracking integrated with this repository.
All users who have signed in and can access this repository can register an issue.
</dd>
</dl>
</div>
</div>
*@
<div class="align-right" style="margin-top: 20px;">
<input type="submit" class="btn btn-success" value="Apply changes"/>
</div>

View File

@@ -1,23 +1,29 @@
@(systemSettings: gitbucket.core.service.SystemSettingsService.SystemSettings)(implicit context: gitbucket.core.controller.Context)
@import context._
<div class="box-header">
@if(systemSettings.allowAccountRegistration){
<div class="pull-right">
<a href="@path/register" class="btn btn-mini">Create new account</a>
</div>
}
<span class="strong">Sign in</span>
</div>
<div class="box-content-bottom">
<form action="@path/signin" method="POST" validate="true">
<label for="userName">Username:</label>
<span id="error-userName" class="error"></span>
<input type="text" name="userName" id="userName" style="width: 95%" autofocus/>
<label for="password">Password:</label>
<span id="error-password" class="error"></span>
<input type="password" name="password" id="password" style="width: 95%"/>
<div>
<input type="submit" class="btn btn-success" value="Sign in"/>
</div>
</form>
<div class="panel panel-default">
<div class="panel-heading strong">
@if(systemSettings.allowAccountRegistration){
<div class="pull-right">
<a href="@path/register" class="btn btn-mini">Create new account</a>
</div>
}
Sign in
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">
<form action="@path/signin" method="POST" validate="true">
<div class="form-group">
<label for="userName">Username:</label>
<span id="error-userName" class="error"></span>
<input type="text" name="userName" id="userName" class="form-control" autofocus/>
</div>
<div class="form-group">
<label for="password">Password:</label>
<span id="error-password" class="error"></span>
<input type="password" name="password" id="password" class="form-control"/>
</div>
<input type="submit" class="btn btn-success" value="Sign in"/>
</form>
</li>
</ul>
</div>

Some files were not shown because too many files have changed in this diff Show More