mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-09 06:55:54 +01:00
Merge branch 'master' into fork-and-pullreq
Conflicts: src/main/scala/app/CreateRepositoryController.scala src/main/scala/util/JGitUtil.scala
This commit is contained in:
@@ -8,6 +8,7 @@ class ScalatraBootstrap extends LifeCycle {
|
||||
context.mount(new SearchController, "/")
|
||||
context.mount(new FileUploadController, "/upload")
|
||||
context.mount(new SignInController, "/*")
|
||||
context.mount(new DashboardController, "/*")
|
||||
context.mount(new UserManagementController, "/*")
|
||||
context.mount(new SystemSettingsController, "/*")
|
||||
context.mount(new CreateRepositoryController, "/*")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package app
|
||||
|
||||
import _root_.util.Directory._
|
||||
import _root_.util.{FileUtil, Validations}
|
||||
import _root_.util.{StringUtil, FileUtil, Validations}
|
||||
import org.scalatra._
|
||||
import org.scalatra.json._
|
||||
import org.json4s._
|
||||
@@ -10,7 +10,7 @@ import org.apache.commons.io.FileUtils
|
||||
import model.Account
|
||||
import scala.Some
|
||||
import service.AccountService
|
||||
import javax.servlet.http.{HttpSession, HttpServletRequest}
|
||||
import javax.servlet.http.{HttpServletResponse, HttpSession, HttpServletRequest}
|
||||
import java.text.SimpleDateFormat
|
||||
import javax.servlet.{FilterChain, ServletResponse, ServletRequest}
|
||||
|
||||
@@ -23,16 +23,28 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
implicit val jsonFormats = DefaultFormats
|
||||
|
||||
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
|
||||
val httpRequest = request.asInstanceOf[HttpServletRequest]
|
||||
val path = httpRequest.getRequestURI.substring(request.getServletContext.getContextPath.length)
|
||||
val httpRequest = request.asInstanceOf[HttpServletRequest]
|
||||
val httpResponse = response.asInstanceOf[HttpServletResponse]
|
||||
val context = request.getServletContext.getContextPath
|
||||
val path = httpRequest.getRequestURI.substring(context.length)
|
||||
|
||||
if(path.startsWith("/console/")){
|
||||
Option(httpRequest.getSession.getAttribute("LOGIN_ACCOUNT").asInstanceOf[Account]).collect {
|
||||
case account if(account.isAdmin) => chain.doFilter(request, response)
|
||||
val account = httpRequest.getSession.getAttribute("LOGIN_ACCOUNT").asInstanceOf[Account]
|
||||
if(account == null){
|
||||
// Redirect to login form
|
||||
httpResponse.sendRedirect(context + "/signin?" + path)
|
||||
} else if(account.isAdmin){
|
||||
// H2 Console (administrators only)
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
// Redirect to dashboard
|
||||
httpResponse.sendRedirect(context + "/")
|
||||
}
|
||||
} else if(path.startsWith("/git/")){
|
||||
// Git repository
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
// Scalatra actions
|
||||
super.doFilter(request, response, chain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,4 +193,4 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
49
src/main/scala/app/DashboardController.scala
Normal file
49
src/main/scala/app/DashboardController.scala
Normal file
@@ -0,0 +1,49 @@
|
||||
package app
|
||||
|
||||
import service._
|
||||
import util.UsersAuthenticator
|
||||
|
||||
class DashboardController extends DashboardControllerBase
|
||||
with IssuesService with RepositoryService with AccountService
|
||||
with UsersAuthenticator
|
||||
|
||||
trait DashboardControllerBase extends ControllerBase {
|
||||
self: IssuesService with RepositoryService with UsersAuthenticator =>
|
||||
|
||||
get("/dashboard/issues/repos")(usersOnly {
|
||||
searchIssues("all")
|
||||
})
|
||||
|
||||
get("/dashboard/issues/assigned")(usersOnly {
|
||||
searchIssues("assigned")
|
||||
})
|
||||
|
||||
get("/dashboard/issues/created_by")(usersOnly {
|
||||
searchIssues("created_by")
|
||||
})
|
||||
|
||||
private def searchIssues(filter: String) = {
|
||||
import IssuesService._
|
||||
|
||||
// condition
|
||||
val sessionKey = "dashboard/issues"
|
||||
val condition = if(request.getQueryString == null)
|
||||
session.get(sessionKey).getOrElse(IssueSearchCondition()).asInstanceOf[IssueSearchCondition]
|
||||
else IssueSearchCondition(request)
|
||||
|
||||
session.put(sessionKey, condition)
|
||||
|
||||
val repositories = getAccessibleRepositories(context.loginAccount, baseUrl)
|
||||
//
|
||||
dashboard.html.issues(
|
||||
issues.html.listparts(Nil, 0, 0, 0, condition),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
repositories,
|
||||
condition,
|
||||
filter)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,11 @@ object IssueId extends Table[(String, String, Int)]("ISSUE_ID") with IssueTempla
|
||||
def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
|
||||
}
|
||||
|
||||
object IssueOutline extends Table[(String, String, Int, Int)]("ISSUE_OUTLINE_VIEW") with IssueTemplate {
|
||||
def commentCount = column[Int]("COMMENT_COUNT")
|
||||
def * = userName ~ repositoryName ~ issueId ~ commentCount
|
||||
}
|
||||
|
||||
object Issues extends Table[Issue]("ISSUE") with IssueTemplate with MilestoneTemplate {
|
||||
def openedUserName = column[String]("OPENED_USER_NAME")
|
||||
def assignedUserName = column[String]("ASSIGNED_USER_NAME")
|
||||
|
||||
@@ -7,7 +7,7 @@ import Q.interpolation
|
||||
|
||||
import model._
|
||||
import util.Implicits._
|
||||
import util.StringUtil
|
||||
import util.StringUtil._
|
||||
|
||||
trait IssuesService {
|
||||
import IssuesService._
|
||||
@@ -99,47 +99,38 @@ trait IssuesService {
|
||||
def searchIssue(owner: String, repository: String, condition: IssueSearchCondition,
|
||||
filter: String, userName: Option[String], offset: Int, limit: Int): List[(Issue, List[Label], Int)] = {
|
||||
|
||||
// get issues and comment count
|
||||
val issues = searchIssueQuery(owner, repository, condition, filter, userName)
|
||||
.leftJoin(Query(IssueComments)
|
||||
.filter { t =>
|
||||
(t.byRepository(owner, repository)) &&
|
||||
(t.action inSetBind Seq("comment", "close_comment", "reopen_comment"))
|
||||
// get issues and comment count and labels
|
||||
searchIssueQuery(owner, repository, condition, filter, userName)
|
||||
.innerJoin(IssueOutline).on { (t1, t2) => t1.byIssue(t2.userName, t2.repositoryName, t2.issueId) }
|
||||
.leftJoin (IssueLabels) .on { case ((t1, t2), t3) => t1.byIssue(t3.userName, t3.repositoryName, t3.issueId) }
|
||||
.leftJoin (Labels) .on { case (((t1, t2), t3), t4) => t3.byLabel(t4.userName, t4.repositoryName, t4.labelId) }
|
||||
.map { case (((t1, t2), t3), t4) =>
|
||||
(t1, t2.commentCount, t4.labelId.?, t4.labelName.?, t4.color.?)
|
||||
}
|
||||
.groupBy { _.issueId }
|
||||
.map { case (issueId, t) => issueId ~ t.length }).on((t1, t2) => t1.issueId is t2._1)
|
||||
.sortBy { case (t1, t2) =>
|
||||
(condition.sort match {
|
||||
case "created" => t1.registeredDate
|
||||
case "comments" => t2._2
|
||||
case "updated" => t1.updatedDate
|
||||
}) match {
|
||||
case sort => condition.direction match {
|
||||
case "asc" => sort asc
|
||||
case "desc" => sort desc
|
||||
.sortBy(_._4) // labelName
|
||||
.sortBy { case (t1, commentCount, _,_,_) =>
|
||||
(condition.sort match {
|
||||
case "created" => t1.registeredDate
|
||||
case "comments" => commentCount
|
||||
case "updated" => t1.updatedDate
|
||||
}) match {
|
||||
case sort => condition.direction match {
|
||||
case "asc" => sort asc
|
||||
case "desc" => sort desc
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.map { case (t1, t2) => (t1, t2._2.ifNull(0)) }
|
||||
.drop(offset).take(limit)
|
||||
.list
|
||||
|
||||
// get labels
|
||||
val labels = Query(IssueLabels)
|
||||
.innerJoin(Labels).on { (t1, t2) =>
|
||||
t1.byLabel(t2.userName, t2.repositoryName, t2.labelId)
|
||||
}
|
||||
.filter { case (t1, t2) =>
|
||||
(t1.byRepository(owner, repository)) &&
|
||||
(t1.issueId inSetBind (issues.map(_._1.issueId)))
|
||||
}
|
||||
.sortBy { case (t1, t2) => t1.issueId ~ t2.labelName }
|
||||
.map { case (t1, t2) => (t1.issueId, t2) }
|
||||
.list
|
||||
|
||||
issues.map { case (issue, commentCount) =>
|
||||
(issue, labels.collect { case (issueId, labels) if(issueId == issue.issueId) => labels }, commentCount)
|
||||
}
|
||||
.drop(offset).take(limit)
|
||||
.list
|
||||
.splitWith(_._1.issueId == _._1.issueId)
|
||||
.map { issues => issues.head match {
|
||||
case (issue, commentCount, _,_,_) =>
|
||||
(issue,
|
||||
issues.flatMap { t => t._3.map (
|
||||
Label(issue.userName, issue.repositoryName, _, t._4.get, t._5.get)
|
||||
)} toList,
|
||||
commentCount)
|
||||
}} toList
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,42 +238,47 @@ trait IssuesService {
|
||||
*/
|
||||
def searchIssuesByKeyword(owner: String, repository: String, query: String): List[(Issue, Int, String)] = {
|
||||
import scala.slick.driver.H2Driver.likeEncode
|
||||
val keywords = StringUtil.splitWords(query.toLowerCase)
|
||||
val keywords = splitWords(query.toLowerCase)
|
||||
|
||||
// Search Issue
|
||||
val issues = Query(Issues).filter { t =>
|
||||
keywords.map { keyword =>
|
||||
(t.title.toLowerCase like (s"%${likeEncode(keyword)}%", '^')) ||
|
||||
(t.content.toLowerCase like (s"%${likeEncode(keyword)}%", '^'))
|
||||
} .reduceLeft(_ && _)
|
||||
}.map { t => (t, 0, t.content.?) }
|
||||
val issues = Issues
|
||||
.innerJoin(IssueOutline).on { case (t1, t2) =>
|
||||
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
|
||||
}
|
||||
.filter { case (t1, t2) =>
|
||||
keywords.map { keyword =>
|
||||
(t1.title.toLowerCase like (s"%${likeEncode(keyword)}%", '^')) ||
|
||||
(t1.content.toLowerCase like (s"%${likeEncode(keyword)}%", '^'))
|
||||
} .reduceLeft(_ && _)
|
||||
}
|
||||
.map { case (t1, t2) =>
|
||||
(t1, 0, t1.content.?, t2.commentCount)
|
||||
}
|
||||
|
||||
// Search IssueComment
|
||||
val comments = Query(IssueComments).innerJoin(Issues).on { case (t1, t2) =>
|
||||
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
|
||||
}.filter { case (t1, t2) =>
|
||||
keywords.map { query =>
|
||||
t1.content.toLowerCase like (s"%${likeEncode(query)}%", '^')
|
||||
}.reduceLeft(_ && _)
|
||||
}.map { case (t1, t2) => (t2, t1.commentId, t1.content.?) }
|
||||
val comments = IssueComments
|
||||
.innerJoin(Issues).on { case (t1, t2) =>
|
||||
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
|
||||
}
|
||||
.innerJoin(IssueOutline).on { case ((t1, t2), t3) =>
|
||||
t2.byIssue(t3.userName, t3.repositoryName, t3.issueId)
|
||||
}
|
||||
.filter { case ((t1, t2), t3) =>
|
||||
keywords.map { query =>
|
||||
t1.content.toLowerCase like (s"%${likeEncode(query)}%", '^')
|
||||
}.reduceLeft(_ && _)
|
||||
}
|
||||
.map { case ((t1, t2), t3) =>
|
||||
(t2, t1.commentId, t1.content.?, t3.commentCount)
|
||||
}
|
||||
|
||||
def getCommentCount(issue: Issue): Int = {
|
||||
Query(IssueComments)
|
||||
.filter { t =>
|
||||
t.byIssue(issue.userName, issue.repositoryName, issue.issueId) &&
|
||||
(t.action inSetBind Seq("comment", "close_comment", "reopen_comment"))
|
||||
}
|
||||
.map(_.issueId)
|
||||
.list.length
|
||||
}
|
||||
|
||||
issues.union(comments).sortBy { case (issue, commentId, _) =>
|
||||
issues.union(comments).sortBy { case (issue, commentId, _, _) =>
|
||||
issue.issueId ~ commentId
|
||||
}.list.splitWith { case ((issue1, _, _), (issue2, _, _)) =>
|
||||
}.list.splitWith { case ((issue1, _, _, _), (issue2, _, _, _)) =>
|
||||
issue1.issueId == issue2.issueId
|
||||
}.map { result =>
|
||||
val (issue, _, content) = result.head
|
||||
(issue, getCommentCount(issue) , content.getOrElse(""))
|
||||
}.map { _.head match {
|
||||
case (issue, _, content, commentCount) => (issue, commentCount, content.getOrElse(""))
|
||||
}
|
||||
}.toList
|
||||
}
|
||||
|
||||
@@ -290,13 +286,13 @@ trait IssuesService {
|
||||
|
||||
object IssuesService {
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import util.StringUtil._
|
||||
|
||||
val IssueLimit = 30
|
||||
|
||||
case class IssueSearchCondition(
|
||||
labels: Set[String] = Set.empty,
|
||||
milestoneId: Option[Option[Int]] = None,
|
||||
repo: Option[String] = None,
|
||||
state: String = "open",
|
||||
sort: String = "created",
|
||||
direction: String = "desc"){
|
||||
@@ -308,6 +304,7 @@ object IssuesService {
|
||||
case Some(x) => x.toString
|
||||
case None => "none"
|
||||
})},
|
||||
repo.map("for=" + urlEncode(_)),
|
||||
Some("state=" + urlEncode(state)),
|
||||
Some("sort=" + urlEncode(sort)),
|
||||
Some("direction=" + urlEncode(direction))).flatten.mkString("&")
|
||||
@@ -328,6 +325,7 @@ object IssuesService {
|
||||
case "none" => None
|
||||
case x => Some(x.toInt)
|
||||
}),
|
||||
param(request, "for"),
|
||||
param(request, "state", Seq("open", "closed")).getOrElse("open"),
|
||||
param(request, "sort", Seq("created", "comments", "updated")).getOrElse("created"),
|
||||
param(request, "direction", Seq("asc", "desc")).getOrElse("desc"))
|
||||
|
||||
@@ -29,20 +29,24 @@ trait RepositorySearchService { self: IssuesService =>
|
||||
|
||||
def countFiles(owner: String, repository: String, query: String): Int =
|
||||
JGitUtil.withGit(getRepositoryDir(owner, repository)){ git =>
|
||||
searchRepositoryFiles(git, query).length
|
||||
if(JGitUtil.isEmpty(git)) 0 else searchRepositoryFiles(git, query).length
|
||||
}
|
||||
|
||||
def searchFiles(owner: String, repository: String, query: String): List[FileSearchResult] =
|
||||
JGitUtil.withGit(getRepositoryDir(owner, repository)){ git =>
|
||||
val files = searchRepositoryFiles(git, query)
|
||||
val commits = JGitUtil.getLatestCommitFromPaths(git, files.toList.map(_._1), "HEAD")
|
||||
files.map { case (path, text) =>
|
||||
val (highlightText, lineNumber) = getHighlightText(text, query)
|
||||
FileSearchResult(
|
||||
path,
|
||||
commits(path).getCommitterIdent.getWhen,
|
||||
highlightText,
|
||||
lineNumber)
|
||||
if(JGitUtil.isEmpty(git)){
|
||||
Nil
|
||||
} else {
|
||||
val files = searchRepositoryFiles(git, query)
|
||||
val commits = JGitUtil.getLatestCommitFromPaths(git, files.toList.map(_._1), "HEAD")
|
||||
files.map { case (path, text) =>
|
||||
val (highlightText, lineNumber) = getHighlightText(text, query)
|
||||
FileSearchResult(
|
||||
path,
|
||||
commits(path).getCommitterIdent.getWhen,
|
||||
highlightText,
|
||||
lineNumber)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,4 +122,4 @@ object RepositorySearchService {
|
||||
highlightText: String,
|
||||
highlightLineNumber: Int)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,14 +53,11 @@ trait WikiService {
|
||||
*/
|
||||
def getWikiPage(owner: String, repository: String, pageName: String): Option[WikiPageInfo] = {
|
||||
JGitUtil.withGit(Directory.getWikiRepositoryDir(owner, repository)){ git =>
|
||||
try {
|
||||
if(!JGitUtil.isEmpty(git)){
|
||||
JGitUtil.getFileList(git, "master", ".").find(_.name == pageName + ".md").map { file =>
|
||||
WikiPageInfo(file.name, new String(git.getRepository.open(file.id).getBytes, "UTF-8"), file.committer, file.time)
|
||||
}
|
||||
} catch {
|
||||
// TODO no commit, but it should not judge by exception.
|
||||
case e: NullPointerException => None
|
||||
}
|
||||
} else None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +66,7 @@ trait WikiService {
|
||||
*/
|
||||
def getFileContent(owner: String, repository: String, path: String): Option[Array[Byte]] = {
|
||||
JGitUtil.withGit(Directory.getWikiRepositoryDir(owner, repository)){ git =>
|
||||
try {
|
||||
if(!JGitUtil.isEmpty(git)){
|
||||
val index = path.lastIndexOf('/')
|
||||
val parentPath = if(index < 0) "." else path.substring(0, index)
|
||||
val fileName = if(index < 0) path else path.substring(index + 1)
|
||||
@@ -77,10 +74,7 @@ trait WikiService {
|
||||
JGitUtil.getFileList(git, "master", parentPath).find(_.name == fileName).map { file =>
|
||||
git.getRepository.open(file.id).getBytes
|
||||
}
|
||||
} catch {
|
||||
// TODO no commit, but it should not judge by exception.
|
||||
case e: NullPointerException => None
|
||||
}
|
||||
} else None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -537,6 +537,8 @@ object JGitUtil {
|
||||
}
|
||||
}
|
||||
|
||||
def isEmpty(git: Git): Boolean = git.getRepository.resolve(Constants.HEAD) == null
|
||||
|
||||
private def setReceivePack(repository: org.eclipse.jgit.lib.Repository): Unit = {
|
||||
val config = repository.getConfig
|
||||
config.setBoolean("http", null, "receivepack", true)
|
||||
@@ -553,4 +555,4 @@ object JGitUtil {
|
||||
}.find(_._1 != null)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user