mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-05-08 17:37:04 +02:00
Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43c8518a40 | ||
|
|
1b65ae2062 | ||
|
|
3ba31f205e | ||
|
|
bf28c2aacc | ||
|
|
80834220b3 | ||
|
|
cf763993cf | ||
|
|
4d9e1a83c8 | ||
|
|
850ebf2877 | ||
|
|
7923bde014 | ||
|
|
1eab821f9a | ||
|
|
042e76348a | ||
|
|
7d0b8dc1ec | ||
|
|
b0057481d8 | ||
|
|
d70c5947fa | ||
|
|
43f7a61c4b | ||
|
|
5a8516e8e5 | ||
|
|
4727aa90ab | ||
|
|
dcdc0cfa55 | ||
|
|
27dc5597bc | ||
|
|
af37d23a0d | ||
|
|
2143760185 | ||
|
|
e919505f4e | ||
|
|
4fc221f4f9 | ||
|
|
532f418c2f | ||
|
|
19016aa14a | ||
|
|
6016844327 | ||
|
|
352438ee0a | ||
|
|
d1dbdb1642 | ||
|
|
29da986b9f | ||
|
|
0b819ea762 | ||
|
|
b3dbaaae7a | ||
|
|
2dcc14b4d9 | ||
|
|
fe728baee7 | ||
|
|
d6f49eb442 | ||
|
|
890dbf99a7 | ||
|
|
61853a474a | ||
|
|
447183c779 | ||
|
|
b371f76cb6 | ||
|
|
a5971bbdde | ||
|
|
0827fef978 | ||
|
|
a66fcb3a77 | ||
|
|
ac9b93bbba | ||
|
|
7917483dfc | ||
|
|
c0a2c8a235 | ||
|
|
f28dc15252 | ||
|
|
9faa3e8402 | ||
|
|
0f33d66cc2 | ||
|
|
5f9fd23c47 | ||
|
|
e98d275de3 | ||
|
|
6ffb2dbad7 | ||
|
|
eb2bef824d | ||
|
|
16ccf83f7a | ||
|
|
f60685117d | ||
|
|
a830b80965 | ||
|
|
3795de97a4 | ||
|
|
c972782053 | ||
|
|
52f05a911b | ||
|
|
0a007dd4eb | ||
|
|
6223503511 | ||
|
|
2e499e88a6 | ||
|
|
658fe94d0f | ||
|
|
7c2cf86674 | ||
|
|
0db4cd35f1 | ||
|
|
289c38edeb | ||
|
|
650e9f0d0b | ||
|
|
755419fd56 | ||
|
|
458f1521b6 | ||
|
|
18fa77a25c | ||
|
|
db0b1d28bc | ||
|
|
518861ac0f | ||
|
|
fbb4f33b18 | ||
|
|
dc2cf05e8b | ||
|
|
72c79542b7 | ||
|
|
ff7b0ca13f | ||
|
|
18b39fb868 | ||
|
|
b181aeb5ab | ||
|
|
5cb644279c | ||
|
|
d6f8a45889 | ||
|
|
3fdb444961 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,6 +16,8 @@ project/plugins/project/
|
||||
.classpath
|
||||
.project
|
||||
.cache
|
||||
.cache-main
|
||||
.cache-tests
|
||||
.settings
|
||||
|
||||
# IntelliJ specific
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
0;95;0cGitBucket [](https://gitter.im/gitbucket/gitbucket) [](https://travis-ci.org/gitbucket/gitbucket)
|
||||
GitBucket [](https://gitter.im/gitbucket/gitbucket) [](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
|
||||
|
||||
|
||||
42
build.sbt
42
build.sbt
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
--------
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1 +1 @@
|
||||
sbt.version=0.13.13
|
||||
sbt.version=0.13.15
|
||||
|
||||
Binary file not shown.
2
sbt.bat
2
sbt.bat
@@ -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
2
sbt.sh
@@ -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 "$@"
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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}]",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
$(function(){
|
||||
@if(elastic){
|
||||
$('#content@uid').elastic();
|
||||
$('#content@uid').trigger('blur');
|
||||
}
|
||||
|
||||
$('#preview@uid').click(function(){
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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]');
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 can’t 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 can’t 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 can’t 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 can’t 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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
87
src/main/twirl/gitbucket/core/repo/upload.scala.html
Normal file
87
src/main/twirl/gitbucket/core/repo/upload.scala.html
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user