Compare commits

..

44 Commits
quill ... 3.13

Author SHA1 Message Date
Naoki Takezoe
5aa6f5bce3 Update GitBucketVersion to 3.13 2016-04-01 02:00:11 +09:00
Naoki Takezoe
9ba098a805 Update README.md 2016-04-01 01:58:46 +09:00
Naoki Takezoe
b2d8567c26 (refs #1152)Fix label and milestone editing 2016-03-31 19:19:06 +09:00
Naoki Takezoe
19d97c93ce Fix layout of wiki editing form and history 2016-03-31 18:10:16 +09:00
Naoki Takezoe
cad2daa2f9 Merge pull request #1150 from distkloc/commit-message-from-updating-branch
Replace embedded variables in commit message Update Branch button generates
2016-03-30 01:26:52 +09:00
distkloc
585f0b5769 Replace embedded variables in commit message Update Branch button generates with their values 2016-03-29 00:47:09 +09:00
Naoki Takezoe
dd23d1109b BugFix for "Show more repositories" link 2016-03-28 08:35:32 +09:00
Naoki Takezoe
5e84221d39 Merge pull request #1156 from gitbucket/update_ui_sidebar
Move the repository menu to the sidebar
2016-03-28 00:50:53 +09:00
Naoki Takezoe
faf3e6c26b Fix dashboard and search layout 2016-03-28 00:34:48 +09:00
Naoki Takezoe
969da2c63b Tweak CSS styles 2016-03-27 22:53:54 +09:00
Naoki Takezoe
ba61891510 Move the repository menu to the sidebar 2016-03-27 19:05:09 +09:00
Naoki Takezoe
a581871a89 Fix Branches, Tags and Milestones presentaion 2016-03-24 20:17:27 +09:00
Naoki Takezoe
d96e1fa503 Move Branches and Tags link to repository navigation tab 2016-03-24 20:08:55 +09:00
Naoki Takezoe
b66812d76c Fix editor styles 2016-03-24 16:53:22 +09:00
Naoki Takezoe
ae32016856 Update dropdown UI 2016-03-24 15:25:44 +09:00
Naoki Takezoe
56aec15e68 Simplify dashboard UI 2016-03-24 14:48:41 +09:00
Naoki Takezoe
d0c8e33ec5 Separate labels and milestones from issues 2016-03-22 15:19:08 +09:00
Naoki Takezoe
7ab260e688 Simplify issue and pull request creation form 2016-03-22 14:22:20 +09:00
Naoki Takezoe
0d2c923664 Fix broken commits link in the repository header 2016-03-22 08:14:31 +09:00
Naoki Takezoe
e7a47fe3a4 Update file browser and commit list UI 2016-03-22 00:33:07 +09:00
Naoki Takezoe
e454f78c5a Merge pull request #1153 from gitbucket/bootstrap3-default-theme
Move to raw Bootstrap3 from GitHub like theme for Bootstrap3
2016-03-20 21:05:26 +09:00
Naoki Takezoe
192e4ade3e Adjust top margin of comment preview tab 2016-03-19 12:27:23 +09:00
Naoki Takezoe
733797cb6f Remove tab icons 2016-03-18 02:07:04 +09:00
Naoki Takezoe
f0e4157a46 Fix issues and pull request title editing form 2016-03-18 00:50:12 +09:00
Naoki Takezoe
2ad6948bb4 Fix styles of diff view 2016-03-17 01:50:24 +09:00
Naoki Takezoe
dd46d649a6 Fix styles of account setting pages 2016-03-17 01:38:32 +09:00
Naoki Takezoe
bbe8a9b9e4 Adjust sidebar of issues 2016-03-16 16:15:31 +09:00
Naoki Takezoe
376b109602 Simplify commit list 2016-03-16 16:07:33 +09:00
Naoki Takezoe
1d085d52bb Small fix for UI styles 2016-03-16 10:36:52 +09:00
Naoki Takezoe
d1f42e0ed7 Move repository status to right of the header 2016-03-16 09:52:49 +09:00
Naoki Takezoe
101f8598ed Change Fork button to tab 2016-03-16 02:37:16 +09:00
Naoki Takezoe
da62f6f8fb Simplify file list table 2016-03-16 02:12:23 +09:00
Naoki Takezoe
5750286b5d Remove unused style definitions 2016-03-15 22:44:06 +09:00
Naoki Takezoe
f2ca4fb64b Adjust styles 2016-03-15 22:31:44 +09:00
Naoki Takezoe
5f6b577cbf Adjust styles 2016-03-15 21:09:00 +09:00
Naoki Takezoe
62004c279c Move to raw Bootstrap3 from GitHub like theme for Bootstrap3 2016-03-15 02:35:48 +09:00
Naoki Takezoe
838c7fb991 Merge pull request #1149 from McFoggy/github-complaint-readme
rephrase README.md in regard of github complaint
2016-03-12 23:59:02 +09:00
Matthieu Brouillard
93786f0fd6 rephrase README.md in regard of github complaint 2016-03-12 14:43:51 +01:00
Naoki Takezoe
f3514e5625 (refs #1146)BugFix for choosing user type in grpup 2016-03-12 04:11:30 +09:00
Naoki Takezoe
a2b0ee0c24 Merge pull request #1145 from xuwei-k/GenBCode
update Scala 2.11.8, use new Java8 Backend
2016-03-10 14:31:11 +09:00
xuwei-k
2bcab30529 update Scala 2.11.8, use new Java8 Backend
- https://github.com/scala/make-release-notes/blob/9cfbdc8c92f94/experimental-backend.md#emitting-java-8-style-lambdas
- http://d.hatena.ne.jp/xuwei/20150626/1435282696
2016-03-10 14:07:48 +09:00
nazoking
91cda6d245 Merge pull request #1144 from distkloc/pull-request-key-in-issues-api
Add pull_request key in list issues api if an issue is a pull request
2016-03-10 12:35:48 +09:00
distkloc
a82e579d57 Add pull_request key in list issues api if an issue is a pull request 2016-03-10 02:08:09 +09:00
Naoki Takezoe
94421c7a63 Fix font size of Wiki sidebar and footer 2016-03-10 02:07:46 +09:00
124 changed files with 6474 additions and 13063 deletions

View File

@@ -1,7 +1,10 @@
GitBucket [![Gitter chat](https://badges.gitter.im/gitbucket/gitbucket.png)](https://gitter.im/gitbucket/gitbucket) [![Build Status](https://travis-ci.org/gitbucket/gitbucket.svg?branch=master)](https://travis-ci.org/gitbucket/gitbucket)
=========
GitBucket is a 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,16 @@ 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
--------
### 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

View File

@@ -1,6 +1,6 @@
val Organization = "gitbucket"
val Name = "gitbucket"
val GitBucketVersion = "3.12.0"
val GitBucketVersion = "3.13.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,
@@ -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")

View File

@@ -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

View File

@@ -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{

View File

@@ -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)

View File

@@ -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
)
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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))
}

