mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-02 11:36:05 +01:00
(refs #2)Add forked count and repository tree view.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
ALTER TABLE REPOSITORY ADD COLUMN ORIGIN_USER_NAME VARCHAR(100);
|
||||
ALTER TABLE REPOSITORY ADD COLUMN ORIGIN_REPOSITORY_NAME VARCHAR(100);
|
||||
|
||||
ALTER TABLE REPOSITORY ADD COLUMN PARENT_USER_NAME VARCHAR(100);
|
||||
ALTER TABLE REPOSITORY ADD COLUMN PARENT_REPOSITORY_NAME VARCHAR(100);
|
||||
|
||||
CREATE TABLE PULL_REQUEST(
|
||||
USER_NAME VARCHAR(100) NOT NULL,
|
||||
|
||||
@@ -116,8 +116,20 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
||||
if(getRepository(loginUserName, repository.name, baseUrl).isEmpty){
|
||||
// Insert to the database at first
|
||||
// TODO Is private repository cloneable?
|
||||
createRepository(repository.name, loginUserName, repository.repository.description,
|
||||
repository.repository.isPrivate, Some(repository.name), Some(repository.owner))
|
||||
|
||||
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)
|
||||
|
||||
@@ -203,6 +203,14 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
}
|
||||
})
|
||||
|
||||
get("/:owner/:repository/network/members")(referrersOnly { repository =>
|
||||
repo.html.forked(
|
||||
getForkedRepositoryTree(
|
||||
repository.repository.originUserName.getOrElse(repository.owner),
|
||||
repository.repository.originRepositoryName.getOrElse(repository.name)),
|
||||
repository)
|
||||
})
|
||||
|
||||
/**
|
||||
* Provides HTML of the file list.
|
||||
*
|
||||
|
||||
@@ -11,7 +11,9 @@ object Repositories extends Table[Repository]("REPOSITORY") with BasicTemplate {
|
||||
def lastActivityDate = column[java.util.Date]("LAST_ACTIVITY_DATE")
|
||||
def originUserName = column[String]("ORIGIN_USER_NAME")
|
||||
def originRepositoryName = column[String]("ORIGIN_REPOSITORY_NAME")
|
||||
def * = userName ~ repositoryName ~ isPrivate ~ description.? ~ defaultBranch ~ registeredDate ~ updatedDate ~ lastActivityDate ~ originUserName.? ~ originRepositoryName.? <> (Repository, Repository.unapply _)
|
||||
def parentUserName = column[String]("PARENT_USER_NAME")
|
||||
def parentRepositoryName = column[String]("PARENT_REPOSITORY_NAME")
|
||||
def * = userName ~ repositoryName ~ isPrivate ~ description.? ~ defaultBranch ~ registeredDate ~ updatedDate ~ lastActivityDate ~ originUserName.? ~ originRepositoryName.? ~ parentUserName.? ~ parentRepositoryName.? <> (Repository, Repository.unapply _)
|
||||
|
||||
def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
|
||||
}
|
||||
@@ -26,5 +28,7 @@ case class Repository(
|
||||
updatedDate: java.util.Date,
|
||||
lastActivityDate: java.util.Date,
|
||||
originUserName: Option[String],
|
||||
originRepositoryName: Option[String]
|
||||
originRepositoryName: Option[String],
|
||||
parentUserName: Option[String],
|
||||
parentRepositoryName: Option[String]
|
||||
)
|
||||
|
||||
@@ -19,7 +19,8 @@ trait RepositoryService { self: AccountService =>
|
||||
* @param originUserName specify for the forked repository. (default is None)
|
||||
*/
|
||||
def createRepository(repositoryName: String, userName: String, description: Option[String], isPrivate: Boolean,
|
||||
originRepositoryName: Option[String] = None, originUserName: Option[String] = None): Unit = {
|
||||
originRepositoryName: Option[String] = None, originUserName: Option[String] = None,
|
||||
parentRepositoryName: Option[String] = None, parentUserName: Option[String] = None): Unit = {
|
||||
Repositories insert
|
||||
Repository(
|
||||
userName = userName,
|
||||
@@ -31,7 +32,9 @@ trait RepositoryService { self: AccountService =>
|
||||
updatedDate = currentDate,
|
||||
lastActivityDate = currentDate,
|
||||
originUserName = originUserName,
|
||||
originRepositoryName = originRepositoryName)
|
||||
originRepositoryName = originRepositoryName,
|
||||
parentUserName = parentUserName,
|
||||
parentRepositoryName = parentRepositoryName)
|
||||
|
||||
IssueId insert (userName, repositoryName, 0)
|
||||
}
|
||||
@@ -87,7 +90,13 @@ trait RepositoryService { self: AccountService =>
|
||||
}
|
||||
|
||||
q1.union(q2).filter(visibleFor(_, loginUserName)).sortBy(_.lastActivityDate desc).list map { repository =>
|
||||
new RepositoryInfo(JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl), repository)
|
||||
new RepositoryInfo(
|
||||
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl),
|
||||
repository,
|
||||
getForkedCount(
|
||||
repository.originUserName.getOrElse(repository.userName),
|
||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +110,13 @@ trait RepositoryService { self: AccountService =>
|
||||
*/
|
||||
def getRepository(userName: String, repositoryName: String, baseUrl: String): Option[RepositoryInfo] = {
|
||||
(Query(Repositories) filter { t => t.byRepository(userName, repositoryName) } firstOption) map { repository =>
|
||||
new RepositoryInfo(JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl), repository)
|
||||
new RepositoryInfo(
|
||||
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl),
|
||||
repository,
|
||||
getForkedCount(
|
||||
repository.originUserName.getOrElse(repository.userName),
|
||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +130,13 @@ trait RepositoryService { self: AccountService =>
|
||||
def getAccessibleRepositories(account: Option[Account], baseUrl: String): List[RepositoryInfo] = {
|
||||
|
||||
def newRepositoryInfo(repository: Repository): RepositoryInfo = {
|
||||
new RepositoryInfo(JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl), repository)
|
||||
new RepositoryInfo(
|
||||
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl),
|
||||
repository,
|
||||
getForkedCount(
|
||||
repository.originUserName.getOrElse(repository.userName),
|
||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||
))
|
||||
}
|
||||
|
||||
(account match {
|
||||
@@ -194,28 +215,39 @@ trait RepositoryService { self: AccountService =>
|
||||
}
|
||||
}
|
||||
|
||||
// def getBaseRepositories(userName: String, repositoryName: String, repositories: List[String] = Nil): List[String] = {
|
||||
// Query(Repositories).filter { t =>
|
||||
// (t.originUserName is userName.bind) && (t.originRepositoryName is repositoryName.bind)
|
||||
// }.map(_.userName).list match {
|
||||
// case Nil => repositories.sorted
|
||||
// case list => list.map { x =>
|
||||
// getBaseRepositories(x, repositoryName, x :: repositories)
|
||||
// }.flatten
|
||||
// }
|
||||
// }
|
||||
// TODO It must be _.length instead of map (_.issueId) list).length.
|
||||
// But it does not work on Slick 1.0.1 (worked on Slick 1.0.0).
|
||||
// https://github.com/slick/slick/issues/170
|
||||
private def getForkedCount(userName: String, repositoryName: String): Int =
|
||||
Query(Repositories).filter { t =>
|
||||
(t.originUserName is userName.bind) && (t.originRepositoryName is repositoryName.bind)
|
||||
}.list.length
|
||||
|
||||
|
||||
def getForkedRepositoryTree(userName: String, repositoryName: String): RepositoryTreeNode = {
|
||||
RepositoryTreeNode(userName, repositoryName,
|
||||
Query(Repositories).filter { t =>
|
||||
(t.parentUserName is userName.bind) && (t.parentRepositoryName is repositoryName.bind)
|
||||
}.map { t =>
|
||||
t.userName ~ t.repositoryName
|
||||
}.list.map { case (userName, repositoryName) =>
|
||||
getForkedRepositoryTree(userName, repositoryName)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object RepositoryService {
|
||||
|
||||
case class RepositoryInfo(owner: String, name: String, url: String, repository: Repository,
|
||||
commitCount: Int, branchList: List[String], tags: List[util.JGitUtil.TagInfo]){
|
||||
commitCount: Int, forkedCount: Int, branchList: List[String], tags: List[util.JGitUtil.TagInfo]){
|
||||
|
||||
def this(repo: JGitUtil.RepositoryInfo, model: Repository) = {
|
||||
this(repo.owner, repo.name, repo.url, model, repo.commitCount, repo.branchList, repo.tags)
|
||||
def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int) = {
|
||||
this(repo.owner, repo.name, repo.url, model, repo.commitCount, forkedCount, repo.branchList, repo.tags)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
case class RepositoryTreeNode(owner: String, name: String, children: List[RepositoryTreeNode])
|
||||
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
}
|
||||
</div>
|
||||
@if(repository.repository.originUserName.isDefined){
|
||||
<div class="small muted">forked from <a href="@path/@repository.repository.originUserName/@repository.repository.originRepositoryName">@repository.repository.originUserName/@repository.repository.originRepositoryName</a></div>
|
||||
<div class="small muted">forked from <a href="@path/@repository.repository.parentUserName/@repository.repository.parentRepositoryName">@repository.repository.parentUserName/@repository.repository.parentRepositoryName</a></div>
|
||||
}
|
||||
@if(repository.repository.description.isDefined){
|
||||
<div>@repository.repository.description</div>
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
@import context._
|
||||
@import view.helpers._
|
||||
<div class="pull-right">
|
||||
<div class="input-prepend">
|
||||
<input type="button" id="fork" class="btn" value="Fork" style="margin-bottom: 10px;"/>
|
||||
<span class="add-on"><a href="@url(repository)/network/members">@repository.forkedCount</a></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="head">
|
||||
<a href="@url(repository.owner)">@repository.owner</a> / <a href="@url(repository)">@repository.name</a>
|
||||
@@ -12,7 +15,7 @@
|
||||
@defining(repository.repository){ x =>
|
||||
@if(repository.repository.originRepositoryName.isDefined){
|
||||
<div class="forked">
|
||||
forked from <a href="@path/@x.originUserName/@x.originRepositoryName">@x.originUserName/@x.originRepositoryName</a>
|
||||
forked from <a href="@path/@x.parentUserName/@x.parentRepositoryName">@x.parentUserName/@x.parentRepositoryName</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
28
src/main/twirl/repo/forked.scala.html
Normal file
28
src/main/twirl/repo/forked.scala.html
Normal file
@@ -0,0 +1,28 @@
|
||||
@(members: service.RepositoryService.RepositoryTreeNode,
|
||||
repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
|
||||
@import context._
|
||||
@import view.helpers._
|
||||
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) {
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="@url(repository)/network/members">Members</a></li>
|
||||
</ul>
|
||||
<h3>Members of the @repository.name Network</h3>
|
||||
<ul>
|
||||
@renderTree(members)
|
||||
</ul>
|
||||
}
|
||||
|
||||
@renderTree(node: service.RepositoryService.RepositoryTreeNode) = {
|
||||
<li>
|
||||
<div style="font-size: 120%; margin-bottom: 8px;">
|
||||
@avatar(node.owner, 20) <a href="@url(node.owner)">@node.owner</a> / <a href="@path/@node.owner/@node.name">@node.name</a>
|
||||
</div>
|
||||
@if(node.children.nonEmpty){
|
||||
<ul>
|
||||
@node.children.map { child =>
|
||||
@renderTree(child)
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
@@ -70,6 +70,19 @@ table.global-nav th a:link, table.global-nav th a:hover, table.global-nav th a:v
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div.input-prepend span.add-on {
|
||||
background-color: white;
|
||||
-webkit-border-radius: 0 4px 4px 0;
|
||||
-moz-border-radius: 0 4px 4px 0;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
/*
|
||||
div.input-prepend span.add-on a {
|
||||
color: #333;
|
||||
}
|
||||
*/
|
||||
|
||||
/* ======================================================================== */
|
||||
/* General Styles */
|
||||
/* ======================================================================== */
|
||||
|
||||
Reference in New Issue
Block a user