mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-05-07 23:46:44 +02:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a79f105eea | ||
|
|
b916595da3 | ||
|
|
90b63090cc | ||
|
|
345685ed7c | ||
|
|
1d9fe5770e | ||
|
|
ec307b84d3 | ||
|
|
5d7346db91 | ||
|
|
443498433d | ||
|
|
a58ce07736 | ||
|
|
1903c3990c | ||
|
|
90441c8eec | ||
|
|
601919bcc6 | ||
|
|
6903b096f5 | ||
|
|
e44fed09fa | ||
|
|
8ed0c8a170 | ||
|
|
31118ac285 | ||
|
|
c851b7582f | ||
|
|
102a02d527 | ||
|
|
1968bf871a | ||
|
|
5cd4e48173 | ||
|
|
111d212cb5 | ||
|
|
d648d34393 | ||
|
|
bbaa5b38e7 | ||
|
|
3c50a78be2 | ||
|
|
82cc1fa530 | ||
|
|
8c0581973e | ||
|
|
bfa15f5d75 | ||
|
|
57e87b581d | ||
|
|
5aa6f5bce3 | ||
|
|
9ba098a805 | ||
|
|
b2d8567c26 | ||
|
|
19d97c93ce | ||
|
|
cad2daa2f9 | ||
|
|
585f0b5769 | ||
|
|
dd23d1109b | ||
|
|
5e84221d39 | ||
|
|
faf3e6c26b | ||
|
|
969da2c63b | ||
|
|
ba61891510 | ||
|
|
a581871a89 | ||
|
|
d96e1fa503 | ||
|
|
b66812d76c | ||
|
|
ae32016856 | ||
|
|
56aec15e68 | ||
|
|
d0c8e33ec5 | ||
|
|
7ab260e688 | ||
|
|
0d2c923664 | ||
|
|
e7a47fe3a4 | ||
|
|
e454f78c5a | ||
|
|
192e4ade3e | ||
|
|
733797cb6f | ||
|
|
f0e4157a46 | ||
|
|
2ad6948bb4 | ||
|
|
dd46d649a6 | ||
|
|
bbe8a9b9e4 | ||
|
|
376b109602 | ||
|
|
1d085d52bb | ||
|
|
d1f42e0ed7 | ||
|
|
101f8598ed | ||
|
|
da62f6f8fb | ||
|
|
5750286b5d | ||
|
|
f2ca4fb64b | ||
|
|
5f6b577cbf | ||
|
|
62004c279c | ||
|
|
838c7fb991 | ||
|
|
93786f0fd6 | ||
|
|
f3514e5625 | ||
|
|
a2b0ee0c24 | ||
|
|
2bcab30529 | ||
|
|
91cda6d245 | ||
|
|
a82e579d57 | ||
|
|
94421c7a63 | ||
|
|
6431d25409 | ||
|
|
ff3205b6c7 |
25
README.md
25
README.md
@@ -1,7 +1,10 @@
|
||||
GitBucket [](https://gitter.im/gitbucket/gitbucket) [](https://travis-ci.org/gitbucket/gitbucket)
|
||||
=========
|
||||
|
||||
GitBucket is a GitHub clone powered by Scala which has easy installation and high extensibility.
|
||||
GitBucket is a Git platform powered by Scala offering:
|
||||
- easy installation
|
||||
- high extensibility by plugins
|
||||
- API compatibility with Github
|
||||
|
||||
Features
|
||||
--------
|
||||
@@ -56,10 +59,28 @@ Support
|
||||
- Make sure check whether there is a same question or request in the past.
|
||||
- When raise a new issue, write subject in **English** at least.
|
||||
- We can also support in Japaneses other than English at [gitter room for Japanese](https://gitter.im/gitbucket/gitbucket_ja).
|
||||
- First priority of GitBucket is easy installation and reproduce GitHub behavior, so we might reject if your request is against it.
|
||||
- First priority of GitBucket is easy installation and API compatibility with GitHub, so we might reject if your request is against it.
|
||||
|
||||
Release Notes
|
||||
--------
|
||||
### 4.0 - 30 Apr 2016
|
||||
|
||||
- MySQL and PostgreSQL support
|
||||
- Data export and import
|
||||
- Migration system has been switched to [solidbase](https://github.com/gitbucket/solidbase)
|
||||
|
||||
### 3.14 - 30 Apr 2016
|
||||
|
||||
- File attachment and search for wiki pages
|
||||
- New extension points to add menus
|
||||
- Content-Type of webhooks has been choosable
|
||||
|
||||
### 3.13 - 1 Apr 2016
|
||||
- Refresh user interface for wide screen
|
||||
- Add `pull_request` key in list issues API for pull requests
|
||||
- Add `X-Hub-Signature` security to webhooks
|
||||
- Provide SHA-256 checksum for `gitbucket.war`
|
||||
|
||||
### 3.12 - 27 Feb 2016
|
||||
- New GitHub UI
|
||||
- Improve mobile view
|
||||
|
||||
10
build.sbt
10
build.sbt
@@ -1,6 +1,6 @@
|
||||
val Organization = "gitbucket"
|
||||
val Name = "gitbucket"
|
||||
val GitBucketVersion = "3.12.0"
|
||||
val GitBucketVersion = "3.14.0"
|
||||
val ScalatraVersion = "2.4.0"
|
||||
val JettyVersion = "9.3.6.v20151106"
|
||||
|
||||
@@ -10,7 +10,7 @@ sourcesInBase := false
|
||||
organization := Organization
|
||||
name := Name
|
||||
version := GitBucketVersion
|
||||
scalaVersion := "2.11.7"
|
||||
scalaVersion := "2.11.8"
|
||||
|
||||
// dependency settings
|
||||
resolvers ++= Seq(
|
||||
@@ -18,6 +18,7 @@ resolvers ++= Seq(
|
||||
"sonatype-snapshot" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
)
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scala-lang.modules" %% "scala-java8-compat" % "0.7.0",
|
||||
"org.eclipse.jgit" % "org.eclipse.jgit.http.server" % "4.1.1.201511131810-r",
|
||||
"org.eclipse.jgit" % "org.eclipse.jgit.archive" % "4.1.1.201511131810-r",
|
||||
"org.scalatra" %% "scalatra" % ScalatraVersion,
|
||||
@@ -25,7 +26,7 @@ libraryDependencies ++= Seq(
|
||||
"org.json4s" %% "json4s-jackson" % "3.3.0",
|
||||
"io.github.gitbucket" %% "scalatra-forms" % "1.0.0",
|
||||
"commons-io" % "commons-io" % "2.4",
|
||||
"io.github.gitbucket" % "markedj" % "1.0.7",
|
||||
"io.github.gitbucket" % "markedj" % "1.0.8",
|
||||
"org.apache.commons" % "commons-compress" % "1.10",
|
||||
"org.apache.commons" % "commons-email" % "1.4",
|
||||
"org.apache.httpcomponents" % "httpclient" % "4.5.1",
|
||||
@@ -38,7 +39,6 @@ libraryDependencies ++= Seq(
|
||||
"com.mchange" % "c3p0" % "0.9.5.2",
|
||||
"com.typesafe" % "config" % "1.3.0",
|
||||
"com.typesafe.akka" %% "akka-actor" % "2.3.14",
|
||||
"io.getquill" %% "quill-jdbc" % "0.4.1",
|
||||
"fr.brouillard.oss.security.xhub" % "xhub4j-core" % "1.0.0",
|
||||
"com.enragedginger" %% "akka-quartz-scheduler" % "1.4.0-akka-2.3.x" exclude("c3p0","c3p0"),
|
||||
"org.eclipse.jetty" % "jetty-webapp" % JettyVersion % "provided",
|
||||
@@ -52,7 +52,7 @@ libraryDependencies ++= Seq(
|
||||
play.twirl.sbt.Import.TwirlKeys.templateImports += "gitbucket.core._"
|
||||
|
||||
// Compiler settings
|
||||
scalacOptions := Seq("-deprecation", "-language:postfixOps")
|
||||
scalacOptions := Seq("-deprecation", "-language:postfixOps", "-Ybackend:GenBCode", "-Ydelambdafy:method", "-target:jvm-1.8")
|
||||
javacOptions in compile ++= Seq("-target", "8", "-source", "8")
|
||||
javaOptions in Jetty += "-Dlogback.configurationFile=/logback-dev.xml"
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "junitxml", "console")
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<extension>
|
||||
<groupId>org.apache.maven.wagon</groupId>
|
||||
<artifactId>wagon-ssh</artifactId>
|
||||
<version>1.0-beta-6</version>
|
||||
<version>2.10</version>
|
||||
</extension>
|
||||
</extensions>
|
||||
</build>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
db.dataSourceClassName="org.h2.jdbcx.JdbcDataSource"
|
||||
db.dataSource.url="jdbc:h2:~/.gitbucket/data;MVCC=true"
|
||||
db.dataSource.user="sa"
|
||||
db.dataSource.password="sa"
|
||||
#db.dataSource.cachePrepStmts=true
|
||||
#db.dataSource.prepStmtCacheSize=250
|
||||
#db.dataSource.prepStmtCacheSqlLimit=2048
|
||||
#db.connectionTimeout=30000
|
||||
@@ -1 +1 @@
|
||||
ALTER TABLE WEB_HOOK ADD COLUMN TOKEN VARCHAR(100);
|
||||
ALTER TABLE WEB_HOOK ADD COLUMN TOKEN VARCHAR(100);
|
||||
|
||||
3
src/main/resources/update/3_14.sql
Normal file
3
src/main/resources/update/3_14.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE WEB_HOOK ADD COLUMN CTYPE VARCHAR(10);
|
||||
|
||||
UPDATE WEB_HOOK SET CTYPE = 'form';
|
||||
@@ -20,6 +20,16 @@ case class ApiIssue(
|
||||
body: String)(repositoryName: RepositoryName, isPullRequest: Boolean){
|
||||
val comments_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/issues/${number}/comments")
|
||||
val html_url = ApiPath(s"/${repositoryName.fullName}/${if(isPullRequest){ "pull" }else{ "issues" }}/${number}")
|
||||
val pull_request = if (isPullRequest) {
|
||||
Some(Map(
|
||||
"url" -> ApiPath(s"/api/v3/repos/${repositoryName.fullName}/pulls/${number}"),
|
||||
"html_url" -> ApiPath(s"/${repositoryName.fullName}/pull/${number}")
|
||||
// "diff_url" -> ApiPath(s"/${repositoryName.fullName}/pull/${number}.diff"),
|
||||
// "patch_url" -> ApiPath(s"/${repositoryName.fullName}/pull/${number}.patch")
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
object ApiIssue{
|
||||
|
||||
@@ -39,7 +39,7 @@ object ApiRepository{
|
||||
description = repository.description.getOrElse(""),
|
||||
watchers = 0,
|
||||
forks = forkedCount,
|
||||
`private` = repository.`private`,
|
||||
`private` = repository.isPrivate,
|
||||
default_branch = repository.defaultBranch,
|
||||
owner = owner
|
||||
)(urlIsHtmlUrl)
|
||||
|
||||
@@ -29,8 +29,8 @@ object ApiUser{
|
||||
def apply(user: Account): ApiUser = ApiUser(
|
||||
login = user.userName,
|
||||
email = user.mailAddress,
|
||||
`type` = if(user.groupAccount){ "Organization" }else{ "User" },
|
||||
site_admin = user.administrator,
|
||||
`type` = if(user.isGroupAccount){ "Organization" }else{ "User" },
|
||||
site_admin = user.isAdmin,
|
||||
created_at = user.registeredDate
|
||||
)
|
||||
}
|
||||
|
||||
@@ -114,23 +114,23 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
// Public Activity
|
||||
case "activity" =>
|
||||
gitbucket.core.account.html.activity(account,
|
||||
if(account.groupAccount) Nil else getGroupsByUserName(userName),
|
||||
if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||
getActivitiesByUser(userName, true))
|
||||
|
||||
// Members
|
||||
case "members" if(account.groupAccount) => {
|
||||
case "members" if(account.isGroupAccount) => {
|
||||
val members = getGroupMembers(account.userName)
|
||||
gitbucket.core.account.html.members(account, members.map(_.userName),
|
||||
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.manager }))
|
||||
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager }))
|
||||
}
|
||||
|
||||
// Repositories
|
||||
case _ => {
|
||||
val members = getGroupMembers(account.userName)
|
||||
gitbucket.core.account.html.repositories(account,
|
||||
if(account.groupAccount) Nil else getGroupsByUserName(userName),
|
||||
if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||
getVisibleRepositories(context.loginAccount, Some(userName)),
|
||||
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.manager }))
|
||||
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager }))
|
||||
}
|
||||
}
|
||||
} getOrElse NotFound
|
||||
@@ -190,7 +190,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
// removeUserRelatedData(userName)
|
||||
|
||||
removeUserRelatedData(userName)
|
||||
updateAccount(account.copy(removed = true))
|
||||
updateAccount(account.copy(isRemoved = true))
|
||||
}
|
||||
|
||||
session.invalidate
|
||||
@@ -360,7 +360,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
case _: List[String] =>
|
||||
val managerPermissions = groups.map { group =>
|
||||
val members = getGroupMembers(group)
|
||||
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.manager })
|
||||
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager })
|
||||
}
|
||||
helper.html.forkrepository(
|
||||
repository,
|
||||
@@ -389,7 +389,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
repositoryName = repository.name,
|
||||
userName = accountName,
|
||||
description = repository.repository.description,
|
||||
isPrivate = repository.repository.`private`,
|
||||
isPrivate = repository.repository.isPrivate,
|
||||
originRepositoryName = Some(originRepositoryName),
|
||||
originUserName = Some(originUserName),
|
||||
parentRepositoryName = Some(repository.name),
|
||||
@@ -398,7 +398,7 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
||||
|
||||
// Add collaborators for group repository
|
||||
val ownerAccount = getAccountByUserName(accountName).get
|
||||
if(ownerAccount.groupAccount){
|
||||
if(ownerAccount.isGroupAccount){
|
||||
getGroupMembers(accountName).foreach { member =>
|
||||
addCollaborator(accountName, repository.name, member.userName)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
if(account == null){
|
||||
// Redirect to login form
|
||||
httpResponse.sendRedirect(baseUrl + "/signin?redirect=" + StringUtil.urlEncode(path))
|
||||
} else if(account.administrator){
|
||||
} else if(account.isAdmin){
|
||||
// H2 Console (administrators only)
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
|
||||
@@ -108,7 +108,9 @@ trait DashboardControllerBase extends ControllerBase {
|
||||
case _ => condition.copy(author = Some(userName))
|
||||
},
|
||||
filter,
|
||||
getGroupNames(userName))
|
||||
getGroupNames(userName),
|
||||
getVisibleRepositories(context.loginAccount, withoutPhysicalInfo = true),
|
||||
getUserRepositories(userName, withoutPhysicalInfo = true))
|
||||
}
|
||||
|
||||
private def searchPullRequests(filter: String) = {
|
||||
@@ -131,7 +133,9 @@ trait DashboardControllerBase extends ControllerBase {
|
||||
case _ => condition.copy(author = Some(userName))
|
||||
},
|
||||
filter,
|
||||
getGroupNames(userName))
|
||||
getGroupNames(userName),
|
||||
getVisibleRepositories(context.loginAccount, withoutPhysicalInfo = true),
|
||||
getUserRepositories(userName, withoutPhysicalInfo = true))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import gitbucket.core.util.{Keys, FileUtil}
|
||||
import gitbucket.core.model.Account
|
||||
import gitbucket.core.service.{AccountService, RepositoryService}
|
||||
import gitbucket.core.servlet.Database
|
||||
import gitbucket.core.util._
|
||||
import gitbucket.core.util.ControlUtil._
|
||||
import gitbucket.core.util.Directory._
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.dircache.DirCache
|
||||
import org.eclipse.jgit.lib.{FileMode, Constants}
|
||||
import org.scalatra._
|
||||
import org.scalatra.servlet.{MultipartConfig, FileUploadSupport, FileItem}
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.commons.io.{IOUtils, FileUtils}
|
||||
|
||||
/**
|
||||
* Provides Ajax based file upload functionality.
|
||||
*
|
||||
* This servlet saves uploaded file.
|
||||
*/
|
||||
class FileUploadController extends ScalatraServlet with FileUploadSupport {
|
||||
class FileUploadController extends ScalatraServlet with FileUploadSupport with RepositoryService with AccountService {
|
||||
|
||||
configureMultipartHandling(MultipartConfig(maxFileSize = Some(3 * 1024 * 1024)))
|
||||
|
||||
@@ -31,6 +37,54 @@ class FileUploadController extends ScalatraServlet with FileUploadSupport {
|
||||
}, FileUtil.isUploadableType)
|
||||
}
|
||||
|
||||
post("/wiki/:owner/:repository"){
|
||||
// Don't accept not logged-in users
|
||||
session.get(Keys.Session.LoginAccount).collect { case loginAccount: Account =>
|
||||
val owner = params("owner")
|
||||
val repository = params("repository")
|
||||
|
||||
// Check whether logged-in user is collaborator
|
||||
collaboratorsOnly(owner, repository, loginAccount){
|
||||
execute({ (file, fileId) =>
|
||||
val fileName = file.getName
|
||||
LockUtil.lock(s"${owner}/${repository}/wiki") {
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))) { git =>
|
||||
val builder = DirCache.newInCore.builder()
|
||||
val inserter = git.getRepository.newObjectInserter()
|
||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||
|
||||
if(headId != null){
|
||||
JGitUtil.processTree(git, headId){ (path, tree) =>
|
||||
if(path != fileName){
|
||||
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val bytes = IOUtils.toByteArray(file.getInputStream)
|
||||
builder.add(JGitUtil.createDirCacheEntry(fileName, FileMode.REGULAR_FILE, inserter.insert(Constants.OBJ_BLOB, bytes)))
|
||||
builder.finish()
|
||||
|
||||
val newHeadId = JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter),
|
||||
Constants.HEAD, loginAccount.userName, loginAccount.mailAddress, s"Uploaded ${fileName}")
|
||||
|
||||
fileName
|
||||
}
|
||||
}
|
||||
}, FileUtil.isImage)
|
||||
}
|
||||
} getOrElse BadRequest
|
||||
}
|
||||
|
||||
private def collaboratorsOnly(owner: String, repository: String, loginAccount: Account)(action: => Any): Any = {
|
||||
implicit val session = Database.getSession(request)
|
||||
loginAccount match {
|
||||
case x if(x.isAdmin) => action
|
||||
case x if(getCollaborators(owner, repository).contains(x.userName)) => action
|
||||
case _ => BadRequest
|
||||
}
|
||||
}
|
||||
|
||||
private def execute(f: (FileItem, String) => Unit, mimeTypeChcker: (String) => Boolean) = fileParams.get("file") match {
|
||||
case Some(file) if(mimeTypeChcker(file.name)) =>
|
||||
defining(FileUtil.generateFileId){ fileId =>
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import gitbucket.core.api._
|
||||
import gitbucket.core.helper.xml
|
||||
import gitbucket.core.model.Account
|
||||
import gitbucket.core.service.{RepositoryService, ActivityService, AccountService, RepositorySearchService, IssuesService}
|
||||
import gitbucket.core.service._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util.ControlUtil._
|
||||
import gitbucket.core.util.{LDAPUtil, Keys, UsersAuthenticator, ReferrerAuthenticator, StringUtil}
|
||||
@@ -110,7 +109,7 @@ trait IndexControllerBase extends ControllerBase {
|
||||
get("/_user/proposals")(usersOnly {
|
||||
contentType = formats("json")
|
||||
org.json4s.jackson.Serialization.write(
|
||||
Map("options" -> getAllUsers(false).filter(!_.groupAccount).map(_.userName).toArray)
|
||||
Map("options" -> getAllUsers(false).filter(!_.isGroupAccount).map(_.userName).toArray)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -138,13 +137,21 @@ trait IndexControllerBase extends ControllerBase {
|
||||
|
||||
target.toLowerCase match {
|
||||
case "issue" => gitbucket.core.search.html.issues(
|
||||
searchIssues(repository.owner, repository.name, query),
|
||||
countFiles(repository.owner, repository.name, query),
|
||||
searchIssues(repository.owner, repository.name, query),
|
||||
countWikiPages(repository.owner, repository.name, query),
|
||||
query, page, repository)
|
||||
|
||||
case "wiki" => gitbucket.core.search.html.wiki(
|
||||
countFiles(repository.owner, repository.name, query),
|
||||
countIssues(repository.owner, repository.name, query),
|
||||
searchWikiPages(repository.owner, repository.name, query),
|
||||
query, page, repository)
|
||||
|
||||
case _ => gitbucket.core.search.html.code(
|
||||
searchFiles(repository.owner, repository.name, query),
|
||||
countIssues(repository.owner, repository.name, query),
|
||||
countWikiPages(repository.owner, repository.name, query),
|
||||
query, page, repository)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
_,
|
||||
getComments(owner, name, issueId.toInt),
|
||||
getIssueLabels(owner, name, issueId.toInt),
|
||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.groupAccount) Nil else List(owner))).sorted,
|
||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted,
|
||||
getMilestonesWithIssueCount(owner, name),
|
||||
getLabels(owner, name),
|
||||
hasWritePermission(owner, name, context.loginAccount),
|
||||
@@ -79,7 +79,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
get("/:owner/:repository/issues/new")(readableUsersOnly { repository =>
|
||||
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||
html.create(
|
||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.groupAccount) Nil else List(owner))).sorted,
|
||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted,
|
||||
getMilestones(owner, name),
|
||||
getLabels(owner, name),
|
||||
hasWritePermission(owner, name, context.loginAccount),
|
||||
@@ -380,7 +380,7 @@ trait IssuesControllerBase extends ControllerBase {
|
||||
"issues",
|
||||
searchIssue(condition, false, (page - 1) * IssueLimit, IssueLimit, owner -> repoName),
|
||||
page,
|
||||
if(!getAccountByUserName(owner).exists(_.groupAccount)){
|
||||
if(!getAccountByUserName(owner).exists(_.isGroupAccount)){
|
||||
(getCollaborators(owner, repoName) :+ owner).sorted
|
||||
} else {
|
||||
getCollaborators(owner, repoName)
|
||||
|
||||
@@ -23,6 +23,7 @@ trait LabelsControllerBase extends ControllerBase {
|
||||
"labelColor" -> trim(label("Color", text(required, color)))
|
||||
)(LabelForm.apply)
|
||||
|
||||
|
||||
get("/:owner/:repository/issues/labels")(referrersOnly { repository =>
|
||||
html.list(
|
||||
getLabels(repository.owner, repository.name),
|
||||
@@ -84,7 +85,11 @@ trait LabelsControllerBase extends ControllerBase {
|
||||
override def validate(name: String, value: String, params: Map[String, String], messages: Messages): Option[String] = {
|
||||
val owner = params("owner")
|
||||
val repository = params("repository")
|
||||
getLabel(owner, repository, value).map(_ => "Name has already been taken.")
|
||||
params.get("labelId").map { labelId =>
|
||||
getLabel(owner, repository, value).filter(_.labelId != labelId.toInt).map(_ => "Name has already been taken.")
|
||||
}.getOrElse {
|
||||
getLabel(owner, repository, value).map(_ => "Name has already been taken.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
(commits.flatten.map(commit => getCommitComments(owner, name, commit.id, true)).flatten.toList ::: getComments(owner, name, issueId))
|
||||
.sortWith((a, b) => a.registeredDate before b.registeredDate),
|
||||
getIssueLabels(owner, name, issueId),
|
||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.groupAccount) Nil else List(owner))).sorted,
|
||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted,
|
||||
getMilestonesWithIssueCount(owner, name),
|
||||
getLabels(owner, name),
|
||||
commits,
|
||||
@@ -178,7 +178,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
}
|
||||
val existIds = using(Git.open(Directory.getRepositoryDir(owner, name))) { git => JGitUtil.getAllCommitIds(git) }.toSet
|
||||
pullRemote(owner, name, pullreq.requestBranch, pullreq.userName, pullreq.repositoryName, pullreq.branch, loginAccount,
|
||||
"Merge branch '${alias}' into ${pullreq.requestBranch}") match {
|
||||
s"Merge branch '${alias}' into ${pullreq.requestBranch}") match {
|
||||
case None => // conflict
|
||||
flash += "error" -> s"Can't automatic merging branch '${alias}' into ${pullreq.requestBranch}."
|
||||
case Some(oldId) =>
|
||||
@@ -370,7 +370,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
originRepository,
|
||||
forkedRepository,
|
||||
hasWritePermission(originRepository.owner, originRepository.name, context.loginAccount),
|
||||
(getCollaborators(originRepository.owner, originRepository.name) ::: (if(getAccountByUserName(originRepository.owner).get.groupAccount) Nil else List(originRepository.owner))).sorted,
|
||||
(getCollaborators(originRepository.owner, originRepository.name) ::: (if(getAccountByUserName(originRepository.owner).get.isGroupAccount) Nil else List(originRepository.owner))).sorted,
|
||||
getMilestones(originRepository.owner, originRepository.name),
|
||||
getLabels(originRepository.owner, originRepository.name)
|
||||
)
|
||||
@@ -524,7 +524,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
||||
"pulls",
|
||||
searchIssue(condition, true, (page - 1) * PullRequestLimit, PullRequestLimit, owner -> repoName),
|
||||
page,
|
||||
if(!getAccountByUserName(owner).exists(_.groupAccount)){
|
||||
if(!getAccountByUserName(owner).exists(_.isGroupAccount)){
|
||||
(getCollaborators(owner, repoName) :+ owner).sorted
|
||||
} else {
|
||||
getCollaborators(owner, repoName)
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.scalatra.i18n.Messages
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.Constants
|
||||
import org.eclipse.jgit.lib.ObjectId
|
||||
import gitbucket.core.model.WebHookContentType
|
||||
|
||||
|
||||
class RepositorySettingsController extends RepositorySettingsControllerBase
|
||||
@@ -49,13 +50,16 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
)(CollaboratorForm.apply)
|
||||
|
||||
// for web hook url addition
|
||||
case class WebHookForm(url: String, events: Set[WebHook.Event], token: Option[String])
|
||||
case class WebHookForm(url: String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])
|
||||
|
||||
def webHookForm(update:Boolean) = mapping(
|
||||
"url" -> trim(label("url", text(required, webHook(update)))),
|
||||
"events" -> webhookEvents,
|
||||
"ctype" -> label("ctype", text()),
|
||||
"token" -> optional(trim(label("token", text(maxlength(100)))))
|
||||
)(WebHookForm.apply)
|
||||
)(
|
||||
(url, events, ctype, token) => WebHookForm(url, events, WebHookContentType.valueOf(ctype), token)
|
||||
)
|
||||
|
||||
// for transfer ownership
|
||||
case class TransferOwnerShipForm(newOwner: String)
|
||||
@@ -87,7 +91,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
repository.name,
|
||||
form.description,
|
||||
repository.repository.parentUserName.map { _ =>
|
||||
repository.repository.`private`
|
||||
repository.repository.isPrivate
|
||||
} getOrElse form.isPrivate
|
||||
)
|
||||
// Change repository name
|
||||
@@ -148,7 +152,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
get("/:owner/:repository/settings/collaborators")(ownerOnly { repository =>
|
||||
html.collaborators(
|
||||
getCollaborators(repository.owner, repository.name),
|
||||
getAccountByUserName(repository.owner).get.groupAccount,
|
||||
getAccountByUserName(repository.owner).get.isGroupAccount,
|
||||
repository)
|
||||
})
|
||||
|
||||
@@ -156,7 +160,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Add the collaborator.
|
||||
*/
|
||||
post("/:owner/:repository/settings/collaborators/add", collaboratorForm)(ownerOnly { (form, repository) =>
|
||||
if(!getAccountByUserName(repository.owner).get.groupAccount){
|
||||
if(!getAccountByUserName(repository.owner).get.isGroupAccount){
|
||||
addCollaborator(repository.owner, repository.name, form.userName)
|
||||
}
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
||||
@@ -166,7 +170,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Add the collaborator.
|
||||
*/
|
||||
get("/:owner/:repository/settings/collaborators/remove")(ownerOnly { repository =>
|
||||
if(!getAccountByUserName(repository.owner).get.groupAccount){
|
||||
if(!getAccountByUserName(repository.owner).get.isGroupAccount){
|
||||
removeCollaborator(repository.owner, repository.name, params("name"))
|
||||
}
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/collaborators")
|
||||
@@ -183,7 +187,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Display the web hook edit page.
|
||||
*/
|
||||
get("/:owner/:repository/settings/hooks/new")(ownerOnly { repository =>
|
||||
val webhook = WebHook(repository.owner, repository.name, "", None)
|
||||
val webhook = WebHook(repository.owner, repository.name, "", WebHookContentType.FORM, None)
|
||||
html.edithooks(webhook, Set(WebHook.Push), repository, flash.get("info"), true)
|
||||
})
|
||||
|
||||
@@ -191,7 +195,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Add the web hook URL.
|
||||
*/
|
||||
post("/:owner/:repository/settings/hooks/new", webHookForm(false))(ownerOnly { (form, repository) =>
|
||||
addWebHook(repository.owner, repository.name, form.url, form.events, form.token)
|
||||
addWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
||||
flash += "info" -> s"Webhook ${form.url} created"
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||
})
|
||||
@@ -221,7 +225,8 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
|
||||
val url = params("url")
|
||||
val token = Some(params("token"))
|
||||
val dummyWebHookInfo = WebHook(repository.owner, repository.name, url, token)
|
||||
val ctype = WebHookContentType.valueOf(params("ctype"))
|
||||
val dummyWebHookInfo = WebHook(repository.owner, repository.name, url, ctype, token)
|
||||
val dummyPayload = {
|
||||
val ownerAccount = getAccountByUserName(repository.owner).get
|
||||
val commits = if(repository.commitCount == 0) List.empty else git.log
|
||||
@@ -280,7 +285,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
* Update web hook settings.
|
||||
*/
|
||||
post("/:owner/:repository/settings/hooks/edit", webHookForm(true))(ownerOnly { (form, repository) =>
|
||||
updateWebHook(repository.owner, repository.name, form.url, form.events, form.token)
|
||||
updateWebHook(repository.owner, repository.name, form.url, form.events, form.ctype, form.token)
|
||||
flash += "info" -> s"webhook ${form.url} updated"
|
||||
redirect(s"/${repository.owner}/${repository.name}/settings/hooks")
|
||||
})
|
||||
@@ -364,7 +369,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||
getAccountByUserName(value) match {
|
||||
case None => Some("User does not exist.")
|
||||
case Some(x) if(x.groupAccount)
|
||||
case Some(x) if(x.isGroupAccount)
|
||||
=> Some("User does not exist.")
|
||||
case Some(x) if(x.userName == params("owner") || getCollaborators(params("owner"), params("repository")).contains(x.userName))
|
||||
=> Some("User can access this repository already.")
|
||||
|
||||
@@ -497,6 +497,10 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
getForkedRepositories(
|
||||
repository.repository.originUserName.getOrElse(repository.owner),
|
||||
repository.repository.originRepositoryName.getOrElse(repository.name)),
|
||||
context.loginAccount match {
|
||||
case None => List()
|
||||
case account: Option[Account] => getGroupsByUserName(account.get.userName)
|
||||
}, // groups of current user
|
||||
repository)
|
||||
})
|
||||
|
||||
@@ -507,13 +511,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
val ref = multiParams("splat").head
|
||||
JGitUtil.getTreeId(git, ref).map{ treeId =>
|
||||
html.find(ref,
|
||||
treeId,
|
||||
repository,
|
||||
context.loginAccount match {
|
||||
case None => List()
|
||||
case account: Option[Account] => getGroupsByUserName(account.get.userName)
|
||||
})
|
||||
html.find(ref, treeId, repository)
|
||||
} getOrElse NotFound
|
||||
}
|
||||
})
|
||||
@@ -575,10 +573,6 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
|
||||
html.files(revision, repository,
|
||||
if(path == ".") Nil else path.split("/").toList, // current path
|
||||
context.loginAccount match {
|
||||
case None => List()
|
||||
case account: Option[Account] => getGroupsByUserName(account.get.userName)
|
||||
}, // groups of current user
|
||||
new JGitUtil.CommitInfo(lastModifiedCommit), // last modified commit
|
||||
files, readme, hasWritePermission(repository.owner, repository.name, context.loginAccount),
|
||||
getPullRequestFromBranch(repository.owner, repository.name, revstr, repository.repository.defaultBranch),
|
||||
|
||||
@@ -157,7 +157,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
get("/admin/users")(adminOnly {
|
||||
val includeRemoved = params.get("includeRemoved").map(_.toBoolean).getOrElse(false)
|
||||
val users = getAllUsers(includeRemoved)
|
||||
val members = users.collect { case account if(account.groupAccount) =>
|
||||
val members = users.collect { case account if(account.isGroupAccount) =>
|
||||
account.userName -> getGroupMembers(account.userName).map(_.userName)
|
||||
}.toMap
|
||||
|
||||
@@ -196,12 +196,12 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
}
|
||||
|
||||
updateAccount(account.copy(
|
||||
password = form.password.map(sha1).getOrElse(account.password),
|
||||
fullName = form.fullName,
|
||||
mailAddress = form.mailAddress,
|
||||
administrator = form.isAdmin,
|
||||
url = form.url,
|
||||
removed = form.isRemoved))
|
||||
password = form.password.map(sha1).getOrElse(account.password),
|
||||
fullName = form.fullName,
|
||||
mailAddress = form.mailAddress,
|
||||
isAdmin = form.isAdmin,
|
||||
url = form.url,
|
||||
isRemoved = form.isRemoved))
|
||||
|
||||
updateImage(userName, form.fileId, form.clearImage)
|
||||
redirect("/admin/users")
|
||||
|
||||
@@ -12,7 +12,8 @@ import org.eclipse.jgit.api.Git
|
||||
import org.scalatra.i18n.Messages
|
||||
|
||||
class WikiController extends WikiControllerBase
|
||||
with WikiService with RepositoryService with AccountService with ActivityService with CollaboratorsAuthenticator with ReferrerAuthenticator
|
||||
with WikiService with RepositoryService with AccountService with ActivityService
|
||||
with CollaboratorsAuthenticator with ReferrerAuthenticator
|
||||
|
||||
trait WikiControllerBase extends ControllerBase {
|
||||
self: WikiService with RepositoryService with ActivityService with CollaboratorsAuthenticator with ReferrerAuthenticator =>
|
||||
|
||||
@@ -11,7 +11,7 @@ trait AccountComponent { self: Profile =>
|
||||
val fullName = column[String]("FULL_NAME")
|
||||
val mailAddress = column[String]("MAIL_ADDRESS")
|
||||
val password = column[String]("PASSWORD")
|
||||
val administrator = column[Boolean]("ADMINISTRATOR")
|
||||
val isAdmin = column[Boolean]("ADMINISTRATOR")
|
||||
val url = column[String]("URL")
|
||||
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||
@@ -19,7 +19,7 @@ trait AccountComponent { self: Profile =>
|
||||
val image = column[String]("IMAGE")
|
||||
val groupAccount = column[Boolean]("GROUP_ACCOUNT")
|
||||
val removed = column[Boolean]("REMOVED")
|
||||
def * = (userName, fullName, mailAddress, password, administrator, url.?, registeredDate, updatedDate, lastLoginDate.?, image.?, groupAccount, removed) <> (Account.tupled, Account.unapply)
|
||||
def * = (userName, fullName, mailAddress, password, isAdmin, url.?, registeredDate, updatedDate, lastLoginDate.?, image.?, groupAccount, removed) <> (Account.tupled, Account.unapply)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ case class Account(
|
||||
fullName: String,
|
||||
mailAddress: String,
|
||||
password: String,
|
||||
administrator: Boolean,
|
||||
isAdmin: Boolean,
|
||||
url: Option[String],
|
||||
registeredDate: java.util.Date,
|
||||
updatedDate: java.util.Date,
|
||||
lastLoginDate: Option[java.util.Date],
|
||||
image: Option[String],
|
||||
groupAccount: Boolean,
|
||||
removed: Boolean
|
||||
isGroupAccount: Boolean,
|
||||
isRemoved: Boolean
|
||||
)
|
||||
|
||||
@@ -8,13 +8,13 @@ trait GroupMemberComponent { self: Profile =>
|
||||
class GroupMembers(tag: Tag) extends Table[GroupMember](tag, "GROUP_MEMBER") {
|
||||
val groupName = column[String]("GROUP_NAME", O PrimaryKey)
|
||||
val userName = column[String]("USER_NAME", O PrimaryKey)
|
||||
val manager = column[Boolean]("MANAGER")
|
||||
def * = (groupName, userName, manager) <> (GroupMember.tupled, GroupMember.unapply)
|
||||
val isManager = column[Boolean]("MANAGER")
|
||||
def * = (groupName, userName, isManager) <> (GroupMember.tupled, GroupMember.unapply)
|
||||
}
|
||||
}
|
||||
|
||||
case class GroupMember(
|
||||
groupName: String,
|
||||
userName: String,
|
||||
manager: Boolean
|
||||
isManager: Boolean
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ trait RepositoryComponent extends TemplateComponent { self: Profile =>
|
||||
case class Repository(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
`private`: Boolean,
|
||||
isPrivate: Boolean,
|
||||
description: Option[String],
|
||||
defaultBranch: String,
|
||||
registeredDate: java.util.Date,
|
||||
|
||||
@@ -3,21 +3,42 @@ package gitbucket.core.model
|
||||
trait WebHookComponent extends TemplateComponent { self: Profile =>
|
||||
import profile.simple._
|
||||
|
||||
implicit val whContentTypeColumnType = MappedColumnType.base[WebHookContentType, String](whct => whct.code , code => WebHookContentType.valueOf(code))
|
||||
|
||||
lazy val WebHooks = TableQuery[WebHooks]
|
||||
|
||||
class WebHooks(tag: Tag) extends Table[WebHook](tag, "WEB_HOOK") with BasicTemplate {
|
||||
val url = column[String]("URL")
|
||||
val token = column[Option[String]]("TOKEN", O.Nullable)
|
||||
def * = (userName, repositoryName, url, token) <> ((WebHook.apply _).tupled, WebHook.unapply)
|
||||
val ctype = column[WebHookContentType]("CTYPE", O.NotNull)
|
||||
def * = (userName, repositoryName, url, ctype, token) <> ((WebHook.apply _).tupled, WebHook.unapply)
|
||||
|
||||
def byPrimaryKey(owner: String, repository: String, url: String) = byRepository(owner, repository) && (this.url === url.bind)
|
||||
}
|
||||
}
|
||||
|
||||
case class WebHookContentType(val code: String, val ctype: String)
|
||||
|
||||
object WebHookContentType {
|
||||
object JSON extends WebHookContentType("json", "application/json")
|
||||
|
||||
object FORM extends WebHookContentType("form", "application/x-www-form-urlencoded")
|
||||
|
||||
val values: Vector[WebHookContentType] = Vector(JSON, FORM)
|
||||
|
||||
private val map: Map[String, WebHookContentType] = values.map(enum => enum.code -> enum).toMap
|
||||
|
||||
def apply(code: String): WebHookContentType = map(code)
|
||||
|
||||
def valueOf(code: String): WebHookContentType = map(code)
|
||||
def valueOpt(code: String): Option[WebHookContentType] = map.get(code)
|
||||
}
|
||||
|
||||
case class WebHook(
|
||||
userName: String,
|
||||
repositoryName: String,
|
||||
url: String,
|
||||
ctype: WebHookContentType,
|
||||
token: Option[String]
|
||||
)
|
||||
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
package gitbucket.core.plugin
|
||||
|
||||
import javax.servlet.ServletContext
|
||||
import gitbucket.core.controller.ControllerBase
|
||||
import gitbucket.core.controller.{Context, ControllerBase}
|
||||
import gitbucket.core.model.Account
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import gitbucket.core.util.ControlUtil._
|
||||
import gitbucket.core.util.Version
|
||||
|
||||
/**
|
||||
* Trait for define plugin interface.
|
||||
* To provide plugin, put Plugin class which mixed in this trait into the package root.
|
||||
* To provide a plugin, put a Plugin class which extends this class into the package root.
|
||||
*/
|
||||
trait Plugin {
|
||||
abstract class Plugin {
|
||||
|
||||
val pluginId: String
|
||||
val pluginName: String
|
||||
@@ -77,6 +79,76 @@ trait Plugin {
|
||||
*/
|
||||
def receiveHooks(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[ReceiveHook] = Nil
|
||||
|
||||
/**
|
||||
* Override to add global menus.
|
||||
*/
|
||||
val globalMenus: Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add global menus.
|
||||
*/
|
||||
def globalMenus(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add repository menus.
|
||||
*/
|
||||
val repositoryMenus: Seq[(RepositoryInfo, Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add repository menus.
|
||||
*/
|
||||
def repositoryMenus(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(RepositoryInfo, Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add repository setting tabs.
|
||||
*/
|
||||
val repositorySettingTabs: Seq[(RepositoryInfo, Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add repository setting tabs.
|
||||
*/
|
||||
def repositorySettingTabs(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(RepositoryInfo, Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add profile tabs.
|
||||
*/
|
||||
val profileTabs: Seq[(Account, Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add profile tabs.
|
||||
*/
|
||||
def profileTabs(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Account, Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add system setting menus.
|
||||
*/
|
||||
val systemSettingMenus: Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add system setting menus.
|
||||
*/
|
||||
def systemSettingMenus(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add account setting menus.
|
||||
*/
|
||||
val accountSettingMenus: Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add account setting menus.
|
||||
*/
|
||||
def accountSettingMenus(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add dashboard tabs.
|
||||
*/
|
||||
val dashboardTabs: Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* Override to add dashboard tabs.
|
||||
*/
|
||||
def dashboardTabs(registry: PluginRegistry, context: ServletContext, settings: SystemSettings): Seq[(Context) => Option[Link]] = Nil
|
||||
|
||||
/**
|
||||
* This method is invoked in initialization of plugin system.
|
||||
* Register plugin functionality to PluginRegistry.
|
||||
@@ -100,6 +172,27 @@ trait Plugin {
|
||||
(receiveHooks ++ receiveHooks(registry, context, settings)).foreach { receiveHook =>
|
||||
registry.addReceiveHook(receiveHook)
|
||||
}
|
||||
(globalMenus ++ globalMenus(registry, context, settings)).foreach { globalMenu =>
|
||||
registry.addGlobalMenu(globalMenu)
|
||||
}
|
||||
(repositoryMenus ++ repositoryMenus(registry, context, settings)).foreach { repositoryMenu =>
|
||||
registry.addRepositoryMenu(repositoryMenu)
|
||||
}
|
||||
(repositorySettingTabs ++ repositorySettingTabs(registry, context, settings)).foreach { repositorySettingTab =>
|
||||
registry.addRepositorySettingTab(repositorySettingTab)
|
||||
}
|
||||
(profileTabs ++ profileTabs(registry, context, settings)).foreach { profileTab =>
|
||||
registry.addProfileTab(profileTab)
|
||||
}
|
||||
(systemSettingMenus ++ systemSettingMenus(registry, context, settings)).foreach { systemSettingMenu =>
|
||||
registry.addSystemSettingMenu(systemSettingMenu)
|
||||
}
|
||||
(accountSettingMenus ++ accountSettingMenus(registry, context, settings)).foreach { accountSettingMenu =>
|
||||
registry.addAccountSettingMenu(accountSettingMenu)
|
||||
}
|
||||
(dashboardTabs ++ dashboardTabs(registry, context, settings)).foreach { dashboardTab =>
|
||||
registry.addDashboardTab(dashboardTab)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,9 +3,9 @@ package gitbucket.core.plugin
|
||||
import java.io.{File, FilenameFilter, InputStream}
|
||||
import java.net.URLClassLoader
|
||||
import javax.servlet.ServletContext
|
||||
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
||||
|
||||
import gitbucket.core.controller.{Context, ControllerBase}
|
||||
import gitbucket.core.model.Account
|
||||
import gitbucket.core.service.ProtectedBranchService.ProtectedBranchReceiveHook
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
@@ -33,6 +33,14 @@ class PluginRegistry {
|
||||
private val receiveHooks = new ListBuffer[ReceiveHook]
|
||||
receiveHooks += new ProtectedBranchReceiveHook()
|
||||
|
||||
private val globalMenus = new ListBuffer[(Context) => Option[Link]]
|
||||
private val repositoryMenus = new ListBuffer[(RepositoryInfo, Context) => Option[Link]]
|
||||
private val repositorySettingTabs = new ListBuffer[(RepositoryInfo, Context) => Option[Link]]
|
||||
private val profileTabs = new ListBuffer[(Account, Context) => Option[Link]]
|
||||
private val systemSettingMenus = new ListBuffer[(Context) => Option[Link]]
|
||||
private val accountSettingMenus = new ListBuffer[(Context) => Option[Link]]
|
||||
private val dashboardTabs = new ListBuffer[(Context) => Option[Link]]
|
||||
|
||||
def addPlugin(pluginInfo: PluginInfo): Unit = {
|
||||
plugins += pluginInfo
|
||||
}
|
||||
@@ -107,17 +115,47 @@ class PluginRegistry {
|
||||
|
||||
def getReceiveHooks: Seq[ReceiveHook] = receiveHooks.toSeq
|
||||
|
||||
private case class GlobalAction(
|
||||
method: String,
|
||||
path: String,
|
||||
function: (HttpServletRequest, HttpServletResponse, Context) => Any
|
||||
)
|
||||
def addGlobalMenu(globalMenu: (Context) => Option[Link]): Unit = {
|
||||
globalMenus += globalMenu
|
||||
}
|
||||
|
||||
private case class RepositoryAction(
|
||||
method: String,
|
||||
path: String,
|
||||
function: (HttpServletRequest, HttpServletResponse, Context, RepositoryInfo) => Any
|
||||
)
|
||||
def getGlobalMenus: Seq[(Context) => Option[Link]] = globalMenus.toSeq
|
||||
|
||||
def addRepositoryMenu(repositoryMenu: (RepositoryInfo, Context) => Option[Link]): Unit = {
|
||||
repositoryMenus += repositoryMenu
|
||||
}
|
||||
|
||||
def getRepositoryMenus: Seq[(RepositoryInfo, Context) => Option[Link]] = repositoryMenus.toSeq
|
||||
|
||||
def addRepositorySettingTab(repositorySettingTab: (RepositoryInfo, Context) => Option[Link]): Unit = {
|
||||
repositorySettingTabs += repositorySettingTab
|
||||
}
|
||||
|
||||
def getRepositorySettingTabs: Seq[(RepositoryInfo, Context) => Option[Link]] = repositorySettingTabs.toSeq
|
||||
|
||||
def addProfileTab(profileTab: (Account, Context) => Option[Link]): Unit = {
|
||||
profileTabs += profileTab
|
||||
}
|
||||
|
||||
def getProfileTabs: Seq[(Account, Context) => Option[Link]] = profileTabs.toSeq
|
||||
|
||||
def addSystemSettingMenu(systemSettingMenu: (Context) => Option[Link]): Unit = {
|
||||
systemSettingMenus += systemSettingMenu
|
||||
}
|
||||
|
||||
def getSystemSettingMenus: Seq[(Context) => Option[Link]] = systemSettingMenus.toSeq
|
||||
|
||||
def addAccountSettingMenu(accountSettingMenu: (Context) => Option[Link]): Unit = {
|
||||
accountSettingMenus += accountSettingMenu
|
||||
}
|
||||
|
||||
def getAccountSettingMenus: Seq[(Context) => Option[Link]] = accountSettingMenus.toSeq
|
||||
|
||||
def addDashboardTab(dashboardTab: (Context) => Option[Link]): Unit = {
|
||||
dashboardTabs += dashboardTab
|
||||
}
|
||||
|
||||
def getDashboardTabs: Seq[(Context) => Option[Link]] = dashboardTabs.toSeq
|
||||
|
||||
}
|
||||
|
||||
@@ -201,6 +239,8 @@ object PluginRegistry {
|
||||
|
||||
}
|
||||
|
||||
case class Link(id: String, label: String, path: String, icon: Option[String] = None)
|
||||
|
||||
case class PluginInfo(
|
||||
pluginId: String,
|
||||
pluginName: String,
|
||||
|
||||
@@ -5,8 +5,6 @@ import profile.simple._
|
||||
|
||||
import gitbucket.core.model.{Account, AccessToken}
|
||||
import gitbucket.core.util.StringUtil
|
||||
import gitbucket.core.servlet.Database._
|
||||
import io.getquill._
|
||||
|
||||
import scala.util.Random
|
||||
|
||||
@@ -29,36 +27,28 @@ trait AccessTokenService {
|
||||
var hash: String = null
|
||||
do{
|
||||
token = makeAccessTokenString
|
||||
hash = tokenToHash(token)
|
||||
} while (
|
||||
db.run(quote { (hash: String) => query[AccessToken].filter(_.tokenHash == hash).nonEmpty })(hash).head
|
||||
)
|
||||
hash = tokenToHash(token)
|
||||
}while(AccessTokens.filter(_.tokenHash === hash.bind).exists.run)
|
||||
val newToken = AccessToken(
|
||||
userName = userName,
|
||||
note = note,
|
||||
tokenHash = hash)
|
||||
|
||||
// TODO Remain Slick code
|
||||
val tokenId = (AccessTokens returning AccessTokens.map(_.accessTokenId)) += newToken
|
||||
(tokenId, token)
|
||||
}
|
||||
|
||||
def getAccountByAccessToken(token: String): Option[Account] =
|
||||
db.run(quote { (tokenHash: String) =>
|
||||
query[AccessToken].filter(_.tokenHash == tokenHash)
|
||||
.join(query[Account]).on { (t, a) => t.userName == a.userName && a.registeredDate == false }
|
||||
.map { case (t, a) => a }
|
||||
})(tokenToHash(token)).headOption
|
||||
def getAccountByAccessToken(token: String)(implicit s: Session): Option[Account] =
|
||||
Accounts
|
||||
.innerJoin(AccessTokens)
|
||||
.filter{ case (ac, t) => (ac.userName === t.userName) && (t.tokenHash === tokenToHash(token).bind) && (ac.removed === false.bind) }
|
||||
.map{ case (ac, t) => ac }
|
||||
.firstOption
|
||||
|
||||
def getAccessTokens(userName: String): List[AccessToken] =
|
||||
db.run(quote { (userName: String) =>
|
||||
query[AccessToken].filter(_.userName == userName).sortBy(_.accessTokenId)(Ord.desc)
|
||||
})(userName)
|
||||
def getAccessTokens(userName: String)(implicit s: Session): List[AccessToken] =
|
||||
AccessTokens.filter(_.userName === userName.bind).sortBy(_.accessTokenId.desc).list
|
||||
|
||||
def deleteAccessToken(userName: String, accessTokenId: Int): Unit =
|
||||
db.run(quote { (userName: String, accessTokenId: Int) =>
|
||||
query[AccessToken].filter { t => t.userName == userName && t.accessTokenId == accessTokenId }.delete
|
||||
})(List((userName, accessTokenId)))
|
||||
def deleteAccessToken(userName: String, accessTokenId: Int)(implicit s: Session): Unit =
|
||||
AccessTokens filter (t => t.userName === userName.bind && t.accessTokenId === accessTokenId) delete
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
package gitbucket.core.service
|
||||
|
||||
import java.util.Date
|
||||
|
||||
import gitbucket.core.model.{GroupMember, Account, Collaborator, Repository}
|
||||
import gitbucket.core.model.{GroupMember, Account}
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.util.{StringUtil, LDAPUtil}
|
||||
import gitbucket.core.service.SystemSettingsService.SystemSettings
|
||||
import profile.simple._
|
||||
import StringUtil._
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import gitbucket.core.servlet.Database._
|
||||
import io.getquill._
|
||||
// TODO Why is direct import required?
|
||||
import gitbucket.core.model.Profile.dateColumnType
|
||||
|
||||
trait AccountService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[AccountService])
|
||||
|
||||
def authenticate(settings: SystemSettings, userName: String, password: String): Option[Account] =
|
||||
def authenticate(settings: SystemSettings, userName: String, password: String)(implicit s: Session): Option[Account] =
|
||||
if(settings.ldapAuthentication){
|
||||
ldapAuthentication(settings, userName, password)
|
||||
} else {
|
||||
@@ -27,21 +24,22 @@ trait AccountService {
|
||||
/**
|
||||
* Authenticate by internal database.
|
||||
*/
|
||||
private def defaultAuthentication(userName: String, password: String) = {
|
||||
private def defaultAuthentication(userName: String, password: String)(implicit s: Session) = {
|
||||
getAccountByUserName(userName).collect {
|
||||
case account if(!account.groupAccount && account.password == sha1(password)) => Some(account)
|
||||
case account if(!account.isGroupAccount && account.password == sha1(password)) => Some(account)
|
||||
} getOrElse None
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate by LDAP.
|
||||
*/
|
||||
private def ldapAuthentication(settings: SystemSettings, userName: String, password: String): Option[Account] = {
|
||||
private def ldapAuthentication(settings: SystemSettings, userName: String, password: String)
|
||||
(implicit s: Session): Option[Account] = {
|
||||
LDAPUtil.authenticate(settings.ldap.get, userName, password) match {
|
||||
case Right(ldapUserInfo) => {
|
||||
// Create or update account by LDAP information
|
||||
getAccountByUserName(ldapUserInfo.userName, true) match {
|
||||
case Some(x) if(!x.removed) => {
|
||||
case Some(x) if(!x.isRemoved) => {
|
||||
if(settings.ldap.get.mailAttribute.getOrElse("").isEmpty) {
|
||||
updateAccount(x.copy(fullName = ldapUserInfo.fullName))
|
||||
} else {
|
||||
@@ -49,16 +47,16 @@ trait AccountService {
|
||||
}
|
||||
getAccountByUserName(ldapUserInfo.userName)
|
||||
}
|
||||
case Some(x) if(x.removed) => {
|
||||
case Some(x) if(x.isRemoved) => {
|
||||
logger.info("LDAP Authentication Failed: Account is already registered but disabled.")
|
||||
defaultAuthentication(userName, password)
|
||||
}
|
||||
case None => getAccountByMailAddress(ldapUserInfo.mailAddress, true) match {
|
||||
case Some(x) if(!x.removed) => {
|
||||
case Some(x) if(!x.isRemoved) => {
|
||||
updateAccount(x.copy(fullName = ldapUserInfo.fullName))
|
||||
getAccountByUserName(ldapUserInfo.userName)
|
||||
}
|
||||
case Some(x) if(x.removed) => {
|
||||
case Some(x) if(x.isRemoved) => {
|
||||
logger.info("LDAP Authentication Failed: Account is already registered but disabled.")
|
||||
defaultAuthentication(userName, password)
|
||||
}
|
||||
@@ -76,163 +74,113 @@ trait AccountService {
|
||||
}
|
||||
}
|
||||
|
||||
def getAccountByUserName(userName: String, includeRemoved: Boolean = false): Option[Account] = {
|
||||
db.run(quote { (userName: String, includeRemoved: Boolean) =>
|
||||
query[Account].filter { t =>
|
||||
if(includeRemoved){
|
||||
t.userName == userName
|
||||
} else {
|
||||
t.userName == userName && t.removed == false
|
||||
}
|
||||
}
|
||||
})(userName, includeRemoved).headOption
|
||||
}
|
||||
def getAccountByUserName(userName: String, includeRemoved: Boolean = false)(implicit s: Session): Option[Account] =
|
||||
Accounts filter(t => (t.userName === userName.bind) && (t.removed === false.bind, !includeRemoved)) firstOption
|
||||
|
||||
|
||||
def getAccountsByUserNames(userNames: Set[String], knowns:Set[Account], includeRemoved: Boolean = false): Map[String, Account] = {
|
||||
def getAccountsByUserNames(userNames: Set[String], knowns:Set[Account], includeRemoved: Boolean = false)(implicit s: Session): Map[String, Account] = {
|
||||
val map = knowns.map(a => a.userName -> a).toMap
|
||||
val needs = userNames -- map.keySet
|
||||
if(needs.isEmpty){
|
||||
map
|
||||
} else {
|
||||
map ++ db.run(quote { (userNames: Set[String]) =>
|
||||
query[Account].filter { t => userNames.contains(t.userName) && t.removed == false }
|
||||
})(userNames.toSet).map { a => a.userName -> a }.toMap
|
||||
}else{
|
||||
map ++ Accounts.filter(t => (t.userName inSetBind needs) && (t.removed === false.bind, !includeRemoved)).list.map(a => a.userName -> a).toMap
|
||||
}
|
||||
}
|
||||
|
||||
def getAccountByMailAddress(mailAddress: String, includeRemoved: Boolean = false): Option[Account] = {
|
||||
db.run(quote { (mailAddress: String, includeRemoved: Boolean) =>
|
||||
query[Account].filter { t =>
|
||||
if(includeRemoved){
|
||||
t.mailAddress.toLowerCase == mailAddress.toLowerCase
|
||||
} else {
|
||||
t.mailAddress.toLowerCase == mailAddress.toLowerCase && t.removed == false
|
||||
}
|
||||
}
|
||||
})(mailAddress, includeRemoved).headOption
|
||||
}
|
||||
def getAccountByMailAddress(mailAddress: String, includeRemoved: Boolean = false)(implicit s: Session): Option[Account] =
|
||||
Accounts filter(t => (t.mailAddress.toLowerCase === mailAddress.toLowerCase.bind) && (t.removed === false.bind, !includeRemoved)) firstOption
|
||||
|
||||
def getAllUsers(includeRemoved: Boolean = true): List[Account] = {
|
||||
db.run(
|
||||
if(includeRemoved){
|
||||
quote { query[Account].sortBy(_.userName) }
|
||||
} else {
|
||||
quote { query[Account].filter(_.removed == false).sortBy(_.userName) }
|
||||
}
|
||||
)
|
||||
}
|
||||
def getAllUsers(includeRemoved: Boolean = true)(implicit s: Session): List[Account] =
|
||||
if(includeRemoved){
|
||||
Accounts sortBy(_.userName) list
|
||||
} else {
|
||||
Accounts filter (_.removed === false.bind) sortBy(_.userName) list
|
||||
}
|
||||
|
||||
def createAccount(userName: String, password: String, fullName: String, mailAddress: String, isAdmin: Boolean, url: Option[String]): Unit = {
|
||||
db.run(quote { query[Account].insert })(Account(
|
||||
def createAccount(userName: String, password: String, fullName: String, mailAddress: String, isAdmin: Boolean, url: Option[String])
|
||||
(implicit s: Session): Unit =
|
||||
Accounts insert Account(
|
||||
userName = userName,
|
||||
password = password,
|
||||
fullName = fullName,
|
||||
mailAddress = mailAddress,
|
||||
administrator = isAdmin,
|
||||
isAdmin = isAdmin,
|
||||
url = url,
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate,
|
||||
lastLoginDate = None,
|
||||
image = None,
|
||||
groupAccount = false,
|
||||
removed = false
|
||||
))
|
||||
}
|
||||
isGroupAccount = false,
|
||||
isRemoved = false)
|
||||
|
||||
def updateAccount(account: Account): Unit = {
|
||||
db.run(quote { (userName: String, password: String, fullName: String, mailAddress: String, administrator: Boolean,
|
||||
url: Option[String], registeredDate: Date, updatedDate: Date, lastLoginDate: Option[Date], removed: Boolean) =>
|
||||
query[Account].filter(_.userName == userName).update(
|
||||
_.password -> password,
|
||||
_.fullName -> fullName,
|
||||
_.mailAddress -> mailAddress,
|
||||
_.administrator -> administrator,
|
||||
_.url -> url,
|
||||
_.registeredDate -> registeredDate,
|
||||
_.updatedDate -> updatedDate,
|
||||
_.lastLoginDate -> lastLoginDate,
|
||||
_.removed -> removed
|
||||
)
|
||||
})((
|
||||
account.userName,
|
||||
account.password,
|
||||
account.fullName,
|
||||
account.mailAddress,
|
||||
account.administrator,
|
||||
account.url,
|
||||
account.registeredDate,
|
||||
currentDate,
|
||||
account.lastLoginDate,
|
||||
account.removed
|
||||
))
|
||||
}
|
||||
def updateAccount(account: Account)(implicit s: Session): Unit =
|
||||
Accounts
|
||||
.filter { a => a.userName === account.userName.bind }
|
||||
.map { a => (a.password, a.fullName, a.mailAddress, a.isAdmin, a.url.?, a.registeredDate, a.updatedDate, a.lastLoginDate.?, a.removed) }
|
||||
.update (
|
||||
account.password,
|
||||
account.fullName,
|
||||
account.mailAddress,
|
||||
account.isAdmin,
|
||||
account.url,
|
||||
account.registeredDate,
|
||||
currentDate,
|
||||
account.lastLoginDate,
|
||||
account.isRemoved)
|
||||
|
||||
def updateAvatarImage(userName: String, image: Option[String]): Unit = {
|
||||
db.run(quote { (userName: String, image: Option[String]) =>
|
||||
query[Account].filter(_.userName == userName).update(_.image -> image)
|
||||
})((userName, image))
|
||||
}
|
||||
def updateAvatarImage(userName: String, image: Option[String])(implicit s: Session): Unit =
|
||||
Accounts.filter(_.userName === userName.bind).map(_.image.?).update(image)
|
||||
|
||||
def updateLastLoginDate(userName: String): Unit = {
|
||||
db.run(quote { (userName: String, lastLoginDate: Option[Date]) =>
|
||||
query[Account].filter(_.userName == userName).update(_.lastLoginDate -> lastLoginDate)
|
||||
})((userName, Some(currentDate)))
|
||||
}
|
||||
def updateLastLoginDate(userName: String)(implicit s: Session): Unit =
|
||||
Accounts.filter(_.userName === userName.bind).map(_.lastLoginDate).update(currentDate)
|
||||
|
||||
def createGroup(groupName: String, url: Option[String]): Unit = {
|
||||
db.run( quote { query[Account].insert })(List(Account(
|
||||
def createGroup(groupName: String, url: Option[String])(implicit s: Session): Unit =
|
||||
Accounts insert Account(
|
||||
userName = groupName,
|
||||
password = "",
|
||||
fullName = groupName,
|
||||
mailAddress = groupName + "@devnull",
|
||||
administrator = false,
|
||||
isAdmin = false,
|
||||
url = url,
|
||||
registeredDate = currentDate,
|
||||
updatedDate = currentDate,
|
||||
lastLoginDate = None,
|
||||
image = None,
|
||||
groupAccount = true,
|
||||
removed = false
|
||||
)))
|
||||
}
|
||||
isGroupAccount = true,
|
||||
isRemoved = false)
|
||||
|
||||
def updateGroup(groupName: String, url: Option[String], removed: Boolean): Unit = {
|
||||
db.run(quote { (groupName: String, url: Option[String], removed: Boolean) =>
|
||||
query[Account].filter(_.userName == groupName).update(_.url -> url, _.removed -> removed)
|
||||
})(List((groupName, url, removed)))
|
||||
}
|
||||
|
||||
def updateGroupMembers(groupName: String, members: List[(String, Boolean)]): Unit = {
|
||||
db.run(quote { (groupName: String) => query[GroupMember].filter(_.groupName == groupName).delete })(groupName)
|
||||
def updateGroup(groupName: String, url: Option[String], removed: Boolean)(implicit s: Session): Unit =
|
||||
Accounts.filter(_.userName === groupName.bind).map(t => t.url.? -> t.removed).update(url, removed)
|
||||
|
||||
def updateGroupMembers(groupName: String, members: List[(String, Boolean)])(implicit s: Session): Unit = {
|
||||
GroupMembers.filter(_.groupName === groupName.bind).delete
|
||||
members.foreach { case (userName, isManager) =>
|
||||
db.run(quote { query[GroupMember].insert })(GroupMember(groupName, userName, isManager))
|
||||
GroupMembers insert GroupMember (groupName, userName, isManager)
|
||||
}
|
||||
}
|
||||
|
||||
def getGroupMembers(groupName: String): List[GroupMember] = {
|
||||
db.run(quote { (groupName: String) =>
|
||||
query[GroupMember].filter(_.groupName == groupName).sortBy(_.userName)
|
||||
})(groupName)
|
||||
def getGroupMembers(groupName: String)(implicit s: Session): List[GroupMember] =
|
||||
GroupMembers
|
||||
.filter(_.groupName === groupName.bind)
|
||||
.sortBy(_.userName)
|
||||
.list
|
||||
|
||||
def getGroupsByUserName(userName: String)(implicit s: Session): List[String] =
|
||||
GroupMembers
|
||||
.filter(_.userName === userName.bind)
|
||||
.sortBy(_.groupName)
|
||||
.map(_.groupName)
|
||||
.list
|
||||
|
||||
def removeUserRelatedData(userName: String)(implicit s: Session): Unit = {
|
||||
GroupMembers.filter(_.userName === userName.bind).delete
|
||||
Collaborators.filter(_.collaboratorName === userName.bind).delete
|
||||
Repositories.filter(_.userName === userName.bind).delete
|
||||
}
|
||||
|
||||
def getGroupsByUserName(userName: String): List[String] = {
|
||||
db.run(quote { (userName: String) =>
|
||||
query[GroupMember].filter(_.userName == userName).sortBy(_.groupName).map(_.groupName)
|
||||
})(userName)
|
||||
}
|
||||
|
||||
def removeUserRelatedData(userName: String): Unit = {
|
||||
db.run(quote { (userName: String) => query[GroupMember].filter(_.userName == userName).delete })(userName)
|
||||
db.run(quote { (userName: String) => query[Collaborator].filter(_.collaboratorName == userName).delete })(userName)
|
||||
db.run(quote { (userName: String) => query[Repository].filter(_.userName == userName).delete })(userName)
|
||||
}
|
||||
|
||||
def getGroupNames(userName: String): List[String] = {
|
||||
List(userName) ++ db.run(quote { (userName: String) =>
|
||||
query[Collaborator].filter(_.collaboratorName == userName).sortBy(_.userName).map(_.userName)
|
||||
})(userName)
|
||||
def getGroupNames(userName: String)(implicit s: Session): List[String] = {
|
||||
List(userName) ++
|
||||
Collaborators.filter(_.collaboratorName === userName.bind).sortBy(_.userName).map(_.userName).list
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,180 +1,194 @@
|
||||
package gitbucket.core.service
|
||||
|
||||
import gitbucket.core.model._
|
||||
import gitbucket.core.model.Activity
|
||||
import gitbucket.core.model.Profile._
|
||||
import gitbucket.core.util.JGitUtil
|
||||
import profile.simple._
|
||||
|
||||
import gitbucket.core.servlet.Database._
|
||||
import io.getquill._
|
||||
|
||||
trait ActivityService {
|
||||
|
||||
def deleteOldActivities(limit: Int): Int =
|
||||
db.run (quote { (limit: Int) =>
|
||||
query[Activity].map(_.activityId).sortBy(x => x)(Ord.desc).drop(limit)
|
||||
})(limit).headOption.map { activityId =>
|
||||
db.run (
|
||||
quote { (activityId: Int) => query[Activity].filter(_.activityId <= activityId).delete }
|
||||
)(activityId)
|
||||
def deleteOldActivities(limit: Int)(implicit s: Session): Int = {
|
||||
Activities.map(_.activityId).sortBy(_ desc).drop(limit).firstOption.map { id =>
|
||||
Activities.filter(_.activityId <= id.bind).delete
|
||||
} getOrElse 0
|
||||
}
|
||||
|
||||
def getActivitiesByUser(activityUserName: String, isPublic: Boolean): List[Activity] =
|
||||
db.run(quote { (activityUserName: String, isPublic: Boolean) =>
|
||||
query[Activity].join(query[Repository]).on((a, r) => a.userName == r.userName && a.repositoryName == r.repositoryName)
|
||||
.filter { case (a, r) =>
|
||||
if(isPublic){
|
||||
a.activityUserName == activityUserName
|
||||
} else {
|
||||
a.activityUserName == activityUserName && r.`private` == false
|
||||
}
|
||||
def getActivitiesByUser(activityUserName: String, isPublic: Boolean)(implicit s: Session): List[Activity] =
|
||||
Activities
|
||||
.innerJoin(Repositories).on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||
.filter { case (t1, t2) =>
|
||||
if(isPublic){
|
||||
(t1.activityUserName === activityUserName.bind) && (t2.isPrivate === false.bind)
|
||||
} else {
|
||||
(t1.activityUserName === activityUserName.bind)
|
||||
}
|
||||
.sortBy { case (a, r) => a.activityId }(Ord.desc)
|
||||
.map { case (a, r) => a }
|
||||
.take(30)
|
||||
})(activityUserName, isPublic)
|
||||
}
|
||||
.sortBy { case (t1, t2) => t1.activityId desc }
|
||||
.map { case (t1, t2) => t1 }
|
||||
.take(30)
|
||||
.list
|
||||
|
||||
def getRecentActivities(): List[Activity] =
|
||||
db.run(quote {
|
||||
query[Activity].join(query[Repository]).on((a, r) => a.userName == r.userName && a.repositoryName == r.repositoryName)
|
||||
.filter { case (a, r) => r.`private` == false}
|
||||
.sortBy { case (a, r) => a.activityId }(Ord.desc)
|
||||
.map { case (a, r) => a }
|
||||
.take(30)
|
||||
})
|
||||
def getRecentActivities()(implicit s: Session): List[Activity] =
|
||||
Activities
|
||||
.innerJoin(Repositories).on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||
.filter { case (t1, t2) => t2.isPrivate === false.bind }
|
||||
.sortBy { case (t1, t2) => t1.activityId desc }
|
||||
.map { case (t1, t2) => t1 }
|
||||
.take(30)
|
||||
.list
|
||||
|
||||
def getRecentActivitiesByOwners(owners : Set[String]): List[Activity] =
|
||||
db.run(quote { (owners: Set[String]) =>
|
||||
query[Activity].join(query[Repository]).on((a, r) => a.userName == r.userName && a.repositoryName == r.repositoryName)
|
||||
.filter { case (a, r) => r.`private` == false || owners.contains(r.userName) }
|
||||
.sortBy { case (a, r) => a.activityId }(Ord.desc)
|
||||
.map { case (a, r) => a }
|
||||
.take(30)
|
||||
})(owners)
|
||||
def getRecentActivitiesByOwners(owners : Set[String])(implicit s: Session): List[Activity] =
|
||||
Activities
|
||||
.innerJoin(Repositories).on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||
.filter { case (t1, t2) => (t2.isPrivate === false.bind) || (t2.userName inSetBind owners) }
|
||||
.sortBy { case (t1, t2) => t1.activityId desc }
|
||||
.map { case (t1, t2) => t1 }
|
||||
.take(30)
|
||||
.list
|
||||
|
||||
def recordCreateRepositoryActivity(userName: String, repositoryName: String, activityUserName: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordCreateRepositoryActivity(userName: String, repositoryName: String, activityUserName: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"create_repository",
|
||||
s"[user:${activityUserName}] created [repo:${userName}/${repositoryName}]",
|
||||
None)
|
||||
None,
|
||||
currentDate)
|
||||
|
||||
def recordCreateIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordCreateIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
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): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordCloseIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"close_issue",
|
||||
s"[user:${activityUserName}] closed issue [issue:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title))
|
||||
Some(title),
|
||||
currentDate)
|
||||
|
||||
def recordClosePullRequestActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordClosePullRequestActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"close_issue",
|
||||
s"[user:${activityUserName}] closed pull request [pullreq:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title))
|
||||
Some(title),
|
||||
currentDate)
|
||||
|
||||
def recordReopenIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordReopenIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"reopen_issue",
|
||||
s"[user:${activityUserName}] reopened issue [issue:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title))
|
||||
Some(title),
|
||||
currentDate)
|
||||
|
||||
def recordCommentIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, comment: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordCommentIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, comment: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"comment_issue",
|
||||
s"[user:${activityUserName}] commented on issue [issue:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(cut(comment, 200)))
|
||||
Some(cut(comment, 200)),
|
||||
currentDate)
|
||||
|
||||
def recordCommentPullRequestActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, comment: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordCommentPullRequestActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, comment: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"comment_issue",
|
||||
s"[user:${activityUserName}] commented on pull request [pullreq:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(cut(comment, 200)))
|
||||
Some(cut(comment, 200)),
|
||||
currentDate)
|
||||
|
||||
def recordCommentCommitActivity(userName: String, repositoryName: String, activityUserName: String, commitId: String, comment: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordCommentCommitActivity(userName: String, repositoryName: String, activityUserName: String, commitId: String, comment: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"comment_commit",
|
||||
s"[user:${activityUserName}] commented on commit [commit:${userName}/${repositoryName}@${commitId}]",
|
||||
Some(cut(comment, 200)))
|
||||
Some(cut(comment, 200)),
|
||||
currentDate
|
||||
)
|
||||
|
||||
def recordCreateWikiPageActivity(userName: String, repositoryName: String, activityUserName: String, pageName: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordCreateWikiPageActivity(userName: String, repositoryName: String, activityUserName: String, pageName: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"create_wiki",
|
||||
s"[user:${activityUserName}] created the [repo:${userName}/${repositoryName}] wiki",
|
||||
Some(pageName))
|
||||
Some(pageName),
|
||||
currentDate)
|
||||
|
||||
def recordEditWikiPageActivity(userName: String, repositoryName: String, activityUserName: String, pageName: String, commitId: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordEditWikiPageActivity(userName: String, repositoryName: String, activityUserName: String, pageName: String, commitId: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"edit_wiki",
|
||||
s"[user:${activityUserName}] edited the [repo:${userName}/${repositoryName}] wiki",
|
||||
Some(pageName + ":" + commitId))
|
||||
Some(pageName + ":" + commitId),
|
||||
currentDate)
|
||||
|
||||
def recordPushActivity(userName: String, repositoryName: String, activityUserName: String,
|
||||
branchName: String, commits: List[JGitUtil.CommitInfo]): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
branchName: String, commits: List[JGitUtil.CommitInfo])(implicit s: Session): Unit =
|
||||
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.map { commit => commit.id + ":" + commit.shortMessage }.mkString("\n")),
|
||||
currentDate)
|
||||
|
||||
def recordCreateTagActivity(userName: String, repositoryName: String, activityUserName: String,
|
||||
tagName: String, commits: List[JGitUtil.CommitInfo]): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
tagName: String, commits: List[JGitUtil.CommitInfo])(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"create_tag",
|
||||
s"[user:${activityUserName}] created tag [tag:${userName}/${repositoryName}#${tagName}] at [repo:${userName}/${repositoryName}]",
|
||||
None)
|
||||
None,
|
||||
currentDate)
|
||||
|
||||
def recordDeleteTagActivity(userName: String, repositoryName: String, activityUserName: String,
|
||||
tagName: String, commits: List[JGitUtil.CommitInfo]): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
tagName: String, commits: List[JGitUtil.CommitInfo])(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"delete_tag",
|
||||
s"[user:${activityUserName}] deleted tag ${tagName} at [repo:${userName}/${repositoryName}]",
|
||||
None)
|
||||
None,
|
||||
currentDate)
|
||||
|
||||
def recordCreateBranchActivity(userName: String, repositoryName: String, activityUserName: String, branchName: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordCreateBranchActivity(userName: String, repositoryName: String, activityUserName: String, branchName: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"create_branch",
|
||||
s"[user:${activityUserName}] created branch [branch:${userName}/${repositoryName}#${branchName}] at [repo:${userName}/${repositoryName}]",
|
||||
None)
|
||||
None,
|
||||
currentDate)
|
||||
|
||||
def recordDeleteBranchActivity(userName: String, repositoryName: String, activityUserName: String, branchName: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordDeleteBranchActivity(userName: String, repositoryName: String, activityUserName: String, branchName: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"delete_branch",
|
||||
s"[user:${activityUserName}] deleted branch ${branchName} at [repo:${userName}/${repositoryName}]",
|
||||
None)
|
||||
None,
|
||||
currentDate)
|
||||
|
||||
def recordForkActivity(userName: String, repositoryName: String, activityUserName: String, forkedUserName: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
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}]",
|
||||
None)
|
||||
None,
|
||||
currentDate)
|
||||
|
||||
def recordPullRequestActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordPullRequestActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"open_pullreq",
|
||||
s"[user:${activityUserName}] opened pull request [pullreq:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(title))
|
||||
Some(title),
|
||||
currentDate)
|
||||
|
||||
def recordMergeActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, message: String): Unit =
|
||||
insertActivity(userName, repositoryName, activityUserName,
|
||||
def recordMergeActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, message: String)
|
||||
(implicit s: Session): Unit =
|
||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||
"merge_pullreq",
|
||||
s"[user:${activityUserName}] merged pull request [pullreq:${userName}/${repositoryName}#${issueId}]",
|
||||
Some(message))
|
||||
|
||||
private def insertActivity(userName: String, repositoryName: String, activityUserName: String, activityType: String,
|
||||
message: String, additionalInfo: Option[String]): Unit = {
|
||||
db.run(quote { query[Activity].insert })(Activity(
|
||||
userName = userName,
|
||||
repositoryName = repositoryName,
|
||||
activityUserName = activityUserName,
|
||||
activityType = activityType,
|
||||
message = message,
|
||||
additionalInfo = additionalInfo,
|
||||
activityDate = currentDate
|
||||
))
|
||||
}
|
||||
Some(message),
|
||||
currentDate)
|
||||
|
||||
private def cut(value: String, length: Int): String =
|
||||
if(value.length > length) value.substring(0, length) + "..." else value
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
package gitbucket.core.service
|
||||
|
||||
import gitbucket.core.model.CommitComment
|
||||
import gitbucket.core.util.{StringUtil, Implicits}
|
||||
|
||||
import scala.slick.jdbc.{StaticQuery => Q}
|
||||
import Q.interpolation
|
||||
import gitbucket.core.model.Profile._
|
||||
import profile.simple._
|
||||
|
||||
import gitbucket.core.servlet.Database._
|
||||
import io.getquill._
|
||||
import Implicits._
|
||||
import StringUtil._
|
||||
|
||||
|
||||
trait CommitsService {
|
||||
|
||||
def getCommitComments(owner: String, repository: String, commitId: String, includePullRequest: Boolean) =
|
||||
db.run(quote { (owner: String, repository: String, commitId: String, includePullRequest: Boolean) =>
|
||||
query[CommitComment].filter { t =>
|
||||
t.userName == owner && t.repositoryName == repository && t.commitId == commitId && (t.issueId.isEmpty || includePullRequest)
|
||||
}
|
||||
})(owner, repository, commitId, includePullRequest)
|
||||
def getCommitComments(owner: String, repository: String, commitId: String, includePullRequest: Boolean)(implicit s: Session) =
|
||||
CommitComments filter {
|
||||
t => t.byCommit(owner, repository, commitId) && (t.issueId.isEmpty || includePullRequest)
|
||||
} list
|
||||
|
||||
def getCommitComment(owner: String, repository: String, commentId: String) =
|
||||
def getCommitComment(owner: String, repository: String, commentId: String)(implicit s: Session) =
|
||||
if (commentId forall (_.isDigit))
|
||||
db.run(quote { (owner: String, repository: String, commentId: Int) =>
|
||||
query[CommitComment].filter(t => t.userName == owner && t.repositoryName == repository && t.commentId == commentId)
|
||||
})(owner, repository, commentId.toInt).headOption
|
||||
CommitComments filter { t =>
|
||||
t.byPrimaryKey(commentId.toInt) && t.byRepository(owner, repository)
|
||||
} firstOption
|
||||
else
|
||||
None
|
||||
|
||||
def createCommitComment(owner: String, repository: String, commitId: String, loginUser: String,
|
||||
content: String, fileName: Option[String], oldLine: Option[Int], newLine: Option[Int],
|
||||
issueId: Option[Int])(implicit s: Session): Int =
|
||||
CommitComments.autoInc insert CommitComment( // TODO Remain Slick code
|
||||
CommitComments.autoInc insert CommitComment(
|
||||
userName = owner,
|
||||
repositoryName = repository,
|
||||
commitId = commitId,
|
||||
@@ -41,12 +42,13 @@ trait CommitsService {
|
||||
updatedDate = currentDate,
|
||||
issueId = issueId)
|
||||
|
||||
def updateCommitComment(commentId: Int, content: String) =
|
||||
db.run(quote { (commentId: Int, content: String, updatedDate: java.util.Date) =>
|
||||
query[CommitComment].filter(_.commentId == commentId).update(_.content -> content, _.updatedDate -> updatedDate)
|
||||
})(commentId, content, currentDate)
|
||||
|
||||
def deleteCommitComment(commentId: Int) =
|
||||
db.run(quote { (commentId: Int) => query[CommitComment].filter(_.commentId == commentId).delete })(commentId)
|
||||
def updateCommitComment(commentId: Int, content: String)(implicit s: Session) =
|
||||
CommitComments
|
||||
.filter (_.byPrimaryKey(commentId))
|
||||
.map { t =>
|
||||
t.content -> t.updatedDate
|
||||
}.update (content, currentDate)
|
||||
|
||||
def deleteCommitComment(commentId: Int)(implicit s: Session) =
|
||||
CommitComments filter (_.byPrimaryKey(commentId)) delete
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ object ProtectedBranchService {
|
||||
includeAdministrators: Boolean) extends AccountService with CommitStatusService {
|
||||
|
||||
def isAdministrator(pusher: String)(implicit session: Session): Boolean =
|
||||
pusher == owner || getGroupMembers(owner).filter(gm => gm.userName == pusher && gm.manager).nonEmpty
|
||||
pusher == owner || getGroupMembers(owner).filter(gm => gm.userName == pusher && gm.isManager).nonEmpty
|
||||
|
||||
/**
|
||||
* Can't be force pushed
|
||||
|
||||
@@ -22,7 +22,7 @@ trait RepositoryCreationService {
|
||||
insertRepository(name, owner, description, isPrivate)
|
||||
|
||||
// Add collaborators for group repository
|
||||
if(ownerAccount.groupAccount){
|
||||
if(ownerAccount.isGroupAccount){
|
||||
getGroupMembers(owner).foreach { member =>
|
||||
addCollaborator(owner, name, member.userName)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,30 @@ trait RepositorySearchService { self: IssuesService =>
|
||||
}
|
||||
}
|
||||
|
||||
private def searchRepositoryFiles(git: Git, query: String): List[(String, String)] = {
|
||||
def countWikiPages(owner: String, repository: String, query: String): Int =
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
||||
if(JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||
}
|
||||
|
||||
def searchWikiPages(owner: String, repository: String, query: String): List[FileSearchResult] =
|
||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
||||
if(JGitUtil.isEmpty(git)){
|
||||
Nil
|
||||
} else {
|
||||
val files = searchRepositoryFiles(git, query)
|
||||
val commits = JGitUtil.getLatestCommitFromPaths(git, files.map(_._1), "HEAD")
|
||||
files.map { case (path, text) =>
|
||||
val (highlightText, lineNumber) = getHighlightText(text, query)
|
||||
FileSearchResult(
|
||||
path.replaceFirst("\\.md$", ""),
|
||||
commits(path).getCommitterIdent.getWhen,
|
||||
highlightText,
|
||||
lineNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def searchRepositoryFiles(git: Git, query: String): List[(String, String)] = {
|
||||
val revWalk = new RevWalk(git.getRepository)
|
||||
val objectId = git.getRepository.resolve("HEAD")
|
||||
val revCommit = revWalk.parseCommit(objectId)
|
||||
|
||||
@@ -27,7 +27,7 @@ trait RepositoryService { self: AccountService =>
|
||||
Repository(
|
||||
userName = userName,
|
||||
repositoryName = repositoryName,
|
||||
`private` = isPrivate,
|
||||
isPrivate = isPrivate,
|
||||
description = description,
|
||||
defaultBranch = "master",
|
||||
registeredDate = currentDate,
|
||||
@@ -115,7 +115,7 @@ trait RepositoryService { self: AccountService =>
|
||||
repositoryName = newRepositoryName
|
||||
)) :_*)
|
||||
|
||||
if(account.groupAccount){
|
||||
if(account.isGroupAccount){
|
||||
Collaborators.insertAll(getGroupMembers(newUserName).map(m => Collaborator(newUserName, newRepositoryName, m.userName)) :_*)
|
||||
} else {
|
||||
Collaborators.insertAll(collaborators.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||
@@ -270,9 +270,9 @@ trait RepositoryService { self: AccountService =>
|
||||
(implicit s: Session): List[RepositoryInfo] = {
|
||||
(loginAccount match {
|
||||
// for Administrators
|
||||
case Some(x) if(x.administrator) => Repositories
|
||||
case Some(x) if(x.isAdmin) => Repositories
|
||||
// for Normal Users
|
||||
case Some(x) if(!x.administrator) =>
|
||||
case Some(x) if(!x.isAdmin) =>
|
||||
Repositories filter { t => (t.isPrivate === false.bind) || (t.userName === x.userName) ||
|
||||
(Collaborators.filter { t2 => t2.byRepository(t.userName, t.repositoryName) && (t2.collaboratorName === x.userName.bind)} exists)
|
||||
}
|
||||
@@ -297,8 +297,8 @@ trait RepositoryService { self: AccountService =>
|
||||
}
|
||||
|
||||
private def getRepositoryManagers(userName: String)(implicit s: Session): Seq[String] =
|
||||
if(getAccountByUserName(userName).exists(_.groupAccount)){
|
||||
getGroupMembers(userName).collect { case x if(x.manager) => x.userName }
|
||||
if(getAccountByUserName(userName).exists(_.isGroupAccount)){
|
||||
getGroupMembers(userName).collect { case x if(x.isManager) => x.userName }
|
||||
} else {
|
||||
Seq(userName)
|
||||
}
|
||||
@@ -365,7 +365,7 @@ trait RepositoryService { self: AccountService =>
|
||||
|
||||
def hasWritePermission(owner: String, repository: String, loginAccount: Option[Account])(implicit s: Session): Boolean = {
|
||||
loginAccount match {
|
||||
case Some(a) if(a.administrator) => true
|
||||
case Some(a) if(a.isAdmin) => true
|
||||
case Some(a) if(a.userName == owner) => true
|
||||
case Some(a) if(getCollaborators(owner, repository).contains(a.userName)) => true
|
||||
case _ => false
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package gitbucket.core.service
|
||||
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
import fr.brouillard.oss.security.xhub.XHub
|
||||
import fr.brouillard.oss.security.xhub.XHub.{XHubDigest, XHubConverter}
|
||||
import gitbucket.core.api._
|
||||
@@ -12,7 +11,6 @@ import profile.simple._
|
||||
import gitbucket.core.util.JGitUtil.CommitInfo
|
||||
import gitbucket.core.util.RepositoryName
|
||||
import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
|
||||
import org.apache.http.NameValuePair
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity
|
||||
import org.apache.http.message.BasicNameValuePair
|
||||
@@ -22,6 +20,8 @@ import org.slf4j.LoggerFactory
|
||||
import scala.concurrent._
|
||||
import org.apache.http.HttpRequest
|
||||
import org.apache.http.HttpResponse
|
||||
import gitbucket.core.model.WebHookContentType
|
||||
import org.apache.http.client.entity.EntityBuilder
|
||||
|
||||
|
||||
trait WebHookService {
|
||||
@@ -52,15 +52,15 @@ trait WebHookService {
|
||||
.map{ case (w,t) => w -> t.event }
|
||||
.list.groupBy(_._1).mapValues(_.map(_._2).toSet).headOption
|
||||
|
||||
def addWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], token: Option[String])(implicit s: Session): Unit = {
|
||||
WebHooks insert WebHook(owner, repository, url, token)
|
||||
def addWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])(implicit s: Session): Unit = {
|
||||
WebHooks insert WebHook(owner, repository, url, ctype, token)
|
||||
events.toSet.map{ event: WebHook.Event =>
|
||||
WebHookEvents insert WebHookEvent(owner, repository, url, event)
|
||||
}
|
||||
}
|
||||
|
||||
def updateWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], token: Option[String])(implicit s: Session): Unit = {
|
||||
WebHooks.filter(_.byPrimaryKey(owner, repository, url)).map(w => w.token).update(token)
|
||||
def updateWebHook(owner: String, repository: String, url :String, events: Set[WebHook.Event], ctype: WebHookContentType, token: Option[String])(implicit s: Session): Unit = {
|
||||
WebHooks.filter(_.byPrimaryKey(owner, repository, url)).map(w => (w.ctype, w.token)).update((ctype, token))
|
||||
WebHookEvents.filter(_.byWebHook(owner, repository, url)).delete
|
||||
events.toSet.map{ event: WebHook.Event =>
|
||||
WebHookEvents insert WebHookEvent(owner, repository, url, event)
|
||||
@@ -100,19 +100,29 @@ trait WebHookService {
|
||||
val httpClient = HttpClientBuilder.create.addInterceptorLast(itcp).build
|
||||
logger.debug(s"start web hook invocation for ${webHook.url}")
|
||||
val httpPost = new HttpPost(webHook.url)
|
||||
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
logger.info(s"Content-Type: ${webHook.ctype.ctype}")
|
||||
httpPost.addHeader("Content-Type", webHook.ctype.ctype)
|
||||
httpPost.addHeader("X-Github-Event", event.name)
|
||||
httpPost.addHeader("X-Github-Delivery", java.util.UUID.randomUUID().toString)
|
||||
|
||||
val params: java.util.List[NameValuePair] = new java.util.ArrayList()
|
||||
params.add(new BasicNameValuePair("payload", json))
|
||||
def postContent = new UrlEncodedFormEntity(params, "UTF-8")
|
||||
httpPost.setEntity(postContent)
|
||||
|
||||
if (!webHook.token.isEmpty) {
|
||||
// TODO find a better way and see how to extract content from postContent
|
||||
val contentAsBytes = URLEncodedUtils.format(params, "UTF-8").getBytes("UTF-8")
|
||||
httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.orNull, contentAsBytes))
|
||||
webHook.ctype match {
|
||||
case WebHookContentType.FORM => {
|
||||
val params: java.util.List[NameValuePair] = new java.util.ArrayList()
|
||||
params.add(new BasicNameValuePair("payload", json))
|
||||
def postContent = new UrlEncodedFormEntity(params, "UTF-8")
|
||||
httpPost.setEntity(postContent)
|
||||
if (webHook.token.exists(_.trim.nonEmpty)) {
|
||||
// TODO find a better way and see how to extract content from postContent
|
||||
val contentAsBytes = URLEncodedUtils.format(params, "UTF-8").getBytes("UTF-8")
|
||||
httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.get, contentAsBytes))
|
||||
}
|
||||
}
|
||||
case WebHookContentType.JSON => {
|
||||
httpPost.setEntity(EntityBuilder.create().setText(json).build())
|
||||
if (!webHook.token.isEmpty) {
|
||||
httpPost.addHeader("X-Hub-Signature", XHub.generateHeaderXHubToken(XHubConverter.HEXA_LOWERCASE, XHubDigest.SHA1, webHook.token.orNull, json.getBytes("UTF-8")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val res = httpClient.execute(httpPost)
|
||||
|
||||
@@ -21,6 +21,7 @@ object AutoUpdate {
|
||||
* The history of versions. A head of this sequence is the current GitBucket version.
|
||||
*/
|
||||
val versions = Seq(
|
||||
new Version(3, 14),
|
||||
new Version(3, 13),
|
||||
new Version(3, 12),
|
||||
new Version(3, 11),
|
||||
|
||||
@@ -76,14 +76,14 @@ class BasicAuthenticationFilter extends Filter with RepositoryService with Accou
|
||||
case Array(_, repositoryOwner, repositoryName, _*) =>
|
||||
getRepository(repositoryOwner, repositoryName.replaceFirst("\\.wiki\\.git$|\\.git$", "")) match {
|
||||
case Some(repository) => {
|
||||
if(!isUpdating && !repository.repository.`private` && settings.allowAnonymousAccess){
|
||||
if(!isUpdating && !repository.repository.isPrivate && settings.allowAnonymousAccess){
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
val passed = for {
|
||||
auth <- Option(request.getHeader("Authorization"))
|
||||
Array(username, password) = decodeAuthHeader(auth).split(":", 2)
|
||||
account <- authenticate(settings, username, password)
|
||||
} yield if(isUpdating || repository.repository.`private`){
|
||||
} yield if(isUpdating || repository.repository.isPrivate){
|
||||
if(hasWritePermission(repository.owner, repository.name, Some(account))){
|
||||
request.setAttribute(Keys.Request.UserName, account.userName)
|
||||
true
|
||||
|
||||
@@ -4,14 +4,10 @@ import javax.servlet._
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import com.mchange.v2.c3p0.ComboPooledDataSource
|
||||
import gitbucket.core.util.DatabaseConfig
|
||||
import io.getquill._
|
||||
import io.getquill.naming.SnakeCase
|
||||
import io.getquill.sources.sql.idiom.H2Dialect
|
||||
import org.scalatra.ScalatraBase
|
||||
import org.slf4j.LoggerFactory
|
||||
import slick.jdbc.JdbcBackend.{Database => SlickDatabase, Session}
|
||||
import gitbucket.core.util.Keys
|
||||
import Database._
|
||||
|
||||
/**
|
||||
* Controls the transaction with the open session in view pattern.
|
||||
@@ -29,20 +25,17 @@ class TransactionFilter extends Filter {
|
||||
// assets don't need transaction
|
||||
chain.doFilter(req, res)
|
||||
} else {
|
||||
db.transaction {
|
||||
// TODO Delete after moving to quill
|
||||
Database() withTransaction { session =>
|
||||
// Register Scalatra error callback to rollback transaction
|
||||
ScalatraBase.onFailure { _ =>
|
||||
logger.debug("Rolled back transaction")
|
||||
session.rollback()
|
||||
}(req.asInstanceOf[HttpServletRequest])
|
||||
Database() withTransaction { session =>
|
||||
// Register Scalatra error callback to rollback transaction
|
||||
ScalatraBase.onFailure { _ =>
|
||||
logger.debug("Rolled back transaction")
|
||||
session.rollback()
|
||||
}(req.asInstanceOf[HttpServletRequest])
|
||||
|
||||
logger.debug("begin transaction")
|
||||
req.setAttribute(Keys.Request.DBSession, session)
|
||||
chain.doFilter(req, res)
|
||||
logger.debug("end transaction")
|
||||
}
|
||||
logger.debug("begin transaction")
|
||||
req.setAttribute(Keys.Request.DBSession, session)
|
||||
chain.doFilter(req, res)
|
||||
logger.debug("end transaction")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,9 +46,6 @@ object Database {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(Database.getClass)
|
||||
|
||||
lazy val db = source(new JdbcSourceConfig[H2Dialect, SnakeCase]("db"))
|
||||
|
||||
// TODO Delete after moving to quill
|
||||
private val dataSource: ComboPooledDataSource = {
|
||||
val ds = new ComboPooledDataSource
|
||||
ds.setDriverClass(DatabaseConfig.driver)
|
||||
@@ -66,19 +56,15 @@ object Database {
|
||||
ds
|
||||
}
|
||||
|
||||
// TODO Delete after moving to quill
|
||||
private val slickDatabase: SlickDatabase = {
|
||||
private val db: SlickDatabase = {
|
||||
SlickDatabase.forDataSource(dataSource)
|
||||
}
|
||||
|
||||
// TODO Delete after moving to quill
|
||||
def apply(): SlickDatabase = slickDatabase
|
||||
def apply(): SlickDatabase = db
|
||||
|
||||
// TODO Delete after moving to quill
|
||||
def getSession(req: ServletRequest): Session =
|
||||
req.getAttribute(Keys.Request.DBSession).asInstanceOf[Session]
|
||||
|
||||
// TODO Delete after moving to quill
|
||||
def closeDataSource(): Unit = dataSource.close
|
||||
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class DefaultGitUploadPack(owner: String, repoName: String) extends DefaultGitCo
|
||||
|
||||
override protected def runTask(user: String)(implicit session: Session): Unit = {
|
||||
getRepository(owner, repoName.replaceFirst("\\.wiki\\Z", "")).foreach { repositoryInfo =>
|
||||
if(!repositoryInfo.repository.`private` || isWritableUser(user, repositoryInfo)){
|
||||
if(!repositoryInfo.repository.isPrivate || isWritableUser(user, repositoryInfo)){
|
||||
using(Git.open(getRepositoryDir(owner, repoName))) { git =>
|
||||
val repository = git.getRepository
|
||||
val upload = new UploadPack(repository)
|
||||
|
||||
@@ -17,7 +17,7 @@ trait OneselfAuthenticator { self: ControllerBase =>
|
||||
{
|
||||
defining(request.paths){ paths =>
|
||||
context.loginAccount match {
|
||||
case Some(x) if(x.administrator) => action
|
||||
case Some(x) if(x.isAdmin) => action
|
||||
case Some(x) if(paths(0) == x.userName) => action
|
||||
case _ => Unauthorized()
|
||||
}
|
||||
@@ -38,10 +38,10 @@ trait OwnerAuthenticator { self: ControllerBase with RepositoryService with Acco
|
||||
defining(request.paths){ paths =>
|
||||
getRepository(paths(0), paths(1)).map { repository =>
|
||||
context.loginAccount match {
|
||||
case Some(x) if(x.administrator) => action(repository)
|
||||
case Some(x) if(x.isAdmin) => action(repository)
|
||||
case Some(x) if(repository.owner == x.userName) => action(repository)
|
||||
case Some(x) if(getGroupMembers(repository.owner).exists { member =>
|
||||
member.userName == x.userName && member.manager == true
|
||||
member.userName == x.userName && member.isManager == true
|
||||
}) => action(repository)
|
||||
case _ => Unauthorized()
|
||||
}
|
||||
@@ -78,7 +78,7 @@ trait AdminAuthenticator { self: ControllerBase =>
|
||||
private def authenticate(action: => Any) = {
|
||||
{
|
||||
context.loginAccount match {
|
||||
case Some(x) if(x.administrator) => action
|
||||
case Some(x) if(x.isAdmin) => action
|
||||
case _ => Unauthorized()
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService =
|
||||
defining(request.paths){ paths =>
|
||||
getRepository(paths(0), paths(1)).map { repository =>
|
||||
context.loginAccount match {
|
||||
case Some(x) if(x.administrator) => action(repository)
|
||||
case Some(x) if(x.isAdmin) => action(repository)
|
||||
case Some(x) if(paths(0) == x.userName) => action(repository)
|
||||
case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
||||
case _ => Unauthorized()
|
||||
@@ -119,11 +119,11 @@ trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
|
||||
{
|
||||
defining(request.paths){ paths =>
|
||||
getRepository(paths(0), paths(1)).map { repository =>
|
||||
if(!repository.repository.`private`){
|
||||
if(!repository.repository.isPrivate){
|
||||
action(repository)
|
||||
} else {
|
||||
context.loginAccount match {
|
||||
case Some(x) if(x.administrator) => action(repository)
|
||||
case Some(x) if(x.isAdmin) => action(repository)
|
||||
case Some(x) if(paths(0) == x.userName) => action(repository)
|
||||
case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
||||
case _ => Unauthorized()
|
||||
@@ -147,8 +147,8 @@ trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService =
|
||||
defining(request.paths){ paths =>
|
||||
getRepository(paths(0), paths(1)).map { repository =>
|
||||
context.loginAccount match {
|
||||
case Some(x) if(x.administrator) => action(repository)
|
||||
case Some(x) if(!repository.repository.`private`) => action(repository)
|
||||
case Some(x) if(x.isAdmin) => action(repository)
|
||||
case Some(x) if(!repository.repository.isPrivate) => action(repository)
|
||||
case Some(x) if(paths(0) == x.userName) => action(repository)
|
||||
case Some(x) if(getCollaborators(paths(0), paths(1)).contains(x.userName)) => action(repository)
|
||||
case _ => Unauthorized()
|
||||
@@ -171,7 +171,7 @@ trait GroupManagerAuthenticator { self: ControllerBase with AccountService =>
|
||||
defining(request.paths){ paths =>
|
||||
context.loginAccount match {
|
||||
case Some(x) if(getGroupMembers(paths(0)).exists { member =>
|
||||
member.userName == x.userName && member.manager
|
||||
member.userName == x.userName && member.isManager
|
||||
}) => action
|
||||
case _ => Unauthorized()
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ trait Notifier extends RepositoryService with AccountService with IssuesService
|
||||
)
|
||||
.distinct
|
||||
.withFilter ( _ != context.loginAccount.get.userName ) // the operation in person is excluded
|
||||
.foreach ( getAccountByUserName(_) filterNot (_.groupAccount) filterNot (LDAPUtil.isDummyMailAddress(_)) foreach (x => notify(x.mailAddress)) )
|
||||
.foreach ( getAccountByUserName(_) filterNot (_.isGroupAccount) filterNot (LDAPUtil.isDummyMailAddress(_)) foreach (x => notify(x.mailAddress)) )
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -5,56 +5,51 @@
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main("Applications"){
|
||||
<div class="container body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
@menu("application", settings.ssh)
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Personal access tokens</div>
|
||||
<div class="panel-body">
|
||||
@if(personalTokens.isEmpty && gneratedToken.isEmpty){
|
||||
No tokens.
|
||||
} else {
|
||||
Tokens you have generated that can be used to access the GitBucket API.
|
||||
<hr style="margin-top: 10px;">
|
||||
}
|
||||
@gneratedToken.map{ case (token, tokenString) =>
|
||||
<div class="alert alert-info">
|
||||
Make sure to copy your new personal access token now. You won't be able to see it again!
|
||||
</div>
|
||||
<a href="@path/@account.userName/_personalToken/delete/@token.accessTokenId" class="btn btn-sm btn-danger pull-right">Delete</a>
|
||||
<div style="width: 50%;">
|
||||
@helper.html.copy("generated-token-copy", tokenString){
|
||||
<input type="text" value="@tokenString" class="form-control input-sm" readonly>
|
||||
}
|
||||
</div>
|
||||
<hr style="margin-top: 10px;">
|
||||
}
|
||||
@personalTokens.zipWithIndex.map { case (token, i) =>
|
||||
@if(i != 0){
|
||||
<hr style="margin-top: 10px;">
|
||||
@menu("application", settings.ssh){
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Personal access tokens</div>
|
||||
<div class="panel-body">
|
||||
@if(personalTokens.isEmpty && gneratedToken.isEmpty){
|
||||
No tokens.
|
||||
} else {
|
||||
Tokens you have generated that can be used to access the GitBucket API.
|
||||
<hr style="margin-top: 10px;">
|
||||
}
|
||||
@gneratedToken.map{ case (token, tokenString) =>
|
||||
<div class="alert alert-info">
|
||||
Make sure to copy your new personal access token now. You won't be able to see it again!
|
||||
</div>
|
||||
<a href="@path/@account.userName/_personalToken/delete/@token.accessTokenId" class="btn btn-sm btn-danger pull-right">Delete</a>
|
||||
<div style="width: 50%;">
|
||||
@helper.html.copy("generated-token-copy", tokenString){
|
||||
<input type="text" value="@tokenString" class="form-control input-sm" readonly>
|
||||
}
|
||||
<strong>@token.note</strong>
|
||||
<a href="@path/@account.userName/_personalToken/delete/@token.accessTokenId" class="btn btn-sm btn-danger pull-right">Delete</a>
|
||||
</div>
|
||||
<hr style="margin-top: 10px;">
|
||||
}
|
||||
@personalTokens.zipWithIndex.map { case (token, i) =>
|
||||
@if(i != 0){
|
||||
<hr>
|
||||
}
|
||||
<strong style="line-height: 30px;">@token.note</strong>
|
||||
<a href="@path/@account.userName/_personalToken/delete/@token.accessTokenId" class="btn btn-sm btn-danger pull-right">Delete</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<form method="POST" action="@path/@account.userName/_personalToken" validate="true">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Generate new token</div>
|
||||
<div class="panel-body">
|
||||
<fieldset>
|
||||
<label for="note" class="strong">Token description</label>
|
||||
<div><span id="error-note" class="error"></span></div>
|
||||
<input type="text" name="note" id="note" class="form-control"/>
|
||||
<p class="muted">What's this token for?</p>
|
||||
</fieldset>
|
||||
<input type="submit" class="btn btn-success" value="Generate token"/>
|
||||
</div>
|
||||
</div>
|
||||
<form method="POST" action="@path/@account.userName/_personalToken" validate="true">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Generate new token</div>
|
||||
<div class="panel-body">
|
||||
<fieldset>
|
||||
<label for="note" class="strong">Token description</label>
|
||||
<div><span id="error-note" class="error"></span></div>
|
||||
<input type="text" name="note" id="note" class="form-control"/>
|
||||
<p class="muted">What's this token for?</p>
|
||||
</fieldset>
|
||||
<input type="submit" class="btn btn-success" value="Generate token"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -4,14 +4,10 @@
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main("Edit your profile"){
|
||||
<div class="container body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
@menu("profile", settings.ssh)
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
@helper.html.information(info)
|
||||
@if(LDAPUtil.isDummyMailAddress(account)){<div class="alert alert-danger">Please register your mail address.</div>}
|
||||
<form action="@url(account.userName)/_edit" method="POST" validate="true">
|
||||
@menu("profile", settings.ssh){
|
||||
@helper.html.information(info)
|
||||
@if(LDAPUtil.isDummyMailAddress(account)){<div class="alert alert-danger">Please register your mail address.</div>}
|
||||
<form action="@url(account.userName)/_edit" method="POST" validate="true">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Profile</div>
|
||||
<div class="panel-body">
|
||||
@@ -49,17 +45,17 @@
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 20px;">
|
||||
<div class="pull-right">
|
||||
<a href="@path/@account.userName/_delete" class="btn btn-danger" id="delete">Delete account</a>
|
||||
</div>
|
||||
<input type="submit" class="btn btn-success" value="Save"/>
|
||||
@if(!LDAPUtil.isDummyMailAddress(account)){<a href="@url(account.userName)" class="btn btn-default">Cancel</a>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="pull-right">
|
||||
<a href="@path/@account.userName/_delete" class="btn btn-danger" id="delete">Delete account</a>
|
||||
</div>
|
||||
<input type="submit" class="btn btn-success" value="Save"/>
|
||||
@if(!LDAPUtil.isDummyMailAddress(account)){<a href="@url(account.userName)" class="btn btn-default">Cancel</a>}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(if(account.isEmpty) "Create group" else "Edit group"){
|
||||
<div class="container body">
|
||||
<div class="body main-center">
|
||||
<form id="form" method="post" action="@if(account.isEmpty){@path/groups/new} else {@path/@account.get.userName/_editgroup}" validate="true">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
@@ -32,7 +32,7 @@
|
||||
</ul>
|
||||
@helper.html.account("memberName", 200)
|
||||
<input type="button" class="btn btn-default" value="Add" id="addMember"/>
|
||||
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.manager).mkString(",")"/>
|
||||
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.isManager).mkString(",")"/>
|
||||
<div>
|
||||
<span class="error" id="error-members"></span>
|
||||
</div>
|
||||
@@ -42,12 +42,12 @@
|
||||
<fieldset class="margin">
|
||||
@if(account.isDefined){
|
||||
<div class="pull-right">
|
||||
<a href="@url(account.get.userName)/_deletegroup" id="delete" class="btn btn-danger btn-lg">Delete Group</a>
|
||||
<a href="@url(account.get.userName)/_deletegroup" id="delete" class="btn btn-danger">Delete Group</a>
|
||||
</div>
|
||||
}
|
||||
<input type="submit" class="btn btn-success btn-lg" value="@if(account.isEmpty){Create Group} else {Update Group}"/>
|
||||
<input type="submit" class="btn btn-success" value="@if(account.isEmpty){Create Group} else {Update Group}"/>
|
||||
@if(account.isDefined){
|
||||
<a href="@url(account.get.userName)" class="btn btn-default btn-lg">Cancel</a>
|
||||
<a href="@url(account.get.userName)" class="btn btn-default">Cancel</a>
|
||||
}
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -103,22 +103,22 @@ $(function(){
|
||||
});
|
||||
|
||||
@members.map { member =>
|
||||
addMemberHTML('@member.userName', @member.manager);
|
||||
addMemberHTML('@member.userName', @member.isManager);
|
||||
}
|
||||
|
||||
function addMemberHTML(userName, isManager){
|
||||
var memberButton = $('<button type="button" class="btn btn-default btn-mini" value="false">Member</button>').data('name', userName);
|
||||
var memberButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="false" name="' + userName + '">Member</label>');
|
||||
if(!isManager){
|
||||
memberButton.addClass('active');
|
||||
}
|
||||
var managerButton = $('<button type="button" class="btn btn-default btn-mini" value="true">Manager</button>').data('name', userName);
|
||||
var managerButton = $('<label class="btn btn-default btn-mini"><input type="radio" value="true" name="' + userName + '">Manager</label>');
|
||||
if(isManager){
|
||||
managerButton.addClass('active');
|
||||
}
|
||||
|
||||
$('#member-list').append($('<li>')
|
||||
.data('name', userName)
|
||||
.append($('<div class="btn-group is_manager" data-toggle="buttons-radio">')
|
||||
.append($('<div class="btn-group is_manager" data-toggle="buttons">')
|
||||
.append(memberButton)
|
||||
.append(managerButton))
|
||||
.append(' ')
|
||||
@@ -130,9 +130,7 @@ $(function(){
|
||||
function updateMembers(){
|
||||
var members = $('#member-list li').map(function(i, e){
|
||||
var userName = $(e).data('name');
|
||||
return userName + ':' + $('button.active').filter(function(i, e){
|
||||
return $(e).data('name') == userName;
|
||||
}).attr('value');
|
||||
return userName + ':' + $(e).find('label.active input[type=radio]').attr('value');
|
||||
}).get().join(',');
|
||||
$('#members').val(members);
|
||||
}
|
||||
|
||||
@@ -4,56 +4,56 @@
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(account.userName){
|
||||
<div class="container body">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="block">
|
||||
<div class="account-image">@avatar(account.userName, 270)</div>
|
||||
<div class="account-fullname">@account.fullName</div>
|
||||
<div class="account-username">@account.userName</div>
|
||||
</div>
|
||||
<div class="block">
|
||||
@if(account.url.isDefined){
|
||||
<div><i class="octicon octicon-home"></i> <a href="@account.url">@account.url</a></div>
|
||||
}
|
||||
<div><i class="octicon octicon-clock"></i> <span class="muted">Joined on</span> @date(account.registeredDate)</div>
|
||||
</div>
|
||||
@if(groupNames.nonEmpty){
|
||||
<div>
|
||||
<div>Groups</div>
|
||||
@groupNames.map { groupName =>
|
||||
@avatarLink(groupName, 36, tooltip = true)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<ul class="nav nav-tabs" style="margin-bottom: 5px;">
|
||||
<li@if(active == "repositories"){ class="active"}><a href="@url(account.userName)?tab=repositories">Repositories</a></li>
|
||||
@if(account.groupAccount){
|
||||
<li@if(active == "members"){ class="active"}><a href="@url(account.userName)?tab=members">Members</a></li>
|
||||
} else {
|
||||
<li@if(active == "activity"){ class="active"}><a href="@url(account.userName)?tab=activity">Public Activity</a></li>
|
||||
}
|
||||
@if(loginAccount.isDefined && loginAccount.get.userName == account.userName){
|
||||
<li class="pull-right">
|
||||
<div class="button-group">
|
||||
<a href="@url(account.userName)/_edit" class="btn btn-default">Edit Your Profile</a>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
@if(loginAccount.isDefined && account.groupAccount && isGroupManager){
|
||||
<li class="pull-right">
|
||||
<div class="button-groRepiosup">
|
||||
<a href="@url(account.userName)/_editgroup" class="btn btn-default">Edit Group</a>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
@body
|
||||
</div>
|
||||
<div class="main-sidebar">
|
||||
<div class="block">
|
||||
<div class="account-image">@avatar(account.userName, 240)</div>
|
||||
<div class="account-fullname">@account.fullName</div>
|
||||
<div class="account-username">@account.userName</div>
|
||||
</div>
|
||||
<div class="block">
|
||||
@if(account.url.isDefined){
|
||||
<div><i class="octicon octicon-home"></i> <a href="@account.url">@account.url</a></div>
|
||||
}
|
||||
<div><i class="octicon octicon-clock"></i> <span class="muted">Joined on</span> @date(account.registeredDate)</div>
|
||||
</div>
|
||||
@if(groupNames.nonEmpty){
|
||||
<div>
|
||||
<div>Groups</div>
|
||||
@groupNames.map { groupName =>
|
||||
@avatarLink(groupName, 36, tooltip = true)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="main-content">
|
||||
<ul class="nav nav-tabs" style="margin-bottom: 5px;">
|
||||
<li@if(active == "repositories"){ class="active"}><a href="@url(account.userName)?tab=repositories">Repositories</a></li>
|
||||
@if(account.isGroupAccount){
|
||||
<li@if(active == "members"){ class="active"}><a href="@url(account.userName)?tab=members">Members</a></li>
|
||||
} else {
|
||||
<li@if(active == "activity"){ class="active"}><a href="@url(account.userName)?tab=activity">Public Activity</a></li>
|
||||
}
|
||||
@gitbucket.core.plugin.PluginRegistry().getProfileTabs.map { tab =>
|
||||
@tab(account, context).map { link =>
|
||||
<li@if(active == link.id){ class="active"}><a href="@path/@link.path">@link.label</a></li>
|
||||
}
|
||||
}
|
||||
@if(loginAccount.isDefined && loginAccount.get.userName == account.userName){
|
||||
<li class="pull-right">
|
||||
<div class="button-group">
|
||||
<a href="@url(account.userName)/_edit" class="btn btn-default">Edit Your Profile</a>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
@if(loginAccount.isDefined && account.isGroupAccount && isGroupManager){
|
||||
<li class="pull-right">
|
||||
<div class="button-group">
|
||||
<a href="@url(account.userName)/_editgroup" class="btn btn-default">Edit Group</a>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
@body
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@(active: String, ssh: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||
@(active: String, ssh: Boolean)(body: Html)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
<div class="box">
|
||||
<ul class="nav nav-tabs nav-stacked side-menu">
|
||||
<div class="main-sidebar">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li@if(active=="profile"){ class="active"}>
|
||||
<a href="@path/@loginAccount.get.userName/_edit">Profile</a>
|
||||
</li>
|
||||
@@ -13,5 +13,15 @@
|
||||
<li@if(active=="application"){ class="active"}>
|
||||
<a href="@path/@loginAccount.get.userName/_application">Applications</a>
|
||||
</li>
|
||||
@gitbucket.core.plugin.PluginRegistry().getAccountSettingMenus.map { menu =>
|
||||
@menu(context).map { link =>
|
||||
<li@if(active==link.id){ class="active"}>
|
||||
<a href="@path/@link.path">@link.label</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
@body
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main("Create a New Repository"){
|
||||
<div class="body" style="width: 600px; margin: 10px auto;">
|
||||
<div class="body main-center">
|
||||
<h2>Create a new repository</h2>
|
||||
<p class="muted">
|
||||
A repository contains all the files for your project, including the revision history.
|
||||
@@ -67,7 +67,7 @@ isCreateRepoOptionPublic: Boolean)(implicit context: gitbucket.core.controller.C
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset class="margin form-actions">
|
||||
<input type="submit" class="btn btn-success btn-lg" value="Create repository"/>
|
||||
<input type="submit" class="btn btn-success" value="Create repository"/>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<div class="repository-content">
|
||||
<div class="block-header">
|
||||
<a href="@url(repository)">@repository.name</a>
|
||||
@if(repository.repository.`private`){
|
||||
@if(repository.repository.isPrivate){
|
||||
<i class="octicon octicon-lock"></i>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -4,45 +4,40 @@
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main("SSH Keys"){
|
||||
<div class="container body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
@menu("ssh", settings.ssh)
|
||||
@menu("ssh", settings.ssh){
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">SSH Keys</div>
|
||||
<div class="panel-body">
|
||||
@if(sshKeys.isEmpty){
|
||||
No keys
|
||||
}
|
||||
@sshKeys.zipWithIndex.map { case (key, i) =>
|
||||
@if(i != 0){
|
||||
<hr>
|
||||
}
|
||||
<strong style="line-height: 30px;">@key.title</strong> (@SshUtil.fingerPrint(key.publicKey).getOrElse("Key is invalid."))
|
||||
<a href="@path/@account.userName/_ssh/delete/@key.sshKeyId" class="btn btn-sm btn-danger pull-right">Delete</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<form method="POST" action="@path/@account.userName/_ssh" validate="true">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">SSH Keys</div>
|
||||
<div class="panel-heading strong">Add an SSH Key</div>
|
||||
<div class="panel-body">
|
||||
@if(sshKeys.isEmpty){
|
||||
No keys
|
||||
}
|
||||
@sshKeys.zipWithIndex.map { case (key, i) =>
|
||||
@if(i != 0){
|
||||
<hr style="margin-top: 10px;">
|
||||
}
|
||||
<strong>@key.title</strong> (@SshUtil.fingerPrint(key.publicKey).getOrElse("Key is invalid."))
|
||||
<a href="@path/@account.userName/_ssh/delete/@key.sshKeyId" class="btn btn-sm btn-danger pull-right">Delete</a>
|
||||
}
|
||||
<fieldset class="form-group">
|
||||
<label for="title" class="strong">Title</label>
|
||||
<div><span id="error-title" class="error"></span></div>
|
||||
<input type="text" name="title" id="title" class="form-control"/>
|
||||
</fieldset>
|
||||
<fieldset class="form-group">
|
||||
<label for="publicKey" class="strong">Key</label>
|
||||
<div><span id="error-publicKey" class="error"></span></div>
|
||||
<textarea name="publicKey" id="publicKey" class="form-control" style="height: 250px;"></textarea>
|
||||
</fieldset>
|
||||
<input type="submit" class="btn btn-success" value="Add"/>
|
||||
</div>
|
||||
</div>
|
||||
<form method="POST" action="@path/@account.userName/_ssh" validate="true">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Add an SSH Key</div>
|
||||
<div class="panel-body">
|
||||
<fieldset class="form-group">
|
||||
<label for="title" class="strong">Title</label>
|
||||
<div><span id="error-title" class="error"></span></div>
|
||||
<input type="text" name="title" id="title" class="form-control"/>
|
||||
</fieldset>
|
||||
<fieldset class="form-group">
|
||||
<label for="publicKey" class="strong">Key</label>
|
||||
<div><span id="error-publicKey" class="error"></span></div>
|
||||
<textarea name="publicKey" id="publicKey" class="form-control" style="height: 250px;"></textarea>
|
||||
</fieldset>
|
||||
<input type="submit" class="btn btn-success" value="Add"/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
@(active: String)(body: Html)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
<div class="container body">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<ul class="nav nav-tabs nav-stacked side-menu" id="system-admin-menu-container">
|
||||
<li@if(active=="users"){ class="active"}>
|
||||
<a href="@path/admin/users">User Management</a>
|
||||
</li>
|
||||
<li@if(active=="system"){ class="active"}>
|
||||
<a href="@path/admin/system">System Settings</a>
|
||||
</li>
|
||||
<li@if(active=="plugins"){ class="active"}>
|
||||
<a href="@path/admin/plugins">Plugins</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="@path/console/login.jsp">H2 Console</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
@body
|
||||
</div>
|
||||
<div class="main-sidebar">
|
||||
<ul class="nav nav-pills nav-stacked" id="system-admin-menu-container">
|
||||
<li@if(active=="users"){ class="active"}>
|
||||
<a href="@path/admin/users">User Management</a>
|
||||
</li>
|
||||
<li@if(active=="system"){ class="active"}>
|
||||
<a href="@path/admin/system">System Settings</a>
|
||||
</li>
|
||||
<li@if(active=="plugins"){ class="active"}>
|
||||
<a href="@path/admin/plugins">Plugins</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="@path/console/login.jsp">H2 Console</a>
|
||||
</li>
|
||||
@gitbucket.core.plugin.PluginRegistry().getSystemSettingMenus.map { menu =>
|
||||
@menu(context).map { link =>
|
||||
<li@if(active==link.id){ class="active"}>
|
||||
<a href="@path/@link.path">@link.label</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
@body
|
||||
</div>
|
||||
</div>
|
||||
@@ -13,7 +13,7 @@
|
||||
<input type="text" name="userName" id="userName" class="form-control" value="@account.map(_.userName)"@if(account.isDefined){ readonly}/>
|
||||
@if(account.isDefined){
|
||||
<label for="removed">
|
||||
<input type="checkbox" name="removed" id="removed" value="true" @if(account.get.removed){checked}/>
|
||||
<input type="checkbox" name="removed" id="removed" value="true" @if(account.get.isRemoved){checked}/>
|
||||
Disable
|
||||
</label>
|
||||
<div>
|
||||
@@ -53,10 +53,10 @@
|
||||
<fieldset class="form-group">
|
||||
<label class="strong">User Type:</label>
|
||||
<label class="radio" for="userType_Normal">
|
||||
<input type="radio" name="isAdmin" id="userType_Normal" value="false"@if(account.isEmpty || !account.get.administrator){ checked}/> Normal
|
||||
<input type="radio" name="isAdmin" id="userType_Normal" value="false"@if(account.isEmpty || !account.get.isAdmin){ checked}/> Normal
|
||||
</label>
|
||||
<label class="radio" for="userType_Admin">
|
||||
<input type="radio" name="isAdmin" id="userType_Admin" value="true"@if(account.isDefined && account.get.administrator){ checked}/> Administrator
|
||||
<input type="radio" name="isAdmin" id="userType_Admin" value="true"@if(account.isDefined && account.get.isAdmin){ checked}/> Administrator
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset class="form-group">
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<input type="text" name="groupName" id="groupName" class="form-control" value="@account.map(_.userName)"@if(account.isDefined){ readonly}/>
|
||||
@if(account.isDefined){
|
||||
<label for="removed">
|
||||
<input type="checkbox" name="removed" id="removed" value="true" @if(account.get.removed){checked}/>
|
||||
<input type="checkbox" name="removed" id="removed" value="true" @if(account.get.isRemoved){checked}/>
|
||||
Disable
|
||||
</label>
|
||||
}
|
||||
@@ -38,7 +38,7 @@
|
||||
</ul>
|
||||
@helper.html.account("memberName", 200)
|
||||
<input type="button" class="btn btn-default" value="Add" id="addMember"/>
|
||||
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.manager).mkString(",")"/>
|
||||
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.isManager).mkString(",")"/>
|
||||
<div>
|
||||
<span class="error" id="error-members"></span>
|
||||
</div>
|
||||
@@ -98,7 +98,7 @@ $(function(){
|
||||
});
|
||||
|
||||
@members.map { member =>
|
||||
addMemberHTML('@member.userName', @member.manager);
|
||||
addMemberHTML('@member.userName', @member.isManager);
|
||||
}
|
||||
|
||||
function addMemberHTML(userName, isManager){
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
<table class="table table-bordered table-hover">
|
||||
@users.map { account =>
|
||||
<tr>
|
||||
<td @if(account.removed){style="background-color: #dddddd;"}>
|
||||
<td @if(account.isRemoved){style="background-color: #dddddd;"}>
|
||||
<div class="pull-right">
|
||||
@if(account.groupAccount){
|
||||
@if(account.isGroupAccount){
|
||||
<a href="@path/admin/users/@account.userName/_editgroup">Edit</a>
|
||||
} else {
|
||||
<a href="@path/admin/users/@account.userName/_edituser">Edit</a>
|
||||
@@ -25,16 +25,16 @@
|
||||
<div class="strong">
|
||||
@avatar(account.userName, 20)
|
||||
<a href="@url(account.userName)">@account.userName</a>
|
||||
@if(account.groupAccount){
|
||||
@if(account.isGroupAccount){
|
||||
(Group)
|
||||
} else {
|
||||
@if(account.administrator){
|
||||
@if(account.isAdmin){
|
||||
(Administrator)
|
||||
} else {
|
||||
(Normal)
|
||||
}
|
||||
}
|
||||
@if(account.groupAccount){
|
||||
@if(account.isGroupAccount){
|
||||
@members(account.userName).map { userName =>
|
||||
@avatar(userName, 20, tooltip = true)
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<hr>
|
||||
@if(!account.groupAccount){
|
||||
@if(!account.isGroupAccount){
|
||||
<i class="octicon octicon-mail"></i> @account.mailAddress
|
||||
}
|
||||
@account.url.map { url =>
|
||||
@@ -52,7 +52,7 @@
|
||||
<div>
|
||||
<span class="muted">Registered:</span> @datetime(account.registeredDate)
|
||||
<span class="muted">Updated:</span> @datetime(account.updatedDate)
|
||||
@if(!account.groupAccount){
|
||||
@if(!account.isGroupAccount){
|
||||
<span class="muted">Last Login:</span> @account.lastLoginDate.map(datetime)
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -4,18 +4,8 @@
|
||||
groups: List[String])(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
<span class="small">
|
||||
<a class="button-link@if(condition.state == "open"){ selected}" href="@condition.copy(state = "open").toURL">
|
||||
<i class="octicon octicon-issue-opened @(if(condition.state == "open"){"active"})"></i>
|
||||
@openCount Open
|
||||
</a>
|
||||
<a class="button-link@if(condition.state == "closed"){ selected}" href="@condition.copy(state = "closed").toURL">
|
||||
<i class="octicon octicon-check @(if(condition.state == "closed"){"active"})"></i>
|
||||
@closedCount Closed
|
||||
</a>
|
||||
</span>
|
||||
<div class="pull-right" id="table-issues-control">
|
||||
@helper.html.dropdown("Visibility", flat = true){
|
||||
<div id="table-issues-control">
|
||||
@helper.html.dropdown("Visibility"){
|
||||
<li>
|
||||
<a href="@(condition.copy(visibility = (if(condition.visibility == Some("private")) None else Some("private"))).toURL)">
|
||||
@helper.html.checkicon(condition.visibility == Some("private"))
|
||||
@@ -29,7 +19,7 @@
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@helper.html.dropdown("Organization", flat = true){
|
||||
@helper.html.dropdown("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)">
|
||||
@@ -39,7 +29,7 @@
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@helper.html.dropdown("Sort", flat = true){
|
||||
@helper.html.dropdown("Sort"){
|
||||
<li>
|
||||
<a href="@condition.copy(sort="created", direction="desc").toURL">
|
||||
@helper.html.checkicon(condition.sort == "created" && condition.direction == "desc") Newest
|
||||
|
||||
@@ -4,15 +4,17 @@
|
||||
closedCount: Int,
|
||||
condition: gitbucket.core.service.IssuesService.IssueSearchCondition,
|
||||
filter: String,
|
||||
groups: List[String])(implicit context: gitbucket.core.controller.Context)
|
||||
groups: List[String],
|
||||
recentRepositories: List[gitbucket.core.service.RepositoryService.RepositoryInfo],
|
||||
userRepositories: List[gitbucket.core.service.RepositoryService.RepositoryInfo])(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main("Issues"){
|
||||
<div class="body">
|
||||
@sidebar(recentRepositories, userRepositories){
|
||||
@dashboard.html.tab("issues")
|
||||
<div class="container">
|
||||
@issuesnavi(filter, "issues", condition)
|
||||
@issuesnavi(filter, openCount, closedCount, condition)
|
||||
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@
|
||||
@issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
|
||||
<tr>
|
||||
<td style="padding-top: 12px; padding-bottom: 12px;">
|
||||
@if(issue.isPullRequest){
|
||||
<i class="octicon octicon-git-pull-request @(if(issue.closed) "closed" else "open")"></i>
|
||||
} else {
|
||||
<i class="octicon octicon-issue-@(if(issue.closed) "closed" else "opened")"></i>
|
||||
}
|
||||
<a href="@path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a> ・
|
||||
@if(issue.isPullRequest){
|
||||
<a href="@path/@issue.userName/@issue.repositoryName/pull/@issue.issueId" class="issue-title">@issue.title</a>
|
||||
@@ -50,7 +45,7 @@
|
||||
</a>
|
||||
}
|
||||
</span>
|
||||
<div class="small muted" style="margin-left: 20px; margin-top: 2px;">
|
||||
<div class="small muted" style="margin-top: 2px;">
|
||||
#@issue.issueId opened by @user(issue.openedUserName, styleClass="username") @datetime(issue.registeredDate)
|
||||
@milestone.map { milestone =>
|
||||
<span style="margin: 20px;"><a href="@condition.copy(milestone = Some(Some(milestone))).toURL" class="username"><i class="octicon octicon-milestone"></i> @milestone</a></span>
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
@(filter: String,
|
||||
active: String,
|
||||
openCount: Int,
|
||||
closedCount: Int,
|
||||
condition: gitbucket.core.service.IssuesService.IssueSearchCondition)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
<ul class="nav nav-pills pull-left" style="line-height: 14px;">
|
||||
<ul class="nav nav-pills pull-left" style="line-height: 14px; margin-bottom: 10px;">
|
||||
<li class="@(if(condition.state == "open"){"active"})">
|
||||
<a href="@condition.copy(state = "open").toURL">Open <span class="badge">@openCount</span></a>
|
||||
</li>
|
||||
<li class="@(if(condition.state == "closed"){"active"})">
|
||||
<a href="@condition.copy(state = "closed").toURL">Closed <span class="badge">@closedCount</span></a>
|
||||
</li>
|
||||
@*
|
||||
<li class="@if(filter == "created_by"){active}">
|
||||
<a href="@path/dashboard/@active/created_by@condition.copy(author = None, assigned = None).toURL">Created</a>
|
||||
</li>
|
||||
@@ -13,8 +21,5 @@
|
||||
<li class="@if(filter == "mentioned"){active}">
|
||||
<a href="@path/dashboard/@active/mentioned@condition.copy(author = None, assigned = None).toURL">Mentioned</a>
|
||||
</li>
|
||||
*@
|
||||
</ul>
|
||||
<form method="GET" id="search-filter-form" action="@path/dashboard/@active" class="pull-right">
|
||||
<input type="text" id="search-filter-box" class="form-control input-lg" name="q" style="width: 400px;"
|
||||
value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
|
||||
</form>
|
||||
|
||||
@@ -4,15 +4,17 @@
|
||||
closedCount: Int,
|
||||
condition: gitbucket.core.service.IssuesService.IssueSearchCondition,
|
||||
filter: String,
|
||||
groups: List[String])(implicit context: gitbucket.core.controller.Context)
|
||||
groups: List[String],
|
||||
recentRepositories: List[gitbucket.core.service.RepositoryService.RepositoryInfo],
|
||||
userRepositories: List[gitbucket.core.service.RepositoryService.RepositoryInfo])(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main("Pull Requests"){
|
||||
<div class="body">
|
||||
@sidebar(recentRepositories, userRepositories){
|
||||
@dashboard.html.tab("pulls")
|
||||
<div class="container">
|
||||
@issuesnavi(filter, "pulls", condition)
|
||||
@issuesnavi(filter, openCount, closedCount, condition)
|
||||
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
73
src/main/twirl/gitbucket/core/dashboard/sidebar.scala.html
Normal file
73
src/main/twirl/gitbucket/core/dashboard/sidebar.scala.html
Normal file
@@ -0,0 +1,73 @@
|
||||
@(recentRepositories: List[gitbucket.core.service.RepositoryService.RepositoryInfo],
|
||||
userRepositories: List[gitbucket.core.service.RepositoryService.RepositoryInfo])(body: Html)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
<div class="container body">
|
||||
<div class="dashboard-sidebar">
|
||||
@if(loginAccount.isEmpty){
|
||||
<div id="dashboard-signin-form">@html.signinform(settings)</div>
|
||||
} else {
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">
|
||||
Your repositories <span class="badge">@userRepositories.size</span>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@if(userRepositories.isEmpty){
|
||||
<li class="list-group-item">No repositories</li>
|
||||
} else {
|
||||
@defining(20){ max =>
|
||||
@userRepositories.zipWithIndex.map { case (repository, i) =>
|
||||
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
|
||||
@helper.html.repositoryicon(repository, false)
|
||||
@if(repository.owner == loginAccount.get.userName){
|
||||
<a href="@url(repository)"><span class="strong">@repository.name</span></a>
|
||||
} else {
|
||||
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
@if(userRepositories.size > max){
|
||||
<li class="list-group-item show-more">
|
||||
<a href="javascript:void(0);" id="show-more-repos">Show @{userRepositories.size - max} more repositories...</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Recent updated repositories</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@if(recentRepositories.isEmpty){
|
||||
<li class="list-group-item">No repositories</li>
|
||||
} else {
|
||||
@defining(20){ max =>
|
||||
@recentRepositories.zipWithIndex.map { case (repository, i) =>
|
||||
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
|
||||
@helper.html.repositoryicon(repository, false)
|
||||
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
|
||||
</li>
|
||||
}
|
||||
@if(recentRepositories.size > max){
|
||||
<li class="list-group-item show-more">
|
||||
<a href="javascript:void(0);" id="show-more-recent-repos">Show @{recentRepositories.size - max} more repositories...</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dashboard-content">
|
||||
@body
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
$('#show-more-repos, #show-more-recent-repos').click(function(e){
|
||||
$(e.target).parents('ul.list-group').find('li.repo-link').show();
|
||||
$(e.target).parents('li.show-more').remove();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -1,52 +1,15 @@
|
||||
@(active: String = "")(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
<div class="dashboard-nav">
|
||||
<div class="container">
|
||||
<a href="@path/" @if(active == ""){ class="active"}>
|
||||
<i class="octicon octicon-rss"></i> News Feed
|
||||
</a>
|
||||
@if(loginAccount.isDefined){
|
||||
<a href="@path/dashboard/pulls" @if(active == "pulls" ){ class="active"}>
|
||||
<i class="octicon octicon-git-pull-request"></i> Pull Requests
|
||||
</a>
|
||||
<a href="@path/dashboard/issues" @if(active == "issues"){ class="active"}>
|
||||
<i class="octicon octicon-issue-opened"></i> Issues
|
||||
</a>
|
||||
<ul class="nav nav-tabs" style="margin-bottom: 20px;">
|
||||
<li @if(active == ""){ class="active"}><a href="@path/">News Feed</a></li>
|
||||
@if(loginAccount.isDefined){
|
||||
<li @if(active == "pulls" ){ class="active"}><a href="@path/dashboard/pulls">Pull Requests</a></li>
|
||||
<li @if(active == "issues"){ class="active"}><a href="@path/dashboard/issues">Issues</a></li>
|
||||
@gitbucket.core.plugin.PluginRegistry().getDashboardTabs.map { tab =>
|
||||
@tab(context).map { link =>
|
||||
<li @if(active == link.id){ class="active"}><a href="@path/@link.path">@link.label</a></li>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<style type="text/css">
|
||||
div.dashboard-nav {
|
||||
border-bottom: 1px solid #ddd;
|
||||
text-align: right;
|
||||
height: 32px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
div.dashboard-nav a {
|
||||
line-height: 10px;
|
||||
margin-left: 20px;
|
||||
padding-bottom: 13px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.dashboard-nav .octicon{
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
font-size: 16px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
div.dashboard-nav a:hover,div.dashboard-nav a:hover .octicon {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
div.dashboard-nav a.active {
|
||||
border-bottom: 2px solid #bb4444;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
}
|
||||
</ul>
|
||||
|
||||
@@ -61,8 +61,10 @@
|
||||
}
|
||||
|
||||
@detailActivity(activity: gitbucket.core.model.Activity, image: String) = {
|
||||
@*
|
||||
<div class="activity-icon-large"><i class="mega-octicon octicon-@image"></i></div>
|
||||
<div class="activity-content">
|
||||
*@
|
||||
<div>
|
||||
<div class="muted small">@helper.html.datetimeago(activity.activityDate)</div>
|
||||
<div class="strong">
|
||||
@avatar(activity.activityUserName, 16)
|
||||
@@ -75,8 +77,10 @@
|
||||
}
|
||||
|
||||
@customActivity(activity: gitbucket.core.model.Activity, image: String)(additionalInfo: Any) = {
|
||||
@*
|
||||
<div class="activity-icon-large"><i class="mega-octicon octicon-@image"></i></div>
|
||||
<div class="activity-content">
|
||||
*@
|
||||
<div>
|
||||
<div class="muted small">@helper.html.datetimeago(activity.activityDate)</div>
|
||||
<div class="strong">
|
||||
@avatar(activity.activityUserName, 16)
|
||||
@@ -87,12 +91,14 @@
|
||||
}
|
||||
|
||||
@simpleActivity(activity: gitbucket.core.model.Activity, image: String) = {
|
||||
@*
|
||||
<div class="activity-icon-small"><i class="octicon octicon-@image"></i></div>
|
||||
<div class="activity-content">
|
||||
*@
|
||||
<div>
|
||||
<span class="muted small">@helper.html.datetimeago(activity.activityDate)</span>
|
||||
<div>
|
||||
@avatar(activity.activityUserName, 16)
|
||||
@activityMessage(activity.message)
|
||||
<span class="muted small">@helper.html.datetimeago(activity.activityDate)</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
<div class="input-group" style="margin-bottom: 0px;">
|
||||
@html
|
||||
<span class="input-group-btn">
|
||||
<span id="@id" class="btn btn-sm btn-default" @if(style.nonEmpty){style="@style"}
|
||||
<span id="@id" class="btn btn-default" @if(style.nonEmpty){style="@style"}
|
||||
data-clipboard-text="@value" data-placement="bottom" title="copy to clipboard"><i class="octicon octicon-clippy"></i></span>
|
||||
</span>
|
||||
</div>
|
||||
} else {
|
||||
<span id="@id" class="btn btn-sm btn-default" @if(style.nonEmpty){style="@style"}
|
||||
<span id="@id" class="btn btn-default" @if(style.nonEmpty){style="@style"}
|
||||
data-clipboard-text="@value" data-placement="bottom" title="copy to clipboard"><i class="octicon octicon-clippy"></i></span>
|
||||
}
|
||||
<script>
|
||||
|
||||
@@ -49,8 +49,8 @@
|
||||
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
||||
@if(newCommitId.isDefined){
|
||||
<div class="pull-right align-right">
|
||||
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="ignore-whitespace" value="1"/>Ignore Space</label>
|
||||
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
|
||||
<label class="no-margin"><input type="checkbox" class="ignore-whitespace" value="1"/> Ignore Space</label>
|
||||
<label class="no-margin"><input type="checkbox" class="toggle-notes" checked> Show notes</label>
|
||||
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-default btn-sm" title="View the whole file at version @newCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
|
||||
</div>
|
||||
}
|
||||
@@ -60,8 +60,8 @@
|
||||
@if(diff.changeType == ChangeType.ADD || diff.changeType == ChangeType.MODIFY){
|
||||
@if(newCommitId.isDefined){
|
||||
<div class="pull-right align-right">
|
||||
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="ignore-whitespace" value="1"/>Ignore Space</label>
|
||||
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
|
||||
<label class="no-margin"><input type="checkbox" class="ignore-whitespace" value="1"/> Ignore Space</label>
|
||||
<label class="no-margin"><input type="checkbox" class="toggle-notes" checked> Show notes</label>
|
||||
<a href="@url(repository)/blob/@newCommitId.get/@diff.newPath" class="btn btn-default btn-sm" title="View the whole file at version @newCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
|
||||
</div>
|
||||
}
|
||||
@@ -77,7 +77,7 @@
|
||||
@if(diff.changeType == ChangeType.DELETE){
|
||||
@if(oldCommitId.isDefined){
|
||||
<div class="pull-right align-right">
|
||||
<label class="checkbox no-margin" style="display: inline-block;"><input type="checkbox" class="toggle-notes" checked><span>Show notes</span></label>
|
||||
<label class="no-margin"><input type="checkbox" class="toggle-notes" checked> Show notes</label>
|
||||
<a href="@url(repository)/blob/@oldCommitId.get/@diff.oldPath" class="btn btn-default btn-sm" title="View the whole file at version @oldCommitId.get.substring(0, 10)" data-toggle="tooltip">View</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
@(value : String = "",
|
||||
prefix: String = "",
|
||||
style : String = "",
|
||||
right : Boolean = false,
|
||||
flat : Boolean = false)(body: Html)
|
||||
right : Boolean = false)(body: Html)
|
||||
<div class="btn-group" @if(style.nonEmpty){style="@style"}>
|
||||
<button
|
||||
@if(flat){style="border: none; background-color: #eee;"}
|
||||
class="dropdown-toggle @if(!flat){btn btn-default} else {flat} btn-sm" data-toggle="dropdown">
|
||||
class="dropdown-toggle btn btn-default btn-sm" data-toggle="dropdown">
|
||||
@if(value.isEmpty){
|
||||
<i class="octicon octicon-gear"></i>
|
||||
} else {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
@import gitbucket.core._
|
||||
@import gitbucket.core.view.helpers._
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs fill-width" style="margin-top: 12px; margin-bottom: 10px;">
|
||||
<ul class="nav nav-tabs fill-width" style="margin-bottom: 10px;">
|
||||
<li class="active"><a href="#tab@uid" data-toggle="tab">Write</a></li>
|
||||
<li><a href="#tab@(uid+1)" data-toggle="tab" id="preview@uid">Preview</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@import gitbucket.core.service.RepositoryService
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@if(repository.repository.`private`){
|
||||
@if(repository.repository.isPrivate){
|
||||
<i class="@{if(large){"mega-"}}octicon octicon-lock"></i>
|
||||
} else {
|
||||
@if(repository.repository.originUserName.isDefined){
|
||||
|
||||
@@ -4,91 +4,19 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@main("GitBucket"){
|
||||
<div class="body">
|
||||
@dashboard.html.sidebar(recentRepositories, userRepositories){
|
||||
@settings.information.map { information =>
|
||||
<div class="alert alert-info" style="background-color: white; color: #555; border-color: #4183c4; font-size: small; line-height: 120%;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
@Html(information)
|
||||
</div>
|
||||
}
|
||||
@dashboard.html.tab()
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="pull-right">
|
||||
<a href="@path/activities.atom"><img src="@assets/common/images/feed.png" alt="activities"></a>
|
||||
</div>
|
||||
@helper.html.activities(activities)
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
@settings.information.map { information =>
|
||||
<div class="alert alert-info" style="background-color: white; color: #555; border-color: #4183c4; font-size: small; line-height: 120%;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
@Html(information)
|
||||
</div>
|
||||
}
|
||||
@if(loginAccount.isEmpty){
|
||||
<div id="dashboard-signin-form">@signinform(settings)</div>
|
||||
} else {
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">
|
||||
<div class="pull-right">
|
||||
<a href="@path/new" class="btn btn-success btn-sm">New repository</a>
|
||||
</div>
|
||||
Your repositories <span class="badge">@userRepositories.size</span>
|
||||
</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@if(userRepositories.isEmpty){
|
||||
<li class="list-group-item">No repositories</li>
|
||||
} else {
|
||||
@defining(20){ max =>
|
||||
@userRepositories.zipWithIndex.map { case (repository, i) =>
|
||||
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
|
||||
@helper.html.repositoryicon(repository, false)
|
||||
@if(repository.owner == loginAccount.get.userName){
|
||||
<a href="@url(repository)"><span class="strong">@repository.name</span></a>
|
||||
} else {
|
||||
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
@if(userRepositories.size > max){
|
||||
<li class="list-group-item show-more">
|
||||
<a href="javascript:void(0);" id="show-more-repos">Show @{userRepositories.size - max} more repositories...</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong">Recent updated repositories</div>
|
||||
<ul class="list-group list-group-flush">
|
||||
@if(recentRepositories.isEmpty){
|
||||
<li class="list-group-item">No repositories</li>
|
||||
} else {
|
||||
@defining(20){ max =>
|
||||
@recentRepositories.zipWithIndex.map { case (repository, i) =>
|
||||
<li class="list-group-item repo-link" style="@if(i > max - 1){display:none;}">
|
||||
@helper.html.repositoryicon(repository, false)
|
||||
<a href="@url(repository)">@repository.owner/<span class="strong">@repository.name</span></a>
|
||||
</li>
|
||||
}
|
||||
@if(recentRepositories.size > max){
|
||||
<li class="list-group-item show-more">
|
||||
<a href="javascript:void(0);" id="show-more-recent-repos">Show @{recentRepositories.size - max} more repositories...</a>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<a href="@path/activities.atom"><img src="@assets/common/images/feed.png" alt="activities"></a>
|
||||
</div>
|
||||
@helper.html.activities(activities)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
<script>
|
||||
$(function(){
|
||||
$('#show-more-repos, #show-more-recent-repos').click(function(e){
|
||||
$(e.target).parents('ul.list-group').find('li.repo-link').show();
|
||||
$(e.target).parents('li.show-more').remove();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
<div class="text-right">
|
||||
<input type="hidden" name="issueId" value="@issue.issueId"/>
|
||||
@if((reopenable || !issue.closed) && (hasWritePermission || issue.openedUserName == loginAccount.get.userName)){
|
||||
<input type="submit" class="btn btn-lg btn-default" tabindex="3" formaction="@url(repository)/issue_comments/state" value="@{if(issue.closed) "Reopen" else "Close"}" id="action"/>
|
||||
<input type="submit" class="btn btn-default" tabindex="3" formaction="@url(repository)/issue_comments/state" value="@{if(issue.closed) "Reopen" else "Close"}" id="action"/>
|
||||
}
|
||||
<input type="submit" class="btn btn-lg btn-success" tabindex="2" formaction="@url(repository)/issue_comments/new" value="Comment"/>
|
||||
<input type="submit" class="btn btn-success" tabindex="2" formaction="@url(repository)/issue_comments/new" value="Comment"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,31 +8,26 @@
|
||||
@html.main(s"New Issue - ${repository.owner}/${repository.name}", Some(repository)){
|
||||
@html.menu("issues", repository){
|
||||
<form action="@url(repository)/issues/new" method="POST" validate="true" class="form-group">
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<div class="issue-avatar-image">@avatarLink(loginAccount.get.userName, 48)</div>
|
||||
<div class="panel panel-default issue-box">
|
||||
<div class="panel-body">
|
||||
<span id="error-title" class="error"></span>
|
||||
<input type="text" id="issue-title" name="title" class="form-control input-lg" value="" placeholder="Title" autofocus/>
|
||||
@helper.html.preview(
|
||||
repository = repository,
|
||||
content = "",
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableLineBreaks = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = hasWritePermission,
|
||||
style = "height: 200px; max-height: 250px;",
|
||||
elastic = true
|
||||
)
|
||||
<div class="align-right">
|
||||
<input type="submit" class="btn btn-lg btn-success" value="Submit new issue"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-md-9">
|
||||
<span id="error-title" class="error"></span>
|
||||
<input type="text" id="issue-title" name="title" class="form-control" value="" placeholder="Title" style="margin-bottom: 6px;" autofocus/>
|
||||
@helper.html.preview(
|
||||
repository = repository,
|
||||
content = "",
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableLineBreaks = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = hasWritePermission,
|
||||
style = "height: 200px; max-height: 250px;",
|
||||
elastic = true
|
||||
)
|
||||
<div class="align-right">
|
||||
<input type="submit" class="btn btn-success" value="Submit new issue"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-3">
|
||||
@issueinfo(None, Nil, Nil, collaborators, milestones.map(x => (x, 0, 0)), labels, hasWritePermission, repository)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<textarea id="edit-content-@commentId" class="form-control">@content</textarea>
|
||||
}
|
||||
<div>
|
||||
<input type="button" id="cancel-comment-@commentId" class="btn btn-lg btn-danger" value="Cancel"/>
|
||||
<input type="button" id="update-comment-@commentId" class="btn btn-lg btn-default pull-right" value="Update comment"/>
|
||||
<input type="button" id="cancel-comment-@commentId" class="btn btn-danger" value="Cancel"/>
|
||||
<input type="button" id="update-comment-@commentId" class="btn btn-default pull-right" value="Update comment"/>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
<textarea id="edit-content" class="form-control">@content.getOrElse("")</textarea>
|
||||
}
|
||||
<div>
|
||||
<input type="button" id="cancel-issue" class="btn btn-lg btn-danger" value="Cancel"/>
|
||||
<input type="button" id="update-issue" class="btn btn-lg btn-default pull-right" value="Update comment"/>
|
||||
<input type="button" id="cancel-issue" class="btn btn-danger" value="Cancel"/>
|
||||
<input type="button" id="update-issue" class="btn btn-default pull-right" value="Update comment"/>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<a class="btn btn-success" href="@url(repository)/issues/new">New issue</a>
|
||||
</div>
|
||||
<div class="edit-title pull-right" style="display: none;">
|
||||
<a class="btn btn-default" href="#" id="update">Save</a> <a href="#" id="cancel">Cancel</a>
|
||||
<a class="btn btn-success" href="#" id="update">Save</a> <a class="btn btn-default" href="#" id="cancel">Cancel</a>
|
||||
</div>
|
||||
<h1>
|
||||
<span class="show-title">
|
||||
@@ -46,12 +46,12 @@
|
||||
</span>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row" style="margin-top: 15px;">
|
||||
<div class="col-md-10">
|
||||
<div class="row-fluid" style="margin-top: 15px;">
|
||||
<div class="col-md-9">
|
||||
@commentlist(Some(issue), comments, hasWritePermission, repository)
|
||||
@commentform(issue, true, hasWritePermission, repository)
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-3">
|
||||
@issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
<div style="margin-bottom: 8px;">
|
||||
<div style="margin-bottom: 14px;">
|
||||
<span class="muted small strong">Labels</span>
|
||||
@if(hasWritePermission){
|
||||
<div class="pull-right">
|
||||
@helper.html.dropdown(right = true) {
|
||||
@helper.html.dropdown("Edit", right = true) {
|
||||
@labels.map { label =>
|
||||
<li>
|
||||
<a href="#" class="toggle-label" data-label-id="@label.labelId">
|
||||
@@ -33,11 +33,11 @@
|
||||
@labellist(issueLabels)
|
||||
</ul>
|
||||
<hr/>
|
||||
<div style="margin-bottom: 8px;">
|
||||
<div style="margin-bottom: 14px;">
|
||||
<span class="muted small strong">Milestone</span>
|
||||
@if(hasWritePermission){
|
||||
<div class="pull-right">
|
||||
@helper.html.dropdown(right = true) {
|
||||
@helper.html.dropdown("Edit", right = true) {
|
||||
<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>
|
||||
@@ -85,11 +85,11 @@
|
||||
<input type="hidden" name="milestoneId" value=""/>
|
||||
}
|
||||
<hr/>
|
||||
<div style="margin-bottom: 8px;">
|
||||
<div style="margin-bottom: 14px;">
|
||||
<span class="muted small strong">Assignee</span>
|
||||
@if(hasWritePermission){
|
||||
<div class="pull-right">
|
||||
@helper.html.dropdown(right = true) {
|
||||
@helper.html.dropdown("Edit", right = true) {
|
||||
<li><a href="javascript:void(0);" class="assign" data-name=""><i class="octicon octicon-x"></i> Clear assignee</a></li>
|
||||
@collaborators.map { collaborator =>
|
||||
<li>
|
||||
@@ -114,7 +114,7 @@
|
||||
}
|
||||
@issue.map { issue =>
|
||||
<hr/>
|
||||
<div style="margin-bottom: 8px;">
|
||||
<div style="margin-bottom: 14px;">
|
||||
@defining((issue.openedUserName :: comments.map(_.commentedUserName)).distinct){ participants =>
|
||||
<div class="muted small strong">@participants.size @plural(participants.size, "participant")</div>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@(issueLabels: List[gitbucket.core.model.Label])
|
||||
@if(issueLabels.isEmpty){
|
||||
<li><span class="muted small">None yet</span></li>
|
||||
<li><span class="muted">None yet</span></li>
|
||||
}
|
||||
@issueLabels.map { label =>
|
||||
<li><span class="issue-label" style="background-color: #@label.color; color: #@label.fontColor;">@label.labelName</span></li>
|
||||
|
||||
@@ -5,9 +5,12 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"Labels - ${repository.owner}/${repository.name}"){
|
||||
@html.menu("issues", repository){
|
||||
@issues.html.navigation("labels", hasWritePermission, repository)
|
||||
<br>
|
||||
@html.menu("labels", repository){
|
||||
@if(loginAccount.isDefined){
|
||||
<div class="pull-right" style="margin-bottom: 10px;">
|
||||
<a class="btn btn-success" href="javascript:void(0);" id="new-label-button">New label</a>
|
||||
</div>
|
||||
}
|
||||
<table class="table table-bordered table-hover table-issues" id="new-label-table" style="display: none;">
|
||||
<tr><td></td></tr>
|
||||
</table>
|
||||
|
||||
@@ -13,7 +13,24 @@
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main((if(target == "issues") "Issues" else "Pull requests") + s" - ${repository.owner}/${repository.name}", Some(repository)){
|
||||
@html.menu(target, repository){
|
||||
@navigation(target, true, repository, Some(condition))
|
||||
<ul class="nav nav-pills pull-left" style="line-height: 14px; margin-bottom: 10px;">
|
||||
<li class="@if(condition.state == "open"){active}">
|
||||
<a href="@condition.copy(state = "open").toURL">Open <span class="badge">@openCount</span></a>
|
||||
</li>
|
||||
<li class="@if(condition.state == "closed"){active}">
|
||||
<a href="@condition.copy(state = "closed").toURL">Closed <span class="badge">@closedCount</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
<form method="GET" id="search-filter-form" class="form-inline pull-right">
|
||||
@if(loginAccount.isDefined){
|
||||
@if(target == "issues"){
|
||||
<a class="btn btn-success" href="@url(repository)/issues/new">New issue</a>
|
||||
}
|
||||
@if(target == "pulls"){
|
||||
<a class="btn btn-success" href="@url(repository)/compare">New pull request</a>
|
||||
}
|
||||
}
|
||||
</form>
|
||||
@listparts(target, issues, page, openCount, closedCount, condition, collaborators, milestones, labels, Some(repository), hasWritePermission)
|
||||
@if(hasWritePermission){
|
||||
<form id="batcheditForm" method="POST">
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@import gitbucket.core.service.IssuesService.IssueInfo
|
||||
@*
|
||||
@if(condition.nonEmpty){
|
||||
<div style="width: 100%; display: inline-block;">
|
||||
<a href="@gitbucket.core.service.IssuesService.IssueSearchCondition().toURL" class="header-link">
|
||||
@@ -20,23 +21,14 @@
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
*@
|
||||
<table class="table table-bordered table-hover table-issues">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="background-color: #eee;">
|
||||
<input type="checkbox"/>
|
||||
<span class="small">
|
||||
<a class="button-link@if(condition.state == "open"){ selected}" href="@condition.copy(state = "open").toURL">
|
||||
<i class="octicon octicon-issue-opened @(if(condition.state == "open"){"active"})"></i>
|
||||
@openCount Open
|
||||
</a>
|
||||
<a class="button-link@if(condition.state == "closed"){ selected}" href="@condition.copy(state = "closed").toURL">
|
||||
<i class="octicon octicon-check @(if(condition.state == "closed"){"active"})"></i>
|
||||
@closedCount Closed
|
||||
</a>
|
||||
</span>
|
||||
<div class="pull-right" id="table-issues-control">
|
||||
@helper.html.dropdown("Author", flat = true) {
|
||||
<span id="table-issues-control">
|
||||
@helper.html.dropdown("Author") {
|
||||
@collaborators.map { collaborator =>
|
||||
<li>
|
||||
<a href="@condition.copy(author = (if(condition.author == Some(collaborator)) None else Some(collaborator))).toURL">
|
||||
@@ -46,7 +38,7 @@
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@helper.html.dropdown("Label", flat = true) {
|
||||
@helper.html.dropdown("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">
|
||||
@@ -57,7 +49,7 @@
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@helper.html.dropdown("Milestone", flat = true) {
|
||||
@helper.html.dropdown("Milestone") {
|
||||
<li>
|
||||
<a href="@condition.copy(milestone = Some(None)).toURL">
|
||||
@helper.html.checkicon(condition.milestone == Some(None)) Issues with no milestone
|
||||
@@ -71,7 +63,7 @@
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@helper.html.dropdown("Assignee", flat = true) {
|
||||
@helper.html.dropdown("Assignee") {
|
||||
@collaborators.map { collaborator =>
|
||||
<li>
|
||||
<a href="@condition.copy(assigned = Some(collaborator)).toURL">
|
||||
@@ -81,7 +73,7 @@
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@helper.html.dropdown("Sort", flat = true){
|
||||
@helper.html.dropdown("Sort"){
|
||||
<li>
|
||||
<a href="@condition.copy(sort="created", direction="desc").toURL">
|
||||
@helper.html.checkicon(condition.sort == "created" && condition.direction == "desc") Newest
|
||||
@@ -113,14 +105,14 @@
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</div>
|
||||
</span>
|
||||
@if(hasWritePermission){
|
||||
<div class="pull-right" id="table-issues-batchedit">
|
||||
@helper.html.dropdown("Mark as", flat = true) {
|
||||
<span id="table-issues-batchedit">
|
||||
@helper.html.dropdown("Mark as") {
|
||||
<li><a href="javascript:void(0);" class="toggle-state" data-id="open">Open</a></li>
|
||||
<li><a href="javascript:void(0);" class="toggle-state" data-id="close">Close</a></li>
|
||||
}
|
||||
@helper.html.dropdown("Label", flat = true) {
|
||||
@helper.html.dropdown("Label") {
|
||||
@labels.map { label =>
|
||||
<li>
|
||||
<a href="javascript:void(0);" class="toggle-label" data-id="@label.labelId">
|
||||
@@ -131,19 +123,19 @@
|
||||
</li>
|
||||
}
|
||||
}
|
||||
@helper.html.dropdown("Milestone", flat = true) {
|
||||
@helper.html.dropdown("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>
|
||||
}
|
||||
}
|
||||
@helper.html.dropdown("Assignee", flat = true) {
|
||||
@helper.html.dropdown("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>@avatar(collaborator, 20) @collaborator</a></li>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
</th>
|
||||
</tr>
|
||||
@@ -157,9 +149,11 @@
|
||||
} else {
|
||||
No pull requests to show.
|
||||
}
|
||||
@*
|
||||
@if(condition.labels.nonEmpty || condition.milestone.isDefined){
|
||||
<a href="@condition.copy(labels = Set.empty, milestone = None).toURL">Clear active filters.</a>
|
||||
} else {
|
||||
*@
|
||||
@if(repository.isDefined){
|
||||
@if(target == "issues"){
|
||||
<a href="@url(repository.get)/issues/new">Create a new issue.</a>
|
||||
@@ -167,7 +161,9 @@
|
||||
<a href="@url(repository.get)/compare">Create a new pull request.</a>
|
||||
}
|
||||
}
|
||||
@*
|
||||
}
|
||||
*@
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@@ -177,7 +173,9 @@
|
||||
@if(hasWritePermission){
|
||||
<input type="checkbox" value="@issue.issueId"/>
|
||||
}
|
||||
@*
|
||||
<i class="octicon octicon-issue-@(if(issue.closed) "closed" else "opened")" style="margin-right: 3px;"></i>
|
||||
*@
|
||||
@if(repository.isEmpty){
|
||||
<a href="@path/@issue.userName/@issue.repositoryName">@issue.repositoryName</a> ・
|
||||
}
|
||||
@@ -204,7 +202,7 @@
|
||||
</a>
|
||||
}
|
||||
</span>
|
||||
<div class="small muted" style="margin-left: 40px; margin-top: 2px;">
|
||||
<div class="small muted" style="margin-left: 12px; margin-top: 2px;">
|
||||
#@issue.issueId opened @helper.html.datetimeago(issue.registeredDate) by @user(issue.openedUserName, styleClass="username")
|
||||
@milestone.map { milestone =>
|
||||
<span style="margin: 20px;"><a href="@condition.copy(milestone = Some(Some(milestone))).toURL" class="username"><i class="octicon octicon-milestone"></i> @milestone</a></span>
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"Milestones - ${repository.owner}/${repository.name}"){
|
||||
@html.menu("issues", repository){
|
||||
@html.menu("milestones", repository){
|
||||
@if(milestone.isEmpty){
|
||||
<h4>New milestone</h4>
|
||||
<div class="muted">Create a new milestone to help organize your issues and pull requests.</div>
|
||||
} else {
|
||||
@issues.html.navigation("milestones", false, repository)
|
||||
<br><br>
|
||||
}
|
||||
<hr style="margin-top: 12px; margin-bottom: 18px;" class="fill-width"/>
|
||||
<form method="POST" action="@url(repository)/issues/milestones/@if(milestone.isEmpty){new}else{@milestone.get.milestoneId/edit}" validate="true">
|
||||
@@ -30,7 +27,7 @@
|
||||
<hr>
|
||||
<div class="pull-right">
|
||||
@if(milestone.isEmpty){
|
||||
<input type="submit" class="btn btn-default" value="Create milestone"/>
|
||||
<input type="submit" class="btn btn-success" value="Create milestone"/>
|
||||
} else {
|
||||
@if(milestone.get.closedDate.isDefined){
|
||||
<input type="button" class="btn btn-default" value="Open" id="open"
|
||||
@@ -39,7 +36,7 @@
|
||||
<input type="button" class="btn btn-default" value="Close" id="close"
|
||||
onclick="location.href='@url(repository)/issues/milestones/@milestone.get.milestoneId/open';"/>
|
||||
}
|
||||
<input type="submit" class="btn btn-default" value="Update milestone"/>
|
||||
<input type="submit" class="btn btn-success" value="Update milestone"/>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -5,20 +5,21 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"Milestones - ${repository.owner}/${repository.name}"){
|
||||
@html.menu("issues", repository){
|
||||
@issues.html.navigation("milestones", hasWritePermission, repository)
|
||||
<br>
|
||||
@html.menu("milestones", repository){
|
||||
@if(loginAccount.isDefined){
|
||||
<div class="pull-right" style="margin-bottom: 10px;">
|
||||
<a class="btn btn-success" href="@url(repository)/issues/milestones/new">New milestone</a>
|
||||
</div>
|
||||
}
|
||||
<table class="table table-bordered table-hover table-issues">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="background-color: #eee;">
|
||||
<span class="small">
|
||||
<a class="button-link@if(state == "open"){ selected}" href="?state=open">
|
||||
<i class="octicon octicon-milestone @(if(state == "open"){"active"})"></i>
|
||||
@milestones.count(_._1.closedDate.isEmpty) Open
|
||||
</a>
|
||||
<a class="button-link@if(state == "closed"){ selected}" href="?state=closed">
|
||||
<i class="octicon octicon-milestone @(if(state == "closed"){"active"})"></i>
|
||||
@milestones.count(_._1.closedDate.isDefined) Closed
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
@(active: String,
|
||||
newButton: Boolean,
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
condition: Option[gitbucket.core.service.IssuesService.IssueSearchCondition] = None)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
<ul class="nav nav-pills pull-left" style="line-height: 14px;">
|
||||
<li class="@if(active == "issues" ){active}"><a href="@url(repository)/issues">Issues</a></li>
|
||||
<li class="@if(active == "pulls" ){active}"><a href="@url(repository)/pulls">Pull requests</a></li>
|
||||
<li class="@if(active == "labels" ){active}"><a href="@url(repository)/issues/labels">Labels</a></li>
|
||||
<li class="@if(active == "milestones"){active}"><a href="@url(repository)/issues/milestones">Milestones</a></li>
|
||||
</ul>
|
||||
<form method="GET" id="search-filter-form" style="margin-bottom: 0px;" class="form-inline pull-right">
|
||||
@condition.map { condition =>
|
||||
@if(loginAccount.isDefined){
|
||||
<div class="form-group" style="width: 300px">
|
||||
<div class="input-group" style="margin-bottom: 0px;">
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default btn-lg dropdown-toggle" data-toggle="dropdown">
|
||||
Filter
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="?q=is:open">Open issues and pull requests</a></li>
|
||||
<li><a href="?q=is:open+is:issue+author:@urlEncode(loginAccount.get.userName)">Your issues</a></li>
|
||||
<li><a href="?q=is:open+is:pr+author:@urlEncode(loginAccount.get.userName)">Your pull requests</a></li>
|
||||
<li><a href="?q=is:open+assignee:@urlEncode(loginAccount.get.userName)">Everything assigned to you</a></li>
|
||||
@*
|
||||
<li><a href="?q=is:open+mentions:@urlEncode(loginAccount.get.userName)">Everything mentioning you</a></li>
|
||||
*@
|
||||
</ul>
|
||||
</div>
|
||||
<input type="text" id="search-filter-box" class="form-control input-lg" size="40" name="q" value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
|
||||
</div>
|
||||
</div>
|
||||
} else {
|
||||
<input type="text" id="search-filter-box" class="form-control input-lg" size="40" name="q" value="is:@{if(active == "issues") "issue" else "pr"} @condition.toFilterString"/>
|
||||
}
|
||||
}
|
||||
@if(loginAccount.isDefined){
|
||||
@if(newButton){
|
||||
@if(active == "issues"){
|
||||
<a class="btn btn-success btn-lg" href="@url(repository)/issues/new">New issue</a>
|
||||
}
|
||||
@if(active == "pulls"){
|
||||
<a class="btn btn-success btn-lg" href="@url(repository)/compare">New pull request</a>
|
||||
}
|
||||
@if(active == "labels"){
|
||||
<a class="btn btn-success btn-lg" href="javascript:void(0);" id="new-label-button">New label</a>
|
||||
}
|
||||
@if(active == "milestones"){
|
||||
<a class="btn btn-success btn-lg" href="@url(repository)/issues/milestones/new">New milestone</a>
|
||||
}
|
||||
}
|
||||
}
|
||||
</form>
|
||||
@@ -11,10 +11,7 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<link rel="icon" href="@assets/common/images/gitbucket.png" type="image/vnd.microsoft.icon" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="@assets/vendors/bootstrap-theme-github-3.1.1/css/bootstrap.css" rel="stylesheet">
|
||||
@*
|
||||
<link href="@assets/vendors/bootstrap-theme-github-3.1.1/css/bootstrap-responsive.css" rel="stylesheet">
|
||||
*@
|
||||
<link href="@assets/vendors/bootstrap-3.3.6/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="@assets/vendors/octicons/octicons.css" rel="stylesheet">
|
||||
<link href="@assets/vendors/datepicker/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
|
||||
<link href="@assets/vendors/colorpicker/css/bootstrap-colorpicker.css" rel="stylesheet">
|
||||
@@ -25,7 +22,7 @@
|
||||
<script src="@assets/vendors/dropzone/dropzone.js"></script>
|
||||
<script src="@assets/common/js/validation.js"></script>
|
||||
<script src="@assets/common/js/gitbucket.js"></script>
|
||||
<script src="@assets/vendors/bootstrap-theme-github-3.1.1/js/bootstrap.js"></script>
|
||||
<script src="@assets/vendors/bootstrap-3.3.6/js/bootstrap.js"></script>
|
||||
<script src="@assets/vendors/bootstrap3-typeahead/bootstrap3-typeahead.js"></script>
|
||||
<script src="@assets/vendors/datepicker/js/moment.js"></script>
|
||||
<script src="@assets/vendors/datepicker/js/bootstrap-datetimepicker.min.js"></script>
|
||||
@@ -36,7 +33,7 @@
|
||||
<script src="@assets/vendors/facebox/facebox.js"></script>
|
||||
<script src="@assets/vendors/jquery-hotkeys/jquery.hotkeys.js"></script>
|
||||
@repository.map { repository =>
|
||||
@if(!repository.repository.`private`){
|
||||
@if(!repository.repository.isPrivate){
|
||||
<meta name="go-import" content="@context.baseUrl.replaceFirst("^https?://", "")/@repository.owner/@repository.name git @repository.httpUrl" />
|
||||
}
|
||||
}
|
||||
@@ -44,7 +41,7 @@
|
||||
<body>
|
||||
<form id="search" action="@path/search" method="POST" class="form-inline">
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container" style="width: 980px;">
|
||||
<div class="container">
|
||||
@* TODO: for plugi-ins?
|
||||
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
@@ -53,14 +50,14 @@
|
||||
</button>
|
||||
*@
|
||||
<a class="navbar-brand" href="@path/">
|
||||
<img src="@assets/common/images/gitbucket.png" style="width: 24px; height: 24px;"/>GitBucket
|
||||
<img src="@assets/common/images/gitbucket.png" style="width: 24px; height: 24px; display: inline;"/>GitBucket
|
||||
@defining(AutoUpdate.getCurrentVersion){ version =>
|
||||
<span class="header-version">@version.majorVersion.@version.minorVersion</span>
|
||||
}
|
||||
</a>
|
||||
@if(loginAccount.isDefined){
|
||||
@repository.map { repository =>
|
||||
<input type="text" name="query" class="form-control" style="width: 200px; margin-bottom: 0px;" placeholder="Search this repository"/>
|
||||
<input type="text" name="query" class="form-control" style="width: 400px; margin-top: 3px; margin-bottom: 3px;" placeholder="Search this repository"/>
|
||||
<input type="hidden" name="owner" value="@repository.owner"/>
|
||||
<input type="hidden" name="repository" value="@repository.name"/>
|
||||
}
|
||||
@@ -69,11 +66,16 @@
|
||||
} else {
|
||||
@* TODO: merge with below *@
|
||||
@repository.map { repository =>
|
||||
<input type="text" name="query" class="form-control" style="width: 200px; margin-top: 6px; margin-bottom: 0px;" placeholder="Search this repository"/>
|
||||
<input type="text" name="query" class="form-control" style="width: 400px; margin-top: 3px; margin-bottom: 3px;" placeholder="Search this repository"/>
|
||||
<input type="hidden" name="owner" value="@repository.owner"/>
|
||||
<input type="hidden" name="repository" value="@repository.name"/>
|
||||
}
|
||||
}
|
||||
@gitbucket.core.plugin.PluginRegistry().getGlobalMenus.map { menu =>
|
||||
@menu(context).map { link =>
|
||||
<a href="@path/@link.path" class="global-header-menu">@link.label</a>
|
||||
}
|
||||
}
|
||||
@if(loginAccount.isDefined){
|
||||
<div class="pull-right" style="margin-top: 6px;">
|
||||
<div class="btn-group" style="margin-right: 8px;">
|
||||
@@ -92,7 +94,7 @@
|
||||
<ul class="dropdown-menu pull-right">
|
||||
<li><a href="@url(loginAccount.get.userName)">Your profile</a></li>
|
||||
<li><a href="@url(loginAccount.get.userName)/_edit">Account settings</a></li>
|
||||
@if(loginAccount.get.administrator){
|
||||
@if(loginAccount.get.isAdmin){
|
||||
<li><a href="@path/admin/users">System administration</a></li>
|
||||
}
|
||||
<li><a href="@path/signout">Sign out</a></li>
|
||||
@@ -100,7 +102,7 @@
|
||||
</div>
|
||||
</div>
|
||||
} else {
|
||||
<a href="@path/signin?redirect=@urlEncode(currentPath)" class="btn btn-default pull-right" style="margin-top: 6px;" id="signin">Sign in</a>
|
||||
<a href="@path/signin?redirect=@urlEncode(currentPath)" class="btn btn-default pull-right" style="margin-top: 3px; margin-bottom: 3px;" id="signin">Sign in</a>
|
||||
}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
@(active: String,
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
id: Option[String] = None,
|
||||
isRepoTop: Boolean = false,
|
||||
isNoGroup: Boolean = true,
|
||||
info: Option[Any] = None,
|
||||
error: Option[Any] = None)(body: Html)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
|
||||
@menuitem(path: String, name: String, icon: String, label: String, count: Int = 0) = {
|
||||
@menuitem(path: String, name: String, label: String, icon: String, count: Int = 0) = {
|
||||
<li @if(active == name){class="active"}>
|
||||
<a href="@url(repository)@path">
|
||||
<i class="menu-icon @if(active == name){menu-icon-active} octicon octicon-@{icon} "></i> <span class="pc">@label</span>
|
||||
@if(count > 0){
|
||||
<span class="badge">@count</span>
|
||||
}
|
||||
<i class="menu-icon octicon octicon-@icon"></i>
|
||||
<span class="pc">
|
||||
@label
|
||||
@if(count > 0){
|
||||
<span class="badge">@count</span>
|
||||
}
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@@ -24,29 +25,6 @@
|
||||
@helper.html.information(info)
|
||||
@helper.html.error(error)
|
||||
<div class="head">
|
||||
@if(repository.commitCount > 0){
|
||||
<div class="input-group pull-right">
|
||||
<span class="fork">
|
||||
<span class="input-group-btn">
|
||||
@if(loginAccount.isEmpty){
|
||||
<a title="You must be signed in to fork a repository" href="@path/signin?redirect=@urlEncode(s"${path}/${repository.owner}/${repository.name}")" class="btn btn-default">Fork</a>
|
||||
} else {
|
||||
@if(isNoGroup) {
|
||||
<a id="fork-link" href="javascript:void(0);" class="btn btn-default">Fork</a>
|
||||
} else {
|
||||
<a href="@path/@repository.owner/@repository.name/fork" class="btn btn-default" rel="facebox">Fork</a>
|
||||
}
|
||||
}
|
||||
</span>
|
||||
<span class="count"><a href="@url(repository)/network/members">@repository.forkedCount</a></span>
|
||||
</span>
|
||||
</div>
|
||||
@if(loginAccount.isDefined && isNoGroup){
|
||||
<form id="fork-form" method="post" action="@path/@repository.owner/@repository.name/fork" style="display: none;">
|
||||
<input type="hidden" name="account" value="@loginAccount.get.userName"/>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
@helper.html.repositoryicon(repository, true)
|
||||
<a href="@url(repository.owner)">@repository.owner</a> / <a href="@url(repository)" class="strong">@repository.name</a>
|
||||
|
||||
@@ -56,92 +34,39 @@
|
||||
forked from <a href="@path/@x.parentUserName/@x.parentRepositoryName">@x.parentUserName/@x.parentRepositoryName</a>
|
||||
</div>
|
||||
}
|
||||
@x.description.map { description =>
|
||||
<div class="normal muted" style="margin-left: 36px; font-size: 80%;">@detectAndRenderLinks(description)</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<ul class="headmenu">
|
||||
@menuitem("" , "code" , "code" , "Code")
|
||||
@menuitem("/issues", "issues" , "issue-opened" , "Issues", repository.issueCount)
|
||||
@menuitem("/pulls" , "pulls" , "git-pull-request" , "Pull Requests", repository.pullCount)
|
||||
@menuitem("/wiki" , "wiki" , "book" , "Wiki")
|
||||
@if(loginAccount.isDefined && (loginAccount.get.administrator || repository.managers.contains(loginAccount.get.userName))){
|
||||
@menuitem("/settings" , "settings" , "tools", "Settings")
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container body">
|
||||
@if(isRepoTop){
|
||||
@repository.repository.description.map { description =>
|
||||
<p class="description">@detectAndRenderLinks(description)</p>
|
||||
}
|
||||
<div style="margin-bottom: 10px; padding: 4px;" class="panel panel-default">
|
||||
<table class="fill-width">
|
||||
<tr>
|
||||
<td style="width: 33%; text-align: center;">
|
||||
<a href="@url(repository)/commits/@encodeRefName(id.getOrElse(""))" class="header-link">
|
||||
<i class="octicon octicon-history"></i>
|
||||
@if(repository.commitCount > 10000){
|
||||
<strong>10000+</strong> commits
|
||||
} else {
|
||||
<strong>@repository.commitCount</strong> commits
|
||||
}
|
||||
</a>
|
||||
</td>
|
||||
<td style="width: 33%; text-align: center;">
|
||||
<a href="@url(repository)/branches" class="header-link" class="header-link">
|
||||
<i class="octicon octicon-git-branch"></i>
|
||||
<strong>@repository.branchList.length</strong> branches
|
||||
</a>
|
||||
</td>
|
||||
<td style="width: 33%; text-align: center;">
|
||||
<a href="@url(repository)/tags" class="header-link" class="header-link">
|
||||
<i class="octicon octicon-tag"></i>
|
||||
<strong>@repository.tags.length</strong> releases
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
@body
|
||||
<div class="main-sidebar">
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
@menuitem("" ,"files" ,"Files", "code")
|
||||
@if(repository.commitCount != 0) {
|
||||
@menuitem("/branches" ,"branches" ,"Branches", "git-branch", repository.branchList.length)
|
||||
@menuitem("/tags" ,"tags" ,"Tags", "tag", repository.tags.length)
|
||||
}
|
||||
@menuitem("/issues" ,"issues" ,"Issues", "issue-opened", repository.issueCount)
|
||||
@menuitem("/pulls" ,"pulls" ,"Pull Requests", "git-pull-request", repository.pullCount)
|
||||
@menuitem("/issues/labels" ,"labels" ,"Labels", "tag")
|
||||
@menuitem("/issues/milestones" ,"milestones" ,"Milestones", "milestone")
|
||||
@menuitem("/wiki" ,"wiki" ,"Wiki", "book")
|
||||
@menuitem("/network/members", "fork", "Forks", "repo-forked", repository.forkedCount)
|
||||
@if(loginAccount.isDefined && (loginAccount.get.isAdmin || repository.managers.contains(loginAccount.get.userName))){
|
||||
@menuitem("/settings" , "settings" , "Settings", "tools")
|
||||
}
|
||||
@gitbucket.core.plugin.PluginRegistry().getRepositoryMenus.map { menu =>
|
||||
@menu(repository, context).map { link =>
|
||||
@menuitem(link.path, link.id, link.label, link.icon.getOrElse("ruby"))
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
@body
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(function(){
|
||||
|
||||
$('ul.sidemenu a').mouseover(function(e){
|
||||
var target = e.target;
|
||||
if(e.target.tagName == "I"){
|
||||
target = e.target.parentElement;
|
||||
}
|
||||
$(target).prev('div.gradient').css('border-left', '1px solid silver');
|
||||
});
|
||||
|
||||
$('ul.sidemenu a').mouseout(function(e){
|
||||
var target = e.target;
|
||||
if(e.target.tagName == "I"){
|
||||
target = e.target.parentElement;
|
||||
}
|
||||
$(target).prev('div.gradient').css('border-left', '1px solid #eee');
|
||||
});
|
||||
|
||||
$('a[rel*=facebox]').facebox({
|
||||
'loadingImage': '@assets/vendors/facebox/loading.gif',
|
||||
'closeImage': '@assets/vendors/facebox/closelabel.png'
|
||||
});
|
||||
|
||||
$(document).on("click", ".js-fork-owner-select-target", function() {
|
||||
if (!$(this).hasClass("disabled")) {
|
||||
var account = $(this).text().replace("@@", "");
|
||||
$("#account").val(account);
|
||||
$("#fork").submit();
|
||||
}
|
||||
});
|
||||
|
||||
@if(loginAccount.isDefined){
|
||||
$(document).on("click", "a#fork-link", function(e) {
|
||||
e.preventDefault();
|
||||
$('#fork-form').submit();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -4,42 +4,41 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@import gitbucket.core.model._
|
||||
<div class="commit-list">
|
||||
@commits.map { day =>
|
||||
<div class="muted" style="background-color: white;">
|
||||
<i class="octicon octicon-git-commit"></i> Commits on @date(day.head.commitTime)
|
||||
</div>
|
||||
<div class="list-group box-commits">
|
||||
@day.map { commit =>
|
||||
<div class="list-group-item">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="@url(repository)/commit/@commit.id" class="link monospace" style="line-height: 16px;">@commit.id.substring(0, 7)</a></li>
|
||||
<li><a href="@url(repository)/tree/@commit.id" style="line-height: 16px;"><i class="octicon octicon-code link"></i></a></li>
|
||||
</ul>
|
||||
<table class="table table-bordered">
|
||||
@commits.map { day =>
|
||||
<tr>
|
||||
<th rowspan="@day.size" width="100">@date(day.head.commitTime)</th>
|
||||
@day.zipWithIndex.map { case (commit, i) =>
|
||||
@if(i != 0){ <tr> }
|
||||
<td>
|
||||
<div class="pull-right text-right">
|
||||
<a href="@url(repository)/commit/@commit.id" class="monospace commit-message strong"><i class="octicon octicon-diff" style="color: black;"></i>@commit.id.substring(0, 7)</a><br>
|
||||
<a href="@url(repository)/tree/@commit.id" class="button-link">Browse files »</a>
|
||||
</div>
|
||||
<div>
|
||||
<div class="commit-avatar-image">@avatarLink(commit, 40)</div>
|
||||
<div>
|
||||
<div class="commit-avatar-image">@avatarLink(commit, 40)</div>
|
||||
<a href="@url(repository)/commit/@commit.id" class="commit-message strong">@link(commit.summary, repository)</a>
|
||||
@if(commit.description.isDefined){
|
||||
<a href="javascript:void(0)" onclick="$('#description-@commit.id').toggle();" class="omit">...</a>
|
||||
}
|
||||
<br>
|
||||
@if(commit.description.isDefined){
|
||||
<pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre>
|
||||
}
|
||||
<div>
|
||||
<a href="@url(repository)/commit/@commit.id" class="commit-message" style="font-weight: bold;">@link(commit.summary, repository)</a>
|
||||
@if(commit.description.isDefined){
|
||||
<a href="javascript:void(0)" onclick="$('#description-@commit.id').toggle();" class="omit">...</a>
|
||||
@user(commit.authorName, commit.authorEmailAddress, "username")
|
||||
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
|
||||
@if(commit.isDifferentFromAuthor) {
|
||||
<span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span>
|
||||
@user(commit.committerName, commit.committerEmailAddress, "username")
|
||||
<span class="muted">committed @helper.html.datetimeago(commit.authorTime)</span>
|
||||
}
|
||||
<br>
|
||||
@if(commit.description.isDefined){
|
||||
<pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre>
|
||||
}
|
||||
<div style="margin-top: 2px;">
|
||||
@user(commit.authorName, commit.authorEmailAddress, "username")
|
||||
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
|
||||
@if(commit.isDifferentFromAuthor) {
|
||||
<span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span>
|
||||
@user(commit.committerName, commit.committerEmailAddress, "username")
|
||||
<span class="muted">committed @helper.html.datetimeago(commit.authorTime)</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</table>
|
||||
|
||||
@@ -54,36 +54,31 @@
|
||||
<div id="pull-request-form" @*class="box"*@ style="display: none; margin-bottom: 20px;">
|
||||
<form method="POST" action="@path/@originRepository.owner/@originRepository.name/pulls/new" validate="true">
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<div class="issue-avatar-image">@avatarLink(loginAccount.get.userName, 48)</div>
|
||||
<div class="panel panel-default issue-box">
|
||||
<div class="panel-body">
|
||||
<span class="error" id="error-title"></span>
|
||||
<input type="text" name="title" class="form-control input-lg" placeholder="Title"/>
|
||||
@helper.html.preview(
|
||||
repository = repository,
|
||||
content = "",
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableLineBreaks = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = true,
|
||||
style = "height: 200px;"
|
||||
)
|
||||
<input type="hidden" name="targetUserName" value="@originRepository.owner"/>
|
||||
<input type="hidden" name="targetBranch" value="@originId"/>
|
||||
<input type="hidden" name="requestUserName" value="@forkedRepository.owner"/>
|
||||
<input type="hidden" name="requestRepositoryName" value="@forkedRepository.name"/>
|
||||
<input type="hidden" name="requestBranch" value="@forkedId"/>
|
||||
<input type="hidden" name="commitIdFrom" value="@sourceId"/>
|
||||
<input type="hidden" name="commitIdTo" value="@commitId"/>
|
||||
<div class="align-right">
|
||||
<input type="submit" class="btn btn-lg btn-success" value="Create pull request"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<span class="error" id="error-title"></span>
|
||||
<input type="text" name="title" class="form-control" style="margin-bottom: 6px;" placeholder="Title"/>
|
||||
@helper.html.preview(
|
||||
repository = repository,
|
||||
content = "",
|
||||
enableWikiLink = false,
|
||||
enableRefsLink = true,
|
||||
enableLineBreaks = true,
|
||||
enableTaskList = true,
|
||||
hasWritePermission = true,
|
||||
style = "height: 200px;"
|
||||
)
|
||||
<input type="hidden" name="targetUserName" value="@originRepository.owner"/>
|
||||
<input type="hidden" name="targetBranch" value="@originId"/>
|
||||
<input type="hidden" name="requestUserName" value="@forkedRepository.owner"/>
|
||||
<input type="hidden" name="requestRepositoryName" value="@forkedRepository.name"/>
|
||||
<input type="hidden" name="requestBranch" value="@forkedId"/>
|
||||
<input type="hidden" name="commitIdFrom" value="@sourceId"/>
|
||||
<input type="hidden" name="commitIdTo" value="@commitId"/>
|
||||
<div class="align-right">
|
||||
<input type="submit" class="btn btn-success" value="Create pull request"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-3">
|
||||
@issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map((_, 0, 0)), labels, hasOriginWritePermission, repository)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
@import gitbucket.core.model._
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<div class="col-md-9">
|
||||
<div id="comment-list">
|
||||
@issues.html.commentlist(Some(issue), comments, hasWritePermission, repository, Some(pullreq))
|
||||
</div>
|
||||
@@ -44,7 +44,7 @@
|
||||
@issues.html.commentform(issue, !merged, hasWritePermission, repository)
|
||||
}
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="col-md-3">
|
||||
@issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<div class="pull-right">
|
||||
<form method="POST" action="@url(originRepository)/pull/@pullreq.issueId/update_branch">
|
||||
<input type="hidden" name="expected_head_oid" value="@pullreq.commitIdFrom">
|
||||
<button class="btn"@if(!status.canUpdate){ disabled="true"} id="update-branch-button">Update branch</button>
|
||||
<button class="btn btn-default"@if(!status.canUpdate){ disabled="true"} id="update-branch-button">Update branch</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
@@ -83,7 +83,7 @@
|
||||
</div>
|
||||
@if(status.hasMergePermission){
|
||||
<div style="padding:15px;border-top:solid 1px #e5e5e5;background:#fafafa">
|
||||
<input type="button" class="btn btn-lg @if(!status.hasProblem){btn-success} else {btn-default}" id="merge-pull-request-button" value="Merge pull request"@if(!status.canMerge){ disabled="true"}/>
|
||||
<input type="button" class="btn @if(!status.hasProblem){btn-success} else {btn-default}" id="merge-pull-request-button" value="Merge pull request"@if(!status.canMerge){ disabled="true"}/>
|
||||
You can also merge branches on the <a href="#" class="show-command-line">command line</a>.
|
||||
<div id="command-line" style="display: none;margin-top: 15px;">
|
||||
<hr />
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="edit-title pull-right" style="display: none;">
|
||||
<a class="btn" href="#" id="update">Save</a> <a href="#" id="cancel">Cancel</a>
|
||||
<a class="btn btn-success" href="#" id="update">Save</a> <a class="btn btn-default" href="#" id="cancel">Cancel</a>
|
||||
</div>
|
||||
<h1>
|
||||
<span class="show-title">
|
||||
@@ -35,7 +35,7 @@
|
||||
</span>
|
||||
<span class="edit-title" style="display: none;">
|
||||
<span id="error-edit-title" class="error"></span>
|
||||
<input type="text" style="width: 700px;" id="edit-title" value="@issue.title"/>
|
||||
<input type="text" class="form-control" style="width: 700px;" id="edit-title" value="@issue.title"/>
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
@@ -66,7 +66,7 @@
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
<ul class="nav nav-tabs fill-width pull-left" id="pullreq-tab">
|
||||
<ul class="nav nav-tabs fill-width" id="pullreq-tab">
|
||||
<li><a href="#conversation">Conversation <span class="badge">@comments.flatMap @{
|
||||
case comment: IssueComment => Some(comment)
|
||||
case _: CommitComment => None
|
||||
@@ -74,7 +74,7 @@
|
||||
<li><a href="#commits">Commits <span class="badge">@commits.size</span></a></li>
|
||||
<li><a href="#files">Files Changed <span class="badge">@diffs.size</span></a></li>
|
||||
</ul>
|
||||
<div class="tab-content fill-width pull-left">
|
||||
<div class="tab-content fill-width" style="padding-top: 20px;">
|
||||
<div class="tab-pane" id="conversation">
|
||||
@flash.get("error").map{ error =>
|
||||
<div class="alert alert-error">@error</div>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"${(repository.name :: pathList).mkString("/")} at ${encodeRefName(branch)} - ${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
@html.menu("files", repository){
|
||||
<div class="head">
|
||||
<div class="pull-right hide-if-blame"><div class="btn-group">
|
||||
<a href="@url(repository)/find/@encodeRefName(branch)" class="btn btn-sm btn-default" data-hotkey="t">Find file</a>
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
</div>
|
||||
<div class="box-header">
|
||||
@avatar(latestCommit, 20)
|
||||
@avatar(latestCommit, 28)
|
||||
@user(latestCommit.authorName, latestCommit.authorEmailAddress, "username strong")
|
||||
<span class="muted">@helper.html.datetimeago(latestCommit.commitTime)</span>
|
||||
<a href="@url(repository)/commit/@latestCommit.id" class="commit-message">@link(latestCommit.summary, repository)</a>
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
<h1>Branches</h1>
|
||||
<table class="table table-bordered table-hover table-issues branches">
|
||||
@html.menu("branches", repository){
|
||||
<table class="table table-bordered table-hover branches">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="background: #f5f5f5;color: #666;">All branches</th>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(commit.shortMessage, Some(repository)){
|
||||
@html.menu("code", repository){
|
||||
@html.menu("files", repository){
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th class="box-header">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
@html.menu("files", repository){
|
||||
<div class="head">
|
||||
@if(pathList.isEmpty){
|
||||
@helper.html.branchcontrol(
|
||||
@@ -33,47 +33,47 @@
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<div class="commit-list">
|
||||
@commits.map { day =>
|
||||
<div class="muted" style="background-color: white;">
|
||||
<i class="octicon octicon-git-commit"></i> Commits on @date(day.head.commitTime)
|
||||
</div>
|
||||
<div class="list-group box-commits">
|
||||
@day.map { commit =>
|
||||
<div class="list-group-item">
|
||||
<ul class="nav nav-pills pull-right">
|
||||
<li><a href="@url(repository)/commit/@commit.id" class="link monospace" style="line-height: 16px;">@commit.id.substring(0, 7)</a></li>
|
||||
<li><a href="@url(repository)/tree/@commit.id" style="line-height: 16px;"><i class="octicon octicon-code link"></i></a></li>
|
||||
</ul>
|
||||
|
||||
<table class="table table-bordered">
|
||||
@commits.map { day =>
|
||||
<tr>
|
||||
<th rowspan="@day.size" width="100">@date(day.head.commitTime)</th>
|
||||
@day.zipWithIndex.map { case (commit, i) =>
|
||||
@if(i != 0){ <tr> }
|
||||
<td>
|
||||
<div class="pull-right text-right">
|
||||
<a href="@url(repository)/commit/@commit.id" class="monospace commit-message strong"><i class="octicon octicon-diff" style="color: black;"></i>@commit.id.substring(0, 7)</a><br>
|
||||
<a href="@url(repository)/tree/@commit.id" class="button-link">Browse files »</a>
|
||||
</div>
|
||||
<div>
|
||||
<div class="commit-avatar-image">@avatarLink(commit, 40)</div>
|
||||
<div>
|
||||
<div class="commit-avatar-image">@avatarLink(commit, 40)</div>
|
||||
<a href="@url(repository)/commit/@commit.id" class="commit-message strong">@link(commit.summary, repository)</a>
|
||||
@if(commit.description.isDefined){
|
||||
<a href="javascript:void(0)" onclick="$('#description-@commit.id').toggle();" class="omit">...</a>
|
||||
}
|
||||
<br>
|
||||
@if(commit.description.isDefined){
|
||||
<pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre>
|
||||
}
|
||||
<div>
|
||||
<a href="@url(repository)/commit/@commit.id" class="commit-message" style="font-weight: bold;">@link(commit.summary, repository)</a>
|
||||
@if(commit.description.isDefined){
|
||||
<a href="javascript:void(0)" onclick="$('#description-@commit.id').toggle();" class="omit">...</a>
|
||||
@user(commit.authorName, commit.authorEmailAddress, "username")
|
||||
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
|
||||
@if(commit.isDifferentFromAuthor) {
|
||||
<span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span>
|
||||
@user(commit.committerName, commit.committerEmailAddress, "username")
|
||||
<span class="muted">committed @helper.html.datetimeago(commit.authorTime)</span>
|
||||
}
|
||||
<br>
|
||||
@if(commit.description.isDefined){
|
||||
<pre id="description-@commit.id" style="display: none;" class="commit-description">@link(commit.description.get, repository)</pre>
|
||||
}
|
||||
<div style="margin-top: 2px;">
|
||||
@user(commit.authorName, commit.authorEmailAddress, "username")
|
||||
<span class="muted">authored @helper.html.datetimeago(commit.authorTime)</span>
|
||||
@if(commit.isDifferentFromAuthor) {
|
||||
<span class="octicon octicon-arrow-right" style="margin-top : -2px;"></span>
|
||||
@user(commit.committerName, commit.committerEmailAddress, "username")
|
||||
<span class="muted">committed @helper.html.datetimeago(commit.authorTime)</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
</table>
|
||||
|
||||
<nav style="text-align: center; margin-top: 30px">
|
||||
<nav style="text-align: center; margin-top: 20px">
|
||||
<ul class="pagination">
|
||||
@if(page <= 1){
|
||||
<li class="disabled"><span>Newer</span></li>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"Deleting ${path} at ${fileName} - ${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
@html.menu("files", repository){
|
||||
<form method="POST" action="@url(repository)/remove" validate="true">
|
||||
<div class="head">
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(if(fileName.isEmpty) "New File" else s"Editing ${fileName.get} at ${branch} - ${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
@html.menu("files", repository){
|
||||
@if(protectedBranch){
|
||||
<div class="alert alert-danger">branch @branch is protected.</div>
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
@pathList.zipWithIndex.map { case (section, i) =>
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
|
||||
}
|
||||
<input type="text" name="newFileName" id="newFileName" placeholder="Name your file..." value="@fileName"/>
|
||||
<input type="text" name="newFileName" id="newFileName" class="form-control" placeholder="Name your file..." value="@fileName"/>
|
||||
<input type="hidden" name="oldFileName" id="oldFileName" value="@fileName"/>
|
||||
<input type="hidden" name="branch" id="branch" value="@branch"/>
|
||||
<input type="hidden" name="path" id="path" value="@pathList.mkString("/")"/>
|
||||
@@ -27,7 +27,7 @@
|
||||
<tr>
|
||||
<th>
|
||||
<div class="pull-right">
|
||||
<select id="wrap" class="form-control" style="margin-bottom: 0px; height: 26px; padding: 0px;">
|
||||
<select id="wrap" class="form-control" style="margin-bottom: 0px; padding: 0px;">
|
||||
<optgroup label="Line Wrap Mode">
|
||||
<option value="false">No wrap</option>
|
||||
<option value="true">Soft wrap</option>
|
||||
@@ -58,11 +58,11 @@
|
||||
</div>
|
||||
<div style="text-align: right;">
|
||||
@if(fileName.isEmpty){
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)/@{pathList.mkString("/")}" class="btn btn-lg btn-danger">Cancel</a>
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)/@{pathList.mkString("/")}" class="btn btn-danger">Cancel</a>
|
||||
} else {
|
||||
<a href="@url(repository)/blob/@encodeRefName(branch)/@{(pathList ++ Seq(fileName.get)).mkString("/")}" class="btn btn-lg btn-danger">Cancel</a>
|
||||
<a href="@url(repository)/blob/@encodeRefName(branch)/@{(pathList ++ Seq(fileName.get)).mkString("/")}" class="btn btn-danger">Cancel</a>
|
||||
}
|
||||
<input type="submit" id="commit" class="btn btn-lg btn-success" value="Commit changes" disabled="true"/>
|
||||
<input type="submit" id="commit" class="btn btn-success" value="Commit changes" disabled="true"/>
|
||||
<input type="hidden" id="charset" name="charset" value="@content.charset"/>
|
||||
<input type="hidden" id="lineSeparator" name="lineSeparator" value="@content.lineSeparator"/>
|
||||
<input type="hidden" id="content" name="content" value=""/>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@(branch: String,
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
pathList: List[String],
|
||||
groupNames: List[String],
|
||||
latestCommit: gitbucket.core.util.JGitUtil.CommitInfo,
|
||||
files: List[gitbucket.core.util.JGitUtil.FileInfo],
|
||||
readme: Option[(List[String], String)],
|
||||
@@ -22,17 +21,25 @@
|
||||
} else {
|
||||
s"${(repository.name :: pathList).mkString("/")} at ${encodeRefName(branch)} - ${repository.owner}/${repository.name}"
|
||||
}, Some(repository)) {
|
||||
@html.menu("code", repository, Some(branch), pathList.isEmpty, groupNames.isEmpty, info, error){
|
||||
@html.menu("files", repository, Some(branch), info, error){
|
||||
<div class="head">
|
||||
@if(pathList.isEmpty){
|
||||
<div class="pull-right pc">
|
||||
@if(platform != "linux" && platform != null){
|
||||
<a href="@openRepoUrl(repository.httpUrl)" id="repository-clone-url" class="btn btn-sm btn-default"><i class="octicon octicon-desktop-download"></i></a>
|
||||
}
|
||||
<a href="@{url(repository)}/archive/@{encodeRefName(branch)}.zip" class="btn btn-sm btn-default">Download ZIP</a>
|
||||
<div class="pull-right pc">
|
||||
<div class="btn-group">
|
||||
<a href="@url(repository)/find/@encodeRefName(branch)" class="btn btn-sm btn-default pc" data-hotkey="t"><i class="octicon octicon-search"></i></a>
|
||||
<a href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default"><i class="octicon octicon-history"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
@if(pathList.isEmpty){
|
||||
@if(platform != "linux" && platform != null){
|
||||
<div class="pull-right pc" style="margin-right: 5px;">
|
||||
<div class="btn-group">
|
||||
<a href="@openRepoUrl(repository.httpUrl)" id="repository-clone-url" class="btn btn-sm btn-default"><i class="octicon octicon-desktop-download"></i></a>
|
||||
<a href="@{url(repository)}/archive/@{encodeRefName(branch)}.zip" class="btn btn-sm btn-default"><i class="octicon octicon-cloud-download"></i> Download ZIP</a>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="pull-right pc">
|
||||
<div style="width: 240px; margin-top: 2px; margin-right: 5px; margin-left: 5px;">
|
||||
<div style="width: 240px; margin-right: 5px; margin-left: 5px;">
|
||||
@helper.html.copy("repository-url-copy", repository.httpUrl){
|
||||
@if(repository.sshUrl.isDefined){
|
||||
<div class="btn-group input-group-btn">
|
||||
@@ -60,35 +67,29 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="pull-right">
|
||||
<div class="btn-group">
|
||||
<a href="@url(repository)/new/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default pc" title="Create a new file here" @if(!hasWritePermission){disabled}>New file</i></a>
|
||||
<a href="@url(repository)/find/@encodeRefName(branch)" class="btn btn-sm btn-default pc" data-hotkey="t">Find file</a>
|
||||
@if(pathList.nonEmpty){
|
||||
<a href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default" data-toggle="tooltip" data-placement="bottom" title="Browse commits for this branch">History</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@helper.html.branchcontrol(branch, repository, hasWritePermission){
|
||||
@repository.branchList.map { x =>
|
||||
<li><a href="@url(repository)/tree/@encodeRefName(x)">@helper.html.checkicon(x == branch) @x</a></li>
|
||||
}
|
||||
}
|
||||
@if(pathList.isEmpty){
|
||||
@*
|
||||
@branchPullRequest.map{ case (pullRequest, issue) =>
|
||||
<a href="@url(repository)/pull/@pullRequest.issueId" class="btn btn-sm btn-pullrequest-branch" title="@issue.title" data-toggle="tooltip">View #@pullRequest.issueId</a>
|
||||
}.getOrElse {
|
||||
<a href="@url(repository)/compare?head=@urlEncode(encodeRefName(branch))" class="btn btn-sm btn-success" @if(loginAccount.isEmpty){disabled}>New pull request</a>
|
||||
}
|
||||
*@
|
||||
} else {
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
|
||||
@pathList.zipWithIndex.map { case (section, i) =>
|
||||
<a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
|
||||
}
|
||||
}
|
||||
<a href="@url(repository)/new/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-sm btn-default pc" title="Create a new file here" @if(!hasWritePermission){disabled}><i class="octicon octicon-plus"></i></a>
|
||||
</div>
|
||||
<table class="table table-file-list">
|
||||
<table class="table table-hover">
|
||||
@*
|
||||
<tr>
|
||||
<th colspan="4" style="font-weight: normal; border: none;">
|
||||
<a href="@url(repository)/commit/@latestCommit.id" class="commit-message">@link(latestCommit.summary, repository)</a>
|
||||
@@ -98,8 +99,9 @@
|
||||
}
|
||||
</th>
|
||||
</tr>
|
||||
*@
|
||||
<tr>
|
||||
<td colspan="4" class="latest-commit">
|
||||
<th colspan="4" class="latest-commit">
|
||||
<div>
|
||||
<div class="pull-right align-right monospace" style="line-height: 18px;">
|
||||
<a href="@url(repository)/commit/@latestCommit.id" class="commit-id"><span class="muted">latest commit</span> @latestCommit.id.substring(0, 10)</a>
|
||||
@@ -119,7 +121,7 @@
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</th>
|
||||
</tr>
|
||||
@if(pathList.size > 0){
|
||||
<tr>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
@(branch: String,
|
||||
treeId: String,
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
|
||||
groupNames: List[String]
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository, Some(branch), false, groupNames.isEmpty){
|
||||
@html.menu("files", repository, Some(branch)){
|
||||
|
||||
<div>
|
||||
<div class="find-input">
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
@(originRepository: Option[gitbucket.core.service.RepositoryService.RepositoryInfo],
|
||||
members: List[(String, String)],
|
||||
groupNames: List[String],
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("network", repository){
|
||||
<ul class="nav nav-tabs fill-width pull-left">
|
||||
<li class="active"><a href="@url(repository)/network/members">Members</a></li>
|
||||
</ul>
|
||||
<h3>Members of the @repository.name Network</h3>
|
||||
@html.menu("fork", repository){
|
||||
<h1>
|
||||
Forked repositories
|
||||
@if(loginAccount.isEmpty){
|
||||
<a href="@path/signin?redirect=@urlEncode(s"${path}/${repository.owner}/${repository.name}")" class="btn btn-success">Fork</a>
|
||||
} else {
|
||||
@if(groupNames.isEmpty) {
|
||||
<a id="fork-link" href="javascript:void(0);" class="btn btn-success">Fork</a>
|
||||
} else {
|
||||
<a href="@path/@repository.owner/@repository.name/fork" class="btn btn-success" rel="facebox">Fork</a>
|
||||
}
|
||||
}
|
||||
</h1>
|
||||
@if(loginAccount.isDefined && groupNames.isEmpty){
|
||||
<form id="fork-form" method="post" action="@path/@repository.owner/@repository.name/fork" style="display: none;">
|
||||
<input type="hidden" name="account" value="@loginAccount.get.userName"/>
|
||||
</form>
|
||||
}
|
||||
<div class="block">
|
||||
@if(originRepository.isDefined){
|
||||
@avatar(originRepository.get.owner, 20)
|
||||
@@ -33,3 +47,26 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
<script>
|
||||
$(function(){
|
||||
$('a[rel*=facebox]').facebox({
|
||||
'loadingImage': '@assets/vendors/facebox/loading.gif',
|
||||
'closeImage': '@assets/vendors/facebox/closelabel.png'
|
||||
});
|
||||
|
||||
$(document).on("click", ".js-fork-owner-select-target", function() {
|
||||
if (!$(this).hasClass("disabled")) {
|
||||
var account = $(this).text().replace("@@", "");
|
||||
$("#account").val(account);
|
||||
$("#fork").submit();
|
||||
}
|
||||
});
|
||||
|
||||
@if(loginAccount.isDefined){
|
||||
$(document).on("click", "a#fork-link", function(e) {
|
||||
e.preventDefault();
|
||||
$('#fork-form').submit();
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
@html.menu("files", repository){
|
||||
@if(!hasWritePermission){
|
||||
<h3>This is an empty repository</h3>
|
||||
} else {
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
@import context._
|
||||
@import gitbucket.core.view.helpers._
|
||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@html.menu("code", repository){
|
||||
<h1>Tags</h1>
|
||||
@html.menu("tags", repository){
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@(files: List[gitbucket.core.service.RepositorySearchService.FileSearchResult],
|
||||
issueCount: Int,
|
||||
wikiCount: Int,
|
||||
query: String,
|
||||
page: Int,
|
||||
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
|
||||
@@ -7,7 +8,7 @@
|
||||
@import gitbucket.core.view.helpers._
|
||||
@import gitbucket.core.service.RepositorySearchService._
|
||||
@html.main("Search Results", Some(repository)){
|
||||
@menu("code", files.size, issueCount, query, repository){
|
||||
@menu("code", files.size, issueCount, wikiCount, query, repository){
|
||||
@if(files.isEmpty){
|
||||
<h4>We couldn't find any code matching '@query'</h4>
|
||||
} else {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user