View File

@@ -110,7 +110,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)
)
})

View File

@@ -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)

View File

@@ -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.")
}
}
}

View File

@@ -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)

View File

@@ -87,7 +87,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 +148,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 +156,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 +166,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")
@@ -364,7 +364,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.")

View File

@@ -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),

View File

@@ -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")

View File

@@ -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
)

View File

@@ -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
)

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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()
}

View File

@@ -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)) )
}

View File

@@ -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>
}

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -4,56 +4,51 @@
@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 style="float: left; width: 250px;">
<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 style="margin-left: 260px; overflow: hidden;">
<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>
}
@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>
}

View File

@@ -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>
@@ -15,3 +15,6 @@
</li>
</ul>
</div>
<div class="main-content">
@body
</div>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>
}

View File

@@ -1,25 +1,23 @@
@(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>
</ul>
</div>
<div class="main-content">
@body
</div>
</div>

View File

@@ -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">

View File

@@ -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){

View File

@@ -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>

View File

@@ -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>&nbsp;&nbsp;
<a class="button-link@if(condition.state == "closed"){ selected}" href="@condition.copy(state = "closed").toURL">
<i class="octicon octicon-check @(if(condition.state == "closed"){"active"})"></i>
@closedCount Closed
</a>
</span>
<div class="pull-right" id="table-issues-control">
@helper.html.dropdown("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

View File

@@ -4,15 +4,19 @@
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">
@dashboard.html.tab("issues")
<div class="container">
@issuesnavi(filter, "issues", condition)
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
@sidebar(recentRepositories, userRepositories){
<div style="overflow: hidden;">
@dashboard.html.tab("issues")
<div class="container">
@issuesnavi(filter, openCount, closedCount, condition)
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
</div>
</div>
</div>
}
}

View File

@@ -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>&nbsp;&#xFF65;
@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>

View File

@@ -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>

View File

@@ -4,15 +4,19 @@
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">
@dashboard.html.tab("pulls")
<div class="container">
@issuesnavi(filter, "pulls", condition)
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
@sidebar(recentRepositories, userRepositories){
<div style="overflow: hidden;">
@dashboard.html.tab("pulls")
<div class="container">
@issuesnavi(filter, openCount, closedCount, condition)
@issueslist(issues, page, openCount, closedCount, condition, filter, groups)
</div>
</div>
</div>
}
}

