mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-07 05:55:51 +01:00
Merge branch 'master' into add-features-to-ldapauth
This commit is contained in:
1
src/main/resources/update/1_12.sql
Normal file
1
src/main/resources/update/1_12.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE GROUP_MEMBER ADD COLUMN MANAGER BOOLEAN DEFAULT FALSE;
|
||||||
@@ -20,7 +20,6 @@ class ScalatraBootstrap extends LifeCycle {
|
|||||||
context.mount(new DashboardController, "/*")
|
context.mount(new DashboardController, "/*")
|
||||||
context.mount(new UserManagementController, "/*")
|
context.mount(new UserManagementController, "/*")
|
||||||
context.mount(new SystemSettingsController, "/*")
|
context.mount(new SystemSettingsController, "/*")
|
||||||
context.mount(new CreateRepositoryController, "/*")
|
|
||||||
context.mount(new AccountController, "/*")
|
context.mount(new AccountController, "/*")
|
||||||
context.mount(new RepositoryViewerController, "/*")
|
context.mount(new RepositoryViewerController, "/*")
|
||||||
context.mount(new WikiController, "/*")
|
context.mount(new WikiController, "/*")
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import service._
|
import service._
|
||||||
import util.{FileUtil, OneselfAuthenticator}
|
import util._
|
||||||
import util.StringUtil._
|
import util.StringUtil._
|
||||||
import util.Directory._
|
import util.Directory._
|
||||||
|
import util.ControlUtil._
|
||||||
import jp.sf.amateras.scalatra.forms._
|
import jp.sf.amateras.scalatra.forms._
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
|
import org.scalatra.i18n.Messages
|
||||||
|
import org.eclipse.jgit.api.Git
|
||||||
|
import org.eclipse.jgit.lib.{FileMode, Constants}
|
||||||
|
import org.eclipse.jgit.dircache.DirCache
|
||||||
|
import model.GroupMember
|
||||||
|
|
||||||
class AccountController extends AccountControllerBase
|
class AccountController extends AccountControllerBase
|
||||||
with AccountService with RepositoryService with ActivityService with OneselfAuthenticator
|
with AccountService with RepositoryService with ActivityService with WikiService with LabelsService
|
||||||
|
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
|
||||||
|
|
||||||
trait AccountControllerBase extends AccountManagementControllerBase {
|
trait AccountControllerBase extends AccountManagementControllerBase {
|
||||||
self: AccountService with RepositoryService with ActivityService with OneselfAuthenticator =>
|
self: AccountService with RepositoryService with ActivityService with WikiService with LabelsService
|
||||||
|
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator =>
|
||||||
|
|
||||||
case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String,
|
case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String,
|
||||||
url: Option[String], fileId: Option[String])
|
url: Option[String], fileId: Option[String])
|
||||||
@@ -37,6 +45,40 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
"clearImage" -> trim(label("Clear image" , boolean()))
|
"clearImage" -> trim(label("Clear image" , boolean()))
|
||||||
)(AccountEditForm.apply)
|
)(AccountEditForm.apply)
|
||||||
|
|
||||||
|
case class NewGroupForm(groupName: String, url: Option[String], fileId: Option[String], members: String)
|
||||||
|
case class EditGroupForm(groupName: String, url: Option[String], fileId: Option[String], members: String, clearImage: Boolean)
|
||||||
|
|
||||||
|
val newGroupForm = mapping(
|
||||||
|
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier, uniqueUserName))),
|
||||||
|
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||||
|
"fileId" -> trim(label("File ID" ,optional(text()))),
|
||||||
|
"members" -> trim(label("Members" ,text(required, members)))
|
||||||
|
)(NewGroupForm.apply)
|
||||||
|
|
||||||
|
val editGroupForm = mapping(
|
||||||
|
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier))),
|
||||||
|
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||||
|
"fileId" -> trim(label("File ID" ,optional(text()))),
|
||||||
|
"members" -> trim(label("Members" ,text(required, members))),
|
||||||
|
"clearImage" -> trim(label("Clear image" ,boolean()))
|
||||||
|
)(EditGroupForm.apply)
|
||||||
|
|
||||||
|
case class RepositoryCreationForm(owner: String, name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean)
|
||||||
|
case class ForkRepositoryForm(owner: String, name: String)
|
||||||
|
|
||||||
|
val newRepositoryForm = mapping(
|
||||||
|
"owner" -> trim(label("Owner" , text(required, maxlength(40), identifier, existsAccount))),
|
||||||
|
"name" -> trim(label("Repository name", text(required, maxlength(40), identifier, uniqueRepository))),
|
||||||
|
"description" -> trim(label("Description" , optional(text()))),
|
||||||
|
"isPrivate" -> trim(label("Repository Type", boolean())),
|
||||||
|
"createReadme" -> trim(label("Create README" , boolean()))
|
||||||
|
)(RepositoryCreationForm.apply)
|
||||||
|
|
||||||
|
val forkRepositoryForm = mapping(
|
||||||
|
"owner" -> trim(label("Repository owner", text(required))),
|
||||||
|
"name" -> trim(label("Repository name", text(required)))
|
||||||
|
)(ForkRepositoryForm.apply)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays user information.
|
* Displays user information.
|
||||||
*/
|
*/
|
||||||
@@ -51,14 +93,20 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
getActivitiesByUser(userName, true))
|
getActivitiesByUser(userName, true))
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
case "members" if(account.isGroupAccount) =>
|
case "members" if(account.isGroupAccount) => {
|
||||||
_root_.account.html.members(account, getGroupMembers(account.userName))
|
val members = getGroupMembers(account.userName)
|
||||||
|
_root_.account.html.members(account, members.map(_.userName),
|
||||||
|
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager }))
|
||||||
|
}
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
case _ =>
|
case _ => {
|
||||||
|
val members = getGroupMembers(account.userName)
|
||||||
_root_.account.html.repositories(account,
|
_root_.account.html.repositories(account,
|
||||||
if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
if(account.isGroupAccount) Nil else getGroupsByUserName(userName),
|
||||||
getVisibleRepositories(context.loginAccount, baseUrl, Some(userName)))
|
getVisibleRepositories(context.loginAccount, baseUrl, Some(userName)),
|
||||||
|
context.loginAccount.exists(x => members.exists { member => member.userName == x.userName && member.isManager }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} getOrElse NotFound
|
} getOrElse NotFound
|
||||||
}
|
}
|
||||||
@@ -134,4 +182,228 @@ trait AccountControllerBase extends AccountManagementControllerBase {
|
|||||||
} else NotFound
|
} else NotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get("/groups/new")(usersOnly {
|
||||||
|
account.html.group(None, List(GroupMember("", context.loginAccount.get.userName, true)))
|
||||||
|
})
|
||||||
|
|
||||||
|
post("/groups/new", newGroupForm)(usersOnly { form =>
|
||||||
|
createGroup(form.groupName, form.url)
|
||||||
|
updateGroupMembers(form.groupName, form.members.split(",").map {
|
||||||
|
_.split(":") match {
|
||||||
|
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||||
|
}
|
||||||
|
}.toList)
|
||||||
|
updateImage(form.groupName, form.fileId, false)
|
||||||
|
redirect(s"/${form.groupName}")
|
||||||
|
})
|
||||||
|
|
||||||
|
get("/:groupName/_editgroup")(managersOnly {
|
||||||
|
defining(params("groupName")){ groupName =>
|
||||||
|
account.html.group(getAccountByUserName(groupName, true), getGroupMembers(groupName))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
get("/:groupName/_deletegroup")(managersOnly {
|
||||||
|
defining(params("groupName")){ groupName =>
|
||||||
|
// Remove from GROUP_MEMBER
|
||||||
|
updateGroupMembers(groupName, Nil)
|
||||||
|
// Remove repositories
|
||||||
|
getRepositoryNamesOfUser(groupName).foreach { repositoryName =>
|
||||||
|
deleteRepository(groupName, repositoryName)
|
||||||
|
FileUtils.deleteDirectory(getRepositoryDir(groupName, repositoryName))
|
||||||
|
FileUtils.deleteDirectory(getWikiRepositoryDir(groupName, repositoryName))
|
||||||
|
FileUtils.deleteDirectory(getTemporaryDir(groupName, repositoryName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
redirect("/")
|
||||||
|
})
|
||||||
|
|
||||||
|
post("/:groupName/_editgroup", editGroupForm)(managersOnly { form =>
|
||||||
|
defining(params("groupName"), form.members.split(",").map {
|
||||||
|
_.split(":") match {
|
||||||
|
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||||
|
}
|
||||||
|
}.toList){ case (groupName, members) =>
|
||||||
|
getAccountByUserName(groupName, true).map { account =>
|
||||||
|
updateGroup(groupName, form.url, false)
|
||||||
|
|
||||||
|
// Update GROUP_MEMBER
|
||||||
|
updateGroupMembers(form.groupName, members)
|
||||||
|
// Update COLLABORATOR for group repositories
|
||||||
|
getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
||||||
|
removeCollaborators(form.groupName, repositoryName)
|
||||||
|
members.foreach { case (userName, isManager) =>
|
||||||
|
addCollaborator(form.groupName, repositoryName, userName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateImage(form.groupName, form.fileId, form.clearImage)
|
||||||
|
redirect(s"/${form.groupName}")
|
||||||
|
|
||||||
|
} getOrElse NotFound
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the new repository form.
|
||||||
|
*/
|
||||||
|
get("/new")(usersOnly {
|
||||||
|
account.html.newrepo(getGroupsByUserName(context.loginAccount.get.userName))
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new repository.
|
||||||
|
*/
|
||||||
|
post("/new", newRepositoryForm)(usersOnly { form =>
|
||||||
|
LockUtil.lock(s"${form.owner}/${form.name}/create"){
|
||||||
|
if(getRepository(form.owner, form.name, baseUrl).isEmpty){
|
||||||
|
val ownerAccount = getAccountByUserName(form.owner).get
|
||||||
|
val loginAccount = context.loginAccount.get
|
||||||
|
val loginUserName = loginAccount.userName
|
||||||
|
|
||||||
|
// Insert to the database at first
|
||||||
|
createRepository(form.name, form.owner, form.description, form.isPrivate)
|
||||||
|
|
||||||
|
// Add collaborators for group repository
|
||||||
|
if(ownerAccount.isGroupAccount){
|
||||||
|
getGroupMembers(form.owner).foreach { member =>
|
||||||
|
addCollaborator(form.owner, form.name, member.userName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert default labels
|
||||||
|
insertDefaultLabels(form.owner, form.name)
|
||||||
|
|
||||||
|
// Create the actual repository
|
||||||
|
val gitdir = getRepositoryDir(form.owner, form.name)
|
||||||
|
JGitUtil.initRepository(gitdir)
|
||||||
|
|
||||||
|
if(form.createReadme){
|
||||||
|
using(Git.open(gitdir)){ git =>
|
||||||
|
val builder = DirCache.newInCore.builder()
|
||||||
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
|
val content = if(form.description.nonEmpty){
|
||||||
|
form.name + "\n" +
|
||||||
|
"===============\n" +
|
||||||
|
"\n" +
|
||||||
|
form.description.get
|
||||||
|
} else {
|
||||||
|
form.name + "\n" +
|
||||||
|
"===============\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.add(JGitUtil.createDirCacheEntry("README.md", FileMode.REGULAR_FILE,
|
||||||
|
inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
|
||||||
|
builder.finish()
|
||||||
|
|
||||||
|
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter),
|
||||||
|
loginAccount.fullName, loginAccount.mailAddress, "Initial commit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Wiki repository
|
||||||
|
createWikiRepository(loginAccount, form.owner, form.name)
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
recordCreateRepositoryActivity(form.owner, form.name, loginUserName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirect to the repository
|
||||||
|
redirect(s"/${form.owner}/${form.name}")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
get("/:owner/:repository/fork")(readableUsersOnly { repository =>
|
||||||
|
val loginAccount = context.loginAccount.get
|
||||||
|
val loginUserName = loginAccount.userName
|
||||||
|
|
||||||
|
LockUtil.lock(s"${loginUserName}/${repository.name}/create"){
|
||||||
|
if(repository.owner == loginUserName){
|
||||||
|
// redirect to the repository
|
||||||
|
redirect(s"/${repository.owner}/${repository.name}")
|
||||||
|
} else {
|
||||||
|
getForkedRepositories(repository.owner, repository.name).find(_._1 == loginUserName).map { case (owner, name) =>
|
||||||
|
// redirect to the repository
|
||||||
|
redirect(s"/${owner}/${name}")
|
||||||
|
} getOrElse {
|
||||||
|
// Insert to the database at first
|
||||||
|
val originUserName = repository.repository.originUserName.getOrElse(repository.owner)
|
||||||
|
val originRepositoryName = repository.repository.originRepositoryName.getOrElse(repository.name)
|
||||||
|
|
||||||
|
createRepository(
|
||||||
|
repositoryName = repository.name,
|
||||||
|
userName = loginUserName,
|
||||||
|
description = repository.repository.description,
|
||||||
|
isPrivate = repository.repository.isPrivate,
|
||||||
|
originRepositoryName = Some(originRepositoryName),
|
||||||
|
originUserName = Some(originUserName),
|
||||||
|
parentRepositoryName = Some(repository.name),
|
||||||
|
parentUserName = Some(repository.owner)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Insert default labels
|
||||||
|
insertDefaultLabels(loginUserName, repository.name)
|
||||||
|
|
||||||
|
// clone repository actually
|
||||||
|
JGitUtil.cloneRepository(
|
||||||
|
getRepositoryDir(repository.owner, repository.name),
|
||||||
|
getRepositoryDir(loginUserName, repository.name))
|
||||||
|
|
||||||
|
// Create Wiki repository
|
||||||
|
JGitUtil.cloneRepository(
|
||||||
|
getWikiRepositoryDir(repository.owner, repository.name),
|
||||||
|
getWikiRepositoryDir(loginUserName, repository.name))
|
||||||
|
|
||||||
|
// insert commit id
|
||||||
|
using(Git.open(getRepositoryDir(loginUserName, repository.name))){ git =>
|
||||||
|
JGitUtil.getRepositoryInfo(loginUserName, repository.name, baseUrl).branchList.foreach { branch =>
|
||||||
|
JGitUtil.getCommitLog(git, branch) match {
|
||||||
|
case Right((commits, _)) => commits.foreach { commit =>
|
||||||
|
if(!existsCommitId(loginUserName, repository.name, commit.id)){
|
||||||
|
insertCommitId(loginUserName, repository.name, commit.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Left(_) => ???
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
recordForkActivity(repository.owner, repository.name, loginUserName)
|
||||||
|
// redirect to the repository
|
||||||
|
redirect(s"/${loginUserName}/${repository.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
private def insertDefaultLabels(userName: String, repositoryName: String): Unit = {
|
||||||
|
createLabel(userName, repositoryName, "bug", "fc2929")
|
||||||
|
createLabel(userName, repositoryName, "duplicate", "cccccc")
|
||||||
|
createLabel(userName, repositoryName, "enhancement", "84b6eb")
|
||||||
|
createLabel(userName, repositoryName, "invalid", "e6e6e6")
|
||||||
|
createLabel(userName, repositoryName, "question", "cc317c")
|
||||||
|
createLabel(userName, repositoryName, "wontfix", "ffffff")
|
||||||
|
}
|
||||||
|
|
||||||
|
private def existsAccount: Constraint = new Constraint(){
|
||||||
|
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
||||||
|
if(getAccountByUserName(value).isEmpty) Some("User or group does not exist.") else None
|
||||||
|
}
|
||||||
|
|
||||||
|
private def uniqueRepository: Constraint = new Constraint(){
|
||||||
|
override def validate(name: String, value: String, params: Map[String, String], messages: Messages): Option[String] =
|
||||||
|
params.get("owner").flatMap { userName =>
|
||||||
|
getRepositoryNamesOfUser(userName).find(_ == value).map(_ => "Repository already exists.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def members: Constraint = new Constraint(){
|
||||||
|
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||||
|
if(value.split(",").exists {
|
||||||
|
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
||||||
|
}) None else Some("Must select one manager at least.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,199 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import util.Directory._
|
|
||||||
import util.ControlUtil._
|
|
||||||
import util._
|
|
||||||
import service._
|
|
||||||
import org.eclipse.jgit.api.Git
|
|
||||||
import jp.sf.amateras.scalatra.forms._
|
|
||||||
import org.eclipse.jgit.lib.{FileMode, Constants}
|
|
||||||
import org.eclipse.jgit.dircache.DirCache
|
|
||||||
import org.scalatra.i18n.Messages
|
|
||||||
|
|
||||||
class CreateRepositoryController extends CreateRepositoryControllerBase
|
|
||||||
with RepositoryService with AccountService with WikiService with LabelsService with ActivityService
|
|
||||||
with UsersAuthenticator with ReadableUsersAuthenticator
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new repository.
|
|
||||||
*/
|
|
||||||
trait CreateRepositoryControllerBase extends ControllerBase {
|
|
||||||
self: RepositoryService with AccountService with WikiService with LabelsService with ActivityService
|
|
||||||
with UsersAuthenticator with ReadableUsersAuthenticator =>
|
|
||||||
|
|
||||||
case class RepositoryCreationForm(owner: String, name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean)
|
|
||||||
|
|
||||||
case class ForkRepositoryForm(owner: String, name: String)
|
|
||||||
|
|
||||||
val newForm = mapping(
|
|
||||||
"owner" -> trim(label("Owner" , text(required, maxlength(40), identifier, existsAccount))),
|
|
||||||
"name" -> trim(label("Repository name", text(required, maxlength(40), identifier, unique))),
|
|
||||||
"description" -> trim(label("Description" , optional(text()))),
|
|
||||||
"isPrivate" -> trim(label("Repository Type", boolean())),
|
|
||||||
"createReadme" -> trim(label("Create README" , boolean()))
|
|
||||||
)(RepositoryCreationForm.apply)
|
|
||||||
|
|
||||||
val forkForm = mapping(
|
|
||||||
"owner" -> trim(label("Repository owner", text(required))),
|
|
||||||
"name" -> trim(label("Repository name", text(required)))
|
|
||||||
)(ForkRepositoryForm.apply)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the new repository form.
|
|
||||||
*/
|
|
||||||
get("/new")(usersOnly {
|
|
||||||
html.newrepo(getGroupsByUserName(context.loginAccount.get.userName))
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create new repository.
|
|
||||||
*/
|
|
||||||
post("/new", newForm)(usersOnly { form =>
|
|
||||||
LockUtil.lock(s"${form.owner}/${form.name}/create"){
|
|
||||||
if(getRepository(form.owner, form.name, baseUrl).isEmpty){
|
|
||||||
val ownerAccount = getAccountByUserName(form.owner).get
|
|
||||||
val loginAccount = context.loginAccount.get
|
|
||||||
val loginUserName = loginAccount.userName
|
|
||||||
|
|
||||||
// Insert to the database at first
|
|
||||||
createRepository(form.name, form.owner, form.description, form.isPrivate)
|
|
||||||
|
|
||||||
// Add collaborators for group repository
|
|
||||||
if(ownerAccount.isGroupAccount){
|
|
||||||
getGroupMembers(form.owner).foreach { userName =>
|
|
||||||
addCollaborator(form.owner, form.name, userName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert default labels
|
|
||||||
insertDefaultLabels(form.owner, form.name)
|
|
||||||
|
|
||||||
// Create the actual repository
|
|
||||||
val gitdir = getRepositoryDir(form.owner, form.name)
|
|
||||||
JGitUtil.initRepository(gitdir)
|
|
||||||
|
|
||||||
if(form.createReadme){
|
|
||||||
using(Git.open(gitdir)){ git =>
|
|
||||||
val builder = DirCache.newInCore.builder()
|
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
|
||||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
|
||||||
val content = if(form.description.nonEmpty){
|
|
||||||
form.name + "\n" +
|
|
||||||
"===============\n" +
|
|
||||||
"\n" +
|
|
||||||
form.description.get
|
|
||||||
} else {
|
|
||||||
form.name + "\n" +
|
|
||||||
"===============\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.add(JGitUtil.createDirCacheEntry("README.md", FileMode.REGULAR_FILE,
|
|
||||||
inserter.insert(Constants.OBJ_BLOB, content.getBytes("UTF-8"))))
|
|
||||||
builder.finish()
|
|
||||||
|
|
||||||
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter),
|
|
||||||
loginAccount.fullName, loginAccount.mailAddress, "Initial commit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Wiki repository
|
|
||||||
createWikiRepository(loginAccount, form.owner, form.name)
|
|
||||||
|
|
||||||
// Record activity
|
|
||||||
recordCreateRepositoryActivity(form.owner, form.name, loginUserName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// redirect to the repository
|
|
||||||
redirect(s"/${form.owner}/${form.name}")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
get("/:owner/:repository/fork")(readableUsersOnly { repository =>
|
|
||||||
val loginAccount = context.loginAccount.get
|
|
||||||
val loginUserName = loginAccount.userName
|
|
||||||
|
|
||||||
LockUtil.lock(s"${loginUserName}/${repository.name}/create"){
|
|
||||||
if(repository.owner == loginUserName){
|
|
||||||
// redirect to the repository
|
|
||||||
redirect(s"/${repository.owner}/${repository.name}")
|
|
||||||
} else {
|
|
||||||
getForkedRepositories(repository.owner, repository.name).find(_._1 == loginUserName).map { case (owner, name) =>
|
|
||||||
// redirect to the repository
|
|
||||||
redirect(s"/${owner}/${name}")
|
|
||||||
} getOrElse {
|
|
||||||
// Insert to the database at first
|
|
||||||
val originUserName = repository.repository.originUserName.getOrElse(repository.owner)
|
|
||||||
val originRepositoryName = repository.repository.originRepositoryName.getOrElse(repository.name)
|
|
||||||
|
|
||||||
createRepository(
|
|
||||||
repositoryName = repository.name,
|
|
||||||
userName = loginUserName,
|
|
||||||
description = repository.repository.description,
|
|
||||||
isPrivate = repository.repository.isPrivate,
|
|
||||||
originRepositoryName = Some(originRepositoryName),
|
|
||||||
originUserName = Some(originUserName),
|
|
||||||
parentRepositoryName = Some(repository.name),
|
|
||||||
parentUserName = Some(repository.owner)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Insert default labels
|
|
||||||
insertDefaultLabels(loginUserName, repository.name)
|
|
||||||
|
|
||||||
// clone repository actually
|
|
||||||
JGitUtil.cloneRepository(
|
|
||||||
getRepositoryDir(repository.owner, repository.name),
|
|
||||||
getRepositoryDir(loginUserName, repository.name))
|
|
||||||
|
|
||||||
// Create Wiki repository
|
|
||||||
JGitUtil.cloneRepository(
|
|
||||||
getWikiRepositoryDir(repository.owner, repository.name),
|
|
||||||
getWikiRepositoryDir(loginUserName, repository.name))
|
|
||||||
|
|
||||||
// insert commit id
|
|
||||||
using(Git.open(getRepositoryDir(loginUserName, repository.name))){ git =>
|
|
||||||
JGitUtil.getRepositoryInfo(loginUserName, repository.name, baseUrl).branchList.foreach { branch =>
|
|
||||||
JGitUtil.getCommitLog(git, branch) match {
|
|
||||||
case Right((commits, _)) => commits.foreach { commit =>
|
|
||||||
if(!existsCommitId(loginUserName, repository.name, commit.id)){
|
|
||||||
insertCommitId(loginUserName, repository.name, commit.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case Left(_) => ???
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Record activity
|
|
||||||
recordForkActivity(repository.owner, repository.name, loginUserName)
|
|
||||||
// redirect to the repository
|
|
||||||
redirect(s"/${loginUserName}/${repository.name}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
private def insertDefaultLabels(userName: String, repositoryName: String): Unit = {
|
|
||||||
createLabel(userName, repositoryName, "bug", "fc2929")
|
|
||||||
createLabel(userName, repositoryName, "duplicate", "cccccc")
|
|
||||||
createLabel(userName, repositoryName, "enhancement", "84b6eb")
|
|
||||||
createLabel(userName, repositoryName, "invalid", "e6e6e6")
|
|
||||||
createLabel(userName, repositoryName, "question", "cc317c")
|
|
||||||
createLabel(userName, repositoryName, "wontfix", "ffffff")
|
|
||||||
}
|
|
||||||
|
|
||||||
private def existsAccount: Constraint = new Constraint(){
|
|
||||||
override def validate(name: String, value: String, messages: Messages): Option[String] =
|
|
||||||
if(getAccountByUserName(value).isEmpty) Some("User or group does not exist.") else None
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Duplicate check for the repository name.
|
|
||||||
*/
|
|
||||||
private def unique: Constraint = new Constraint(){
|
|
||||||
override def validate(name: String, value: String, params: Map[String, String], messages: Messages): Option[String] =
|
|
||||||
params.get("owner").flatMap { userName =>
|
|
||||||
getRepositoryNamesOfUser(userName).find(_ == value).map(_ => "Repository already exists.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -105,9 +105,9 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
} getOrElse NotFound
|
} getOrElse NotFound
|
||||||
})
|
})
|
||||||
|
|
||||||
get("/:owner/:repository/pull/:id/delete/:branchName")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/pull/:id/delete/*")(collaboratorsOnly { repository =>
|
||||||
params("id").toIntOpt.map { issueId =>
|
params("id").toIntOpt.map { issueId =>
|
||||||
val branchName = params("branchName")
|
val branchName = multiParams("splat").head
|
||||||
val userName = context.loginAccount.get.userName
|
val userName = context.loginAccount.get.userName
|
||||||
if(repository.repository.defaultBranch != branchName){
|
if(repository.repository.defaultBranch != branchName){
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
} map { objectId =>
|
} map { objectId =>
|
||||||
if(raw){
|
if(raw){
|
||||||
// Download
|
// Download
|
||||||
defining(JGitUtil.getContent(git, objectId, false).get){ bytes =>
|
defining(JGitUtil.getContentFromId(git, objectId, false).get){ bytes =>
|
||||||
contentType = FileUtil.getContentType(path, bytes)
|
contentType = FileUtil.getContentType(path, bytes)
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
// Viewer
|
// Viewer
|
||||||
val large = FileUtil.isLarge(git.getRepository.getObjectDatabase.open(objectId).getSize)
|
val large = FileUtil.isLarge(git.getRepository.getObjectDatabase.open(objectId).getSize)
|
||||||
val viewer = if(FileUtil.isImage(path)) "image" else if(large) "large" else "other"
|
val viewer = if(FileUtil.isImage(path)) "image" else if(large) "large" else "other"
|
||||||
val bytes = if(viewer == "other") JGitUtil.getContent(git, objectId, false) else None
|
val bytes = if(viewer == "other") JGitUtil.getContentFromId(git, objectId, false) else None
|
||||||
|
|
||||||
val content = if(viewer == "other"){
|
val content = if(viewer == "other"){
|
||||||
if(bytes.isDefined && FileUtil.isText(bytes.get)){
|
if(bytes.isDefined && FileUtil.isText(bytes.get)){
|
||||||
@@ -159,8 +159,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
/**
|
/**
|
||||||
* Deletes branch.
|
* Deletes branch.
|
||||||
*/
|
*/
|
||||||
get("/:owner/:repository/delete/:branchName")(collaboratorsOnly { repository =>
|
get("/:owner/:repository/delete/*")(collaboratorsOnly { repository =>
|
||||||
val branchName = params("branchName")
|
val branchName = multiParams("splat").head
|
||||||
val userName = context.loginAccount.get.userName
|
val userName = context.loginAccount.get.userName
|
||||||
if(repository.repository.defaultBranch != branchName){
|
if(repository.repository.defaultBranch != branchName){
|
||||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||||
@@ -208,7 +208,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
while(walk.next){
|
while(walk.next){
|
||||||
val name = walk.getPathString
|
val name = walk.getPathString
|
||||||
val mode = walk.getFileMode(0)
|
val mode = walk.getFileMode(0)
|
||||||
if(mode != FileMode.TREE){
|
if(mode == FileMode.REGULAR_FILE){
|
||||||
walk.getObjectId(objectId, 0)
|
walk.getObjectId(objectId, 0)
|
||||||
val entry = new ZipEntry(name)
|
val entry = new ZipEntry(name)
|
||||||
val loader = reader.open(objectId)
|
val loader = reader.open(objectId)
|
||||||
@@ -277,7 +277,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
val readme = files.find { file =>
|
val readme = files.find { file =>
|
||||||
readmeFiles.contains(file.name.toLowerCase)
|
readmeFiles.contains(file.name.toLowerCase)
|
||||||
}.map { file =>
|
}.map { file =>
|
||||||
file -> StringUtil.convertFromByteArray(JGitUtil.getContent(Git.open(getRepositoryDir(repository.owner, repository.name)), file.id, true).get)
|
file -> StringUtil.convertFromByteArray(JGitUtil.getContentFromId(
|
||||||
|
Git.open(getRepositoryDir(repository.owner, repository.name)), file.id, true).get)
|
||||||
}
|
}
|
||||||
|
|
||||||
repo.html.files(revision, repository,
|
repo.html.files(revision, repository,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import util.AdminAuthenticator
|
|||||||
import util.StringUtil._
|
import util.StringUtil._
|
||||||
import util.ControlUtil._
|
import util.ControlUtil._
|
||||||
import jp.sf.amateras.scalatra.forms._
|
import jp.sf.amateras.scalatra.forms._
|
||||||
|
import org.scalatra.i18n.Messages
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import util.Directory._
|
import util.Directory._
|
||||||
|
|
||||||
@@ -23,10 +24,10 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
|
|||||||
fileId: Option[String], clearImage: Boolean, isRemoved: Boolean)
|
fileId: Option[String], clearImage: Boolean, isRemoved: Boolean)
|
||||||
|
|
||||||
case class NewGroupForm(groupName: String, url: Option[String], fileId: Option[String],
|
case class NewGroupForm(groupName: String, url: Option[String], fileId: Option[String],
|
||||||
memberNames: Option[String])
|
members: String)
|
||||||
|
|
||||||
case class EditGroupForm(groupName: String, url: Option[String], fileId: Option[String],
|
case class EditGroupForm(groupName: String, url: Option[String], fileId: Option[String],
|
||||||
memberNames: Option[String], clearImage: Boolean, isRemoved: Boolean)
|
members: String, clearImage: Boolean, isRemoved: Boolean)
|
||||||
|
|
||||||
val newUserForm = mapping(
|
val newUserForm = mapping(
|
||||||
"userName" -> trim(label("Username" ,text(required, maxlength(100), identifier, uniqueUserName))),
|
"userName" -> trim(label("Username" ,text(required, maxlength(100), identifier, uniqueUserName))),
|
||||||
@@ -51,28 +52,28 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
|
|||||||
)(EditUserForm.apply)
|
)(EditUserForm.apply)
|
||||||
|
|
||||||
val newGroupForm = mapping(
|
val newGroupForm = mapping(
|
||||||
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier, uniqueUserName))),
|
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier, uniqueUserName))),
|
||||||
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||||
"fileId" -> trim(label("File ID" ,optional(text()))),
|
"fileId" -> trim(label("File ID" ,optional(text()))),
|
||||||
"memberNames" -> trim(label("Member Names" ,optional(text())))
|
"members" -> trim(label("Members" ,text(required, members)))
|
||||||
)(NewGroupForm.apply)
|
)(NewGroupForm.apply)
|
||||||
|
|
||||||
val editGroupForm = mapping(
|
val editGroupForm = mapping(
|
||||||
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier))),
|
"groupName" -> trim(label("Group name" ,text(required, maxlength(100), identifier))),
|
||||||
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
"url" -> trim(label("URL" ,optional(text(maxlength(200))))),
|
||||||
"fileId" -> trim(label("File ID" ,optional(text()))),
|
"fileId" -> trim(label("File ID" ,optional(text()))),
|
||||||
"memberNames" -> trim(label("Member Names" ,optional(text()))),
|
"members" -> trim(label("Members" ,text(required, members))),
|
||||||
"clearImage" -> trim(label("Clear image" ,boolean())),
|
"clearImage" -> trim(label("Clear image" ,boolean())),
|
||||||
"removed" -> trim(label("Disable" ,boolean()))
|
"removed" -> trim(label("Disable" ,boolean()))
|
||||||
)(EditGroupForm.apply)
|
)(EditGroupForm.apply)
|
||||||
|
|
||||||
get("/admin/users")(adminOnly {
|
get("/admin/users")(adminOnly {
|
||||||
val includeRemoved = params.get("includeRemoved").map(_.toBoolean).getOrElse(false)
|
val includeRemoved = params.get("includeRemoved").map(_.toBoolean).getOrElse(false)
|
||||||
val users = getAllUsers(includeRemoved)
|
val users = getAllUsers(includeRemoved)
|
||||||
|
val members = users.collect { case account if(account.isGroupAccount) =>
|
||||||
val members = users.collect { case account if(account.isGroupAccount) =>
|
account.userName -> getGroupMembers(account.userName).map(_.userName)
|
||||||
account.userName -> getGroupMembers(account.userName)
|
|
||||||
}.toMap
|
}.toMap
|
||||||
|
|
||||||
admin.users.html.list(users, members, includeRemoved)
|
admin.users.html.list(users, members, includeRemoved)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -127,7 +128,11 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
|
|||||||
|
|
||||||
post("/admin/users/_newgroup", newGroupForm)(adminOnly { form =>
|
post("/admin/users/_newgroup", newGroupForm)(adminOnly { form =>
|
||||||
createGroup(form.groupName, form.url)
|
createGroup(form.groupName, form.url)
|
||||||
updateGroupMembers(form.groupName, form.memberNames.map(_.split(",").toList).getOrElse(Nil))
|
updateGroupMembers(form.groupName, form.members.split(",").map {
|
||||||
|
_.split(":") match {
|
||||||
|
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||||
|
}
|
||||||
|
}.toList)
|
||||||
updateImage(form.groupName, form.fileId, false)
|
updateImage(form.groupName, form.fileId, false)
|
||||||
redirect("/admin/users")
|
redirect("/admin/users")
|
||||||
})
|
})
|
||||||
@@ -139,7 +144,11 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
|
|||||||
})
|
})
|
||||||
|
|
||||||
post("/admin/users/:groupName/_editgroup", editGroupForm)(adminOnly { form =>
|
post("/admin/users/:groupName/_editgroup", editGroupForm)(adminOnly { form =>
|
||||||
defining(params("groupName"), form.memberNames.map(_.split(",").toList).getOrElse(Nil)){ case (groupName, memberNames) =>
|
defining(params("groupName"), form.members.split(",").map {
|
||||||
|
_.split(":") match {
|
||||||
|
case Array(userName, isManager) => (userName, isManager.toBoolean)
|
||||||
|
}
|
||||||
|
}.toList){ case (groupName, members) =>
|
||||||
getAccountByUserName(groupName, true).map { account =>
|
getAccountByUserName(groupName, true).map { account =>
|
||||||
updateGroup(groupName, form.url, form.isRemoved)
|
updateGroup(groupName, form.url, form.isRemoved)
|
||||||
|
|
||||||
@@ -155,11 +164,11 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Update GROUP_MEMBER
|
// Update GROUP_MEMBER
|
||||||
updateGroupMembers(form.groupName, memberNames)
|
updateGroupMembers(form.groupName, members)
|
||||||
// Update COLLABORATOR for group repositories
|
// Update COLLABORATOR for group repositories
|
||||||
getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
getRepositoryNamesOfUser(form.groupName).foreach { repositoryName =>
|
||||||
removeCollaborators(form.groupName, repositoryName)
|
removeCollaborators(form.groupName, repositoryName)
|
||||||
memberNames.foreach { userName =>
|
members.foreach { case (userName, isManager) =>
|
||||||
addCollaborator(form.groupName, repositoryName, userName)
|
addCollaborator(form.groupName, repositoryName, userName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,8 +181,17 @@ trait UserManagementControllerBase extends AccountManagementControllerBase {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
post("/admin/users/_usercheck")(adminOnly {
|
// TODO Move to other generic controller?
|
||||||
|
post("/admin/users/_usercheck"){
|
||||||
getAccountByUserName(params("userName")).isDefined
|
getAccountByUserName(params("userName")).isDefined
|
||||||
})
|
}
|
||||||
|
|
||||||
|
private def members: Constraint = new Constraint(){
|
||||||
|
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||||
|
if(value.split(",").exists {
|
||||||
|
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
|
||||||
|
}) None else Some("Must select one manager at least.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ import scala.slick.driver.H2Driver.simple._
|
|||||||
object GroupMembers extends Table[GroupMember]("GROUP_MEMBER") {
|
object GroupMembers extends Table[GroupMember]("GROUP_MEMBER") {
|
||||||
def groupName = column[String]("GROUP_NAME", O PrimaryKey)
|
def groupName = column[String]("GROUP_NAME", O PrimaryKey)
|
||||||
def userName = column[String]("USER_NAME", O PrimaryKey)
|
def userName = column[String]("USER_NAME", O PrimaryKey)
|
||||||
def * = groupName ~ userName <> (GroupMember, GroupMember.unapply _)
|
def isManager = column[Boolean]("MANAGER")
|
||||||
|
def * = groupName ~ userName ~ isManager <> (GroupMember, GroupMember.unapply _)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class GroupMember(
|
case class GroupMember(
|
||||||
groupName: String,
|
groupName: String,
|
||||||
userName: String
|
userName: String,
|
||||||
|
isManager: Boolean
|
||||||
)
|
)
|
||||||
@@ -128,18 +128,17 @@ trait AccountService {
|
|||||||
def updateGroup(groupName: String, url: Option[String], removed: Boolean): Unit =
|
def updateGroup(groupName: String, url: Option[String], removed: Boolean): Unit =
|
||||||
Accounts.filter(_.userName is groupName.bind).map(t => t.url.? ~ t.removed).update(url, removed)
|
Accounts.filter(_.userName is groupName.bind).map(t => t.url.? ~ t.removed).update(url, removed)
|
||||||
|
|
||||||
def updateGroupMembers(groupName: String, members: List[String]): Unit = {
|
def updateGroupMembers(groupName: String, members: List[(String, Boolean)]): Unit = {
|
||||||
Query(GroupMembers).filter(_.groupName is groupName.bind).delete
|
Query(GroupMembers).filter(_.groupName is groupName.bind).delete
|
||||||
members.foreach { userName =>
|
members.foreach { case (userName, isManager) =>
|
||||||
GroupMembers insert GroupMember (groupName, userName)
|
GroupMembers insert GroupMember (groupName, userName, isManager)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def getGroupMembers(groupName: String): List[String] =
|
def getGroupMembers(groupName: String): List[GroupMember] =
|
||||||
Query(GroupMembers)
|
Query(GroupMembers)
|
||||||
.filter(_.groupName is groupName.bind)
|
.filter(_.groupName is groupName.bind)
|
||||||
.sortBy(_.userName)
|
.sortBy(_.userName)
|
||||||
.map(_.userName)
|
|
||||||
.list
|
.list
|
||||||
|
|
||||||
def getGroupsByUserName(userName: String): List[String] =
|
def getGroupsByUserName(userName: String): List[String] =
|
||||||
|
|||||||
@@ -63,8 +63,8 @@ RepositorySearchService { self: IssuesService =>
|
|||||||
val list = new ListBuffer[(String, String)]
|
val list = new ListBuffer[(String, String)]
|
||||||
|
|
||||||
while (treeWalk.next()) {
|
while (treeWalk.next()) {
|
||||||
if(treeWalk.getFileMode(0) != FileMode.TREE){
|
if(treeWalk.getFileMode(0) == FileMode.REGULAR_FILE){
|
||||||
JGitUtil.getContent(git, treeWalk.getObjectId(0), false).foreach { bytes =>
|
JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).foreach { bytes =>
|
||||||
if(FileUtil.isText(bytes)){
|
if(FileUtil.isText(bytes)){
|
||||||
val text = StringUtil.convertFromByteArray(bytes)
|
val text = StringUtil.convertFromByteArray(bytes)
|
||||||
val lowerText = text.toLowerCase
|
val lowerText = text.toLowerCase
|
||||||
|
|||||||
@@ -147,7 +147,8 @@ trait RepositoryService { self: AccountService =>
|
|||||||
getForkedCount(
|
getForkedCount(
|
||||||
repository.originUserName.getOrElse(repository.userName),
|
repository.originUserName.getOrElse(repository.userName),
|
||||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||||
))
|
),
|
||||||
|
getRepositoryManagers(repository.userName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +163,8 @@ trait RepositoryService { self: AccountService =>
|
|||||||
getForkedCount(
|
getForkedCount(
|
||||||
repository.originUserName.getOrElse(repository.userName),
|
repository.originUserName.getOrElse(repository.userName),
|
||||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||||
))
|
),
|
||||||
|
getRepositoryManagers(repository.userName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,10 +197,18 @@ trait RepositoryService { self: AccountService =>
|
|||||||
getForkedCount(
|
getForkedCount(
|
||||||
repository.originUserName.getOrElse(repository.userName),
|
repository.originUserName.getOrElse(repository.userName),
|
||||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||||
))
|
),
|
||||||
|
getRepositoryManagers(repository.userName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def getRepositoryManagers(userName: String): Seq[String] =
|
||||||
|
if(getAccountByUserName(userName).exists(_.isGroupAccount)){
|
||||||
|
getGroupMembers(userName).collect { case x if(x.isManager) => x.userName }
|
||||||
|
} else {
|
||||||
|
Seq(userName)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the last activity date of the repository.
|
* Updates the last activity date of the repository.
|
||||||
*/
|
*/
|
||||||
@@ -280,19 +290,19 @@ object RepositoryService {
|
|||||||
|
|
||||||
case class RepositoryInfo(owner: String, name: String, url: String, repository: Repository,
|
case class RepositoryInfo(owner: String, name: String, url: String, repository: Repository,
|
||||||
issueCount: Int, pullCount: Int, commitCount: Int, forkedCount: Int,
|
issueCount: Int, pullCount: Int, commitCount: Int, forkedCount: Int,
|
||||||
branchList: List[String], tags: List[util.JGitUtil.TagInfo]){
|
branchList: Seq[String], tags: Seq[util.JGitUtil.TagInfo], managers: Seq[String]){
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates instance with issue count and pull request count.
|
* Creates instance with issue count and pull request count.
|
||||||
*/
|
*/
|
||||||
def this(repo: JGitUtil.RepositoryInfo, model: Repository, issueCount: Int, pullCount: Int, forkedCount: Int) =
|
def this(repo: JGitUtil.RepositoryInfo, model: Repository, issueCount: Int, pullCount: Int, forkedCount: Int, managers: Seq[String]) =
|
||||||
this(repo.owner, repo.name, repo.url, model, issueCount, pullCount, repo.commitCount, forkedCount, repo.branchList, repo.tags)
|
this(repo.owner, repo.name, repo.url, model, issueCount, pullCount, repo.commitCount, forkedCount, repo.branchList, repo.tags, managers)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates instance without issue count and pull request count.
|
* Creates instance without issue count and pull request count.
|
||||||
*/
|
*/
|
||||||
def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int) =
|
def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int, managers: Seq[String]) =
|
||||||
this(repo.owner, repo.name, repo.url, model, 0, 0, repo.commitCount, forkedCount, repo.branchList, repo.tags)
|
this(repo.owner, repo.name, repo.url, model, 0, 0, repo.commitCount, forkedCount, repo.branchList, repo.tags, managers)
|
||||||
}
|
}
|
||||||
|
|
||||||
case class RepositoryTreeNode(owner: String, name: String, children: List[RepositoryTreeNode])
|
case class RepositoryTreeNode(owner: String, name: String, children: List[RepositoryTreeNode])
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ trait WikiService {
|
|||||||
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||||
} else {
|
} else {
|
||||||
created = false
|
created = false
|
||||||
updated = JGitUtil.getContent(git, tree.getEntryObjectId, true).map(new String(_, "UTF-8") != content).getOrElse(false)
|
updated = JGitUtil.getContentFromId(git, tree.getEntryObjectId, true).map(new String(_, "UTF-8") != content).getOrElse(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -268,35 +268,35 @@ trait WikiService {
|
|||||||
*/
|
*/
|
||||||
def deleteWikiPage(owner: String, repository: String, pageName: String,
|
def deleteWikiPage(owner: String, repository: String, pageName: String,
|
||||||
committer: String, mailAddress: String, message: String): Unit = {
|
committer: String, mailAddress: String, message: String): Unit = {
|
||||||
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
LockUtil.lock(s"${owner}/${repository}/wiki"){
|
||||||
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
using(Git.open(Directory.getWikiRepositoryDir(owner, repository))){ git =>
|
||||||
val builder = DirCache.newInCore.builder()
|
val builder = DirCache.newInCore.builder()
|
||||||
val inserter = git.getRepository.newObjectInserter()
|
val inserter = git.getRepository.newObjectInserter()
|
||||||
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
val headId = git.getRepository.resolve(Constants.HEAD + "^{commit}")
|
||||||
var removed = false
|
var removed = false
|
||||||
|
|
||||||
using(new RevWalk(git.getRepository)){ revWalk =>
|
using(new RevWalk(git.getRepository)){ revWalk =>
|
||||||
using(new TreeWalk(git.getRepository)){ treeWalk =>
|
using(new TreeWalk(git.getRepository)){ treeWalk =>
|
||||||
val index = treeWalk.addTree(revWalk.parseTree(headId))
|
val index = treeWalk.addTree(revWalk.parseTree(headId))
|
||||||
treeWalk.setRecursive(true)
|
treeWalk.setRecursive(true)
|
||||||
while(treeWalk.next){
|
while(treeWalk.next){
|
||||||
val path = treeWalk.getPathString
|
val path = treeWalk.getPathString
|
||||||
val tree = treeWalk.getTree(index, classOf[CanonicalTreeParser])
|
val tree = treeWalk.getTree(index, classOf[CanonicalTreeParser])
|
||||||
if(path != pageName + ".md"){
|
if(path != pageName + ".md"){
|
||||||
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
|
||||||
} else {
|
} else {
|
||||||
removed = true
|
removed = true
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(removed){
|
if(removed){
|
||||||
builder.finish()
|
builder.finish()
|
||||||
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer, mailAddress, message)
|
JGitUtil.createNewCommit(git, inserter, headId, builder.getDirCache.writeTree(inserter), committer, mailAddress, message)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ object AutoUpdate {
|
|||||||
* The history of versions. A head of this sequence is the current BitBucket version.
|
* The history of versions. A head of this sequence is the current BitBucket version.
|
||||||
*/
|
*/
|
||||||
val versions = Seq(
|
val versions = Seq(
|
||||||
|
Version(1, 12),
|
||||||
Version(1, 11),
|
Version(1, 11),
|
||||||
Version(1, 10),
|
Version(1, 10),
|
||||||
Version(1, 9),
|
Version(1, 9),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.slf4j.LoggerFactory
|
|||||||
|
|
||||||
import javax.servlet.ServletConfig
|
import javax.servlet.ServletConfig
|
||||||
import javax.servlet.ServletContext
|
import javax.servlet.ServletContext
|
||||||
import javax.servlet.http.HttpServletRequest
|
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
|
||||||
import util.{StringUtil, Keys, JGitUtil, Directory}
|
import util.{StringUtil, Keys, JGitUtil, Directory}
|
||||||
import util.ControlUtil._
|
import util.ControlUtil._
|
||||||
import util.Implicits._
|
import util.Implicits._
|
||||||
@@ -23,7 +23,7 @@ import util.JGitUtil.CommitInfo
|
|||||||
* This servlet provides only Git repository functionality.
|
* This servlet provides only Git repository functionality.
|
||||||
* Authentication is provided by [[servlet.BasicAuthenticationFilter]].
|
* Authentication is provided by [[servlet.BasicAuthenticationFilter]].
|
||||||
*/
|
*/
|
||||||
class GitRepositoryServlet extends GitServlet {
|
class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[GitRepositoryServlet])
|
private val logger = LoggerFactory.getLogger(classOf[GitRepositoryServlet])
|
||||||
|
|
||||||
@@ -48,6 +48,18 @@ class GitRepositoryServlet extends GitServlet {
|
|||||||
super.init(config)
|
super.init(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def service(req: HttpServletRequest, res: HttpServletResponse): Unit = {
|
||||||
|
val agent = req.getHeader("USER-AGENT")
|
||||||
|
if(agent == null || !agent.startsWith("git/")){
|
||||||
|
// redirect for browsers
|
||||||
|
val paths = req.getRequestURI.split("/")
|
||||||
|
val baseUrl = loadSystemSettings().baseUrl.getOrElse(req.getServletContext.getContextPath)
|
||||||
|
res.sendRedirect(baseUrl + "/" + paths.dropRight(1).last + "/" + paths.last.replaceFirst("\\.git$", ""))
|
||||||
|
} else {
|
||||||
|
// response for git client
|
||||||
|
super.service(req, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest] with SystemSettingsService {
|
class GitBucketReceivePackFactory extends ReceivePackFactory[HttpServletRequest] with SystemSettingsService {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ trait OneselfAuthenticator { self: ControllerBase =>
|
|||||||
/**
|
/**
|
||||||
* Allows only the repository owner and administrators.
|
* Allows only the repository owner and administrators.
|
||||||
*/
|
*/
|
||||||
trait OwnerAuthenticator { self: ControllerBase with RepositoryService =>
|
trait OwnerAuthenticator { self: ControllerBase with RepositoryService with AccountService =>
|
||||||
protected def ownerOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
protected def ownerOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
||||||
protected def ownerOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
protected def ownerOnly[T](action: (T, RepositoryInfo) => Any) = (form: T) => { authenticate(action(form, _)) }
|
||||||
|
|
||||||
@@ -40,6 +40,9 @@ trait OwnerAuthenticator { self: ControllerBase with RepositoryService =>
|
|||||||
context.loginAccount match {
|
context.loginAccount match {
|
||||||
case Some(x) if(x.isAdmin) => action(repository)
|
case Some(x) if(x.isAdmin) => action(repository)
|
||||||
case Some(x) if(repository.owner == x.userName) => 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.isManager == true
|
||||||
|
}) => action(repository)
|
||||||
case _ => Unauthorized()
|
case _ => Unauthorized()
|
||||||
}
|
}
|
||||||
} getOrElse NotFound()
|
} getOrElse NotFound()
|
||||||
@@ -106,7 +109,7 @@ trait CollaboratorsAuthenticator { self: ControllerBase with RepositoryService =
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows only the repository owner and administrators.
|
* Allows only the repository owner (or manager for group repository) and administrators.
|
||||||
*/
|
*/
|
||||||
trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
|
trait ReferrerAuthenticator { self: ControllerBase with RepositoryService =>
|
||||||
protected def referrersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
protected def referrersOnly(action: (RepositoryInfo) => Any) = { authenticate(action) }
|
||||||
@@ -155,3 +158,24 @@ trait ReadableUsersAuthenticator { self: ControllerBase with RepositoryService =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows only the group managers.
|
||||||
|
*/
|
||||||
|
trait GroupManagerAuthenticator { self: ControllerBase with AccountService =>
|
||||||
|
protected def managersOnly(action: => Any) = { authenticate(action) }
|
||||||
|
protected def managersOnly[T](action: T => Any) = (form: T) => { authenticate(action(form)) }
|
||||||
|
|
||||||
|
private def authenticate(action: => Any) = {
|
||||||
|
{
|
||||||
|
defining(request.paths){ paths =>
|
||||||
|
context.loginAccount match {
|
||||||
|
case Some(x) if(getGroupMembers(paths(0)).exists { member =>
|
||||||
|
member.userName == x.userName && member.isManager
|
||||||
|
}) => action
|
||||||
|
case _ => Unauthorized()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,17 +11,20 @@ import org.eclipse.jgit.revwalk.filter._
|
|||||||
import org.eclipse.jgit.treewalk._
|
import org.eclipse.jgit.treewalk._
|
||||||
import org.eclipse.jgit.treewalk.filter._
|
import org.eclipse.jgit.treewalk.filter._
|
||||||
import org.eclipse.jgit.diff.DiffEntry.ChangeType
|
import org.eclipse.jgit.diff.DiffEntry.ChangeType
|
||||||
import org.eclipse.jgit.errors.MissingObjectException
|
import org.eclipse.jgit.errors.{ConfigInvalidException, MissingObjectException}
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import org.eclipse.jgit.api.errors.NoHeadException
|
import org.eclipse.jgit.api.errors.NoHeadException
|
||||||
import service.RepositoryService
|
import service.RepositoryService
|
||||||
import org.eclipse.jgit.dircache.DirCacheEntry
|
import org.eclipse.jgit.dircache.DirCacheEntry
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides complex JGit operations.
|
* Provides complex JGit operations.
|
||||||
*/
|
*/
|
||||||
object JGitUtil {
|
object JGitUtil {
|
||||||
|
|
||||||
|
private val logger = LoggerFactory.getLogger(JGitUtil.getClass)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The repository data.
|
* The repository data.
|
||||||
*
|
*
|
||||||
@@ -45,9 +48,10 @@ object JGitUtil {
|
|||||||
* @param commitId the last commit id
|
* @param commitId the last commit id
|
||||||
* @param committer the last committer name
|
* @param committer the last committer name
|
||||||
* @param mailAddress the committer's mail address
|
* @param mailAddress the committer's mail address
|
||||||
|
* @param linkUrl the url of submodule
|
||||||
*/
|
*/
|
||||||
case class FileInfo(id: ObjectId, isDirectory: Boolean, name: String, time: Date, message: String, commitId: String,
|
case class FileInfo(id: ObjectId, isDirectory: Boolean, name: String, time: Date, message: String, commitId: String,
|
||||||
committer: String, mailAddress: String)
|
committer: String, mailAddress: String, linkUrl: Option[String])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The commit data.
|
* The commit data.
|
||||||
@@ -104,6 +108,15 @@ object JGitUtil {
|
|||||||
*/
|
*/
|
||||||
case class TagInfo(name: String, time: Date, id: String)
|
case class TagInfo(name: String, time: Date, id: String)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The submodule data
|
||||||
|
*
|
||||||
|
* @param name the module name
|
||||||
|
* @param path the path in the repository
|
||||||
|
* @param url the repository url of this module
|
||||||
|
*/
|
||||||
|
case class SubmoduleInfo(name: String, path: String, url: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns RevCommit from the commit or tag id.
|
* Returns RevCommit from the commit or tag id.
|
||||||
*
|
*
|
||||||
@@ -162,7 +175,7 @@ object JGitUtil {
|
|||||||
* @return HTML of the file list
|
* @return HTML of the file list
|
||||||
*/
|
*/
|
||||||
def getFileList(git: Git, revision: String, path: String = "."): List[FileInfo] = {
|
def getFileList(git: Git, revision: String, path: String = "."): List[FileInfo] = {
|
||||||
val list = new scala.collection.mutable.ListBuffer[(ObjectId, FileMode, String, String)]
|
val list = new scala.collection.mutable.ListBuffer[(ObjectId, FileMode, String, String, Option[String])]
|
||||||
|
|
||||||
using(new RevWalk(git.getRepository)){ revWalk =>
|
using(new RevWalk(git.getRepository)){ revWalk =>
|
||||||
val objectId = git.getRepository.resolve(revision)
|
val objectId = git.getRepository.resolve(revision)
|
||||||
@@ -195,22 +208,28 @@ object JGitUtil {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
while (treeWalk.next()) {
|
while (treeWalk.next()) {
|
||||||
list.append((treeWalk.getObjectId(0), treeWalk.getFileMode(0), treeWalk.getPathString, treeWalk.getNameString))
|
// submodule
|
||||||
|
val linkUrl = if(treeWalk.getFileMode(0) == FileMode.GITLINK){
|
||||||
|
getSubmodules(git, revCommit.getTree).find(_.path == treeWalk.getPathString).map(_.url)
|
||||||
|
} else None
|
||||||
|
|
||||||
|
list.append((treeWalk.getObjectId(0), treeWalk.getFileMode(0), treeWalk.getPathString, treeWalk.getNameString, linkUrl))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val commits = getLatestCommitFromPaths(git, list.toList.map(_._3), revision)
|
val commits = getLatestCommitFromPaths(git, list.toList.map(_._3), revision)
|
||||||
list.map { case (objectId, fileMode, path, name) =>
|
list.map { case (objectId, fileMode, path, name, linkUrl) =>
|
||||||
FileInfo(
|
FileInfo(
|
||||||
objectId,
|
objectId,
|
||||||
fileMode == FileMode.TREE,
|
fileMode == FileMode.TREE || fileMode == FileMode.GITLINK,
|
||||||
name,
|
name,
|
||||||
commits(path).getCommitterIdent.getWhen,
|
commits(path).getCommitterIdent.getWhen,
|
||||||
commits(path).getShortMessage,
|
commits(path).getShortMessage,
|
||||||
commits(path).getName,
|
commits(path).getName,
|
||||||
commits(path).getCommitterIdent.getName,
|
commits(path).getCommitterIdent.getName,
|
||||||
commits(path).getCommitterIdent.getEmailAddress)
|
commits(path).getCommitterIdent.getEmailAddress,
|
||||||
|
linkUrl)
|
||||||
}.sortWith { (file1, file2) =>
|
}.sortWith { (file1, file2) =>
|
||||||
(file1.isDirectory, file2.isDirectory) match {
|
(file1.isDirectory, file2.isDirectory) match {
|
||||||
case (true , false) => true
|
case (true , false) => true
|
||||||
@@ -325,27 +344,6 @@ object JGitUtil {
|
|||||||
}.toMap
|
}.toMap
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get object content of the given id as String from the Git repository.
|
|
||||||
*
|
|
||||||
* @param git the Git object
|
|
||||||
* @param id the object id
|
|
||||||
* @param large if false then returns None for the large file
|
|
||||||
* @return the object or None if object does not exist
|
|
||||||
*/
|
|
||||||
def getContent(git: Git, id: ObjectId, large: Boolean): Option[Array[Byte]] = try {
|
|
||||||
val loader = git.getRepository.getObjectDatabase.open(id)
|
|
||||||
if(large == false && FileUtil.isLarge(loader.getSize)){
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
using(git.getRepository.getObjectDatabase){ db =>
|
|
||||||
Some(db.open(id).getBytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
case e: MissingObjectException => None
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the tuple of diff of the given commit and the previous commit id.
|
* Returns the tuple of diff of the given commit and the previous commit id.
|
||||||
*/
|
*/
|
||||||
@@ -377,7 +375,7 @@ object JGitUtil {
|
|||||||
DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None, None)
|
DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None, None)
|
||||||
} else {
|
} else {
|
||||||
DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None,
|
DiffInfo(ChangeType.ADD, null, treeWalk.getPathString, None,
|
||||||
JGitUtil.getContent(git, treeWalk.getObjectId(0), false).filter(FileUtil.isText).map(convertFromByteArray))
|
JGitUtil.getContentFromId(git, treeWalk.getObjectId(0), false).filter(FileUtil.isText).map(convertFromByteArray))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
(buffer.toList, None)
|
(buffer.toList, None)
|
||||||
@@ -400,8 +398,8 @@ object JGitUtil {
|
|||||||
DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath, None, None)
|
DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath, None, None)
|
||||||
} else {
|
} else {
|
||||||
DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath,
|
DiffInfo(diff.getChangeType, diff.getOldPath, diff.getNewPath,
|
||||||
JGitUtil.getContent(git, diff.getOldId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray),
|
JGitUtil.getContentFromId(git, diff.getOldId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray),
|
||||||
JGitUtil.getContent(git, diff.getNewId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray))
|
JGitUtil.getContentFromId(git, diff.getNewId.toObjectId, false).filter(FileUtil.isText).map(convertFromByteArray))
|
||||||
}
|
}
|
||||||
}.toList
|
}.toList
|
||||||
}
|
}
|
||||||
@@ -494,4 +492,73 @@ object JGitUtil {
|
|||||||
newHeadId.getName
|
newHeadId.getName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read submodule information from .gitmodules
|
||||||
|
*/
|
||||||
|
def getSubmodules(git: Git, tree: RevTree): List[SubmoduleInfo] = {
|
||||||
|
val repository = git.getRepository
|
||||||
|
getContentFromPath(git, tree, ".gitmodules", true).map { bytes =>
|
||||||
|
(try {
|
||||||
|
val config = new BlobBasedConfig(repository.getConfig(), bytes)
|
||||||
|
config.getSubsections("submodule").asScala.map { module =>
|
||||||
|
val path = config.getString("submodule", module, "path")
|
||||||
|
val url = config.getString("submodule", module, "url")
|
||||||
|
SubmoduleInfo(module, path, url)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case e: ConfigInvalidException => {
|
||||||
|
logger.error("Failed to load .gitmodules file for " + repository.getDirectory(), e)
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
}).toList
|
||||||
|
} getOrElse Nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get object content of the given path as byte array from the Git repository.
|
||||||
|
*
|
||||||
|
* @param git the Git object
|
||||||
|
* @param revTree the rev tree
|
||||||
|
* @param path the path
|
||||||
|
* @param fetchLargeFile if false then returns None for the large file
|
||||||
|
* @return the byte array of content or None if object does not exist
|
||||||
|
*/
|
||||||
|
def getContentFromPath(git: Git, revTree: RevTree, path: String, fetchLargeFile: Boolean): Option[Array[Byte]] = {
|
||||||
|
@scala.annotation.tailrec
|
||||||
|
def getPathObjectId(path: String, walk: TreeWalk): Option[ObjectId] = walk.next match {
|
||||||
|
case true if(walk.getPathString == path) => Some(walk.getObjectId(0))
|
||||||
|
case true => getPathObjectId(path, walk)
|
||||||
|
case false => None
|
||||||
|
}
|
||||||
|
|
||||||
|
using(new TreeWalk(git.getRepository)){ treeWalk =>
|
||||||
|
treeWalk.addTree(revTree)
|
||||||
|
treeWalk.setRecursive(true)
|
||||||
|
getPathObjectId(path, treeWalk)
|
||||||
|
} flatMap { objectId =>
|
||||||
|
getContentFromId(git, objectId, fetchLargeFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get object content of the given object id as byte array from the Git repository.
|
||||||
|
*
|
||||||
|
* @param git the Git object
|
||||||
|
* @param id the object id
|
||||||
|
* @param fetchLargeFile if false then returns None for the large file
|
||||||
|
* @return the byte array of content or None if object does not exist
|
||||||
|
*/
|
||||||
|
def getContentFromId(git: Git, id: ObjectId, fetchLargeFile: Boolean): Option[Array[Byte]] = try {
|
||||||
|
val loader = git.getRepository.getObjectDatabase.open(id)
|
||||||
|
if(fetchLargeFile == false && FileUtil.isLarge(loader.getSize)){
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
using(git.getRepository.getObjectDatabase){ db =>
|
||||||
|
Some(db.open(id).getBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case e: MissingObjectException => None
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
140
src/main/twirl/account/group.scala.html
Normal file
140
src/main/twirl/account/group.scala.html
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
@(account: Option[model.Account], members: List[model.GroupMember])(implicit context: app.Context)
|
||||||
|
@import context._
|
||||||
|
@import view.helpers._
|
||||||
|
@html.main(if(account.isEmpty) "Create group" else "Edit group"){
|
||||||
|
<div>
|
||||||
|
<form id="form" method="post" action="@if(account.isEmpty){@path/groups/new} else {@path/@account.get.userName/_editgroup}" validate="true">
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span5">
|
||||||
|
<fieldset>
|
||||||
|
<label for="groupName" class="strong">Group name</label>
|
||||||
|
<div>
|
||||||
|
<span id="error-groupName" class="error"></span>
|
||||||
|
</div>
|
||||||
|
<input type="text" name="groupName" id="groupName" value="@account.map(_.userName)"@if(account.isDefined){ readonly}/>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<label class="strong">URL (Optional)</label>
|
||||||
|
<div>
|
||||||
|
<span id="error-url" class="error"></span>
|
||||||
|
</div>
|
||||||
|
<input type="text" name="url" id="url" style="width: 300px;" value="@account.map(_.url)"/>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<label for="avatar" class="strong">Image (Optional)</label>
|
||||||
|
@helper.html.uploadavatar(account)
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<div class="span7">
|
||||||
|
<fieldset>
|
||||||
|
<label class="strong">Members</label>
|
||||||
|
<ul id="member-list" class="collaborator">
|
||||||
|
</ul>
|
||||||
|
@helper.html.account("memberName", 200)
|
||||||
|
<input type="button" class="btn" value="Add" id="addMember"/>
|
||||||
|
<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>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset class="margin">
|
||||||
|
@if(account.isDefined){
|
||||||
|
<div class="pull-right">
|
||||||
|
<a href="@url(account.get.userName)/_deletegroup" id="delete" class="btn btn-danger">Delete Group</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<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">Cancel</a>
|
||||||
|
}
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<script>
|
||||||
|
$(function(){
|
||||||
|
$('input[type=submit]').click(function(){
|
||||||
|
updateMembers();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#addMember').click(function(){
|
||||||
|
$('#error-memberName').text('');
|
||||||
|
var userName = $('#memberName').val();
|
||||||
|
|
||||||
|
// check empty
|
||||||
|
if($.trim(userName) == ''){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check duplication
|
||||||
|
var exists = $('#member-list li').filter(function(){
|
||||||
|
return $(this).data('name') == userName;
|
||||||
|
}).length > 0;
|
||||||
|
if(exists){
|
||||||
|
$('#error-memberName').text('User has been already added.');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check existence
|
||||||
|
$.post('@path/admin/users/_usercheck', {
|
||||||
|
'userName': userName
|
||||||
|
}, function(data, status){
|
||||||
|
if(data == 'true'){
|
||||||
|
addMemberHTML(userName, false);
|
||||||
|
} else {
|
||||||
|
$('#error-memberName').text('User does not exist.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.remove', function(){
|
||||||
|
$(this).parent().remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Don't submit form by ENTER key
|
||||||
|
$('#memberName').keypress(function(e){
|
||||||
|
return !(e.keyCode == 13);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#delete').click(function(){
|
||||||
|
return confirm('Once you delete this group, there is no going back.\nAre you sure?');
|
||||||
|
});
|
||||||
|
|
||||||
|
@members.map { member =>
|
||||||
|
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);
|
||||||
|
if(!isManager){
|
||||||
|
memberButton.addClass('active');
|
||||||
|
}
|
||||||
|
var managerButton = $('<button type="button" class="btn btn-default btn-mini" value="true">Manager</button>').data('name', userName);
|
||||||
|
if(isManager){
|
||||||
|
managerButton.addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#member-list').append($('<li>')
|
||||||
|
.data('name', userName)
|
||||||
|
.append($('<div class="btn-group is_manager" data-toggle="buttons-radio">')
|
||||||
|
.append(memberButton)
|
||||||
|
.append(managerButton))
|
||||||
|
.append(' ')
|
||||||
|
.append($('<a>').attr('href', '@path/' + userName).text(userName))
|
||||||
|
.append(' ')
|
||||||
|
.append($('<a href="#" class="remove pull-right">(remove)</a>')));
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
}).get().join(',');
|
||||||
|
$('#members').val(members);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
@(account: model.Account, groupNames: List[String], active: String)(body: Html)(implicit context: app.Context)
|
@(account: model.Account, groupNames: List[String], active: String,
|
||||||
|
isGroupManager: Boolean = false)(body: Html)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@html.main(account.userName){
|
@html.main(account.userName){
|
||||||
@@ -41,6 +42,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
@if(loginAccount.isDefined && account.isGroupAccount && isGroupManager){
|
||||||
|
<li class="pull-right">
|
||||||
|
<div class="button-group">
|
||||||
|
<a href="@url(account.userName)/_editgroup" class="btn">Edit Group</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
</ul>
|
</ul>
|
||||||
@body
|
@body
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@(account: model.Account, members: List[String])(implicit context: app.Context)
|
@(account: model.Account, members: List[String], isGroupManager: Boolean)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@main(account, Nil, "members"){
|
@main(account, Nil, "members", isGroupManager){
|
||||||
@if(members.isEmpty){
|
@if(members.isEmpty){
|
||||||
No members
|
No members
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@(groupNames: List[String])(implicit context: app.Context)
|
@(groupNames: List[String])(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@main("Create a New Repository"){
|
@html.main("Create a New Repository"){
|
||||||
<div style="width: 600px; margin: 10px auto;">
|
<div style="width: 600px; margin: 10px auto;">
|
||||||
<form id="form" method="post" action="@path/new" validate="true">
|
<form id="form" method="post" action="@path/new" validate="true">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
@(account: model.Account, groupNames: List[String], repositories: List[service.RepositoryService.RepositoryInfo])(implicit context: app.Context)
|
@(account: model.Account, groupNames: List[String],
|
||||||
|
repositories: List[service.RepositoryService.RepositoryInfo],
|
||||||
|
isGroupManager: Boolean)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@main(account, groupNames, "repositories"){
|
@main(account, groupNames, "repositories", isGroupManager){
|
||||||
@if(repositories.isEmpty){
|
@if(repositories.isEmpty){
|
||||||
No repositories
|
No repositories
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
@(account: Option[model.Account], members: List[String])(implicit context: app.Context)
|
@(account: Option[model.Account], members: List[model.GroupMember])(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@html.main(if(account.isEmpty) "New Group" else "Update Group"){
|
@html.main(if(account.isEmpty) "New Group" else "Update Group"){
|
||||||
@admin.html.menu("users"){
|
@admin.html.menu("users"){
|
||||||
<form method="POST" action="@if(account.isEmpty){@path/admin/users/_newgroup} else {@path/admin/users/@account.get.userName/_editgroup}" validate="true">
|
<form method="POST" action="@if(account.isEmpty){@path/admin/users/_newgroup} else {@path/admin/users/@account.get.userName/_editgroup}" validate="true">
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span7">
|
<div class="span5">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="groupName" class="strong">Group name</label>
|
<label for="groupName" class="strong">Group name</label>
|
||||||
<div>
|
<div>
|
||||||
@@ -24,29 +24,23 @@
|
|||||||
<div>
|
<div>
|
||||||
<span id="error-url" class="error"></span>
|
<span id="error-url" class="error"></span>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" name="url" id="url" style="width: 300px;" value="@account.map(_.url)"/>
|
<input type="text" name="url" id="url" value="@account.map(_.url)"/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="avatar" class="strong">Image (Optional)</label>
|
<label for="avatar" class="strong">Image (Optional)</label>
|
||||||
@helper.html.uploadavatar(account)
|
@helper.html.uploadavatar(account)
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
<div class="span5">
|
<div class="span7">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label class="strong">Members</label>
|
<label class="strong">Members</label>
|
||||||
<ul id="members" class="collaborator">
|
<ul id="member-list" class="collaborator">
|
||||||
@members.map { userName =>
|
|
||||||
<li data-name="@userName">
|
|
||||||
<a href="@path/@url(userName)">@userName</a>
|
|
||||||
<a href="#" class="remove">(remove)</a>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
</ul>
|
||||||
@helper.html.account("memberName", 200)
|
@helper.html.account("memberName", 200)
|
||||||
<input type="button" class="btn" value="Add" id="addMember"/>
|
<input type="button" class="btn" value="Add" id="addMember"/>
|
||||||
<input type="hidden" id="memberNames" name="memberNames" value="@members.mkString(",")"/>
|
<input type="hidden" id="members" name="members" value="@members.map(member => member.userName + ":" + member.isManager).mkString(",")"/>
|
||||||
<div>
|
<div>
|
||||||
<span class="error" id="error-memberName"></span>
|
<span class="error" id="error-members"></span>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
@@ -60,6 +54,10 @@
|
|||||||
}
|
}
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
|
$('input[type=submit]').click(function(){
|
||||||
|
updateMembers();
|
||||||
|
});
|
||||||
|
|
||||||
$('#addMember').click(function(){
|
$('#addMember').click(function(){
|
||||||
$('#error-memberName').text('');
|
$('#error-memberName').text('');
|
||||||
var userName = $('#memberName').val();
|
var userName = $('#memberName').val();
|
||||||
@@ -70,7 +68,7 @@ $(function(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check duplication
|
// check duplication
|
||||||
var exists = $('#members li').filter(function(){
|
var exists = $('#member-list li').filter(function(){
|
||||||
return $(this).data('name') == userName;
|
return $(this).data('name') == userName;
|
||||||
}).length > 0;
|
}).length > 0;
|
||||||
if(exists){
|
if(exists){
|
||||||
@@ -83,19 +81,7 @@ $(function(){
|
|||||||
'userName': userName
|
'userName': userName
|
||||||
}, function(data, status){
|
}, function(data, status){
|
||||||
if(data == 'true'){
|
if(data == 'true'){
|
||||||
// add member
|
addMemberHTML(userName, false);
|
||||||
$('#members').append($('<li>')
|
|
||||||
.data('name', userName)
|
|
||||||
.append($('<a>').attr('href', '@path/' + userName).text(userName))
|
|
||||||
.append(' ')
|
|
||||||
.append($('<a>').attr('href', '#').addClass('remove').text('(remove)')));
|
|
||||||
$('#memberName').val('');
|
|
||||||
|
|
||||||
// update hidden value
|
|
||||||
var userNames = $('#members li').map(function(i, e){
|
|
||||||
return $(e).data('name');
|
|
||||||
}).get().join(',');
|
|
||||||
$('#memberNames').val(userNames);
|
|
||||||
} else {
|
} else {
|
||||||
$('#error-memberName').text('User does not exist.');
|
$('#error-memberName').text('User does not exist.');
|
||||||
}
|
}
|
||||||
@@ -103,20 +89,47 @@ $(function(){
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.remove', function(){
|
$(document).on('click', '.remove', function(){
|
||||||
// remove member
|
|
||||||
$(this).parent().remove();
|
$(this).parent().remove();
|
||||||
|
|
||||||
// update hidden value
|
|
||||||
var userNames = $('#members li').map(function(i, e){
|
|
||||||
return $(e).data('name');
|
|
||||||
}).get().join(',');
|
|
||||||
$('#memberNames').val(userNames);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Don't submit form by ENTER key
|
// Don't submit form by ENTER key
|
||||||
$('#memberName').keypress(function(e){
|
$('#memberName').keypress(function(e){
|
||||||
console.log(e.keyCode);
|
|
||||||
return !(e.keyCode == 13);
|
return !(e.keyCode == 13);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@members.map { member =>
|
||||||
|
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);
|
||||||
|
if(!isManager){
|
||||||
|
memberButton.addClass('active');
|
||||||
|
}
|
||||||
|
var managerButton = $('<button type="button" class="btn btn-default btn-mini" value="true">Manager</button>').data('name', userName);
|
||||||
|
if(isManager){
|
||||||
|
managerButton.addClass('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#member-list').append($('<li>')
|
||||||
|
.data('name', userName)
|
||||||
|
.append($('<div class="btn-group is_manager" data-toggle="buttons-radio">')
|
||||||
|
.append(memberButton)
|
||||||
|
.append(managerButton))
|
||||||
|
.append(' ')
|
||||||
|
.append($('<a>').attr('href', '@path/' + userName).text(userName))
|
||||||
|
.append(' ')
|
||||||
|
.append($('<a href="#" class="remove pull-right">(remove)</a>')));
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
}).get().join(',');
|
||||||
|
$('#members').val(members);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<th class="box-header@if(active=="network"){ active}">
|
<th class="box-header@if(active=="network"){ active}">
|
||||||
<a href="@url(repository)/network/members">Network</a>
|
<a href="@url(repository)/network/members">Network</a>
|
||||||
</th>
|
</th>
|
||||||
@if(loginAccount.isDefined && (loginAccount.get.isAdmin || loginAccount.get.userName == repository.owner)){
|
@if(loginAccount.isDefined && (loginAccount.get.isAdmin || repository.managers.contains(loginAccount.get.userName))){
|
||||||
<th class="box-header@if(active=="settings"){ active}">
|
<th class="box-header@if(active=="settings"){ active}">
|
||||||
<a href="@url(repository)/settings">Settings</a>
|
<a href="@url(repository)/settings">Settings</a>
|
||||||
</th>
|
</th>
|
||||||
|
|||||||
@@ -54,7 +54,11 @@
|
|||||||
}
|
}
|
||||||
@if(loginAccount.isDefined){
|
@if(loginAccount.isDefined){
|
||||||
<a href="@url(loginAccount.get.userName)" class="username menu">@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</a>
|
<a href="@url(loginAccount.get.userName)" class="username menu">@avatar(loginAccount.get.userName, 20) @loginAccount.get.userName</a>
|
||||||
<a href="@path/new" class="menu" data-toggle="tooltip" data-placement="bottom" title="Create a new repo"><i class="icon-plus"></i></a>
|
<a class="dropdown-toggle menu" data-toggle="dropdown" href="#"><i class="icon-plus"></i><span class="caret" style="vertical-align: middle;"></span></a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="@path/new">New repository</a></li>
|
||||||
|
<li><a href="@path/groups/new">New group</a></li>
|
||||||
|
</ul>
|
||||||
<a href="@url(loginAccount.get.userName)/_edit" class="menu" data-toggle="tooltip" data-placement="bottom" title="Account settings"><i class="icon-user"></i></a>
|
<a href="@url(loginAccount.get.userName)/_edit" class="menu" data-toggle="tooltip" data-placement="bottom" title="Account settings"><i class="icon-user"></i></a>
|
||||||
@if(loginAccount.get.isAdmin){
|
@if(loginAccount.get.isAdmin){
|
||||||
<a href="@path/admin/users" class="menu" data-toggle="tooltip" data-placement="bottom" title="Administration"><i class="icon-wrench"></i></a>
|
<a href="@path/admin/users" class="menu" data-toggle="tooltip" data-placement="bottom" title="Administration"><i class="icon-wrench"></i></a>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@(branchInfo: List[(String, java.util.Date)],
|
@(branchInfo: Seq[(String, java.util.Date)],
|
||||||
hasWritePermission: Boolean,
|
hasWritePermission: Boolean,
|
||||||
repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
|
repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
|
|||||||
@@ -55,14 +55,22 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td width="16">
|
<td width="16">
|
||||||
@if(file.isDirectory){
|
@if(file.isDirectory){
|
||||||
<img src="@assets/common/images/folder.png"/>
|
@if(file.linkUrl.isDefined){
|
||||||
|
<img src="@assets/common/images/folder_link.png"/>
|
||||||
|
} else {
|
||||||
|
<img src="@assets/common/images/folder.png"/>
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
<img src="@assets/common/images/file.png"/>
|
<img src="@assets/common/images/file.png"/>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
@if(file.isDirectory){
|
@if(file.isDirectory){
|
||||||
<a href="@url(repository)/tree@{(encodeRefName(branch) :: pathList).mkString("/", "/", "/")}@file.name">@file.name</a>
|
@if(file.linkUrl.isDefined){
|
||||||
|
<a href="@file.linkUrl">@file.name</a>
|
||||||
|
} else {
|
||||||
|
<a href="@url(repository)/tree@{(encodeRefName(branch) :: pathList).mkString("/", "/", "/")}@file.name">@file.name</a>
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
<a href="@url(repository)/blob@{(encodeRefName(branch) :: pathList).mkString("/", "/", "/")}@file.name">@file.name</a>
|
<a href="@url(repository)/blob@{(encodeRefName(branch) :: pathList).mkString("/", "/", "/")}@file.name">@file.name</a>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
<a href="@url(collaboratorName)">@collaboratorName</a>
|
<a href="@url(collaboratorName)">@collaboratorName</a>
|
||||||
@if(!isGroupRepository){
|
@if(!isGroupRepository){
|
||||||
<a href="@url(repository)/settings/collaborators/remove?name=@collaboratorName" class="remove">(remove)</a>
|
<a href="@url(repository)/settings/collaborators/remove?name=@collaboratorName" class="remove">(remove)</a>
|
||||||
|
} else {
|
||||||
|
@if(repository.managers.contains(collaboratorName)){
|
||||||
|
(Manager)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/main/webapp/assets/common/images/folder_link.png
Normal file
BIN
src/main/webapp/assets/common/images/folder_link.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 785 B |
@@ -2,8 +2,9 @@ package service
|
|||||||
|
|
||||||
import org.specs2.mutable.Specification
|
import org.specs2.mutable.Specification
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
import model.GroupMember
|
||||||
|
|
||||||
class AccountServiceServiceSpec extends Specification with ServiceSpecBase {
|
class AccountServiceSpec extends Specification with ServiceSpecBase {
|
||||||
|
|
||||||
"AccountService" should {
|
"AccountService" should {
|
||||||
val RootMailAddress = "root@localhost"
|
val RootMailAddress = "root@localhost"
|
||||||
@@ -63,9 +64,9 @@ class AccountServiceServiceSpec extends Specification with ServiceSpecBase {
|
|||||||
AccountService.getGroupMembers(group1) must_== Nil
|
AccountService.getGroupMembers(group1) must_== Nil
|
||||||
AccountService.getGroupsByUserName(user1) must_== Nil
|
AccountService.getGroupsByUserName(user1) must_== Nil
|
||||||
|
|
||||||
AccountService.updateGroupMembers(group1, List(user1))
|
AccountService.updateGroupMembers(group1, List((user1, true)))
|
||||||
|
|
||||||
AccountService.getGroupMembers(group1) must_== List(user1)
|
AccountService.getGroupMembers(group1) must_== List(GroupMember(group1, user1, true))
|
||||||
AccountService.getGroupsByUserName(user1) must_== List(group1)
|
AccountService.getGroupsByUserName(user1) must_== List(group1)
|
||||||
|
|
||||||
AccountService.updateGroupMembers(group1, Nil)
|
AccountService.updateGroupMembers(group1, Nil)
|
||||||
Reference in New Issue
Block a user