diff --git a/src/main/resources/update/gitbucket-core_4.14.sql b/src/main/resources/update/gitbucket-core_4.14.sql
new file mode 100644
index 0000000..1ba0103
--- /dev/null
+++ b/src/main/resources/update/gitbucket-core_4.14.sql
@@ -0,0 +1,26 @@
+CREATE OR REPLACE VIEW ISSUE_OUTLINE_VIEW AS
+
+ SELECT
+ A.USER_NAME,
+ A.REPOSITORY_NAME,
+ A.ISSUE_ID,
+ COALESCE(B.COMMENT_COUNT, 0) + COALESCE(C.COMMENT_COUNT, 0) AS COMMENT_COUNT,
+ COALESCE(D.ORDERING, 9999) AS PRIORITY
+
+ FROM ISSUE A
+
+ LEFT OUTER JOIN (
+ SELECT USER_NAME, REPOSITORY_NAME, ISSUE_ID, COUNT(COMMENT_ID) AS COMMENT_COUNT FROM ISSUE_COMMENT
+ WHERE ACTION IN ('comment', 'close_comment', 'reopen_comment')
+ GROUP BY USER_NAME, REPOSITORY_NAME, ISSUE_ID
+ ) B
+ ON (A.USER_NAME = B.USER_NAME AND A.REPOSITORY_NAME = B.REPOSITORY_NAME AND A.ISSUE_ID = B.ISSUE_ID)
+
+ LEFT OUTER JOIN (
+ SELECT USER_NAME, REPOSITORY_NAME, ISSUE_ID, COUNT(COMMENT_ID) AS COMMENT_COUNT FROM COMMIT_COMMENT
+ GROUP BY USER_NAME, REPOSITORY_NAME, ISSUE_ID
+ ) C
+ ON (A.USER_NAME = C.USER_NAME AND A.REPOSITORY_NAME = C.REPOSITORY_NAME AND A.ISSUE_ID = C.ISSUE_ID)
+
+ LEFT OUTER JOIN PRIORITY D
+ ON (A.PRIORITY_ID = D.PRIORITY_ID);
diff --git a/src/main/resources/update/gitbucket-core_4.14.xml b/src/main/resources/update/gitbucket-core_4.14.xml
new file mode 100644
index 0000000..9f8a9a7
--- /dev/null
+++ b/src/main/resources/update/gitbucket-core_4.14.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/scala/ScalatraBootstrap.scala b/src/main/scala/ScalatraBootstrap.scala
index 949bf80..f9be729 100644
--- a/src/main/scala/ScalatraBootstrap.scala
+++ b/src/main/scala/ScalatraBootstrap.scala
@@ -44,6 +44,7 @@
context.mount(new RepositoryViewerController, "/*")
context.mount(new WikiController, "/*")
context.mount(new LabelsController, "/*")
+ context.mount(new PrioritiesController, "/*")
context.mount(new MilestonesController, "/*")
context.mount(new IssuesController, "/*")
context.mount(new PullRequestsController, "/*")
diff --git a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala
index 33adca1..8d678b3 100644
--- a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala
+++ b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala
@@ -34,5 +34,9 @@
),
new Version("4.12.0"),
new Version("4.12.1"),
- new Version("4.13.0")
+ new Version("4.13.0"),
+ new Version("4.14.0",
+ new LiquibaseMigration("update/gitbucket-core_4.14.xml"),
+ new SqlMigration("update/gitbucket-core_4.14.sql")
+ )
)
diff --git a/src/main/scala/gitbucket/core/controller/AccountController.scala b/src/main/scala/gitbucket/core/controller/AccountController.scala
index c2235f7..045fbfe 100644
--- a/src/main/scala/gitbucket/core/controller/AccountController.scala
+++ b/src/main/scala/gitbucket/core/controller/AccountController.scala
@@ -20,13 +20,13 @@
class AccountController extends AccountControllerBase
with AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
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 {
self: AccountService with RepositoryService with ActivityService with WikiService with LabelsService with SshKeyService
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,
description: Option[String], url: Option[String], fileId: Option[String])
diff --git a/src/main/scala/gitbucket/core/controller/ApiController.scala b/src/main/scala/gitbucket/core/controller/ApiController.scala
index e873ae2..9da265e 100644
--- a/src/main/scala/gitbucket/core/controller/ApiController.scala
+++ b/src/main/scala/gitbucket/core/controller/ApiController.scala
@@ -33,6 +33,7 @@
with WebHookIssueCommentService
with WikiService
with ActivityService
+ with PrioritiesService
with OwnerAuthenticator
with UsersAuthenticator
with GroupManagerAuthenticator
@@ -52,6 +53,7 @@
with RepositoryCreationService
with IssueCreationService
with HandleCommentService
+ with PrioritiesService
with OwnerAuthenticator
with UsersAuthenticator
with GroupManagerAuthenticator
@@ -365,6 +367,7 @@
data.body,
data.assignees.headOption,
milestone.map(_.milestoneId),
+ None,
data.labels,
loginAccount)
JsonFormat(ApiIssue(issue, RepositoryName(repository), ApiUser(loginAccount)))
diff --git a/src/main/scala/gitbucket/core/controller/IssuesController.scala b/src/main/scala/gitbucket/core/controller/IssuesController.scala
index d149915..9bebbfc 100644
--- a/src/main/scala/gitbucket/core/controller/IssuesController.scala
+++ b/src/main/scala/gitbucket/core/controller/IssuesController.scala
@@ -27,6 +27,7 @@
with PullRequestService
with WebHookIssueCommentService
with CommitsService
+ with PrioritiesService
trait IssuesControllerBase extends ControllerBase {
self: IssuesService
@@ -41,10 +42,11 @@
with ReferrerAuthenticator
with WritableUsersAuthenticator
with PullRequestService
- with WebHookIssueCommentService =>
+ with WebHookIssueCommentService
+ with PrioritiesService =>
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 IssueStateForm(issueId: Int, content: Option[String])
@@ -53,6 +55,7 @@
"content" -> trim(optional(text())),
"assignedUserName" -> trim(optional(text())),
"milestoneId" -> trim(optional(number())),
+ "priorityId" -> trim(optional(number())),
"labelNames" -> trim(optional(text()))
)(IssueCreateForm.apply)
@@ -94,6 +97,7 @@
getIssueLabels(owner, name, issueId.toInt),
getAssignableUserNames(owner, name),
getMilestonesWithIssueCount(owner, name),
+ getPriorities(owner, name),
getLabels(owner, name),
isIssueEditable(repository),
isIssueManageable(repository),
@@ -109,6 +113,8 @@
html.create(
getAssignableUserNames(owner, name),
getMilestones(owner, name),
+ getPriorities(owner, name),
+ getDefaultPriority(owner, name),
getLabels(owner, name),
isIssueManageable(repository),
getContentTemplate(repository, "ISSUE_TEMPLATE"),
@@ -125,6 +131,7 @@
form.content,
form.assignedUserName,
form.milestoneId,
+ form.priorityId,
form.labelNames.toArray.flatMap(_.split(",")),
context.loginAccount.get)
@@ -291,6 +298,11 @@
} 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 =>
defining(params.get("value")){ action =>
action match {
@@ -335,6 +347,14 @@
}
})
+ 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 =>
(Directory.getAttachedDir(repository.owner, repository.name) match {
case dir if(dir.exists && dir.isDirectory) =>
@@ -348,6 +368,7 @@
val assignedUserName = (key: String) => params.get(key) filter (_.trim != "")
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) = {
params("checked").split(',') map(_.toInt) foreach execute
@@ -370,6 +391,7 @@
page,
getAssignableUserNames(owner, repoName),
getMilestones(owner, repoName),
+ getPriorities(owner, repoName),
getLabels(owner, repoName),
countIssue(condition.copy(state = "open" ), false, owner -> repoName),
countIssue(condition.copy(state = "closed"), false, owner -> repoName),
diff --git a/src/main/scala/gitbucket/core/controller/PrioritiesController.scala b/src/main/scala/gitbucket/core/controller/PrioritiesController.scala
new file mode 100644
index 0000000..e0e010a
--- /dev/null
+++ b/src/main/scala/gitbucket/core/controller/PrioritiesController.scala
@@ -0,0 +1,111 @@
+package gitbucket.core.controller
+
+import gitbucket.core.issues.priorities.html
+import gitbucket.core.service.{RepositoryService, AccountService, IssuesService, PrioritiesService}
+import gitbucket.core.util.{ReferrerAuthenticator, WritableUsersAuthenticator}
+import gitbucket.core.util.Implicits._
+import io.github.gitbucket.scalatra.forms._
+import org.scalatra.i18n.Messages
+import org.scalatra.Ok
+
+class PrioritiesController extends PrioritiesControllerBase
+ with PrioritiesService with IssuesService with RepositoryService with AccountService
+with ReferrerAuthenticator with WritableUsersAuthenticator
+
+trait PrioritiesControllerBase extends ControllerBase {
+ self: PrioritiesService with IssuesService with RepositoryService
+ with ReferrerAuthenticator with WritableUsersAuthenticator =>
+
+ case class PriorityForm(priorityName: String, description: Option[String], color: String)
+
+ val priorityForm = mapping(
+ "priorityName" -> trim(label("Priority name", text(required, priorityName, uniquePriorityName, maxlength(100)))),
+ "description" -> trim(label("Description", optional(text(maxlength(255))))),
+ "priorityColor" -> trim(label("Color", text(required, color)))
+ )(PriorityForm.apply)
+
+
+ get("/:owner/:repository/issues/priorities")(referrersOnly { repository =>
+ html.list(
+ getPriorities(repository.owner, repository.name),
+ countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
+ repository,
+ hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
+ })
+
+ ajaxGet("/:owner/:repository/issues/priorities/new")(writableUsersOnly { repository =>
+ html.edit(None, repository)
+ })
+
+ ajaxPost("/:owner/:repository/issues/priorities/new", priorityForm)(writableUsersOnly { (form, repository) =>
+ val priorityId = createPriority(repository.owner, repository.name, form.priorityName, form.description, form.color.substring(1))
+ html.priority(
+ getPriority(repository.owner, repository.name, priorityId).get,
+ countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
+ repository,
+ hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
+ })
+
+ ajaxGet("/:owner/:repository/issues/priorities/:priorityId/edit")(writableUsersOnly { repository =>
+ getPriority(repository.owner, repository.name, params("priorityId").toInt).map { priority =>
+ html.edit(Some(priority), repository)
+ } getOrElse NotFound()
+ })
+
+ ajaxPost("/:owner/:repository/issues/priorities/:priorityId/edit", priorityForm)(writableUsersOnly { (form, repository) =>
+ updatePriority(repository.owner, repository.name, params("priorityId").toInt, form.priorityName, form.description, form.color.substring(1))
+ html.priority(
+ getPriority(repository.owner, repository.name, params("priorityId").toInt).get,
+ countIssueGroupByPriorities(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
+ repository,
+ hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
+ })
+
+ ajaxPost("/:owner/:repository/issues/priorities/reorder")(writableUsersOnly { (repository) =>
+ reorderPriorities(repository.owner, repository.name, params("order")
+ .split(",")
+ .map(id => id.toInt)
+ .zipWithIndex
+ .toMap)
+
+ Ok()
+ })
+
+ ajaxPost("/:owner/:repository/issues/priorities/default")(writableUsersOnly { (repository) =>
+ setDefaultPriority(repository.owner, repository.name, priorityId("priorityId"))
+ Ok()
+ })
+
+ ajaxPost("/:owner/:repository/issues/priorities/:priorityId/delete")(writableUsersOnly { repository =>
+ deletePriority(repository.owner, repository.name, params("priorityId").toInt)
+ Ok()
+ })
+
+ val priorityId: String => Option[Int] = (key: String) => params.get(key).flatMap(_.toIntOpt)
+
+ /**
+ * Constraint for the identifier such as user name, repository name or page name.
+ */
+ private def priorityName: Constraint = new Constraint(){
+ override def validate(name: String, value: String, messages: Messages): Option[String] =
+ if(value.contains(',')){
+ Some(s"${name} contains invalid character.")
+ } else if(value.startsWith("_") || value.startsWith("-")){
+ Some(s"${name} starts with invalid character.")
+ } else {
+ None
+ }
+ }
+
+ private def uniquePriorityName: Constraint = new Constraint(){
+ override def validate(name: String, value: String, params: Map[String, String], messages: Messages): Option[String] = {
+ val owner = params("owner")
+ val repository = params("repository")
+ params.get("priorityId").map { priorityId =>
+ getPriority(owner, repository, value).filter(_.priorityId != priorityId.toInt).map(_ => "Name has already been taken.")
+ }.getOrElse {
+ getPriority(owner, repository, value).map(_ => "Name has already been taken.")
+ }
+ }
+ }
+}
diff --git a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala
index 4029ff0..88328e1 100644
--- a/src/main/scala/gitbucket/core/controller/PullRequestsController.scala
+++ b/src/main/scala/gitbucket/core/controller/PullRequestsController.scala
@@ -24,14 +24,14 @@
with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with LabelsService
with CommitsService with ActivityService with WebHookPullRequestService
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator
- with CommitStatusService with MergeService with ProtectedBranchService
+ with CommitStatusService with MergeService with ProtectedBranchService with PrioritiesService
trait PullRequestsControllerBase extends ControllerBase {
self: RepositoryService with AccountService with IssuesService with MilestonesService with LabelsService
with CommitsService with ActivityService with PullRequestService with WebHookPullRequestService
with ReadableUsersAuthenticator with ReferrerAuthenticator with WritableUsersAuthenticator
- with CommitStatusService with MergeService with ProtectedBranchService =>
+ with CommitStatusService with MergeService with ProtectedBranchService with PrioritiesService =>
val pullRequestForm = mapping(
"title" -> trim(label("Title" , text(required, maxlength(100)))),
@@ -45,6 +45,7 @@
"commitIdTo" -> trim(text(required, maxlength(40))),
"assignedUserName" -> trim(optional(text())),
"milestoneId" -> trim(optional(number())),
+ "priorityId" -> trim(optional(number())),
"labelNames" -> trim(optional(text()))
)(PullRequestForm.apply)
@@ -64,6 +65,7 @@
commitIdTo: String,
assignedUserName: Option[String],
milestoneId: Option[Int],
+ priorityId: Option[Int],
labelNames: Option[String]
)
@@ -93,6 +95,7 @@
getIssueLabels(owner, name, issueId),
getAssignableUserNames(owner, name),
getMilestonesWithIssueCount(owner, name),
+ getPriorities(owner, name),
getLabels(owner, name),
commits,
diffs,
@@ -390,6 +393,7 @@
hasDeveloperRole(originRepository.owner, originRepository.name, context.loginAccount),
getAssignableUserNames(originRepository.owner, originRepository.name),
getMilestones(originRepository.owner, originRepository.name),
+ getPriorities(originRepository.owner, originRepository.name),
getLabels(originRepository.owner, originRepository.name)
)
}
@@ -445,6 +449,7 @@
content = form.content,
assignedUserName = if (manageable) form.assignedUserName else None,
milestoneId = if (manageable) form.milestoneId else None,
+ priorityId = if (manageable) form.priorityId else None,
isPullRequest = true)
createPullRequest(
@@ -518,6 +523,7 @@
page,
getAssignableUserNames(owner, repoName),
getMilestones(owner, repoName),
+ getPriorities(owner, repoName),
getLabels(owner, repoName),
countIssue(condition.copy(state = "open" ), true, owner -> repoName),
countIssue(condition.copy(state = "closed"), true, owner -> repoName),
diff --git a/src/main/scala/gitbucket/core/model/BasicTemplate.scala b/src/main/scala/gitbucket/core/model/BasicTemplate.scala
index c3b8e1b..2ebb28c 100644
--- a/src/main/scala/gitbucket/core/model/BasicTemplate.scala
+++ b/src/main/scala/gitbucket/core/model/BasicTemplate.scala
@@ -38,6 +38,20 @@
byRepository(owner, repository) && (this.labelName === labelName.bind)
}
+ trait PriorityTemplate extends BasicTemplate { self: Table[_] =>
+ val priorityId = column[Int]("PRIORITY_ID")
+ val priorityName = column[String]("PRIORITY_NAME")
+
+ def byPriority(owner: String, repository: String, priorityId: Int) =
+ byRepository(owner, repository) && (this.priorityId === priorityId.bind)
+
+ def byPriority(userName: Rep[String], repositoryName: Rep[String], priorityId: Rep[Int]) =
+ byRepository(userName, repositoryName) && (this.priorityId === priorityId)
+
+ def byPriority(owner: String, repository: String, priorityName: String) =
+ byRepository(owner, repository) && (this.priorityName === priorityName.bind)
+ }
+
trait MilestoneTemplate extends BasicTemplate { self: Table[_] =>
val milestoneId = column[Int]("MILESTONE_ID")
diff --git a/src/main/scala/gitbucket/core/model/Issue.scala b/src/main/scala/gitbucket/core/model/Issue.scala
index fd7a5ce..7167195 100644
--- a/src/main/scala/gitbucket/core/model/Issue.scala
+++ b/src/main/scala/gitbucket/core/model/Issue.scala
@@ -13,12 +13,13 @@
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")
- 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 assignedUserName = column[String]("ASSIGNED_USER_NAME")
val title = column[String]("TITLE")
@@ -27,7 +28,7 @@
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
val updatedDate = column[java.util.Date]("UPDATED_DATE")
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)
}
@@ -39,6 +40,7 @@
issueId: Int,
openedUserName: String,
milestoneId: Option[Int],
+ priorityId: Option[Int],
assignedUserName: Option[String],
title: String,
content: Option[String],
diff --git a/src/main/scala/gitbucket/core/model/Priorities.scala b/src/main/scala/gitbucket/core/model/Priorities.scala
new file mode 100644
index 0000000..eb31740
--- /dev/null
+++ b/src/main/scala/gitbucket/core/model/Priorities.scala
@@ -0,0 +1,43 @@
+package gitbucket.core.model
+
+trait PriorityComponent extends TemplateComponent { self: Profile =>
+ import profile.api._
+
+ lazy val Priorities = TableQuery[Priorities]
+
+ class Priorities(tag: Tag) extends Table[Priority](tag, "PRIORITY") with PriorityTemplate {
+ override val priorityId = column[Int]("PRIORITY_ID", O AutoInc)
+ override val priorityName = column[String]("PRIORITY_NAME")
+ val description = column[String]("DESCRIPTION")
+ val ordering = column[Int]("ORDERING")
+ val isDefault = column[Boolean]("IS_DEFAULT")
+ val color = column[String]("COLOR")
+ def * = (userName, repositoryName, priorityId, priorityName, description.?, isDefault, ordering, color) <> (Priority.tupled, Priority.unapply)
+
+ def byPrimaryKey(owner: String, repository: String, priorityId: Int) = byPriority(owner, repository, priorityId)
+ def byPrimaryKey(userName: Rep[String], repositoryName: Rep[String], priorityId: Rep[Int]) = byPriority(userName, repositoryName, priorityId)
+ }
+}
+
+case class Priority (
+ userName: String,
+ repositoryName: String,
+ priorityId: Int = 0,
+ priorityName: String,
+ description: Option[String],
+ isDefault: Boolean,
+ ordering: Int = 0,
+ color: String){
+
+ val fontColor = {
+ val r = color.substring(0, 2)
+ val g = color.substring(2, 4)
+ val b = color.substring(4, 6)
+
+ if(Integer.parseInt(r, 16) + Integer.parseInt(g, 16) + Integer.parseInt(b, 16) > 408){
+ "000000"
+ } else {
+ "ffffff"
+ }
+ }
+}
diff --git a/src/main/scala/gitbucket/core/model/Profile.scala b/src/main/scala/gitbucket/core/model/Profile.scala
index 332e7ea..876188c 100644
--- a/src/main/scala/gitbucket/core/model/Profile.scala
+++ b/src/main/scala/gitbucket/core/model/Profile.scala
@@ -47,6 +47,7 @@
with IssueCommentComponent
with IssueLabelComponent
with LabelComponent
+ with PriorityComponent
with MilestoneComponent
with PullRequestComponent
with RepositoryComponent
diff --git a/src/main/scala/gitbucket/core/service/IssueCreationService.scala b/src/main/scala/gitbucket/core/service/IssueCreationService.scala
index 09e7dfb..ad67266 100644
--- a/src/main/scala/gitbucket/core/service/IssueCreationService.scala
+++ b/src/main/scala/gitbucket/core/service/IssueCreationService.scala
@@ -12,7 +12,7 @@
self: RepositoryService with WebHookIssueCommentService with LabelsService with IssuesService with ActivityService =>
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 = {
val owner = repository.owner
@@ -23,7 +23,8 @@
// insert issue
val issueId = insertIssue(owner, name, userName, title, body,
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
// insert labels
diff --git a/src/main/scala/gitbucket/core/service/IssuesService.scala b/src/main/scala/gitbucket/core/service/IssuesService.scala
index 4a9b12c..a782dde 100644
--- a/src/main/scala/gitbucket/core/service/IssuesService.scala
+++ b/src/main/scala/gitbucket/core/service/IssuesService.scala
@@ -97,6 +97,30 @@
.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] = {
val status = PullRequests
.filter { pr =>
@@ -136,21 +160,23 @@
(implicit s: Session): List[IssueInfo] = {
// get issues and comment count and labels
val result = searchIssueQueryBase(condition, pullRequest, offset, limit, repos)
- .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 (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 }
- .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 =>
- (t1, t2.commentCount, t4.map(_.labelId), t4.map(_.labelName), t4.map(_.color), t5.map(_.title))
+ .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 (Milestones) .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => t1.byMilestone(t5.userName, t5.repositoryName, t5.milestoneId) }
+ .joinLeft (Priorities) .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 => t1.byPriority(t6.userName, t6.repositoryName, t6.priorityId) }
+ .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 => i asc }
+ .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
.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 {
- case (issue, commentCount, _, _, _, milestone) =>
+ case (issue, commentCount, _, _, _, milestone, priority) =>
IssueInfo(issue,
issues.flatMap { t => t._3.map (Label(issue.userName, issue.repositoryName, _, t._4.get, t._5.get))} toList,
milestone,
+ priority,
commentCount,
getCommitStatues(issue.userName, issue.repositoryName, issue.issueId))
}} toList
@@ -204,6 +230,10 @@
case "asc" => t1.updatedDate asc
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
@@ -219,6 +249,7 @@
.foldLeft[Rep[Boolean]](false) ( _ || _ ) &&
(t1.closed === (condition.state == "closed").bind) &&
(t1.milestoneId.? isEmpty, condition.milestone == Some(None)) &&
+ (t1.priorityId.? isEmpty, condition.priority == Some(None)) &&
(t1.assignedUserName.? isEmpty, condition.assigned == Some(None)) &&
(t1.openedUserName === condition.author.get.bind, condition.author.isDefined) &&
(t1.pullRequest === pullRequest.bind) &&
@@ -227,6 +258,11 @@
(t2.byPrimaryKey(t1.userName, t1.repositoryName, t1.milestoneId)) &&
(t2.title === condition.milestone.get.get.bind)
} 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
(t1.assignedUserName === condition.assigned.get.get.bind, condition.assigned.flatten.isDefined) &&
// Label filter
@@ -253,7 +289,7 @@
}
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 = {
// next id number
sql"SELECT ISSUE_ID + 1 FROM ISSUE_ID WHERE USER_NAME = $owner AND REPOSITORY_NAME = $repository FOR UPDATE".as[Int]
@@ -264,6 +300,7 @@
id,
loginUser,
milestoneId,
+ priorityId,
assignedUserName,
title,
content,
@@ -316,6 +353,10 @@
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 = {
IssueComments.filter (_.byPrimaryKey(commentId)).map(t => (t.content, t.updatedDate)).update(content, currentDate)
}
@@ -430,6 +471,7 @@
case class IssueSearchCondition(
labels: Set[String] = Set.empty,
milestone: Option[Option[String]] = None,
+ priority: Option[Option[String]] = None,
author: Option[String] = None,
assigned: Option[Option[String]] = None,
mentioned: Option[String] = None,
@@ -459,6 +501,10 @@
case Some(x) => s"milestone:${x}"
case None => "no:milestone"
}},
+ priority.map { _ match {
+ case Some(x) => s"priority:${x}"
+ case None => "no:priority"
+ }},
(sort, direction) match {
case ("created" , "desc") => None
case ("created" , "asc" ) => Some("sort:created-asc")
@@ -466,6 +512,8 @@
case ("comments", "asc" ) => Some("sort:comments-asc")
case ("updated" , "desc") => Some("sort:updated-desc")
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)
},
visibility.map(visibility => s"visibility:${visibility}")
@@ -480,6 +528,10 @@
case Some(x) => "milestone=" + urlEncode(x)
case None => "milestone=none"
},
+ priority.map {
+ case Some(x) => "priority=" + urlEncode(x)
+ case None => "priority=none"
+ },
author .map(x => "author=" + urlEncode(x)),
assigned.map {
case Some(x) => "assigned=" + urlEncode(x)
@@ -512,6 +564,10 @@
case "none" => None
case x => Some(x)
},
+ param(request, "priority").map {
+ case "none" => None
+ case x => Some(x)
+ },
param(request, "author"),
param(request, "assigned").map {
case "none" => None
@@ -519,7 +575,7 @@
},
param(request, "mentioned"),
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, "visibility"),
param(request, "groups").map(_.split(",").toSet).getOrElse(Set.empty)
@@ -535,6 +591,6 @@
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])
}
diff --git a/src/main/scala/gitbucket/core/service/PrioritiesService.scala b/src/main/scala/gitbucket/core/service/PrioritiesService.scala
new file mode 100644
index 0000000..cafff4b
--- /dev/null
+++ b/src/main/scala/gitbucket/core/service/PrioritiesService.scala
@@ -0,0 +1,84 @@
+package gitbucket.core.service
+
+import gitbucket.core.model.Priority
+import gitbucket.core.model.Profile._
+import gitbucket.core.model.Profile.profile.blockingApi._
+import gitbucket.core.util.StringUtil
+
+trait PrioritiesService {
+
+ def getPriorities(owner: String, repository: String)(implicit s: Session): List[Priority] =
+ Priorities.filter(_.byRepository(owner, repository)).sortBy(_.ordering asc).list
+
+ def getPriority(owner: String, repository: String, priorityId: Int)(implicit s: Session): Option[Priority] =
+ Priorities.filter(_.byPrimaryKey(owner, repository, priorityId)).firstOption
+
+ def getPriority(owner: String, repository: String, priorityName: String)(implicit s: Session): Option[Priority] =
+ Priorities.filter(_.byPriority(owner, repository, priorityName)).firstOption
+
+ def createPriority(owner: String, repository: String, priorityName: String, description: Option[String], color: String)(implicit s: Session): Int = {
+ val ordering = Priorities.filter(_.byRepository(owner, repository))
+ .list
+ .map(p => p.ordering)
+ .reduceOption(_ max _)
+ .map(m => m + 1)
+ .getOrElse(0)
+
+ Priorities returning Priorities.map(_.priorityId) insert Priority(
+ userName = owner,
+ repositoryName = repository,
+ priorityName = priorityName,
+ description = description,
+ isDefault = false,
+ ordering = ordering,
+ color = color
+ )
+ }
+
+ def updatePriority(owner: String, repository: String, priorityId: Int, priorityName: String, description: Option[String], color: String)
+ (implicit s: Session): Unit =
+ Priorities.filter(_.byPrimaryKey(owner, repository, priorityId))
+ .map(t => (t.priorityName, t.description.?, t.color))
+ .update(priorityName, description, color)
+
+ def reorderPriorities(owner: String, repository: String, order: Map[Int, Int])
+ (implicit s: Session): Unit = {
+
+ Priorities.filter(_.byRepository(owner, repository))
+ .list
+ .foreach(p => Priorities
+ .filter(_.byPrimaryKey(owner, repository, p.priorityId))
+ .map(_.ordering)
+ .update(order.get(p.priorityId).get))
+ }
+
+ def deletePriority(owner: String, repository: String, priorityId: Int)(implicit s: Session): Unit = {
+ Issues.filter(_.byRepository(owner, repository))
+ .filter(_.priorityId === priorityId)
+ .map(_.priorityId?)
+ .update(None)
+
+ Priorities.filter(_.byPrimaryKey(owner, repository, priorityId)).delete
+ }
+
+ def getDefaultPriority(owner: String, repository: String)(implicit s: Session): Option[Priority] = {
+ Priorities
+ .filter(_.byRepository(owner, repository))
+ .filter(_.isDefault)
+ .list
+ .headOption
+ }
+
+ def setDefaultPriority(owner: String, repository: String, priorityId: Option[Int])(implicit s: Session): Unit = {
+ Priorities
+ .filter(_.byRepository(owner, repository))
+ .filter(_.isDefault)
+ .map(_.isDefault)
+ .update(false)
+
+ priorityId.foreach(id => Priorities
+ .filter(_.byPrimaryKey(owner, repository, id))
+ .map(_.isDefault)
+ .update(true))
+ }
+}
diff --git a/src/main/scala/gitbucket/core/service/RepositoryCreationService.scala b/src/main/scala/gitbucket/core/service/RepositoryCreationService.scala
index 7381bbc..2aa4196 100644
--- a/src/main/scala/gitbucket/core/service/RepositoryCreationService.scala
+++ b/src/main/scala/gitbucket/core/service/RepositoryCreationService.scala
@@ -10,7 +10,7 @@
import org.eclipse.jgit.lib.{FileMode, Constants}
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)
(implicit s: Session) {
@@ -30,6 +30,9 @@
// Insert default labels
insertDefaultLabels(owner, name)
+ // Insert default priorities
+ insertDefaultPriorities(owner, name)
+
// Create the actual repository
val gitdir = getRepositoryDir(owner, name)
JGitUtil.initRepository(gitdir)
@@ -74,5 +77,13 @@
createLabel(userName, repositoryName, "wontfix", "ffffff")
}
+ def insertDefaultPriorities(userName: String, repositoryName: String)(implicit s: Session): Unit = {
+ createPriority(userName, repositoryName, "highest", Some("All defects at this priority must be fixed before any public product is delivered."), "fc2929")
+ createPriority(userName, repositoryName, "very high", Some("Issues must be addressed before a final product is delivered."), "fc5629")
+ createPriority(userName, repositoryName, "high", Some("Issues should be addressed before a final product is delivered. If the issue cannot be resolved before delivery, it should be prioritized for the next release."), "fc9629")
+ createPriority(userName, repositoryName, "important", Some("Issues can be shipped with a final product, but should be reviewed before the next release."), "fccd29")
+ createPriority(userName, repositoryName, "default", Some("Default."), "acacac")
+ setDefaultPriority(userName, repositoryName, getPriority(userName, repositoryName, "default").map(_.priorityId))
+ }
}
diff --git a/src/main/scala/gitbucket/core/service/RepositoryService.scala b/src/main/scala/gitbucket/core/service/RepositoryService.scala
index 598031e..9426507 100644
--- a/src/main/scala/gitbucket/core/service/RepositoryService.scala
+++ b/src/main/scala/gitbucket/core/service/RepositoryService.scala
@@ -66,6 +66,7 @@
val issues = Issues .filter(_.byRepository(oldUserName, oldRepositoryName)).list
val pullRequests = PullRequests .filter(_.byRepository(oldUserName, oldRepositoryName)).list
val labels = Labels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
+ val priorities = Priorities .filter(_.byRepository(oldUserName, oldRepositoryName)).list
val issueComments = IssueComments .filter(_.byRepository(oldUserName, oldRepositoryName)).list
val issueLabels = IssueLabels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
val commitComments = CommitComments .filter(_.byRepository(oldUserName, oldRepositoryName)).list
@@ -95,14 +96,19 @@
WebHooks .insertAll(webHooks .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
WebHookEvents.insertAll(webHookEvents .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
Milestones .insertAll(milestones .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
+ Priorities .insertAll(priorities .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
IssueId .insertAll(issueId .map(_.copy(_1 = newUserName, _2 = newRepositoryName)) :_*)
val newMilestones = Milestones.filter(_.byRepository(newUserName, newRepositoryName)).list
+ val newPriorities = Priorities.filter(_.byRepository(newUserName, newRepositoryName)).list
Issues.insertAll(issues.map { x => x.copy(
userName = newUserName,
repositoryName = newRepositoryName,
milestoneId = x.milestoneId.map { id =>
newMilestones.find(_.title == milestones.find(_.milestoneId == id).get.title).get.milestoneId
+ },
+ priorityId = x.priorityId.map { id =>
+ newPriorities.find(_.priorityName == priorities.find(_.priorityId == id).get.priorityName).get.priorityId
}
)} :_*)
@@ -161,6 +167,7 @@
IssueComments .filter(_.byRepository(userName, repositoryName)).delete
PullRequests .filter(_.byRepository(userName, repositoryName)).delete
Issues .filter(_.byRepository(userName, repositoryName)).delete
+ Priorities .filter(_.byRepository(userName, repositoryName)).delete
IssueId .filter(_.byRepository(userName, repositoryName)).delete
Milestones .filter(_.byRepository(userName, repositoryName)).delete
WebHooks .filter(_.byRepository(userName, repositoryName)).delete
diff --git a/src/main/scala/gitbucket/core/util/StringUtil.scala b/src/main/scala/gitbucket/core/util/StringUtil.scala
index d1eadf3..908fd25 100644
--- a/src/main/scala/gitbucket/core/util/StringUtil.scala
+++ b/src/main/scala/gitbucket/core/util/StringUtil.scala
@@ -136,6 +136,4 @@
// }
// b.toString
// }
-
-
}
diff --git a/src/main/twirl/gitbucket/core/dashboard/issueslist.scala.html b/src/main/twirl/gitbucket/core/dashboard/issueslist.scala.html
index 7463451..ec28060 100644
--- a/src/main/twirl/gitbucket/core/dashboard/issueslist.scala.html
+++ b/src/main/twirl/gitbucket/core/dashboard/issueslist.scala.html
@@ -17,7 +17,7 @@
- @issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
+ @issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) =>
@issue.userName/@issue.repositoryName ・
diff --git a/src/main/twirl/gitbucket/core/issues/create.scala.html b/src/main/twirl/gitbucket/core/issues/create.scala.html
index bd73e61..1522d79 100644
--- a/src/main/twirl/gitbucket/core/issues/create.scala.html
+++ b/src/main/twirl/gitbucket/core/issues/create.scala.html
@@ -1,5 +1,7 @@
@(collaborators: List[String],
milestones: List[gitbucket.core.model.Milestone],
+ priorities: List[gitbucket.core.model.Priority],
+ defaultPriority: Option[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label],
isManageable: Boolean,
content: String,
@@ -29,7 +31,7 @@
- @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, defaultPriority, labels, isManageable, repository)
diff --git a/src/main/twirl/gitbucket/core/issues/issue.scala.html b/src/main/twirl/gitbucket/core/issues/issue.scala.html
index e42ffbf..e42d6b9 100644
--- a/src/main/twirl/gitbucket/core/issues/issue.scala.html
+++ b/src/main/twirl/gitbucket/core/issues/issue.scala.html
@@ -3,6 +3,7 @@
issueLabels: List[gitbucket.core.model.Label],
collaborators: List[String],
milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
+ priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label],
isEditable: Boolean,
isManageable: Boolean,
@@ -54,7 +55,7 @@
@gitbucket.core.issues.html.commentform(issue, true, isEditable, isManageable, repository)
- @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, None, labels, isManageable, repository)
}
diff --git a/src/main/twirl/gitbucket/core/issues/issueinfo.scala.html b/src/main/twirl/gitbucket/core/issues/issueinfo.scala.html
index 27cd255..4b0fb88 100644
--- a/src/main/twirl/gitbucket/core/issues/issueinfo.scala.html
+++ b/src/main/twirl/gitbucket/core/issues/issueinfo.scala.html
@@ -3,6 +3,8 @@
issueLabels: List[gitbucket.core.model.Label],
collaborators: List[String],
milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
+ priorities: List[gitbucket.core.model.Priority],
+ defaultPriority: Option[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label],
isManageable: Boolean,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
@@ -32,6 +34,40 @@
@gitbucket.core.issues.html.labellist(issueLabels)
+
+
+ Priority
+ @if(isManageable){
+
+ }
+
+
+ @issue.flatMap(_.priorityId).orElse(defaultPriority.map(_.priorityId)).map { priorityId =>
+ @priorities.collect { case priority if(priority.priorityId == priorityId) =>
+ @priority.priorityName
+ }
+ }.getOrElse {
+ No priority
+ }
+
+@if(issue.isEmpty){
+
+}
+
+
Milestone
@if(isManageable){
@@ -152,6 +188,20 @@
);
});
+ $('a.priority').click(function(){
+ var priorityName = $(this).data('name');
+ var priorityId = $(this).data('id');
+ var description = $(this).attr('title');
+ 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, description, color, fontColor);
+ }
+ );
+ });
+
$('a.assign').click(function(){
var $this = $(this);
var userName = $this.data('name');
@@ -188,6 +238,16 @@
$('input[name=milestoneId]').val(milestoneId);
});
+ $('a.priority').click(function(){
+ var priorityName = $(this).data('name');
+ var priorityId = $(this).data('id');
+ var description = $(this).attr('title');
+ var color = $(this).data('color');
+ var fontColor = $(this).data('font-color');
+ displayPriority(priorityName, priorityId, description, color, fontColor);
+ $('input[name=priorityId]').val(priorityId);
+ });
+
$('a.assign').click(function(){
var $this = $(this);
var userName = $this.data('name');
@@ -222,6 +282,23 @@
}
}
+ function displayPriority(priorityName, priorityId, description, color, fontColor){
+ $('a.priority i.octicon-check').removeClass('octicon-check');
+ if(priorityId == ''){
+ $('#label-priority').html($(' ').text('No priority'));
+ } else {
+ $('#label-priority').html($('').text(priorityName)
+ .attr('href', '@helpers.url(repository)/issues?priority=' + encodeURIComponent(priorityName) + '&state=open')
+ .attr('title', description)
+ .css({
+ "background-color": color,
+ "color": fontColor
+ }));
+
+ $('a.priority[data-id=' + priorityId + '] i').addClass('octicon-check');
+ }
+ }
+
function displayAssignee($this, userName){
$('a.assign i.octicon-check').removeClass('octicon-check');
if(userName == ''){
diff --git a/src/main/twirl/gitbucket/core/issues/list.scala.html b/src/main/twirl/gitbucket/core/issues/list.scala.html
index e90eb65..21d25c4 100644
--- a/src/main/twirl/gitbucket/core/issues/list.scala.html
+++ b/src/main/twirl/gitbucket/core/issues/list.scala.html
@@ -3,6 +3,7 @@
page: Int,
collaborators: List[String],
milestones: List[gitbucket.core.model.Milestone],
+ priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label],
openCount: Int,
closedCount: Int,
@@ -38,7 +39,7 @@
}
}
- @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){
|
}
- @issues.map { case IssueInfo(issue, labels, milestone, commentCount, commitStatus) =>
+ @issues.map { case IssueInfo(issue, labels, milestone, priority, commentCount, commitStatus) =>
@if(isManageable){
@@ -208,6 +243,10 @@
#@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 =>
+
+ @priority.priorityName
+ }
@milestone.map { milestone =>
@milestone
}
diff --git a/src/main/twirl/gitbucket/core/issues/priorities/edit.scala.html b/src/main/twirl/gitbucket/core/issues/priorities/edit.scala.html
new file mode 100644
index 0000000..3219b4b
--- /dev/null
+++ b/src/main/twirl/gitbucket/core/issues/priorities/edit.scala.html
@@ -0,0 +1,67 @@
+@(priority: Option[gitbucket.core.model.Priority],
+ repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
+@import gitbucket.core.view.helpers
+@defining(priority.map(_.priorityId).getOrElse("new")){ priorityId =>
+
+
+}
diff --git a/src/main/twirl/gitbucket/core/issues/priorities/list.scala.html b/src/main/twirl/gitbucket/core/issues/priorities/list.scala.html
new file mode 100644
index 0000000..185ba84
--- /dev/null
+++ b/src/main/twirl/gitbucket/core/issues/priorities/list.scala.html
@@ -0,0 +1,124 @@
+@(priorities: List[gitbucket.core.model.Priority],
+ counts: Map[String, Int],
+ repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
+ hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context)
+@import gitbucket.core.view.helpers
+@gitbucket.core.html.main(s"Priorities - ${repository.owner}/${repository.name}"){
+ @gitbucket.core.html.menu("priorities", repository){
+ @if(hasWritePermission){
+
+ }
+
+
+
+
+
+
+ @priorities.map { priority =>
+ @gitbucket.core.issues.priorities.html.priority(priority, counts, repository, hasWritePermission)
+ }
+
+
+ No priorities to show.
+ @if(hasWritePermission){
+ Click on the "New priority" button above to create one.
+ }
+ |
+
+
+
+ }
+}
+
diff --git a/src/main/twirl/gitbucket/core/issues/priorities/priority.scala.html b/src/main/twirl/gitbucket/core/issues/priorities/priority.scala.html
new file mode 100644
index 0000000..637b11a
--- /dev/null
+++ b/src/main/twirl/gitbucket/core/issues/priorities/priority.scala.html
@@ -0,0 +1,49 @@
+@(priority: gitbucket.core.model.Priority,
+ counts: Map[String, Int],
+ repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
+ hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context)
+@import gitbucket.core.view.helpers
+
+
+
+
+ @if(hasWritePermission) {
+
+ }
+
+
+
+ @priority.description.getOrElse("")
+
+
+
+ @if(hasWritePermission){
+
+ } else if(priority.isDefault) {
+
+ }
+
+
+
+
+ @counts.get(priority.priorityName).getOrElse(0) open issues
+
+
+ @if(hasWritePermission){
+
+ }
+
+ |
+
diff --git a/src/main/twirl/gitbucket/core/main.scala.html b/src/main/twirl/gitbucket/core/main.scala.html
index 64369ef..c252432 100644
--- a/src/main/twirl/gitbucket/core/main.scala.html
+++ b/src/main/twirl/gitbucket/core/main.scala.html
@@ -18,8 +18,12 @@
+
+
+
+
diff --git a/src/main/twirl/gitbucket/core/menu.scala.html b/src/main/twirl/gitbucket/core/menu.scala.html
index 5ee9370..c6f5f70 100644
--- a/src/main/twirl/gitbucket/core/menu.scala.html
+++ b/src/main/twirl/gitbucket/core/menu.scala.html
@@ -39,6 +39,7 @@
@menuitem("/issues", "issues", "Issues", "issue-opened", repository.issueCount)
@menuitem("/pulls", "pulls", "Pull requests", "git-pull-request", repository.pullCount)
@menuitem("/issues/labels", "labels", "Labels", "tag")
+ @menuitem("/issues/priorities", "priorities", "Priorities", "flame")
@menuitem("/issues/milestones", "milestones", "Milestones", "milestone")
} else {
@repository.repository.options.externalIssuesUrl.map { externalIssuesUrl =>
diff --git a/src/main/twirl/gitbucket/core/pulls/compare.scala.html b/src/main/twirl/gitbucket/core/pulls/compare.scala.html
index 714f324..ea09ab7 100644
--- a/src/main/twirl/gitbucket/core/pulls/compare.scala.html
+++ b/src/main/twirl/gitbucket/core/pulls/compare.scala.html
@@ -14,6 +14,7 @@
hasOriginWritePermission: Boolean,
collaborators: List[String],
milestones: List[gitbucket.core.model.Milestone],
+ priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label])(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@gitbucket.core.html.main(s"Pull requests - ${repository.owner}/${repository.name}", Some(repository)){
@@ -81,7 +82,7 @@
- @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, None, labels, hasOriginWritePermission, repository)
diff --git a/src/main/twirl/gitbucket/core/pulls/conversation.scala.html b/src/main/twirl/gitbucket/core/pulls/conversation.scala.html
index c05d293..9ac8954 100644
--- a/src/main/twirl/gitbucket/core/pulls/conversation.scala.html
+++ b/src/main/twirl/gitbucket/core/pulls/conversation.scala.html
@@ -5,6 +5,7 @@
issueLabels: List[gitbucket.core.model.Label],
collaborators: List[String],
milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
+ priorities: List[gitbucket.core.model.Priority],
labels: List[gitbucket.core.model.Label],
isEditable: Boolean,
isManageable: Boolean,
@@ -45,7 +46,7 @@
}
- @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, None, labels, isManageable, repository)
|