mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-01 11:06:06 +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_USER_NAME VARCHAR(100);
|
||||||
ALTER TABLE REPOSITORY ADD COLUMN ORIGIN_REPOSITORY_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(
|
CREATE TABLE PULL_REQUEST(
|
||||||
USER_NAME VARCHAR(100) NOT NULL,
|
USER_NAME VARCHAR(100) NOT NULL,
|
||||||
|
|||||||
@@ -116,8 +116,20 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
|||||||
if(getRepository(loginUserName, repository.name, baseUrl).isEmpty){
|
if(getRepository(loginUserName, repository.name, baseUrl).isEmpty){
|
||||||
// Insert to the database at first
|
// Insert to the database at first
|
||||||
// TODO Is private repository cloneable?
|
// 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
|
// Insert default labels
|
||||||
insertDefaultLabels(loginUserName, repository.name)
|
insertDefaultLabels(loginUserName, repository.name)
|
||||||
|
|||||||
@@ -202,6 +202,14 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
BadRequest
|
BadRequest
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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.
|
* 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 lastActivityDate = column[java.util.Date]("LAST_ACTIVITY_DATE")
|
||||||
def originUserName = column[String]("ORIGIN_USER_NAME")
|
def originUserName = column[String]("ORIGIN_USER_NAME")
|
||||||
def originRepositoryName = column[String]("ORIGIN_REPOSITORY_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)
|
def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
|
||||||
}
|
}
|
||||||
@@ -26,5 +28,7 @@ case class Repository(
|
|||||||
updatedDate: java.util.Date,
|
updatedDate: java.util.Date,
|
||||||
lastActivityDate: java.util.Date,
|
lastActivityDate: java.util.Date,
|
||||||
originUserName: Option[String],
|
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)
|
* @param originUserName specify for the forked repository. (default is None)
|
||||||
*/
|
*/
|
||||||
def createRepository(repositoryName: String, userName: String, description: Option[String], isPrivate: Boolean,
|
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
|
Repositories insert
|
||||||
Repository(
|
Repository(
|
||||||
userName = userName,
|
userName = userName,
|
||||||
@@ -31,8 +32,10 @@ trait RepositoryService { self: AccountService =>
|
|||||||
updatedDate = currentDate,
|
updatedDate = currentDate,
|
||||||
lastActivityDate = currentDate,
|
lastActivityDate = currentDate,
|
||||||
originUserName = originUserName,
|
originUserName = originUserName,
|
||||||
originRepositoryName = originRepositoryName)
|
originRepositoryName = originRepositoryName,
|
||||||
|
parentUserName = parentUserName,
|
||||||
|
parentRepositoryName = parentRepositoryName)
|
||||||
|
|
||||||
IssueId insert (userName, repositoryName, 0)
|
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 =>
|
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] = {
|
def getRepository(userName: String, repositoryName: String, baseUrl: String): Option[RepositoryInfo] = {
|
||||||
(Query(Repositories) filter { t => t.byRepository(userName, repositoryName) } firstOption) map { repository =>
|
(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 getAccessibleRepositories(account: Option[Account], baseUrl: String): List[RepositoryInfo] = {
|
||||||
|
|
||||||
def newRepositoryInfo(repository: Repository): 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 {
|
(account match {
|
||||||
@@ -194,28 +215,39 @@ trait RepositoryService { self: AccountService =>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// def getBaseRepositories(userName: String, repositoryName: String, repositories: List[String] = Nil): List[String] = {
|
// TODO It must be _.length instead of map (_.issueId) list).length.
|
||||||
// Query(Repositories).filter { t =>
|
// But it does not work on Slick 1.0.1 (worked on Slick 1.0.0).
|
||||||
// (t.originUserName is userName.bind) && (t.originRepositoryName is repositoryName.bind)
|
// https://github.com/slick/slick/issues/170
|
||||||
// }.map(_.userName).list match {
|
private def getForkedCount(userName: String, repositoryName: String): Int =
|
||||||
// case Nil => repositories.sorted
|
Query(Repositories).filter { t =>
|
||||||
// case list => list.map { x =>
|
(t.originUserName is userName.bind) && (t.originRepositoryName is repositoryName.bind)
|
||||||
// getBaseRepositories(x, repositoryName, x :: repositories)
|
}.list.length
|
||||||
// }.flatten
|
|
||||||
// }
|
|
||||||
// }
|
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 {
|
object RepositoryService {
|
||||||
|
|
||||||
case class RepositoryInfo(owner: String, name: String, url: String, repository: Repository,
|
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) = {
|
def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int) = {
|
||||||
this(repo.owner, repo.name, repo.url, model, repo.commitCount, repo.branchList, repo.tags)
|
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>
|
</div>
|
||||||
@if(repository.repository.originUserName.isDefined){
|
@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){
|
@if(repository.repository.description.isDefined){
|
||||||
<div>@repository.repository.description</div>
|
<div>@repository.repository.description</div>
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<input type="button" id="fork" class="btn" value="Fork" style="margin-bottom: 10px;"/>
|
<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>
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<a href="@url(repository.owner)">@repository.owner</a> / <a href="@url(repository)">@repository.name</a>
|
<a href="@url(repository.owner)">@repository.owner</a> / <a href="@url(repository)">@repository.name</a>
|
||||||
@@ -12,7 +15,7 @@
|
|||||||
@defining(repository.repository){ x =>
|
@defining(repository.repository){ x =>
|
||||||
@if(repository.repository.originRepositoryName.isDefined){
|
@if(repository.repository.originRepositoryName.isDefined){
|
||||||
<div class="forked">
|
<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>
|
</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;
|
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 */
|
/* General Styles */
|
||||||
/* ======================================================================== */
|
/* ======================================================================== */
|
||||||
|
|||||||
Reference in New Issue
Block a user