Priorities now integrated with issues.

This commit is contained in:
Istvan Meszaros
2017-06-06 22:43:33 +02:00
parent c908b5e642
commit 889e94a494
21 changed files with 268 additions and 37 deletions

View File

@@ -20,13 +20,13 @@ import org.scalatra.BadRequest
class AccountController extends AccountControllerBase class AccountController extends AccountControllerBase
with AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService with AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
with AccessTokenService with WebHookService with RepositoryCreationService with AccessTokenService with WebHookService with PrioritiesService with RepositoryCreationService
trait AccountControllerBase extends AccountManagementControllerBase { trait AccountControllerBase extends AccountManagementControllerBase {
self: AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService self: AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator with OneselfAuthenticator with UsersAuthenticator with GroupManagerAuthenticator with ReadableUsersAuthenticator
with AccessTokenService with WebHookService with RepositoryCreationService => with AccessTokenService with WebHookService with PrioritiesService with RepositoryCreationService =>
case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String, case class AccountNewForm(userName: String, password: String, fullName: String, mailAddress: String,
description: Option[String], url: Option[String], fileId: Option[String]) description: Option[String], url: Option[String], fileId: Option[String])

View File

@@ -33,6 +33,7 @@ class ApiController extends ApiControllerBase
with WebHookIssueCommentService with WebHookIssueCommentService
with WikiService with WikiService
with ActivityService with ActivityService
with PrioritiesService
with OwnerAuthenticator with OwnerAuthenticator
with UsersAuthenticator with UsersAuthenticator
with GroupManagerAuthenticator with GroupManagerAuthenticator
@@ -52,6 +53,7 @@ trait ApiControllerBase extends ControllerBase {
with RepositoryCreationService with RepositoryCreationService
with IssueCreationService with IssueCreationService
with HandleCommentService with HandleCommentService
with PrioritiesService
with OwnerAuthenticator with OwnerAuthenticator
with UsersAuthenticator with UsersAuthenticator
with GroupManagerAuthenticator with GroupManagerAuthenticator
@@ -365,6 +367,7 @@ trait ApiControllerBase extends ControllerBase {
data.body, data.body,
data.assignees.headOption, data.assignees.headOption,
milestone.map(_.milestoneId), milestone.map(_.milestoneId),
None,
data.labels, data.labels,
loginAccount) loginAccount)
JsonFormat(ApiIssue(issue, RepositoryName(repository), ApiUser(loginAccount))) JsonFormat(ApiIssue(issue, RepositoryName(repository), ApiUser(loginAccount)))

View File

@@ -27,6 +27,7 @@ class IssuesController extends IssuesControllerBase
with PullRequestService with PullRequestService
with WebHookIssueCommentService with WebHookIssueCommentService
with CommitsService with CommitsService
with PrioritiesService
trait IssuesControllerBase extends ControllerBase { trait IssuesControllerBase extends ControllerBase {
self: IssuesService self: IssuesService
@@ -41,10 +42,11 @@ trait IssuesControllerBase extends ControllerBase {
with ReferrerAuthenticator with ReferrerAuthenticator
with WritableUsersAuthenticator with WritableUsersAuthenticator
with PullRequestService with PullRequestService
with WebHookIssueCommentService => with WebHookIssueCommentService
with PrioritiesService =>
case class IssueCreateForm(title: String, content: Option[String], case class IssueCreateForm(title: String, content: Option[String],
assignedUserName: Option[String], milestoneId: Option[Int], labelNames: Option[String]) assignedUserName: Option[String], milestoneId: Option[Int], priorityId: Option[Int], labelNames: Option[String])
case class CommentForm(issueId: Int, content: String) case class CommentForm(issueId: Int, content: String)
case class IssueStateForm(issueId: Int, content: Option[String]) case class IssueStateForm(issueId: Int, content: Option[String])
@@ -53,6 +55,7 @@ trait IssuesControllerBase extends ControllerBase {
"content" -> trim(optional(text())), "content" -> trim(optional(text())),
"assignedUserName" -> trim(optional(text())), "assignedUserName" -> trim(optional(text())),
"milestoneId" -> trim(optional(number())), "milestoneId" -> trim(optional(number())),
"priorityId" -> trim(optional(number())),
"labelNames" -> trim(optional(text())) "labelNames" -> trim(optional(text()))
)(IssueCreateForm.apply) )(IssueCreateForm.apply)
@@ -94,6 +97,7 @@ trait IssuesControllerBase extends ControllerBase {
getIssueLabels(owner, name, issueId.toInt), getIssueLabels(owner, name, issueId.toInt),
getAssignableUserNames(owner, name), getAssignableUserNames(owner, name),
getMilestonesWithIssueCount(owner, name), getMilestonesWithIssueCount(owner, name),
getPriorities(owner, name),
getLabels(owner, name), getLabels(owner, name),
isIssueEditable(repository), isIssueEditable(repository),
isIssueManageable(repository), isIssueManageable(repository),
@@ -109,6 +113,7 @@ trait IssuesControllerBase extends ControllerBase {
html.create( html.create(
getAssignableUserNames(owner, name), getAssignableUserNames(owner, name),
getMilestones(owner, name), getMilestones(owner, name),
getPriorities(owner, name),
getLabels(owner, name), getLabels(owner, name),
isIssueManageable(repository), isIssueManageable(repository),
getContentTemplate(repository, "ISSUE_TEMPLATE"), getContentTemplate(repository, "ISSUE_TEMPLATE"),
@@ -125,6 +130,7 @@ trait IssuesControllerBase extends ControllerBase {
form.content, form.content,
form.assignedUserName, form.assignedUserName,
form.milestoneId, form.milestoneId,
form.priorityId,
form.labelNames.toArray.flatMap(_.split(",")), form.labelNames.toArray.flatMap(_.split(",")),
context.loginAccount.get) context.loginAccount.get)
@@ -291,6 +297,11 @@ trait IssuesControllerBase extends ControllerBase {
} getOrElse Ok() } getOrElse Ok()
}) })
ajaxPost("/:owner/:repository/issues/:id/priority")(writableUsersOnly { repository =>
updatePriorityId(repository.owner, repository.name, params("id").toInt, priorityId("priorityId"))
Ok("updated")
})
post("/:owner/:repository/issues/batchedit/state")(writableUsersOnly { repository => post("/:owner/:repository/issues/batchedit/state")(writableUsersOnly { repository =>
defining(params.get("value")){ action => defining(params.get("value")){ action =>
action match { action match {
@@ -335,6 +346,14 @@ trait IssuesControllerBase extends ControllerBase {
} }
}) })
post("/:owner/:repository/issues/batchedit/priority")(writableUsersOnly { repository =>
defining(priorityId("value")){ value =>
executeBatch(repository) {
updatePriorityId(repository.owner, repository.name, _, value)
}
}
})
get("/:owner/:repository/_attached/:file")(referrersOnly { repository => get("/:owner/:repository/_attached/:file")(referrersOnly { repository =>
(Directory.getAttachedDir(repository.owner, repository.name) match { (Directory.getAttachedDir(repository.owner, repository.name) match {
case dir if(dir.exists && dir.isDirectory) => case dir if(dir.exists && dir.isDirectory) =>
@@ -348,6 +367,7 @@ trait IssuesControllerBase extends ControllerBase {
val assignedUserName = (key: String) => params.get(key) filter (_.trim != "") val assignedUserName = (key: String) => params.get(key) filter (_.trim != "")
val milestoneId: String => Option[Int] = (key: String) => params.get(key).flatMap(_.toIntOpt) val milestoneId: String => Option[Int] = (key: String) => params.get(key).flatMap(_.toIntOpt)
val priorityId: String => Option[Int] = (key: String) => params.get(key).flatMap(_.toIntOpt)
private def executeBatch(repository: RepositoryService.RepositoryInfo)(execute: Int => Unit) = { private def executeBatch(repository: RepositoryService.RepositoryInfo)(execute: Int => Unit) = {
params("checked").split(',') map(_.toInt) foreach execute params("checked").split(',') map(_.toInt) foreach execute
@@ -370,6 +390,7 @@ trait IssuesControllerBase extends ControllerBase {
page, page,
getAssignableUserNames(owner, repoName), getAssignableUserNames(owner, repoName),
getMilestones(owner, repoName), getMilestones(owner, repoName),
getPriorities(owner, repoName),
getLabels(owner, repoName), getLabels(owner, repoName),
countIssue(condition.copy(state = "open" ), false, owner -> repoName), countIssue(condition.copy(state = "open" ), false, owner -> repoName),
countIssue(condition.copy(state = "closed"), false, owner -> repoName), countIssue(condition.copy(state = "closed"), false, owner -> repoName),

View File

@@ -27,7 +27,7 @@ trait PrioritiesControllerBase extends ControllerBase {
get("/:owner/:repository/issues/priorities")(referrersOnly { repository => get("/:owner/:repository/issues/priorities")(referrersOnly { repository =>
html.list( html.list(
getPriorities(repository.owner, repository.name), getPriorities(repository.owner, repository.name),
Map.empty, // TODO countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
repository, repository,
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)) hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
}) })
@@ -40,7 +40,7 @@ trait PrioritiesControllerBase extends ControllerBase {
val priorityId = createPriority(repository.owner, repository.name, form.priorityName, form.color.substring(1)) val priorityId = createPriority(repository.owner, repository.name, form.priorityName, form.color.substring(1))
html.priority( html.priority(
getPriority(repository.owner, repository.name, priorityId).get, getPriority(repository.owner, repository.name, priorityId).get,
Map.empty, // TODO, countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
repository, repository,
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)) hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
}) })
@@ -55,7 +55,7 @@ trait PrioritiesControllerBase extends ControllerBase {
updatePriority(repository.owner, repository.name, params("priorityId").toInt, form.priorityName, form.color.substring(1)) updatePriority(repository.owner, repository.name, params("priorityId").toInt, form.priorityName, form.color.substring(1))
html.priority( html.priority(
getPriority(repository.owner, repository.name, params("priorityId").toInt).get, getPriority(repository.owner, repository.name, params("priorityId").toInt).get,
Map.empty, // TODO, countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
repository, repository,
hasDeveloperRole(repository.owner, repository.name, context.loginAccount)) hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
}) })

View File

@@ -24,14 +24,14 @@ class PullRequestsController extends PullRequestsControllerBase
with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with LabelsService with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with LabelsService
with CommitsService with ActivityService with WebHookPullRequestService with CommitsService with ActivityService with WebHookPullRequestService
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator
with CommitStatusService with MergeService with ProtectedBranchService with CommitStatusService with MergeService with ProtectedBranchService with PrioritiesService
trait PullRequestsControllerBase extends ControllerBase { trait PullRequestsControllerBase extends ControllerBase {
self: RepositoryService with AccountService with IssuesService with MilestonesService with LabelsService self: RepositoryService with AccountService with IssuesService with MilestonesService with LabelsService
with CommitsService with ActivityService with PullRequestService with WebHookPullRequestService with CommitsService with ActivityService with PullRequestService with WebHookPullRequestService
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator
with CommitStatusService with MergeService with ProtectedBranchService => with CommitStatusService with MergeService with ProtectedBranchService with PrioritiesService =>
val pullRequestForm = mapping( val pullRequestForm = mapping(
"title" -> trim(label("Title" , text(required, maxlength(100)))), "title" -> trim(label("Title" , text(required, maxlength(100)))),
@@ -45,6 +45,7 @@ trait PullRequestsControllerBase extends ControllerBase {
"commitIdTo" -> trim(text(required, maxlength(40))), "commitIdTo" -> trim(text(required, maxlength(40))),
"assignedUserName" -> trim(optional(text())), "assignedUserName" -> trim(optional(text())),
"milestoneId" -> trim(optional(number())), "milestoneId" -> trim(optional(number())),
"priorityId" -> trim(optional(number())),
"labelNames" -> trim(optional(text())) "labelNames" -> trim(optional(text()))
)(PullRequestForm.apply) )(PullRequestForm.apply)
@@ -64,6 +65,7 @@ trait PullRequestsControllerBase extends ControllerBase {
commitIdTo: String, commitIdTo: String,
assignedUserName: Option[String], assignedUserName: Option[String],
milestoneId: Option[Int], milestoneId: Option[Int],
priorityId: Option[Int],
labelNames: Option[String] labelNames: Option[String]
) )
@@ -93,6 +95,7 @@ trait PullRequestsControllerBase extends ControllerBase {
getIssueLabels(owner, name, issueId), getIssueLabels(owner, name, issueId),
getAssignableUserNames(owner, name), getAssignableUserNames(owner, name),
getMilestonesWithIssueCount(owner, name), getMilestonesWithIssueCount(owner, name),
getPriorities(owner, name),
getLabels(owner, name), getLabels(owner, name),
commits, commits,
diffs, diffs,
@@ -390,6 +393,7 @@ trait PullRequestsControllerBase extends ControllerBase {
hasDeveloperRole(originRepository.owner, originRepository.name, context.loginAccount), hasDeveloperRole(originRepository.owner, originRepository.name, context.loginAccount),
getAssignableUserNames(originRepository.owner, originRepository.name), getAssignableUserNames(originRepository.owner, originRepository.name),
getMilestones(originRepository.owner, originRepository.name), getMilestones(originRepository.owner, originRepository.name),
getPriorities(originRepository.owner, originRepository.name),
getLabels(originRepository.owner, originRepository.name) getLabels(originRepository.owner, originRepository.name)
) )
} }
@@ -445,6 +449,7 @@ trait PullRequestsControllerBase extends ControllerBase {
content = form.content, content = form.content,
assignedUserName = if (manageable) form.assignedUserName else None, assignedUserName = if (manageable) form.assignedUserName else None,
milestoneId = if (manageable) form.milestoneId else None, milestoneId = if (manageable) form.milestoneId else None,
priorityId = if (manageable) form.priorityId else None,
isPullRequest = true) isPullRequest = true)
createPullRequest( createPullRequest(
@@ -518,6 +523,7 @@ trait PullRequestsControllerBase extends ControllerBase {
page, page,
getAssignableUserNames(owner, repoName), getAssignableUserNames(owner, repoName),
getMilestones(owner, repoName), getMilestones(owner, repoName),
getPriorities(owner, repoName),
getLabels(owner, repoName), getLabels(owner, repoName),
countIssue(condition.copy(state = "open" ), true, owner -> repoName), countIssue(condition.copy(state = "open" ), true, owner -> repoName),
countIssue(condition.copy(state = "closed"), true, owner -> repoName), countIssue(condition.copy(state = "closed"), true, owner -> repoName),

View File

@@ -13,12 +13,13 @@ trait IssueComponent extends TemplateComponent { self: Profile =>
def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository) def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository)
} }
class IssueOutline(tag: Tag) extends Table[(String, String, Int, Int)](tag, "ISSUE_OUTLINE_VIEW") with IssueTemplate { class IssueOutline(tag: Tag) extends Table[(String, String, Int, Int, Int)](tag, "ISSUE_OUTLINE_VIEW") with IssueTemplate {
val commentCount = column[Int]("COMMENT_COUNT") val commentCount = column[Int]("COMMENT_COUNT")
def * = (userName, repositoryName, issueId, commentCount) val priority = column[Int]("PRIORITY")
def * = (userName, repositoryName, issueId, commentCount, priority)
} }
class Issues(tag: Tag) extends Table[Issue](tag, "ISSUE") with IssueTemplate with MilestoneTemplate { class Issues(tag: Tag) extends Table[Issue](tag, "ISSUE") with IssueTemplate with MilestoneTemplate with PriorityTemplate {
val openedUserName = column[String]("OPENED_USER_NAME") val openedUserName = column[String]("OPENED_USER_NAME")
val assignedUserName = column[String]("ASSIGNED_USER_NAME") val assignedUserName = column[String]("ASSIGNED_USER_NAME")
val title = column[String]("TITLE") val title = column[String]("TITLE")
@@ -27,7 +28,7 @@ trait IssueComponent extends TemplateComponent { self: Profile =>
val registeredDate = column[java.util.Date]("REGISTERED_DATE") val registeredDate = column[java.util.Date]("REGISTERED_DATE")
val updatedDate = column[java.util.Date]("UPDATED_DATE") val updatedDate = column[java.util.Date]("UPDATED_DATE")
val pullRequest = column[Boolean]("PULL_REQUEST") val pullRequest = column[Boolean]("PULL_REQUEST")
def * = (userName, repositoryName, issueId, openedUserName, milestoneId.?, assignedUserName.?, title, content.?, closed, registeredDate, updatedDate, pullRequest) <> (Issue.tupled, Issue.unapply) def * = (userName, repositoryName, issueId, openedUserName, milestoneId.?, priorityId.?, assignedUserName.?, title, content.?, closed, registeredDate, updatedDate, pullRequest) <> (Issue.tupled, Issue.unapply)
def byPrimaryKey(owner: String, repository: String, issueId: Int) = byIssue(owner, repository, issueId) def byPrimaryKey(owner: String, repository: String, issueId: Int) = byIssue(owner, repository, issueId)
} }
@@ -39,6 +40,7 @@ case class Issue(
issueId: Int, issueId: Int,
openedUserName: String, openedUserName: String,
milestoneId: Option[Int], milestoneId: Option[Int],
priorityId: Option[Int],
assignedUserName: Option[String], assignedUserName: Option[String],
title: String, title: String,
content: Option[String], content: Option[String],

View File

@@ -12,7 +12,7 @@ trait IssueCreationService {
self: RepositoryService with WebHookIssueCommentService with LabelsService with IssuesService with ActivityService => self: RepositoryService with WebHookIssueCommentService with LabelsService with IssuesService with ActivityService =>
def createIssue(repository: RepositoryInfo, title:String, body:Option[String], def createIssue(repository: RepositoryInfo, title:String, body:Option[String],
assignee: Option[String], milestoneId: Option[Int], labelNames: Seq[String], assignee: Option[String], milestoneId: Option[Int], priorityId: Option[Int], labelNames: Seq[String],
loginAccount: Account)(implicit context: Context, s: Session) : Issue = { loginAccount: Account)(implicit context: Context, s: Session) : Issue = {
val owner = repository.owner val owner = repository.owner
@@ -23,7 +23,8 @@ trait IssueCreationService {
// insert issue // insert issue
val issueId = insertIssue(owner, name, userName, title, body, val issueId = insertIssue(owner, name, userName, title, body,
if (manageable) assignee else None, if (manageable) assignee else None,
if (manageable) milestoneId else None) if (manageable) milestoneId else None,
if (manageable) priorityId else None)
val issue: Issue = getIssue(owner, name, issueId.toString).get val issue: Issue = getIssue(owner, name, issueId.toString).get
// insert labels // insert labels

View File

@@ -97,6 +97,30 @@ trait IssuesService {
.list.toMap .list.toMap
} }
/**
* Returns the Map which contains issue count for each priority.
*
* @param owner the repository owner
* @param repository the repository name
* @param condition the search condition
* @return the Map which contains issue count for each priority (key is priority name, value is issue count)
*/
def countIssueGroupByPriorities(owner: String, repository: String, condition: IssueSearchCondition,
filterUser: Map[String, String])(implicit s: Session): Map[String, Int] = {
searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), false)
.join(Priorities).on { case t1 ~ t2 =>
t1.byPriority(t2.userName, t2.repositoryName, t2.priorityId)
}
.groupBy { case t1 ~ t2 =>
t2.priorityName
}
.map { case priorityName ~ t =>
priorityName -> t.length
}
.list.toMap
}
def getCommitStatues(userName: String, repositoryName: String, issueId: Int)(implicit s: Session): Option[CommitStatusInfo] = { def getCommitStatues(userName: String, repositoryName: String, issueId: Int)(implicit s: Session): Option[CommitStatusInfo] = {
val status = PullRequests val status = PullRequests
.filter { pr => .filter { pr =>
@@ -139,18 +163,20 @@ trait IssuesService {
.joinLeft (IssueLabels) .on { case t1 ~ t2 ~ i ~ t3 => t1.byIssue(t3.userName, t3.repositoryName, t3.issueId) } .joinLeft (IssueLabels) .on { case t1 ~ t2 ~ i ~ t3 => t1.byIssue(t3.userName, t3.repositoryName, t3.issueId) }
.joinLeft (Labels) .on { case t1 ~ t2 ~ i ~ t3 ~ t4 => t3.map(_.byLabel(t4.userName, t4.repositoryName, t4.labelId)) } .joinLeft (Labels) .on { case t1 ~ t2 ~ i ~ t3 ~ t4 => t3.map(_.byLabel(t4.userName, t4.repositoryName, t4.labelId)) }
.joinLeft (Milestones) .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => t1.byMilestone(t5.userName, t5.repositoryName, t5.milestoneId) } .joinLeft (Milestones) .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => t1.byMilestone(t5.userName, t5.repositoryName, t5.milestoneId) }
.sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => i asc } .joinLeft (Priorities) .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 => t1.byPriority(t6.userName, t6.repositoryName, t6.priorityId) }
.map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 => i asc }
(t1, t2.commentCount, t4.map(_.labelId), t4.map(_.labelName), t4.map(_.color), t5.map(_.title)) .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 =>
(t1, t2.commentCount, t4.map(_.labelId), t4.map(_.labelName), t4.map(_.color), t5.map(_.title), t6.map(_.priorityName))
} }
.list .list
.splitWith { (c1, c2) => c1._1.userName == c2._1.userName && c1._1.repositoryName == c2._1.repositoryName && c1._1.issueId == c2._1.issueId } .splitWith { (c1, c2) => c1._1.userName == c2._1.userName && c1._1.repositoryName == c2._1.repositoryName && c1._1.issueId == c2._1.issueId }
result.map { issues => issues.head match { result.map { issues => issues.head match {
case (issue, commentCount, _, _, _, milestone) => case (issue, commentCount, _, _, _, milestone, priority) =>
IssueInfo(issue, IssueInfo(issue,
issues.flatMap { t => t._3.map (Label(issue.userName, issue.repositoryName, _, t._4.get, t._5.get))} toList, issues.flatMap { t => t._3.map (Label(issue.userName, issue.repositoryName, _, t._4.get, t._5.get))} toList,
milestone, milestone,
priority,
commentCount, commentCount,
getCommitStatues(issue.userName, issue.repositoryName, issue.issueId)) getCommitStatues(issue.userName, issue.repositoryName, issue.issueId))
}} toList }} toList
@@ -204,6 +230,10 @@ trait IssuesService {
case "asc" => t1.updatedDate asc case "asc" => t1.updatedDate asc
case "desc" => t1.updatedDate desc case "desc" => t1.updatedDate desc
} }
case "priority" => condition.direction match {
case "asc" => t2.priority asc
case "desc" => t2.priority desc
}
} }
} }
.drop(offset).take(limit).zipWithIndex .drop(offset).take(limit).zipWithIndex
@@ -219,6 +249,7 @@ trait IssuesService {
.foldLeft[Rep[Boolean]](false) ( _ || _ ) && .foldLeft[Rep[Boolean]](false) ( _ || _ ) &&
(t1.closed === (condition.state == "closed").bind) && (t1.closed === (condition.state == "closed").bind) &&
(t1.milestoneId.? isEmpty, condition.milestone == Some(None)) && (t1.milestoneId.? isEmpty, condition.milestone == Some(None)) &&
(t1.priorityId.? isEmpty, condition.priority == Some(None)) &&
(t1.assignedUserName.? isEmpty, condition.assigned == Some(None)) && (t1.assignedUserName.? isEmpty, condition.assigned == Some(None)) &&
(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) && (t1.openedUserName === condition.author.get.bind, condition.author.isDefined) &&
(t1.pullRequest === pullRequest.bind) && (t1.pullRequest === pullRequest.bind) &&
@@ -227,6 +258,11 @@ trait IssuesService {
(t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) && (t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) &&
(t2.title === condition.milestone.get.get.bind) (t2.title === condition.milestone.get.get.bind)
} exists, condition.milestone.flatten.isDefined) && } exists, condition.milestone.flatten.isDefined) &&
// Priority filter
(Priorities filter { t2 =>
(t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.priorityId)) &&
(t2.priorityName === condition.priority.get.get.bind)
} exists, condition.priority.flatten.isDefined) &&
// Assignee filter // Assignee filter
(t1.assignedUserName === condition.assigned.get.get.bind, condition.assigned.flatten.isDefined) && (t1.assignedUserName === condition.assigned.get.get.bind, condition.assigned.flatten.isDefined) &&
// Label filter // Label filter
@@ -253,7 +289,7 @@ trait IssuesService {
} }
def insertIssue(owner: String, repository: String, loginUser: String, title: String, content: Option[String], def insertIssue(owner: String, repository: String, loginUser: String, title: String, content: Option[String],
assignedUserName: Option[String], milestoneId: Option[Int], assignedUserName: Option[String], milestoneId: Option[Int], priorityId: Option[Int],
isPullRequest: Boolean = false)(implicit s: Session): Int = { isPullRequest: Boolean = false)(implicit s: Session): Int = {
// next id number // next id number
sql"SELECT ISSUE_ID + 1 FROM ISSUE_ID WHERE USER_NAME = $owner AND REPOSITORY_NAME = $repository FOR UPDATE".as[Int] sql"SELECT ISSUE_ID + 1 FROM ISSUE_ID WHERE USER_NAME = $owner AND REPOSITORY_NAME = $repository FOR UPDATE".as[Int]
@@ -264,6 +300,7 @@ trait IssuesService {
id, id,
loginUser, loginUser,
milestoneId, milestoneId,
priorityId,
assignedUserName, assignedUserName,
title, title,
content, content,
@@ -316,6 +353,10 @@ trait IssuesService {
Issues.filter (_.byPrimaryKey(owner, repository, issueId)).map(_.milestoneId?).update (milestoneId) Issues.filter (_.byPrimaryKey(owner, repository, issueId)).map(_.milestoneId?).update (milestoneId)
} }
def updatePriorityId(owner: String, repository: String, issueId: Int, priorityId: Option[Int])(implicit s: Session): Int = {
Issues.filter (_.byPrimaryKey(owner, repository, issueId)).map(_.priorityId?).update (priorityId)
}
def updateComment(commentId: Int, content: String)(implicit s: Session): Int = { def updateComment(commentId: Int, content: String)(implicit s: Session): Int = {
IssueComments.filter (_.byPrimaryKey(commentId)).map(t => (t.content, t.updatedDate)).update(content, currentDate) IssueComments.filter (_.byPrimaryKey(commentId)).map(t => (t.content, t.updatedDate)).update(content, currentDate)
} }
@@ -430,6 +471,7 @@ object IssuesService {
case class IssueSearchCondition( case class IssueSearchCondition(
labels: Set[String] = Set.empty, labels: Set[String] = Set.empty,
milestone: Option[Option[String]] = None, milestone: Option[Option[String]] = None,
priority: Option[Option[String]] = None,
author: Option[String] = None, author: Option[String] = None,
assigned: Option[Option[String]] = None, assigned: Option[Option[String]] = None,
mentioned: Option[String] = None, mentioned: Option[String] = None,
@@ -459,6 +501,10 @@ object IssuesService {
case Some(x) => s"milestone:${x}" case Some(x) => s"milestone:${x}"
case None => "no:milestone" case None => "no:milestone"
}}, }},
priority.map { _ match {
case Some(x) => s"priority:${x}"
case None => "no:priority"
}},
(sort, direction) match { (sort, direction) match {
case ("created" , "desc") => None case ("created" , "desc") => None
case ("created" , "asc" ) => Some("sort:created-asc") case ("created" , "asc" ) => Some("sort:created-asc")
@@ -466,6 +512,8 @@ object IssuesService {
case ("comments", "asc" ) => Some("sort:comments-asc") case ("comments", "asc" ) => Some("sort:comments-asc")
case ("updated" , "desc") => Some("sort:updated-desc") case ("updated" , "desc") => Some("sort:updated-desc")
case ("updated" , "asc" ) => Some("sort:updated-asc") case ("updated" , "asc" ) => Some("sort:updated-asc")
case ("priority", "desc") => Some("sort:priority-desc")
case ("priority", "asc" ) => Some("sort:priority-asc")
case x => throw new MatchError(x) case x => throw new MatchError(x)
}, },
visibility.map(visibility => s"visibility:${visibility}") visibility.map(visibility => s"visibility:${visibility}")
@@ -480,6 +528,10 @@ object IssuesService {
case Some(x) => "milestone=" + urlEncode(x) case Some(x) => "milestone=" + urlEncode(x)
case None => "milestone=none" case None => "milestone=none"
}, },
priority.map {
case Some(x) => "priority=" + urlEncode(x)
case None => "priority=none"
},
author .map(x => "author=" + urlEncode(x)), author .map(x => "author=" + urlEncode(x)),
assigned.map { assigned.map {
case Some(x) => "assigned=" + urlEncode(x) case Some(x) => "assigned=" + urlEncode(x)
@@ -512,6 +564,10 @@ object IssuesService {
case "none" => None case "none" => None
case x => Some(x) case x => Some(x)
}, },
param(request, "priority").map {
case "none" => None
case x => Some(x)
},
param(request, "author"), param(request, "author"),
param(request, "assigned").map { param(request, "assigned").map {
case "none" => None case "none" => None
@@ -519,7 +575,7 @@ object IssuesService {
}, },
param(request, "mentioned"), param(request, "mentioned"),
param(request, "state", Seq("open", "closed")).getOrElse("open"), param(request, "state", Seq("open", "closed")).getOrElse("open"),
param(request, "sort", Seq("created", "comments", "updated")).getOrElse("created"), param(request, "sort", Seq("created", "comments", "updated", "priority")).getOrElse("created"),
param(request, "direction", Seq("asc", "desc")).getOrElse("desc"), param(request, "direction", Seq("asc", "desc")).getOrElse("desc"),
param(request, "visibility"), param(request, "visibility"),
param(request, "groups").map(_.split(",").toSet).getOrElse(Set.empty) param(request, "groups").map(_.split(",").toSet).getOrElse(Set.empty)
@@ -535,6 +591,6 @@ object IssuesService {
case class CommitStatusInfo(count: Int, successCount: Int, context: Option[String], state: Option[CommitState], targetUrl: Option[String], description: Option[String]) case class CommitStatusInfo(count: Int, successCount: Int, context: Option[String], state: Option[CommitState], targetUrl: Option[String], description: Option[String])
case class IssueInfo(issue: Issue, labels: List[Label], milestone: Option[String], commentCount: Int, status:Option[CommitStatusInfo]) case class IssueInfo(issue: Issue, labels: List[Label], milestone: Option[String], priority: Option[String], commentCount: Int, status:Option[CommitStatusInfo])
} }

View File

@@ -50,7 +50,11 @@ trait PrioritiesService {
} }
def deletePriority(owner: String, repository: String, priorityId: Int)(implicit s: Session): Unit = { def deletePriority(owner: String, repository: String, priorityId: Int)(implicit s: Session): Unit = {
// TODO update affected issues Issues.filter(_.byRepository(owner, repository))
.filter(_.priorityId === priorityId)
.map(_.priorityId?)
.update(None)
Priorities.filter(_.byPrimaryKey(owner, repository, priorityId)).delete Priorities.filter(_.byPrimaryKey(owner, repository, priorityId)).delete
} }
} }

View File

@@ -10,7 +10,7 @@ import org.eclipse.jgit.dircache.DirCache
import org.eclipse.jgit.lib.{FileMode, Constants} import org.eclipse.jgit.lib.{FileMode, Constants}
trait RepositoryCreationService { trait RepositoryCreationService {
self: AccountService with RepositoryService with LabelsService with WikiService with ActivityService => self: AccountService with RepositoryService with LabelsService with WikiService with ActivityService with PrioritiesService =>
def createRepository(loginAccount: Account, owner: String, name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean) def createRepository(loginAccount: Account, owner: String, name: String, description: Option[String], isPrivate: Boolean, createReadme: Boolean)
(implicit s: Session) { (implicit s: Session) {
@@ -30,6 +30,9 @@ trait RepositoryCreationService {
// Insert default labels // Insert default labels
insertDefaultLabels(owner, name) insertDefaultLabels(owner, name)
// Insert default priorities
insertDefaultPriorities(owner, name)
// Create the actual repository // Create the actual repository
val gitdir = getRepositoryDir(owner, name) val gitdir = getRepositoryDir(owner, name)
JGitUtil.initRepository(gitdir) JGitUtil.initRepository(gitdir)
@@ -74,5 +77,9 @@ trait RepositoryCreationService {
createLabel(userName, repositoryName, "wontfix", "ffffff") createLabel(userName, repositoryName, "wontfix", "ffffff")
} }
def insertDefaultPriorities(userName: String, repositoryName: String)(implicit s: Session): Unit = {
createPriority(userName, repositoryName, "high", "fc2929")
createPriority(userName, repositoryName, "medium", "fcc629")
createPriority(userName, repositoryName, "low", "acacac")
}
} }

View File

@@ -17,7 +17,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) => @issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) =>
<tr> <tr>
<td style="padding-top: 12px; padding-bottom: 12px;"> <td style="padding-top: 12px; padding-bottom: 12px;">
<a href="@context.path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a>&nbsp;&#xFF65; <a href="@context.path/@issue.userName/@issue.repositoryName">@issue.userName/@issue.repositoryName</a>&nbsp;&#xFF65;

View File

@@ -1,5 +1,6 @@
@(collaborators: List[String], @(collaborators: List[String],
milestones: List[gitbucket.core.model.Milestone], milestones: List[gitbucket.core.model.Milestone],
priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label], labels: List[gitbucket.core.model.Label],
isManageable: Boolean, isManageable: Boolean,
content: String, content: String,
@@ -29,7 +30,7 @@
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
@gitbucket.core.issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map(x => (x, 0, 0)), labels, isManageable, repository) @gitbucket.core.issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map(x => (x, 0, 0)), priorities, labels, isManageable, repository)
</div> </div>
</div> </div>
</form> </form>

View File

@@ -3,6 +3,7 @@
issueLabels: List[gitbucket.core.model.Label], issueLabels: List[gitbucket.core.model.Label],
collaborators: List[String], collaborators: List[String],
milestones: List[(gitbucket.core.model.Milestone, Int, Int)], milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label], labels: List[gitbucket.core.model.Label],
isEditable: Boolean, isEditable: Boolean,
isManageable: Boolean, isManageable: Boolean,
@@ -54,7 +55,7 @@
@gitbucket.core.issues.html.commentform(issue, true, isEditable, isManageable, repository) @gitbucket.core.issues.html.commentform(issue, true, isEditable, isManageable, repository)
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
@gitbucket.core.issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, isManageable, repository) @gitbucket.core.issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, priorities, labels, isManageable, repository)
</div> </div>
</div> </div>
} }