View 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>

View File

@@ -1,52 +1,10 @@
@(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>
}
</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 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>
}
</ul>

View File

@@ -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>
}

View File

@@ -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>

View File

@@ -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>
}

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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){

View File

@@ -4,91 +4,21 @@
@import context._
@import gitbucket.core.view.helpers._
@main("GitBucket"){
<div class="body">
@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">&times;</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>
@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">&times;</button>
@Html(information)
</div>
}
<div style="overflow: hidden;">
@dashboard.html.tab()
<div class="container">
<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>
</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>

View File

@@ -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>

View File

@@ -9,30 +9,25 @@
@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="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>

View File

@@ -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(){

View File

@@ -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(){

View File

@@ -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">
@@ -47,11 +47,11 @@
</div>
<hr>
<div class="row" style="margin-top: 15px;">
<div class="col-md-10">
<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>

View File

@@ -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>
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">

View File

@@ -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>&nbsp;&nbsp;
<a class="button-link@if(condition.state == "closed"){ selected}" href="@condition.copy(state = "closed").toURL">
<i class="octicon octicon-check @(if(condition.state == "closed"){"active"})"></i>
@closedCount Closed
</a>
</span>
<div class="pull-right" id="table-issues-control">
@helper.html.dropdown("Author", flat = true) {
<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>&nbsp;&#xFF65;
}
@@ -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>

View File

@@ -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>

View File

@@ -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>&nbsp;&nbsp;
<a class="button-link@if(state == "closed"){ selected}" href="?state=closed">
<i class="octicon octicon-milestone @(if(state == "closed"){"active"})"></i>
@milestones.count(_._1.closedDate.isDefined) Closed
</a>
</span>

View File

@@ -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>

View File

@@ -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,7 +66,7 @@
} 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"/>
}
@@ -92,7 +89,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 +97,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>

View File

@@ -1,17 +1,15 @@
@(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, 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>
@label
@if(count > 0){
<span class="badge">@count</span>
}
@@ -24,29 +22,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 +31,34 @@
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")
@if(repository.commitCount != 0) {
@menuitem("/branches" ,"branches" ,"Branches", repository.branchList.length)
@menuitem("/tags" ,"tags" ,"Tags", repository.tags.length)
}
@menuitem("/issues" ,"issues" ,"Issues", repository.issueCount)
@menuitem("/pulls" ,"pulls" ,"Pull Requests", repository.pullCount)
@menuitem("/issues/labels" ,"labels" ,"Labels")
@menuitem("/issues/milestones" ,"milestones" ,"Milestones")
@menuitem("/wiki" ,"wiki" ,"Wiki")
@menuitem("/network/members", "fork", "Forks", repository.forkedCount)
@if(loginAccount.isDefined && (loginAccount.get.isAdmin || repository.managers.contains(loginAccount.get.userName))){
@menuitem("/settings" , "settings" , "Settings")
}
</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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"}/>
&nbsp;&nbsp;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 />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">

View File

@@ -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>

View File

@@ -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> /

View File

@@ -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=""/>

View File

@@ -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>

View File

@@ -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">

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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>

View File

@@ -3,36 +3,30 @@
@import context._
@import gitbucket.core.view.helpers._
@html.menu("", repository){
<div class="row">
<div class="col-md-3">
<div class="box">
<ul class="nav nav-tabs nav-stacked side-menu">
<li@if(active=="code"){ class="active"}>
<a href="@url(repository)/search?q=@urlEncode(query)&type=code">
@if(fileCount != 0){
<span class="badge pull-right">@fileCount</span>
}
Code
</a>
</li>
<li@if(active=="issue"){ class="active"}>
<a href="@url(repository)/search?q=@urlEncode(query)&type=issue">
@if(issueCount != 0){
<span class="badge pull-right">@issueCount</span>
}
Issue
</a>
</li>
</ul>
</div>
</div>
<div class="col-md-9">
<form action="@url(repository)/search" method="GET" class="form-inline">
<input type="text" name="q" value="@query" class="form-control" style="width: 80%; margin-bottom: 0px;"/>
<input type="submit" value="Search" class="btn btn-default" style="width: 15%;"/>
<input type="hidden" name="type" value="@active"/>
</form>
@body
</div>
</div>
<div style="overflow: hidden;">
<ul class="nav nav-tabs" style="margin-bottom: 20px;">
<li@if(active=="code"){ class="active"}>
<a href="@url(repository)/search?q=@urlEncode(query)&type=code">
Files
@if(fileCount != 0){
<span class="badge">@fileCount</span>
}
</a>
</li>
<li@if(active=="issue"){ class="active"}>
<a href="@url(repository)/search?q=@urlEncode(query)&type=issue">
Issues
@if(issueCount != 0){
<span class="badge">@issueCount</span>
}
</a>
</li>
</ul>
<form action="@url(repository)/search" method="GET" class="form-inline">
<input type="text" name="q" value="@query" class="form-control" style="width: 400px; margin-bottom: 0px;"/>
<input type="submit" value="Search" class="btn btn-default"/>
<input type="hidden" name="type" value="@active"/>
</form>
@body
</div>
}

View File

@@ -57,7 +57,7 @@
</div>
</div>
</div>
<input class="btn btn-success btn-lg" type="submit" value="Save changes" />
<input class="btn btn-success" type="submit" value="Save changes" />
</div>
</div>
</form>

View File

@@ -9,7 +9,6 @@
@helper.html.information(info)
<div class="panel panel-default">
<div class="panel-heading strong">
<a href="@url(repository)/settings/hooks/new" class="btn btn-success btn-sm pull-right">Add webhook</a>
Webhooks
</div>
<div class="panel-body">
@@ -18,8 +17,9 @@
When the specified events happen, well send a POST request to each of the URLs you provide.
Learn more in <a href="https://github.com/takezoe/gitbucket/wiki/API-WebHook" target="_blank">GitBucket Wiki Webhook Page</a>.
</p>
<a href="@url(repository)/settings/hooks/new" class="btn btn-success pull-right" style="margin-bottom: 10px;">Add webhook</a>
<table class="table table-condensed" style="margin-bottom:0">
<table class="table table-condensed" style="margin-bottom:0px;">
@webHooks.map { case (webHook, events) =>
<tr><td style="vertical-align: middle;">
<a href="@url(repository)/settings/hooks/edit?url=@urlEncode(webHook.url)" class="css-truncate" style="max-width:360px">

View File

@@ -1,31 +1,25 @@
@(active: String, repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(body: Html)(implicit context: gitbucket.core.controller.Context)
@import context._
@import gitbucket.core.view.helpers._
<div class="row">
<div class="col-md-3">
<div class="box">
<ul class="nav nav-tabs nav-stacked side-menu">
<li@if(active=="options"){ class="active"}>
<a href="@url(repository)/settings/options">Options</a>
</li>
<li@if(active=="collaborators"){ class="active"}>
<a href="@url(repository)/settings/collaborators">Collaborators</a>
</li>
@if(!repository.branchList.isEmpty){
<li@if(active=="branches"){ class="active"}>
<a href="@url(repository)/settings/branches">Branches</a>
</li>
}
<li@if(active=="hooks"){ class="active"}>
<a href="@url(repository)/settings/hooks">Service Hooks</a>
</li>
<li@if(active=="danger"){ class="active"}>
<a href="@url(repository)/settings/danger">Danger Zone</a>
</li>
</ul>
</div>
</div>
<div class="col-md-9">
@body
</div>
</div>
<div style="overflow: hidden;">
<ul class="nav nav-tabs" style="margin-bottom: 20px;">
<li@if(active=="options"){ class="active"}>
<a href="@url(repository)/settings/options">Options</a>
</li>
<li@if(active=="collaborators"){ class="active"}>
<a href="@url(repository)/settings/collaborators">Collaborators</a>
</li>
@if(!repository.branchList.isEmpty){
<li@if(active=="branches"){ class="active"}>
<a href="@url(repository)/settings/branches">Branches</a>
</li>
}
<li@if(active=="hooks"){ class="active"}>
<a href="@url(repository)/settings/hooks">Service Hooks</a>
</li>
<li@if(active=="danger"){ class="active"}>
<a href="@url(repository)/settings/danger">Danger Zone</a>
</li>
</ul>
@body
</div>

View File

@@ -21,7 +21,7 @@
<fieldset class="margin">
<label class="radio">
<input type="radio" name="isPrivate" value="false"
@if(!repository.repository.`private` ){ checked }
@if(!repository.repository.isPrivate ){ checked }
@if(repository.repository.parentUserName.isDefined){ disabled }
>
<span class="strong"><i class="octicon octicon-repo"></i> Public</span><br>
@@ -31,7 +31,7 @@
</label>
<label class="radio">
<input type="radio" name="isPrivate" value="true"
@if(repository.repository.`private` ){ checked }
@if(repository.repository.isPrivate ){ checked }
@if(repository.repository.parentUserName.isDefined){ disabled }
>
<span class="strong"><i class="octicon octicon-lock"></i> Private</span><br>

View File

@@ -5,45 +5,41 @@
@import gitbucket.core.view.helpers._
@html.main(s"${if(pageName.isEmpty) "New Page" else pageName} - ${repository.owner}/${repository.name}", Some(repository)){
@html.menu("wiki", repository){
<ul class="nav nav-tabs fill-width">
<li>
<h1 class="wiki-title"><span class="muted">Editing</span> @if(pageName.isEmpty){New Page} else {@pageName}</h1>
</li>
<li class="pull-right">
<div>
@if(page.isDefined){
<a class="btn btn-small btn-default" href="@url(repository)/wiki/@urlEncode(pageName)/_delete" id="delete">Delete Page</a>
<a class="btn btn-small btn-default" href="@url(repository)/wiki/@urlEncode(pageName)/_history">Page History</a>
}
<a class="btn btn-small btn-success" href="@url(repository)/wiki/_new">New Page</a>
<div class="pull-right">
@if(page.isDefined){
<a class="btn btn-small btn-default" href="@url(repository)/wiki/@urlEncode(pageName)/_history">Page History</a>
<a class="btn btn-small btn-danger" href="@url(repository)/wiki/@urlEncode(pageName)/_delete" id="delete">Delete Page</a>
}
<a class="btn btn-small btn-success" href="@url(repository)/wiki/_new">New Page</a>
</div>
<h1><span class="muted">Editing</span> @if(pageName.isEmpty){New Page} else {@pageName}</h1>
<div style="overflow: hidden;">
<form action="@url(repository)/wiki/@if(page.isEmpty){_new} else {_edit}" method="POST" validate="true">
<span id="error-pageName" class="error"></span>
<input type="text" name="pageName" value="@pageName" class="form-control" style="font-weight: bold; margin-bottom: 10px;" placeholder="Input a page name."/>
@helper.html.preview(
repository = repository,
content = page.map(_.content).getOrElse(""),
enableWikiLink = true,
enableRefsLink = false,
enableLineBreaks = false,
enableTaskList = false,
hasWritePermission = false,
style = "height: 400px;",
styleClass = "monospace",
placeholder = ""
)
<div class="form-group">
<label for="message">Edit Message</label>
<input type="text" id="message" name="message" value="" class="form-control" placeholder="Write a small message here explaining this change. (Optional)"/>
</div>
</li>
</ul>
<form action="@url(repository)/wiki/@if(page.isEmpty){_new} else {_edit}" method="POST" validate="true">
<span id="error-pageName" class="error"></span>
<input type="text" name="pageName" value="@pageName" class="form-control input-lg" style="font-weight: bold;" placeholder="Input a page name."/>
@helper.html.preview(
repository = repository,
content = page.map(_.content).getOrElse(""),
enableWikiLink = true,
enableRefsLink = false,
enableLineBreaks = false,
enableTaskList = false,
hasWritePermission = false,
style = "height: 400px;",
styleClass = "monospace",
placeholder = ""
)
<div class="form-group">
<label for="message">Edit Message</label>
<input type="text" id="message" name="message" value="" class="form-control" placeholder="Write a small message here explaining this change. (Optional)"/>
</div>
<div class="form-group pull-right">
<input type="hidden" name="currentPageName" value="@pageName"/>
<input type="hidden" name="id" value="@page.map(_.id)"/>
<input type="submit" value="Save" class="btn btn-success btn-lg">
</div>
</form>
<div class="form-group pull-right">
<input type="hidden" name="currentPageName" value="@pageName"/>
<input type="hidden" name="id" value="@page.map(_.id)"/>
<input type="submit" value="Save" class="btn btn-success">
</div>
</form>
</div>
}
}
<script>

View File

@@ -5,31 +5,25 @@
@import gitbucket.core.view.helpers._
@html.main(s"History - ${repository.owner}/${repository.name}", Some(repository)){
@html.menu("wiki", repository){
<ul class="nav nav-tabs fill-width">
<li>
<h1 class="wiki-title">
@if(pageName.isEmpty){
<span class="muted">History</span>
} else {
<span class="muted">History for</span> @pageName.get
}
</h1>
</li>
<li class="pull-right">
<div>
@if(pageName.isEmpty){
@if(loginAccount.isDefined){
<a class="btn btn-small" href="@url(repository)/wiki/_new">New Page</a>
}
} else {
@if(loginAccount.isDefined){
<a class="btn btn-small btn-default" href="@url(repository)/wiki/@urlEncode(pageName)/_edit">Edit Page</a>
<a class="btn btn-small btn-success" href="@url(repository)/wiki/_new">New Page</a>
}
}
</div>
</li>
</ul>
<div class="pull-right">
@if(pageName.isEmpty){
@if(loginAccount.isDefined){
<a class="btn btn-small" href="@url(repository)/wiki/_new">New Page</a>
}
} else {
@if(loginAccount.isDefined){
<a class="btn btn-small btn-default" href="@url(repository)/wiki/@urlEncode(pageName)/_edit">Edit Page</a>
<a class="btn btn-small btn-success" href="@url(repository)/wiki/_new">New Page</a>
}
}
</div>
<h1 class="wiki-title">
@if(pageName.isEmpty){
<span class="muted">History</span>
} else {
<span class="muted">History for</span> @pageName.get
}
</h1>
<table class="table table-bordered fill-width pull-left">
<thead>
<tr>

View File

@@ -10,23 +10,20 @@
@import gitbucket.core.service.WikiService.{wikiHttpUrl, wikiSshUrl}
@html.main(s"${pageName} - ${repository.owner}/${repository.name}", Some(repository)){
@html.menu("wiki", repository){
<ul class="nav nav-tabs fill-width">
<li>
<h1 class="wiki-title">@pageName</h1>
<div class="small">
<span class="muted"><strong>@page.committer</strong> edited this page @helper.html.datetimeago(page.time)</span>
<div>
@if(hasWritePermission){
<div class="pull-right">
<a class="btn btn-default" href="@url(repository)/wiki/@urlEncode(pageName)/_edit">Edit Page</a>
<a class="btn btn-success" href="@url(repository)/wiki/_new">New Page</a>
</div>
</li>
<li class="pull-right">
@if(hasWritePermission){
<div>
<a class="btn btn-small btn-default" href="@url(repository)/wiki/@urlEncode(pageName)/_edit">Edit Page</a>
<a class="btn btn-small btn-success" href="@url(repository)/wiki/_new">New Page</a>
</div>
}
</li>
</ul>
<div style="width: 200px; margin-top: 20px;" class="pull-right pc">
}
<h1 class="wiki-title">@pageName</h1>
<div>
<span class="muted"><strong>@page.committer</strong> edited this page @helper.html.datetimeago(page.time)</span>
</div>
<hr>
</div>
<div style="width: 250px; margin-top: 20px;" class="pull-right pc">
@defining(15){ max =>
<div class="panel panel-default">
<div class="panel-heading strong">
@@ -64,7 +61,7 @@
</a>
}
}
<div class="small">
<div>
<strong>Clone this wiki locally</strong>
</div>
@helper.html.copy("repository-url-copy", wikiHttpUrl(repository)){
@@ -76,7 +73,7 @@
</div>
}
</div>
<div style="width: 680px;" class="pull-left">
<div style="margin-right: 260px;">
<div class="markdown-body">
@markdown(
markdown = page.content,

View File

@@ -37,29 +37,29 @@ h6 {
}
.octicon,.mega-octicon{
color : #999;
width: 14px;
height: 14px;
/*font-size: 14px;*/
text-align: center;
color : #999;
width: 14px;
height: 14px;
/*font-size: 14px;*/
text-align: center;
}
.mega-octicon{
width: 32px;
height: 32px;
width: 32px;
height: 32px;
}
.octicon.active,.mega-octicon.active{
color : #333;
color : #333;
}
.octicon-cloud-download{
color: #333;
margin-right: 5px;
color: #333;
margin-right: 5px;
}
.head .octicon, .head .mega-octicon{
color : #BBB;
color : #BBB;
}
.align-right {
@@ -113,6 +113,11 @@ pre.reset {
border-radius: initial;
}
label.radio, label.checkbox {
position: relative;
left: 20px;
}
/* ======================================================================== */
/* Global Header */
/* ======================================================================== */
@@ -130,12 +135,14 @@ span.header-version {
font-size: small;
}
/*
div.input-group>span.fork {
display: table;
margin-left: 4px;
margin-top: 1px;
}
*/
/*
div.input-group>span.fork>span.count {
background-color: white;
-webkit-border-radius: 0 4px 4px 0;
@@ -144,10 +151,10 @@ div.input-group>span.fork>span.count {
border: 1px solid #d8d8d8;
border-left: none;
font-size: 11px;
/*height: 24px;*/
padding: 3px 6px;
display: table-cell;
}
*/
a.global-header-menu {
color: black;
@@ -168,13 +175,18 @@ div.head {
}
div.head div.forked {
font-size: 60%;
font-size: 65%;
margin-left: 36px;
color: #999999;
}
div.container {
/*
width: 980px;
*/
width: 100%;
padding-left: 10px;
padding-right: 10px;
}
div.container-wide {
@@ -182,12 +194,28 @@ div.container-wide {
padding-right: 10px;
}
/*
div.pagination {
margin-top: 0px;
margin-bottom: 0px;
div.main-sidebar {
width: 250px;
float: left;
}
div.main-content {
margin-left: 260px;
}
div.main-center {
width: 980px;
margin: 0 auto;
}
div.dashboard-sidebar {
width: 300px;
float: left;
}
div.dashboard-content {
margin-left: 310px;
}
*/
div.body {
margin-top: 20px;
@@ -199,7 +227,7 @@ span.error {
}
.small {
font-size: 80%;
font-size: 90%;
}
.strong {
@@ -240,17 +268,19 @@ div.show-more a {
color: #7aa1d3;
}
/*
span.count-right {
float: right;
font-weight: bold;
}
*/
.monospace {
font-family: Consolas, 'Courier New', Courier, Monaco, monospace;
}
table.table th {
background-color: #d9edf7;
background-color: #f5f5f5;
}
div.block-header {
@@ -286,22 +316,6 @@ div.box-header {
margin-bottom: 0px;
}
.commit-list {
position: relative;
}
.commit-list::before {
position: absolute;
top: 0;
bottom: 0;
left: 6px;
z-index: -1;
display: block;
width: 2px;
content: "";
background-color: #f3f3f3;
}
ul.nav-pills .link {
color: #4078c0;
}
@@ -311,15 +325,6 @@ ul.nav-pills i.link {
height: 0;
}
div.box-commits {
background-color: white;
padding-left: 0px;
padding-right: 0px;
margin-top: 10px;
margin-bottom: 10px;
margin-left: 20px;
}
div.box-content {
background-color: white;
border: 1px solid #d8d8d8;
@@ -389,7 +394,6 @@ ul.dropdown-menu {
ul.dropdown-menu li {
border-bottom: 1px solid #eee;
font-size: 85%;
}
ul.dropdown-menu li a {
@@ -440,53 +444,10 @@ a.btn-danger:hover .octicon {
/* Head Menu */
/****************************************************************************/
div.headbar {
background-color: #fafafa;
padding-top: 19px;
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
ul.headmenu {
margin-top: 20px;
margin-left: 0px;
padding-left: 0px;
margin-bottom: -1px;
}
ul.headmenu a:hover {
text-decoration: none;
color: black;
}
ul.headmenu li {
display: inline-block;
list-style-type: none;
font-size: 13px;
}
ul.headmenu li a {
color: #666;
padding: 8px 10px;
display: block;
}
ul.headmenu li.active a {
color: black;
}
ul.headmenu li.active {
border-top: 3px solid #bb4444;
border-left: 1px solid #eee;
border-right: 1px solid #eee;
border-bottom: none;
background-color: white;
}
ul.headmenu li.active a {
background-color: #fff;
}
/****************************************************************************/
/* Create Repository */
/****************************************************************************/
@@ -518,59 +479,6 @@ div.signin-form {
font-weight: bold;
}
/****************************************************************************/
/* Stacked side menu */
/****************************************************************************/
ul.nav-stacked.side-menu {
margin-bottom: 0px;
}
ul.nav-stacked.side-menu li {
background-color: #f9f9f9;
margin-bottom: -3px;
}
a#show-pages-index {
color: #333;
text-decoration: none;
}
ul.nav-stacked.side-menu li a:hover {
background-color: transparent;
}
ul.nav-stacked.side-menu li.active a {
background-color: white;
color: #333;
font-weight: bold;
border-left: 2px solid #cc6666;
}
ul.nav-stacked.side-menu li.active a:hover {
background-color: white;
color: #4183c4;
font-weight: bold;
border-left: 2px solid #cc6666;
}
.nav-tabs.nav-stacked.side-menu > li > a {
border: 1px solid #d8d8d8;
border-radius: 0px;
background-color: white;
color: #4183c4;
padding: 8px 15px 9px;
}
.nav-tabs.nav-stacked.side-menu > li:first-child > a {
border-top-right-radius: 3px;
border-top-left-radius: 3px;
}
.nav-tabs.nav-stacked.side-menu > li:last-child > a {
border-bottom-right-radius: 3px;
border-bottom-left-radius: 3px;
}
/****************************************************************************/
/* Repositories */
/****************************************************************************/
@@ -610,7 +518,7 @@ div.repository-content {
display: inline-block;
float: right;
position: relative;
top: -3px;
top: -6px;
height: 20px;
}
.branches .branch-a-b-count{
@@ -640,6 +548,7 @@ div.repository-content {
/****************************************************************************/
/* Activity */
/****************************************************************************/
/*
div.activity-icon-large {
position: absolute;
padding-top: 8px;
@@ -653,7 +562,7 @@ div.activity-icon-small {
div.activity-content {
margin-left: 40px;
}
*/
div[class^="activity-icon"] i{
color: #BBB;
}
@@ -703,7 +612,7 @@ table.blobview {
table-layout: fixed;
}
*/
/*
table.table-file-list {
margin-bottom: 0px;
border: 1px solid #ddd;
@@ -715,6 +624,7 @@ table.table-file-list th, table.table-file-list td {
padding-top: 8px;
padding-bottom: 8px;
}
*/
table.table-file-list td.latest-commit {
padding-top: 4px;
@@ -726,7 +636,7 @@ table.table-file-list td {
background-color: #F8F8F8;
font-size: small;
}
/*
table.table-file-list .file-icon {
padding-right: 1px;
}
@@ -754,7 +664,7 @@ th, td, .table th, .table td {
padding-bottom: 4px;
line-height: 100%;
}
*/
div.commit-avatar-image {
float: left;
margin-right: 4px;
@@ -997,11 +907,6 @@ div.issue-avatar-image {
float: left;
}
div.issue-box {
margin-bottom: 5px;
margin-left: 50px;
}
div.issue-participants {
margin-bottom: 15px;
margin-left: 50px;
@@ -1010,14 +915,10 @@ div.issue-participants {
div.issue-comment-box, div.commit-comment-box {
margin-bottom: 15px;
margin-left: 70px;
max-width: 820px;
/*max-width: 820px;*/
/*padding: 8px;*/
}
div.issue-box > div.panel-body {
padding: 4px;
}
div.issue-comment-box > div.panel-body,
div.commit-comment-box > div.panel-body {
padding: 8px;
@@ -1211,7 +1112,7 @@ div.wiki-sidebar-dotted {
margin-bottom: 20px;
margin-top: 20px;
border-radius: 3px;
font-size: 16px;
font-size: 14px;
}
div.wiki-footer {

View File

@@ -0,0 +1,587 @@
/*!
* Bootstrap v3.3.6 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
text-shadow: 0 1px 0 #fff;
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #265a88;
border-color: #245580;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
background-color: #419641;
background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-color: #e8e8e8;
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-color: #2e6da4;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 767px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
background-repeat: repeat-x;
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
background-repeat: repeat-x;
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
background-repeat: repeat-x;
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
background-repeat: repeat-x;
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
background-repeat: repeat-x;
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
background-repeat: repeat-x;
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
background-repeat: repeat-x;
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
background-repeat: repeat-x;
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
background-repeat: repeat-x;
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
}
/*# sourceMappingURL=bootstrap-theme.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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