Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4088b2c1e8 | ||
|
|
919d55c002 | ||
|
|
068bbd0c3b | ||
|
|
9f50528192 | ||
|
|
4c149cf01c | ||
|
|
c86c706406 | ||
|
|
3b0a0f55b5 | ||
|
|
4232b8184e | ||
|
|
e5f3dfe293 | ||
|
|
22af94d36a | ||
|
|
d6b6781861 | ||
|
|
2222299793 | ||
|
|
fdd9a184b5 | ||
|
|
99492e3f8e | ||
|
|
a42c40bbc1 | ||
|
|
2794f9fcfc | ||
|
|
28c0262e74 | ||
|
|
8634191bd2 | ||
|
|
f73c86d533 | ||
|
|
f042d709ac | ||
|
|
e2a6149a93 | ||
|
|
b2a7e2c7e2 | ||
|
|
89fc143075 | ||
|
|
a754a92799 | ||
|
|
dc26fcf609 | ||
|
|
b9db57eeef | ||
|
|
9b377c727d | ||
|
|
e5b8d81bb4 | ||
|
|
c85b31a7d5 | ||
|
|
6580e5458a | ||
|
|
4e4e65eaa6 | ||
|
|
9d19aad384 | ||
|
|
c16a9f234b | ||
|
|
ace551c33d | ||
|
|
1e6e686692 | ||
|
|
afdcc3f7c0 | ||
|
|
00e64bc46c | ||
|
|
a959e1820f | ||
|
|
3dfbdbfe51 | ||
|
|
5c46dc0bd3 | ||
|
|
db60db674f | ||
|
|
687a4f14e1 | ||
|
|
bb10365b8b | ||
|
|
74ed3bf6a0 | ||
|
|
d1d7fdc488 | ||
|
|
67775a4c62 | ||
|
|
317b5cb096 | ||
|
|
2929517d7e | ||
|
|
51e788396d | ||
|
|
1321653bf6 | ||
|
|
3899854854 | ||
|
|
c0ca842ba7 | ||
|
|
24b05d28db | ||
|
|
f0268b105c | ||
|
|
0a46e180a9 | ||
|
|
e6a215a9c3 | ||
|
|
8ca7117065 | ||
|
|
ba0a07b835 | ||
|
|
4a35b65c2c | ||
|
|
836fa47812 | ||
|
|
5b658ef6ff | ||
|
|
e9ff24d9a7 | ||
|
|
a92051a4c3 | ||
|
|
77b3650580 | ||
|
|
67ee6857ad | ||
|
|
5ab15c0a14 | ||
|
|
96a3f2c301 | ||
|
|
85707264c4 | ||
|
|
ed05422ea8 | ||
|
|
8f10c8051e | ||
|
|
41fff399b5 | ||
|
|
9e237647b0 | ||
|
|
1be53c6746 | ||
|
|
f2368b03c0 | ||
|
|
95284c0b36 | ||
|
|
f1e21a93fb | ||
|
|
689811f659 | ||
|
|
33ccb4e98c | ||
|
|
29f6e98f9c | ||
|
|
775c8cc064 | ||
|
|
9a974d047c | ||
|
|
3fd252d2db | ||
|
|
43edb034c8 | ||
|
|
e629ca391e | ||
|
|
0db7eba3f2 | ||
|
|
a472abc88e | ||
|
|
861c619c19 | ||
|
|
124f331963 | ||
|
|
76fcd44191 | ||
|
|
2124c0b88c | ||
|
|
2f5ab8e3b9 | ||
|
|
32c8b6914b | ||
|
|
7e9d940f64 | ||
|
|
59e826b630 | ||
|
|
88f3ee4b13 | ||
|
|
b7380a084e | ||
|
|
cb65e790ae | ||
|
|
573eabee93 | ||
|
|
f0d4c6546a | ||
|
|
b0943c87c8 | ||
|
|
4f1208ea98 | ||
|
|
02f16639ea | ||
|
|
cd5e28c0b8 | ||
|
|
6f7579f8d9 | ||
|
|
f1d2a71b49 | ||
|
|
fd4fe0dc0d | ||
|
|
3d5e4a4225 | ||
|
|
10ffb452d0 | ||
|
|
3218bddbdc | ||
|
|
59f78dcbcb | ||
|
|
0fe062a02f | ||
|
|
869eaf8cfd | ||
|
|
5b445c9736 | ||
|
|
6f3f3eaa99 | ||
|
|
a23ce92676 | ||
|
|
ad55d5199d | ||
|
|
bd05895761 | ||
|
|
c68dd6c891 | ||
|
|
daae9ae1e7 | ||
|
|
33dde75ae1 | ||
|
|
b2e1e1796d | ||
|
|
1ff68111b4 | ||
|
|
dff816324d | ||
|
|
a33e2c6e36 | ||
|
|
75ef82d18a | ||
|
|
eb3c522122 | ||
|
|
6c8bcfc62e |
2
.gitignore
vendored
@@ -1,5 +1,7 @@
|
||||
*.class
|
||||
*.log
|
||||
.ensime
|
||||
.ensime_cache
|
||||
|
||||
# sbt specific
|
||||
dist/*
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
language: scala
|
||||
sudo: false
|
||||
script:
|
||||
- . env.sh
|
||||
- sbt test
|
||||
|
||||
18
README.md
@@ -14,12 +14,13 @@ The current version of GitBucket provides a basic features below:
|
||||
- Wiki
|
||||
- Issues
|
||||
- Fork / Pull request
|
||||
- Mail notification
|
||||
- Email notification
|
||||
- Activity timeline
|
||||
- User management (for Administrators)
|
||||
- Group (like Organization in Github)
|
||||
- Group (like Organization in GitHub)
|
||||
- LDAP integration
|
||||
- Gravatar support
|
||||
- Plug-in system
|
||||
|
||||
Following features are not implemented, but we will make them in the future release!
|
||||
|
||||
@@ -79,8 +80,21 @@ Run the following commands in `Terminal` to
|
||||
- start gitbucket: `launchctl load ~/Library/LaunchAgents/gitbucket.plist`
|
||||
- stop gitbucket: `launchctl unload ~/Library/LaunchAgents/gitbucket.plist`
|
||||
|
||||
Plug-ins
|
||||
--------
|
||||
GitBucket has the plug-in system to extend GitBucket from outside of GitBucket. Some plug-ins are available now:
|
||||
|
||||
- [gitbucket-gist-plugin](https://github.com/takezoe/gitbucket-gist-plugin)
|
||||
- [gitbucket-announce-plugin](https://github.com/McFoggy/gitbucket-announce-plugin)
|
||||
|
||||
Release Notes
|
||||
--------
|
||||
### 3.5 - 1 Aug 2015
|
||||
- Octicons has been applied
|
||||
- Global header has been enhanced. Now it's further similar to GitHub.
|
||||
- Default compare / pull request target has been changed to the parent repository
|
||||
- A lot of updates for [gitbucket-gist-plugin](https://github.com/takezoe/gitbucket-gist-plugin)
|
||||
|
||||
### 3.4 - 27 Jun 2015
|
||||
- Declarative style plug-in definition
|
||||
- New extension point to add markup render
|
||||
|
||||
@@ -44,7 +44,7 @@ GITBUCKET_WAR_DIR=$GITBUCKET_DIR/lib
|
||||
GITBUCKET_WAR_FILE=$GITBUCKET_WAR_DIR/gitbucket.war
|
||||
|
||||
# GitBucket version to fetch when installing
|
||||
GITBUCKET_VERSION=2.1
|
||||
GITBUCKET_VERSION=3.5
|
||||
|
||||
#
|
||||
# End of configuration section. Ignore this part
|
||||
|
||||
@@ -2,7 +2,7 @@ Automatic Schema Updating
|
||||
========
|
||||
GitBucket uses H2 database to manage project and account data. GitBucket updates database schema automatically in the first run after the upgrading.
|
||||
|
||||
To release a new version of GitBucket, add the version definition to the [servlet.AutoUpdate](https://github.com/takezoe/gitbucket/blob/master/src/main/scala/servlet/AutoUpdateListener.scala) at first.
|
||||
To release a new version of GitBucket, add the version definition to the [gitbucket.core.servlet.AutoUpdate](https://github.com/takezoe/gitbucket/blob/master/src/main/scala/gitbucket/core/servlet/AutoUpdate.scala) at first.
|
||||
|
||||
```scala
|
||||
object AutoUpdate {
|
||||
|
||||
@@ -16,6 +16,8 @@ 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
|
||||
...
|
||||
@@ -24,15 +26,38 @@ C:\gitbucket> sbt
|
||||
> ~ ;copy-resources;aux-compile
|
||||
```
|
||||
|
||||
Linux:
|
||||
|
||||
```
|
||||
~/gitbucket$ ./sbt.sh
|
||||
...
|
||||
> container:start
|
||||
...
|
||||
> ~ ;copy-resources;aux-compile
|
||||
```
|
||||
|
||||
Build war file
|
||||
--------
|
||||
|
||||
To build war file, run the following command:
|
||||
|
||||
Windows:
|
||||
|
||||
```
|
||||
C:\gitbucket> sbt package
|
||||
```
|
||||
|
||||
Linux:
|
||||
|
||||
```
|
||||
~/gitbucket$ ./sbt.sh package
|
||||
```
|
||||
|
||||
`gitbucket_2.11-x.x.x.war` is generated into `target/scala-2.11`.
|
||||
|
||||
To build executable war file, run Ant at the top of the source tree. It generates executable `gitbucket.war` into `target/scala-2.11`. We release this war file as release artifact. Please note the current build.xml works on Windows only. Replace `sbt.bat` with `sbt.sh` in build.xml if you want to run it on Linux.
|
||||
To build executable war file, run
|
||||
|
||||
* Windows: Not available
|
||||
* Linux: `./release/make-release-war.sh`
|
||||
|
||||
at the top of the source tree. It generates executable `gitbucket.war` into `target/scala-2.11`. We release this war file as release artifact.
|
||||
|
||||
@@ -379,21 +379,21 @@
|
||||
<path d="M588.909,926.673 L560.094,910.545 L564.713,935.73 L588.909,926.673 z" fill-opacity="0" stroke="#B3B3B3" stroke-width="1.313" stroke-linecap="round"/>
|
||||
</g>
|
||||
<g id="rect3075-11">
|
||||
<path d="M779.562,898.094 C779.396,912.75 779.229,927.406 779.062,942.062 C781.26,942.084 783.458,942.104 785.656,942.125 C784.104,943.688 782.552,945.25 781,946.813 C801.708,967.521 822.417,988.229 843.125,1008.938 C858.531,993.531 873.938,978.125 889.344,962.719 C868.635,942 847.927,921.281 827.219,900.563 C825.49,902.302 823.76,904.042 822.031,905.781 C822.052,903.386 822.073,900.99 822.094,898.594 C807.917,898.427 793.74,898.261 779.563,898.094 z" fill="#FFFFFF"/>
|
||||
<path d="M779.562,898.094 C779.396,912.75 779.229,927.406 779.062,942.062 C781.26,942.084 783.458,942.104 785.656,942.125 C784.104,943.688 782.552,945.25 781,946.813 C801.708,967.521 822.417,988.229 843.125,1008.938 C858.531,993.531 873.938,978.125 889.344,962.719 C868.635,942 847.927,921.281 827.219,900.563 C825.49,902.302 823.76,904.042 822.031,905.781 C822.052,903.386 822.073,900.99 822.094,898.594 C807.917,898.427 793.74,898.261 779.563,898.094 z" fill-opacity="0" stroke="#B3B3B3" stroke-width="9" stroke-linecap="round"/>
|
||||
<path d="M1389.845,733.625 C1389.679,748.281 1389.512,762.937 1389.345,777.593 C1391.543,777.615 1393.741,777.635 1395.939,777.656 C1394.387,779.219 1392.835,780.781 1391.283,782.344 C1411.991,803.052 1432.7,823.76 1453.408,844.469 C1468.814,829.062 1484.221,813.656 1499.627,798.25 C1478.918,777.531 1458.21,756.812 1437.502,736.094 C1435.773,737.833 1434.043,739.573 1432.314,741.312 C1432.335,738.917 1432.356,736.521 1432.377,734.125 C1418.2,733.958 1404.023,733.792 1389.846,733.625 z" fill="#FFFFFF"/>
|
||||
<path d="M1389.845,733.625 C1389.679,748.281 1389.512,762.937 1389.345,777.593 C1391.543,777.615 1393.741,777.635 1395.939,777.656 C1394.387,779.219 1392.835,780.781 1391.283,782.344 C1411.991,803.052 1432.7,823.76 1453.408,844.469 C1468.814,829.062 1484.221,813.656 1499.627,798.25 C1478.918,777.531 1458.21,756.812 1437.502,736.094 C1435.773,737.833 1434.043,739.573 1432.314,741.312 C1432.335,738.917 1432.356,736.521 1432.377,734.125 C1418.2,733.958 1404.023,733.792 1389.846,733.625 z" fill-opacity="0" stroke="#B3B3B3" stroke-width="9" stroke-linecap="round"/>
|
||||
</g>
|
||||
<path d="M606.483,964.91 L606.483,951.243 L672.089,951.243 L672.089,964.91 z" fill="#B3B3B3" id="rect2995-0-2-8-6"/>
|
||||
<g id="rect3075-11-7">
|
||||
<path d="M786.383,905.075 C786.256,916.229 786.129,927.383 786.003,938.537 C787.675,938.553 789.348,938.568 791.021,938.584 C789.839,939.773 788.658,940.963 787.477,942.152 C803.237,957.911 818.997,973.671 834.756,989.431 C846.481,977.706 858.206,965.982 869.93,954.257 C854.171,938.489 838.411,922.722 822.651,906.954 C821.335,908.278 820.019,909.602 818.703,910.926 C818.719,909.102 818.734,907.279 818.751,905.456 C807.961,905.329 797.172,905.202 786.383,905.075 z" fill="#FFFFFF"/>
|
||||
<path d="M786.383,905.075 C786.256,916.229 786.129,927.383 786.003,938.537 C787.675,938.553 789.348,938.568 791.021,938.584 C789.839,939.773 788.658,940.963 787.477,942.152 C803.237,957.911 818.997,973.671 834.756,989.431 C846.481,977.706 858.206,965.982 869.93,954.257 C854.171,938.489 838.411,922.722 822.651,906.954 C821.335,908.278 820.019,909.602 818.703,910.926 C818.719,909.102 818.734,907.279 818.751,905.456 C807.961,905.329 797.172,905.202 786.383,905.075 z" fill-opacity="0" stroke="#FFFFFF" stroke-width="6.849" stroke-linecap="round"/>
|
||||
<path d="M1396.666,740.606 C1396.539,751.76 1396.412,762.914 1396.286,774.068 C1397.958,774.084 1399.631,774.099 1401.304,774.115 C1400.122,775.304 1398.941,776.494 1397.76,777.683 C1413.52,793.442 1429.28,809.202 1445.039,824.962 C1456.764,813.237 1468.489,801.513 1480.213,789.788 C1464.454,774.02 1448.694,758.253 1432.934,742.485 C1431.618,743.809 1430.302,745.133 1428.986,746.457 C1429.002,744.633 1429.017,742.81 1429.034,740.987 C1418.244,740.86 1407.455,740.733 1396.666,740.606 z" fill="#FFFFFF"/>
|
||||
<path d="M1396.666,740.606 C1396.539,751.76 1396.412,762.914 1396.286,774.068 C1397.958,774.084 1399.631,774.099 1401.304,774.115 C1400.122,775.304 1398.941,776.494 1397.76,777.683 C1413.52,793.442 1429.28,809.202 1445.039,824.962 C1456.764,813.237 1468.489,801.513 1480.213,789.788 C1464.454,774.02 1448.694,758.253 1432.934,742.485 C1431.618,743.809 1430.302,745.133 1428.986,746.457 C1429.002,744.633 1429.017,742.81 1429.034,740.987 C1418.244,740.86 1407.455,740.733 1396.666,740.606 z" fill-opacity="0" stroke="#FFFFFF" stroke-width="6.849" stroke-linecap="round"/>
|
||||
</g>
|
||||
<g id="path3100-2">
|
||||
<path d="M813.748,916.688 C818.255,921.195 818.255,928.501 813.748,933.008 C809.242,937.514 801.935,937.514 797.429,933.008 C792.922,928.501 792.922,921.195 797.429,916.688 C801.935,912.182 809.242,912.182 813.748,916.688 z" fill="#FFFFFF"/>
|
||||
<path d="M813.748,916.688 C818.255,921.195 818.255,928.501 813.748,933.008 C809.242,937.514 801.935,937.514 797.429,933.008 C792.922,928.501 792.922,921.195 797.429,916.688 C801.935,912.182 809.242,912.182 813.748,916.688 z" fill-opacity="0" stroke="#B3B3B3" stroke-width="7.585" stroke-linecap="round"/>
|
||||
<path d="M1424.031,752.219 C1428.538,756.726 1428.538,764.032 1424.031,768.539 C1419.525,773.045 1412.218,773.045 1407.712,768.539 C1403.205,764.032 1403.205,756.726 1407.712,752.219 C1412.218,747.713 1419.525,747.713 1424.031,752.219 z" fill="#FFFFFF"/>
|
||||
<path d="M1424.031,752.219 C1428.538,756.726 1428.538,764.032 1424.031,768.539 C1419.525,773.045 1412.218,773.045 1407.712,768.539 C1403.205,764.032 1403.205,756.726 1407.712,752.219 C1412.218,747.713 1419.525,747.713 1424.031,752.219 z" fill-opacity="0" stroke="#B3B3B3" stroke-width="7.585" stroke-linecap="round"/>
|
||||
</g>
|
||||
<g id="rect4114">
|
||||
<path d="M813.845,955.107 L834.888,934.064 L864.012,963.188 L842.969,984.231 z" fill="#FFFFFF"/>
|
||||
<path d="M813.845,955.107 L834.888,934.064 L864.012,963.188 L842.969,984.231 z" fill-opacity="0" stroke="#B3B3B3" stroke-width="7.133"/>
|
||||
<path d="M1424.128,790.638 L1445.171,769.595 L1474.295,798.719 L1453.252,819.762 z" fill="#FFFFFF"/>
|
||||
<path d="M1424.128,790.638 L1445.171,769.595 L1474.295,798.719 L1453.252,819.762 z" fill-opacity="0" stroke="#B3B3B3" stroke-width="7.133"/>
|
||||
</g>
|
||||
<g id="path2991-7-6">
|
||||
<path d="M969.889,84.636 C969.889,122.652 939.072,153.469 901.056,153.469 C863.04,153.469 832.223,122.652 832.223,84.636 C832.223,46.62 863.04,15.803 901.056,15.803 C939.072,15.803 969.889,46.62 969.889,84.636 z" fill="#A0A0A0"/>
|
||||
@@ -750,5 +750,45 @@
|
||||
</g>
|
||||
<path d="M1396.792,592.168 C1426.908,592.168 1450.613,610.989 1450.613,639.54" fill-opacity="0" stroke="#A0A0A0" stroke-width="20" stroke-linecap="round"/>
|
||||
<path d="M1397.792,545.653 C1453.613,544.493 1499.627,588.735 1499.627,636.54" fill-opacity="0" stroke="#A0A0A0" stroke-width="20" stroke-linecap="round"/>
|
||||
<path d="M871.125,1039.025 C871.125,1039.025 873.794,1016.889 908.043,1011.524 C919.748,1009.691 945.861,1005.107 945.861,978.522" fill-opacity="0" stroke="#A0A0A0" stroke-width="17.059" id="path3207"/>
|
||||
<g id="rect3818-4-8-4">
|
||||
<path d="M869.474,950.497 L871.997,950.497 L871.997,1031.308 L869.474,1031.308 z" fill="#A0A0A0"/>
|
||||
<g>
|
||||
<path d="M869.474,950.497 L871.997,950.497 L871.997,1031.308 L869.474,1031.308 z" fill="#A0A0A0"/>
|
||||
<path d="M869.474,950.497 L871.997,950.497 L871.997,1031.308 L869.474,1031.308 z" fill-opacity="0" stroke="#A0A0A0" stroke-width="15"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path3795-4-8-7-8">
|
||||
<path d="M888.074,1051.656 C888.074,1060.906 880.5,1068.405 871.159,1068.405 C861.817,1068.405 854.243,1060.906 854.243,1051.656 C854.243,1042.406 861.817,1034.908 871.159,1034.908 C880.5,1034.908 888.074,1042.406 888.074,1051.656 z" fill="#FFFFFF"/>
|
||||
<path d="M888.074,1051.656 C888.074,1060.906 880.5,1068.405 871.159,1068.405 C861.817,1068.405 854.243,1060.906 854.243,1051.656 C854.243,1042.406 861.817,1034.908 871.159,1034.908 C880.5,1034.908 888.074,1042.406 888.074,1051.656 z" fill-opacity="0" stroke="#A0A0A0" stroke-width="7.989"/>
|
||||
</g>
|
||||
<g id="path3795-8-4-8">
|
||||
<path d="M886.883,935.155 C886.883,944.404 879.31,951.903 869.968,951.903 C860.626,951.903 853.054,944.404 853.054,935.155 C853.054,925.904 860.626,918.405 869.968,918.405 C879.31,918.405 886.883,925.904 886.883,935.155 z" fill="#FFFFFF"/>
|
||||
<path d="M886.883,935.155 C886.883,944.404 879.31,951.903 869.968,951.903 C860.626,951.903 853.054,944.404 853.054,935.155 C853.054,925.904 860.626,918.405 869.968,918.405 C879.31,918.405 886.883,925.904 886.883,935.155 z" fill-opacity="0" stroke="#A0A0A0" stroke-width="7.989"/>
|
||||
</g>
|
||||
<g id="path3795-8-4-8-2">
|
||||
<path d="M965.046,971.602 C965.046,980.852 957.472,988.351 948.13,988.351 C938.789,988.351 931.215,980.852 931.215,971.602 C931.215,962.352 938.789,954.854 948.13,954.854 C957.472,954.854 965.046,962.352 965.046,971.602 z" fill="#FFFFFF"/>
|
||||
<path d="M965.046,971.602 C965.046,980.852 957.472,988.351 948.13,988.351 C938.789,988.351 931.215,980.852 931.215,971.602 C931.215,962.352 938.789,954.854 948.13,954.854 C957.472,954.854 965.046,962.352 965.046,971.602 z" fill-opacity="0" stroke="#A0A0A0" stroke-width="7.989"/>
|
||||
</g>
|
||||
<path d="M1114.353,1042.412 C1114.353,1042.412 1117.022,1020.275 1151.271,1014.91 C1162.976,1013.077 1189.089,1008.493 1189.089,981.909" fill-opacity="0" stroke="#000000" stroke-width="17.059" id="path3207"/>
|
||||
<g id="rect3818-4-8-4">
|
||||
<path d="M1112.701,953.884 L1115.225,953.884 L1115.225,1034.695 L1112.701,1034.695 z" fill="#A0A0A0"/>
|
||||
<g>
|
||||
<path d="M1112.701,953.884 L1115.225,953.884 L1115.225,1034.695 L1112.701,1034.695 z" fill="#A0A0A0"/>
|
||||
<path d="M1112.701,953.884 L1115.225,953.884 L1115.225,1034.695 L1112.701,1034.695 z" fill-opacity="0" stroke="#000000" stroke-width="15"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="path3795-4-8-7-8">
|
||||
<path d="M1131.302,1055.043 C1131.302,1064.293 1123.728,1071.791 1114.386,1071.791 C1105.045,1071.791 1097.471,1064.293 1097.471,1055.043 C1097.471,1045.792 1105.045,1038.294 1114.386,1038.294 C1123.728,1038.294 1131.302,1045.792 1131.302,1055.043 z" fill="#FFFFFF"/>
|
||||
<path d="M1131.302,1055.043 C1131.302,1064.293 1123.728,1071.791 1114.386,1071.791 C1105.045,1071.791 1097.471,1064.293 1097.471,1055.043 C1097.471,1045.792 1105.045,1038.294 1114.386,1038.294 C1123.728,1038.294 1131.302,1045.792 1131.302,1055.043 z" fill-opacity="0" stroke="#000000" stroke-width="7.989"/>
|
||||
</g>
|
||||
<g id="path3795-8-4-8">
|
||||
<path d="M1130.111,938.542 C1130.111,947.791 1122.537,955.29 1113.196,955.29 C1103.854,955.29 1096.282,947.791 1096.282,938.542 C1096.282,929.291 1103.854,921.792 1113.196,921.792 C1122.537,921.792 1130.111,929.291 1130.111,938.542 z" fill="#FFFFFF"/>
|
||||
<path d="M1130.111,938.542 C1130.111,947.791 1122.537,955.29 1113.196,955.29 C1103.854,955.29 1096.282,947.791 1096.282,938.542 C1096.282,929.291 1103.854,921.792 1113.196,921.792 C1122.537,921.792 1130.111,929.291 1130.111,938.542 z" fill-opacity="0" stroke="#000000" stroke-width="7.989"/>
|
||||
</g>
|
||||
<g id="path3795-8-4-8-2">
|
||||
<path d="M1208.274,974.989 C1208.274,984.239 1200.7,991.738 1191.358,991.738 C1182.016,991.738 1174.443,984.239 1174.443,974.989 C1174.443,965.739 1182.016,958.241 1191.358,958.241 C1200.7,958.241 1208.274,965.739 1208.274,974.989 z" fill="#FFFFFF"/>
|
||||
<path d="M1208.274,974.989 C1208.274,984.239 1200.7,991.738 1191.358,991.738 C1182.016,991.738 1174.443,984.239 1174.443,974.989 C1174.443,965.739 1182.016,958.241 1191.358,958.241 C1200.7,958.241 1208.274,965.739 1208.274,974.989 z" fill-opacity="0" stroke="#000000" stroke-width="7.989"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 82 KiB |
@@ -6,6 +6,17 @@ Update version number
|
||||
|
||||
Note to update version number in files below:
|
||||
|
||||
### project/build.scala
|
||||
|
||||
```scala
|
||||
object MyBuild extends Build {
|
||||
val Organization = "gitbucket"
|
||||
val Name = "gitbucket"
|
||||
val Version = "3.3.0" // <---- update version!!
|
||||
val ScalaVersion = "2.11.6"
|
||||
val ScalatraVersion = "2.3.1"
|
||||
```
|
||||
|
||||
### src/main/scala/gitbucket/core/servlet/AutoUpdate.scala
|
||||
|
||||
```scala
|
||||
@@ -19,13 +30,6 @@ object AutoUpdate {
|
||||
new Version(3, 2),
|
||||
```
|
||||
|
||||
### env.sh
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
export GITBUCKET_VERSION=3.3.0 # <---- update here!!
|
||||
```
|
||||
|
||||
Generate release files
|
||||
--------
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -10,7 +10,7 @@ import sbtassembly.AssemblyKeys._
|
||||
object MyBuild extends Build {
|
||||
val Organization = "gitbucket"
|
||||
val Name = "gitbucket"
|
||||
val Version = System.getenv("GITBUCKET_VERSION")
|
||||
val Version = "3.6.0"
|
||||
val ScalaVersion = "2.11.6"
|
||||
val ScalatraVersion = "2.3.1"
|
||||
|
||||
@@ -50,7 +50,7 @@ object MyBuild extends Build {
|
||||
"org.json4s" %% "json4s-jackson" % "3.2.11",
|
||||
"jp.sf.amateras" %% "scalatra-forms" % "0.1.0",
|
||||
"commons-io" % "commons-io" % "2.4",
|
||||
"org.pegdown" % "pegdown" % "1.4.1", // 1.4.2 has incompatible APi changes
|
||||
"org.pegdown" % "pegdown" % "1.5.0",
|
||||
"org.apache.commons" % "commons-compress" % "1.9",
|
||||
"org.apache.commons" % "commons-email" % "1.3.3",
|
||||
"org.apache.httpcomponents" % "httpclient" % "4.3.6",
|
||||
@@ -64,9 +64,8 @@ object MyBuild extends Build {
|
||||
"junit" % "junit" % "4.12" % "test",
|
||||
"com.mchange" % "c3p0" % "0.9.5",
|
||||
"com.typesafe" % "config" % "1.2.1",
|
||||
"com.typesafe.play" %% "twirl-compiler" % "1.0.4",
|
||||
"com.typesafe.akka" %% "akka-actor" % "2.3.10",
|
||||
"com.enragedginger" %% "akka-quartz-scheduler" % "1.3.0-akka-2.3.x"
|
||||
"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,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
source ../env.sh
|
||||
. ./env.sh
|
||||
|
||||
cd ../
|
||||
./sbt.sh clean assembly
|
||||
|
||||
3
release/env.sh
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
export GITBUCKET_VERSION=`cat ../project/build.scala | grep 'val Version' | cut -d \" -f 2`
|
||||
echo "GITBUCKET_VERSION: $GITBUCKET_VERSION"
|
||||
@@ -1,3 +1,15 @@
|
||||
#!/bin/sh
|
||||
source ../env.sh
|
||||
ant -f build.xml all
|
||||
D="$(dirname "$0")"
|
||||
D="$(cd "${D}"; pwd)"
|
||||
DD="$(dirname "${D}")"
|
||||
(
|
||||
for f in "${D}/env.sh" "${D}/build.xml"; do
|
||||
if [ ! -s "${f}" ]; then
|
||||
echo >&2 "$0: Unable to access file '${f}'"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
. "${D}/env.sh"
|
||||
cd "${DD}"
|
||||
ant -f "${D}/build.xml" all
|
||||
)
|
||||
|
||||
17
release/pom.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>jp.sf.amateras</groupId>
|
||||
<artifactId>gitbucket-assembly</artifactId>
|
||||
<version>0.0.1</version>
|
||||
<build>
|
||||
<extensions>
|
||||
<extension>
|
||||
<groupId>org.apache.maven.wagon</groupId>
|
||||
<artifactId>wagon-ssh</artifactId>
|
||||
<version>1.0-beta-6</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
</build>
|
||||
</project>
|
||||
1
sbt.sh
@@ -1,3 +1,2 @@
|
||||
#!/bin/sh
|
||||
source ./env.sh
|
||||
java -Dsbt.log.noformat=true -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256m -Xmx512M -Xss2M -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 -jar `dirname $0`/sbt-launch-0.13.8.jar "$@"
|
||||
|
||||
@@ -32,6 +32,7 @@ class ScalatraBootstrap extends LifeCycle {
|
||||
context.mount(new DashboardController, "/*")
|
||||
context.mount(new UserManagementController, "/*")
|
||||
context.mount(new SystemSettingsController, "/*")
|
||||
context.mount(new PluginsController, "/*")
|
||||
context.mount(new AccountController, "/*")
|
||||
context.mount(new RepositoryViewerController, "/*")
|
||||
context.mount(new WikiController, "/*")
|
||||
|
||||
@@ -15,7 +15,7 @@ case class ApiRepository(
|
||||
default_branch: String,
|
||||
owner: ApiUser) {
|
||||
val forks_count = forks
|
||||
val watchers_coun = watchers
|
||||
val watchers_count = watchers
|
||||
val url = ApiPath(s"/api/v3/repos/${full_name}")
|
||||
val http_url = ApiPath(s"/git/${full_name}.git")
|
||||
val clone_url = ApiPath(s"/git/${full_name}.git")
|
||||
|
||||
@@ -91,7 +91,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
val newRepositoryForm = mapping(
|
||||
"owner" -> trim(label("Owner" , text(required, maxlength(40), identifier, existsAccount))),
|
||||
"name" -> trim(label("Repository name", text(required, maxlength(40), identifier, uniqueRepository))),
|
||||
"name" -> trim(label("Repository name", text(required, maxlength(40), repository, uniqueRepository))),
|
||||
"description" -> trim(label("Description" , optional(text()))),
|
||||
"isPrivate" -> trim(label("Repository Type", boolean())),
|
||||
"createReadme" -> trim(label("Create README" , boolean()))
|
||||
@@ -467,6 +467,14 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
parentUserName = Some(repository.owner)
|
||||
)
|
||||
|
||||
// Add collaborators for group repository
|
||||
val ownerAccount = getAccountByUserName(accountName).get
|
||||
if(ownerAccount.isGroupAccount){
|
||||
getGroupMembers(accountName).foreach { member =>
|
||||
addCollaborator(accountName, repository.name, member.userName)
|
||||
}
|
||||
}
|
||||
|
||||
// Insert default labels
|
||||
insertDefaultLabels(accountName, repository.name)
|
||||
|
||||
|
||||
@@ -257,6 +257,12 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
} getOrElse NotFound
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/new/label")(collaboratorsOnly { repository =>
|
||||
val labelNames = params("labelNames").split(",")
|
||||
val labels = getLabels(repository.owner, repository.name).filter(x => labelNames.contains(x.labelName))
|
||||
html.labellist(labels)
|
||||
})
|
||||
|
||||
ajaxPost("/:owner/:repository/issues/:id/label/new")(collaboratorsOnly { repository =>
|
||||
defining(params("id").toInt){ issueId =>
|
||||
registerIssueLabel(repository.owner, repository.name, issueId, params("labelId").toInt)
|
||||
@@ -459,7 +465,11 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
"issues",
|
||||
searchIssue(condition, false, (page - 1) * IssueLimit, IssueLimit, owner -> repoName),
|
||||
page,
|
||||
(getCollaborators(owner, repoName) :+ owner).sorted,
|
||||
if(!getAccountByUserName(owner).exists(_.isGroupAccount)){
|
||||
(getCollaborators(owner, repoName) :+ owner).sorted
|
||||
} else {
|
||||
getCollaborators(owner, repoName)
|
||||
},
|
||||
getMilestones(owner, repoName),
|
||||
getLabels(owner, repoName),
|
||||
countIssue(condition.copy(state = "open" ), false, owner -> repoName),
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import gitbucket.core.admin.plugins.html
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.util.AdminAuthenticator
|
||||
|
||||
class PluginsController extends ControllerBase with AdminAuthenticator {
|
||||
get("/admin/plugins")(adminOnly {
|
||||
html.plugins(PluginRegistry().getPlugins())
|
||||
})
|
||||
}
|
||||
@@ -46,7 +46,10 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
"requestRepositoryName" -> trim(text(required, maxlength(100))),
|
||||
"requestBranch" -> trim(text(required, maxlength(100))),
|
||||
"commitIdFrom" -> trim(text(required, maxlength(40))),
|
||||
"commitIdTo" -> trim(text(required, maxlength(40)))
|
||||
"commitIdTo" -> trim(text(required, maxlength(40))),
|
||||
"assignedUserName" -> trim(optional(text())),
|
||||
"milestoneId" -> trim(optional(number())),
|
||||
"labelNames" -> trim(optional(text()))
|
||||
)(PullRequestForm.apply)
|
||||
|
||||
val mergeForm = mapping(
|
||||
@@ -62,7 +65,11 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
requestRepositoryName: String,
|
||||
requestBranch: String,
|
||||
commitIdFrom: String,
|
||||
commitIdTo: String)
|
||||
commitIdTo: String,
|
||||
assignedUserName: Option[String],
|
||||
milestoneId: Option[Int],
|
||||
labelNames: Option[String]
|
||||
)
|
||||
|
||||
case class MergeForm(message: String)
|
||||
|
||||
@@ -176,7 +183,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
pullreq,
|
||||
statuses,
|
||||
repository,
|
||||
s"${context.baseUrl}/git/${pullreq.requestUserName}/${pullreq.requestRepositoryName}.git")
|
||||
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName, context.baseUrl).get)
|
||||
}
|
||||
} getOrElse NotFound
|
||||
})
|
||||
@@ -307,32 +314,44 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
originRepository.owner, originRepository.name, originId,
|
||||
forkedRepository.owner, forkedRepository.name, forkedId)
|
||||
|
||||
(oldGit.getRepository.resolve(rootId), newGit.getRepository.resolve(forkedId))
|
||||
(Option(oldGit.getRepository.resolve(rootId)), Option(newGit.getRepository.resolve(forkedId)))
|
||||
} else {
|
||||
// Commit id
|
||||
(oldGit.getRepository.resolve(originId), newGit.getRepository.resolve(forkedId))
|
||||
(Option(oldGit.getRepository.resolve(originId)), Option(newGit.getRepository.resolve(forkedId)))
|
||||
}
|
||||
|
||||
val (commits, diffs) = getRequestCompareInfo(
|
||||
originRepository.owner, originRepository.name, oldId.getName,
|
||||
forkedRepository.owner, forkedRepository.name, newId.getName)
|
||||
(oldId, newId) match {
|
||||
case (Some(oldId), Some(newId)) => {
|
||||
val (commits, diffs) = getRequestCompareInfo(
|
||||
originRepository.owner, originRepository.name, oldId.getName,
|
||||
forkedRepository.owner, forkedRepository.name, newId.getName)
|
||||
|
||||
html.compare(
|
||||
commits,
|
||||
diffs,
|
||||
(forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match {
|
||||
case (Some(userName), Some(repositoryName)) => (userName, repositoryName) :: getForkedRepositories(userName, repositoryName)
|
||||
case _ => (forkedRepository.owner, forkedRepository.name) :: getForkedRepositories(forkedRepository.owner, forkedRepository.name)
|
||||
},
|
||||
commits.flatten.map(commit => getCommitComments(forkedRepository.owner, forkedRepository.name, commit.id, false)).flatten.toList,
|
||||
originId,
|
||||
forkedId,
|
||||
oldId.getName,
|
||||
newId.getName,
|
||||
forkedRepository,
|
||||
originRepository,
|
||||
forkedRepository,
|
||||
hasWritePermission(forkedRepository.owner, forkedRepository.name, context.loginAccount))
|
||||
html.compare(
|
||||
commits,
|
||||
diffs,
|
||||
(forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match {
|
||||
case (Some(userName), Some(repositoryName)) => (userName, repositoryName) :: getForkedRepositories(userName, repositoryName)
|
||||
case _ => (forkedRepository.owner, forkedRepository.name) :: getForkedRepositories(forkedRepository.owner, forkedRepository.name)
|
||||
},
|
||||
commits.flatten.map(commit => getCommitComments(forkedRepository.owner, forkedRepository.name, commit.id, false)).flatten.toList,
|
||||
originId,
|
||||
forkedId,
|
||||
oldId.getName,
|
||||
newId.getName,
|
||||
forkedRepository,
|
||||
originRepository,
|
||||
forkedRepository,
|
||||
hasWritePermission(originRepository.owner, originRepository.name, context.loginAccount),
|
||||
(getCollaborators(originRepository.owner, originRepository.name) ::: (if(getAccountByUserName(originRepository.owner).get.isGroupAccount) Nil else List(originRepository.owner))).sorted,
|
||||
getMilestones(originRepository.owner, originRepository.name),
|
||||
getLabels(originRepository.owner, originRepository.name)
|
||||
)
|
||||
}
|
||||
case (oldId, newId) =>
|
||||
redirect(s"/${forkedRepository.owner}/${forkedRepository.name}/compare/" +
|
||||
s"${originOwner}:${oldId.map(_ => originId).getOrElse(originRepository.repository.defaultBranch)}..." +
|
||||
s"${forkedOwner}:${newId.map(_ => forkedId).getOrElse(forkedRepository.repository.defaultBranch)}")
|
||||
}
|
||||
}
|
||||
}) getOrElse NotFound
|
||||
})
|
||||
@@ -368,46 +387,61 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
post("/:owner/:repository/pulls/new", pullRequestForm)(referrersOnly { (form, repository) =>
|
||||
val loginUserName = context.loginAccount.get.userName
|
||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||
val writable = hasWritePermission(owner, name, context.loginAccount)
|
||||
val loginUserName = context.loginAccount.get.userName
|
||||
|
||||
val issueId = createIssue(
|
||||
owner = repository.owner,
|
||||
repository = repository.name,
|
||||
loginUser = loginUserName,
|
||||
title = form.title,
|
||||
content = form.content,
|
||||
assignedUserName = None,
|
||||
milestoneId = None,
|
||||
isPullRequest = true)
|
||||
val issueId = createIssue(
|
||||
owner = repository.owner,
|
||||
repository = repository.name,
|
||||
loginUser = loginUserName,
|
||||
title = form.title,
|
||||
content = form.content,
|
||||
assignedUserName = if(writable) form.assignedUserName else None,
|
||||
milestoneId = if(writable) form.milestoneId else None,
|
||||
isPullRequest = true)
|
||||
|
||||
createPullRequest(
|
||||
originUserName = repository.owner,
|
||||
originRepositoryName = repository.name,
|
||||
issueId = issueId,
|
||||
originBranch = form.targetBranch,
|
||||
requestUserName = form.requestUserName,
|
||||
requestRepositoryName = form.requestRepositoryName,
|
||||
requestBranch = form.requestBranch,
|
||||
commitIdFrom = form.commitIdFrom,
|
||||
commitIdTo = form.commitIdTo)
|
||||
createPullRequest(
|
||||
originUserName = repository.owner,
|
||||
originRepositoryName = repository.name,
|
||||
issueId = issueId,
|
||||
originBranch = form.targetBranch,
|
||||
requestUserName = form.requestUserName,
|
||||
requestRepositoryName = form.requestRepositoryName,
|
||||
requestBranch = form.requestBranch,
|
||||
commitIdFrom = form.commitIdFrom,
|
||||
commitIdTo = form.commitIdTo)
|
||||
|
||||
// fetch requested branch
|
||||
fetchAsPullRequest(repository.owner, repository.name, form.requestUserName, form.requestRepositoryName, form.requestBranch, issueId)
|
||||
|
||||
// record activity
|
||||
recordPullRequestActivity(repository.owner, repository.name, loginUserName, issueId, form.title)
|
||||
|
||||
// call web hook
|
||||
callPullRequestWebHook("opened", repository, issueId, context.baseUrl, context.loginAccount.get)
|
||||
|
||||
// notifications
|
||||
getIssue(repository.owner, repository.name, issueId.toString) foreach { issue =>
|
||||
Notifier().toNotify(repository, issue, form.content.getOrElse("")){
|
||||
Notifier.msgPullRequest(s"${context.baseUrl}/${repository.owner}/${repository.name}/pull/${issueId}")
|
||||
// insert labels
|
||||
if(writable){
|
||||
form.labelNames.map { value =>
|
||||
val labels = getLabels(owner, name)
|
||||
value.split(",").foreach { labelName =>
|
||||
labels.find(_.labelName == labelName).map { label =>
|
||||
registerIssueLabel(repository.owner, repository.name, issueId, label.labelId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
|
||||
// fetch requested branch
|
||||
fetchAsPullRequest(owner, name, form.requestUserName, form.requestRepositoryName, form.requestBranch, issueId)
|
||||
|
||||
// record activity
|
||||
recordPullRequestActivity(owner, name, loginUserName, issueId, form.title)
|
||||
|
||||
// call web hook
|
||||
callPullRequestWebHook("opened", repository, issueId, context.baseUrl, context.loginAccount.get)
|
||||
|
||||
// notifications
|
||||
getIssue(owner, name, issueId.toString) foreach { issue =>
|
||||
Notifier().toNotify(repository, issue, form.content.getOrElse("")){
|
||||
Notifier.msgPullRequest(s"${context.baseUrl}/${owner}/${name}/pull/${issueId}")
|
||||
}
|
||||
}
|
||||
|
||||
redirect(s"/${owner}/${name}/pull/${issueId}")
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -459,7 +493,11 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
"pulls",
|
||||
searchIssue(condition, true, (page - 1) * PullRequestLimit, PullRequestLimit, owner -> repoName),
|
||||
page,
|
||||
(getCollaborators(owner, repoName) :+ owner).sorted,
|
||||
if(!getAccountByUserName(owner).exists(_.isGroupAccount)){
|
||||
(getCollaborators(owner, repoName) :+ owner).sorted
|
||||
} else {
|
||||
getCollaborators(owner, repoName)
|
||||
},
|
||||
getMilestones(owner, repoName),
|
||||
getLabels(owner, repoName),
|
||||
countIssue(condition.copy(state = "open" ), true, owner -> repoName),
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.apache.commons.io.FileUtils
|
||||
import org.eclipse.jgit.api.{ArchiveCommand, Git}
|
||||
import org.eclipse.jgit.archive.{TgzFormat, ZipFormat}
|
||||
import org.eclipse.jgit.dircache.DirCache
|
||||
import org.eclipse.jgit.errors.MissingObjectException
|
||||
import org.eclipse.jgit.lib._
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
import org.eclipse.jgit.treewalk._
|
||||
@@ -249,7 +250,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
)
|
||||
|
||||
redirect(s"/${repository.owner}/${repository.name}/blob/${form.branch}/${
|
||||
if(form.path.length == 0) form.newFileName else s"${form.path}/${form.newFileName}"
|
||||
if(form.path.length == 0) urlEncode(form.newFileName) else s"${form.path}/${urlEncode(form.newFileName)}"
|
||||
}")
|
||||
})
|
||||
|
||||
@@ -270,7 +271,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
)
|
||||
|
||||
redirect(s"/${repository.owner}/${repository.name}/blob/${form.branch}/${
|
||||
if(form.path.length == 0) form.newFileName else s"${form.path}/${form.newFileName}"
|
||||
if(form.path.length == 0) urlEncode(form.newFileName) else s"${form.path}/${urlEncode(form.newFileName)}"
|
||||
}")
|
||||
})
|
||||
|
||||
@@ -344,16 +345,21 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
get("/:owner/:repository/commit/:id")(referrersOnly { repository =>
|
||||
val id = params("id")
|
||||
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
defining(JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))){ revCommit =>
|
||||
JGitUtil.getDiffs(git, id) match { case (diffs, oldCommitId) =>
|
||||
html.commit(id, new JGitUtil.CommitInfo(revCommit),
|
||||
JGitUtil.getBranchesOfCommit(git, revCommit.getName),
|
||||
JGitUtil.getTagsOfCommit(git, revCommit.getName),
|
||||
getCommitComments(repository.owner, repository.name, id, false),
|
||||
repository, diffs, oldCommitId, hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||
try {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
|
||||
defining(JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))) { revCommit =>
|
||||
JGitUtil.getDiffs(git, id) match {
|
||||
case (diffs, oldCommitId) =>
|
||||
html.commit(id, new JGitUtil.CommitInfo(revCommit),
|
||||
JGitUtil.getBranchesOfCommit(git, revCommit.getName),
|
||||
JGitUtil.getTagsOfCommit(git, revCommit.getName),
|
||||
getCommitComments(repository.owner, repository.name, id, false),
|
||||
repository, diffs, oldCommitId, hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
case e:MissingObjectException => NotFound
|
||||
}
|
||||
})
|
||||
|
||||
@@ -436,10 +442,16 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
* Displays branches.
|
||||
*/
|
||||
get("/:owner/:repository/branches")(referrersOnly { repository =>
|
||||
val branches = JGitUtil.getBranches(repository.owner, repository.name, repository.repository.defaultBranch)
|
||||
.sortBy(br => (br.mergeInfo.isEmpty, br.commitTime))
|
||||
.map(br => br -> getPullRequestByRequestCommit(repository.owner, repository.name, repository.repository.defaultBranch, br.name, br.commitId))
|
||||
.reverse
|
||||
val branches = JGitUtil.getBranches(
|
||||
owner = repository.owner,
|
||||
name = repository.name,
|
||||
defaultBranch = repository.repository.defaultBranch,
|
||||
origin = repository.repository.originUserName.isEmpty
|
||||
)
|
||||
.sortBy(br => (br.mergeInfo.isEmpty, br.commitTime))
|
||||
.map(br => br -> getPullRequestByRequestCommit(repository.owner, repository.name, repository.repository.defaultBranch, br.name, br.commitId))
|
||||
.reverse
|
||||
|
||||
html.branches(branches, hasWritePermission(repository.owner, repository.name, context.loginAccount), repository)
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package gitbucket.core.plugin
|
||||
|
||||
import gitbucket.core.model.Session
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
|
||||
/**
|
||||
* Define the Git repository routing.
|
||||
*
|
||||
* @param urlPattern the regular expression which matches the repository path (e.g. "gist/(.+?)/(.+?)\\.git")
|
||||
* @param localPath the string to assemble local file path of repository (e.g. "gist/$1/$2")
|
||||
* @param filter the filter for request to the Git repository which is defined by this routing
|
||||
*/
|
||||
case class GitRepositoryRouting(urlPattern: String, localPath: String, filter: GitRepositoryFilter){
|
||||
|
||||
def this(urlPattern: String, localPath: String) = {
|
||||
this(urlPattern, localPath, new GitRepositoryFilter(){
|
||||
def filter(repositoryName: String, userName: Option[String], settings: SystemSettings, isUpdating: Boolean)
|
||||
(implicit session: Session): Boolean = true
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters request to plug-in served repository. This is used to provide authentication mainly.
|
||||
*/
|
||||
trait GitRepositoryFilter {
|
||||
|
||||
/**
|
||||
* Filters request to Git repository. If this method returns true then request is accepted.
|
||||
*
|
||||
* @param path the repository path which starts with '/'
|
||||
* @param userName the authenticated user name or None
|
||||
* @param settings the system settings
|
||||
* @param isUpdating true if update request, otherwise false
|
||||
* @param session the database session
|
||||
* @return true if allow accessing to repository, otherwise false.
|
||||
*/
|
||||
def filter(path: String, userName: Option[String], settings: SystemSettings, isUpdating: Boolean)
|
||||
(implicit session: Session): Boolean
|
||||
|
||||
}
|
||||
@@ -22,38 +22,71 @@ trait Plugin {
|
||||
*/
|
||||
val images: Seq[(String, Array[Byte])] = Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides images.
|
||||
*/
|
||||
def images(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, Array[Byte])] = Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides controllers.
|
||||
*/
|
||||
val controllers: Seq[(String, ControllerBase)] = Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides controllers.
|
||||
*/
|
||||
def controllers(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, ControllerBase)] = Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides JavaScript.
|
||||
*/
|
||||
val javaScripts: Seq[(String, String)] = Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides JavaScript.
|
||||
*/
|
||||
def javaScripts(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, String)] = Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides renderers.
|
||||
*/
|
||||
val renderers: Seq[(String, Renderer)] = Nil
|
||||
|
||||
/**
|
||||
* Override to declare this plug-in provides renderers.
|
||||
*/
|
||||
def renderers(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(String, Renderer)] = Nil
|
||||
|
||||
/**
|
||||
* Override to add git repository routings.
|
||||
*/
|
||||
val repositoryRoutings: Seq[GitRepositoryRouting] = Nil
|
||||
|
||||
/**
|
||||
* Override to add git repository routings.
|
||||
*/
|
||||
def repositoryRoutings(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[GitRepositoryRouting] = Nil
|
||||
|
||||
/**
|
||||
* This method is invoked in initialization of plugin system.
|
||||
* Register plugin functionality to PluginRegistry.
|
||||
*/
|
||||
def initialize(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Unit = {
|
||||
images.foreach { case (id, in) =>
|
||||
(images ++ images(registry, context, settings)).foreach { case (id, in) =>
|
||||
registry.addImage(id, in)
|
||||
}
|
||||
controllers.foreach { case (path, controller) =>
|
||||
(controllers ++ controllers(registry, context, settings)).foreach { case (path, controller) =>
|
||||
registry.addController(path, controller)
|
||||
}
|
||||
javaScripts.foreach { case (path, script) =>
|
||||
(javaScripts ++ javaScripts(registry, context, settings)).foreach { case (path, script) =>
|
||||
registry.addJavaScript(path, script)
|
||||
}
|
||||
renderers.foreach { case (extension, renderer) =>
|
||||
(renderers ++ renderers(registry, context, settings)).foreach { case (extension, renderer) =>
|
||||
registry.addRenderer(extension, renderer)
|
||||
}
|
||||
(repositoryRoutings ++ repositoryRoutings(registry, context, settings)).foreach { routing =>
|
||||
registry.addRepositoryRouting(routing)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@ class PluginRegistry {
|
||||
renderers ++= Seq(
|
||||
"md" -> MarkdownRenderer, "markdown" -> MarkdownRenderer
|
||||
)
|
||||
private val repositoryRoutings = new ListBuffer[GitRepositoryRouting]
|
||||
|
||||
def addPlugin(pluginInfo: PluginInfo): Unit = {
|
||||
plugins += pluginInfo
|
||||
@@ -61,7 +62,7 @@ class PluginRegistry {
|
||||
addController(path, controller)
|
||||
}
|
||||
|
||||
def getControllers(): List[(ControllerBase, String)] = controllers.toList
|
||||
def getControllers(): Seq[(ControllerBase, String)] = controllers.toSeq
|
||||
|
||||
def addJavaScript(path: String, script: String): Unit = {
|
||||
javaScripts += ((path, script))
|
||||
@@ -81,6 +82,22 @@ class PluginRegistry {
|
||||
|
||||
def renderableExtensions: Seq[String] = renderers.keys.toSeq
|
||||
|
||||
def addRepositoryRouting(routing: GitRepositoryRouting): Unit = {
|
||||
repositoryRoutings += routing
|
||||
}
|
||||
|
||||
def getRepositoryRoutings(): Seq[GitRepositoryRouting] = {
|
||||
repositoryRoutings.toSeq
|
||||
}
|
||||
|
||||
def getRepositoryRouting(repositoryPath: String): Option[GitRepositoryRouting] = {
|
||||
PluginRegistry().getRepositoryRoutings().find {
|
||||
case GitRepositoryRouting(urlPath, _, _) => {
|
||||
repositoryPath.matches("/" + urlPath + "(/.*)?")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private case class GlobalAction(
|
||||
method: String,
|
||||
path: String,
|
||||
|
||||
@@ -20,7 +20,7 @@ trait Renderer {
|
||||
object MarkdownRenderer extends Renderer {
|
||||
override def render(request: RenderRequest): Html = {
|
||||
import request._
|
||||
Html(Markdown.toHtml(fileContent, repository, enableWikiLink, enableRefsLink)(context))
|
||||
Html(Markdown.toHtml(fileContent, repository, enableWikiLink, enableRefsLink, enableAnchor)(context))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,4 +41,5 @@ case class RenderRequest(filePath: List[String],
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
enableWikiLink: Boolean,
|
||||
enableRefsLink: Boolean,
|
||||
enableAnchor: Boolean,
|
||||
context: Context)
|
||||
@@ -33,7 +33,7 @@ class AccessTokenAuthenticationFilter extends Filter with AccessTokenService {
|
||||
case None => chain.doFilter(req, res)
|
||||
case Some(Left(_)) => {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED)
|
||||
response.setContentType("Content-Type: application/json; charset=utf-8")
|
||||
response.setContentType("application/json; charset=utf-8")
|
||||
val w = response.getWriter()
|
||||
w.print("""{ "message": "Bad credentials" }""")
|
||||
w.close()
|
||||
|
||||
@@ -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, 6),
|
||||
new Version(3, 5),
|
||||
new Version(3, 4),
|
||||
new Version(3, 3),
|
||||
new Version(3, 2),
|
||||
@@ -166,4 +168,4 @@ object AutoUpdate {
|
||||
} else Version(0, 0)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ package gitbucket.core.servlet
|
||||
|
||||
import javax.servlet._
|
||||
import javax.servlet.http._
|
||||
import gitbucket.core.plugin.{GitRepositoryFilter, GitRepositoryRouting, PluginRegistry}
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.service.{RepositoryService, AccountService, SystemSettingsService}
|
||||
import gitbucket.core.util.{ControlUtil, Keys, Implicits}
|
||||
import gitbucket.core.util.{Keys, Implicits}
|
||||
import org.slf4j.LoggerFactory
|
||||
import Implicits._
|
||||
import ControlUtil._
|
||||
|
||||
/**
|
||||
* Provides BASIC Authentication for [[GitRepositoryServlet]].
|
||||
@@ -20,7 +21,7 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
|
||||
def destroy(): Unit = {}
|
||||
|
||||
def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {
|
||||
implicit val request = req.asInstanceOf[HttpServletRequest]
|
||||
val request = req.asInstanceOf[HttpServletRequest]
|
||||
val response = res.asInstanceOf[HttpServletResponse]
|
||||
|
||||
val wrappedResponse = new HttpServletResponseWrapper(response){
|
||||
@@ -31,47 +32,13 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
|
||||
val settings = loadSystemSettings()
|
||||
|
||||
try {
|
||||
defining(request.paths){
|
||||
case Array(_, repositoryOwner, repositoryName, _*) =>
|
||||
getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match {
|
||||
case Some(repository) => {
|
||||
if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){
|
||||
chain.doFilter(req, wrappedResponse)
|
||||
} else {
|
||||
request.getHeader("Authorization") match {
|
||||
case null => requireAuth(response)
|
||||
case auth => decodeAuthHeader(auth).split(":", 2) match {
|
||||
case Array(username, password) => {
|
||||
authenticate(settings, username, password) match {
|
||||
case Some(account) => {
|
||||
if (isUpdating || repository.repository.isPrivate) {
|
||||
if(hasWritePermission(repository.owner, repository.name, Some(account))){
|
||||
request.setAttribute(Keys.Request.UserName, account.userName)
|
||||
chain.doFilter(req, wrappedResponse)
|
||||
} else {
|
||||
requireAuth(response)
|
||||
}
|
||||
} else {
|
||||
chain.doFilter(req, wrappedResponse)
|
||||
}
|
||||
}
|
||||
case _ => requireAuth(response)
|
||||
}
|
||||
}
|
||||
case _ => requireAuth(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.")
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND)
|
||||
}
|
||||
}
|
||||
case _ => {
|
||||
logger.debug(s"Not enough path arguments: ${request.paths}")
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND)
|
||||
}
|
||||
PluginRegistry().getRepositoryRouting(request.gitRepositoryPath).map { case GitRepositoryRouting(_, _, filter) =>
|
||||
// served by plug-ins
|
||||
pluginRepository(request, wrappedResponse, chain, settings, isUpdating, filter)
|
||||
|
||||
}.getOrElse {
|
||||
// default repositories
|
||||
defaultRepository(request, wrappedResponse, chain, settings, isUpdating)
|
||||
}
|
||||
} catch {
|
||||
case ex: Exception => {
|
||||
@@ -81,6 +48,67 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
|
||||
}
|
||||
}
|
||||
|
||||
private def pluginRepository(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain,
|
||||
settings: SystemSettings, isUpdating: Boolean, filter: GitRepositoryFilter): Unit = {
|
||||
implicit val r = request
|
||||
|
||||
val account = for {
|
||||
auth <- Option(request.getHeader("Authorization"))
|
||||
Array(username, password) = decodeAuthHeader(auth).split(":", 2)
|
||||
account <- authenticate(settings, username, password)
|
||||
} yield {
|
||||
request.setAttribute(Keys.Request.UserName, account.userName)
|
||||
account
|
||||
}
|
||||
|
||||
if(filter.filter(request.gitRepositoryPath, account.map(_.userName), settings, isUpdating)){
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
requireAuth(response)
|
||||
}
|
||||
}
|
||||
|
||||
private def defaultRepository(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain,
|
||||
settings: SystemSettings, isUpdating: Boolean): Unit = {
|
||||
implicit val r = request
|
||||
|
||||
request.paths match {
|
||||
case Array(_, repositoryOwner, repositoryName, _*) =>
|
||||
getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", ""), "") match {
|
||||
case Some(repository) => {
|
||||
if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
val passed = for {
|
||||
auth <- Option(request.getHeader("Authorization"))
|
||||
Array(username, password) = decodeAuthHeader(auth).split(":", 2)
|
||||
account <- authenticate(settings, username, password)
|
||||
} yield if(isUpdating || repository.repository.isPrivate){
|
||||
if(hasWritePermission(repository.owner, repository.name, Some(account))){
|
||||
request.setAttribute(Keys.Request.UserName, account.userName)
|
||||
true
|
||||
} else false
|
||||
} else true
|
||||
|
||||
if(passed.getOrElse(false)){
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
requireAuth(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
case None => {
|
||||
logger.debug(s"Repository ${repositoryOwner}/${repositoryName} is not found.")
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND)
|
||||
}
|
||||
}
|
||||
case _ => {
|
||||
logger.debug(s"Not enough path arguments: ${request.paths}")
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def requireAuth(response: HttpServletResponse): Unit = {
|
||||
response.setHeader("WWW-Authenticate", "BASIC realm=\"GitBucket\"")
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package gitbucket.core.servlet
|
||||
|
||||
import java.io.File
|
||||
|
||||
import gitbucket.core.api
|
||||
import gitbucket.core.model.Session
|
||||
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
|
||||
import gitbucket.core.service.IssuesService.IssueSearchCondition
|
||||
import gitbucket.core.service.WebHookService._
|
||||
import gitbucket.core.service._
|
||||
@@ -18,7 +21,6 @@ import org.eclipse.jgit.transport.resolver._
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import javax.servlet.ServletConfig
|
||||
import javax.servlet.ServletContext
|
||||
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
|
||||
|
||||
|
||||
@@ -35,20 +37,8 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||
override def init(config: ServletConfig): Unit = {
|
||||
setReceivePackFactory(new GitBucketReceivePackFactory())
|
||||
|
||||
// TODO are there any other ways...?
|
||||
super.init(new ServletConfig(){
|
||||
def getInitParameter(name: String): String = name match {
|
||||
case "base-path" => Directory.RepositoryHome
|
||||
case "export-all" => "true"
|
||||
case name => config.getInitParameter(name)
|
||||
}
|
||||
def getInitParameterNames(): java.util.Enumeration[String] = {
|
||||
config.getInitParameterNames
|
||||
}
|
||||
|
||||
def getServletContext(): ServletContext = config.getServletContext
|
||||
def getServletName(): String = config.getServletName
|
||||
})
|
||||
val root: File = new File(Directory.RepositoryHome)
|
||||
setRepositoryResolver(new GitBucketRepositoryResolver(new FileResolver[HttpServletRequest](root, true)))
|
||||
|
||||
super.init(config)
|
||||
}
|
||||
@@ -67,32 +57,52 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||
}
|
||||
}
|
||||
|
||||
class GitBucketRepositoryResolver(parent: FileResolver[HttpServletRequest]) extends RepositoryResolver[HttpServletRequest] {
|
||||
|
||||
private val resolver = new FileResolver[HttpServletRequest](new File(Directory.GitBucketHome), true)
|
||||
|
||||
override def open(req: HttpServletRequest, name: String): Repository = {
|
||||
// Rewrite repository path if routing is marched
|
||||
PluginRegistry().getRepositoryRouting("/" + name).map { case GitRepositoryRouting(urlPattern, localPath, _) =>
|
||||
val path = urlPattern.r.replaceFirstIn(name, localPath)
|
||||
resolver.open(req, path)
|
||||
}.getOrElse {
|
||||
parent.open(req, name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest] with SystemSettingsService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[GitBucketReceivePackFactory])
|
||||
|
||||
override def create(request: HttpServletRequest, db: Repository): ReceivePack = {
|
||||
val receivePack = new ReceivePack(db)
|
||||
val pusher = request.getAttribute(Keys.Request.UserName).asInstanceOf[String]
|
||||
|
||||
logger.debug("requestURI: " + request.getRequestURI)
|
||||
logger.debug("pusher:" + pusher)
|
||||
if(PluginRegistry().getRepositoryRouting(request.gitRepositoryPath).isEmpty){
|
||||
val pusher = request.getAttribute(Keys.Request.UserName).asInstanceOf[String]
|
||||
|
||||
defining(request.paths){ paths =>
|
||||
val owner = paths(1)
|
||||
val repository = paths(2).stripSuffix(".git")
|
||||
logger.debug("requestURI: " + request.getRequestURI)
|
||||
logger.debug("pusher:" + pusher)
|
||||
|
||||
logger.debug("repository:" + owner + "/" + repository)
|
||||
defining(request.paths){ paths =>
|
||||
val owner = paths(1)
|
||||
val repository = paths(2).stripSuffix(".git")
|
||||
|
||||
if(!repository.endsWith(".wiki")){
|
||||
defining(request) { implicit r =>
|
||||
val hook = new CommitLogHook(owner, repository, pusher, baseUrl)
|
||||
receivePack.setPreReceiveHook(hook)
|
||||
receivePack.setPostReceiveHook(hook)
|
||||
logger.debug("repository:" + owner + "/" + repository)
|
||||
|
||||
if(!repository.endsWith(".wiki")){
|
||||
defining(request) { implicit r =>
|
||||
val hook = new CommitLogHook(owner, repository, pusher, baseUrl)
|
||||
receivePack.setPreReceiveHook(hook)
|
||||
receivePack.setPostReceiveHook(hook)
|
||||
}
|
||||
}
|
||||
}
|
||||
receivePack
|
||||
}
|
||||
|
||||
receivePack
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package gitbucket.core.ssh
|
||||
|
||||
import gitbucket.core.model.Session
|
||||
import gitbucket.core.plugin.{GitRepositoryRouting, PluginRegistry}
|
||||
import gitbucket.core.service.{RepositoryService, AccountService, SystemSettingsService}
|
||||
import gitbucket.core.servlet.{Database, CommitLogHook}
|
||||
import gitbucket.core.util.{Directory, ControlUtil}
|
||||
import org.apache.sshd.server.{CommandFactory, Environment, ExitCallback, Command}
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.{InputStream, OutputStream}
|
||||
import java.io.{File, InputStream, OutputStream}
|
||||
import ControlUtil._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import Directory._
|
||||
@@ -15,11 +16,11 @@ import org.apache.sshd.server.command.UnknownCommand
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException
|
||||
|
||||
object GitCommand {
|
||||
val CommandRegex = """\Agit-(upload|receive)-pack '/([a-zA-Z0-9\-_.]+)/([a-zA-Z0-9\-_.]+).git'\Z""".r
|
||||
val DefaultCommandRegex = """\Agit-(upload|receive)-pack '/([a-zA-Z0-9\-_.]+)/([a-zA-Z0-9\-_.]+).git'\Z""".r
|
||||
val SimpleCommandRegex = """\Agit-(upload|receive)-pack '/(.+\.git)'\Z""".r
|
||||
}
|
||||
|
||||
abstract class GitCommand(val owner: String, val repoName: String) extends Command {
|
||||
self: RepositoryService with AccountService =>
|
||||
abstract class GitCommand() extends Command {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[GitCommand])
|
||||
protected var err: OutputStream = null
|
||||
@@ -71,6 +72,11 @@ abstract class GitCommand(val owner: String, val repoName: String) extends Comma
|
||||
this.in = in
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class DefaultGitCommand(val owner: String, val repoName: String) extends GitCommand {
|
||||
self: RepositoryService with AccountService =>
|
||||
|
||||
protected def isWritableUser(username: String, repositoryInfo: RepositoryService.RepositoryInfo)
|
||||
(implicit session: Session): Boolean =
|
||||
getAccountByUserName(username) match {
|
||||
@@ -80,7 +86,8 @@ abstract class GitCommand(val owner: String, val repoName: String) extends Comma
|
||||
|
||||
}
|
||||
|
||||
class GitUploadPack(owner: String, repoName: String, baseUrl: String) extends GitCommand(owner, repoName)
|
||||
|
||||
class DefaultGitUploadPack(owner: String, repoName: String, baseUrl: String) extends DefaultGitCommand(owner, repoName)
|
||||
with RepositoryService with AccountService {
|
||||
|
||||
override protected def runTask(user: String)(implicit session: Session): Unit = {
|
||||
@@ -94,11 +101,10 @@ class GitUploadPack(owner: String, repoName: String, baseUrl: String) extends Gi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class GitReceivePack(owner: String, repoName: String, baseUrl: String) extends GitCommand(owner, repoName)
|
||||
with SystemSettingsService with RepositoryService with AccountService {
|
||||
class DefaultGitReceivePack(owner: String, repoName: String, baseUrl: String) extends DefaultGitCommand(owner, repoName)
|
||||
with RepositoryService with AccountService {
|
||||
|
||||
override protected def runTask(user: String)(implicit session: Session): Unit = {
|
||||
getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", ""), baseUrl).foreach { repositoryInfo =>
|
||||
@@ -116,18 +122,56 @@ class GitReceivePack(owner: String, repoName: String, baseUrl: String) extends G
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PluginGitUploadPack(repoName: String, baseUrl: String, routing: GitRepositoryRouting) extends GitCommand
|
||||
with SystemSettingsService {
|
||||
|
||||
override protected def runTask(user: String)(implicit session: Session): Unit = {
|
||||
if(routing.filter.filter("/" + repoName, Some(user), loadSystemSettings(), false)){
|
||||
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
||||
using(Git.open(new File(Directory.GitBucketHome, path))){ git =>
|
||||
val repository = git.getRepository
|
||||
val upload = new UploadPack(repository)
|
||||
upload.upload(in, out, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PluginGitReceivePack(repoName: String, baseUrl: String, routing: GitRepositoryRouting) extends GitCommand
|
||||
with SystemSettingsService {
|
||||
|
||||
override protected def runTask(user: String)(implicit session: Session): Unit = {
|
||||
if(routing.filter.filter("/" + repoName, Some(user), loadSystemSettings(), true)){
|
||||
val path = routing.urlPattern.r.replaceFirstIn(repoName, routing.localPath)
|
||||
using(Git.open(new File(Directory.GitBucketHome, path))){ git =>
|
||||
val repository = git.getRepository
|
||||
val receive = new ReceivePack(repository)
|
||||
receive.receive(in, out, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class GitCommandFactory(baseUrl: String) extends CommandFactory {
|
||||
private val logger = LoggerFactory.getLogger(classOf[GitCommandFactory])
|
||||
|
||||
override def createCommand(command: String): Command = {
|
||||
import GitCommand._
|
||||
logger.debug(s"command: $command")
|
||||
|
||||
command match {
|
||||
case GitCommand.CommandRegex("upload", owner, repoName) => new GitUploadPack(owner, repoName, baseUrl)
|
||||
case GitCommand.CommandRegex("receive", owner, repoName) => new GitReceivePack(owner, repoName, baseUrl)
|
||||
case SimpleCommandRegex ("upload" , repoName) if(pluginRepository(repoName)) => new PluginGitUploadPack (repoName, baseUrl, routing(repoName))
|
||||
case SimpleCommandRegex ("receive", repoName) if(pluginRepository(repoName)) => new PluginGitReceivePack(repoName, baseUrl, routing(repoName))
|
||||
case DefaultCommandRegex("upload" , owner, repoName) => new DefaultGitUploadPack (owner, repoName, baseUrl)
|
||||
case DefaultCommandRegex("receive", owner, repoName) => new DefaultGitReceivePack(owner, repoName, baseUrl)
|
||||
case _ => new UnknownCommand(command)
|
||||
}
|
||||
}
|
||||
|
||||
private def pluginRepository(repoName: String): Boolean = PluginRegistry().getRepositoryRouting("/" + repoName).isDefined
|
||||
private def routing(repoName: String): GitRepositoryRouting = PluginRegistry().getRepositoryRouting("/" + repoName).get
|
||||
|
||||
}
|
||||
|
||||
@@ -72,6 +72,8 @@ object Implicits {
|
||||
|
||||
def hasAttribute(name: String): Boolean = request.getAttribute(name) != null
|
||||
|
||||
def gitRepositoryPath: String = request.getRequestURI.replaceFirst("^/git/", "/")
|
||||
|
||||
}
|
||||
|
||||
implicit class RichSession(session: HttpSession){
|
||||
|
||||
@@ -791,7 +791,7 @@ object JGitUtil {
|
||||
return git.log.add(startCommit).addPath(path).setMaxCount(1).call.iterator.next
|
||||
}
|
||||
|
||||
def getBranches(owner: String, name: String, defaultBranch: String): Seq[BranchInfo] = {
|
||||
def getBranches(owner: String, name: String, defaultBranch: String, origin: Boolean): Seq[BranchInfo] = {
|
||||
using(Git.open(getRepositoryDir(owner, name))){ git =>
|
||||
val repo = git.getRepository
|
||||
val defaultObject = if (repo.getAllRefs.keySet().contains(defaultBranch)) {
|
||||
@@ -802,20 +802,20 @@ object JGitUtil {
|
||||
|
||||
git.branchList.call.asScala.map { ref =>
|
||||
val walk = new RevWalk(repo)
|
||||
try{
|
||||
try {
|
||||
val defaultCommit = walk.parseCommit(defaultObject)
|
||||
val branchName = ref.getName.stripPrefix("refs/heads/")
|
||||
val branchCommit = if(branchName == defaultBranch){
|
||||
defaultCommit
|
||||
}else{
|
||||
} else {
|
||||
walk.parseCommit(ref.getObjectId)
|
||||
}
|
||||
val when = branchCommit.getCommitterIdent.getWhen
|
||||
val committer = branchCommit.getCommitterIdent.getName
|
||||
val committerEmail = branchCommit.getCommitterIdent.getEmailAddress
|
||||
val mergeInfo = if(branchName==defaultBranch){
|
||||
val mergeInfo = if(origin && branchName == defaultBranch){
|
||||
None
|
||||
}else{
|
||||
} else {
|
||||
walk.reset()
|
||||
walk.setRevFilter( RevFilter.MERGE_BASE )
|
||||
walk.markStart(branchCommit)
|
||||
|
||||
@@ -75,7 +75,7 @@ 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))) { case (subject, msg) =>
|
||||
msg(Markdown.toHtml(content, r, false, true, false))) { case (subject, msg) =>
|
||||
recipients(issue) { to =>
|
||||
val email = new HtmlEmail
|
||||
email.setHostName(smtp.host)
|
||||
@@ -87,7 +87,7 @@ class Mailer(private val smtp: Smtp) extends Notifier {
|
||||
email.setSSLOnConnect(ssl)
|
||||
}
|
||||
smtp.fromAddress
|
||||
.map (_ -> smtp.fromName.orNull)
|
||||
.map (_ -> smtp.fromName.getOrElse(context.loginAccount.get.userName))
|
||||
.orElse (Some("notifications@gitbucket.com" -> context.loginAccount.get.userName))
|
||||
.foreach { case (address, name) =>
|
||||
email.setFrom(address, name)
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.scalatra.i18n.Messages
|
||||
trait Validations {
|
||||
|
||||
/**
|
||||
* Constraint for the identifier such as user name, repository name or page name.
|
||||
* Constraint for the identifier such as user name or page name.
|
||||
*/
|
||||
def identifier: Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
@@ -19,6 +19,23 @@ trait Validations {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constraint for the repository identifier.
|
||||
*/
|
||||
def repository: Constraint = new Constraint(){
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
if(!value.matches("[a-zA-Z0-9\\-\\+_.]+")){
|
||||
Some(s"${name} contains invalid character.")
|
||||
} else if(value.startsWith("_") || value.startsWith("-")){
|
||||
Some(s"${name} starts with invalid character.")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constraint for the color pattern.
|
||||
*/
|
||||
def color = pattern("#[0-9a-fA-F]{6}")
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,28 +10,64 @@ trait LinkConverter { self: RequestCache =>
|
||||
* Converts issue id, username and commit id to link.
|
||||
*/
|
||||
protected def convertRefsLinks(value: String, repository: RepositoryService.RepositoryInfo,
|
||||
issueIdPrefix: String = "#", escapeHtml: Boolean = true)(implicit context: Context): String = {
|
||||
issueIdPrefix: String = "#", escapeHtml: Boolean = true)(implicit context: Context): String = {
|
||||
|
||||
// escape HTML tags
|
||||
val escaped = if(escapeHtml) value.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """) else value
|
||||
|
||||
escaped
|
||||
// convert issue id to link
|
||||
.replaceBy(("(?<=(^|\\W))" + issueIdPrefix + "([0-9]+)(?=(\\W|$))").r){ m =>
|
||||
getIssue(repository.owner, repository.name, m.group(2)) match {
|
||||
case Some(issue) if(issue.isPullRequest)
|
||||
=> Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/pull/${m.group(2)}">#${m.group(2)}</a>""")
|
||||
case Some(_) => Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/issues/${m.group(2)}">#${m.group(2)}</a>""")
|
||||
case None => Some(s"""#${m.group(2)}""")
|
||||
}
|
||||
// convert username/project@SHA to link
|
||||
.replaceBy("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)/([a-zA-Z0-9\\-_\\.]+)@([a-f0-9]{40})(?=(\\W|$))".r){ m =>
|
||||
getAccountByUserName(m.group(2)).map { _ =>
|
||||
s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/commit/${m.group(4)}">${m.group(2)}/${m.group(3)}@${m.group(4).substring(0, 7)}</a>"""
|
||||
}
|
||||
}
|
||||
|
||||
// convert username/project#Num to link
|
||||
.replaceBy( ("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)/([a-zA-Z0-9\\-_\\.]+)" + issueIdPrefix + "([0-9]+)(?=(\\W|$))").r){ m =>
|
||||
getIssue(m.group(2), m.group(3), m.group(4)) match {
|
||||
case Some(issue) if (issue.isPullRequest)
|
||||
=> Some( s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/pull/${m.group(4)}">${m.group(2)}/${m.group(3)}#${m.group(4)}</a>""")
|
||||
case Some(_) => Some( s"""<a href="${context.path}/${m.group(2)}/${m.group(3)}/issues/${m.group(4)}">${m.group(2)}/${m.group(3)}#${m.group(4)}</a>""")
|
||||
case None => Some( s"""${m.group(2)}/${m.group(3)}#${m.group(4)}""")
|
||||
}
|
||||
}
|
||||
|
||||
// convert username@SHA to link
|
||||
.replaceBy( ("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)@([a-f0-9]{40})(?=(\\W|$))").r ) { m =>
|
||||
getAccountByUserName(m.group(2)).map { _ =>
|
||||
s"""<a href="${context.path}/${m.group(2)}/${repository.name}/commit/${m.group(3)}">${m.group(2)}@${m.group(3).substring(0, 7)}</a>"""
|
||||
}
|
||||
}
|
||||
|
||||
// convert username#Num to link
|
||||
.replaceBy( ("(?<=(^|\\W))([a-zA-Z0-9\\-_]+)" + issueIdPrefix + "([0-9]+)(?=(\\W|$))").r ) { m =>
|
||||
getIssue(m.group(2), repository.name, m.group(3)) match {
|
||||
case Some(issue) if(issue.isPullRequest)
|
||||
=> Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/pull/${m.group(3)}">${m.group(2)}#${m.group(3)}</a>""")
|
||||
case Some(_) => Some(s"""<a href="${context.path}/${m.group(2)}/${repository.name}/issues/${m.group(3)}">${m.group(2)}#${m.group(3)}</a>""")
|
||||
case None => Some(s"""${m.group(2)}#${m.group(3)}""")
|
||||
}
|
||||
}
|
||||
|
||||
// convert issue id to link
|
||||
.replaceBy(("(?<=(^|\\W))(GH-|" + issueIdPrefix + ")([0-9]+)(?=(\\W|$))").r){ m =>
|
||||
getIssue(repository.owner, repository.name, m.group(3)) match {
|
||||
case Some(issue) if(issue.isPullRequest)
|
||||
=> Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/pull/${m.group(3)}">${m.group(2)}${m.group(3)}</a>""")
|
||||
case Some(_) => Some(s"""<a href="${context.path}/${repository.owner}/${repository.name}/issues/${m.group(3)}">${m.group(2)}${m.group(3)}</a>""")
|
||||
case None => Some(s"""${m.group(2)}${m.group(3)}""")
|
||||
}
|
||||
}
|
||||
|
||||
// convert @username to link
|
||||
.replaceBy("(?<=(^|\\W))@([a-zA-Z0-9\\-_]+)(?=(\\W|$))".r){ m =>
|
||||
.replaceBy("(?<=(^|\\W))@([a-zA-Z0-9\\-_\\.]+)(?=(\\W|$))".r){ m =>
|
||||
getAccountByUserName(m.group(2)).map { _ =>
|
||||
s"""<a href="${context.path}/${m.group(2)}">@${m.group(2)}</a>"""
|
||||
}
|
||||
}
|
||||
|
||||
// convert commit id to link
|
||||
.replaceAll("(?<=(^|\\W))([a-f0-9]{40})(?=(\\W|$))", s"""<a href="${context.path}/${repository.owner}/${repository.name}/commit/$$2">$$2</a>""")
|
||||
.replaceAll("(?<=(^|[^\\w/@]))([a-f0-9]{40})(?=(\\W|$))", s"""<a href="${context.path}/${repository.owner}/${repository.name}/commit/$$2">$$2</a>""")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,20 @@ object Markdown {
|
||||
|
||||
/**
|
||||
* Converts Markdown of Wiki pages to HTML.
|
||||
*
|
||||
* @param repository the repository which contains the 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 enableTaskList if true then task list syntax is available
|
||||
* @param hasWritePermission
|
||||
* @param pages the list of existing Wiki pages
|
||||
*/
|
||||
def toHtml(markdown: String,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
enableWikiLink: Boolean,
|
||||
enableRefsLink: Boolean,
|
||||
enableAnchor: Boolean,
|
||||
enableTaskList: Boolean = false,
|
||||
hasWritePermission: Boolean = false,
|
||||
pages: List[String] = Nil)(implicit context: Context): String = {
|
||||
@@ -38,10 +47,14 @@ object Markdown {
|
||||
} else s
|
||||
|
||||
val rootNode = new PegDownProcessor(
|
||||
Extensions.AUTOLINKS | Extensions.WIKILINKS | Extensions.FENCED_CODE_BLOCKS | Extensions.TABLES | Extensions.HARDWRAPS | Extensions.SUPPRESS_ALL_HTML
|
||||
Extensions.AUTOLINKS | Extensions.WIKILINKS | Extensions.FENCED_CODE_BLOCKS |
|
||||
Extensions.TABLES | Extensions.HARDWRAPS | Extensions.SUPPRESS_ALL_HTML | Extensions.STRIKETHROUGH
|
||||
).parseMarkdown(source.toCharArray)
|
||||
|
||||
new GitBucketHtmlSerializer(markdown, repository, enableWikiLink, enableRefsLink, enableTaskList, hasWritePermission, pages).toHtml(rootNode)
|
||||
new GitBucketHtmlSerializer(
|
||||
markdown, repository, enableWikiLink, enableRefsLink, enableAnchor, enableTaskList,
|
||||
hasWritePermission, pages
|
||||
).toHtml(rootNode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +113,7 @@ class GitBucketHtmlSerializer(
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
enableWikiLink: Boolean,
|
||||
enableRefsLink: Boolean,
|
||||
enableAnchor: Boolean,
|
||||
enableTaskList: Boolean,
|
||||
hasWritePermission: Boolean,
|
||||
pages: List[String]
|
||||
@@ -108,9 +122,9 @@ class GitBucketHtmlSerializer(
|
||||
Map[String, VerbatimSerializer](VerbatimSerializer.DEFAULT -> new GitBucketVerbatimSerializer).asJava
|
||||
) with LinkConverter with RequestCache {
|
||||
|
||||
override protected def printImageTag(imageNode: SuperNode, url: String): Unit = {
|
||||
printer.print("<a target=\"_blank\" href=\"").print(fixUrl(url, true)).print("\">")
|
||||
.print("<img src=\"").print(fixUrl(url, true)).print("\" alt=\"").printEncoded(printChildrenToString(imageNode)).print("\"/></a>")
|
||||
override protected def printImageTag(rendering: LinkRenderer.Rendering): Unit = {
|
||||
printer.print("<a target=\"_blank\" href=\"").print(fixUrl(rendering.href, true)).print("\">")
|
||||
.print("<img src=\"").print(fixUrl(rendering.href, true)).print("\" alt=\"").printEncoded(rendering.text).print("\"/></a>")
|
||||
}
|
||||
|
||||
override protected def printLink(rendering: LinkRenderer.Rendering): Unit = {
|
||||
@@ -150,15 +164,34 @@ class GitBucketHtmlSerializer(
|
||||
|
||||
private def printHeaderTag(node: HeaderNode): Unit = {
|
||||
val tag = s"h${node.getLevel}"
|
||||
val headerTextString = printChildrenToString(node)
|
||||
val anchorName = GitBucketHtmlSerializer.generateAnchorName(headerTextString)
|
||||
val child = node.getChildren.asScala.headOption
|
||||
val anchorName = child match {
|
||||
case Some(x: AnchorLinkNode) => x.getName
|
||||
case Some(x: TextNode) => x.getText
|
||||
case _ => GitBucketHtmlSerializer.generateAnchorName(extractText(node)) // TODO
|
||||
}
|
||||
|
||||
printer.print(s"""<$tag class="markdown-head">""")
|
||||
printer.print(s"""<a class="markdown-anchor-link" href="#$anchorName"></a>""")
|
||||
printer.print(s"""<a class="markdown-anchor" name="$anchorName"></a>""")
|
||||
visitChildren(node)
|
||||
if(enableAnchor){
|
||||
printer.print(s"""<a class="markdown-anchor-link" href="#$anchorName"></a>""")
|
||||
printer.print(s"""<a class="markdown-anchor" name="$anchorName"></a>""")
|
||||
}
|
||||
child match {
|
||||
case Some(x: AnchorLinkNode) => printer.print(x.getText)
|
||||
case _ => visitChildren(node)
|
||||
}
|
||||
printer.print(s"</$tag>")
|
||||
}
|
||||
|
||||
private def extractText(node: Node): String = {
|
||||
val sb = new StringBuilder()
|
||||
node.getChildren.asScala.map {
|
||||
case x: TextNode => sb.append(x.getText)
|
||||
case x: Node => sb.append(extractText(x))
|
||||
}
|
||||
sb.toString()
|
||||
}
|
||||
|
||||
override def visit(node: HeaderNode): Unit = {
|
||||
printHeaderTag(node)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.util.{Date, Locale, TimeZone}
|
||||
|
||||
import gitbucket.core.controller.Context
|
||||
import gitbucket.core.model.CommitState
|
||||
import gitbucket.core.plugin.{RenderRequest, PluginRegistry, Renderer}
|
||||
import gitbucket.core.plugin.{RenderRequest, PluginRegistry}
|
||||
import gitbucket.core.service.{RepositoryService, RequestCache}
|
||||
import gitbucket.core.util.{FileUtil, JGitUtil, StringUtil}
|
||||
|
||||
@@ -91,16 +91,16 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
enableTaskList: Boolean = false,
|
||||
hasWritePermission: Boolean = false,
|
||||
pages: List[String] = Nil)(implicit context: Context): Html =
|
||||
Html(Markdown.toHtml(value, repository, enableWikiLink, enableRefsLink, enableTaskList, hasWritePermission, pages))
|
||||
Html(Markdown.toHtml(value, repository, enableWikiLink, enableRefsLink, enableTaskList, true, hasWritePermission, pages))
|
||||
|
||||
def renderMarkup(filePath: List[String], fileContent: String, branch: String,
|
||||
repository: RepositoryService.RepositoryInfo,
|
||||
enableWikiLink: Boolean, enableRefsLink: Boolean)(implicit context: Context): Html = {
|
||||
enableWikiLink: Boolean, enableRefsLink: Boolean, enableAnchor: Boolean)(implicit context: Context): Html = {
|
||||
|
||||
val fileName = filePath.reverse.head.toLowerCase
|
||||
val extension = FileUtil.getExtension(fileName)
|
||||
val renderer = PluginRegistry().getRenderer(extension)
|
||||
renderer.render(RenderRequest(filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, context))
|
||||
renderer.render(RenderRequest(filePath, fileContent, branch, repository, enableWikiLink, enableRefsLink, enableAnchor, context))
|
||||
}
|
||||
|
||||
def isRenderable(fileName: String): Boolean = {
|
||||
@@ -155,6 +155,11 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
||||
.replaceAll("\\[commit:([^\\s]+?)/([^\\s]+?)\\@([^\\s]+?)\\]", (m: Match) => s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/commit/${m.group(3)}">${m.group(1)}/${m.group(2)}@${m.group(3).substring(0, 7)}</a>""")
|
||||
)
|
||||
|
||||
/**
|
||||
* Remove html tags from the given Html instance.
|
||||
*/
|
||||
def removeHtml(html: Html): Html = Html(html.body.replaceAll("<.+?>", ""))
|
||||
|
||||
/**
|
||||
* URL encode except '/'.
|
||||
*/
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
</div>
|
||||
<div class="block">
|
||||
@if(account.url.isDefined){
|
||||
<div><i class="icon-home"></i> <a href="@account.url">@account.url</a></div>
|
||||
<div><i class="octicon octicon-home"></i> <a href="@account.url">@account.url</a></div>
|
||||
}
|
||||
<div><i class="icon-time"></i> <span class="muted">Joined on</span> @date(account.registeredDate)</div>
|
||||
<div><i class="octicon octicon-clock"></i> <span class="muted">Joined on</span> @date(account.registeredDate)</div>
|
||||
</div>
|
||||
@if(groupNames.nonEmpty){
|
||||
<div>
|
||||
|
||||
@@ -13,7 +13,7 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="javascript:void(0);" data-name="@loginAccount.get.userName"><i class="icon-ok"></i> <span>@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</span></a></li>
|
||||
<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>
|
||||
}
|
||||
@@ -31,7 +31,7 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
|
||||
<fieldset class="margin">
|
||||
<label class="radio">
|
||||
<input type="radio" name="isPrivate" value="false" @if(isCreateRepoOptionPublic){checked}>
|
||||
<span class="strong"><img src="@assets/common/images/repo_public.png"/> </i> Public</span><br>
|
||||
<span class="strong"><i class="octicon octicon-repo"></i> </i> Public</span><br>
|
||||
<div>
|
||||
<span>All users and guests can read this repository.</span>
|
||||
</div>
|
||||
@@ -40,7 +40,7 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
|
||||
<fieldset>
|
||||
<label class="radio">
|
||||
<input type="radio" name="isPrivate" value="true" @if(!isCreateRepoOptionPublic){checked}>
|
||||
<span class="strong"><img src="@assets/common/images/repo_private.png"/> </i> Private</span><br>
|
||||
<span class="strong"><i class="octicon octicon-lock"></i> </i> Private</span><br>
|
||||
<div>
|
||||
<span>Only collaborators can read this repository.</span>
|
||||
</div>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<div class="block-header">
|
||||
<a href="@url(repository)">@repository.name</a>
|
||||
@if(repository.repository.isPrivate){
|
||||
<i class="icon-lock"></i>
|
||||
<i class="octicon octicon-lock"></i>
|
||||
}
|
||||
</div>
|
||||
@if(repository.repository.originUserName.isDefined){
|
||||
|
||||
@@ -3,19 +3,20 @@
|
||||
<div class="container">
|
||||
<div class="row-fluid">
|
||||
<div class="span3">
|
||||
<div class="box">
|
||||
<ul class="nav nav-tabs nav-stacked side-menu">
|
||||
<li@if(active=="users"){ class="active"}>
|
||||
<a href="@path/admin/users">User Management</a>
|
||||
</li>
|
||||
<li@if(active=="system"){ class="active"}>
|
||||
<a href="@path/admin/system">System Settings</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="@path/console/login.jsp">H2 Console</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<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>
|
||||
</li>
|
||||
<li@if(active=="system"){ class="active"}>
|
||||
<a href="@path/admin/system">System Settings</a>
|
||||
</li>
|
||||
<li@if(active=="plugins"){ class="active"}>
|
||||
<a href="@path/admin/plugins">Plugins</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="@path/console/login.jsp">H2 Console</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="span9">
|
||||
@body
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
@(plugins: List[gitbucket.core.plugin.PluginInfo])(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main("Plugins"){
|
||||
@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>
|
||||
|
||||
@plugins.map {plugin =>
|
||||
<div class="box">
|
||||
<div class="box-header">@plugin.pluginName</div>
|
||||
<div class="box-content">
|
||||
<p><span class="strong">Id: </span>@plugin.pluginId</p>
|
||||
<p><span class="strong">Version: </span>@plugin.version</p>
|
||||
<p><span class="strong">Name: </span>@plugin.pluginName</p>
|
||||
<p class="muted">@plugin.description</p>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
<p>No plugin detected on your gitbucket installation.</p>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,9 +280,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<fieldset>
|
||||
<div class="align-right" style="margin-top: 20px;">
|
||||
<input type="submit" class="btn btn-success" value="Apply changes"/>
|
||||
</fieldset>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@
|
||||
<div>
|
||||
<hr>
|
||||
@if(!account.isGroupAccount){
|
||||
<i class="icon-envelope"></i> @account.mailAddress
|
||||
<i class="octicon octicon-mail"></i> @account.mailAddress
|
||||
}
|
||||
@account.url.map { url =>
|
||||
<i class="icon-home"></i> @url
|
||||
<i class="octicon octicon-home"></i> @url
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
@import gitbucket.core.view.helpers._
|
||||
<span class="small">
|
||||
<a class="button-link@if(condition.state == "open"){ selected}" href="@condition.copy(state = "open").toURL">
|
||||
<img src="@assets/common/images/status-open@(if(condition.state == "open"){"-active"}).png"/>
|
||||
<i class="octicon octicon-issue-opened @(if(condition.state == "open"){"active"})"></i>
|
||||
@openCount Open
|
||||
</a>
|
||||
<a class="button-link@if(condition.state == "closed"){ selected}" href="@condition.copy(state = "closed").toURL">
|
||||
<img src="@assets/common/images/status-closed@(if(condition.state == "closed"){"-active"}).png"/>
|
||||
<i class="octicon octicon-check @(if(condition.state == "closed"){"active"})"></i>
|
||||
@closedCount Closed
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
<tr>
|
||||
<td style="padding-top: 15px; padding-bottom: 15px;">
|
||||
@if(issue.isPullRequest){
|
||||
<img src="@assets/common/images/pullreq-@(if(issue.closed) "closed" else "open").png"/>
|
||||
<i class="octicon octicon-git-pull-request @(if(issue.closed) "closed" else "open")"></i>
|
||||
} else {
|
||||
<img src="@assets/common/images/issue-@(if(issue.closed) "closed" else "open").png"/>
|
||||
<i class="octicon octicon-issue-@(if(issue.closed) "closed" else "opened")"></i>
|
||||
}
|
||||
<a href="@path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a> ・
|
||||
@if(issue.isPullRequest){
|
||||
@@ -39,18 +39,19 @@
|
||||
}
|
||||
@if(commentCount > 0){
|
||||
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">
|
||||
<img src="@assets/common/images/comment-active.png"> @commentCount
|
||||
<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;">
|
||||
<img src="@assets/common/images/comment.png"> @commentCount
|
||||
<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"><img src="@assets/common/images/milestone.png"> @milestone</a></span>
|
||||
<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>
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
<div class="dashboard-nav">
|
||||
<div class="container">
|
||||
<a href="@path/" @if(active == ""){ class="active"}>
|
||||
<img src="@assets/common/images/menu-feed.png">
|
||||
<i class="octicon octicon-rss"></i>
|
||||
News Feed
|
||||
</a>
|
||||
@if(loginAccount.isDefined){
|
||||
<a href="@path/dashboard/pulls" @if(active == "pulls" ){ class="active"}>
|
||||
<img src="@assets/common/images/menu-pulls.png">
|
||||
<i class="octicon octicon-git-pull-request"></i>
|
||||
Pull Requests
|
||||
</a>
|
||||
<a href="@path/dashboard/issues" @if(active == "issues"){ class="active"}>
|
||||
<img src="@assets/common/images/menu-issues.png">
|
||||
<i class="octicon octicon-issue-opened"></i>
|
||||
Issues
|
||||
</a>
|
||||
}
|
||||
@@ -27,6 +27,8 @@ div.dashboard-nav {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
div.dashboard-nav a {
|
||||
line-height: 10px;
|
||||
margin-left: 20px;
|
||||
@@ -36,8 +38,17 @@ div.dashboard-nav a {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.dashboard-nav a:hover {
|
||||
|
||||
div.dashboard-nav .octicon{
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
font-size: 16px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.dashboard-nav a:hover,div.dashboard-nav a:hover .octicon {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
div.dashboard-nav a.active {
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
@activities.map { activity =>
|
||||
<div class="block">
|
||||
@(activity.activityType match {
|
||||
case "open_issue" => detailActivity(activity, "activity-issue.png")
|
||||
case "comment_issue" => detailActivity(activity, "activity-comment.png")
|
||||
case "comment_commit" => detailActivity(activity, "activity-comment.png")
|
||||
case "close_issue" => detailActivity(activity, "activity-issue-close.png")
|
||||
case "reopen_issue" => detailActivity(activity, "activity-issue-reopen.png")
|
||||
case "open_pullreq" => detailActivity(activity, "activity-merge.png")
|
||||
case "merge_pullreq" => detailActivity(activity, "activity-merge.png")
|
||||
case "create_repository" => simpleActivity(activity, "activity-create-repository.png")
|
||||
case "create_branch" => simpleActivity(activity, "activity-branch.png")
|
||||
case "delete_branch" => simpleActivity(activity, "activity-delete.png")
|
||||
case "create_tag" => simpleActivity(activity, "activity-tag.png")
|
||||
case "delete_tag" => simpleActivity(activity, "activity-delete.png")
|
||||
case "fork" => simpleActivity(activity, "activity-fork.png")
|
||||
case "push" => customActivity(activity, "activity-commit.png"){
|
||||
case "open_issue" => detailActivity(activity, "issue-opened")
|
||||
case "comment_issue" => detailActivity(activity, "comment-discussion")
|
||||
case "comment_commit" => detailActivity(activity, "comment-discussion")
|
||||
case "close_issue" => detailActivity(activity, "issue-closed")
|
||||
case "reopen_issue" => detailActivity(activity, "issue-reopened")
|
||||
case "open_pullreq" => detailActivity(activity, "git-pull-request")
|
||||
case "merge_pullreq" => detailActivity(activity, "git-merge")
|
||||
case "create_repository" => simpleActivity(activity, "repo")
|
||||
case "create_branch" => simpleActivity(activity, "git-branch")
|
||||
case "delete_branch" => simpleActivity(activity, "circle-slash")
|
||||
case "create_tag" => simpleActivity(activity, "tag")
|
||||
case "delete_tag" => simpleActivity(activity, "circle-slash")
|
||||
case "fork" => simpleActivity(activity, "repo-forked")
|
||||
case "push" => customActivity(activity, "git-commit"){
|
||||
<div class="small activity-message">
|
||||
{activity.additionalInfo.get.split("\n").reverse.take(4).zipWithIndex.map{ case (commit, i) =>
|
||||
if(i == 3){
|
||||
@@ -37,12 +37,12 @@
|
||||
}}
|
||||
</div>
|
||||
}
|
||||
case "create_wiki" => customActivity(activity, "activity-wiki.png"){
|
||||
case "create_wiki" => customActivity(activity, "book"){
|
||||
<div class="small activity-message">
|
||||
Created <a href={s"${path}/${activity.userName}/${activity.repositoryName}/wiki/${activity.additionalInfo.get}"}>{activity.additionalInfo.get}</a>.
|
||||
</div>
|
||||
}
|
||||
case "edit_wiki" => customActivity(activity, "activity-wiki.png"){
|
||||
case "edit_wiki" => customActivity(activity, "book"){
|
||||
activity.additionalInfo.get.split(":") match {
|
||||
case Array(pageName, commitId) =>
|
||||
<div class="small activity-message">
|
||||
@@ -61,7 +61,7 @@
|
||||
}
|
||||
|
||||
@detailActivity(activity: gitbucket.core.model.Activity, image: String) = {
|
||||
<div class="activity-icon-large"><img src="@assets/common/images/@image"/></div>
|
||||
<div class="activity-icon-large"><i class="mega-octicon octicon-@image"></i></div>
|
||||
<div class="activity-content">
|
||||
<div class="muted small">@helper.html.datetimeago(activity.activityDate)</div>
|
||||
<div class="strong">
|
||||
@@ -75,7 +75,7 @@
|
||||
}
|
||||
|
||||
@customActivity(activity: gitbucket.core.model.Activity, image: String)(additionalInfo: Any) = {
|
||||
<div class="activity-icon-large"><img src="@assets/common/images/@image"/></div>
|
||||
<div class="activity-icon-large"><i class="mega-octicon octicon-@image"></i></div>
|
||||
<div class="activity-content">
|
||||
<div class="muted small">@helper.html.datetimeago(activity.activityDate)</div>
|
||||
<div class="strong">
|
||||
@@ -87,7 +87,7 @@
|
||||
}
|
||||
|
||||
@simpleActivity(activity: gitbucket.core.model.Activity, image: String) = {
|
||||
<div class="activity-icon-small"><img src="@assets/common/images/@image"/></div>
|
||||
<div class="activity-icon-small"><i class="octicon octicon-@image"></i></div>
|
||||
<div class="activity-content">
|
||||
<div>
|
||||
@avatar(activity.activityUserName, 16)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@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 = true
|
||||
mini = false
|
||||
) {
|
||||
<li><div id="branch-control-title">Switch branches<button id="branch-control-close" class="pull-right">×</button></div></li>
|
||||
<li><input id="branch-control-input" type="text" placeholder="Find or create branch ..."/></li>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@(condition: => Boolean)
|
||||
@if(condition){
|
||||
<i class="icon-ok"></i>
|
||||
<i class="octicon octicon-check"></i>
|
||||
} else {
|
||||
<i class="icon-white"></i>
|
||||
}
|
||||
@@ -24,8 +24,8 @@
|
||||
</span>
|
||||
<span class="pull-right">
|
||||
@if(hasWritePermission || loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false)){
|
||||
<a href="#" data-comment-id="@comment.commentId"><i class="icon-pencil"></i></a>
|
||||
<a href="#" data-comment-id="@comment.commentId"><i class="icon-remove-circle"></i></a>
|
||||
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-pencil"></i></a>
|
||||
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-x"></i></a>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@(id: String, value: String)(html: Html)
|
||||
<div class="input-append" style="margin-bottom: 0px;">
|
||||
@(id: String, value: String, prepend: Boolean = false)(html: Html)
|
||||
<div class="input-append @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="icon-check"></i></span>
|
||||
<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>
|
||||
</div>
|
||||
<script>
|
||||
// copy to clipboard
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@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="icon-calendar"></i></span>
|
||||
<span class="add-on"><i class="octicon octicon-calendar"></i></span>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
|
||||
@@ -25,16 +25,16 @@
|
||||
<span class="pull-right diffstat" data-diff-id="@i"></span>
|
||||
<a href="#diff-@i">
|
||||
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
||||
<img src="@assets/common/images/diff_move.png"/> @diff.oldPath -> @diff.newPath
|
||||
<i class="octicon octicon-diff-renamed"></i> @diff.oldPath -> @diff.newPath
|
||||
}
|
||||
@if(diff.changeType == ChangeType.ADD){
|
||||
<img src="@assets/common/images/diff_add.png"/> @diff.newPath
|
||||
<i class="octicon octicon-diff-added"></i> @diff.newPath
|
||||
}
|
||||
@if(diff.changeType == ChangeType.MODIFY){
|
||||
<img src="@assets/common/images/diff_edit.png"/> @diff.newPath
|
||||
<i class="octicon octicon-diff-modified"></i> @diff.newPath
|
||||
}
|
||||
@if(diff.changeType == ChangeType.DELETE){
|
||||
<img src="@assets/common/images/diff_delete.png"/> @diff.oldPath
|
||||
<i class="octicon octicon-diff-removed"></i> @diff.oldPath
|
||||
}
|
||||
</a>
|
||||
</li>
|
||||
@@ -55,7 +55,7 @@
|
||||
</div>
|
||||
}
|
||||
<span class="diffstat">
|
||||
<img src="@assets/common/images/diff_move.png"/>
|
||||
<i class="octicon octicon-diff-renamed"></i>
|
||||
</span> @diff.oldPath -> @diff.newPath
|
||||
}
|
||||
@if(diff.changeType == ChangeType.ADD || diff.changeType == ChangeType.MODIFY){
|
||||
@@ -68,9 +68,9 @@
|
||||
}
|
||||
<span class="diffstat">
|
||||
@if(diff.changeType == ChangeType.ADD){
|
||||
<img src="@assets/common/images/diff_add.png"/>
|
||||
<i class="octicon octicon-diff-added"></i>
|
||||
}else{
|
||||
<img src="@assets/common/images/diff_edit.png"/>
|
||||
<i class="octicon octicon-diff-modified"></i>
|
||||
}
|
||||
</span>
|
||||
@diff.newPath
|
||||
@@ -83,7 +83,7 @@
|
||||
</div>
|
||||
}
|
||||
<span class="diffstat">
|
||||
<img src="@assets/common/images/diff_delete.png"/>
|
||||
<i class="octicon octicon-diff-removed"></i>
|
||||
</span> @diff.oldPath
|
||||
}
|
||||
</th>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@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">
|
||||
@if(value.isEmpty){
|
||||
<i class="icon-cog"></i>
|
||||
<i class="octicon octicon-gear"></i>
|
||||
} else {
|
||||
@if(prefix.nonEmpty){
|
||||
<span class="muted">@prefix:</span>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<published>@datetimeRFC3339(activity.activityDate)</published>
|
||||
<updated>@datetimeRFC3339(activity.activityDate)</updated>
|
||||
<link type="text/html" rel="alternate" href="@context.baseUrl/@activity.userName/@activity.repositoryName" />
|
||||
<title type="html">@activity.activityType</title>
|
||||
<title type="html">@removeHtml(activityMessage(activity.message))</title>
|
||||
<author>
|
||||
<name>@activity.activityUserName</name>
|
||||
<uri>@url(activity.activityUserName)</uri>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
enableTaskList: Boolean,
|
||||
hasWritePermission: Boolean,
|
||||
style: String = "",
|
||||
styleClass: String = "",
|
||||
placeholder: String = "Leave a comment",
|
||||
elastic: Boolean = false,
|
||||
uid: Long = new java.util.Date().getTime())(implicit context: gitbucket.core.controller.Context)
|
||||
@@ -20,7 +21,9 @@
|
||||
<div class="tab-pane active" id="tab@uid">
|
||||
<span id="error-content" class="error"></span>
|
||||
@textarea = {
|
||||
<textarea id="content@uid" name="content"@if(style.nonEmpty){ style="@style"} placeholder="@placeholder">@content</textarea>
|
||||
<textarea id="content@uid" name="content" placeholder="@placeholder"
|
||||
@if(style.nonEmpty){ style="@style"}
|
||||
@if(styleClass.nonEmpty){ class="@styleClass" }>@content</textarea>
|
||||
}
|
||||
@if(enableWikiLink){
|
||||
@textarea
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@if(repository.repository.isPrivate){
|
||||
<img src="@assets/common/images/repo_private@{if(large){"_lg"}}.png"/>
|
||||
<i class="@{if(large){"mega-"}}octicon octicon-lock"></i>
|
||||
} else {
|
||||
@if(repository.repository.originUserName.isDefined){
|
||||
<img src="@assets/common/images/repo_fork@{if(large){"_lg"}}.png"/>
|
||||
<i class="@{if(large){"mega-"}}octicon octicon-repo-forked"></i>
|
||||
} else {
|
||||
<img src="@assets/common/images/repo_public@{if(large){"_lg"}}.png"/>
|
||||
<i class="@{if(large){"mega-"}}octicon octicon-repo"></i>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,57 +24,64 @@
|
||||
@if(loginAccount.isEmpty){
|
||||
@signinform(settings)
|
||||
} else {
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th class="metal">
|
||||
<div class="pull-right">
|
||||
<a href="@path/new" class="btn btn-success btn-mini">New repository</a>
|
||||
</div>
|
||||
Your repositories (@userRepositories.size)
|
||||
</th>
|
||||
</tr>
|
||||
<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){
|
||||
<tr>
|
||||
<td>No repositories</td>
|
||||
</tr>
|
||||
<div class="box-content-bottom">
|
||||
No repositories
|
||||
</div>
|
||||
} else {
|
||||
@userRepositories.map { repository =>
|
||||
<tr>
|
||||
<td>
|
||||
@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>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<div class="box-content-bottom" style="padding: 0px;">
|
||||
@defining(20){ max =>
|
||||
@userRepositories.zipWithIndex.map { case (repository, i) =>
|
||||
<div class="box-content-row 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>
|
||||
}
|
||||
@if(userRepositories.size > max){
|
||||
<div class="box-content-row show-more">
|
||||
<a href="javascript:void(0);" id="show-more-repos">Show more @{userRepositories.size - max} pages...</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th class="metal">
|
||||
Recent updated repositories
|
||||
</th>
|
||||
</tr>
|
||||
@if(recentRepositories.isEmpty){
|
||||
<tr>
|
||||
<td>No repositories</td>
|
||||
</tr>
|
||||
} else {
|
||||
<div class="box-header">
|
||||
<span class="strong">Recent updated repositories</span>
|
||||
</div>
|
||||
@if(recentRepositories.isEmpty){
|
||||
<div class="box-content-bottom">
|
||||
No repositories
|
||||
</div>
|
||||
} else {
|
||||
<div class="box-content-bottom" style="padding: 0px;">
|
||||
@recentRepositories.map { repository =>
|
||||
<tr>
|
||||
<td>
|
||||
@helper.html.repositoryicon(repository, false)
|
||||
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<div class="box-content-row">
|
||||
@helper.html.repositoryicon(repository, false)
|
||||
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<script>
|
||||
$(function(){
|
||||
$('#show-more-repos').click(function(e){
|
||||
$('div.repo-link').show();
|
||||
$(e.target).parents('div.show-more').remove();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -8,18 +8,27 @@
|
||||
<hr/><br/>
|
||||
<form method="POST" validate="true">
|
||||
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
|
||||
<div class="box issue-comment-box">
|
||||
<div class="issue-comment-box">
|
||||
<div class="box-content">
|
||||
@helper.html.preview(repository, "", false, true, true, hasWritePermission, "width: 635px; height: 100px; max-height: 150px;", elastic = true)
|
||||
@helper.html.preview(
|
||||
repository = repository,
|
||||
content = "",
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = hasWritePermission,
|
||||
style = "",
|
||||
elastic = true
|
||||
)
|
||||
<div style="text-align: right;">
|
||||
<input type="hidden" name="issueId" value="@issue.issueId"/>
|
||||
<input type="submit" class="btn btn-success" formaction="@url(repository)/issue_comments/new" value="Comment"/>
|
||||
@if((reopenable || !issue.closed) && (hasWritePermission || issue.openedUserName == loginAccount.get.userName)){
|
||||
<input type="submit" class="btn" formaction="@url(repository)/issue_comments/state" value="@{if(issue.closed) "Reopen" else "Close"}" id="action"/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<input type="hidden" name="issueId" value="@issue.issueId"/>
|
||||
<input type="submit" class="btn btn-success" formaction="@url(repository)/issue_comments/new" value="Comment"/>
|
||||
@if((reopenable || !issue.closed) && (hasWritePermission || issue.openedUserName == loginAccount.get.userName)){
|
||||
<input type="submit" class="btn" formaction="@url(repository)/issue_comments/state" value="@{if(issue.closed) "Reopen" else "Close"}" id="action"/>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
<script>
|
||||
|
||||
@@ -8,16 +8,16 @@
|
||||
@import gitbucket.core.model.CommitComment
|
||||
@if(issue.isDefined){
|
||||
<div class="issue-avatar-image">@avatar(issue.get.openedUserName, 48)</div>
|
||||
<div class="box issue-comment-box">
|
||||
<div class="box-header-small">
|
||||
<div class="issue-comment-box">
|
||||
<div class="box-header">
|
||||
@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)){
|
||||
<a href="#" data-issue-id="@issue.get.issueId"><i class="icon-pencil" aria-label="Edit"></i></a>
|
||||
<a href="#" data-issue-id="@issue.get.issueId"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div class="box-content issue-content markdown-body" id="issueContent">
|
||||
<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>
|
||||
</div>
|
||||
@@ -28,7 +28,7 @@
|
||||
@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-small">
|
||||
<div class="box-header">
|
||||
@user(comment.commentedUserName, styleClass="username strong")
|
||||
<span class="muted">
|
||||
@if(comment.action == "comment"){
|
||||
@@ -41,12 +41,12 @@
|
||||
<span class="pull-right">
|
||||
@if(comment.action != "commit" && comment.action != "merge" && comment.action != "refer"
|
||||
&& (hasWritePermission || loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false))){
|
||||
<a href="#" data-comment-id="@comment.commentId"><i class="icon-pencil" aria-label="Edit"></i></a>
|
||||
<a href="#" data-comment-id="@comment.commentId"><i class="icon-remove-circle" aria-label="Remove"></i></a>
|
||||
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-pencil" aria-label="Edit"></i></a>
|
||||
<a href="#" data-comment-id="@comment.commentId"><i class="octicon octicon-x" aria-label="Remove"></i></a>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div class="box-content"class="issue-content" id="commentContent-@comment.commentId">
|
||||
<div class="box-content-bottom issue-content" id="commentContent-@comment.commentId">
|
||||
@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>
|
||||
@@ -78,8 +78,8 @@
|
||||
</div>
|
||||
}
|
||||
@if(comment.action == "close" || comment.action == "close_comment"){
|
||||
<div class="small issue-comment-action">
|
||||
<span class="label label-important">Closed</span>
|
||||
<div class="issue-comment-action">
|
||||
<i class="octicon octicon-circle-slash danger"></i>
|
||||
@avatar(comment.commentedUserName, 20)
|
||||
@if(issue.isDefined && issue.get.isPullRequest){
|
||||
@user(comment.commentedUserName, styleClass="username strong") closed the pull request @helper.html.datetimeago(comment.registeredDate)
|
||||
@@ -89,14 +89,14 @@
|
||||
</div>
|
||||
}
|
||||
@if(comment.action == "reopen" || comment.action == "reopen_comment"){
|
||||
<div class="small issue-comment-action">
|
||||
<span class="label label-success">Reopened</span>
|
||||
<div class="issue-comment-action issue-reopened">
|
||||
<i class="octicon octicon-primitive-dot"></i>
|
||||
@avatar(comment.commentedUserName, 20)
|
||||
@user(comment.commentedUserName, styleClass="username strong") reopened the issue @helper.html.datetimeago(comment.registeredDate)
|
||||
</div>
|
||||
}
|
||||
@if(comment.action == "delete_branch"){
|
||||
<div class="small issue-comment-action">
|
||||
<div class="issue-comment-action">
|
||||
<span class="label">Deleted</span>
|
||||
@avatar(comment.commentedUserName, 20)
|
||||
@user(comment.commentedUserName, styleClass="username strong") deleted the <span class="label label-info monospace">@pullreq.map(_.requestBranch)</span> branch @helper.html.datetimeago(comment.registeredDate)
|
||||
@@ -110,7 +110,7 @@
|
||||
<script>
|
||||
$(function(){
|
||||
@if(issue.isDefined){
|
||||
$('.issue-comment-box i.icon-pencil').click(function(){
|
||||
$('.issue-comment-box i.octicon-pencil').click(function(){
|
||||
var id = $(this).closest('a').data('comment-id');
|
||||
var url = '@url(repository)/issue_comments/_data/' + id;
|
||||
var $content = $('#commentContent-' + id);
|
||||
@@ -130,7 +130,7 @@ $(function(){
|
||||
});
|
||||
return false;
|
||||
});
|
||||
$('.issue-comment-box i.icon-remove-circle').click(function(){
|
||||
$('.issue-comment-box i.octicon-x').click(function(){
|
||||
if(confirm('Are you sure you want to delete this?')) {
|
||||
var id = $(this).closest('a').data('comment-id');
|
||||
$.post('@url(repository)/issue_comments/delete/' + id,
|
||||
@@ -144,7 +144,7 @@ $(function(){
|
||||
return false;
|
||||
});
|
||||
}
|
||||
$(document).on('click', '.commit-comment-box i.icon-pencil', function(){
|
||||
$(document).on('click', '.commit-comment-box i.octicon-pencil', function(){
|
||||
var id = $(this).closest('a').data('comment-id');
|
||||
var url = '@url(repository)/commit_comments/_data/' + id;
|
||||
var $content = $('.commit-commentContent-' + id, $(this).closest('.box'));
|
||||
@@ -158,7 +158,7 @@ $(function(){
|
||||
});
|
||||
return false;
|
||||
});
|
||||
$(document).on('click', '.commit-comment-box i.icon-remove-circle', function(){
|
||||
$(document).on('click', '.commit-comment-box i.octicon-x', function(){
|
||||
if(confirm('Are you sure you want to delete this?')) {
|
||||
var id = $(this).closest('a').data('comment-id');
|
||||
$.post('@url(repository)/commit_comments/delete/' + id,
|
||||
|
||||
@@ -10,139 +10,33 @@
|
||||
@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="span9">
|
||||
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
|
||||
<div class="box issue-box">
|
||||
<div class="box-content">
|
||||
<span id="error-title" class="error"></span>
|
||||
<input type="text" name="title" value="" placeholder="Title" style="width: 565px;" autofocus/>
|
||||
<div>
|
||||
<span id="label-assigned">No one is assigned</span>
|
||||
@if(hasWritePermission){
|
||||
<input type="hidden" name="assignedUserName" value=""/>
|
||||
@helper.html.dropdown() {
|
||||
<li><a href="javascript:void(0);" class="assign" data-name=""><i class="icon-remove-circle"></i> Clear assignee</a></li>
|
||||
@collaborators.map { collaborator =>
|
||||
<li><a href="javascript:void(0);" class="assign" data-name="@collaborator"><i class="icon-while"></i>@avatar(collaborator, 20) @collaborator</a></li>
|
||||
}
|
||||
}
|
||||
}
|
||||
<div class="pull-right">
|
||||
<span id="label-milestone">No milestone</span>
|
||||
@if(hasWritePermission){
|
||||
<input type="hidden" name="milestoneId" value=""/>
|
||||
@helper.html.dropdown() {
|
||||
<li><a href="javascript:void(0);" class="milestone" data-id=""><i class="icon-remove-circle"></i> Clear this milestone</a></li>
|
||||
@milestones.filter(_.closedDate.isEmpty).map { milestone =>
|
||||
<li>
|
||||
<a href="javascript:void(0);" class="milestone" data-id="@milestone.milestoneId" data-title="@milestone.title">
|
||||
<i class="icon-while"></i> @milestone.title
|
||||
<div class="small" style="padding-left: 20px;">
|
||||
@milestone.dueDate.map { dueDate =>
|
||||
@if(isPast(dueDate)){
|
||||
<img src="@assets/common/images/alert.png"/><span class="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>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
<div class="row-fluid">
|
||||
<div class="span10">
|
||||
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
|
||||
<div class="box issue-box">
|
||||
<div class="box-content">
|
||||
<span id="error-title" class="error"></span>
|
||||
<input type="text" name="title" value="" placeholder="Title" style="width: 690px;" autofocus/>
|
||||
@helper.html.preview(
|
||||
repository = repository,
|
||||
content = "",
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = hasWritePermission,
|
||||
style = "width: 690px; height: 200px; max-height: 250px;",
|
||||
elastic = true
|
||||
)
|
||||
<div class="align-right">
|
||||
<input type="submit" class="btn btn-success" value="Submit new issue"/>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
@helper.html.preview(repository, "", false, true, true, hasWritePermission, "width: 565px; height: 200px; max-height: 250px;", elastic = true)
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<input type="submit" class="btn btn-success" value="Submit new issue"/>
|
||||
<div class="span2">
|
||||
@issueinfo(None, Nil, Nil, collaborators, milestones.map(x => (x, 0, 0)), labels, hasWritePermission, repository)
|
||||
</div>
|
||||
</div>
|
||||
<div class="span3">
|
||||
@if(hasWritePermission){
|
||||
<span class="strong">Labels</span>
|
||||
<div>
|
||||
<div id="label-list">
|
||||
<ul class="label-list nav nav-pills nav-stacked">
|
||||
@labels.map { label =>
|
||||
<li>
|
||||
<a href="javascript:void(0);" class="toggle-label" data-label="@label.labelName" data-bgcolor="@label.color" data-fgcolor="@label.fontColor">
|
||||
<span style="background-color: #@label.color;" class="label-color"> </span>
|
||||
@label.labelName
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
<input type="hidden" name="labelNames" value=""/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
<script>
|
||||
$(function(){
|
||||
$('a.assign').click(function(){
|
||||
var userName = $(this).data('name');
|
||||
$('a.assign i.icon-ok').attr('class', 'icon-white');
|
||||
|
||||
if(userName == ''){
|
||||
$('#label-assigned').text('No one will be assigned');
|
||||
} else {
|
||||
$('#label-assigned').html($('<span>')
|
||||
.append($('<a class="username strong">').attr('href', '@path/' + userName).text(userName))
|
||||
.append(' will be assigned'));
|
||||
$('a.assign[data-name=' + jqSelectorEscape(userName) + '] i').attr('class', 'icon-ok');
|
||||
}
|
||||
$('input[name=assignedUserName]').val(userName);
|
||||
});
|
||||
|
||||
$('a.milestone').click(function(){
|
||||
var title = $(this).data('title');
|
||||
var milestoneId = $(this).data('id');
|
||||
$('a.milestone i.icon-ok').attr('class', 'icon-white');
|
||||
|
||||
if(milestoneId == ''){
|
||||
$('#label-milestone').text('No milestone');
|
||||
} else {
|
||||
$('#label-milestone').html($('<span class="strong">').text(title));
|
||||
$('a.milestone[data-id=' + milestoneId + '] i').attr('class', 'icon-ok');
|
||||
}
|
||||
$('input[name=milestoneId]').val(milestoneId);
|
||||
});
|
||||
|
||||
$('a.toggle-label').click(function(){
|
||||
if($(this).data('selected') == true){
|
||||
$(this).css({
|
||||
'background-color': 'white',
|
||||
'color' : 'black',
|
||||
'font-weight' : 'normal'
|
||||
});
|
||||
$(this).data('selected', false);
|
||||
} else {
|
||||
$(this).css({
|
||||
'background-color': '#' + $(this).data('bgcolor'),
|
||||
'color' : '#' + $(this).data('fgcolor'),
|
||||
'font-weight' : 'bold'
|
||||
});
|
||||
$(this).data('selected', true);
|
||||
}
|
||||
|
||||
var labelNames = Array();
|
||||
$('a.toggle-label').each(function(i, e){
|
||||
if($(e).data('selected') == true){
|
||||
labelNames.push($(e).data('label'));
|
||||
}
|
||||
});
|
||||
$('input[name=labelNames]').val(labelNames.join(','));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import context._
|
||||
<span id="error-edit-content-@commentId" class="error"></span>
|
||||
@helper.html.attached(owner, repository){
|
||||
<textarea style="width: 635px; height: 100px;" id="edit-content-@commentId">@content</textarea>
|
||||
<textarea id="edit-content-@commentId">@content</textarea>
|
||||
}
|
||||
<div>
|
||||
<input type="button" id="cancel-comment-@commentId" class="btn btn-small btn-danger" value="Cancel"/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@(content: Option[String], issueId: Int, owner: String, repository: String)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@helper.html.attached(owner, repository){
|
||||
<textarea style="width: 635px; height: 100px; max-height: 300px;" id="edit-content">@content.getOrElse("")</textarea>
|
||||
<textarea id="edit-content">@content.getOrElse("")</textarea>
|
||||
}
|
||||
<div>
|
||||
<input type="button" id="cancel-issue" class="btn btn-small btn-danger" value="Cancel"/>
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
<div>
|
||||
<div class="show-title pull-right">
|
||||
@if(hasWritePermission || loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
|
||||
<a class="btn btn-small" href="#" id="edit">Edit</a>
|
||||
<a class="btn" href="#" id="edit">Edit</a>
|
||||
}
|
||||
<a class="btn btn-small btn-success" href="@url(repository)/issues/new">New issue</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>
|
||||
@@ -45,13 +45,14 @@
|
||||
</span>
|
||||
<br/><br/>
|
||||
<hr>
|
||||
<br/>
|
||||
<div class="row-fluid">
|
||||
<div class="span10">
|
||||
@commentlist(Some(issue), comments, hasWritePermission, repository)
|
||||
@commentform(issue, true, hasWritePermission, repository)
|
||||
</div>
|
||||
<div class="span2">
|
||||
@issueinfo(issue, comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
||||
@issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@(issue: gitbucket.core.model.Issue,
|
||||
@(issue: Option[gitbucket.core.model.Issue],
|
||||
comments: List[gitbucket.core.model.Comment],
|
||||
issueLabels: List[gitbucket.core.model.Label],
|
||||
collaborators: List[String],
|
||||
@@ -6,6 +6,7 @@
|
||||
labels: List[gitbucket.core.model.Label],
|
||||
hasWritePermission: Boolean,
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
<div style="margin-bottom: 8px;">
|
||||
<span class="muted small strong">Labels</span>
|
||||
@@ -22,6 +23,9 @@
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@if(issue.isEmpty){
|
||||
<input type="hidden" name="labelNames" value=""/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -34,15 +38,18 @@
|
||||
@if(hasWritePermission){
|
||||
<div class="pull-right">
|
||||
@helper.html.dropdown(right = true) {
|
||||
<li><a href="javascript:void(0);" class="milestone" data-id=""><i class="icon-remove-circle"></i> Clear this milestone</a></li>
|
||||
<li><a href="javascript:void(0);" class="milestone" data-id=""><i class="octicon octicon-x"></i> Clear this milestone</a></li>
|
||||
@milestones.filter(_._1.closedDate.isEmpty).map { case (milestone, _, _) =>
|
||||
<li>
|
||||
<a href="javascript:void(0);" class="milestone" data-id="@milestone.milestoneId" data-title="@milestone.title">
|
||||
@helper.html.checkicon(Some(milestone.milestoneId) == issue.milestoneId) @milestone.title
|
||||
@issue.map { issue =>
|
||||
@helper.html.checkicon(Some(milestone.milestoneId) == issue.milestoneId)
|
||||
}
|
||||
@milestone.title
|
||||
<div class="small" style="padding-left: 20px;">
|
||||
@milestone.dueDate.map { dueDate =>
|
||||
@if(isPast(dueDate)){
|
||||
<img src="@assets/common/images/alert.png"/><span class="milestone-alert">Due by @date(dueDate)</span>
|
||||
<i class="octicon octicon-alert" style="color:#BD2C00;"></i><span class="milestone-alert">Due by @date(dueDate)</span>
|
||||
} else {
|
||||
<span class="muted">Due by @date(dueDate)</span>
|
||||
}
|
||||
@@ -58,14 +65,14 @@
|
||||
}
|
||||
</div>
|
||||
<div id="milestone-progress-area">
|
||||
@issue.milestoneId.map { milestoneId =>
|
||||
@issue.flatMap(_.milestoneId).map { milestoneId =>
|
||||
@milestones.collect { case (milestone, openCount, closeCount) if(milestone.milestoneId == milestoneId) =>
|
||||
@issues.milestones.html.progress(openCount + closeCount, closeCount)
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<span id="label-milestone">
|
||||
@issue.milestoneId.map { milestoneId =>
|
||||
@issue.flatMap(_.milestoneId).map { milestoneId =>
|
||||
@milestones.collect { case (milestone, _, _) if(milestone.milestoneId == milestoneId) =>
|
||||
<span class="strong small">@milestone.title</span>
|
||||
}
|
||||
@@ -73,17 +80,20 @@
|
||||
<span class="muted small">No milestone</span>
|
||||
}
|
||||
</span>
|
||||
@if(issue.isEmpty){
|
||||
<input type="hidden" name="milestoneId" value=""/>
|
||||
}
|
||||
<hr/>
|
||||
<div style="margin-bottom: 8px;">
|
||||
<span class="muted small strong">Assignee</span>
|
||||
@if(hasWritePermission){
|
||||
<div class="pull-right">
|
||||
@helper.html.dropdown(right = true) {
|
||||
<li><a href="javascript:void(0);" class="assign" data-name=""><i class="icon-remove-circle"></i> Clear assignee</a></li>
|
||||
<li><a href="javascript:void(0);" class="assign" data-name=""><i class="octicon octicon-x"></i> Clear assignee</a></li>
|
||||
@collaborators.map { collaborator =>
|
||||
<li>
|
||||
<a href="javascript:void(0);" class="assign" data-name="@collaborator">
|
||||
@helper.html.checkicon(Some(collaborator) == issue.assignedUserName)@avatar(collaborator, 20) @collaborator
|
||||
@helper.html.checkicon(issue.exists(_.assignedUserName == collaborator))@avatar(collaborator, 20) @collaborator
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@@ -92,39 +102,37 @@
|
||||
}
|
||||
</div>
|
||||
<span id="label-assigned">
|
||||
@issue.assignedUserName.map { userName =>
|
||||
@issue.flatMap(_.assignedUserName).map { userName =>
|
||||
@avatar(userName, 20) @user(userName, styleClass="username strong small")
|
||||
}.getOrElse{
|
||||
<span class="muted small">No one</span>
|
||||
}
|
||||
</span>
|
||||
<hr/>
|
||||
<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>
|
||||
@if(issue.isEmpty){
|
||||
<input type="hidden" name="assignedUserName" value=""/>
|
||||
}
|
||||
@issue.map { issue =>
|
||||
<hr/>
|
||||
<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>
|
||||
}
|
||||
<script>
|
||||
$(function(){
|
||||
@issue.map { issue =>
|
||||
$('a.toggle-label').click(function(){
|
||||
var path, icon;
|
||||
var i = $(this).children('i');
|
||||
if(i.hasClass('icon-ok')){
|
||||
path = 'delete';
|
||||
icon = 'icon-white';
|
||||
} else {
|
||||
path = 'new';
|
||||
icon = 'icon-ok';
|
||||
}
|
||||
var path = switchLabel($(this));
|
||||
$.post('@url(repository)/issues/@issue.issueId/label/' + path,
|
||||
{
|
||||
labelId : $(this).data('label-id')
|
||||
},
|
||||
function(data){
|
||||
i.removeClass().addClass(icon);
|
||||
$('ul.label-list').empty().html(data);
|
||||
});
|
||||
{ labelId : $(this).data('label-id') },
|
||||
function(data){
|
||||
$('ul.label-list').empty().html(data);
|
||||
}
|
||||
);
|
||||
return false;
|
||||
});
|
||||
|
||||
@@ -132,42 +140,92 @@ $(function(){
|
||||
var title = $(this).data('title');
|
||||
var milestoneId = $(this).data('id');
|
||||
$.post('@url(repository)/issues/@issue.issueId/milestone',
|
||||
{
|
||||
milestoneId: milestoneId
|
||||
},
|
||||
function(data){
|
||||
console.log(data);
|
||||
$('a.milestone i.icon-ok').attr('class', 'icon-white');
|
||||
if(milestoneId == ''){
|
||||
$('#label-milestone').html($('<span class="muted small">').text('No milestone'));
|
||||
$('#milestone-progress-area').empty();
|
||||
} else {
|
||||
$('#label-milestone').html($('<span class="strong small">').text(title));
|
||||
$('#milestone-progress-area').html(data);
|
||||
$('a.milestone[data-id=' + milestoneId + '] i').attr('class', 'icon-ok');
|
||||
{ milestoneId: milestoneId },
|
||||
function(data){
|
||||
displayMilestone(title, milestoneId, data);
|
||||
}
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
$('a.assign').click(function(){
|
||||
var $this = $(this);
|
||||
var userName = $this.data('name');
|
||||
$.post('@url(repository)/issues/@issue.issueId/assign',
|
||||
{
|
||||
assignedUserName: userName
|
||||
},
|
||||
function(){
|
||||
$('a.assign i.icon-ok').attr('class', 'icon-white');
|
||||
if(userName == ''){
|
||||
$('#label-assigned').html($('<span class="muted small">').text('No one'));
|
||||
} else {
|
||||
$('#label-assigned').empty()
|
||||
.append($this.find('img.avatar-mini').clone(false)).append(' ')
|
||||
.append($('<a class="username strong small">').attr('href', '@context.path/' + userName).text(userName));
|
||||
$('a.assign[data-name=' + jqSelectorEscape(userName) + '] i').attr('class', 'icon-ok');
|
||||
{ assignedUserName: userName },
|
||||
function(){
|
||||
displayAssignee($this, userName);
|
||||
}
|
||||
);
|
||||
});
|
||||
}.getOrElse {
|
||||
$('a.toggle-label').click(function(){
|
||||
switchLabel($(this));
|
||||
var labelNames = Array();
|
||||
$('a.toggle-label').each(function(i, e){
|
||||
if($(e).children('i').hasClass('icon-ok') == true){
|
||||
labelNames.push($(e).text().trim());
|
||||
}
|
||||
});
|
||||
$('input[name=labelNames]').val(labelNames.join(','));
|
||||
|
||||
$.post('@url(repository)/issues/new/label',
|
||||
{ labelNames : labelNames.join(',') },
|
||||
function(data){
|
||||
$('ul.label-list').empty().html(data);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$('a.milestone').click(function(){
|
||||
var title = $(this).data('title');
|
||||
var milestoneId = $(this).data('id');
|
||||
displayMilestone(title, milestoneId);
|
||||
$('input[name=milestoneId]').val(milestoneId);
|
||||
});
|
||||
|
||||
$('a.assign').click(function(){
|
||||
var $this = $(this);
|
||||
var userName = $this.data('name');
|
||||
displayAssignee($this, userName);
|
||||
$('input[name=assignedUserName]').val(userName);
|
||||
});
|
||||
}
|
||||
|
||||
function switchLabel($this){
|
||||
var i = $this.children('i');
|
||||
if(i.hasClass('icon-ok')){
|
||||
i.removeClass().addClass('icon-white');
|
||||
return 'delete';
|
||||
} else {
|
||||
i.removeClass().addClass('icon-ok');
|
||||
return 'new';
|
||||
}
|
||||
}
|
||||
|
||||
function displayMilestone(title, milestoneId, progress){
|
||||
$('a.milestone i.icon-ok').attr('class', 'icon-white');
|
||||
if(milestoneId == ''){
|
||||
$('#label-milestone').html($('<span class="muted small">').text('No milestone'));
|
||||
$('#milestone-progress-area').empty();
|
||||
} else {
|
||||
$('#label-milestone').html($('<span class="strong small">').text(title));
|
||||
if(progress){
|
||||
$('#milestone-progress-area').html(progress);
|
||||
}
|
||||
$('a.milestone[data-id=' + milestoneId + '] i').attr('class', 'icon-ok');
|
||||
}
|
||||
}
|
||||
|
||||
function displayAssignee($this, userName){
|
||||
$('a.assign i.icon-ok').attr('class', 'icon-white');
|
||||
if(userName == ''){
|
||||
$('#label-assigned').html($('<span class="muted small">').text('No one'));
|
||||
} else {
|
||||
$('#label-assigned').empty()
|
||||
.append($this.find('img.avatar-mini').clone(false)).append(' ')
|
||||
.append($('<a class="username strong small">').attr('href', '@context.path/' + userName).text(userName));
|
||||
$('a.assign[data-name=' + jqSelectorEscape(userName) + '] i').attr('class', 'icon-ok');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<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;">
|
||||
<img src="@assets/common/images/label_@(if(label.fontColor == "ffffff") "white" else "black").png" style="width: 12px;"/>
|
||||
<i class="octicon octicon-tag" style="color: #@label.fontColor;"></i>
|
||||
@label.labelName
|
||||
</span>
|
||||
</a>
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
@if(condition.nonEmpty){
|
||||
<div>
|
||||
<a href="@gitbucket.core.service.IssuesService.IssueSearchCondition().toURL" class="header-link">
|
||||
<img src="@assets/common/images/clear.png" class="header-icon"/>
|
||||
<img src="@assets/common/images/clear_hover.png" class="header-icon-hover" style="display: none;"/>
|
||||
<i class="octicon octicon-x" ></i>
|
||||
<span class="strong">Clear current search query, filters, and sorts</span>
|
||||
</a>
|
||||
</div>
|
||||
@@ -28,11 +27,11 @@
|
||||
<input type="checkbox"/>
|
||||
<span class="small">
|
||||
<a class="button-link@if(condition.state == "open"){ selected}" href="@condition.copy(state = "open").toURL">
|
||||
<img src="@assets/common/images/status-open@(if(condition.state == "open"){"-active"}).png"/>
|
||||
<i class="octicon octicon-issue-opened @(if(condition.state == "open"){"active"})"></i>
|
||||
@openCount Open
|
||||
</a>
|
||||
<a class="button-link@if(condition.state == "closed"){ selected}" href="@condition.copy(state = "closed").toURL">
|
||||
<img src="@assets/common/images/status-closed@(if(condition.state == "closed"){"-active"}).png"/>
|
||||
<i class="octicon octicon-check @(if(condition.state == "closed"){"active"})"></i>
|
||||
@closedCount Closed
|
||||
</a>
|
||||
</span>
|
||||
@@ -139,7 +138,7 @@
|
||||
}
|
||||
}
|
||||
@helper.html.dropdown("Assignee", flat = true) {
|
||||
<li><a href="javascript:void(0);" class="toggle-assign" data-name=""><i class="icon-remove-circle"></i> Clear assignee</a></li>
|
||||
<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>
|
||||
}
|
||||
@@ -176,7 +175,7 @@
|
||||
@if(hasWritePermission){
|
||||
<input type="checkbox" value="@issue.issueId"/>
|
||||
}
|
||||
<img src="@assets/common/images/issue-@(if(issue.closed) "closed" else "open").png" style="margin-right: 20px;"/>
|
||||
<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> ・
|
||||
}
|
||||
@@ -195,18 +194,19 @@
|
||||
}
|
||||
@if(commentCount > 0){
|
||||
<a href="@path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">
|
||||
<img src="@assets/common/images/comment-active.png"> @commentCount
|
||||
<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;">
|
||||
<img src="@assets/common/images/comment.png"> @commentCount
|
||||
<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"><img src="@assets/common/images/milestone.png"> @milestone</a></span>
|
||||
<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>
|
||||
@@ -216,4 +216,3 @@
|
||||
<div class="pull-right">
|
||||
@helper.html.paginator(page, (if(condition.state == "open") openCount else closedCount), gitbucket.core.service.IssuesService.IssueLimit, 10, condition.toURL)
|
||||
</div>
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@
|
||||
<th style="background-color: #eee;">
|
||||
<span class="small">
|
||||
<a class="button-link@if(state == "open"){ selected}" href="?state=open">
|
||||
<img src="@assets/common/images/milestone@(if(state == "open"){"-active"}).png"/>
|
||||
<i class="octicon octicon-milestone @(if(state == "open"){"active"})"></i>
|
||||
@milestones.count(_._1.closedDate.isEmpty) Open
|
||||
</a>
|
||||
<a class="button-link@if(state == "closed"){ selected}" href="?state=closed">
|
||||
<img src="@assets/common/images/milestone@(if(state == "closed"){"-active"}).png"/>
|
||||
<i class="octicon octicon-milestone @(if(state == "closed"){"active"})"></i>
|
||||
@milestones.count(_._1.closedDate.isDefined) Closed
|
||||
</a>
|
||||
</span>
|
||||
@@ -38,7 +38,7 @@
|
||||
} else {
|
||||
@milestone.dueDate.map { dueDate =>
|
||||
@if(isPast(dueDate)){
|
||||
<img src="@assets/common/images/alert.png"/><span class="muted milestone-alert">Due by @date(dueDate)</span>
|
||||
<i class="octicon octicon-alert" style="color:#BD2C00;"></i><span class="muted milestone-alert">Due by @date(dueDate)</span>
|
||||
} else {
|
||||
<span class="muted">Due by @date(dueDate)</span>
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<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/octicons/octicons.css" rel="stylesheet">
|
||||
<link href="@assets/vendors/datepicker/css/datepicker.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"/>
|
||||
@@ -40,7 +41,7 @@
|
||||
<form id="search" action="@path/search" method="POST">
|
||||
<div class="navbar">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<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>
|
||||
@@ -51,27 +52,39 @@
|
||||
@defining(AutoUpdate.getCurrentVersion){ version =>
|
||||
<span class="header-version">@version.majorVersion.@version.minorVersion</span>
|
||||
}
|
||||
|
||||
</a>
|
||||
@repository.map { repository =>
|
||||
<input type="text" name="query" style="width: 200px; margin-top: 5px; 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>
|
||||
}
|
||||
<div class="nav-collapse collapse pull-right header-menu">
|
||||
@repository.map { repository =>
|
||||
<input type="text" name="query" style="width: 300px; 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="@url(loginAccount.get.userName)" class="username menu">@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</a>
|
||||
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#"><i class="icon-plus"></i><span class="caret" style="vertical-align: middle;"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="@path/new">New repository</a></li>
|
||||
<li><a href="@path/groups/new">New group</a></li>
|
||||
</ul>
|
||||
<a href="@url(loginAccount.get.userName)/_edit" class="menu" data-toggle="tooltip" data-placement="bottom" title="Account settings"><i class="icon-user" aria-label="Account settings"></i></a>
|
||||
@if(loginAccount.get.isAdmin){
|
||||
<a href="@path/admin/users" class="menu" data-toggle="tooltip" data-placement="bottom" title="Administration"><i class="icon-wrench" aria-label="Administration"></i></a>
|
||||
}
|
||||
<a href="@path/signout" class="menu-last" data-toggle="tooltip" data-placement="bottom" title="Sign out"><i class="icon-share-alt" aria-label="Sign out"></i></a>
|
||||
<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 btn-last" id="signin">Sign in</a>
|
||||
<a href="@path/signin?redirect=@urlEncode(currentPath)" class="btn" id="signin">Sign in</a>
|
||||
}
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
|
||||
@@ -9,16 +9,10 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
|
||||
@sidemenu(path: String, name: String, label: String, count: Int = 0) = {
|
||||
@sidemenu(path: String, name: String, icon: String, label: String, count: Int = 0) = {
|
||||
<li @if(active == name){class="active"} @if(!expand){data-toggle="tooltip" data-placement="left" data-original-title="@label"}>
|
||||
<div class="@if(active == name){margin} else {gradient} pull-left"></div>
|
||||
<a href="@url(repository)@path">
|
||||
@if(active == name){
|
||||
<img src="@assets/common/images/menu-@{name}-active.png">
|
||||
} else {
|
||||
<img src="@assets/common/images/menu-@{name}-active.png" class="menu-icon-active" style="display:none;">
|
||||
<img src="@assets/common/images/menu-@{name}.png" class="menu-icon">
|
||||
}
|
||||
<i class="menu-icon @if(active == name){menu-icon-active} octicon octicon-@{icon} "></i>
|
||||
@if(expand){ @label}
|
||||
@if(expand && count > 0){
|
||||
<div class="pull-right"><span class="label">@count</span></div>
|
||||
@@ -27,13 +21,6 @@
|
||||
</li>
|
||||
}
|
||||
|
||||
@sidemenuPlugin(path: String, name: String, label: String, icon: String) = {
|
||||
<li @if(active == name){class="active"}>
|
||||
<div class="@if(active == name){margin} else {gradient} pull-left"></div>
|
||||
<a href="@url(repository)@path"><img src="@icon"/>@if(expand){ @label}</a>
|
||||
</li>
|
||||
}
|
||||
|
||||
<div class="container">
|
||||
@helper.html.information(info)
|
||||
@helper.html.error(error)
|
||||
@@ -41,7 +28,7 @@
|
||||
<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" class="btn btn-small" style="margin-bottom: 10px;">Fork</a>
|
||||
<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>
|
||||
@@ -51,12 +38,12 @@
|
||||
}
|
||||
<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>
|
||||
@if(loginAccount.isDefined && isNoGroup){
|
||||
<form id="fork-form" method="post" action="@path/@repository.owner/@repository.name/fork">
|
||||
<input type="hidden" name="account" value="@loginAccount.get.userName"/>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
<div class="head">
|
||||
@helper.html.repositoryicon(repository, true)
|
||||
@@ -76,12 +63,12 @@
|
||||
<div style="width: @if(expand){170px} else {40px};" class="pull-right">
|
||||
<ul class="sidemenu">
|
||||
<li style="height: 12px"><div class="gradient pull-left" style="height: 12px"></div></li>
|
||||
@sidemenu("" , "code" , "Code")
|
||||
@sidemenu("/issues", "issues", "Issues", repository.issueCount)
|
||||
@sidemenu("/pulls" , "pulls" , "Pull Requests", repository.pullCount)
|
||||
@sidemenu("/wiki" , "wiki" , "Wiki")
|
||||
@sidemenu("" , "code" , "code" , "Code")
|
||||
@sidemenu("/issues", "issues" , "issue-opened" , "Issues", repository.issueCount)
|
||||
@sidemenu("/pulls" , "pulls" , "git-pull-request" , "Pull Requests", repository.pullCount)
|
||||
@sidemenu("/wiki" , "wiki" , "book" , "Wiki")
|
||||
@if(loginAccount.isDefined && (loginAccount.get.isAdmin || repository.managers.contains(loginAccount.get.userName))){
|
||||
@sidemenu("/settings", "settings", "Settings")
|
||||
@sidemenu("/settings" , "settings" , "tools", "Settings")
|
||||
}
|
||||
<li style="height: 12px"><div class="gradient pull-left" style="height: 12px"></div></li>
|
||||
</ul>
|
||||
@@ -99,10 +86,10 @@
|
||||
}
|
||||
@id.map { id =>
|
||||
<div style="margin-top: 10px;">
|
||||
<a href="@{url(repository)}/archive/@{encodeRefName(id)}.zip" class="btn btn-small" style="width: 147px;"><i class="icon-download-alt"></i>Download ZIP</a>
|
||||
<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>
|
||||
</div>
|
||||
<div style="margin-top: 10px;">
|
||||
<a href="@{url(repository)}/archive/@{encodeRefName(id)}.tar.gz" class="btn btn-small" style="width: 147px;"><i class="icon-download-alt"></i>Download TAR.GZ</a>
|
||||
<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>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -112,27 +99,24 @@
|
||||
@repository.repository.description.map { description =>
|
||||
<p class="description">@description</p>
|
||||
}
|
||||
<div style="border: 1px solid #eee; padding: 4px; margin-bottom: 10px;">
|
||||
<div style="margin-bottom: 10px;" class="box-content">
|
||||
<table class="fill-width">
|
||||
<tr>
|
||||
<td style="width: 33%; text-align: center;">
|
||||
<a href="@url(repository)/commits/@encodeRefName(id.getOrElse(""))" class="header-link">
|
||||
<img src="@assets/common/images/header-commits-hover.png" class="header-icon-hover" style="display: none;"/>
|
||||
<img src="@assets/common/images/header-commits.png" class="header-icon"/>
|
||||
<i class="octicon octicon-history"></i>
|
||||
<strong>@repository.commitCount</strong> commits
|
||||
</a>
|
||||
</td>
|
||||
<td style="width: 33%; text-align: center;">
|
||||
<a href="@url(repository)/branches" class="header-link" class="header-link">
|
||||
<img src="@assets/common/images/header-branches-hover.png" class="header-icon-hover" style="display: none;"/>
|
||||
<img src="@assets/common/images/header-branches.png" class="header-icon"/>
|
||||
<i class="octicon octicon-git-branch"></i>
|
||||
<strong>@repository.branchList.length</strong> branches
|
||||
</a>
|
||||
</td>
|
||||
<td style="width: 33%; text-align: center;">
|
||||
<a href="@url(repository)/tags" class="header-link" class="header-link">
|
||||
<img src="@assets/common/images/header-tags-hover.png" class="header-icon-hover" style="display: none;"/>
|
||||
<img src="@assets/common/images/header-tags.png" class="header-icon"/>
|
||||
<i class="octicon octicon-tag"></i>
|
||||
<strong>@repository.tags.length</strong> releases
|
||||
</a>
|
||||
</td>
|
||||
@@ -145,44 +129,21 @@
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
$('a.header-link').mouseover(function(e){
|
||||
var target = e.target;
|
||||
if(e.target.tagName != 'A'){
|
||||
target = e.target.parentElement;
|
||||
}
|
||||
$(target).children('strong' ).css('color', '#0088cc');
|
||||
$(target).children('img.header-icon-hover').css('display', 'inline');
|
||||
$(target).children('img.header-icon' ).css('display', 'none');
|
||||
});
|
||||
|
||||
$('a.header-link').mouseout(function(e){
|
||||
var target = e.target;
|
||||
if(e.target.tagName != 'A'){
|
||||
target = e.target.parentElement;
|
||||
}
|
||||
$(target).children('strong' ).css('color', 'black');
|
||||
$(target).children('img.header-icon-hover').css('display', 'none');
|
||||
$(target).children('img.header-icon' ).css('display', 'inline');
|
||||
});
|
||||
|
||||
$('ul.sidemenu a').mouseover(function(e){
|
||||
var target = e.target;
|
||||
if(e.target.tagName == "IMG"){
|
||||
if(e.target.tagName == "I"){
|
||||
target = e.target.parentElement;
|
||||
}
|
||||
$(target).prev ('div.gradient' ).css('border-left', '1px solid silver');
|
||||
$(target).children('img.menu-icon-active').css('display', 'inline');
|
||||
$(target).children('img.menu-icon' ).css('display', 'none');
|
||||
});
|
||||
|
||||
$('ul.sidemenu a').mouseout(function(e){
|
||||
var target = e.target;
|
||||
if(e.target.tagName == "IMG"){
|
||||
if(e.target.tagName == "I"){
|
||||
target = e.target.parentElement;
|
||||
}
|
||||
$(target).prev ('div.gradient' ).css('border-left', '1px solid #eee');
|
||||
$(target).children('img.menu-icon-active').css('display', 'none');
|
||||
$(target).children('img.menu-icon' ).css('display', 'inline');
|
||||
});
|
||||
|
||||
$('a[rel*=facebox]').facebox({
|
||||
|
||||
@@ -4,32 +4,42 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@import gitbucket.core.model._
|
||||
<div class="box">
|
||||
<table class="table table-file-list" style="border: 1px solid silver;">
|
||||
@commits.map { day =>
|
||||
<tr>
|
||||
<th colspan="4" class="box-header" style="font-weight: normal;">@date(day.head.commitTime)</th>
|
||||
</tr>
|
||||
@day.map { commit =>
|
||||
<tr>
|
||||
<td style="width: 20%;">
|
||||
@avatar(commit, 20)
|
||||
@user(commit.authorName, commit.authorEmailAddress, "username")
|
||||
</td>
|
||||
<td>@commit.shortMessage</td>
|
||||
<td style="width: 10%; text-align: right">
|
||||
<span class="badge" style="display: inline">@if(comments.isDefined){
|
||||
@comments.get.flatMap @{
|
||||
case comment: CommitComment => Some(comment)
|
||||
case other => None
|
||||
}.count(t => t.commitId == commit.id && !t.pullRequest)
|
||||
}</span>
|
||||
</td>
|
||||
<td style="width: 10%; text-align: right;">
|
||||
<a href="@url(repository)/commit/@commit.id" class="monospace">@commit.id.substring(0, 7)</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
<div class="commit-list">
|
||||
@commits.map { day =>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -9,18 +9,16 @@
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
originRepository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
forkedRepository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||
hasOriginWritePermission: Boolean,
|
||||
collaborators: List[String],
|
||||
milestones: List[gitbucket.core.model.Milestone],
|
||||
labels: List[gitbucket.core.model.Label])(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"Pull Requests - ${repository.owner}/${repository.name}", Some(repository)){
|
||||
@html.menu("pulls", repository){
|
||||
<div class="pullreq-info">
|
||||
<div id="compare-info">
|
||||
<a href="#" id="edit-compare-condition" class="btn btn-mini pull-right">Edit</a>
|
||||
<span class="label label-info monospace">@originRepository.owner:@originId</span> ... <span class="label label-info monospace">@forkedRepository.owner:@forkedId</span>
|
||||
</div>
|
||||
<div id="compare-edit" style="display: none;">
|
||||
<a href="#" id="cancel-condition-editing" class="pull-right"><i class="icon-remove-circle"></i></a>
|
||||
<div id="compare-edit">
|
||||
@helper.html.dropdown(originRepository.owner + "/" + originRepository.name, "base fork") {
|
||||
@members.map { case (owner, name) =>
|
||||
<li><a href="#" class="origin-owner" data-owner="@owner" data-name="@name">@helper.html.checkicon(owner == originRepository.owner) @owner/@name</a></li>
|
||||
@@ -43,33 +41,52 @@
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if(commits.nonEmpty && hasWritePermission){
|
||||
<div style="margin-bottom: 10px;" id="create-pull-request">
|
||||
<a href="#" class="btn btn-success" id="show-form">Create pull request</a>
|
||||
<div class="check-conflict" style="display: none;">
|
||||
<img src="@assets/common/images/indicator.gif"/> Checking...
|
||||
</div>
|
||||
<div id="pull-request-form" class="box" style="display: none;">
|
||||
<div class="box-content">
|
||||
<form method="POST" action="@path/@originRepository.owner/@originRepository.name/pulls/new" validate="true">
|
||||
<div style="width: 240px; position: absolute; margin-left: 610px;">
|
||||
<div class="check-conflict" style="display: none;">
|
||||
<img src="@assets/common/images/indicator.gif"/> Checking...
|
||||
</div>
|
||||
@if(commits.nonEmpty && loginAccount.isDefined){
|
||||
<div style="margin-bottom: 10px; padding: 8px; background-color: #fff9ea" id="create-pull-request" class="box-content">
|
||||
<a href="#" class="btn btn-success" id="show-form">Create pull request</a>
|
||||
|
||||
<span class="muted">Discuss and review the changes in this comparison with others.</span>
|
||||
</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="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
|
||||
<div class="box issue-box">
|
||||
<div class="box-content">
|
||||
<span class="error" id="error-title"></span>
|
||||
<input type="text" name="title" style="width: 690px" placeholder="Title"/>
|
||||
@helper.html.preview(
|
||||
repository = repository,
|
||||
content = "",
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = true,
|
||||
style = "width: 690px; height: 200px;"
|
||||
)
|
||||
<input type="hidden" name="targetUserName" value="@originRepository.owner"/>
|
||||
<input type="hidden" name="targetBranch" value="@originId"/>
|
||||
<input type="hidden" name="requestUserName" value="@forkedRepository.owner"/>
|
||||
<input type="hidden" name="requestRepositoryName" value="@forkedRepository.name"/>
|
||||
<input type="hidden" name="requestBranch" value="@forkedId"/>
|
||||
<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"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 600px; border-right: 1px solid #d4d4d4;">
|
||||
<span class="error" id="error-title"></span>
|
||||
<input type="text" name="title" style="width: 580px" placeholder="Title"/>
|
||||
@helper.html.preview(repository, "", false, true, true, hasWritePermission, "width: 580px; height: 200px;")
|
||||
<input type="hidden" name="targetUserName" value="@originRepository.owner"/>
|
||||
<input type="hidden" name="targetBranch" value="@originId"/>
|
||||
<input type="hidden" name="requestUserName" value="@forkedRepository.owner"/>
|
||||
<input type="hidden" name="requestRepositoryName" value="@forkedRepository.name"/>
|
||||
<input type="hidden" name="requestBranch" value="@forkedId"/>
|
||||
<input type="hidden" name="commitIdFrom" value="@sourceId"/>
|
||||
<input type="hidden" name="commitIdTo" value="@commitId"/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="span2">
|
||||
@issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map((_, 0, 0)), labels, hasOriginWritePermission, repository)
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
@if(commits.isEmpty){
|
||||
@@ -82,72 +99,119 @@
|
||||
</tr>
|
||||
</table>
|
||||
} else {
|
||||
@pulls.html.commits(commits, Some(comments), repository)
|
||||
@helper.html.diff(diffs, repository, Some(commitId), Some(sourceId), true, None, hasWritePermission, false)
|
||||
<div style="margin-bottom: 10px;" class="box-content">
|
||||
<table class="fill-width">
|
||||
<tr>
|
||||
<td style="width: 25%; text-align: center;">
|
||||
<i class="octicon octicon-commit"></i>
|
||||
@defining(commits.flatten){ commits =>
|
||||
<strong>@commits.size</strong> @plural(commits.size, "commit")
|
||||
}
|
||||
</td>
|
||||
<td style="width: 25%; text-align: center;">
|
||||
<i class="octicon octicon-diff"></i>
|
||||
<strong>@diffs.size</strong> @plural(diffs.size, "file") changed
|
||||
</td>
|
||||
<td style="width: 25%; text-align: center;">
|
||||
<i class="octicon octicon-comment"></i>
|
||||
@defining(comments.collect { case c: gitbucket.core.model.CommitComment => c }){ comments =>
|
||||
<strong>@comments.size</strong> commit @plural(comments.size, "comment")
|
||||
}
|
||||
</td>
|
||||
<td style="width: 25%; text-align: center;">
|
||||
<i class="octicon octicon-organization"></i>
|
||||
@defining(commits.flatMap(_.map(_.authorEmailAddress)).distinct){ contributors =>
|
||||
<strong>@contributors.size</strong> @plural(contributors.size, "contributor")
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="box" style="margin-bottom: 20px;">
|
||||
@commits.map { day =>
|
||||
<div style="margin-top: 8px; margin-bottom: 8px;" class="muted">
|
||||
Commits on @date(day.head.commitTime)
|
||||
</div>
|
||||
<table style="width: 100%;">
|
||||
@day.map { commit =>
|
||||
<tr>
|
||||
<td style="width: 20%;">
|
||||
<i class="octicon octicon-git-commit"></i>
|
||||
@avatar(commit, 20)
|
||||
@user(commit.authorName, commit.authorEmailAddress, "username strong")
|
||||
</td>
|
||||
<td><span class="monospace">@commit.shortMessage</span></td>
|
||||
@*
|
||||
<span class="badge" style="display: inline">@if(comments.isDefined){
|
||||
@comments.get.flatMap @{
|
||||
case comment: CommitComment => Some(comment)
|
||||
case other => None
|
||||
}.count(t => t.commitId == commit.id && !t.pullRequest)
|
||||
}</span>
|
||||
*@
|
||||
<td style="width: 10%; text-align: right;">
|
||||
<a href="@url(repository)/commit/@commit.id" class="monospace">@commit.id.substring(0, 7)</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
</div>
|
||||
@helper.html.diff(diffs, repository, Some(commitId), Some(sourceId), true, None, false, false)
|
||||
<p>Showing you all comments on commits in this comparison.</p>
|
||||
@issues.html.commentlist(None, comments, hasWritePermission, repository, None)
|
||||
@issues.html.commentlist(None, comments, false, repository, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
<script>
|
||||
$(function(){
|
||||
$('#edit-compare-condition').click(function(){
|
||||
$('#compare-info').hide();
|
||||
$('#compare-edit').show();
|
||||
});
|
||||
|
||||
$('#cancel-condition-editing').click(function(){
|
||||
$('#compare-info').show();
|
||||
$('#compare-edit').hide();
|
||||
});
|
||||
|
||||
$('a.origin-owner, a.forked-owner, a.origin-branch, a.forked-branch').click(function(){
|
||||
var e = $(this);
|
||||
e.parents('ul').find('i').attr('class', 'icon-white');
|
||||
e.find('i').attr('class', 'icon-ok');
|
||||
e.find('i').attr('class', 'octicon-check');
|
||||
e.parents('div.btn-group').find('button span.strong').text(e.text());
|
||||
|
||||
@if(members.isEmpty){
|
||||
location.href = '@url(repository)/compare/' +
|
||||
$.trim($('i.icon-ok').parents('a.origin-branch').data('branch')) + '...' +
|
||||
$.trim($('i.icon-ok').parents('a.forked-branch').data('branch'));
|
||||
$.trim($('i.octicon-check').parents('a.origin-branch').data('branch')) + '...' +
|
||||
$.trim($('i.octicon-check').parents('a.forked-branch').data('branch'));
|
||||
} else {
|
||||
location.href = '@path/' +
|
||||
$.trim($('i.icon-ok').parents('a.forked-owner' ).data('owner')) + '/' +
|
||||
$.trim($('i.icon-ok').parents('a.forked-owner' ).data('name')) +'/compare/' +
|
||||
$.trim($('i.icon-ok').parents('a.origin-owner' ).data('owner')) + ':' +
|
||||
$.trim($('i.icon-ok').parents('a.origin-branch').data('branch')) + '...' +
|
||||
$.trim($('i.icon-ok').parents('a.forked-owner' ).data('owner')) + ':' +
|
||||
$.trim($('i.icon-ok').parents('a.forked-branch').data('branch'));
|
||||
$.trim($('i.octicon-check').parents('a.forked-owner' ).data('owner')) + '/' +
|
||||
$.trim($('i.octicon-check').parents('a.forked-owner' ).data('name')) +'/compare/' +
|
||||
$.trim($('i.octicon-check').parents('a.origin-owner' ).data('owner')) + ':' +
|
||||
$.trim($('i.octicon-check').parents('a.origin-branch').data('branch')) + '...' +
|
||||
$.trim($('i.octicon-check').parents('a.forked-owner' ).data('owner')) + ':' +
|
||||
$.trim($('i.octicon-check').parents('a.forked-branch').data('branch'));
|
||||
}
|
||||
});
|
||||
|
||||
$('#show-form').click(function(){
|
||||
$(this).hide();
|
||||
$('#create-pull-request').hide();
|
||||
$('#pull-request-form').show();
|
||||
});
|
||||
if(location.search.substr(1).split("&").indexOf("expand=1")!=-1){
|
||||
$('#show-form').click();
|
||||
}
|
||||
|
||||
@if(hasWritePermission){
|
||||
function checkConflict(from, to, noConflictHandler, hasConflictHandler){
|
||||
@if(loginAccount.isDefined){
|
||||
function checkConflict(from, to){
|
||||
$('.check-conflict').show();
|
||||
$.get('@url(repository)/compare/' + from + '...' + to + '/mergecheck',
|
||||
$.get('@url(forkedRepository)/compare/' + from + '...' + to + '/mergecheck',
|
||||
function(data){ $('.check-conflict').html(data); });
|
||||
}
|
||||
|
||||
@if(members.isEmpty){
|
||||
checkConflict(
|
||||
$.trim($('i.icon-ok').parents('a.origin-branch').data('branch')),
|
||||
$.trim($('i.icon-ok').parents('a.forked-branch').data('branch'))
|
||||
$.trim($('i.octicon-check').parents('a.origin-branch').data('branch')),
|
||||
$.trim($('i.octicon-check').parents('a.forked-branch').data('branch'))
|
||||
);
|
||||
} else {
|
||||
checkConflict(
|
||||
$.trim($('i.icon-ok').parents('a.origin-owner' ).data('owner')) + ":" +
|
||||
$.trim($('i.icon-ok').parents('a.origin-branch').data('branch')),
|
||||
$.trim($('i.icon-ok').parents('a.forked-owner' ).data('owner')) + ":" +
|
||||
$.trim($('i.icon-ok').parents('a.forked-branch').data('branch'))
|
||||
$.trim($('i.octicon-check').parents('a.origin-owner' ).data('owner')) + ":" +
|
||||
$.trim($('i.octicon-check').parents('a.origin-branch').data('branch')),
|
||||
$.trim($('i.octicon-check').parents('a.forked-owner' ).data('owner')) + ":" +
|
||||
$.trim($('i.octicon-check').parents('a.forked-branch').data('branch'))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="span2">
|
||||
@issues.html.issueinfo(issue, comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
||||
@issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
@(hasConflict: Boolean)
|
||||
@if(hasConflict){
|
||||
<h4>We can’t automatically merge these branches</h4>
|
||||
<p>Don't worry, you can still submit the pull request.</p>
|
||||
} else {
|
||||
<h4 style="color: #468847;">Able to merge</h4>
|
||||
<p>These branches can be automatically merged.</p>
|
||||
}
|
||||
<input type="submit" class="btn btn-success btn-block" value="Create pull request"/>
|
||||
<div style="margin-top: 8px;">
|
||||
@if(hasConflict){
|
||||
<i class="octicon octicon-x" style="color: #bd2c00"></i>
|
||||
<span class="strong" style="color: #bd2c00;">Can’t automatically merge.</span>
|
||||
<span class="muted">Don’t worry, you can still create the pull request.</span>
|
||||
} else {
|
||||
<i class="octicon octicon-check" style="color: #6cc644"></i>
|
||||
<span class="strong" style="color: #6cc644">Able to merge.</span>
|
||||
<span class="muted">These branches can be automatically merged.</span>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
issue: gitbucket.core.model.Issue,
|
||||
pullreq: gitbucket.core.model.PullRequest,
|
||||
statuses: List[model.CommitStatus],
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
requestRepositoryUrl: String)(implicit context: gitbucket.core.controller.Context)
|
||||
originRepository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
forkedRepository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||
@import gitbucket.core.service.SystemSettingsService
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@import model.CommitState
|
||||
@@ -22,100 +23,101 @@
|
||||
@status.description.map{ desc => <span class="muted">— @desc</span> }
|
||||
</div>
|
||||
}
|
||||
}else{
|
||||
@defining(statuses.groupBy(_.state)){ stateMap => @defining(CommitState.combine(stateMap.keySet)){ state =>
|
||||
<div class="build-status-item">
|
||||
<a class="pull-right" id="toggle-all-checks"></a>
|
||||
<span class="build-status-icon text-@{state.name}">@commitStateIcon(state)</span>
|
||||
<strong class="text-@{state.name}">@commitStateText(state, pullreq.commitIdTo)</strong>
|
||||
<span class="text-@{state.name}">— @{stateMap.map{ case (keyState, states) => states.size+" "+keyState.name }.mkString(", ")} checks</span>
|
||||
</div>
|
||||
<div class="build-statuses-list" style="@if(state==CommitState.SUCCESS){ display:none; }else{ }">
|
||||
@statuses.map{ status =>
|
||||
} else {
|
||||
@defining(statuses.groupBy(_.state)){ stateMap =>
|
||||
@defining(CommitState.combine(stateMap.keySet)){ state =>
|
||||
<div class="build-status-item">
|
||||
@status.targetUrl.map{ url => <a class="pull-right" href="@url">Details</a> }
|
||||
<span class="build-status-icon text-@{status.state.name}">@commitStateIcon(status.state)</span>
|
||||
<span class="text-@{status.state.name}">@status.context</span>
|
||||
@status.description.map{ desc => <span class="muted">— @desc</span> }
|
||||
<a class="pull-right" id="toggle-all-checks"></a>
|
||||
<span class="build-status-icon text-@{state.name}">@commitStateIcon(state)</span>
|
||||
<strong class="text-@{state.name}">@commitStateText(state, pullreq.commitIdTo)</strong>
|
||||
<span class="text-@{state.name}">— @{stateMap.map{ case (keyState, states) => states.size+" "+keyState.name }.mkString(", ")} checks</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} }
|
||||
<div class="build-statuses-list" style="@if(state==CommitState.SUCCESS){ display:none; }else{ }">
|
||||
@statuses.map{ status =>
|
||||
<div class="build-status-item">
|
||||
@status.targetUrl.map{ url => <a class="pull-right" href="@url">Details</a> }
|
||||
<span class="build-status-icon text-@{status.state.name}">@commitStateIcon(status.state)</span>
|
||||
<span class="text-@{status.state.name}">@status.context</span>
|
||||
@status.description.map{ desc => <span class="muted">— @desc</span> }
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</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"}/>
|
||||
</div>
|
||||
<div>
|
||||
@if(hasConflict){
|
||||
<span class="strong">We can’t automatically merge this pull request.</span>
|
||||
} else{ @if(hasProblem){
|
||||
<span class="strong">Merge with caution!</span>
|
||||
} else {
|
||||
<span class="strong">This pull request can be automatically merged.</span>
|
||||
} }
|
||||
</div>
|
||||
<div class="small">
|
||||
@if(hasConflict){
|
||||
<a href="#" id="show-command-line">Use the command line</a> to resolve conflicts before continuing.
|
||||
} else {
|
||||
You can also merge branches on the <a href="#" id="show-command-line">command line</a>.
|
||||
}
|
||||
</div>
|
||||
<div id="command-line" style="display: none;">
|
||||
<hr>
|
||||
@if(hasConflict){
|
||||
<span class="strong">Checkout via command line</span>
|
||||
<p>
|
||||
If you cannot merge a pull request automatically here, you have the option of checking
|
||||
it out via command line to resolve conflicts and perform a manual merge.
|
||||
</p>
|
||||
} else {
|
||||
<span class="strong">Merging via command line</span>
|
||||
<p>
|
||||
If you do not want to use the merge button or an automatic merge cannot be performed,
|
||||
you can perform a manual merge on the command line.
|
||||
</p>
|
||||
}
|
||||
@helper.html.copy("repository-url-copy", requestRepositoryUrl){
|
||||
<input type="text" style="width: 500px;" value="@requestRepositoryUrl" id="repository-url" readonly>
|
||||
}
|
||||
<div>
|
||||
<p>
|
||||
<span class="strong">Step 1:</span> Check out a new branch to test the changes — run this from your project directory
|
||||
</p>
|
||||
@defining(s"git checkout -b ${pullreq.requestUserName}-${pullreq.requestBranch} ${pullreq.branch}"){ command =>
|
||||
@helper.html.copy("merge-command-copy-1", command){
|
||||
<pre style="width: 500px; float: left;">@command</pre>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span class="strong">Step 2:</span> Bring in @{pullreq.requestUserName}'s changes and test
|
||||
</p>
|
||||
@defining(s"git pull ${requestRepositoryUrl} ${pullreq.requestBranch}"){ command =>
|
||||
@helper.html.copy("merge-command-copy-2", command){
|
||||
<pre style="width: 500px; float: left;">@command</pre>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span class="strong">Step 3:</span> Merge the changes and update the server
|
||||
</p>
|
||||
@defining(s"git checkout ${pullreq.branch}\ngit merge ${pullreq.requestUserName}-${pullreq.requestBranch}\ngit push origin ${pullreq.branch}"){ command =>
|
||||
@helper.html.copy("merge-command-copy-3", command){
|
||||
<pre style="width: 500px; float: left;">@command</pre>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</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"}/>
|
||||
</div>
|
||||
<div>
|
||||
@if(hasConflict){
|
||||
<span class="strong">We can’t automatically merge this pull request.</span>
|
||||
} else {
|
||||
@if(hasProblem){
|
||||
<span class="strong">Merge with caution!</span>
|
||||
} else {
|
||||
<span class="strong">This pull request can be automatically merged.</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="small">
|
||||
@if(hasConflict){
|
||||
<a href="#" id="show-command-line">Use the command line</a> to resolve conflicts before continuing.
|
||||
} else {
|
||||
You can also merge branches on the <a href="#" id="show-command-line">command line</a>.
|
||||
}
|
||||
</div>
|
||||
<div id="command-line" style="display: none;">
|
||||
<hr>
|
||||
@if(hasConflict){
|
||||
<span class="strong">Checkout via command line</span>
|
||||
<p>
|
||||
If you cannot merge a pull request automatically here, you have the option of checking
|
||||
it out via command line to resolve conflicts and perform a manual merge.
|
||||
</p>
|
||||
} else {
|
||||
<span class="strong">Merging via command line</span>
|
||||
<p>
|
||||
If you do not want to use the merge button or an automatic merge cannot be performed,
|
||||
you can perform a manual merge on the command line.
|
||||
</p>
|
||||
}
|
||||
@helper.html.copy("repository-url-copy", forkedRepository.httpUrl, true){
|
||||
<div class="btn-group" data-toggle="buttons-radio">
|
||||
<button class="btn btn-small active" type="button" id="repository-url-http">HTTP</button>
|
||||
@if(settings.ssh && loginAccount.isDefined){
|
||||
<button class="btn btn-small" type="button" id="repository-url-ssh" style="border-radius: 0px;">SSH</button>
|
||||
}
|
||||
</div>
|
||||
<input type="text" style="width: 500px;" value="@forkedRepository.httpUrl" id="repository-url" readonly>
|
||||
}
|
||||
<div>
|
||||
<p>
|
||||
<span class="strong">Step 1:</span> From your project repository, check out a new branch and test the changes.
|
||||
</p>
|
||||
@defining(s"git checkout -b ${pullreq.requestUserName}-${pullreq.requestBranch} ${pullreq.branch}\n" +
|
||||
s"git pull ${forkedRepository.httpUrl} ${pullreq.requestBranch}"){ command =>
|
||||
@helper.html.copy("merge-command-copy-1", command){
|
||||
<pre style="width: 600px; float: left; font-size: 12px; border-radius: 3px 0px 3px 3px;" id="merge-command">@Html(command)</pre>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<span class="strong">Step 2:</span> Merge the changes and update on the server.
|
||||
</p>
|
||||
@defining(s"git checkout ${pullreq.branch}\ngit merge --no-ff ${pullreq.requestUserName}-${pullreq.requestBranch}\n" +
|
||||
s"git push origin ${pullreq.branch}"){ command =>
|
||||
@helper.html.copy("merge-command-copy-2", command){
|
||||
<pre style="width: 600px; float: left; font-size: 12px; border-radius: 3px 0px 3px 3px;">@command</pre>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="confirm-merge-form" style="display: none;">
|
||||
<form method="POST" action="@url(repository)/pull/@pullreq.issueId/merge">
|
||||
<form method="POST" action="@url(originRepository)/pull/@pullreq.issueId/merge">
|
||||
<div class="strong">
|
||||
Merge pull request #@issue.issueId from @{pullreq.requestUserName}/@{pullreq.requestBranch}
|
||||
</div>
|
||||
@@ -149,5 +151,31 @@ $(function(){
|
||||
$('#merge-pull-request').hide();
|
||||
$('#confirm-merge-form').show();
|
||||
});
|
||||
|
||||
@if(settings.ssh && loginAccount.isDefined){
|
||||
$('#repository-url-http').click(function(){
|
||||
// Update URL box
|
||||
$('#repository-url').val('@forkedRepository.httpUrl');
|
||||
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
|
||||
// Update command guidance
|
||||
$('#merge-command').text($('#merge-command').text().replace(
|
||||
'@forkedRepository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)',
|
||||
'@forkedRepository.httpUrl'
|
||||
));
|
||||
$('#merge-command-copy-1').attr('data-clipboard-text', $('#merge-command').text());
|
||||
});
|
||||
|
||||
$('#repository-url-ssh').click(function(){
|
||||
// Update URL box
|
||||
$('#repository-url').val('@forkedRepository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)');
|
||||
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
|
||||
// Update command guidance
|
||||
$('#merge-command').text($('#merge-command').text().replace(
|
||||
'@forkedRepository.httpUrl',
|
||||
'@forkedRepository.sshUrl(settings.sshPort.getOrElse(SystemSettingsService.DefaultSshPort), loginAccount.get.userName)'
|
||||
));
|
||||
$('#merge-command-copy-1').attr('data-clipboard-text', $('#merge-command').text());
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -18,9 +18,9 @@
|
||||
<div>
|
||||
<div class="show-title pull-right">
|
||||
@if(hasWritePermission || loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
|
||||
<a class="btn btn-small" href="#" id="edit">Edit</a>
|
||||
<a class="btn" href="#" id="edit">Edit</a>
|
||||
}
|
||||
<a class="btn btn-small btn-success" href="@url(repository)/issues/new">New issue</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>
|
||||
|
||||
@@ -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="icon icon-th-list"></i></a>
|
||||
<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>
|
||||
</div></div>
|
||||
<div class="line-age-legend">
|
||||
<span>Newer</span>
|
||||
@@ -47,57 +47,50 @@
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered blobview">
|
||||
<tr>
|
||||
<th style="font-weight: normal;">
|
||||
<div class="pull-left">
|
||||
@avatar(latestCommit, 20)
|
||||
@user(latestCommit.authorName, latestCommit.authorEmailAddress, "username strong")
|
||||
<span class="muted">@helper.html.datetimeago(latestCommit.commitTime)</span>
|
||||
<a href="@url(repository)/commit/@latestCommit.id" class="commit-message">@link(latestCommit.summary, repository)</a>
|
||||
</div>
|
||||
<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-mini" href="?raw=true">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-mini" href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")">History</a>
|
||||
@if(hasWritePermission){
|
||||
<a class="btn btn-mini btn-danger" href="@url(repository)/remove/@encodeRefName(branch)/@pathList.mkString("/")">Delete</a>
|
||||
}
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
@if(content.viewType == "text"){
|
||||
@defining(isRenderable(pathList.reverse.head)){ isRrenderable =>
|
||||
@if(!isBlame && isRrenderable) {
|
||||
<div class="box-content markdown-body" style="border: none; padding-left: 16px; padding-right: 16px;">
|
||||
@renderMarkup(pathList, content.content.get, branch, repository, false, false)
|
||||
</div>
|
||||
} else {
|
||||
<pre class="prettyprint linenums blob @if(!isRrenderable){ no-renderable } ">@content.content.get</pre>
|
||||
}
|
||||
}
|
||||
}
|
||||
@if(content.viewType == "image"){
|
||||
<img src="?raw=true"/>
|
||||
}
|
||||
@if(content.viewType == "large" || content.viewType == "binary"){
|
||||
<div style="text-align: center; padding-top: 20px; padding-bottom: 20px;">
|
||||
<a href="?raw=true">View Raw</a><br>
|
||||
<br>
|
||||
(Sorry about that, but we can't show files that are this big right now)
|
||||
<div class="box-header">
|
||||
@avatar(latestCommit, 20)
|
||||
@user(latestCommit.authorName, latestCommit.authorEmailAddress, "username strong")
|
||||
<span class="muted">@helper.html.datetimeago(latestCommit.commitTime)</span>
|
||||
<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-mini" href="?raw=true">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-mini" 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>
|
||||
}
|
||||
</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;">
|
||||
@renderMarkup(pathList, content.content.get, branch, repository, false, false, true)
|
||||
</div>
|
||||
} else {
|
||||
<div class="box-content-bottom">
|
||||
<pre class="prettyprint linenums blob @if(!isRrenderable){ no-renderable } ">@content.content.get</pre>
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
}
|
||||
}
|
||||
@if(content.viewType == "image"){
|
||||
<div class="box-content-bottom">
|
||||
<img src="?raw=true"/>
|
||||
</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>
|
||||
<br>
|
||||
(Sorry about that, but we can't show files that are this big right now)
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
<script src="@assets/vendors/jquery/jquery.ba-hashchange.js"></script>
|
||||
@@ -111,36 +104,38 @@ $(window).load(function(){
|
||||
function updateSourceLineNum(){
|
||||
$('.source-line-num').remove();
|
||||
var pos = pre.find('ol.linenums').position();
|
||||
$('<div class="source-line-num">').css({
|
||||
height:pre.height(),
|
||||
width:'48px',
|
||||
cursor:'pointer',
|
||||
position: 'absolute',
|
||||
top : pos.top + 'px',
|
||||
left : pos.left + 'px'
|
||||
}).click(function(e){
|
||||
$(window).hashchange(function(){})
|
||||
var pos = $(this).data("pos");
|
||||
if(!pos){
|
||||
pos = $('ol.linenums li').map(function(){ return {id:$(this).attr("id"),top:$(this).position().top} }).toArray();
|
||||
$(this).data("pos",pos);
|
||||
}
|
||||
for(var i=0;i<pos.length-1;i++){
|
||||
if(pos[i+1].top>e.pageY){
|
||||
break;
|
||||
if(pos){
|
||||
$('<div class="source-line-num">').css({
|
||||
height : pre.height(),
|
||||
width : '48px',
|
||||
cursor : 'pointer',
|
||||
position: 'absolute',
|
||||
top : pos.top + 'px',
|
||||
left : pos.left + 'px'
|
||||
}).click(function(e){
|
||||
$(window).hashchange(function(){})
|
||||
var pos = $(this).data("pos");
|
||||
if(!pos){
|
||||
pos = $('ol.linenums li').map(function(){ return { id: $(this).attr("id"), top: $(this).position().top} }).toArray();
|
||||
$(this).data("pos",pos);
|
||||
}
|
||||
}
|
||||
var line = pos[i].id.replace(/^L/,'');
|
||||
var hash = location.hash;
|
||||
if(e.shiftKey == true && hash.match(/#L\d+(-L\d+)?/)){
|
||||
var lines = hash.split('-');
|
||||
location.hash = lines[0] + '-L' + line;
|
||||
} else {
|
||||
var p = $("#L"+line).attr('id',"");
|
||||
location.hash = '#L' + line;
|
||||
p.attr('id','L'+line);
|
||||
}
|
||||
}).appendTo(pre);
|
||||
for(var i = 0; i < pos.length-1; i++){
|
||||
if(pos[i + 1].top > e.pageY){
|
||||
break;
|
||||
}
|
||||
}
|
||||
var line = pos[i].id.replace(/^L/,'');
|
||||
var hash = location.hash;
|
||||
if(e.shiftKey == true && hash.match(/#L\d+(-L\d+)?/)){
|
||||
var lines = hash.split('-');
|
||||
location.hash = lines[0] + '-L' + line;
|
||||
} else {
|
||||
var p = $("#L"+line).attr('id',"");
|
||||
location.hash = '#L' + line;
|
||||
p.attr('id','L'+line);
|
||||
}
|
||||
}).appendTo(pre);
|
||||
}
|
||||
}
|
||||
var repository = $('.blame-action').data('repository');
|
||||
$('.blame-action').click(function(e){
|
||||
@@ -152,11 +147,11 @@ $(window).load(function(){
|
||||
});
|
||||
|
||||
function updateBlame(){
|
||||
var m = /^\/(blame|blob)(\/.*)$/.exec(location.href.substring(repository.length));
|
||||
var m = /\/(blame|blob)(\/.*)$/.exec(location.href);
|
||||
var mode = m[1];
|
||||
$('.blame-action').toggleClass("active", mode=='blame').attr('href', repository + (m[1]=='blame'?'/blob':'/blame')+m[2]);
|
||||
if(pre.parents("td").find(".blame").length){
|
||||
pre.parents("div.container").toggleClass("blame-container", mode=='blame');
|
||||
$('.blame-action').toggleClass("active", mode=='blame').attr('href', repository + (m[1] == 'blame' ? '/blob' : '/blame') + m[2]);
|
||||
if(pre.parents("div.box-content-bottom").find(".blame").length){
|
||||
pre.parents("div.container").toggleClass("blame-container", mode == 'blame');
|
||||
updateSourceLineNum();
|
||||
return;
|
||||
}
|
||||
@@ -164,47 +159,47 @@ $(window).load(function(){
|
||||
updateSourceLineNum();
|
||||
return;
|
||||
}
|
||||
$(document.body).toggleClass('no-box-shadow',document.body.style.boxShadow===undefined);
|
||||
$(document.body).toggleClass('no-box-shadow', document.body.style.boxShadow===undefined);
|
||||
$('.blame-action').addClass("active");
|
||||
var base = $('<div class="blame">').css({height:pre.height()}).prependTo(pre.parents("td")[0]);
|
||||
var base = $('<div class="blame">').css({height: pre.height()}).prependTo(pre.parents("div.box-content-bottom"));
|
||||
base.parents("div.container").addClass("blame-container");
|
||||
updateSourceLineNum();
|
||||
$.get($('.blame-action').data('url')).done(function(data){
|
||||
var blame = data.blame;
|
||||
var index = [];
|
||||
for(var i=0;i<blame.length;i++){
|
||||
for(var j=0;j<blame[i].lines.length;j++){
|
||||
index[blame[i].lines[j]]=blame[i];
|
||||
for(var i = 0; i < blame.length; i++){
|
||||
for(var j = 0; j < blame[i].lines.length; j++){
|
||||
index[blame[i].lines[j]] = blame[i];
|
||||
}
|
||||
}
|
||||
var blame, lastDiv, now=new Date().getTime();
|
||||
var blame, lastDiv, now = new Date().getTime();
|
||||
|
||||
$('pre.prettyprint ol.linenums li').each(function(i, e){
|
||||
var p=$(e).position();
|
||||
var h=$(e).height();
|
||||
var p = $(e).position();
|
||||
var h = $(e).height();
|
||||
if(blame == index[i]){
|
||||
lastDiv.css("min-height",(p.top+h+1) - lastDiv.position().top);
|
||||
lastDiv.css("min-height",(p.top + h + 1) - lastDiv.position().top);
|
||||
}else{
|
||||
$(e).addClass('blame-sep')
|
||||
blame = index[i];
|
||||
var sha = $('<div class="blame-sha">')
|
||||
.append($('<a>').attr("href",data.root+'/commit/'+blame.id).text(blame.id.substr(0,7)));
|
||||
.append($('<a>').attr("href", data.root + '/commit/' + blame.id).text(blame.id.substr(0,7)));
|
||||
if(blame.prev){
|
||||
sha.append($('<br />'))
|
||||
.append($('<a class="muted-link">').text('prev').attr("href",data.root+'/blame/'+blame.prev+'/'+(blame.prevPath||data.path)));
|
||||
.append($('<a class="muted-link">').text('prev').attr("href", data.root + '/blame/' + blame.prev + '/' + (blame.prevPath || data.path)));
|
||||
}
|
||||
lastDiv = $('<div class="blame-info">')
|
||||
.addClass('heat'+Math.min(10,Math.max(1,Math.ceil((now-blame.commited)/(24*3600*1000*70)))))
|
||||
.toggleClass('blame-last',blame.id==data.last)
|
||||
.addClass('heat' + Math.min(10, Math.max(1, Math.ceil((now - blame.commited) / (24 * 3600 * 1000 * 70)))))
|
||||
.toggleClass('blame-last', blame.id == data.last)
|
||||
.data('line', (i + 1))
|
||||
.css({
|
||||
"top" : p.top + 'px',
|
||||
"min-height" : h+'px'
|
||||
"top" : p.top + 'px',
|
||||
"min-height" : h + 'px'
|
||||
})
|
||||
.append(sha)
|
||||
.append($(blame.avatar).addClass('avatar').css({"float":"left"}))
|
||||
.append($(blame.avatar).addClass('avatar').css({"float": "left"}))
|
||||
.append($('<div class="blame-commit-title">').text(blame.message))
|
||||
.append($('<div class="muted">').html(blame.author+ " authed "+blame.authed))
|
||||
.append($('<div class="muted">').html(blame.author + " authed " + blame.authed))
|
||||
.appendTo(base);
|
||||
}
|
||||
});
|
||||
@@ -214,6 +209,8 @@ $(window).load(function(){
|
||||
updateBlame();
|
||||
});
|
||||
|
||||
var scrolling = false;
|
||||
|
||||
/**
|
||||
* Hightlight lines which are specified by URL hash.
|
||||
*/
|
||||
@@ -224,7 +221,7 @@ function updateHighlighting(){
|
||||
var lines = hash.substr(1).split('-');
|
||||
if(lines.length == 1){
|
||||
$('#' + lines[0]).addClass('highlight');
|
||||
if(!updateHighlighting.scrolling){
|
||||
if(!scrolling){
|
||||
$(window).scrollTop($('#' + lines[0]).offset().top - 40);
|
||||
}
|
||||
} else if(lines.length > 1){
|
||||
@@ -233,11 +230,11 @@ function updateHighlighting(){
|
||||
for(var i = start; i <= end; i++){
|
||||
$('#L' + i).addClass('highlight');
|
||||
}
|
||||
if(!updateHighlighting.scrolling){
|
||||
if(!scrolling){
|
||||
$(window).scrollTop($('#L' + start).offset().top - 40);
|
||||
}
|
||||
}
|
||||
updateHighlighting.scrolling = true;
|
||||
scrolling = true;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -30,16 +30,24 @@
|
||||
}
|
||||
}.getOrElse{
|
||||
@if(context.loginAccount.isDefined){
|
||||
<a href="@url(repository)/compare/@{encodeRefName(repository.repository.defaultBranch)}...@{encodeRefName(branch.name)}?expand=1" class="btn btn-small">New Pull Request</a>
|
||||
}else{
|
||||
<a href="@url(repository)/compare/@{encodeRefName(repository.repository.defaultBranch)}...@{encodeRefName(branch.name)}" class="btn btn-small">Compare</a>
|
||||
<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 can’t delete this branch because it has an open pull request"><i class="icon icon-trash icon-white"></i></a>
|
||||
<a class="btn disabled btn-mini" data-toggle="tooltip" title="You can’t 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="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="icon icon-trash icon-white"></i></a>
|
||||
<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>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +83,7 @@
|
||||
<script>
|
||||
$(function(){
|
||||
$('.delete-branch').click(function(e){
|
||||
var branchName = $(e.target).data('name');
|
||||
var branchName = $(e.target).closest('a').data('name');
|
||||
return confirm('Are you sure you want to remove the ' + branchName + ' branch?');
|
||||
});
|
||||
$('*[data-toggle=tooltip]').tooltip().css("white-space","nowrap");
|
||||
|
||||
@@ -15,7 +15,16 @@
|
||||
}
|
||||
<div class="box issue-comment-box">
|
||||
<div class="box-content">
|
||||
@helper.html.preview(repository, "", false, true, true, hasWritePermission, "width: 635px; height: 100px; max-height: 150px;", elastic = true)
|
||||
@helper.html.preview(
|
||||
repository = repository,
|
||||
content = "",
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = hasWritePermission,
|
||||
style = "width: 635px; height: 100px; max-height: 150px;",
|
||||
elastic = true
|
||||
)
|
||||
</div>
|
||||
@if(fileName.isDefined){
|
||||
<div class="pull-right" style="margin-top: 10px;">
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
@html.menu("code", repository){
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th>
|
||||
<th class="box-header">
|
||||
<div class="pull-right align-right">
|
||||
<a href="@url(repository)/tree/@commit.id" class="btn btn-small">Browse code</a>
|
||||
</div>
|
||||
@@ -25,7 +25,7 @@
|
||||
<div class="small" style="font-weight: normal;">
|
||||
@if(branches.nonEmpty){
|
||||
<span class="muted">
|
||||
<img src="@assets/common/images/branch.png"/>
|
||||
<i class="octicon octicon-git-branch"></i>
|
||||
@branches.zipWithIndex.map { case (branch, i) =>
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)" class="branch" id="branch-@i">@branch</a>
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
}
|
||||
@if(tags.nonEmpty){
|
||||
<span class="muted">
|
||||
<img src="@assets/common/images/tag.png"/>
|
||||
<i class="octicon octicon-tag"></i>
|
||||
@tags.zipWithIndex.map { case (tag, i) =>
|
||||
<a href="@url(repository)/tree/@tag" class="tag" id="tag-@i">@tag</a>
|
||||
}
|
||||
@@ -74,7 +74,7 @@
|
||||
</div>
|
||||
@if(commit.isDifferentFromAuthor) {
|
||||
<div class="committer">
|
||||
<span class="icon-arrow-right"></span>
|
||||
<span class="octicon octicon-arrow-right"></span>
|
||||
<span>@user(commit.committerName, commit.committerEmailAddress, "username strong")</span>
|
||||
<span class="muted"> committed @helper.html.datetimeago(commit.commitTime)</span>
|
||||
</div>
|
||||
|
||||
@@ -10,72 +10,81 @@
|
||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
<div class="head">
|
||||
@helper.html.branchcontrol(
|
||||
branch,
|
||||
repository,
|
||||
hasWritePermission
|
||||
){
|
||||
@repository.branchList.map { x =>
|
||||
<li><a href="@url(repository)/commits/@encodeRefName(x)">@helper.html.checkicon(x == branch) @x</a></li>
|
||||
}
|
||||
}
|
||||
@if(pathList.isEmpty){
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> / Commit History
|
||||
@helper.html.branchcontrol(
|
||||
branch,
|
||||
repository,
|
||||
hasWritePermission
|
||||
){
|
||||
@repository.branchList.map { x =>
|
||||
<li><a href="@url(repository)/commits/@encodeRefName(x)">@helper.html.checkicon(x == branch) @x</a></li>
|
||||
}
|
||||
}
|
||||
}
|
||||
@if(pathList.nonEmpty){
|
||||
<span class="muted">History for</span>
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
|
||||
<a class="strong" href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
|
||||
@pathList.zipWithIndex.map { case (section, i) =>
|
||||
@if(i == pathList.length - 1){
|
||||
@section
|
||||
<span class="strong">@section</span>
|
||||
} else {
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
|
||||
<a class="strong" href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
@commits.map { day =>
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th>@date(day.head.commitTime)</th>
|
||||
</tr>
|
||||
@day.map { commit =>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="pull-right align-right">
|
||||
<a href="@url(repository)/commit/@commit.id" class="btn btn-small monospace">@commit.id.substring(0, 10)</a><br>
|
||||
<a href="@url(repository)/tree/@commit.id" class="small">Browse code</a>
|
||||
</div>
|
||||
<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="icon-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="commit-list">
|
||||
@commits.map { day =>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="pagination" style="text-align: center; margin-top: 30px">
|
||||
<ul>
|
||||
@if(page <= 1){
|
||||
<li class="disabled"><span>Newer</span></li>
|
||||
} else {
|
||||
<li><a href="?page=@{page - 1}">Newer</a></li>
|
||||
}
|
||||
</table>
|
||||
}
|
||||
<div class="btn-group">
|
||||
<button class="btn" onclick="location.href='?page=@{page - 1}'"@if(page <= 1){ disabled="true"}>< Newer</button>
|
||||
<button class="btn" onclick="location.href='?page=@{page + 1}'"@if(!hasNext){ disabled="true"}>Older ></button>
|
||||
@if(!hasNext){
|
||||
<li class="disabled"><span>Older</span></li>
|
||||
} else {
|
||||
<li><a href="?page=@{page + 1}">Older</a></li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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-mini" data-toggle="tooltip" data-placement="bottom" data-hotkey="t" title="Quickly jump between files"><i class="icon icon-th-list"></i></a>
|
||||
<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>
|
||||
@if(pathList.nonEmpty){
|
||||
<a href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-mini" data-toggle="tooltip" data-placement="bottom" title="Browse commits for this branch"><i class="icon icon-time"></i></a>
|
||||
<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>
|
||||
}
|
||||
</div></div>
|
||||
@branchPullRequest.map{ case (pullRequest, issue) =>
|
||||
<a href="@url(repository)/pull/@pullRequest.issueId" class="btn btn-pullrequest-branch btn-mini" title="@issue.title" data-toggle="tooltip">#@pullRequest.issueId</a>
|
||||
<a href="@url(repository)/pull/@pullRequest.issueId" class="btn btn-small 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-success btn-mini"><i class="icon-white icon-retweet" data-toggle="tooltip" title="Compare, review, create a pull request"></i></a>
|
||||
<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>
|
||||
}
|
||||
@helper.html.branchcontrol(
|
||||
branch,
|
||||
@@ -39,12 +39,12 @@
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
|
||||
}
|
||||
@if(hasWritePermission){
|
||||
<a href="@url(repository)/new/@encodeRefName(branch)/@pathList.mkString("/")"><img src="@assets/common/images/newfile.png"/></a>
|
||||
<a href="@url(repository)/new/@encodeRefName(branch)/@pathList.mkString("/")" title="Create a new file here" style="text-decoration: none;">+</i></a>
|
||||
}
|
||||
</div>
|
||||
<table class="table table-file-list">
|
||||
<tr>
|
||||
<th colspan="4" style="font-weight: normal;">
|
||||
<th colspan="4" style="font-weight: normal; border: none;">
|
||||
<a href="@url(repository)/commit/@latestCommit.id" class="commit-message">@link(latestCommit.summary, repository)</a>
|
||||
@if(latestCommit.description.isDefined){
|
||||
<a href="javascript:void(0)" onclick="$('#description-@latestCommit.id').toggle();" class="omit">...</a>
|
||||
@@ -66,7 +66,7 @@
|
||||
</div>
|
||||
@if(latestCommit.isDifferentFromAuthor) {
|
||||
<div class="committer">
|
||||
<span class="icon-arrow-right"></span>
|
||||
<span class="octicon octicon-arrow-right"></span>
|
||||
<span>@user(latestCommit.committerName, latestCommit.committerEmailAddress, "username strong")</span>
|
||||
<span class="muted"> committed @helper.html.datetimeago(latestCommit.commitTime)</span>
|
||||
</div>
|
||||
@@ -77,7 +77,7 @@
|
||||
</tr>
|
||||
@if(pathList.size > 0){
|
||||
<tr>
|
||||
<td width="16"></td>
|
||||
<td width="16" class="file-icon"></td>
|
||||
<td><a href="@url(repository)@if(pathList.size > 1){/tree/@encodeRefName(branch)/@pathList.init.mkString("/")}">..</a></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
@@ -85,18 +85,18 @@
|
||||
}
|
||||
@files.map { file =>
|
||||
<tr>
|
||||
<td width="16">
|
||||
<td width="16" class="file-icon">
|
||||
@if(file.isDirectory){
|
||||
@if(file.linkUrl.isDefined){
|
||||
<img src="@assets/common/images/folder_link.png"/>
|
||||
<i class="octicon octicon-file-symlink-directory"></i>
|
||||
} else {
|
||||
<img src="@assets/common/images/folder.png"/>
|
||||
<i class="octicon octicon-file-directory"></i>
|
||||
}
|
||||
} else {
|
||||
<img src="@assets/common/images/file.png"/>
|
||||
<i class="octicon octicon-file-text"></i>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<td class="content-name">
|
||||
@if(file.isDirectory){
|
||||
@if(file.linkUrl.isDefined){
|
||||
<a href="@file.linkUrl">
|
||||
@@ -118,8 +118,8 @@
|
||||
}
|
||||
</td>
|
||||
<td class="mute">
|
||||
<a href="@url(repository)/commit/@file.commitId" class="commit-message">@link(file.message, repository)</a>
|
||||
[@user(file.author, file.mailAddress)]
|
||||
<a href="@url(repository)/commit/@file.commitId" class="commit-message shorten-text" title="@file.message">@link(file.message, repository)</a>
|
||||
<span class="commit-author">[@user(file.author, file.mailAddress)]</span>
|
||||
</td>
|
||||
<td style="text-align: right;">@helper.html.datetimeago(file.time, false)</td>
|
||||
</tr>
|
||||
@@ -128,7 +128,7 @@
|
||||
@readme.map { case(filePath, content) =>
|
||||
<div id="readme">
|
||||
<div class="box-header">@filePath.reverse.head</div>
|
||||
<div class="box-content markdown-body">@renderMarkup(filePath, content, branch, repository, false, false)</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>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
<table id="tree-finder-results" class="table table-file-list" data-url="@url(repository)/tree-list/@treeId">
|
||||
<tbody class="tree-browser-result-template">
|
||||
<tr class="tree-browser-result">
|
||||
<td class="icon"><span class="icon icon-chevron-right"></span></td>
|
||||
<td class="icon"><img src="@assets/common/images/file.png"/></td>
|
||||
<td class="icon"><span class="octicon octicon-chevron-right"></span></td>
|
||||
<td class="icon"><i class="octicon octicon-file-text"></i></td>
|
||||
<td>
|
||||
<a href="@url(repository)/blob/@encodeRefName(branch)"></a>
|
||||
</td>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
Opened by <a href="@url(issue.openedUserName)" class="username">@issue.openedUserName</a>
|
||||
@helper.html.datetimeago(issue.registeredDate)
|
||||
@if(issue.commentCount > 0){
|
||||
<i class="icon-comment"></i><span class="strong">@issue.commentCount</span> @plural(issue.commentCount, "comment")
|
||||
<i class="octicon octicon-comment"></i><span class="strong">@issue.commentCount</span> @plural(issue.commentCount, "comment")
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -91,9 +91,9 @@
|
||||
</div>
|
||||
</div>
|
||||
*@
|
||||
<fieldset>
|
||||
<div class="align-right" style="margin-top: 20px;">
|
||||
<input type="submit" class="btn btn-success" value="Apply changes"/>
|
||||
</fieldset>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,23 @@
|
||||
@(systemSettings: gitbucket.core.service.SystemSettingsService.SystemSettings)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th class="metal">
|
||||
@if(systemSettings.allowAccountRegistration){
|
||||
<div class="pull-right">
|
||||
<a href="@path/register" class="btn btn-mini">Create new account</a>
|
||||
</div>
|
||||
}
|
||||
Sign in
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<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>
|
||||
|
||||
@@ -21,9 +21,19 @@
|
||||
</ul>
|
||||
<form action="@url(repository)/wiki/@if(page.isEmpty){_new} else {_edit}" method="POST" validate="true">
|
||||
<span id="error-pageName" class="error"></span>
|
||||
<input type="text" name="pageName" value="@pageName" style="width: 850px; font-weight: bold;" placeholder="Input a page name."/>
|
||||
@helper.html.preview(repository, page.map(_.content).getOrElse(""), true, false, false, false, "width: 850px; height: 400px;", "")
|
||||
<input type="text" name="message" value="" style="width: 850px;" placeholder="Write a small message here explaining this change. (Optional)"/>
|
||||
<input type="text" name="pageName" value="@pageName" style="width: 910px; font-weight: bold;" placeholder="Input a page name."/>
|
||||
@helper.html.preview(
|
||||
repository = repository,
|
||||
content = page.map(_.content).getOrElse(""),
|
||||
enableWikiLink = true,
|
||||
enableRefsLink = false,
|
||||
enableTaskList = false,
|
||||
hasWritePermission = false,
|
||||
style = "width: 910px; height: 400px;",
|
||||
styleClass = "monospace",
|
||||
placeholder = ""
|
||||
)
|
||||
<input type="text" name="message" value="" style="width: 910px;" placeholder="Write a small message here explaining this change. (Optional)"/>
|
||||
<input type="hidden" name="currentPageName" value="@pageName"/>
|
||||
<input type="hidden" name="id" value="@page.map(_.id)"/>
|
||||
<input type="submit" value="Save" class="btn btn-success">
|
||||
|
||||
@@ -25,20 +25,23 @@
|
||||
</li>
|
||||
</ul>
|
||||
<div style="width: 200px;" class="pull-right">
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th class="metal">Pages <span class="label">@pages.length</span></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<ul style="margin-left: 0px; margin-bottom: 0px; word-break: break-all; width: 182px;">
|
||||
@pages.map { page =>
|
||||
<li style="margin-left:0px; list-style-type: none;"><a href="@url(repository)/wiki/@urlEncode(page)">@page</a></li>
|
||||
}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@defining(15){ max =>
|
||||
<div class="box-header">
|
||||
<span class="strong">Pages</span> <span class="label">@pages.length</span>
|
||||
</div>
|
||||
<div class="box-content-bottom" style="padding: 0px;">
|
||||
@pages.zipWithIndex.map { case (page, i) =>
|
||||
<div class="box-content-row page-link" style="border: none; @if(i > max - 1){display:none;}">
|
||||
<a href="@url(repository)/wiki/@urlEncode(page)" class="strong">@page</a>
|
||||
</div>
|
||||
}
|
||||
@if(pages.size > max){
|
||||
<div class="box-content-row show-more">
|
||||
<a href="javascript:void(0);" id="show-more-pages">Show more @{pages.size - max} pages...</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="small">
|
||||
<strong>Clone this wiki locally</strong>
|
||||
</div>
|
||||
@@ -58,9 +61,14 @@
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if(settings.ssh && loginAccount.isDefined){
|
||||
<script>
|
||||
$(function(){
|
||||
<script>
|
||||
$(function(){
|
||||
$('#show-more-pages').click(function(e){
|
||||
$('div.page-link').show();
|
||||
$(e.target).parents('div.show-more').remove();
|
||||
});
|
||||
|
||||
@if(settings.ssh && loginAccount.isDefined){
|
||||
$('#repository-url-http').click(function(){
|
||||
$('#repository-url').val('@httpUrl(repository)');
|
||||
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
|
||||
@@ -69,6 +77,6 @@
|
||||
$('#repository-url').val('@sshUrl(repository, settings, loginAccount.get.userName)');
|
||||
$('#repository-url-copy').attr('data-clipboard-text', $('#repository-url').val());
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -36,7 +36,34 @@ h6 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.octicon,.mega-octicon{
|
||||
color : #999;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
/*font-size: 14px;*/
|
||||
text-align: center;
|
||||
}
|
||||
.mega-octicon{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.octicon.active,.mega-octicon.active{
|
||||
color : #333;
|
||||
}
|
||||
|
||||
.octicon-cloud-download{
|
||||
color: #333;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.head .octicon,.head .mega-octicon{
|
||||
color : #BBB;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
/* ======================================================================== */
|
||||
/* Global Header */
|
||||
/* ======================================================================== */
|
||||
@@ -55,6 +82,10 @@ div.header-menu {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
div.header-menu .octicon{
|
||||
color: #333;
|
||||
}
|
||||
|
||||
div.header-menu input,
|
||||
div.header-menu a.btn {
|
||||
margin-top: 0px;
|
||||
@@ -73,6 +104,7 @@ div.nav-collapse a.menu-last {
|
||||
.navbar .brand {
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.navbar .brand img {
|
||||
@@ -94,11 +126,13 @@ div.input-prepend span.count {
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
img.plugin-global-menu {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
a.global-header-menu {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
font-size: 80%;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
line-height: 3.5;
|
||||
}
|
||||
|
||||
/* ======================================================================== */
|
||||
@@ -116,7 +150,7 @@ div.head div.forked {
|
||||
}
|
||||
|
||||
div.container {
|
||||
width: 920px;
|
||||
width: 980px;
|
||||
}
|
||||
|
||||
div.container-wide {
|
||||
@@ -145,6 +179,12 @@ span.error {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.shorten-text {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a.commit-message, a.commit-id, a.username, a.issue-comment-count {
|
||||
color: #333333;
|
||||
}
|
||||
@@ -160,6 +200,15 @@ a.omit:hover {
|
||||
background-color: #aaa;
|
||||
}
|
||||
|
||||
div.show-more {
|
||||
text-align: center;
|
||||
border-top: 1px solid silver;
|
||||
}
|
||||
|
||||
div.show-more a {
|
||||
color: #7aa1d3;
|
||||
}
|
||||
|
||||
span.count-right {
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
@@ -188,77 +237,81 @@ h3 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
div.box {
|
||||
background-color: #efefef;
|
||||
padding: 2px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
div.box-header {
|
||||
font-size: 120%;
|
||||
font-weight: bold;
|
||||
background-color: #e0e0e0;
|
||||
background-image: -moz-linear-gradient(#fafafa, #e0e0e0);
|
||||
background-image: -webkit-linear-gradient(#fafafa, #e0e0e0);
|
||||
background-image: linear-gradient(#fafafa, #e0e0e0);
|
||||
background-repeat: repeat-x;
|
||||
background-color: #f5f5f5;
|
||||
margin: 0;
|
||||
border-top-left-radius: 1px;
|
||||
border-top-right-radius: 1px;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-bottom: none;
|
||||
padding: 8px 10px 8px 10px;
|
||||
text-shadow: 0 1px 0 #fff
|
||||
}
|
||||
|
||||
div.box-header-small {
|
||||
background-color: #e0e0e0;
|
||||
background-image: -moz-linear-gradient(#fafafa, #e0e0e0);
|
||||
background-image: -webkit-linear-gradient(#fafafa, #e0e0e0);
|
||||
background-image: linear-gradient(#fafafa, #e0e0e0);
|
||||
background-repeat: repeat-x;
|
||||
margin: 0;
|
||||
border-top-left-radius: 1px;
|
||||
border-top-right-radius: 1px;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-bottom: none;
|
||||
padding: 6px 4px 6px 4px;
|
||||
text-shadow: 0 1px 0 #fff
|
||||
padding: 8px 8px 8px 8px;
|
||||
}
|
||||
|
||||
.inline-comment div.box-header-small {
|
||||
background: #f2f8fa;
|
||||
}
|
||||
|
||||
.commit-list {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.commit-list::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 6px;
|
||||
z-index: -1;
|
||||
display: block;
|
||||
width: 2px;
|
||||
content: "";
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
ul.nav-pills-group .link {
|
||||
color: #4078c0;
|
||||
}
|
||||
|
||||
div.box-commits {
|
||||
background-color: white;
|
||||
border: 1px solid #d8d8d8;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
div.box-content {
|
||||
background-color: white;
|
||||
border: 1px solid #d8d8d8;
|
||||
padding: 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
th.box-header {
|
||||
background-color: #e0e0e0;
|
||||
background-image: -moz-linear-gradient(#fafafa, #e0e0e0);
|
||||
background-image: -webkit-linear-gradient(#fafafa, #e0e0e0);
|
||||
background-image: linear-gradient(#fafafa, #e0e0e0);
|
||||
background-repeat: repeat-x;
|
||||
margin: 0;
|
||||
border-top-left-radius: 1px;
|
||||
border-top-right-radius: 1px;
|
||||
div > div.box-content-row:nth-of-type(1) {
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.box-content-row {
|
||||
border-top: 1px solid #d8d8d8;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
div.box-content-bottom {
|
||||
background-color: white;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-bottom: none;
|
||||
text-shadow: 0 1px 0 #fff
|
||||
padding: 4px;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
th.metal {
|
||||
background-color: #e0e0e0;
|
||||
background-image: -moz-linear-gradient(#fafafa, #e8e8e8);
|
||||
background-image: -webkit-linear-gradient(#fafafa, #e8e8e8);
|
||||
background-image: linear-gradient(#fafafa, #e8e8e8);
|
||||
background-repeat: repeat-x;
|
||||
margin: 0;
|
||||
border-top-left-radius: 1px;
|
||||
border-top-right-radius: 1px;
|
||||
table.table th.box-header {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
th.box-header .octicon {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
dl {
|
||||
@@ -326,6 +379,26 @@ span.highlight {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dz-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn .octicon {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-success .octicon {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-warning .octicon {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-danger .octicon {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* Side Menu */
|
||||
/****************************************************************************/
|
||||
@@ -333,29 +406,30 @@ ul.sidemenu {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
ul.sidemenu img {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
ul.sidemenu a {
|
||||
display: block;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
|
||||
ul.sidemenu a:hover{
|
||||
ul.sidemenu a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
ul.sidemenu li.active {
|
||||
border-top: 1px solid #eee;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-right: 2px solid #bb4444;
|
||||
border-right: 3px solid #bb4444;
|
||||
border-left: 1px solid white;
|
||||
}
|
||||
|
||||
ul.sidemenu div.gradient {
|
||||
width: 5px;
|
||||
height: 30px;
|
||||
background: linear-gradient(to right, #eee, #fff);
|
||||
margin-right: 4px;
|
||||
border-left: 1px solid #eee;
|
||||
ul.sidemenu li.active a {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
ul.sidemenu {
|
||||
background-image: -webkit-linear-gradient(left, #f6f6f6 0%, #fff 8px);
|
||||
background-image: linear-gradient(to right, #f6f6f6 0%, #fff 8px);
|
||||
box-shadow: inset 1px 0 0 #eee;
|
||||
}
|
||||
|
||||
ul.sidemenu div.margin {
|
||||
@@ -366,8 +440,6 @@ ul.sidemenu div.margin {
|
||||
}
|
||||
|
||||
ul.sidemenu li {
|
||||
line-height: 30px;
|
||||
height: 30px;
|
||||
border-left: 1px solid #eee;
|
||||
margin-left:0px;
|
||||
border-right: 2px solid white;
|
||||
@@ -379,6 +451,9 @@ ul.sidemenu span.badge {
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
ul.sidemenu a:hover i.menu-icon, ul.sidemenu i.menu-icon-active {
|
||||
color: #333;
|
||||
}
|
||||
/****************************************************************************/
|
||||
/* Create Repository */
|
||||
/****************************************************************************/
|
||||
@@ -440,36 +515,24 @@ ul.nav-stacked.side-menu li.active a:hover {
|
||||
}
|
||||
|
||||
.nav-tabs.nav-stacked.side-menu > li:first-child > a {
|
||||
-webkit-border-top-right-radius: 0px;
|
||||
|
||||
border-top-right-radius: 0px;
|
||||
-webkit-border-top-left-radius: 0px;
|
||||
|
||||
border-top-left-radius: 0px;
|
||||
-moz-border-radius-topright: 0px;
|
||||
-moz-border-radius-topleft: 0px;
|
||||
border-top-right-radius: 3px;
|
||||
border-top-left-radius: 3px;
|
||||
}
|
||||
|
||||
.nav-tabs.nav-stacked.side-menu > li:last-child > a {
|
||||
-webkit-border-bottom-right-radius: 0px;
|
||||
|
||||
border-bottom-right-radius: 0px;
|
||||
-webkit-border-bottom-left-radius: 0px;
|
||||
|
||||
border-bottom-left-radius: 0px;
|
||||
-moz-border-radius-bottomright: 0px;
|
||||
-moz-border-radius-bottomleft: 0px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* Repositories */
|
||||
/****************************************************************************/
|
||||
div.repository-icon {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
div.repository-content {
|
||||
margin-left: 40px;
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
.branches .muted-link{
|
||||
@@ -545,6 +608,10 @@ div.activity-content {
|
||||
margin-left: 40px;
|
||||
}
|
||||
|
||||
div[class^="activity-icon"] i{
|
||||
color: #BBB;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* Repository Viewer */
|
||||
/****************************************************************************/
|
||||
@@ -555,30 +622,48 @@ p.description {
|
||||
a.header-link {
|
||||
color: #888;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
a.header-link img {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
a.header-link strong {
|
||||
color: black;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
a.header-link:hover {
|
||||
color: #0088cc;
|
||||
color: #4183c4;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.header-link:hover i.octicon, a.header-link:hover strong{
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
a.header-link i.octicon-x{
|
||||
opacity: 1;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 3px;
|
||||
color: #FFF;
|
||||
line-height: 20px;
|
||||
background-color: #777;
|
||||
border-radius: 3px;
|
||||
}
|
||||
a.header-link:hover i.octicon-x{
|
||||
background-color: #4183c4;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
/*
|
||||
table.blobview {
|
||||
table-layout: fixed;
|
||||
}
|
||||
*/
|
||||
|
||||
table.table-file-list {
|
||||
margin-bottom: 0px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
border-collapse: separate;
|
||||
}
|
||||
|
||||
table.table-file-list th, table.table-file-list td {
|
||||
@@ -597,6 +682,25 @@ table.table-file-list td {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
table.table-file-list .file-icon {
|
||||
padding-right: 1px;
|
||||
}
|
||||
|
||||
table.table-file-list .content-name {
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
table.table-file-list .commit-message {
|
||||
max-width: 415px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
table.table-file-list .commit-author {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
th, td, .table th, .table td {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
@@ -776,6 +880,9 @@ a.button-link {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
a.button-link i {
|
||||
color: inherit;
|
||||
}
|
||||
a.selected {
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
@@ -791,12 +898,24 @@ table.table-issues {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
table.table-issues td .octicon-issue-opened,table.table-issues td .octicon-git-pull-request .open {
|
||||
color: #6CC644;
|
||||
}
|
||||
|
||||
table.table-issues td .octicon-issue-closed,table.table-issues td .octicon-git-pull-request .closed{
|
||||
color : #BD2C00;;
|
||||
}
|
||||
|
||||
a.issue-title {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
}
|
||||
|
||||
.label-success {
|
||||
background-color: #6cc644;
|
||||
}
|
||||
|
||||
ul.label-list {
|
||||
list-style-type: none;
|
||||
padding-left: 0px;
|
||||
@@ -900,8 +1019,16 @@ div.issue-info {
|
||||
}
|
||||
|
||||
div.issue-content {
|
||||
padding: 8px;
|
||||
background-color: #fbfbfb;
|
||||
padding: 13px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
div.issue-content p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div.issue-content p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h4#issueTitle {
|
||||
@@ -928,17 +1055,47 @@ div.issue-participants {
|
||||
|
||||
div.issue-comment-box, div.commit-comment-box {
|
||||
margin-bottom: 15px;
|
||||
margin-left: 50px;
|
||||
margin-left: 70px;
|
||||
max-width: 820px;
|
||||
}
|
||||
|
||||
div.issue-comment-box textarea {
|
||||
width: 670px;
|
||||
height: 100px;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
div.issue-comment-action {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 0px;
|
||||
margin-left: 70px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 4px solid #ddd;
|
||||
}
|
||||
|
||||
div.issue-comment-action .octicon {
|
||||
border-radius: 50%;
|
||||
background-color: #f3f3f3;
|
||||
color: #767676;
|
||||
font-size: 18px;
|
||||
margin-right: 7px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
|
||||
div.issue-reopened .octicon {
|
||||
background-color: #6cc644;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
div.issue-comment-action .octicon.danger {
|
||||
background-color: #bd2c00;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-pills > li > span.issue-label {
|
||||
display: block;
|
||||
padding: 0px 8px 2px 8px;
|
||||
@@ -1327,6 +1484,7 @@ div.markdown-body {
|
||||
font: 15px Helvetica, arial, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
div.markdown-body h1 {
|
||||
border-bottom: 1px solid #ddd;
|
||||
font-size: 2.5em;
|
||||
@@ -1519,13 +1677,15 @@ div.markdown-body table colgroup + tbody tr:first-child td:last-child {
|
||||
|
||||
a.markdown-anchor-link {
|
||||
position: absolute;
|
||||
left: -20px;
|
||||
width: 32px;
|
||||
height: 16px;
|
||||
background-image: url(../images/link.png);
|
||||
background-repeat: no-repeat;
|
||||
left: -18px;
|
||||
display: none;
|
||||
color: #999;
|
||||
/* From octicon style */
|
||||
font: normal normal normal 16px/1 octicons;
|
||||
text-decoration: none;
|
||||
text-rendering: auto;
|
||||
}
|
||||
a.markdown-anchor-link:before { content: '\f05c'} /* */
|
||||
|
||||
h1 a.markdown-anchor-link {
|
||||
top: 24px;
|
||||
@@ -1595,9 +1755,12 @@ h6 a.markdown-anchor-link {
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
/*
|
||||
div.container.blame-container{
|
||||
width:1270px;
|
||||
}
|
||||
*/
|
||||
|
||||
.line-age-legend {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 300 B |
|
Before Width: | Height: | Size: 390 B |
|
Before Width: | Height: | Size: 514 B |
|
Before Width: | Height: | Size: 318 B |
|
Before Width: | Height: | Size: 419 B |
|
Before Width: | Height: | Size: 308 B |
|
Before Width: | Height: | Size: 805 B |
|
Before Width: | Height: | Size: 834 B |