Compare commits

...

79 Commits
4.12 ... 4.13

Author SHA1 Message Date
Naoki Takezoe
43c8518a40 (refs #1610)Redirect if the issue is a pull request 2017-05-28 13:26:06 +09:00
takezoe
1b65ae2062 Don't render contents of Commits tab and Files tab if commits is empty. 2017-05-28 12:22:18 +09:00
Naoki Takezoe
3ba31f205e Avoid NoSuchElementException for pull request from behinded branch 2017-05-28 05:15:09 +09:00
Naoki Takezoe
bf28c2aacc Add 4.13 2017-05-27 10:51:04 +09:00
Naoki Takezoe
80834220b3 Update version number to 4.13.0 2017-05-27 10:45:57 +09:00
Naoki Takezoe
cf763993cf Merge pull request #1609 from shiena/load-plugins-alphabetically
Load plugins alphabetically
2017-05-25 18:30:13 +09:00
Naoki Takezoe
4d9e1a83c8 Fix octicons style 2017-05-25 17:22:11 +09:00
Mitsuhiro Koga
850ebf2877 Load plugins alphabetically 2017-05-25 17:01:22 +09:00
Naoki Takezoe
7923bde014 Reformat gitbucket.css 2017-05-25 16:52:08 +09:00
Naoki Takezoe
1eab821f9a Fix size of buttons and labels in the branch liost view 2017-05-25 16:37:50 +09:00
Naoki Takezoe
042e76348a Merge pull request #1605 from gitbucket/feature/file-upload-to-repo
File upload into repository
2017-05-25 16:06:56 +09:00
Naoki Takezoe
7d0b8dc1ec Remove unused CSS styles 2017-05-25 15:39:40 +09:00
Naoki Takezoe
b0057481d8 (refs #1608)Fix updating parent repository relationship bug when repository is renamed or transferred. 2017-05-25 08:31:29 +09:00
Naoki Takezoe
d70c5947fa Fix CSS 2017-05-24 18:31:20 +09:00
Naoki Takezoe
43f7a61c4b Implementing file upload on the repository viewer 2017-05-24 18:19:41 +09:00
Naoki Takezoe
5a8516e8e5 Bump markedj to 1.0.12 2017-05-24 10:08:39 +09:00
Naoki Takezoe
4727aa90ab Merge branch 'feature/file-upload-to-repo' of https://github.com/devagorilla/gitbucket into devagorilla-feature/file-upload-to-repo 2017-05-23 15:33:01 +09:00
Naoki Takezoe
dcdc0cfa55 Fix view 2017-05-23 14:48:34 +09:00
Naoki Takezoe
27dc5597bc Merge pull request #1601 from tkgdsg/dropdown_filter
dropdown menu filter applied to "variable length" dropdown.
2017-05-22 11:40:42 +09:00
Yasuhiro Takagi
af37d23a0d dropdown menu filter applied to "variable length" dropdown
1. dropdown menu filter applied where that length is variable.
2. In compare.scala.html, use default dropdown menu function for filter.
2017-05-21 17:47:53 +09:00
Naoki Takezoe
2143760185 Merge pull request #1597 from tkgdsg/dropdown_filter_modification
function helper.html.dropdown supports filter placeholder
2017-05-18 11:19:15 +09:00
Yasuhiro Takagi
e919505f4e function helper.html.dropdown supports filter placeholder
developer can specify filter input placeholder in dropdown menu.
2017-05-17 21:40:30 +09:00
Naoki Takezoe
4fc221f4f9 Take first 5 commits to store activity 2017-05-15 17:15:49 +09:00
Naoki Takezoe
532f418c2f (refs #1591)Bump markedj to 1.0.11 to allow HTML in markdown 2017-05-13 20:54:20 +09:00
Naoki Takezoe
19016aa14a (refs #1594)Filter the comparing dropdown based on user’s right 2017-05-13 16:06:36 +09:00
Naoki Takezoe
6016844327 Merge pull request #1592 from t-tsutsumi/pr/fix-conditional-expression-default-branch
Fix conditional expression for determining whether it is default branch
2017-05-12 14:46:19 +09:00
t-tsutsumi
352438ee0a Fix conditional expression for determining whether it is default branch 2017-05-12 02:49:25 +09:00
Naoki Takezoe
d1dbdb1642 Merge pull request #1590 from t-tsutsumi/pr/fix-delete-branch-feature-of-pr
Fix delete branch feature of PR does not work at all
2017-05-11 11:26:07 +09:00
t-tsutsumi
29da986b9f Fix delete branch feature of PR does not work at all 2017-05-11 03:32:40 +09:00
Naoki Takezoe
0b819ea762 Merge pull request #1588 from t-tsutsumi/pr/fix-incorrect-redirect-url-and-authorization
Fix incorrect redirect URL and authorization in Update branch feature
2017-05-10 10:06:21 +09:00
Naoki Takezoe
b3dbaaae7a Merge pull request #1587 from t-tsutsumi/pr/disabling-directory-listing-feature
Disabling directory listing feature on Jetty
2017-05-10 10:05:15 +09:00
t-tsutsumi
2dcc14b4d9 Fix incorrect redirect URL and authorization in Update branch feature 2017-05-09 04:55:55 +09:00
t-tsutsumi
fe728baee7 Disabling directory listing feature on Jetty 2017-05-09 04:12:03 +09:00
Naoki Takezoe
d6f49eb442 Merge pull request #1585 from t-tsutsumi/pr/add-scala-ide-specific-files-to-.gitignore
Add Scala-IDE specific files to .gitignore
2017-05-08 02:19:41 +09:00
t-tsutsumi
890dbf99a7 Add Scala-IDE specific files to .gitignore 2017-05-08 00:58:32 +09:00
Naoki Takezoe
61853a474a Rolled back to Jetty 9.3.19.v20170502
because Scalatra 2.5.0 does not work with Jetty 9.4.
2017-05-06 09:52:14 +09:00
Naoki Takezoe
447183c779 Merge pull request #1583 from t-tsutsumi/pr/fix-incorrect-font-color-when-screen-size-768px-or-less
Fix incorrect font color when screen size 768px or less
2017-05-06 09:34:46 +09:00
t-tsutsumi
b371f76cb6 Fix incorrect font color when screen size 768px or less 2017-05-06 08:10:07 +09:00
Naoki Takezoe
a5971bbdde https://github.com/travis-ci/travis-ci/issues/7703 2017-05-06 01:44:20 +09:00
Naoki Takezoe
0827fef978 Merge pull request #1533 from aadrian/dependency_updates
Dependency updates
2017-05-06 00:05:37 +09:00
Naoki Takezoe
a66fcb3a77 Remove uniqId from template arguments 2017-05-05 23:46:07 +09:00
Naoki Takezoe
ac9b93bbba Merge pull request #1579 from tkgdsg/dropdown_filter_improve_and_fix
dropdown helper function improvement & modification
2017-05-05 23:35:09 +09:00
aadrian
7917483dfc Merge branch 'master' into dependency_updates 2017-05-04 15:44:38 +02:00
aadrian
c0a2c8a235 change back to jldap "2009-10-07" until a replacement library is used. 2017-05-04 15:26:32 +02:00
Yasuhiro Takagi
f28dc15252 dropdown helper function improvement & modification
helper can specify placeholder for it's filter.
to identify each dropdown input filter by id, use unique Id.
2017-05-04 12:31:41 +09:00
Naoki Takezoe
9faa3e8402 Bump to 4.12.1 2017-05-04 10:41:46 +09:00
Naoki Takezoe
0f33d66cc2 Merge pull request #1576 from t-tsutsumi/pr/remove-slf4j-jdk14
Remove SLF4J JDK14 binding
2017-05-04 10:04:27 +09:00
t-tsutsumi
5f9fd23c47 Remove SLF4J JDK14 binding 2017-05-04 04:11:04 +09:00
Naoki Takezoe
e98d275de3 Merge pull request #1571 from t-tsutsumi/pr/fix-incorrect-initial-height
Fix incorrect initial height of textarea
2017-05-03 09:39:51 +09:00
t-tsutsumi
6ffb2dbad7 Fix incorrect initial height of textarea 2017-05-02 20:23:34 +09:00
aadrian
eb2bef824d use postgresql-embedded 2.0 (that is supposed to work with Java 9) 2017-05-02 11:17:15 +02:00
Naoki Takezoe
16ccf83f7a Merge pull request #1568 from dbronecki/issue/1567
Fix redirect issue - fixes #1567
2017-05-02 12:40:18 +09:00
aadrian
f60685117d remove sbt-dependency-graph plugin. 2017-05-01 23:35:29 +02:00
aadrian
a830b80965 update jetty, h2 and akka again 2017-05-01 23:33:48 +02:00
Damian Bronecki
3795de97a4 Fix redirect issue by partially reverting to 956af54 - fixes #1567 2017-05-01 22:18:12 +02:00
aadrian
c972782053 use SBT 0.13.15 2017-04-30 18:09:48 +02:00
aadrian
52f05a911b Merge remote-tracking branch 'origin/master' into dependency_updates 2017-04-30 18:06:41 +02:00
Naoki Takezoe
0a007dd4eb Merge pull request #1512 from UprootStaging/safeOpt
Enable safe optimisations in scalac
2017-05-01 00:13:51 +09:00
Naoki Takezoe
6223503511 Merge pull request #1566 from xuwei-k/ApiRepository-watchers-param-unused
fix ApiRepository#apply
2017-05-01 00:03:13 +09:00
Naoki Takezoe
2e499e88a6 Merge pull request #1564 from t-tsutsumi/pr/disabling-server-header
Disabling Server header on Jetty
2017-04-30 19:14:29 +09:00
t-tsutsumi
658fe94d0f Avoid use functional looping style 2017-04-30 17:20:59 +09:00
xuwei-k
7c2cf86674 fix ApiRepository#apply 2017-04-30 16:33:49 +09:00
Naoki Takezoe
0db4cd35f1 Merge remote-tracking branch 'origin/master' 2017-04-30 13:27:28 +09:00
Naoki Takezoe
289c38edeb Update document 2017-04-30 13:27:12 +09:00
Naoki Takezoe
650e9f0d0b Drop sbt launcher 2017-04-30 13:23:59 +09:00
Takuma Tsutsumi
755419fd56 Merge branch 'master' into pr/disabling-server-header 2017-04-30 01:40:51 +09:00
Naoki Takezoe
458f1521b6 Merge pull request #1563 from t-tsutsumi/pr/fix-graceful-shutdown
Graceful shutdown on Jetty requires StatisticsHandler
2017-04-30 01:37:32 +09:00
Naoki Takezoe
18fa77a25c Fixup 2017-04-30 00:27:24 +09:00
hrj
db0b1d28bc Merge remote-tracking branch 'origin/master' into safeOpt 2017-04-29 17:46:14 +05:30
t-tsutsumi
518861ac0f Disabling Server header on Jetty 2017-04-29 20:54:36 +09:00
t-tsutsumi
fbb4f33b18 Graceful shutdown on Jetty requires StatisticsHandler 2017-04-29 20:31:04 +09:00
Naoki Takezoe
dc2cf05e8b Update docs 2017-04-29 09:39:20 +09:00
adrian
72c79542b7 update solidbase and mockito again 2017-04-16 09:53:12 +02:00
adrian
ff7b0ca13f add graph sbt-plug-in to better visualize dependencies.
(cherry picked from commit 4718381d7f)
2017-04-09 15:40:36 +02:00
adrian
18b39fb868 update some dependencies
(cherry picked from commit 60cf09b313)
2017-04-09 15:24:24 +02:00
adrian
b181aeb5ab remove launcher, since a working SBT version is a prerequisite for most projects.
(cherry picked from commit 3fb420e000)
2017-04-09 15:23:32 +02:00
hrj
5cb644279c Enable safe optimisations in scalac 2017-03-29 10:57:58 +05:30
Roy Li
d6f8a45889 added helper case class for json body 2016-07-27 11:33:38 -07:00
Roy Li
3fdb444961 Added new API to handle file upload to repository 2016-07-27 11:23:29 -07:00
37 changed files with 815 additions and 508 deletions

2
.gitignore vendored
View File

@@ -16,6 +16,8 @@ project/plugins/project/
.classpath
.project
.cache
.cache-main
.cache-tests
.settings
# IntelliJ specific

View File

@@ -8,6 +8,7 @@ before_script:
- sudo apt-get install libaio1
- sudo /etc/init.d/mysql stop
- sudo /etc/init.d/postgresql stop
- sudo chmod +x /usr/local/bin/sbt
cache:
directories:
- $HOME/.ivy2/cache

View File

@@ -1,4 +1,4 @@
0;95;0cGitBucket [![Gitter chat](https://badges.gitter.im/gitbucket/gitbucket.png)](https://gitter.im/gitbucket/gitbucket) [![Build Status](https://travis-ci.org/gitbucket/gitbucket.svg?branch=master)](https://travis-ci.org/gitbucket/gitbucket)
GitBucket [![Gitter chat](https://badges.gitter.im/gitbucket/gitbucket.png)](https://gitter.im/gitbucket/gitbucket) [![Build Status](https://travis-ci.org/gitbucket/gitbucket.svg?branch=master)](https://travis-ci.org/gitbucket/gitbucket)
=========
GitBucket is a Git web platform powered by Scala offering:
@@ -68,8 +68,13 @@ Support
Release Notes
-------------
### 4.13 - 29 May 2017
- Uploading files into the repository
- HTML is available in Markdown
- Added filter box to dropdown menus
### 4.12 - 30 Apr 2017
- Gist plug-in provides JavaScript to embed snippet
- [Gist plug-in](https://github.com/gitbucket/gitbucket-gist-plugin) provides JavaScript to embed snippet
- Dropdown menu filter in the branch comparing page
- Caution for the embedded H2 database

View File

@@ -1,8 +1,8 @@
val Organization = "io.github.gitbucket"
val Name = "gitbucket"
val GitBucketVersion = "4.12.0"
val GitBucketVersion = "4.13.0"
val ScalatraVersion = "2.5.0"
val JettyVersion = "9.3.9.v20160517"
val JettyVersion = "9.3.19.v20170502"
lazy val root = (project in file(".")).enablePlugins(SbtTwirl, JettyPlugin)
@@ -25,26 +25,26 @@ libraryDependencies ++= Seq(
"org.eclipse.jgit" % "org.eclipse.jgit.archive" % "4.7.0.201704051617-r",
"org.scalatra" %% "scalatra" % ScalatraVersion,
"org.scalatra" %% "scalatra-json" % ScalatraVersion,
"org.json4s" %% "json4s-jackson" % "3.5.0",
"org.json4s" %% "json4s-jackson" % "3.5.1",
"io.github.gitbucket" %% "scalatra-forms" % "1.1.0",
"commons-io" % "commons-io" % "2.4",
"io.github.gitbucket" % "solidbase" % "1.0.0",
"io.github.gitbucket" % "markedj" % "1.0.10",
"org.apache.commons" % "commons-compress" % "1.11",
"commons-io" % "commons-io" % "2.5",
"io.github.gitbucket" % "solidbase" % "1.0.2",
"io.github.gitbucket" % "markedj" % "1.0.12",
"org.apache.commons" % "commons-compress" % "1.13",
"org.apache.commons" % "commons-email" % "1.4",
"org.apache.httpcomponents" % "httpclient" % "4.5.1",
"org.apache.sshd" % "apache-sshd" % "1.2.0",
"org.apache.tika" % "tika-core" % "1.13",
"org.apache.httpcomponents" % "httpclient" % "4.5.3",
"org.apache.sshd" % "apache-sshd" % "1.4.0" exclude("org.slf4j","slf4j-jdk14"),
"org.apache.tika" % "tika-core" % "1.14",
"com.github.takezoe" %% "blocking-slick-32" % "0.0.8",
"joda-time" % "joda-time" % "2.9.6",
"joda-time" % "joda-time" % "2.9.9",
"com.novell.ldap" % "jldap" % "2009-10-07",
"com.h2database" % "h2" % "1.4.192",
"mysql" % "mysql-connector-java" % "5.1.39",
"org.postgresql" % "postgresql" % "9.4.1208",
"ch.qos.logback" % "logback-classic" % "1.1.7",
"com.zaxxer" % "HikariCP" % "2.4.6",
"com.typesafe" % "config" % "1.3.0",
"com.typesafe.akka" %% "akka-actor" % "2.4.12",
"com.h2database" % "h2" % "1.4.195",
"mysql" % "mysql-connector-java" % "6.0.6",
"org.postgresql" % "postgresql" % "42.0.0",
"ch.qos.logback" % "logback-classic" % "1.2.3",
"com.zaxxer" % "HikariCP" % "2.6.1",
"com.typesafe" % "config" % "1.3.1",
"com.typesafe.akka" %% "akka-actor" % "2.5.0",
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0",
"com.github.bkromhout" % "java-diff-utils" % "2.1.1",
"org.cache2k" % "cache2k-all" % "1.0.0.CR1",
@@ -54,13 +54,13 @@ libraryDependencies ++= Seq(
"javax.servlet" % "javax.servlet-api" % "3.1.0" % "provided",
"junit" % "junit" % "4.12" % "test",
"org.scalatra" %% "scalatra-scalatest" % ScalatraVersion % "test",
"org.mockito" % "mockito-core" % "2.7.16" % "test",
"org.mockito" % "mockito-core" % "2.7.22" % "test",
"com.wix" % "wix-embedded-mysql" % "2.1.4" % "test",
"ru.yandex.qatools.embed" % "postgresql-embedded" % "1.14" % "test"
"ru.yandex.qatools.embed" % "postgresql-embedded" % "2.0" % "test"
)
// Compiler settings
scalacOptions := Seq("-deprecation", "-language:postfixOps")
scalacOptions := Seq("-deprecation", "-language:postfixOps", "-opt:l:method")
javacOptions in compile ++= Seq("-target", "8", "-source", "8")
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"

View File

@@ -1,6 +1,12 @@
How to run from the source tree
========
Install [sbt](http://www.scala-sbt.org/index.html) at first.
```
$ brew install sbt
```
Run for Development
--------

View File

@@ -34,8 +34,6 @@ object GitBucketCoreModule extends Module("gitbucket-core",
Generate release files
--------
Note: Release operation requires [Ant](http://ant.apache.org/) and [Maven](https://maven.apache.org/).
### Make release war file
Run `sbt executable`. The release war file and fingerprint are generated into `target/executable/gitbucket.war`.
@@ -52,4 +50,12 @@ For plug-in development, we have to publish the GitBucket jar file to the Maven
$ sbt publish-signed
```
Then operate release sequence at https://oss.sonatype.org/.
Then logged-in https://oss.sonatype.org/ and delete following files from the staging repository:
- gitbucket_2.12-x.x.x.war
- gitbucket_2.12-x.x.x.war.asc
- gitbucket_2.12-x.x.x.war.asc.md5
- gitbucket_2.12-x.x.x.war.asc.sha1
- gitbucket_2.12-x.x.x.war.md5
At last, close and release the repository.

View File

@@ -1 +1 @@
sbt.version=0.13.13
sbt.version=0.13.15

Binary file not shown.

View File

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

2
sbt.sh
View File

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

View File

@@ -1,4 +1,9 @@
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.StatisticsHandler;
import org.eclipse.jetty.webapp.WebAppContext;
import java.io.File;
@@ -62,6 +67,15 @@ public class JettyLauncher {
// connector.setPort(port);
// server.addConnector(connector);
// Disabling Server header
for (Connector connector : server.getConnectors()) {
for (ConnectionFactory factory : connector.getConnectionFactories()) {
if (factory instanceof HttpConnectionFactory) {
((HttpConnectionFactory) factory).getHttpConfiguration().setSendServerVersion(false);
}
}
}
WebAppContext context = new WebAppContext();
File tmpDir;
@@ -82,6 +96,9 @@ public class JettyLauncher {
}
context.setTempDirectory(tmpDir);
// Disabling the directory listing feature.
context.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false");
ProtectionDomain domain = JettyLauncher.class.getProtectionDomain();
URL location = domain.getCodeSource().getLocation();
@@ -93,7 +110,9 @@ public class JettyLauncher {
context.setInitParameter("org.scalatra.ForceHttps", "true");
}
server.setHandler(context);
Handler handler = addStatisticsHandler(context);
server.setHandler(handler);
server.setStopAtShutdown(true);
server.setStopTimeout(7_000);
server.start();
@@ -122,4 +141,12 @@ public class JettyLauncher {
}
dir.delete();
}
private static Handler addStatisticsHandler(Handler handler) {
// The graceful shutdown is implemented via the statistics handler.
// See the following: https://bugs.eclipse.org/bugs/show_bug.cgi?id=420142
final StatisticsHandler statisticsHandler = new StatisticsHandler();
statisticsHandler.setHandler(handler);
return statisticsHandler;
}
}

View File

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

View File

@@ -37,7 +37,7 @@ object ApiRepository{
name = repository.repositoryName,
full_name = s"${repository.userName}/${repository.repositoryName}",
description = repository.description.getOrElse(""),
watchers = 0,
watchers = watchers,
forks = forkedCount,
`private` = repository.isPrivate,
default_branch = repository.defaultBranch,

View File

@@ -147,6 +147,13 @@ abstract class ControllerBase extends ScalatraFilter
}
}
override def url(path: String, params: Iterable[(String, Any)] = Iterable.empty,
includeContextPath: Boolean = true, includeServletPath: Boolean = true,
absolutize: Boolean = true, withSessionId: Boolean = true)
(implicit request: HttpServletRequest, response: HttpServletResponse): String =
if (path.startsWith("http")) path
else baseUrl + super.url(path, params, false, false, false)
/**
* Extends scalatra-form's trim rule to eliminate CR and LF.
*/

View File

@@ -31,6 +31,13 @@ class FileUploadController extends ScalatraServlet with FileUploadSupport with R
}, FileUtil.isImage)
}
post("/tmp"){
execute({ (file, fileId) =>
FileUtils.writeByteArrayToFile(new java.io.File(getTemporaryDir(session.getId), fileId), file.get)
session += Keys.Session.Upload(fileId) -> file.name
}, _ => true)
}
post("/file/:owner/:repository"){
execute({ (file, fileId) =>
FileUtils.writeByteArrayToFile(new java.io.File(

View File

@@ -76,7 +76,7 @@ trait IssuesControllerBase extends ControllerBase {
get("/:owner/:repository/issues")(referrersOnly { repository =>
val q = request.getParameter("q")
if(Option(q).exists(_.contains("is:pr"))){
redirect(s"/${repository.owner}/${repository.name}/pulls?q=" + StringUtil.urlEncode(q))
redirect(s"/${repository.owner}/${repository.name}/pulls?q=${StringUtil.urlEncode(q)}")
} else {
searchIssues(repository)
}
@@ -84,17 +84,21 @@ trait IssuesControllerBase extends ControllerBase {
get("/:owner/:repository/issues/:id")(referrersOnly { repository =>
defining(repository.owner, repository.name, params("id")){ case (owner, name, issueId) =>
getIssue(owner, name, issueId) map {
html.issue(
_,
getComments(owner, name, issueId.toInt),
getIssueLabels(owner, name, issueId.toInt),
getAssignableUserNames(owner, name),
getMilestonesWithIssueCount(owner, name),
getLabels(owner, name),
isIssueEditable(repository),
isIssueManageable(repository),
repository)
getIssue(owner, name, issueId) map { issue =>
if(issue.isPullRequest){
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
} else {
html.issue(
issue,
getComments(owner, name, issueId.toInt),
getIssueLabels(owner, name, issueId.toInt),
getAssignableUserNames(owner, name),
getMilestonesWithIssueCount(owner, name),
getLabels(owner, name),
isIssueEditable(repository),
isIssueManageable(repository),
repository)
}
} getOrElse NotFound()
}
})

View File

@@ -97,7 +97,9 @@ trait PullRequestsControllerBase extends ControllerBase {
diffs,
isEditable(repository),
isManageable(repository),
hasDeveloperRole(pullreq.requestUserName, pullreq.requestRepositoryName, context.loginAccount),
repository,
getRepository(pullreq.requestUserName, pullreq.requestRepositoryName),
flash.toMap.map(f => f._1 -> f._2.toString))
}
}
@@ -138,22 +140,36 @@ trait PullRequestsControllerBase extends ControllerBase {
} getOrElse NotFound()
})
get("/:owner/:repository/pull/:id/delete/*")(writableUsersOnly { repository =>
params("id").toIntOpt.map { issueId =>
val branchName = multiParams("splat").head
val userName = context.loginAccount.get.userName
if(repository.repository.defaultBranch != branchName){
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
git.branchDelete().setForce(true).setBranchNames(branchName).call()
recordDeleteBranchActivity(repository.owner, repository.name, userName, branchName)
get("/:owner/:repository/pull/:id/delete_branch")(readableUsersOnly { baseRepository =>
(for {
issueId <- params("id").toIntOpt
loginAccount <- context.loginAccount
(issue, pullreq) <- getPullRequest(baseRepository.owner, baseRepository.name, issueId)
owner = pullreq.requestUserName
name = pullreq.requestRepositoryName
if hasDeveloperRole(owner, name, context.loginAccount)
} yield {
val repository = getRepository(owner, name).get
val branchProtection = getProtectedBranchInfo(owner, name, pullreq.requestBranch)
if(branchProtection.enabled){
flash += "error" -> s"branch ${pullreq.requestBranch} is protected."
} else {
if(repository.repository.defaultBranch != pullreq.requestBranch){
val userName = context.loginAccount.get.userName
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
git.branchDelete().setForce(true).setBranchNames(pullreq.requestBranch).call()
recordDeleteBranchActivity(repository.owner, repository.name, userName, pullreq.requestBranch)
}
createComment(baseRepository.owner, baseRepository.name, userName, issueId, pullreq.requestBranch, "delete_branch")
} else {
flash += "error" -> s"""Can't delete the default branch "${pullreq.requestBranch}"."""
}
}
createComment(repository.owner, repository.name, userName, issueId, branchName, "delete_branch")
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
} getOrElse NotFound()
redirect(s"/${baseRepository.owner}/${baseRepository.name}/pull/${issueId}")
}) getOrElse NotFound()
})
post("/:owner/:repository/pull/:id/update_branch")(writableUsersOnly { baseRepository =>
post("/:owner/:repository/pull/:id/update_branch")(readableUsersOnly { baseRepository =>
(for {
issueId <- params("id").toIntOpt
loginAccount <- context.loginAccount
@@ -217,7 +233,7 @@ trait PullRequestsControllerBase extends ControllerBase {
}
}
}
redirect(s"/${repository.owner}/${repository.name}/pull/${issueId}")
redirect(s"/${baseRepository.owner}/${baseRepository.name}/pull/${issueId}")
}) getOrElse NotFound()
})
@@ -359,10 +375,10 @@ trait PullRequestsControllerBase extends ControllerBase {
title,
commits,
diffs,
(forkedRepository.repository.originUserName, forkedRepository.repository.originRepositoryName) match {
((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)
},
}).filter { case (owner, name) => hasGuestRole(owner, name, context.loginAccount) },
commits.flatten.map(commit => getCommitComments(forkedRepository.owner, forkedRepository.name, commit.id, false)).flatten.toList,
originId,
forkedId,

View File

@@ -1,6 +1,6 @@
package gitbucket.core.controller
import java.io.FileInputStream
import java.io.File
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
import gitbucket.core.plugin.PluginRegistry
@@ -18,14 +18,12 @@ import gitbucket.core.service.WebHookService._
import gitbucket.core.view
import gitbucket.core.view.helpers
import io.github.gitbucket.scalatra.forms._
import org.apache.commons.io.IOUtils
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.dircache.{DirCache, DirCacheBuilder}
import org.eclipse.jgit.errors.MissingObjectException
import org.eclipse.jgit.lib._
import org.eclipse.jgit.revwalk.RevCommit
import org.eclipse.jgit.treewalk._
import org.scalatra._
@@ -45,6 +43,13 @@ trait RepositoryViewerControllerBase extends ControllerBase {
ArchiveCommand.registerFormat("zip", new ZipFormat)
ArchiveCommand.registerFormat("tar.gz", new TgzFormat)
case class UploadForm(
branch: String,
path: String,
uploadFiles: String,
message: Option[String]
)
case class EditorForm(
branch: String,
path: String,
@@ -71,6 +76,13 @@ trait RepositoryViewerControllerBase extends ControllerBase {
issueId: Option[Int]
)
val uploadForm = mapping(
"branch" -> trim(label("Branch", text(required))),
"path" -> trim(label("Path", text())),
"uploadFiles" -> trim(label("Upload files", text(required))),
"message" -> trim(label("Message", optional(text()))),
)(UploadForm.apply)
val editorForm = mapping(
"branch" -> trim(label("Branch", text(required))),
"path" -> trim(label("Path", text())),
@@ -173,10 +185,37 @@ trait RepositoryViewerControllerBase extends ControllerBase {
val (branch, path) = repository.splitPath(multiParams("splat").head)
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch).needStatusCheck(context.loginAccount.get.userName)
html.editor(branch, repository, if(path.length == 0) Nil else path.split("/").toList,
None, JGitUtil.ContentInfo("text", None, None, Some("UTF-8")),
protectedBranch)
None, JGitUtil.ContentInfo("text", None, None, Some("UTF-8")), protectedBranch)
})
get("/:owner/:repository/upload/*")(writableUsersOnly { repository =>
val (branch, path) = repository.splitPath(multiParams("splat").head)
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch).needStatusCheck(context.loginAccount.get.userName)
html.upload(branch, repository, if(path.length == 0) Nil else path.split("/").toList, protectedBranch)
})
post("/:owner/:repository/upload", uploadForm)(writableUsersOnly { (form, repository) =>
val files = form.uploadFiles.split("\n").map { line =>
val i = line.indexOf(":")
CommitFile(line.substring(0, i).trim, line.substring(i + 1).trim)
}
commitFiles(
repository = repository,
branch = form.branch,
path = form.path,
files = files,
message = form.message.getOrElse(s"Add files via upload")
)
if(form.path.length == 0){
redirect(s"/${repository.owner}/${repository.name}/tree/${form.branch}")
} else {
redirect(s"/${repository.owner}/${repository.name}/tree/${form.branch}/${form.path}")
}
})
get("/:owner/:repository/edit/*")(writableUsersOnly { repository =>
val (branch, path) = repository.splitPath(multiParams("splat").head)
val protectedBranch = getProtectedBranchInfo(repository.owner, repository.name, branch).needStatusCheck(context.loginAccount.get.userName)
@@ -547,6 +586,114 @@ trait RepositoryViewerControllerBase extends ControllerBase {
}
})
case class UploadFiles(branch: String, path: String, fileIds : Map[String,String], message: String) {
lazy val isValid: Boolean = fileIds.size > 0
}
case class CommitFile(id: String, name: String)
private def commitFiles(repository: RepositoryService.RepositoryInfo,
files: Seq[CommitFile],
branch: String, path: String, message: String) = {
// prepend path to the filename
val newFiles = files.map { file =>
file.copy(name = if(path.length == 0) file.name else s"${path}/${file.name}")
}
_commitFile(repository, branch, message) { case (git, headTip, builder, inserter) =>
JGitUtil.processTree(git, headTip) { (path, tree) =>
if(!newFiles.exists(_.name.contains(path))) {
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
}
}
newFiles.foreach { file =>
val bytes = FileUtils.readFileToByteArray(new File(getTemporaryDir(session.getId), file.id))
builder.add(JGitUtil.createDirCacheEntry(file.name,
FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, bytes)))
builder.finish()
}
}
}
private def commitFile(repository: RepositoryService.RepositoryInfo,
branch: String, path: String, newFileName: Option[String], oldFileName: Option[String],
content: String, charset: String, message: String) = {
val newPath = newFileName.map { newFileName => if(path.length == 0) newFileName else s"${path}/${newFileName}" }
val oldPath = oldFileName.map { oldFileName => if(path.length == 0) oldFileName else s"${path}/${oldFileName}" }
_commitFile(repository, branch, message){ case (git, headTip, builder, inserter) =>
val permission = JGitUtil.processTree(git, headTip){ (path, tree) =>
// Add all entries except the editing file
if(!newPath.contains(path) && !oldPath.contains(path)){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
}
// Retrieve permission if file exists to keep it
oldPath.collect { case x if x == path => tree.getEntryFileMode.getBits }
}.flatten.headOption
newPath.foreach { newPath =>
builder.add(JGitUtil.createDirCacheEntry(newPath,
permission.map { bits => FileMode.fromBits(bits) } getOrElse FileMode.REGULAR_FILE,
inserter.insert(Constants.OBJ_BLOB, content.getBytes(charset))))
}
builder.finish()
}
}
private def _commitFile(repository: RepositoryService.RepositoryInfo,
branch: String, message: String)(f: (Git, ObjectId, DirCacheBuilder, ObjectInserter) => Unit) = {
LockUtil.lock(s"${repository.owner}/${repository.name}") {
using(Git.open(getRepositoryDir(repository.owner, repository.name))) { git =>
val loginAccount = context.loginAccount.get
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headName = s"refs/heads/${branch}"
val headTip = git.getRepository.resolve(headName)
f(git, headTip, builder, inserter)
val commitId = JGitUtil.createNewCommit(git, inserter, headTip, builder.getDirCache.writeTree(inserter),
headName, loginAccount.userName, loginAccount.mailAddress, message)
inserter.flush()
inserter.close()
// update refs
val refUpdate = git.getRepository.updateRef(headName)
refUpdate.setNewObjectId(commitId)
refUpdate.setForceUpdate(false)
refUpdate.setRefLogIdent(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
refUpdate.update()
// update pull request
updatePullRequests(repository.owner, repository.name, branch)
// record activity
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
recordPushActivity(repository.owner, repository.name, loginAccount.userName, branch, List(commitInfo))
// create issue comment by commit message
createIssueComment(repository.owner, repository.name, commitInfo)
// close issue by commit message
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name)
//call web hook
callPullRequestWebHookByRequestBranch("synchronize", repository, branch, context.baseUrl, loginAccount)
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
callWebHookOf(repository.owner, repository.name, WebHook.Push) {
getAccountByUserName(repository.owner).map{ ownerAccount =>
WebHookPushPayload(git, loginAccount, headName, repository, List(commit), ownerAccount,
oldId = headTip, newId = commitId)
}
}
}
}
}
private val readmeFiles = PluginRegistry().renderableExtensions.map { extension =>
s"readme.${extension}"
} ++ Seq("readme.txt", "readme")
@@ -597,84 +744,13 @@ trait RepositoryViewerControllerBase extends ControllerBase {
}
}
private def commitFile(repository: RepositoryService.RepositoryInfo,
branch: String, path: String, newFileName: Option[String], oldFileName: Option[String],
content: String, charset: String, message: String) = {
val newPath = newFileName.map { newFileName => if(path.length == 0) newFileName else s"${path}/${newFileName}" }
val oldPath = oldFileName.map { oldFileName => if(path.length == 0) oldFileName else s"${path}/${oldFileName}" }
LockUtil.lock(s"${repository.owner}/${repository.name}"){
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val loginAccount = context.loginAccount.get
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headName = s"refs/heads/${branch}"
val headTip = git.getRepository.resolve(headName)
val permission = JGitUtil.processTree(git, headTip){ (path, tree) =>
// Add all entries except the editing file
if(!newPath.contains(path) && !oldPath.contains(path)){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
}
// Retrieve permission if file exists to keep it
oldPath.collect { case x if x == path => tree.getEntryFileMode.getBits }
}.flatten.headOption
newPath.foreach { newPath =>
builder.add(JGitUtil.createDirCacheEntry(newPath,
permission.map { bits => FileMode.fromBits(bits) } getOrElse FileMode.REGULAR_FILE,
inserter.insert(Constants.OBJ_BLOB, content.getBytes(charset))))
}
builder.finish()
val commitId = JGitUtil.createNewCommit(git, inserter, headTip, builder.getDirCache.writeTree(inserter),
headName, loginAccount.fullName, loginAccount.mailAddress, message)
inserter.flush()
inserter.close()
// update refs
val refUpdate = git.getRepository.updateRef(headName)
refUpdate.setNewObjectId(commitId)
refUpdate.setForceUpdate(false)
refUpdate.setRefLogIdent(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
//refUpdate.setRefLogMessage("merged", true)
refUpdate.update()
// update pull request
updatePullRequests(repository.owner, repository.name, branch)
// record activity
val commitInfo = new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
recordPushActivity(repository.owner, repository.name, loginAccount.userName, branch, List(commitInfo))
// create issue comment by commit message
createIssueComment(repository.owner, repository.name, commitInfo)
// close issue by commit message
closeIssuesFromMessage(message, loginAccount.userName, repository.owner, repository.name)
// call web hook
callPullRequestWebHookByRequestBranch("synchronize", repository, branch, context.baseUrl, loginAccount)
val commit = new JGitUtil.CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))
callWebHookOf(repository.owner, repository.name, WebHook.Push) {
getAccountByUserName(repository.owner).map{ ownerAccount =>
WebHookPushPayload(git, loginAccount, headName, repository, List(commit), ownerAccount,
oldId = headTip, newId = commitId)
}
}
}
}
}
private def archiveRepository(name: String, suffix: String, repository: RepositoryService.RepositoryInfo): Unit = {
val revision = name.stripSuffix(suffix)
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val oid = git.getRepository.resolve(revision)
val revCommit = JGitUtil.getRevCommitFromId(git, oid)
val sha1 = oid.getName()
val sha1 = oid.getName()
val repositorySuffix = (if(sha1.startsWith(revision)) sha1 else revision).replace('/','-')
val filename = repository.name + "-" + repositorySuffix + suffix

View File

@@ -172,7 +172,7 @@ object PluginRegistry {
if(pluginDir.exists && pluginDir.isDirectory){
pluginDir.listFiles(new FilenameFilter {
override def accept(dir: File, name: String): Boolean = name.endsWith(".jar")
}).foreach { pluginJar =>
}).sortBy(_.getName).foreach { pluginJar =>
val classLoader = new URLClassLoader(Array(pluginJar.toURI.toURL), Thread.currentThread.getContextClassLoader)
try {
val plugin = classLoader.loadClass("Plugin").getDeclaredConstructor().newInstance().asInstanceOf[Plugin]

View File

@@ -59,7 +59,7 @@ trait ActivityService {
Activities insert Activity(userName, repositoryName, activityUserName,
"open_issue",
s"[user:${activityUserName}] opened issue [issue:${userName}/${repositoryName}#${issueId}]",
Some(title),
Some(title),
currentDate)
def recordCloseIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
@@ -132,10 +132,10 @@ trait ActivityService {
Activities insert Activity(userName, repositoryName, activityUserName,
"push",
s"[user:${activityUserName}] pushed to [branch:${userName}/${repositoryName}#${branchName}] at [repo:${userName}/${repositoryName}]",
Some(commits.map { commit => commit.id + ":" + commit.shortMessage }.mkString("\n")),
Some(commits.take(5).map { commit => commit.id + ":" + commit.shortMessage }.mkString("\n")),
currentDate)
def recordCreateTagActivity(userName: String, repositoryName: String, activityUserName: String,
def recordCreateTagActivity(userName: String, repositoryName: String, activityUserName: String,
tagName: String, commits: List[JGitUtil.CommitInfo])(implicit s: Session): Unit =
Activities insert Activity(userName, repositoryName, activityUserName,
"create_tag",
@@ -167,7 +167,7 @@ trait ActivityService {
None,
currentDate)
def recordForkActivity(userName: String, repositoryName: String, activityUserName: String, forkedUserName: String)(implicit s: Session): Unit =
def recordForkActivity(userName: String, repositoryName: String, activityUserName: String, forkedUserName: String)(implicit s: Session): Unit =
Activities insert Activity(userName, repositoryName, activityUserName,
"fork",
s"[user:${activityUserName}] forked [repo:${userName}/${repositoryName}] to [repo:${forkedUserName}/${repositoryName}]",

View File

@@ -81,7 +81,7 @@ trait RepositoryService { self: AccountService =>
Repositories.filter { t =>
(t.parentUserName === oldUserName.bind) && (t.parentRepositoryName === oldRepositoryName.bind)
}.map { t => t.originUserName -> t.originRepositoryName }.update(newUserName, newRepositoryName)
}.map { t => t.parentUserName -> t.parentRepositoryName }.update(newUserName, newRepositoryName)
// Updates activity fk before deleting repository because activity is sorted by activityId
// and it can't be changed by deleting-and-inserting record.

View File

@@ -38,7 +38,6 @@ object Markdown {
val source = if(enableTaskList) escapeTaskList(markdown) else markdown
val options = new Options()
options.setSanitize(true)
options.setBreaks(enableLineBreaks)
val renderer = new GitBucketMarkedRenderer(options, repository,

View File

@@ -18,7 +18,7 @@
</a>
</li>
}
@gitbucket.core.helper.html.dropdown("Organization"){
@gitbucket.core.helper.html.dropdown("Organization", filter = ("organization", "Find Organization...")){
@groups.map { group =>
<li>
<a href="@((if(condition.groups.contains(group)) condition.copy(groups = condition.groups - group) else condition.copy(groups = condition.groups + group)).toURL)">
@@ -60,4 +60,4 @@
</a>
</li>
}
</div>
</div>

View File

@@ -2,43 +2,45 @@
prefix: String = "",
style : String = "",
right : Boolean = false,
filter: String = "")(body: Html)
<div class="btn-group" @if(style.nonEmpty){style="@style"}>
<button
class="dropdown-toggle btn btn-default btn-sm" data-toggle="dropdown">
@if(value.isEmpty){
<i class="octicon octicon-gear"></i>
} else {
@if(prefix.nonEmpty){
<span class="muted">@prefix:</span>
}
<span class="strong">@value</span>
}
<span class="caret"></span>
</button>
<ul class="dropdown-menu@if(right){ pull-right}">
@if(filter.nonEmpty) {
<li><input id="@filter-input" type="text" class="form-control input-sm dropdown-filter-input" placeholder="Filter"/></li>
}
@body
</ul>
</div>
@if(filter.nonEmpty) {
<script>
$(function(){
$('#@{filter}-input').parent().click(function(e) {
e.stopPropagation();
});
$('#@{filter}-input').keyup(function() {
var inputVal = $('#@{filter}-input').val();
$.each($('#@{filter}-input').parent().parent().find('a'), function(index, elem) {
if ( !inputVal || !elem.text.trim() || elem.text.trim().toLowerCase().indexOf(inputVal.toLowerCase()) >=0 ) {
$(elem).parent().show();
filter: (String, String) = ("",""))(body: Html)
@defining(if(filter._1.isEmpty) "" else filter._1 + "-" + scala.util.Random.alphanumeric.take(4).mkString){ filterId =>
<div class="btn-group" @if(style.nonEmpty){style="@style"}>
<button
class="dropdown-toggle btn btn-default btn-sm" data-toggle="dropdown">
@if(value.isEmpty){
<i class="octicon octicon-gear"></i>
} else {
$(elem).parent().hide();
@if(prefix.nonEmpty){
<span class="muted">@prefix:</span>
}
<span class="strong">@value</span>
}
<span class="caret"></span>
</button>
<ul class="dropdown-menu@if(right){ pull-right}">
@if(filterId.nonEmpty) {
<li><input id="@filterId-input" type="text" class="form-control input-sm dropdown-filter-input" placeholder="@filter._2"/></li>
}
@body
</ul>
</div>
@if(filterId.nonEmpty) {
<script>
$(window).load(function(){
$('#@{filterId}-input').parent().click(function(e) {
e.stopPropagation();
});
$('#@{filterId}-input').keyup(function() {
var inputVal = $('#@{filterId}-input').val();
$.each($('#@{filterId}-input').parent().parent().find('a'), function(index, elem) {
if ( !inputVal || !elem.text.trim() || elem.text.trim().toLowerCase().indexOf(inputVal.toLowerCase()) >=0 ) {
$(elem).parent().show();
} else {
$(elem).parent().hide();
}
});
});
});
});
});
</script>
}
</script>
}
}

View File

@@ -44,6 +44,7 @@
$(function(){
@if(elastic){
$('#content@uid').elastic();
$('#content@uid').trigger('blur');
}
$('#preview@uid').click(function(){

View File

@@ -11,7 +11,7 @@
<span class="muted small strong">Labels</span>
@if(isManageable){
<div class="pull-right">
@gitbucket.core.helper.html.dropdown("Edit", right = true, filter = "labels") {
@gitbucket.core.helper.html.dropdown("Edit", right = true, filter = ("labels", "Filter Labels")) {
@labels.map { label =>
<li>
<a href="#" class="toggle-label" data-label-id="@label.labelId">
@@ -36,7 +36,7 @@
<span class="muted small strong">Milestone</span>
@if(isManageable){
<div class="pull-right">
@gitbucket.core.helper.html.dropdown("Edit", right = true, filter = "milestone") {
@gitbucket.core.helper.html.dropdown("Edit", right = true, filter = ("milestone", "Filter Milestone")) {
<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>
@@ -88,7 +88,7 @@
<span class="muted small strong">Assignee</span>
@if(isManageable){
<div class="pull-right">
@gitbucket.core.helper.html.dropdown("Edit", right = true, filter = "assignee") {
@gitbucket.core.helper.html.dropdown("Edit", right = true, filter = ("assignee", "Filter Assignee")) {
<li><a href="javascript:void(0);" class="assign" data-name=""><i class="octicon octicon-x"></i> Clear assignee</a></li>
@collaborators.map { collaborator =>
<li>

View File

@@ -51,6 +51,7 @@
@if(isManageable){
<script>
$(function(){
@*
$('a.header-link').mouseover(function(e){
var target = e.target;
if(e.target.tagName != 'A'){
@@ -70,6 +71,7 @@ $(function(){
$(target).children('img.header-icon-hover').css('display', 'none');
$(target).children('img.header-icon' ).css('display', 'inline');
});
*@
$('.table-issues input[type=checkbox]').change(function(){
var all = $('.table-issues input[type=checkbox][value]');

View File

@@ -27,7 +27,7 @@
<th style="background-color: #eee;">
<input type="checkbox"/>
<span id="table-issues-control">
@gitbucket.core.helper.html.dropdown("Author") {
@gitbucket.core.helper.html.dropdown("Author", filter = ("author", "Find Author...")) {
@collaborators.map { collaborator =>
<li>
<a href="@condition.copy(author = (if(condition.author == Some(collaborator)) None else Some(collaborator))).toURL">
@@ -37,7 +37,7 @@
</li>
}
}
@gitbucket.core.helper.html.dropdown("Label") {
@gitbucket.core.helper.html.dropdown("Label", filter = ("label", "Find Label...")) {
@labels.map { label =>
<li>
<a href="@condition.copy(labels = (if(condition.labels.contains(label.labelName)) condition.labels - label.labelName else condition.labels + label.labelName)).toURL">
@@ -48,7 +48,7 @@
</li>
}
}
@gitbucket.core.helper.html.dropdown("Milestone") {
@gitbucket.core.helper.html.dropdown("Milestone", filter = ("milestone", "Find Milestone...")) {
<li>
<a href="@condition.copy(milestone = (if(condition.milestone == Some(None)) None else Some(None))).toURL">
@gitbucket.core.helper.html.checkicon(condition.milestone == Some(None)) Issues with no milestone
@@ -62,7 +62,7 @@
</li>
}
}
@gitbucket.core.helper.html.dropdown("Assignee") {
@gitbucket.core.helper.html.dropdown("Assignee", filter = ("assignee", "Find Assignee...")) {
<li>
<a href="@condition.copy(assigned = (if(condition.assigned == Some(None)) None else Some(None))).toURL">
@gitbucket.core.helper.html.checkicon(condition.assigned == Some(None)) Assigned to nobody
@@ -116,7 +116,7 @@
<li><a href="javascript:void(0);" class="toggle-state" data-id="open">Open</a></li>
<li><a href="javascript:void(0);" class="toggle-state" data-id="close">Close</a></li>
}
@gitbucket.core.helper.html.dropdown("Label") {
@gitbucket.core.helper.html.dropdown("Label", filter = ("label", "Find Label...")) {
@labels.map { label =>
<li>
<a href="javascript:void(0);" class="toggle-label" data-id="@label.labelId">
@@ -127,13 +127,13 @@
</li>
}
}
@gitbucket.core.helper.html.dropdown("Milestone") {
@gitbucket.core.helper.html.dropdown("Milestone", filter = ("milestone", "Find Milestone...")) {
<li><a href="javascript:void(0);" class="toggle-milestone" data-id="">No milestone</a></li>
@milestones.filter(_.closedDate.isEmpty).map { milestone =>
<li><a href="javascript:void(0);" class="toggle-milestone" data-id="@milestone.milestoneId">@milestone.title</a></li>
}
}
@gitbucket.core.helper.html.dropdown("Assignee") {
@gitbucket.core.helper.html.dropdown("Assignee", filter = ("assignee", "Find Assignee...")) {
<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="octicon"></i>@helpers.avatar(collaborator, 20) @collaborator</a></li>

View File

@@ -72,26 +72,34 @@
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
@if(context.loginAccount.isDefined){
<li class="dropdown">
<li class="dropdown notifications-menu">
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#">
<i class="octicon octicon-plus" style="color: black;"></i><span class="caret" style="color: black; vertical-align: middle;"></span>
</a>
<ul class="dropdown-menu pull-right">
<li><a href="@context.path/new">New repository</a></li>
<li><a href="@context.path/groups/new">New group</a></li>
<ul class="dropdown-menu pull-right" style="width: auto;">
<li>
<ul class="menu">
<li><a href="@context.path/new">New repository</a></li>
<li><a href="@context.path/groups/new">New group</a></li>
</ul>
</li>
</ul>
</li>
<li class="dropdown">
<li class="dropdown notifications-menu">
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#" data-toggle="tooltip" data-placement="bottom" title="Signed is as @context.loginAccount.get.userName">
@helpers.avatar(context.loginAccount.get.userName, 16)<span class="caret" style="color: black; vertical-align: middle;"></span>
</a>
<ul class="dropdown-menu pull-right">
<li><a href="@helpers.url(context.loginAccount.get.userName)">Your profile</a></li>
<li><a href="@helpers.url(context.loginAccount.get.userName)/_edit">Account settings</a></li>
@if(context.loginAccount.get.isAdmin){
<li><a href="@context.path/admin/users">System administration</a></li>
}
<li><a href="@context.path/signout">Sign out</a></li>
<ul class="dropdown-menu pull-right" style="width: auto;">
<li>
<ul class="menu">
<li><a href="@helpers.url(context.loginAccount.get.userName)">Your profile</a></li>
<li><a href="@helpers.url(context.loginAccount.get.userName)/_edit">Account settings</a></li>
@if(context.loginAccount.get.isAdmin){
<li><a href="@context.path/admin/users">System administration</a></li>
}
<li><a href="@context.path/signout">Sign out</a></li>
</ul>
</li>
</ul>
</li>
} else {

View File

@@ -20,23 +20,23 @@
@gitbucket.core.html.menu("pulls", repository){
<div class="pullreq-info">
<div id="compare-edit">
@gitbucket.core.helper.html.dropdown(originRepository.owner + "/" + originRepository.name, "base fork") {
@gitbucket.core.helper.html.dropdown(originRepository.owner + "/" + originRepository.name, "base fork", filter=("origin_repo", "Find Repository...")) {
@members.map { case (owner, name) =>
<li><a href="#" class="origin-owner" data-owner="@owner" data-name="@name">@gitbucket.core.helper.html.checkicon(owner == originRepository.owner) @owner/@name</a></li>
}
}
@gitbucket.core.helper.html.dropdown(originId, "base") {
@gitbucket.core.helper.html.dropdown(originId, "base", filter=("origin_branch", "Find Branch...")) {
@originRepository.branchList.map { branch =>
<li><a href="#" class="origin-branch" data-branch="@helpers.encodeRefName(branch)">@gitbucket.core.helper.html.checkicon(branch == originId) @branch</a></li>
}
}
...
@gitbucket.core.helper.html.dropdown(forkedRepository.owner + "/" + forkedRepository.name, "head fork") {
@gitbucket.core.helper.html.dropdown(forkedRepository.owner + "/" + forkedRepository.name, "head fork", filter=("forked_repo", "Find Repository...")) {
@members.map { case (owner, name) =>
<li><a href="#" class="forked-owner" data-owner="@owner" data-name="@name">@gitbucket.core.helper.html.checkicon(owner == forkedRepository.owner) @owner/@name</a></li>
}
}
@gitbucket.core.helper.html.dropdown(forkedId, "compare") {
@gitbucket.core.helper.html.dropdown(forkedId, "compare", filter=("forked_branch", "Find Branch...")) {
@forkedRepository.branchList.map { branch =>
<li><a href="#" class="forked-branch" data-branch="@helpers.encodeRefName(branch)">@gitbucket.core.helper.html.checkicon(branch == forkedId) @branch</a></li>
}
@@ -221,27 +221,3 @@ $(function(){
}
});
</script>
<script>
function dropdownFilter(dropdownItem,placeHolder,id) {
$('<li><input id="' + id + '" type="text" class="form-control input-sm dropdown-filter-input" placeholder="' + placeHolder + '"/></li>')
.insertBefore($("li:has(a" + dropdownItem + "):first"));
$('#'+id).keyup(function() {
var inputVal = $('#'+id).val();
$.each($('#'+id).parent().parent().find('a'), function(index, elem) {
if (!inputVal || !elem.text.trim() || elem.text.trim().toLowerCase().indexOf(inputVal.toLowerCase()) >= 0) {
$(elem).parent().show();
} else {
$(elem).parent().hide();
}
});
});
}
$(function(){
dropdownFilter('.origin-owner', 'Find Repository...', 'origin-owner-control-input');
dropdownFilter('.origin-branch','Find Branch...', 'origin-branch-control-input');
dropdownFilter('.forked-owner', 'Find Repository...', 'forked-owner-control-input');
dropdownFilter('.forked-branch','Find Branch...', 'forked-branch-control-input');
});
</script>

View File

@@ -1,5 +1,6 @@
@(issue: gitbucket.core.model.Issue,
pullreq: gitbucket.core.model.PullRequest,
commits: Seq[gitbucket.core.util.JGitUtil.CommitInfo],
comments: List[gitbucket.core.model.Comment],
issueLabels: List[gitbucket.core.model.Label],
collaborators: List[String],
@@ -7,7 +8,9 @@
labels: List[gitbucket.core.model.Label],
isEditable: Boolean,
isManageable: Boolean,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
isManageableForkedRepository: Boolean,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
forkedRepository: Option[gitbucket.core.service.RepositoryService.RepositoryInfo])(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
<div class="col-md-9">
<div id="comment-list">
@@ -26,11 +29,11 @@
</div>
</div>
}
@if(isManageable && issue.closed && pullreq.userName == pullreq.requestUserName && merged &&
pullreq.repositoryName == pullreq.requestRepositoryName && repository.branchList.contains(pullreq.requestBranch)){
@if(isManageableForkedRepository && issue.closed && merged &&
forkedRepository.map(r => (r.branchList.contains(pullreq.requestBranch) && r.repository.defaultBranch != pullreq.requestBranch)).getOrElse(false)){
<div class="issue-comment-box" style="background-color: #d0eeff;">
<div class="box-content"class="issue-content" style="border: 1px solid #87a8c9; padding: 10px;">
<a href="@helpers.url(repository)/pull/@issue.issueId/delete/@helpers.encodeRefName(pullreq.requestBranch)" class="btn btn-info pull-right delete-branch" data-name="@pullreq.requestBranch">Delete branch</a>
<a href="@helpers.url(repository)/pull/@issue.issueId/delete_branch" class="btn btn-info pull-right delete-branch" data-name="@pullreq.requestBranch">Delete branch</a>
<div>
<span class="strong">Pull request successfully merged and closed</span>
</div>
@@ -46,21 +49,15 @@
</div>
<script>
$(function(){
$('#cancel-merge-pull-request').click(function(){
$('#confirm-merge-form').hide();
$('#merge-pull-request').show();
@if(commits.nonEmpty){
var checkConflict = $('.check-conflict').show();
if(checkConflict.length){
$.get('@helpers.url(repository)/pull/@issue.issueId/mergeguide', function(data){ $('.check-conflict').html(data); });
}
}
$('.delete-branch').click(function(e){
var branchName = $(e.target).data('name');
return confirm('Are you sure you want to remove the ' + branchName + ' branch?');
});
var checkConflict = $('.check-conflict').show();
if(checkConflict.length){
$.get('@helpers.url(repository)/pull/@issue.issueId/mergeguide', function(data){ $('.check-conflict').html(data); });
}
@if(isManageable){
$('.delete-branch').click(function(e){
var branchName = $(e.target).data('name');
return confirm('Are you sure you want to remove the ' + branchName + ' branch?');
});
}
});
</script>

View File

@@ -9,7 +9,9 @@
diffs: Seq[gitbucket.core.util.JGitUtil.DiffInfo],
isEditable: Boolean,
isManageable: Boolean,
isManageableForkedRepository: Boolean,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
forkedRepository: Option[gitbucket.core.service.RepositoryService.RepositoryInfo],
flash: Map[String, String])(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@import gitbucket.core.model.IssueComment
@@ -83,13 +85,17 @@
@flash.get("info").map{ info =>
<div class="alert alert-info">@info</div>
}
@gitbucket.core.pulls.html.conversation(issue, pullreq, comments, issueLabels, collaborators, milestones, labels, isEditable, isManageable, repository)
@gitbucket.core.pulls.html.conversation(issue, pullreq, commits, comments, issueLabels, collaborators, milestones, labels, isEditable, isManageable, isManageableForkedRepository, repository, forkedRepository)
</div>
<div class="tab-pane" id="commits">
@gitbucket.core.pulls.html.commits(dayByDayCommits, Some(comments), repository)
@if(commits.nonEmpty){
@gitbucket.core.pulls.html.commits(dayByDayCommits, Some(comments), repository)
}
</div>
<div class="tab-pane" id="files">
@gitbucket.core.helper.html.diff(diffs, repository, Some(commits.head.id), Some(commits.last.id), true, Some(pullreq.issueId), isManageable, true)
@if(commits.nonEmpty){
@gitbucket.core.helper.html.diff(diffs, repository, commits.headOption.map(_.id), commits.lastOption.map(_.id), true, Some(pullreq.issueId), isManageable, true)
}
</div>
</div>
}
@@ -149,4 +155,4 @@ $(function(){
return false;
});
});
</script>
</script>

View File

@@ -23,7 +23,7 @@
</span>
</td>
<td class="branch-a-b-count">
@if(branch.mergeInfo.isEmpty){
@if(repository.repository.defaultBranch == branch.name){
<span class="badge">Default</span>
} else {
@branch.mergeInfo.map{ info =>
@@ -38,47 +38,49 @@
</td>
<td>
<div class="branch-action">
@branch.mergeInfo.map{ info =>
@prs.map{ case (pull, issue) =>
<a href="@helpers.url(repository)/pull/@issue.issueId" title="@issue.title">#@issue.issueId</a>
@if(issue.closed) {
@if(info.isMerged){
<a href="@helpers.url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-info">Merged</a>
} else {
<a href="@helpers.url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-important">Closed</a>
@if(repository.repository.defaultBranch != branch.name){
@branch.mergeInfo.map{ info =>
@prs.map{ case (pull, issue) =>
<a href="@helpers.url(repository)/pull/@issue.issueId" title="@issue.title">#@issue.issueId</a>
@if(issue.closed) {
@if(info.isMerged){
<a href="@helpers.url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-info">Merged</a>
} else {
<a href="@helpers.url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-important">Closed</a>
}
} else {
<a href="@helpers.url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-success">Open</a>
}
}.getOrElse{
@if(context.loginAccount.isDefined){
<a href="@helpers.url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
helpers.urlEncode(parent) + ":" + helpers.encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
helpers.encodeRefName(repository.repository.defaultBranch)
}}...@{helpers.encodeRefName(branch.name)}?expand=1" class="btn btn-default btn-sm">New Pull request</a>
} else {
<a href="@helpers.url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
helpers.urlEncode(parent) + ":" + helpers.encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
helpers.encodeRefName(repository.repository.defaultBranch)
}}...@{helpers.encodeRefName(branch.name)}" class="btn btn-default btn-sm">Compare</a>
}
}
} else {
<a href="@helpers.url(repository)/pull/@issue.issueId" title="@issue.title" class="label label-success">Open</a>
}
}.getOrElse{
@if(context.loginAccount.isDefined){
<a href="@helpers.url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
helpers.urlEncode(parent) + ":" + helpers.encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
helpers.encodeRefName(repository.repository.defaultBranch)
}}...@{helpers.encodeRefName(branch.name)}?expand=1" class="btn btn-default">New Pull request</a>
} else {
<a href="@helpers.url(repository)/compare/@{repository.repository.parentUserName.map { parent =>
helpers.urlEncode(parent) + ":" + helpers.encodeRefName(repository.repository.defaultBranch)
}.getOrElse {
helpers.encodeRefName(repository.repository.defaultBranch)
}}...@{helpers.encodeRefName(branch.name)}" class="btn btn-default">Compare</a>
}
}
@if(hasWritePermission){
<span style="margin-left: 8px;">
@if(prs.map(!_._2.closed).getOrElse(false)){
<a class="disabled" data-toggle="tooltip" title="You cant delete this branch because it has an open pull request"><i class="octicon octicon-trashcan"></i></a>
} else {
@if(isProtected){
<a class="disabled" data-toggle="tooltip" title="You cant delete a protected branch."><i class="octicon octicon-trashcan"></i></a>
} else {
<a href="@helpers.url(repository)/delete/@helpers.encodeRefName(branch.name)" class="delete-branch" data-name="@branch.name" @if(info.isMerged){ data-toggle="tooltip" title="this branch is merged" }><i class="octicon octicon-trashcan @if(info.isMerged){warning} else {danger}"></i></a>
@if(hasWritePermission){
<span style="margin-left: 8px;">
@if(prs.map(!_._2.closed).getOrElse(false)){
<a class="disabled" data-toggle="tooltip" title="You cant delete this branch because it has an open pull request"><i class="octicon octicon-trashcan"></i></a>
} else {
@if(isProtected){
<a class="disabled" data-toggle="tooltip" title="You cant delete a protected branch."><i class="octicon octicon-trashcan"></i></a>
} else {
<a href="@helpers.url(repository)/delete/@helpers.encodeRefName(branch.name)" class="delete-branch" data-name="@branch.name" @if(info.isMerged){ data-toggle="tooltip" title="this branch is merged" }><i class="octicon octicon-trashcan @if(info.isMerged){warning} else {danger}"></i></a>
}
}
</span>
}
}
</span>
}
}
</div>
</td>
</tr>

View File

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

View File

@@ -22,7 +22,7 @@
s"${(repository.name :: pathList).mkString("/")} at ${helpers.encodeRefName(branch)} - ${repository.owner}/${repository.name}"
}, Some(repository)) {
@gitbucket.core.html.menu("files", repository, Some(branch), info, error){
<div class="head">
<div class="head" style="height: 24px;">
<div class="pull-right">
<div class="btn-group">
<a href="@helpers.url(repository)/find/@helpers.encodeRefName(branch)" class="btn btn-sm btn-default" data-hotkey="t"><i class="octicon octicon-search"></i></a>
@@ -67,27 +67,24 @@
</div>
</div>
}
@gitbucket.core.helper.html.branchcontrol(branch, repository, hasWritePermission){
@repository.branchList.map { x =>
<li><a href="@helpers.url(repository)/tree/@helpers.encodeRefName(x)">@gitbucket.core.helper.html.checkicon(x == branch) @x</a></li>
<div class="pull-left">
@gitbucket.core.helper.html.branchcontrol(branch, repository, hasWritePermission){
@repository.branchList.map { x =>
<li><a href="@helpers.url(repository)/tree/@helpers.encodeRefName(x)">@gitbucket.core.helper.html.checkicon(x == branch) @x</a></li>
}
}
}
@if(pathList.isEmpty){
@*
@branchPullRequest.map{ case (pullRequest, issue) =>
<a href="@url(repository)/pull/@pullRequest.issueId" class="btn btn-sm btn-pullrequest-branch" title="@issue.title" data-toggle="tooltip">View #@pullRequest.issueId</a>
}.getOrElse {
<a href="@url(repository)/compare?head=@urlEncode(encodeRefName(branch))" class="btn btn-sm btn-success" @if(loginAccount.isEmpty){disabled}>New pull request</a>
@if(pathList.nonEmpty){
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)">@repository.name</a> /
@pathList.zipWithIndex.map { case (section, i) =>
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
}
}
*@
} else {
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)">@repository.name</a> /
@pathList.zipWithIndex.map { case (section, i) =>
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
}
}
</div>
@if(hasWritePermission){
<a href="@helpers.url(repository)/new/@helpers.encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default pc" title="Create a new file here"><i class="octicon octicon-plus"></i></a>
<div class="btn-group pull-left" style="margin-left: 4px;">
<a href="@helpers.url(repository)/new/@helpers.encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default pc" title="Create a new file"><i class="octicon octicon-plus"></i></a>
<a href="@helpers.url(repository)/upload/@helpers.encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default pc" title="Upload files"><i class="octicon octicon-cloud-upload"></i></a>
</div>
}
</div>
<table class="table table-hover">

View File

@@ -0,0 +1,87 @@
@(branch: String,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
pathList: List[String],
protectedBranch: Boolean)(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@import gitbucket.core.util.FileUtil
@gitbucket.core.html.main(s"Upload Files at ${branch} - ${repository.owner}/${repository.name}", Some(repository)) {
@gitbucket.core.html.menu("files", repository){
@if(protectedBranch){
<div class="alert alert-danger">branch @branch is protected.</div>
}
<form method="POST" action="@helpers.url(repository)/upload" id="upload-form">
<div class="head">
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)">@repository.name</a> /
@pathList.zipWithIndex.map { case (section, i) =>
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
}
<input type="hidden" name="branch" id="branch" value="@branch"/>
<input type="hidden" name="path" id="path" value="@pathList.mkString("/")"/>
</div>
<table class="table table-bordered">
<tr>
<td id="upload-td">
<div id="upload-area">
Drag files here to add them to your repository
</div>
<ul id="upload-files">
</ul>
</td>
</tr>
</table>
<div class="panel panel-default issue-comment-box">
<div class="panel-body">
<div>
<strong>Commit changes</strong>
</div>
<div class="form-group">
<input type="text" name="message" class="form-control"/>
</div>
<div style="text-align: right;">
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)/@{pathList.mkString("/")}" class="btn btn-danger">Cancel</a>
<input type="submit" id="commit" class="btn btn-success" value="Commit changes" disabled="true"/>
<input type="hidden" id="upload-files-data" name="uploadFiles" value=""/>
</div>
</div>
</div>
</form>
}
}
<script>
$(function(){
$('#upload-area').dropzone({
url: '@context.path/upload/tmp',
maxFilesize: 10,
clickable: true,
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
success: function(file, id) {
file.previewElement.remove();
$('#upload-files').append($('<li class="upload-file">')
.append($('<span>').data('id', id).text(file.name))
.append($('<a class="delete" href="javascript:void(0);" style="margin-left: 4px;">(delete)</a>')));
updateCommitButtonStatus();
}
});
$('#upload-form').submit(function(){
try {
var data = '';
$.each($('li.upload-file span'), function(i, e){
data = data + $(e).data('id') + ':' + $(e).text() + '\n';
});
$('#upload-files-data').val(data);
} catch(e){
console.log(e);
}
});
$(document).on('click', 'a.delete', function(e){
$(e.target).parent().remove();
updateCommitButtonStatus();
});
function updateCommitButtonStatus(){
$('#commit').attr('disabled', $('.upload-file').length == 0);
}
});
</script>

View File

@@ -36,29 +36,29 @@ h6 {
font-size: 18px;
}
.octicon,.mega-octicon{
.octicon {
color : #999;
width: 14px;
height: 14px;
/*font-size: 14px;*/
font-size: 14px;
text-align: center;
}
.mega-octicon{
.mega-octicon {
color : #999;
text-align: center;
width: 32px;
height: 32px;
}
.octicon.active,.mega-octicon.active{
.octicon.active,.mega-octicon.active {
color : #333;
}
.octicon-cloud-download{
.octicon-cloud-download {
color: #333;
margin-right: 5px;
}
.head .octicon, .head .mega-octicon{
.head .octicon, .head .mega-octicon {
color : #BBB;
}
@@ -343,7 +343,7 @@ div.account-image {
ul.dropdown-menu {
padding: 2px 0;
overflow: auto;
max-height: 100vh;
max-height: 100vh;
}
ul.dropdown-menu li {
@@ -413,7 +413,7 @@ div.headbar {
/* Sign-in form */
/****************************************************************************/
div.signin-form {
width: 350px;
width: 300px;
margin: 30px auto;
}
@@ -443,22 +443,19 @@ div.repository-icon {
div.repository-content {
margin-left: 40px;
}
table.branches>thead>tr>th, table.branches>tbody>tr>td{
padding: 12px;
}
.branches .muted-link{
color: #777;
}
.branches .muted-link:hover{
color: #4183c4;
}
/*
.branches .branch-details{
display: inline-block;
width: 490px;
margin-right: 10px;
}
*/
.branches .branch-name{
color: #4183c4;
display: inline-block;
@@ -467,10 +464,12 @@ table.branches>thead>tr>th, table.branches>tbody>tr>td{
background-color: rgba(209,227,237,0.5);
border-radius: 3px;
}
.branches .branch-meta{
color: #aaa;
line-height: 20px;
}
.branches .branch-a-b-count{
color: rgba(0,0,0,0.5);
}
@@ -481,14 +480,49 @@ table.branches>thead>tr>th, table.branches>tbody>tr>td{
text-align: right;
float: left;
}
.branches .a-b-count-widget .count-half:last-child {
text-align: left;
border-left: 1px solid #bbb;
}
.branches .a-b-count-widget .count-half .count-value{
padding: 0 3px;
}
.branches .branch-a-b-count span.badge {
font-size: 100%;
}
div.branch-action a.label {
font-size: 100%;
}
td#upload-td {
padding: 0px;
background-color: #f4f4f4;
border: 1px #ddd solid;
}
div#upload-area {
text-align: center;
padding-top: 40px;
padding-bottom: 40px;
font-size: 120%;
}
ul#upload-files {
list-style: none;
padding-left: 0px;
margin-bottom: 0px;
}
li.upload-file {
border-top: 1px #f4f4f4 solid;
background-color: white;
padding: 4px;
}
/****************************************************************************/
/* Activity */
/****************************************************************************/
@@ -503,39 +537,6 @@ p.description {
color: gray;
}
a.header-link {
color: #888;
font-size: 90%;
}
a.header-link strong {
color: black;
}
a.header-link:hover {
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.table-file-list td.latest-commit {
padding-top: 4px;
padding-bottom: 4px;
@@ -670,6 +671,7 @@ a.button-link {
a.button-link i {
color: inherit;
}
a.selected {
font-weight: bold;
color: black;
@@ -684,11 +686,13 @@ table.table-issues {
margin-top: 12px;
}
table.table-issues td .octicon-issue-opened, table.table-issues td .octicon-git-pull-request .open {
table.table-issues td .octicon-issue-opened,
table.table-issues td .octicon-git-pull-request .open {
color: #6CC644;
}
table.table-issues td .octicon-issue-closed, table.table-issues td .octicon-git-pull-request .closed{
table.table-issues td .octicon-issue-closed,
table.table-issues td .octicon-git-pull-request .closed{
color : #BD2C00;;
}
@@ -896,42 +900,51 @@ li.task-list-item input.task-list-item-checkbox {
border: 2px solid #fff;
border-radius: 50%;
}
.discussion-item-content {
margin-left:36px;
}
.discussion-item-content-head{
padding-right:20px;
background-color: white;
float:left
}
pre.reset.discussion-item-content-text{
border-left: 1px solid rgb(238, 238, 238);
margin: 0 0 0 5px;
padding-left: 25px;
}
.discussion-item-commit .discussion-item-content {
font-size:12px;
}
.discussion-item-icon .octicon {
color: inherit;
}
.discussion-item-merge .discussion-item-icon {
background-color: #6e5494;
color: white;
padding-top: 1px;
padding-left: 3px;
}
.discussion-item-close .discussion-item-icon {
background-color: #bd2c00;
color: white;
padding-top: 1px;
}
.discussion-item-delete_branch .discussion-item-icon {
padding-left: 2px;
padding-top: 1px;
background-color: #767676;
color: white;
}
.discussion-item-reopen .discussion-item-icon {
background-color: #6cc644;
padding-top: 1px;
@@ -1004,6 +1017,7 @@ div.author-info div.committer {
.text-pending {
color: #cea61b;
}
.text-failure {
color: #bd2c00;
}
@@ -1012,13 +1026,14 @@ div.author-info div.committer {
padding: 10px 15px 10px 15px;
border-bottom: 1px solid #eee;
}
.build-statuses-list .build-status-item {
background-color: #fafafa;
padding: 10px 15px 10px 15px;
border-bottom: 1px solid #eee;
}
.merge-indicator{
.merge-indicator {
float:left;
border-radius: 50%;
width: 30px;
@@ -1028,22 +1043,27 @@ div.author-info div.committer {
vertical-align: middle;
margin-right: 10px;
}
.merge-indicator-success{
.merge-indicator-success {
background-color: #6cc644;
}
.merge-indicator-warning{
.merge-indicator-warning {
background-color: #cea61b;
}
.merge-indicator-alert{
.merge-indicator-alert {
background-color: #888;
}
.merge-indicator .octicon{
.merge-indicator .octicon {
color: white;
font-size: 16px;
width: 15px;
height: 15px;
}
.merge-indicator-warning .octicon{
.merge-indicator-warning .octicon {
color: white;
font-size: 30px;
}
@@ -1052,92 +1072,94 @@ div.author-info div.committer {
/* Diff */
/****************************************************************************/
table.diff {
font-size: 12px;
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
width: 100%;
font-size: 12px;
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
width: 100%;
}
table.diff thead {
display: none;
display: none;
}
table.inlinediff td.insert, table.inlinediff td.equal, table.inlinediff td.delete {
width: 900px;
table.inlinediff td.insert,
table.inlinediff td.equal,
table.inlinediff td.delete {
width: 900px;
}
td.insert, td.equal, td.delete, td.empty {
width: 50%;
width: 50%;
}
table.diff td.body{
table.diff td.body {
position: relative;
}
table.diff th.line-num{
table.diff th.line-num {
/*min-width: 20px;*/
width: 20px;
}
table.diff .add-comment {
position: absolute;
background: #4183c4;
top: 0;
left: -7px;
color: white;
padding: 2px 4px;
border: solid 1px #4183c4;
border-radius: 3px;
z-index: 99;
cursor: pointer;
position: absolute;
background: #4183c4;
top: 0;
left: -7px;
color: white;
padding: 2px 4px;
border: solid 1px #4183c4;
border-radius: 3px;
z-index: 99;
cursor: pointer;
}
table.diff .add-comment:hover {
padding: 4px 6px;
top: -1px;
padding: 4px 6px;
top: -1px;
}
table.diff tr td.body b.add-comment{
table.diff tr td.body b.add-comment {
display: none;
}
table.diff tr:hover td.body b.add-comment{
table.diff tr:hover td.body b.add-comment {
display: inline-block;
}
.container-wide table.diff tr:hover td.body b.add-comment{
.container-wide table.diff tr:hover td.body b.add-comment {
display: none;
}
.container-wide table.diff tr:hover td.body:hover b.add-comment,
.container-wide table.diff tr:hover th.line-num:hover + td b.add-comment{
.container-wide table.diff tr:hover th.line-num:hover + td b.add-comment {
display: inline-block;
}
table.diff tbody tr.not-diff {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
tr.not-diff .box {
margin-bottom: 0px;
margin-bottom: 0px;
}
table.diff tbody tr.not-diff:hover {
background-color: #fff;
background-color: #fff;
}
table.diff tbody tr.not-diff:hover td{
background-color: #fff;
table.diff tbody tr.not-diff:hover td {
background-color: #fff;
}
.not-diff > .comment-box-container {
white-space: normal;
line-height: initial;
padding: 10px;
white-space: normal;
line-height: initial;
padding: 10px;
}
.diff .oldline:before, .diff .newline:before {
content: attr(line-number);
content: attr(line-number);
}
.diff .skipline:before {
@@ -1145,32 +1167,36 @@ table.diff tbody tr.not-diff:hover td{
}
.diffstat-bar {
display: inline-block;
/* For IE 6/7 */
*display: inline;
*zoom: 1;
margin-left: 3px;
font-size: 16px;
color: #ddd;
letter-spacing: 1px;
text-align: left;
text-decoration: none;
-webkit-font-smoothing: antialiased;
display: inline-block;
/* For IE 6/7 */
*display: inline;
*zoom: 1;
margin-left: 3px;
font-size: 16px;
color: #ddd;
letter-spacing: 1px;
text-align: left;
text-decoration: none;
-webkit-font-smoothing: antialiased;
}
.text-diff-added {
color: #55a532;
color: #55a532;
}
.text-diff-deleted {
color: #bd2c00;
color: #bd2c00;
}
.diffstat {
font-size: 12px;
font-weight: bold;
color: #666;
white-space: nowrap;
letter-spacing: 0px;
font-size: 12px;
font-weight: bold;
color: #666;
white-space: nowrap;
letter-spacing: 0px;
}
.diff-same{
.diff-same {
background: #DDD;
color: #BBB;
font-size: 16px;
@@ -1178,76 +1204,93 @@ table.diff tbody tr.not-diff:hover td{
font-weight: bold;
text-align: center;
}
/* ------- for imageDiff */
.diff-image-frame{
.diff-image-frame {
display: table-cell;
*float: left; /* for ie7 */
vertical-align: middle;
padding: 20px;
background-color: #eee;
}
.diff-image-frame.diff-old{
.diff-image-frame.diff-old {
padding-right: 2px;
}
.diff-image-frame.diff-new{
.diff-image-frame.diff-new {
padding-left: 2px;
}
.diff-image-frame .diff-meta{
.diff-image-frame .diff-meta {
margin-top: 12px;
color: #999;
font-family: Helvetica,arial,freesans,clean,sans-serif;
font-size: 12px;
}
.diff-image-frame.diff-old .diff-meta .diff{
.diff-image-frame.diff-old .diff-meta .diff {
color: #bd2c00;
}
.diff-image-frame.diff-new .diff-meta .diff{
.diff-image-frame.diff-new .diff-meta .diff {
color: #55a532;
}
.diff-image-frame img{
.diff-image-frame img {
max-height: 410px;
max-width: 410px;
background: url(../images/checker.png);
}
.diff-image-render.diff2up{
.diff-image-render.diff2up {
width:100%;
text-align: center;
display: table;
}
.diff-image-frame.diff-new img{
.diff-image-frame.diff-new img {
border: 1px solid #55a532;
}
.diff-image-frame.diff-old img{
.diff-image-frame.diff-old img {
border: 1px solid #bd2c00;
}
.diff-image-stack{
.diff-image-stack {
position: relative;
background: #EEE;
padding-top: 20px;
}
.diff-image-stack .diff-old,
.diff-image-stack .diff-new{
.diff-image-stack .diff-new {
position:absolute;
overflow: hidden;
margin:0 20px;
}
.diff-image-stack img {
max-width: none;
background: url(../images/checker.png);
}
.diff-image-stack .diff-new{
.diff-image-stack .diff-new {
border: 1px solid #55a532;
background: #EEE;
}
.diff-image-stack .diff-old{
.diff-image-stack .diff-old {
border: 1px solid #bd2c00;
}
.diff-swipe-handle{
.diff-swipe-handle {
position:absolute;
margin-left: 325px;
left: 100px;
}
.diff-silde-bar{
.diff-silde-bar {
width: 200px;
position: absolute;
left: 325px;
@@ -1256,19 +1299,22 @@ table.diff tbody tr.not-diff:hover td{
border: 1px solid gray;
height: 8px;
}
.image-diff-tools{
.image-diff-tools {
text-align: center;
padding: 4px;
background: #f7f7f7;
}
.image-diff-tools{
.image-diff-tools {
font-family: 'Helvetica Neue', Helvetica, arial, freesans, clean, sans-serif;
font-size: 12px;
background: #f7f7f7;
margin: 0;
text-align: center;
}
.image-diff-tools li{
.image-diff-tools li {
background: none;
display: inline;
cursor: pointer;
@@ -1277,33 +1323,39 @@ table.diff tbody tr.not-diff:hover td{
position: relative;
color: #666;
}
.image-diff-tools li:last-child{
.image-diff-tools li:last-child {
border-right: none;
}
.image-diff-tools li.active {
font-weight: bold;
}
.no-canvas .need-canvas{
display: none;
}
.diff-image-stack.swipe .diff-new{
border-right: 1px solid #888;
}
.diff-image-stack.swipe .diff-swipe-handle{
margin-left: 15px;
left: 410px;
}
.diff-image-stack.swipe .diff-silde-bar{
.no-canvas .need-canvas {
display: none;
}
.diff-image-stack.onion .diff-silde-bar{
.diff-image-stack.swipe .diff-new {
border-right: 1px solid #888;
}
.diff-image-stack.swipe .diff-swipe-handle {
margin-left: 15px;
left: 410px;
}
.diff-image-stack.swipe .diff-silde-bar {
display: none;
}
.diff-image-stack.onion .diff-silde-bar {
background: -ms-linear-gradient(left, #bd2c00 0%,#55a532 100%); /* IE10+ */
background: linear-gradient(to right, #bd2c00 0%,#55a532 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#bd2c00', endColorstr='#55a532',GradientType=1 ); /* IE6-9 */
}
.diff-image-stack.blink .diff-silde-bar{
.diff-image-stack.blink .diff-silde-bar {
border-style: dotted;
background-image: linear-gradient(to right, #bd2c00, #bd2c00 50%, #55a532 50%, #55a532 100%);
background-size: 2px 2px;
@@ -1573,7 +1625,7 @@ a.markdown-anchor-link span.octicon {
/****************************************************************************/
/* File finder */
/****************************************************************************/
#tree-finder-field{
#tree-finder-field {
border: none;
box-shadow: none;
padding: 0;
@@ -1583,33 +1635,40 @@ a.markdown-anchor-link span.octicon {
height: inherit;
width: 300px;
}
.find-input{
.find-input {
font-size: 18px;
margin-bottom: 20px;
}
#tree-finder-results td{
#tree-finder-results td {
padding:7px 6px;
}
#tree-finder-results td.icon{
#tree-finder-results td.icon {
width:16px; padding: 7px 2px 7px 6px;
}
#tree-finder-results .tree-browser-result .icon-chevron-right{
#tree-finder-results .tree-browser-result .icon-chevron-right {
visibility: hidden;
}
#tree-finder-results .tree-browser-result.navigation-focus .icon-chevron-right{
#tree-finder-results .tree-browser-result.navigation-focus .icon-chevron-right {
visibility: visible;
}
#tree-finder-results .navigation-focus td{
#tree-finder-results .navigation-focus td {
background: #fff;
}
/****************************************************************************/
/* blame */
/****************************************************************************/
.blobview pre.blob{
.blobview pre.blob {
padding-left: 0;
}
.blobview ol.linenums{
.blobview ol.linenums {
margin-left: 0;
padding-left: 50px;
}
@@ -1623,12 +1682,14 @@ div.container.blame-container{
.line-age-legend {
display: none;
}
.blame-container .line-age-legend {
display: block;
float: right;
font-size: 12px;
color: #777;
}
.blame-container .line-age-legend ol {
display: inline-block;
*display: inline;
@@ -1636,6 +1697,7 @@ div.container.blame-container{
list-style: none;
margin: 0 5px;
}
.blame-container .line-age-legend ol li {
display: inline-block;
*display: inline;
@@ -1643,17 +1705,21 @@ div.container.blame-container{
width: 8px;
height: 10px;
}
.blame-container pre.blob{
margin-left: 350px;
}
.blame-container pre.prettyprint ol.linenums li.blame-sep{
.blame-container pre.prettyprint ol.linenums li.blame-sep {
border-top: 1px solid rgb(219, 219, 219);
margin-top: -1px;
}
.blame-container .hide-if-blame {
display: none;
}
.blame{
.blame {
font-size: 12px;
white-space: normal;
width: 340px;
@@ -1661,19 +1727,23 @@ div.container.blame-container{
min-height: 100px;
display: none;
}
.blame-container .blame{
.blame-container .blame {
display: block;
}
.blame .blame-commit-title{
.blame .blame-commit-title {
font-weight: bold;
color: #333;
line-height: 1.1;
}
.blame .avatar{
.blame .avatar {
margin-right: 4px;
margin-bottom: 4px;
}
.blame .blame-info{
.blame .blame-info {
background: white;
box-shadow:rgba(113, 135, 164, 0.65098) 0px 0px 4px 0px;
position: absolute;
@@ -1681,51 +1751,57 @@ div.container.blame-container{
padding: 2px;
border-right: 2px solid;
}
.no-box-shadow .blame .blame-info{
.no-box-shadow .blame .blame-info {
border-top: 1px solid #888;
border-bottom: 1px solid #888;
border-left: 1px solid #888;
}
.blame-sha{
.blame-sha {
font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
float: right;
text-align: right;
}
.blame-sha .muted-link{
.blame-sha .muted-link {
color: #777;
}
.blame-sha .muted-link:hover{
.blame-sha .muted-link:hover {
color: #4183c4;
}
.blame .blame-info:hover{
.blame .blame-info:hover {
z-index: 100;
box-shadow:rgba(113, 135, 164, 0.65098) 0px 0px 4px 3px;
}
.blame .blame-info.blame-last{
.blame .blame-info.blame-last {
background: #FDFCED;
}
.blame-info.heat1{ border-right-color:#ffeca7}
.blame-info.heat2{ border-right-color:#ffdd8c}
.blame-info.heat3{ border-right-color:#ffdd7c}
.blame-info.heat4{ border-right-color:#fba447}
.blame-info.heat5{ border-right-color:#f68736}
.blame-info.heat6{ border-right-color:#f37636}
.blame-info.heat7{ border-right-color:#ca6632}
.blame-info.heat8{ border-right-color:#c0513f}
.blame-info.heat9{ border-right-color:#a2503a}
.blame-info.heat10{border-right-color:#793738}
.heat1{background-color:#ffeca7}
.heat2{background-color:#ffdd8c}
.heat3{background-color:#ffdd7c}
.heat4{background-color:#fba447}
.heat5{background-color:#f68736}
.heat6{background-color:#f37636}
.heat7{background-color:#ca6632}
.heat8{background-color:#c0513f}
.heat9{background-color:#a2503a}
.heat10{background-color:#793738}
.blame-info.heat1 { border-right-color:#ffeca7 }
.blame-info.heat2 { border-right-color:#ffdd8c }
.blame-info.heat3 { border-right-color:#ffdd7c }
.blame-info.heat4 { border-right-color:#fba447 }
.blame-info.heat5 { border-right-color:#f68736 }
.blame-info.heat6 { border-right-color:#f37636 }
.blame-info.heat7 { border-right-color:#ca6632 }
.blame-info.heat8 { border-right-color:#c0513f }
.blame-info.heat9 { border-right-color:#a2503a }
.blame-info.heat10{ border-right-color:#793738 }
.heat1 { background-color:#ffeca7 }
.heat2 { background-color:#ffdd8c }
.heat3 { background-color:#ffdd7c }
.heat4 { background-color:#fba447 }
.heat5 { background-color:#f68736 }
.heat6 { background-color:#f37636 }
.heat7 { background-color:#ca6632 }
.heat8 { background-color:#c0513f }
.heat9 { background-color:#a2503a }
.heat10{ background-color:#793738 }
/****************************************************************************/
/* Mobile */
@@ -1760,9 +1836,9 @@ div.container.blame-container{
/* Suppress transition animation on load */
/****************************************************************************/
body.page-load * {
-webkit-transition: none !important;
-moz-transition: none !important;
-ms-transition: none !important;
-o-transition: none !important;
transition: none !important;
-webkit-transition: none !important;
-moz-transition: none !important;
-ms-transition: none !important;
-o-transition: none !important;
transition: none !important;
}