View File

@@ -3,6 +3,7 @@
issueLabels: List[gitbucket.core.model.Label], issueLabels: List[gitbucket.core.model.Label],
collaborators: List[String], collaborators: List[String],
milestones: List[(gitbucket.core.model.Milestone, Int, Int)], milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label], labels: List[gitbucket.core.model.Label],
isManageable: Boolean, isManageable: Boolean,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context) repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
@@ -32,6 +33,40 @@
@gitbucket.core.issues.html.labellist(issueLabels) @gitbucket.core.issues.html.labellist(issueLabels)
</ul> </ul>
<hr/> <hr/>
<div style="margin-bottom: 14px;">
<span class="muted small strong">Priority</span>
@if(isManageable){
<div class="pull-right">
@gitbucket.core.helper.html.dropdown("Edit", right = true, filter = ("priority", "Filter Priority")) {
<li><a href="javascript:void(0);" class="priority" data-id=""><i class="octicon octicon-x"></i> Clear priority</a></li>
@priorities.map { priority =>
<li>
<a href="javascript:void(0);" class="priority" data-id="@priority.priorityId" data-name="@priority.priorityName" data-color="#@priority.color" data-font-color="#@priority.fontColor">
@gitbucket.core.helper.html.checkicon(issue.flatMap(is => is.priorityId).map(id => id == priority.priorityId).getOrElse(false))
<span class="label" style="background-color: #@priority.color;">&nbsp;</span>
@priority.priorityName
</a>
</li>
}
}
</div>
}
</div>
<span id="label-priority">
@issue.flatMap(_.priorityId).map { priorityId =>
@priorities.collect { case priority if(priority.priorityId == priorityId) =>
<a class="issue-priority" style="background-color: #@priority.color; color: #@priority.fontColor;" href="@helpers.url(repository)/issues?priority=@helpers.urlEncode(priority.priorityName)&state=open">@priority.priorityName</a>
}
}.getOrElse {
<span class="muted small">No priority</span>
}
</span>
@if(issue.isEmpty){
<input type="hidden" name="priorityId" value=""/>
}
<hr/>
<div style="margin-bottom: 14px;"> <div style="margin-bottom: 14px;">
<span class="muted small strong">Milestone</span> <span class="muted small strong">Milestone</span>
@if(isManageable){ @if(isManageable){
@@ -152,6 +187,19 @@ $(function(){
); );
}); });
$('a.priority').click(function(){
var priorityName = $(this).data('name');
var priorityId = $(this).data('id');
var color = $(this).data('color');
var fontColor = $(this).data('font-color');
$.post('@helpers.url(repository)/issues/@issue.issueId/priority',
{ priorityId: priorityId },
function(data){
displayPriority(priorityName, priorityId, color, fontColor);
}
);
});
$('a.assign').click(function(){ $('a.assign').click(function(){
var $this = $(this); var $this = $(this);
var userName = $this.data('name'); var userName = $this.data('name');
@@ -188,6 +236,15 @@ $(function(){
$('input[name=milestoneId]').val(milestoneId); $('input[name=milestoneId]').val(milestoneId);
}); });
$('a.priority').click(function(){
var priorityName = $(this).data('name');
var priorityId = $(this).data('id');
var color = $(this).data('color');
var fontColor = $(this).data('font-color');
displayPriority(priorityName, priorityId, color, fontColor);
$('input[name=priorityId]').val(priorityId);
});
$('a.assign').click(function(){ $('a.assign').click(function(){
var $this = $(this); var $this = $(this);
var userName = $this.data('name'); var userName = $this.data('name');
@@ -222,6 +279,22 @@ $(function(){
} }
} }
function displayPriority(priorityName, priorityId, color, fontColor){
$('a.priority i.octicon-check').removeClass('octicon-check');
if(priorityId == ''){
$('#label-priority').html($('<span class="muted small">').text('No priority'));
} else {
$('#label-priority').html($('<a class="issue-priority">').text(priorityName)
.attr('href', '@helpers.url(repository)/issues?priority=' + encodeURIComponent(priorityName) + '&state=open')
.css({
"background-color": color,
"color": fontColor
}));
$('a.priority[data-id=' + priorityId + '] i').addClass('octicon-check');
}
}
function displayAssignee($this, userName){ function displayAssignee($this, userName){
$('a.assign i.octicon-check').removeClass('octicon-check'); $('a.assign i.octicon-check').removeClass('octicon-check');
if(userName == ''){ if(userName == ''){

View File

@@ -3,6 +3,7 @@
page: Int, page: Int,
collaborators: List[String], collaborators: List[String],
milestones: List[gitbucket.core.model.Milestone], milestones: List[gitbucket.core.model.Milestone],
priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label], labels: List[gitbucket.core.model.Label],
openCount: Int, openCount: Int,
closedCount: Int, closedCount: Int,
@@ -38,7 +39,7 @@
} }
} }
</form> </form>
@gitbucket.core.issues.html.listparts(target, issues, page, openCount, closedCount, condition, collaborators, milestones, labels, Some(repository), isManageable) @gitbucket.core.issues.html.listparts(target, issues, page, openCount, closedCount, condition, collaborators, milestones, priorities, labels, Some(repository), isManageable)
@if(isManageable){ @if(isManageable){
<form id="batcheditForm" method="POST"> <form id="batcheditForm" method="POST">
<input type="hidden" name="value"/> <input type="hidden" name="value"/>
@@ -119,6 +120,9 @@ $(function(){
$('a.toggle-milestone').click(function(){ $('a.toggle-milestone').click(function(){
submitBatchEdit('@helpers.url(repository)/issues/batchedit/milestone', $(this).data('id')); submitBatchEdit('@helpers.url(repository)/issues/batchedit/milestone', $(this).data('id'));
}); });
$('a.toggle-priority').click(function(){
submitBatchEdit('@helpers.url(repository)/issues/batchedit/priority', $(this).data('id'));
});
}); });
</script> </script>
} }

View File

@@ -6,6 +6,7 @@
condition: gitbucket.core.service.IssuesService.IssueSearchCondition, condition: gitbucket.core.service.IssuesService.IssueSearchCondition,
collaborators: List[String] = Nil, collaborators: List[String] = Nil,
milestones: List[gitbucket.core.model.Milestone] = Nil, milestones: List[gitbucket.core.model.Milestone] = Nil,
priorities: List[gitbucket.core.model.Priority] = Nil,
labels: List[gitbucket.core.model.Label] = Nil, labels: List[gitbucket.core.model.Label] = Nil,
repository: Option[gitbucket.core.service.RepositoryService.RepositoryInfo] = None, repository: Option[gitbucket.core.service.RepositoryService.RepositoryInfo] = None,
isManageable: Boolean = false)(implicit context: gitbucket.core.controller.Context) isManageable: Boolean = false)(implicit context: gitbucket.core.controller.Context)
@@ -48,6 +49,22 @@
</li> </li>
} }
} }
@gitbucket.core.helper.html.dropdown("Priority", filter = ("priority", "Find Priority...")) {
<li>
<a href="@condition.copy(priority = (if(condition.priority == Some(None)) None else Some(None))).toURL">
@gitbucket.core.helper.html.checkicon(condition.priority == Some(None)) Issues with no priority
</a>
</li>
@priorities.map { priority =>
<li>
<a href="@condition.copy(priority = (if(condition.priority == Some(Some(priority.priorityName))) None else Some(Some(priority.priorityName)))).toURL">
@gitbucket.core.helper.html.checkicon(condition.priority == Some(Some(priority.priorityName)))
<span style="background-color: #@priority.color;" class="label-color">&nbsp;&nbsp;</span>
@priority.priorityName
</a>
</li>
}
}
@gitbucket.core.helper.html.dropdown("Milestone", filter = ("milestone", "Find Milestone...")) { @gitbucket.core.helper.html.dropdown("Milestone", filter = ("milestone", "Find Milestone...")) {
<li> <li>
<a href="@condition.copy(milestone = (if(condition.milestone == Some(None)) None else Some(None))).toURL"> <a href="@condition.copy(milestone = (if(condition.milestone == Some(None)) None else Some(None))).toURL">
@@ -88,6 +105,16 @@
@gitbucket.core.helper.html.checkicon(condition.sort == "created" && condition.direction == "asc") Oldest @gitbucket.core.helper.html.checkicon(condition.sort == "created" && condition.direction == "asc") Oldest
</a> </a>
</li> </li>
<li>
<a href="@condition.copy(sort="priority", direction="asc").toURL">
@gitbucket.core.helper.html.checkicon(condition.sort == "priority" && condition.direction == "asc") Highest priority
</a>
</li>
<li>
<a href="@condition.copy(sort="priority", direction="desc" ).toURL">
@gitbucket.core.helper.html.checkicon(condition.sort == "priority" && condition.direction == "desc") Lowest priority
</a>
</li>
<li> <li>
<a href="@condition.copy(sort="comments", direction="desc").toURL"> <a href="@condition.copy(sort="comments", direction="desc").toURL">
@gitbucket.core.helper.html.checkicon(condition.sort == "comments" && condition.direction == "desc") Most commented @gitbucket.core.helper.html.checkicon(condition.sort == "comments" && condition.direction == "desc") Most commented
@@ -127,6 +154,14 @@
</li> </li>
} }
} }
@gitbucket.core.helper.html.dropdown("Priority", filter = ("priority", "Find Priority...")) {
<li><a href="javascript:void(0);" class="toggle-priority" data-id="">No priority</a></li>
@priorities.map { priority =>
<li><a href="javascript:void(0);" class="toggle-priority" data-id="@priority.priorityId">
<span style="background-color: #@priority.color;" class="label">&nbsp;</span>
@priority.priorityName</a></li>
}
}
@gitbucket.core.helper.html.dropdown("Milestone", filter = ("milestone", "Find Milestone...")) { @gitbucket.core.helper.html.dropdown("Milestone", filter = ("milestone", "Find Milestone...")) {
<li><a href="javascript:void(0);" class="toggle-milestone" data-id="">No milestone</a></li> <li><a href="javascript:void(0);" class="toggle-milestone" data-id="">No milestone</a></li>
@milestones.filter(_.closedDate.isEmpty).map { milestone => @milestones.filter(_.closedDate.isEmpty).map { milestone =>
@@ -171,7 +206,7 @@
</td> </td>
</tr> </tr>
} }
@issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) => @issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) =>
<tr> <tr>
<td style="padding-top: 12px; padding-bottom: 12px;"> <td style="padding-top: 12px; padding-bottom: 12px;">
@if(isManageable){ @if(isManageable){
@@ -208,6 +243,10 @@
</span> </span>
<div class="small muted" style="margin-left: 12px; margin-top: 2px;"> <div class="small muted" style="margin-left: 12px; margin-top: 2px;">
#@issue.issueId opened @gitbucket.core.helper.html.datetimeago(issue.registeredDate) by @helpers.user(issue.openedUserName, styleClass="username") #@issue.issueId opened @gitbucket.core.helper.html.datetimeago(issue.registeredDate) by @helpers.user(issue.openedUserName, styleClass="username")
@priority.map(priority => priorities.filter(p => p.priorityName == priority).head).map { priority =>
<span style="margin: 20px;"><a href="@condition.copy(priority = Some(Some(priority.priorityName))).toURL" class="username"><i class="octicon octicon-flame"></i>
<span class="issue-priority issue-priority-inline" style="background-color: #@priority.color; color: #@priority.fontColor;">@priority.priorityName</span></a></span>
}
@milestone.map { milestone => @milestone.map { milestone =>
<span style="margin: 20px;"><a href="@condition.copy(milestone = Some(Some(milestone))).toURL" class="username"><i class="octicon octicon-milestone"></i> @milestone</a></span> <span style="margin: 20px;"><a href="@condition.copy(milestone = Some(Some(milestone))).toURL" class="username"><i class="octicon octicon-milestone"></i> @milestone</a></span>
} }

View File

@@ -14,6 +14,7 @@
hasOriginWritePermission: Boolean, hasOriginWritePermission: Boolean,
collaborators: List[String], collaborators: List[String],
milestones: List[gitbucket.core.model.Milestone], milestones: List[gitbucket.core.model.Milestone],
priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label])(implicit context: gitbucket.core.controller.Context) labels: List[gitbucket.core.model.Label])(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers @import gitbucket.core.view.helpers
@gitbucket.core.html.main(s"Pull requests - ${repository.owner}/${repository.name}", Some(repository)){ @gitbucket.core.html.main(s"Pull requests - ${repository.owner}/${repository.name}", Some(repository)){
@@ -81,7 +82,7 @@
</div> </div>
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
@gitbucket.core.issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map((_, 0, 0)), labels, hasOriginWritePermission, repository) @gitbucket.core.issues.html.issueinfo(None, Nil, Nil, collaborators, milestones.map((_, 0, 0)), priorities, labels, hasOriginWritePermission, repository)
</div> </div>
</div> </div>
</form> </form>

View File

@@ -5,6 +5,7 @@
issueLabels: List[gitbucket.core.model.Label], issueLabels: List[gitbucket.core.model.Label],
collaborators: List[String], collaborators: List[String],
milestones: List[(gitbucket.core.model.Milestone, Int, Int)], milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label], labels: List[gitbucket.core.model.Label],
isEditable: Boolean, isEditable: Boolean,
isManageable: Boolean, isManageable: Boolean,
@@ -45,7 +46,7 @@
} }
</div> </div>
<div class="col-md-3"> <div class="col-md-3">
@gitbucket.core.issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, labels, isManageable, repository) @gitbucket.core.issues.html.issueinfo(Some(issue), comments, issueLabels, collaborators, milestones, priorities, labels, isManageable, repository)
</div> </div>
<script> <script>
$(function(){ $(function(){

View File

@@ -4,6 +4,7 @@
issueLabels: List[gitbucket.core.model.Label], issueLabels: List[gitbucket.core.model.Label],
collaborators: List[String], collaborators: List[String],
milestones: List[(gitbucket.core.model.Milestone, Int, Int)], milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label], labels: List[gitbucket.core.model.Label],
dayByDayCommits: Seq[Seq[gitbucket.core.util.JGitUtil.CommitInfo]], dayByDayCommits: Seq[Seq[gitbucket.core.util.JGitUtil.CommitInfo]],
diffs: Seq[gitbucket.core.util.JGitUtil.DiffInfo], diffs: Seq[gitbucket.core.util.JGitUtil.DiffInfo],
@@ -85,7 +86,7 @@
@flash.get("info").map{ info => @flash.get("info").map{ info =>
<div class="alert alert-info">@info</div> <div class="alert alert-info">@info</div>
} }
@gitbucket.core.pulls.html.conversation(issue, pullreq, commits, comments, issueLabels, collaborators, milestones, labels, isEditable, isManageable, isManageableForkedRepository, repository, forkedRepository) @gitbucket.core.pulls.html.conversation(issue, pullreq, commits, comments, issueLabels, collaborators, milestones, priorities, labels, isEditable, isManageable, isManageableForkedRepository, repository, forkedRepository)
</div> </div>
<div class="tab-pane" id="commits"> <div class="tab-pane" id="commits">
@if(commits.nonEmpty){ @if(commits.nonEmpty){

View File

@@ -823,7 +823,7 @@ div.issue-comment-action .octicon.danger {
color: #fff; color: #fff;
} }
.nav-pills > li > span.issue-label { .nav-pills > li > span.issue-label, .issue-priority {
display: block; display: block;
padding: 0px 8px 2px 8px; padding: 0px 8px 2px 8px;
margin-top: 2px; margin-top: 2px;
@@ -832,6 +832,15 @@ div.issue-comment-action .octicon.danger {
border-radius: 5px; border-radius: 5px;
} }
.issue-priority {
font-size: 90%;
}
.issue-priority-inline {
font-size: 100%;
display: inline;
}
div.attachable { div.attachable {
margin-bottom: 10px; margin-bottom: 10px;
} }

View File

@@ -62,6 +62,7 @@ trait ServiceSpecBase {
content = None, content = None,
assignedUserName = None, assignedUserName = None,
milestoneId = None, milestoneId = None,
priorityId = None,
isPullRequest = true) isPullRequest = true)
} }