diff --git a/src/main/resources/update/1_5.sql b/src/main/resources/update/1_5.sql index 3f85dc0..21a8337 100644 --- a/src/main/resources/update/1_5.sql +++ b/src/main/resources/update/1_5.sql @@ -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, diff --git a/src/main/scala/app/CreateRepositoryController.scala b/src/main/scala/app/CreateRepositoryController.scala index 0aa12e9..403fbed 100644 --- a/src/main/scala/app/CreateRepositoryController.scala +++ b/src/main/scala/app/CreateRepositoryController.scala @@ -116,8 +116,20 @@ 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) diff --git a/src/main/scala/app/RepositoryViewerController.scala b/src/main/scala/app/RepositoryViewerController.scala index e4dc1ed..6c3b1d7 100644 --- a/src/main/scala/app/RepositoryViewerController.scala +++ b/src/main/scala/app/RepositoryViewerController.scala @@ -202,6 +202,14 @@ 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. diff --git a/src/main/scala/model/Repository.scala b/src/main/scala/model/Repository.scala index 1c1f2e9..215b670 100644 --- a/src/main/scala/model/Repository.scala +++ b/src/main/scala/model/Repository.scala @@ -11,7 +11,9 @@ 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 @@ updatedDate: java.util.Date, lastActivityDate: java.util.Date, originUserName: Option[String], - originRepositoryName: Option[String] + originRepositoryName: Option[String], + parentUserName: Option[String], + parentRepositoryName: Option[String] ) diff --git a/src/main/scala/service/RepositoryService.scala b/src/main/scala/service/RepositoryService.scala index b9ce2e2..ff9d66b 100644 --- a/src/main/scala/service/RepositoryService.scala +++ b/src/main/scala/service/RepositoryService.scala @@ -19,7 +19,8 @@ * @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,8 +32,10 @@ updatedDate = currentDate, lastActivityDate = currentDate, originUserName = originUserName, - originRepositoryName = originRepositoryName) - + originRepositoryName = originRepositoryName, + parentUserName = parentUserName, + parentRepositoryName = parentRepositoryName) + IssueId insert (userName, repositoryName, 0) } @@ -87,7 +90,13 @@ } 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 @@ */ 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 @@ 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 @@ } } -// 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]) + } \ No newline at end of file diff --git a/src/main/twirl/account/repositories.scala.html b/src/main/twirl/account/repositories.scala.html index 03f2c6e..97045d9 100644 --- a/src/main/twirl/account/repositories.scala.html +++ b/src/main/twirl/account/repositories.scala.html @@ -16,7 +16,7 @@ } @if(repository.repository.originUserName.isDefined){ -
forked from @repository.repository.originUserName/@repository.repository.originRepositoryName
+
forked from @repository.repository.parentUserName/@repository.repository.parentRepositoryName
} @if(repository.repository.description.isDefined){
@repository.repository.description
diff --git a/src/main/twirl/header.scala.html b/src/main/twirl/header.scala.html index 191374c..c29a1f5 100644 --- a/src/main/twirl/header.scala.html +++ b/src/main/twirl/header.scala.html @@ -2,7 +2,10 @@ @import context._ @import view.helpers._
- +
+ + @repository.forkedCount +
@repository.owner / @repository.name @@ -12,7 +15,7 @@ @defining(repository.repository){ x => @if(repository.repository.originRepositoryName.isDefined){
- forked from @x.originUserName/@x.originRepositoryName + forked from @x.parentUserName/@x.parentRepositoryName
} } diff --git a/src/main/twirl/repo/forked.scala.html b/src/main/twirl/repo/forked.scala.html new file mode 100644 index 0000000..4efe751 --- /dev/null +++ b/src/main/twirl/repo/forked.scala.html @@ -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)) { + +

Members of the @repository.name Network

+ +} + +@renderTree(node: service.RepositoryService.RepositoryTreeNode) = { +
  • +
    + @avatar(node.owner, 20) @node.owner / @node.name +
    + @if(node.children.nonEmpty){ + + } +
  • +} \ No newline at end of file diff --git a/src/main/webapp/assets/common/css/gitbucket.css b/src/main/webapp/assets/common/css/gitbucket.css index 44e1f10..b01b48a 100644 --- a/src/main/webapp/assets/common/css/gitbucket.css +++ b/src/main/webapp/assets/common/css/gitbucket.css @@ -70,6 +70,19 @@ 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 */ /* ======================================================================== */