mirror of
				https://github.com/gitbucket/gitbucket.git
				synced 2025-11-03 20:15:59 +01:00 
			
		
		
		
	Support multiple assignees for Issues and Pull requests (#3055)
This commit is contained in:
		@@ -28,4 +28,26 @@
 | 
			
		||||
  <addPrimaryKey constraintName="IDX_ISSUE_CUSTOM_FIELD_PK" tableName="ISSUE_CUSTOM_FIELD" columnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID, FIELD_ID"/>
 | 
			
		||||
  <addForeignKeyConstraint constraintName="IDX_ISSUE_CUSTOM_FIELD_FK0" baseTableName="ISSUE_CUSTOM_FIELD" baseColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID" referencedTableName="ISSUE" referencedColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID"/>
 | 
			
		||||
  <addForeignKeyConstraint constraintName="IDX_ISSUE_CUSTOM_FIELD_FK1" baseTableName="ISSUE_CUSTOM_FIELD" baseColumnNames="USER_NAME, REPOSITORY_NAME, FIELD_ID" referencedTableName="CUSTOM_FIELD" referencedColumnNames="USER_NAME, REPOSITORY_NAME, FIELD_ID"/>
 | 
			
		||||
 | 
			
		||||
  <!--================================================================================================-->
 | 
			
		||||
  <!-- ISSUE_ASSIGNEE -->
 | 
			
		||||
  <!--================================================================================================-->
 | 
			
		||||
  <createTable tableName="ISSUE_ASSIGNEE">
 | 
			
		||||
    <column name="USER_NAME" type="varchar(100)" nullable="false"/>
 | 
			
		||||
    <column name="REPOSITORY_NAME" type="varchar(100)" nullable="false"/>
 | 
			
		||||
    <column name="ISSUE_ID" type="int" nullable="false"/>
 | 
			
		||||
    <column name="ASSIGNEE_USER_NAME" type="varchar(100)" nullable="false"/>
 | 
			
		||||
  </createTable>
 | 
			
		||||
  <addPrimaryKey constraintName="IDX_ISSUE_ASSIGNEE_PK" tableName="ISSUE_ASSIGNEE" columnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID, ASSIGNEE_USER_NAME"/>
 | 
			
		||||
  <addForeignKeyConstraint constraintName="IDX_ISSUE_ASSIGNEE_FK0" baseTableName="ISSUE_ASSIGNEE" baseColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID" referencedTableName="ISSUE" referencedColumnNames="USER_NAME, REPOSITORY_NAME, ISSUE_ID"/>
 | 
			
		||||
  <!--
 | 
			
		||||
  <addForeignKeyConstraint constraintName="IDX_ISSUE_ASSIGNEE_FK1" baseTableName="ISSUE_ASSIGNEE" baseColumnNames="ASSIGNEE_USER_NAME" referencedTableName="ACCOUNT" referencedColumnNames="USER_NAME"/>
 | 
			
		||||
  -->
 | 
			
		||||
 | 
			
		||||
  <sql>
 | 
			
		||||
    INSERT INTO ISSUE_ASSIGNEE (USER_NAME, REPOSITORY_NAME, ISSUE_ID, ASSIGNEE_USER_NAME)
 | 
			
		||||
      SELECT USER_NAME, REPOSITORY_NAME, ISSUE_ID, ASSIGNED_USER_NAME FROM ISSUE WHERE ASSIGNED_USER_NAME IS NOT NULL
 | 
			
		||||
  </sql>
 | 
			
		||||
 | 
			
		||||
  <dropColumn tableName="ISSUE" columnName="ASSIGNED_USER_NAME"/>
 | 
			
		||||
</changeSet>
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,7 @@ case class ApiIssue(
 | 
			
		||||
  number: Int,
 | 
			
		||||
  title: String,
 | 
			
		||||
  user: ApiUser,
 | 
			
		||||
  assignee: Option[ApiUser],
 | 
			
		||||
  assignees: List[ApiUser],
 | 
			
		||||
  labels: List[ApiLabel],
 | 
			
		||||
  state: String,
 | 
			
		||||
  created_at: Date,
 | 
			
		||||
@@ -21,7 +21,7 @@ case class ApiIssue(
 | 
			
		||||
  milestone: Option[ApiMilestone]
 | 
			
		||||
)(repositoryName: RepositoryName, isPullRequest: Boolean) {
 | 
			
		||||
  val id = 0 // dummy id
 | 
			
		||||
  val assignees = List(assignee).flatten
 | 
			
		||||
  val assignee = assignees.headOption
 | 
			
		||||
  val comments_url = ApiPath(s"/api/v3/repos/${repositoryName.fullName}/issues/${number}/comments")
 | 
			
		||||
  val html_url = ApiPath(s"/${repositoryName.fullName}/${if (isPullRequest) { "pull" } else { "issues" }}/${number}")
 | 
			
		||||
  val pull_request = if (isPullRequest) {
 | 
			
		||||
@@ -43,7 +43,7 @@ object ApiIssue {
 | 
			
		||||
    issue: Issue,
 | 
			
		||||
    repositoryName: RepositoryName,
 | 
			
		||||
    user: ApiUser,
 | 
			
		||||
    assignee: Option[ApiUser],
 | 
			
		||||
    assignees: List[ApiUser],
 | 
			
		||||
    labels: List[ApiLabel],
 | 
			
		||||
    milestone: Option[ApiMilestone]
 | 
			
		||||
  ): ApiIssue =
 | 
			
		||||
@@ -51,7 +51,7 @@ object ApiIssue {
 | 
			
		||||
      number = issue.issueId,
 | 
			
		||||
      title = issue.title,
 | 
			
		||||
      user = user,
 | 
			
		||||
      assignee = assignee,
 | 
			
		||||
      assignees = assignees,
 | 
			
		||||
      labels = labels,
 | 
			
		||||
      milestone = milestone,
 | 
			
		||||
      state = if (issue.closed) { "closed" } else { "open" },
 | 
			
		||||
 
 | 
			
		||||
@@ -21,10 +21,11 @@ case class ApiPullRequest(
 | 
			
		||||
  body: String,
 | 
			
		||||
  user: ApiUser,
 | 
			
		||||
  labels: List[ApiLabel],
 | 
			
		||||
  assignee: Option[ApiUser],
 | 
			
		||||
  assignees: List[ApiUser],
 | 
			
		||||
  draft: Option[Boolean]
 | 
			
		||||
) {
 | 
			
		||||
  val id = 0 // dummy id
 | 
			
		||||
  val assignee = assignees.headOption
 | 
			
		||||
  val html_url = ApiPath(s"${base.repo.html_url.path}/pull/${number}")
 | 
			
		||||
  //val diff_url            = ApiPath(s"${base.repo.html_url.path}/pull/${number}.diff")
 | 
			
		||||
  //val patch_url           = ApiPath(s"${base.repo.html_url.path}/pull/${number}.patch")
 | 
			
		||||
@@ -45,7 +46,7 @@ object ApiPullRequest {
 | 
			
		||||
    baseRepo: ApiRepository,
 | 
			
		||||
    user: ApiUser,
 | 
			
		||||
    labels: List[ApiLabel],
 | 
			
		||||
    assignee: Option[ApiUser],
 | 
			
		||||
    assignees: List[ApiUser],
 | 
			
		||||
    mergedComment: Option[(IssueComment, Account)]
 | 
			
		||||
  ): ApiPullRequest =
 | 
			
		||||
    ApiPullRequest(
 | 
			
		||||
@@ -63,7 +64,7 @@ object ApiPullRequest {
 | 
			
		||||
      body = issue.content.getOrElse(""),
 | 
			
		||||
      user = user,
 | 
			
		||||
      labels = labels,
 | 
			
		||||
      assignee = assignee,
 | 
			
		||||
      assignees = assignees,
 | 
			
		||||
      draft = Some(pullRequest.isDraft)
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
  case class IssueCreateForm(
 | 
			
		||||
    title: String,
 | 
			
		||||
    content: Option[String],
 | 
			
		||||
    assignedUserName: Option[String],
 | 
			
		||||
    assigneeUserNames: Option[String],
 | 
			
		||||
    milestoneId: Option[Int],
 | 
			
		||||
    priorityId: Option[Int],
 | 
			
		||||
    labelNames: Option[String]
 | 
			
		||||
@@ -64,7 +64,7 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
  val issueCreateForm = mapping(
 | 
			
		||||
    "title" -> trim(label("Title", text(required))),
 | 
			
		||||
    "content" -> trim(optional(text())),
 | 
			
		||||
    "assignedUserName" -> trim(optional(text())),
 | 
			
		||||
    "assigneeUserNames" -> trim(optional(text())),
 | 
			
		||||
    "milestoneId" -> trim(optional(number())),
 | 
			
		||||
    "priorityId" -> trim(optional(number())),
 | 
			
		||||
    "labelNames" -> trim(optional(text()))
 | 
			
		||||
@@ -107,6 +107,7 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
            issue,
 | 
			
		||||
            getComments(repository.owner, repository.name, issueId.toInt),
 | 
			
		||||
            getIssueLabels(repository.owner, repository.name, issueId.toInt),
 | 
			
		||||
            getIssueAssignees(repository.owner, repository.name, issueId.toInt),
 | 
			
		||||
            getAssignableUserNames(repository.owner, repository.name),
 | 
			
		||||
            getMilestonesWithIssueCount(repository.owner, repository.name),
 | 
			
		||||
            getPriorities(repository.owner, repository.name),
 | 
			
		||||
@@ -145,7 +146,7 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
            repository,
 | 
			
		||||
            form.title,
 | 
			
		||||
            form.content,
 | 
			
		||||
            form.assignedUserName,
 | 
			
		||||
            form.assigneeUserNames.toSeq.flatMap(_.split(",")),
 | 
			
		||||
            form.milestoneId,
 | 
			
		||||
            form.priorityId,
 | 
			
		||||
            form.labelNames.toSeq.flatMap(_.split(",")),
 | 
			
		||||
@@ -356,15 +357,16 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
    html.labellist(getIssueLabels(repository.owner, repository.name, issueId))
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  ajaxPost("/:owner/:repository/issues/:id/assign")(writableUsersOnly { repository =>
 | 
			
		||||
    updateAssignedUserName(
 | 
			
		||||
      repository.owner,
 | 
			
		||||
      repository.name,
 | 
			
		||||
      params("id").toInt,
 | 
			
		||||
      assignedUserName("assignedUserName"),
 | 
			
		||||
      true
 | 
			
		||||
    )
 | 
			
		||||
    Ok("updated")
 | 
			
		||||
  ajaxPost("/:owner/:repository/issues/:id/assignee/new")(writableUsersOnly { repository =>
 | 
			
		||||
    val issueId = params("id").toInt
 | 
			
		||||
    registerIssueAssignee(repository.owner, repository.name, issueId, params("assigneeUserName"), true)
 | 
			
		||||
    Ok()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  ajaxPost("/:owner/:repository/issues/:id/assignee/delete")(writableUsersOnly { repository =>
 | 
			
		||||
    val issueId = params("id").toInt
 | 
			
		||||
    deleteIssueAssignee(repository.owner, repository.name, issueId, params("assigneeUserName"), true)
 | 
			
		||||
    Ok()
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  ajaxPost("/:owner/:repository/issues/:id/milestone")(writableUsersOnly { repository =>
 | 
			
		||||
@@ -455,7 +457,13 @@ trait IssuesControllerBase extends ControllerBase {
 | 
			
		||||
  post("/:owner/:repository/issues/batchedit/assign")(writableUsersOnly { repository =>
 | 
			
		||||
    val value = assignedUserName("value")
 | 
			
		||||
    executeBatch(repository) {
 | 
			
		||||
      updateAssignedUserName(repository.owner, repository.name, _, value, true)
 | 
			
		||||
      //updateAssignedUserName(repository.owner, repository.name, _, value, true)
 | 
			
		||||
      value match {
 | 
			
		||||
        case Some(assignedUserName) =>
 | 
			
		||||
          registerIssueAssignee(repository.owner, repository.name, _, assignedUserName, true)
 | 
			
		||||
        case None =>
 | 
			
		||||
          deleteAllIssueAssignees(repository.owner, repository.name, _, true)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (params("uri").nonEmpty) {
 | 
			
		||||
      redirect(params("uri"))
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,7 @@ trait LabelsControllerBase extends ControllerBase {
 | 
			
		||||
  get("/:owner/:repository/issues/labels")(referrersOnly { repository =>
 | 
			
		||||
    html.list(
 | 
			
		||||
      getLabels(repository.owner, repository.name),
 | 
			
		||||
      countIssueGroupByLabels(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
 | 
			
		||||
      countIssueGroupByLabels(repository.owner, repository.name, IssuesService.IssueSearchCondition()),
 | 
			
		||||
      repository,
 | 
			
		||||
      hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
 | 
			
		||||
    )
 | 
			
		||||
@@ -59,7 +59,7 @@ trait LabelsControllerBase extends ControllerBase {
 | 
			
		||||
    html.label(
 | 
			
		||||
      getLabel(repository.owner, repository.name, labelId).get,
 | 
			
		||||
      // TODO futility
 | 
			
		||||
      countIssueGroupByLabels(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
 | 
			
		||||
      countIssueGroupByLabels(repository.owner, repository.name, IssuesService.IssueSearchCondition()),
 | 
			
		||||
      repository,
 | 
			
		||||
      hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
 | 
			
		||||
    )
 | 
			
		||||
@@ -76,7 +76,7 @@ trait LabelsControllerBase extends ControllerBase {
 | 
			
		||||
    html.label(
 | 
			
		||||
      getLabel(repository.owner, repository.name, params("labelId").toInt).get,
 | 
			
		||||
      // TODO futility
 | 
			
		||||
      countIssueGroupByLabels(repository.owner, repository.name, IssuesService.IssueSearchCondition(), Map.empty),
 | 
			
		||||
      countIssueGroupByLabels(repository.owner, repository.name, IssuesService.IssueSearchCondition()),
 | 
			
		||||
      repository,
 | 
			
		||||
      hasDeveloperRole(repository.owner, repository.name, context.loginAccount)
 | 
			
		||||
    )
 | 
			
		||||
 
 | 
			
		||||
@@ -92,7 +92,7 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
    commitIdFrom: String,
 | 
			
		||||
    commitIdTo: String,
 | 
			
		||||
    isDraft: Boolean,
 | 
			
		||||
    assignedUserName: Option[String],
 | 
			
		||||
    assignedUserNames: Option[String],
 | 
			
		||||
    milestoneId: Option[Int],
 | 
			
		||||
    priorityId: Option[Int],
 | 
			
		||||
    labelNames: Option[String]
 | 
			
		||||
@@ -131,6 +131,7 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
              getPullRequestComments(repository.owner, repository.name, issue.issueId, commits.flatten),
 | 
			
		||||
              diffs.size,
 | 
			
		||||
              getIssueLabels(repository.owner, repository.name, issueId),
 | 
			
		||||
              getIssueAssignees(repository.owner, repository.name, issueId),
 | 
			
		||||
              getAssignableUserNames(repository.owner, repository.name),
 | 
			
		||||
              getMilestonesWithIssueCount(repository.owner, repository.name),
 | 
			
		||||
              getPriorities(repository.owner, repository.name),
 | 
			
		||||
@@ -571,7 +572,6 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
          loginUser = loginAccount.userName,
 | 
			
		||||
          title = form.title,
 | 
			
		||||
          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
 | 
			
		||||
@@ -591,8 +591,14 @@ trait PullRequestsControllerBase extends ControllerBase {
 | 
			
		||||
          settings = context.settings
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        // insert labels
 | 
			
		||||
        if (manageable) {
 | 
			
		||||
          // insert assignees
 | 
			
		||||
          form.assignedUserNames.foreach { value =>
 | 
			
		||||
            value.split(",").foreach { userName =>
 | 
			
		||||
              registerIssueAssignee(repository.owner, repository.name, issueId, userName)
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          // insert labels
 | 
			
		||||
          form.labelNames.foreach { value =>
 | 
			
		||||
            val labels = getLabels(repository.owner, repository.name)
 | 
			
		||||
            value.split(",").foreach { labelName =>
 | 
			
		||||
 
 | 
			
		||||
@@ -679,7 +679,6 @@ trait RepositoryViewerControllerBase extends ControllerBase {
 | 
			
		||||
      loginUser = loginAccount.userName,
 | 
			
		||||
      title = requestBranch,
 | 
			
		||||
      content = commitMessage,
 | 
			
		||||
      assignedUserName = None,
 | 
			
		||||
      milestoneId = None,
 | 
			
		||||
      priorityId = None,
 | 
			
		||||
      isPullRequest = true
 | 
			
		||||
 
 | 
			
		||||
@@ -29,9 +29,9 @@ trait ApiIssueControllerBase extends ControllerBase {
 | 
			
		||||
    val page = IssueSearchCondition.page(request)
 | 
			
		||||
    // TODO: more api spec condition
 | 
			
		||||
    val condition = IssueSearchCondition(request)
 | 
			
		||||
    val baseOwner = getAccountByUserName(repository.owner).get
 | 
			
		||||
    //val baseOwner = getAccountByUserName(repository.owner).get
 | 
			
		||||
 | 
			
		||||
    val issues: List[(Issue, Account, Option[Account])] =
 | 
			
		||||
    val issues: List[(Issue, Account, List[Account])] =
 | 
			
		||||
      searchIssueByApi(
 | 
			
		||||
        condition = condition,
 | 
			
		||||
        offset = (page - 1) * PullRequestLimit,
 | 
			
		||||
@@ -40,12 +40,12 @@ trait ApiIssueControllerBase extends ControllerBase {
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
    JsonFormat(issues.map {
 | 
			
		||||
      case (issue, issueUser, assignedUser) =>
 | 
			
		||||
      case (issue, issueUser, assigneeUsers) =>
 | 
			
		||||
        ApiIssue(
 | 
			
		||||
          issue = issue,
 | 
			
		||||
          repositoryName = RepositoryName(repository),
 | 
			
		||||
          user = ApiUser(issueUser),
 | 
			
		||||
          assignee = assignedUser.map(ApiUser(_)),
 | 
			
		||||
          assignees = assigneeUsers.map(ApiUser(_)),
 | 
			
		||||
          labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
            .map(ApiLabel(_, RepositoryName(repository))),
 | 
			
		||||
          issue.milestoneId.flatMap { getApiMilestone(repository, _) }
 | 
			
		||||
@@ -61,7 +61,8 @@ trait ApiIssueControllerBase extends ControllerBase {
 | 
			
		||||
    (for {
 | 
			
		||||
      issueId <- params("id").toIntOpt
 | 
			
		||||
      issue <- getIssue(repository.owner, repository.name, issueId.toString)
 | 
			
		||||
      users = getAccountsByUserNames(Set(issue.openedUserName) ++ issue.assignedUserName, Set())
 | 
			
		||||
      assigneeUsers = getIssueAssignees(repository.owner, repository.name, issueId)
 | 
			
		||||
      users = getAccountsByUserNames(Set(issue.openedUserName) ++ assigneeUsers.map(_.assigneeUserName), Set())
 | 
			
		||||
      openedUser <- users.get(issue.openedUserName)
 | 
			
		||||
    } yield {
 | 
			
		||||
      JsonFormat(
 | 
			
		||||
@@ -69,7 +70,7 @@ trait ApiIssueControllerBase extends ControllerBase {
 | 
			
		||||
          issue,
 | 
			
		||||
          RepositoryName(repository),
 | 
			
		||||
          ApiUser(openedUser),
 | 
			
		||||
          issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
 | 
			
		||||
          assigneeUsers.flatMap(x => users.get(x.assigneeUserName)).map(ApiUser(_)),
 | 
			
		||||
          getIssueLabels(repository.owner, repository.name, issue.issueId).map(ApiLabel(_, RepositoryName(repository))),
 | 
			
		||||
          issue.milestoneId.flatMap { getApiMilestone(repository, _) }
 | 
			
		||||
        )
 | 
			
		||||
@@ -92,7 +93,7 @@ trait ApiIssueControllerBase extends ControllerBase {
 | 
			
		||||
          repository,
 | 
			
		||||
          data.title,
 | 
			
		||||
          data.body,
 | 
			
		||||
          data.assignees.headOption,
 | 
			
		||||
          data.assignees,
 | 
			
		||||
          milestone.map(_.milestoneId),
 | 
			
		||||
          None,
 | 
			
		||||
          data.labels,
 | 
			
		||||
@@ -103,7 +104,9 @@ trait ApiIssueControllerBase extends ControllerBase {
 | 
			
		||||
            issue,
 | 
			
		||||
            RepositoryName(repository),
 | 
			
		||||
            ApiUser(loginAccount),
 | 
			
		||||
            issue.assignedUserName.flatMap(getAccountByUserName(_)).map(ApiUser(_)),
 | 
			
		||||
            getIssueAssignees(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
              .flatMap(x => getAccountByUserName(x.assigneeUserName, false))
 | 
			
		||||
              .map(ApiUser.apply),
 | 
			
		||||
            getIssueLabels(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
              .map(ApiLabel(_, RepositoryName(repository))),
 | 
			
		||||
            issue.milestoneId.flatMap { getApiMilestone(repository, _) }
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
    val condition = IssueSearchCondition(request)
 | 
			
		||||
    val baseOwner = getAccountByUserName(repository.owner).get
 | 
			
		||||
 | 
			
		||||
    val issues: List[(Issue, Account, Int, PullRequest, Repository, Account, Option[Account])] =
 | 
			
		||||
    val issues: List[(Issue, Account, Int, PullRequest, Repository, Account, List[Account])] =
 | 
			
		||||
      searchPullRequestByApi(
 | 
			
		||||
        condition = condition,
 | 
			
		||||
        offset = (page - 1) * PullRequestLimit,
 | 
			
		||||
@@ -49,7 +49,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
    JsonFormat(issues.map {
 | 
			
		||||
      case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner, assignee) =>
 | 
			
		||||
      case (issue, issueUser, commentCount, pullRequest, headRepo, headOwner, assignees) =>
 | 
			
		||||
        ApiPullRequest(
 | 
			
		||||
          issue = issue,
 | 
			
		||||
          pullRequest = pullRequest,
 | 
			
		||||
@@ -58,7 +58,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
          user = ApiUser(issueUser),
 | 
			
		||||
          labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
            .map(ApiLabel(_, RepositoryName(repository))),
 | 
			
		||||
          assignee = assignee.map(ApiUser.apply),
 | 
			
		||||
          assignees = assignees.map(ApiUser.apply),
 | 
			
		||||
          mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
        )
 | 
			
		||||
    })
 | 
			
		||||
@@ -99,7 +99,6 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
                      loginUser = context.loginAccount.get.userName,
 | 
			
		||||
                      title = createPullReq.title,
 | 
			
		||||
                      content = createPullReq.body,
 | 
			
		||||
                      assignedUserName = None,
 | 
			
		||||
                      milestoneId = None,
 | 
			
		||||
                      priorityId = None,
 | 
			
		||||
                      isPullRequest = true
 | 
			
		||||
@@ -319,8 +318,8 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
      baseOwner <- users.get(repository.owner)
 | 
			
		||||
      headOwner <- users.get(pullRequest.requestUserName)
 | 
			
		||||
      issueUser <- users.get(issue.openedUserName)
 | 
			
		||||
      assignee = issue.assignedUserName.flatMap { userName =>
 | 
			
		||||
        getAccountByUserName(userName, false)
 | 
			
		||||
      assignees = getIssueAssignees(repository.owner, repository.name, issueId).flatMap { assignedUser =>
 | 
			
		||||
        getAccountByUserName(assignedUser.assigneeUserName, false)
 | 
			
		||||
      }
 | 
			
		||||
      headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
 | 
			
		||||
    } yield {
 | 
			
		||||
@@ -332,7 +331,7 @@ trait ApiPullRequestControllerBase extends ControllerBase {
 | 
			
		||||
        user = ApiUser(issueUser),
 | 
			
		||||
        labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
          .map(ApiLabel(_, RepositoryName(repository))),
 | 
			
		||||
        assignee = assignee.map(ApiUser.apply),
 | 
			
		||||
        assignees = assignees.map(ApiUser.apply),
 | 
			
		||||
        mergedComment = getMergedComment(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,6 @@ trait IssueComponent extends TemplateComponent { self: Profile =>
 | 
			
		||||
      with MilestoneTemplate
 | 
			
		||||
      with PriorityTemplate {
 | 
			
		||||
    val openedUserName = column[String]("OPENED_USER_NAME")
 | 
			
		||||
    val assignedUserName = column[String]("ASSIGNED_USER_NAME")
 | 
			
		||||
    val title = column[String]("TITLE")
 | 
			
		||||
    val content = column[String]("CONTENT")
 | 
			
		||||
    val closed = column[Boolean]("CLOSED")
 | 
			
		||||
@@ -42,7 +41,6 @@ trait IssueComponent extends TemplateComponent { self: Profile =>
 | 
			
		||||
        openedUserName,
 | 
			
		||||
        milestoneId.?,
 | 
			
		||||
        priorityId.?,
 | 
			
		||||
        assignedUserName.?,
 | 
			
		||||
        title,
 | 
			
		||||
        content.?,
 | 
			
		||||
        closed,
 | 
			
		||||
@@ -62,7 +60,6 @@ case class Issue(
 | 
			
		||||
  openedUserName: String,
 | 
			
		||||
  milestoneId: Option[Int],
 | 
			
		||||
  priorityId: Option[Int],
 | 
			
		||||
  assignedUserName: Option[String],
 | 
			
		||||
  title: String,
 | 
			
		||||
  content: Option[String],
 | 
			
		||||
  closed: Boolean,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								src/main/scala/gitbucket/core/model/IssueAssignee.scala
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/main/scala/gitbucket/core/model/IssueAssignee.scala
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
package gitbucket.core.model
 | 
			
		||||
 | 
			
		||||
trait IssueAssigneeComponent extends TemplateComponent { self: Profile =>
 | 
			
		||||
  import profile.api._
 | 
			
		||||
  import self._
 | 
			
		||||
 | 
			
		||||
  lazy val IssueAssignees = TableQuery[IssueAssignees]
 | 
			
		||||
 | 
			
		||||
  class IssueAssignees(tag: Tag) extends Table[IssueAssignee](tag, "ISSUE_ASSIGNEE") with IssueTemplate {
 | 
			
		||||
    val assigneeUserName = column[String]("ASSIGNEE_USER_NAME")
 | 
			
		||||
    def * =
 | 
			
		||||
      (userName, repositoryName, issueId, assigneeUserName)
 | 
			
		||||
        .<>(IssueAssignee.tupled, IssueAssignee.unapply)
 | 
			
		||||
 | 
			
		||||
    def byPrimaryKey(owner: String, repository: String, issueId: Int, assigneeUserName: String) = {
 | 
			
		||||
      byIssue(owner, repository, issueId) && this.assigneeUserName === assigneeUserName.bind
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
case class IssueAssignee(
 | 
			
		||||
  userName: String,
 | 
			
		||||
  repositoryName: String,
 | 
			
		||||
  issueId: Int,
 | 
			
		||||
  assigneeUserName: String
 | 
			
		||||
)
 | 
			
		||||
@@ -74,5 +74,6 @@ trait CoreProfile
 | 
			
		||||
    with AccountPreferenceComponent
 | 
			
		||||
    with CustomFieldComponent
 | 
			
		||||
    with IssueCustomFieldComponent
 | 
			
		||||
    with IssueAssigneeComponent
 | 
			
		||||
 | 
			
		||||
object Profile extends CoreProfile
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ trait IssueCreationService {
 | 
			
		||||
    repository: RepositoryInfo,
 | 
			
		||||
    title: String,
 | 
			
		||||
    body: Option[String],
 | 
			
		||||
    assignee: Option[String],
 | 
			
		||||
    assignees: Seq[String],
 | 
			
		||||
    milestoneId: Option[Int],
 | 
			
		||||
    priorityId: Option[Int],
 | 
			
		||||
    labelNames: Seq[String],
 | 
			
		||||
@@ -35,16 +35,19 @@ trait IssueCreationService {
 | 
			
		||||
      userName,
 | 
			
		||||
      title,
 | 
			
		||||
      body,
 | 
			
		||||
      if (manageable) assignee else None,
 | 
			
		||||
      if (manageable) milestoneId else None,
 | 
			
		||||
      if (manageable) priorityId else None
 | 
			
		||||
    )
 | 
			
		||||
    val issue: Issue = getIssue(owner, name, issueId.toString).get
 | 
			
		||||
 | 
			
		||||
    // insert labels
 | 
			
		||||
    if (manageable) {
 | 
			
		||||
      // insert assignees
 | 
			
		||||
      assignees.foreach { assignee =>
 | 
			
		||||
        registerIssueAssignee(owner, name, issueId, assignee)
 | 
			
		||||
      }
 | 
			
		||||
      // insert labels
 | 
			
		||||
      val labels = getLabels(owner, name)
 | 
			
		||||
      labelNames.map { labelName =>
 | 
			
		||||
      labelNames.foreach { labelName =>
 | 
			
		||||
        labels.find(_.labelName == labelName).map { label =>
 | 
			
		||||
          registerIssueLabel(owner, name, issueId, label.labelId)
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,17 @@ import gitbucket.core.util.StringUtil._
 | 
			
		||||
import gitbucket.core.util.Implicits._
 | 
			
		||||
import gitbucket.core.util.SyntaxSugars._
 | 
			
		||||
import gitbucket.core.controller.Context
 | 
			
		||||
import gitbucket.core.model.{Account, Issue, IssueComment, IssueLabel, Label, PullRequest, Repository, Role}
 | 
			
		||||
import gitbucket.core.model.{
 | 
			
		||||
  Account,
 | 
			
		||||
  Issue,
 | 
			
		||||
  IssueAssignee,
 | 
			
		||||
  IssueComment,
 | 
			
		||||
  IssueLabel,
 | 
			
		||||
  Label,
 | 
			
		||||
  PullRequest,
 | 
			
		||||
  Repository,
 | 
			
		||||
  Role
 | 
			
		||||
}
 | 
			
		||||
import gitbucket.core.model.Profile._
 | 
			
		||||
import gitbucket.core.model.Profile.profile._
 | 
			
		||||
import gitbucket.core.model.Profile.profile.blockingApi._
 | 
			
		||||
@@ -118,8 +128,7 @@ trait IssuesService {
 | 
			
		||||
  def countIssueGroupByLabels(
 | 
			
		||||
    owner: String,
 | 
			
		||||
    repository: String,
 | 
			
		||||
    condition: IssueSearchCondition,
 | 
			
		||||
    filterUser: Map[String, String]
 | 
			
		||||
    condition: IssueSearchCondition
 | 
			
		||||
  )(implicit s: Session): Map[String, Int] = {
 | 
			
		||||
 | 
			
		||||
    searchIssueQuery(Seq(owner -> repository), condition.copy(labels = Set.empty), IssueSearchOption.Issues)
 | 
			
		||||
@@ -244,20 +253,31 @@ trait IssuesService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** for api
 | 
			
		||||
   * @return (issue, issueUser, assignedUser)
 | 
			
		||||
   * @return (issue, issueUser, Seq(assigneeUsers))
 | 
			
		||||
   */
 | 
			
		||||
  def searchIssueByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  ): List[(Issue, Account, Option[Account])] = {
 | 
			
		||||
  ): List[(Issue, Account, List[Account])] = {
 | 
			
		||||
    // get issues and comment count and labels
 | 
			
		||||
    searchIssueQueryBase(condition, IssueSearchOption.Issues, offset, limit, repos)
 | 
			
		||||
      .join(Accounts)
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 => t3.userName === t1.openedUserName }
 | 
			
		||||
      .joinLeft(IssueAssignees)
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 ~ t4 => t4.byIssue(t1.userName, t1.repositoryName, t1.issueId) }
 | 
			
		||||
      .joinLeft(Accounts)
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 ~ t4 => t4.userName === t1.assignedUserName }
 | 
			
		||||
      .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 => i asc }
 | 
			
		||||
      .map { case t1 ~ t2 ~ i ~ t3 ~ t4 => (t1, t3, t4) }
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => t5.userName === t4.map(_.assigneeUserName) }
 | 
			
		||||
      .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => i asc }
 | 
			
		||||
      .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => (t1, t3, t5) }
 | 
			
		||||
      .list
 | 
			
		||||
      .groupBy {
 | 
			
		||||
        case (issue, account, assignedUsers) =>
 | 
			
		||||
          (issue, account)
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        case (_, values) =>
 | 
			
		||||
          (values.head._1, values.head._2, values.flatMap(_._3))
 | 
			
		||||
      }
 | 
			
		||||
      .toList
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** for api
 | 
			
		||||
@@ -265,7 +285,7 @@ trait IssuesService {
 | 
			
		||||
   */
 | 
			
		||||
  def searchPullRequestByApi(condition: IssueSearchCondition, offset: Int, limit: Int, repos: (String, String)*)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  ): List[(Issue, Account, Int, PullRequest, Repository, Account, Option[Account])] = {
 | 
			
		||||
  ): List[(Issue, Account, Int, PullRequest, Repository, Account, List[Account])] = {
 | 
			
		||||
    // get issues and comment count and labels
 | 
			
		||||
    searchIssueQueryBase(condition, IssueSearchOption.PullRequests, offset, limit, repos)
 | 
			
		||||
      .join(PullRequests)
 | 
			
		||||
@@ -276,11 +296,30 @@ trait IssuesService {
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 => t5.userName === t1.openedUserName }
 | 
			
		||||
      .join(Accounts)
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 => t6.userName === t4.userName }
 | 
			
		||||
      .joinLeft(IssueAssignees)
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 => t7.byIssue(t1.userName, t1.repositoryName, t1.issueId) }
 | 
			
		||||
      .joinLeft(Accounts)
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 => t7.userName === t1.assignedUserName }
 | 
			
		||||
      .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 => i asc }
 | 
			
		||||
      .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 => (t1, t5, t2.commentCount, t3, t4, t6, t7) }
 | 
			
		||||
      .on { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => t8.userName === t7.map(_.assigneeUserName) }
 | 
			
		||||
      .sortBy { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => i asc }
 | 
			
		||||
      .map { case t1 ~ t2 ~ i ~ t3 ~ t4 ~ t5 ~ t6 ~ t7 ~ t8 => (t1, t5, t2.commentCount, t3, t4, t6, t8) }
 | 
			
		||||
      .list
 | 
			
		||||
      .groupBy {
 | 
			
		||||
        case (issue, openedUser, commentCount, pullRequest, repository, account, assignedUser) =>
 | 
			
		||||
          (issue, openedUser, commentCount, pullRequest, repository, account)
 | 
			
		||||
      }
 | 
			
		||||
      .map {
 | 
			
		||||
        case (_, values) =>
 | 
			
		||||
          (
 | 
			
		||||
            values.head._1,
 | 
			
		||||
            values.head._2,
 | 
			
		||||
            values.head._3,
 | 
			
		||||
            values.head._4,
 | 
			
		||||
            values.head._5,
 | 
			
		||||
            values.head._6,
 | 
			
		||||
            values.flatMap(_._7)
 | 
			
		||||
          )
 | 
			
		||||
      }
 | 
			
		||||
      .toList
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private def searchIssueQueryBase(
 | 
			
		||||
@@ -347,7 +386,7 @@ trait IssuesService {
 | 
			
		||||
        case _        => t1.closed === true || t1.closed === false
 | 
			
		||||
      }).&&(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) &&
 | 
			
		||||
      (searchOption match {
 | 
			
		||||
        case IssueSearchOption.Issues       => t1.pullRequest === false
 | 
			
		||||
@@ -371,7 +410,13 @@ trait IssuesService {
 | 
			
		||||
          condition.priority.flatten.isDefined
 | 
			
		||||
        )
 | 
			
		||||
        // Assignee filter
 | 
			
		||||
        .&&(t1.assignedUserName === condition.assigned.get.get.bind, condition.assigned.flatten.isDefined)
 | 
			
		||||
        .&&(
 | 
			
		||||
          IssueAssignees filter { a =>
 | 
			
		||||
            a.byIssue(t1.userName, t1.repositoryName, t1.issueId) &&
 | 
			
		||||
            a.assigneeUserName === condition.assigned.get.get.bind
 | 
			
		||||
          } exists,
 | 
			
		||||
          condition.assigned.flatten.isDefined
 | 
			
		||||
        )
 | 
			
		||||
        // Label filter
 | 
			
		||||
        .&&(
 | 
			
		||||
          IssueLabels filter { t2 =>
 | 
			
		||||
@@ -396,7 +441,9 @@ trait IssuesService {
 | 
			
		||||
        .&&(t1.userName inSetBind condition.groups, condition.groups.nonEmpty)
 | 
			
		||||
        // Mentioned filter
 | 
			
		||||
        .&&(
 | 
			
		||||
          (t1.openedUserName === condition.mentioned.get.bind) || t1.assignedUserName === condition.mentioned.get.bind ||
 | 
			
		||||
          (t1.openedUserName === condition.mentioned.get.bind) || (IssueAssignees filter { t1 =>
 | 
			
		||||
            t1.byIssue(t1.userName, t1.repositoryName, t1.issueId) && t1.assigneeUserName === condition.mentioned.get.bind
 | 
			
		||||
          } exists) ||
 | 
			
		||||
            (IssueComments filter { t2 =>
 | 
			
		||||
              (t2.byIssue(t1.userName, t1.repositoryName, t1.issueId)) && (t2.commentedUserName === condition.mentioned.get.bind)
 | 
			
		||||
            } exists),
 | 
			
		||||
@@ -410,7 +457,6 @@ trait IssuesService {
 | 
			
		||||
    loginUser: String,
 | 
			
		||||
    title: String,
 | 
			
		||||
    content: Option[String],
 | 
			
		||||
    assignedUserName: Option[String],
 | 
			
		||||
    milestoneId: Option[Int],
 | 
			
		||||
    priorityId: Option[Int],
 | 
			
		||||
    isPullRequest: Boolean = false
 | 
			
		||||
@@ -427,7 +473,6 @@ trait IssuesService {
 | 
			
		||||
          loginUser,
 | 
			
		||||
          milestoneId,
 | 
			
		||||
          priorityId,
 | 
			
		||||
          assignedUserName,
 | 
			
		||||
          title,
 | 
			
		||||
          content,
 | 
			
		||||
          false,
 | 
			
		||||
@@ -542,35 +587,91 @@ trait IssuesService {
 | 
			
		||||
      .update(true)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def updateAssignedUserName(
 | 
			
		||||
  def getIssueAssignees(owner: String, repository: String, issueId: Int)(
 | 
			
		||||
    implicit s: Session
 | 
			
		||||
  ): List[IssueAssignee] = {
 | 
			
		||||
    IssueAssignees.filter(_.byIssue(owner, repository, issueId)).sortBy(_.assigneeUserName).list
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def registerIssueAssignee(
 | 
			
		||||
    owner: String,
 | 
			
		||||
    repository: String,
 | 
			
		||||
    issueId: Int,
 | 
			
		||||
    assignedUserName: Option[String],
 | 
			
		||||
    assigneeUserName: String,
 | 
			
		||||
    insertComment: Boolean = false
 | 
			
		||||
  )(implicit context: Context, s: Session): Int = {
 | 
			
		||||
    val oldAssigned = getIssue(owner, repository, s"${issueId}").get.assignedUserName
 | 
			
		||||
    val assigned = assignedUserName
 | 
			
		||||
  )(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    val assigner = context.loginAccount.map(_.userName)
 | 
			
		||||
    if (insertComment) {
 | 
			
		||||
      IssueComments insert IssueComment(
 | 
			
		||||
        userName = owner,
 | 
			
		||||
        repositoryName = repository,
 | 
			
		||||
        issueId = issueId,
 | 
			
		||||
        action = "assign",
 | 
			
		||||
        action = "add_assignee",
 | 
			
		||||
        commentedUserName = assigner.getOrElse("Unknown user"),
 | 
			
		||||
        content = s"""${oldAssigned.getOrElse("Not assigned")}:${assigned.getOrElse("Not assigned")}""",
 | 
			
		||||
        content = assigneeUserName,
 | 
			
		||||
        registeredDate = currentDate,
 | 
			
		||||
        updatedDate = currentDate
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
    for (issue <- getIssue(owner, repository, issueId.toString); repo <- getRepository(owner, repository)) {
 | 
			
		||||
      PluginRegistry().getIssueHooks.foreach(_.assigned(issue, repo, assigner, assigned, oldAssigned))
 | 
			
		||||
      PluginRegistry().getIssueHooks.foreach(_.assigned(issue, repo, assigner, Some(assigneeUserName), None))
 | 
			
		||||
    }
 | 
			
		||||
    Issues
 | 
			
		||||
      .filter(_.byPrimaryKey(owner, repository, issueId))
 | 
			
		||||
      .map(t => (t.assignedUserName ?, t.updatedDate))
 | 
			
		||||
      .update(assignedUserName, currentDate)
 | 
			
		||||
    IssueAssignees insert IssueAssignee(owner, repository, issueId, assigneeUserName)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def deleteIssueAssignee(
 | 
			
		||||
    owner: String,
 | 
			
		||||
    repository: String,
 | 
			
		||||
    issueId: Int,
 | 
			
		||||
    assigneeUserName: String,
 | 
			
		||||
    insertComment: Boolean = false
 | 
			
		||||
  )(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    val assigner = context.loginAccount.map(_.userName)
 | 
			
		||||
    if (insertComment) {
 | 
			
		||||
      IssueComments insert IssueComment(
 | 
			
		||||
        userName = owner,
 | 
			
		||||
        repositoryName = repository,
 | 
			
		||||
        issueId = issueId,
 | 
			
		||||
        action = "delete_assignee",
 | 
			
		||||
        commentedUserName = assigner.getOrElse("Unknown user"),
 | 
			
		||||
        content = assigneeUserName,
 | 
			
		||||
        registeredDate = currentDate,
 | 
			
		||||
        updatedDate = currentDate
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO Notify plugins of unassignment as doing in registerIssueAssignee()?
 | 
			
		||||
 | 
			
		||||
    IssueAssignees filter (_.byPrimaryKey(owner, repository, issueId, assigneeUserName)) delete
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def deleteAllIssueAssignees(owner: String, repository: String, issueId: Int, insertComment: Boolean = false)(
 | 
			
		||||
    implicit context: Context,
 | 
			
		||||
    s: Session
 | 
			
		||||
  ): Int = {
 | 
			
		||||
    val assigner = context.loginAccount.map(_.userName)
 | 
			
		||||
    if (insertComment) {
 | 
			
		||||
      IssueComments insert IssueComment(
 | 
			
		||||
        userName = owner,
 | 
			
		||||
        repositoryName = repository,
 | 
			
		||||
        issueId = issueId,
 | 
			
		||||
        action = "delete_assign",
 | 
			
		||||
        commentedUserName = assigner.getOrElse("Unknown user"),
 | 
			
		||||
        content = "All assignees",
 | 
			
		||||
        registeredDate = currentDate,
 | 
			
		||||
        updatedDate = currentDate
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO Notify plugins of unassignment as doing in registerIssueAssignee()?
 | 
			
		||||
 | 
			
		||||
    IssueAssignees filter (_.byIssue(owner, repository, issueId)) delete
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  def updateMilestoneId(
 | 
			
		||||
 
 | 
			
		||||
@@ -379,8 +379,12 @@ trait WebHookPullRequestService extends WebHookService {
 | 
			
		||||
    settings: SystemSettings
 | 
			
		||||
  )(implicit s: Session, context: JsonFormat.Context): Unit = {
 | 
			
		||||
    callWebHookOf(repository.owner, repository.name, WebHook.Issues, settings) {
 | 
			
		||||
      val assigneeUsers = getIssueAssignees(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
      val users =
 | 
			
		||||
        getAccountsByUserNames(Set(repository.owner, issue.openedUserName) ++ issue.assignedUserName, Set(sender))
 | 
			
		||||
        getAccountsByUserNames(
 | 
			
		||||
          Set(repository.owner, issue.openedUserName) ++ assigneeUsers.map(_.assigneeUserName),
 | 
			
		||||
          Set(sender)
 | 
			
		||||
        )
 | 
			
		||||
      for {
 | 
			
		||||
        repoOwner <- users.get(repository.owner)
 | 
			
		||||
        issueUser <- users.get(issue.openedUserName)
 | 
			
		||||
@@ -393,7 +397,7 @@ trait WebHookPullRequestService extends WebHookService {
 | 
			
		||||
            issue,
 | 
			
		||||
            RepositoryName(repository),
 | 
			
		||||
            ApiUser(issueUser),
 | 
			
		||||
            issue.assignedUserName.flatMap(users.get(_)).map(ApiUser(_)),
 | 
			
		||||
            assigneeUsers.flatMap(x => users.get(x.assigneeUserName)).map(ApiUser(_)),
 | 
			
		||||
            getIssueLabels(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
              .map(ApiLabel(_, RepositoryName(repository))),
 | 
			
		||||
            getApiMilestone(repository, issue.milestoneId getOrElse (0))
 | 
			
		||||
@@ -415,16 +419,15 @@ trait WebHookPullRequestService extends WebHookService {
 | 
			
		||||
    callWebHookOf(repository.owner, repository.name, WebHook.PullRequest, settings) {
 | 
			
		||||
      for {
 | 
			
		||||
        (issue, pullRequest) <- getPullRequest(repository.owner, repository.name, issueId)
 | 
			
		||||
        assignees = getIssueAssignees(repository.owner, repository.name, issueId)
 | 
			
		||||
        users = getAccountsByUserNames(
 | 
			
		||||
          Set(repository.owner, pullRequest.requestUserName, issue.openedUserName),
 | 
			
		||||
          Set(repository.owner, pullRequest.requestUserName, issue.openedUserName) ++ assignees.map(_.assigneeUserName),
 | 
			
		||||
          Set(sender)
 | 
			
		||||
        )
 | 
			
		||||
        baseOwner <- users.get(repository.owner)
 | 
			
		||||
        headOwner <- users.get(pullRequest.requestUserName)
 | 
			
		||||
        issueUser <- users.get(issue.openedUserName)
 | 
			
		||||
        assignee = issue.assignedUserName.flatMap { userName =>
 | 
			
		||||
          getAccountByUserName(userName, false)
 | 
			
		||||
        }
 | 
			
		||||
        assigneeUsers = assignees.flatMap(x => users.get(x.assigneeUserName))
 | 
			
		||||
        headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
 | 
			
		||||
        labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
          .map(ApiLabel(_, RepositoryName(repository)))
 | 
			
		||||
@@ -433,7 +436,7 @@ trait WebHookPullRequestService extends WebHookService {
 | 
			
		||||
          action = action,
 | 
			
		||||
          issue = issue,
 | 
			
		||||
          issueUser = issueUser,
 | 
			
		||||
          assignee = assignee,
 | 
			
		||||
          assignees = assigneeUsers,
 | 
			
		||||
          pullRequest = pullRequest,
 | 
			
		||||
          headRepository = headRepo,
 | 
			
		||||
          headOwner = headOwner,
 | 
			
		||||
@@ -481,8 +484,8 @@ trait WebHookPullRequestService extends WebHookService {
 | 
			
		||||
        requestRepository.name,
 | 
			
		||||
        requestBranch
 | 
			
		||||
      )
 | 
			
		||||
      assignee = issue.assignedUserName.flatMap { userName =>
 | 
			
		||||
        getAccountByUserName(userName, false)
 | 
			
		||||
      assignees = getIssueAssignees(requestRepository.owner, requestRepository.name, issue.issueId).flatMap { x =>
 | 
			
		||||
        getAccountByUserName(x.assigneeUserName, false)
 | 
			
		||||
      }
 | 
			
		||||
      baseRepo <- getRepository(pullRequest.userName, pullRequest.repositoryName)
 | 
			
		||||
      labels = getIssueLabels(pullRequest.userName, pullRequest.repositoryName, issue.issueId)
 | 
			
		||||
@@ -492,7 +495,7 @@ trait WebHookPullRequestService extends WebHookService {
 | 
			
		||||
        action = action,
 | 
			
		||||
        issue = issue,
 | 
			
		||||
        issueUser = issueUser,
 | 
			
		||||
        assignee = assignee,
 | 
			
		||||
        assignees = assignees,
 | 
			
		||||
        pullRequest = pullRequest,
 | 
			
		||||
        headRepository = requestRepository,
 | 
			
		||||
        headOwner = headOwner,
 | 
			
		||||
@@ -522,15 +525,17 @@ trait WebHookPullRequestReviewCommentService extends WebHookService {
 | 
			
		||||
  )(implicit s: Session, c: JsonFormat.Context): Unit = {
 | 
			
		||||
    import WebHookService._
 | 
			
		||||
    callWebHookOf(repository.owner, repository.name, WebHook.PullRequestReviewComment, settings) {
 | 
			
		||||
      val assignees = getIssueAssignees(repository.owner, pullRequest.requestUserName, issue.issueId)
 | 
			
		||||
      val users =
 | 
			
		||||
        getAccountsByUserNames(Set(repository.owner, pullRequest.requestUserName, issue.openedUserName), Set(sender))
 | 
			
		||||
        getAccountsByUserNames(
 | 
			
		||||
          Set(repository.owner, pullRequest.requestUserName, issue.openedUserName) ++ assignees.map(_.assigneeUserName),
 | 
			
		||||
          Set(sender)
 | 
			
		||||
        )
 | 
			
		||||
      for {
 | 
			
		||||
        baseOwner <- users.get(repository.owner)
 | 
			
		||||
        headOwner <- users.get(pullRequest.requestUserName)
 | 
			
		||||
        issueUser <- users.get(issue.openedUserName)
 | 
			
		||||
        assignee = issue.assignedUserName.flatMap { userName =>
 | 
			
		||||
          getAccountByUserName(userName, false)
 | 
			
		||||
        }
 | 
			
		||||
        assigneeUsers = assignees.flatMap(x => users.get(x.assigneeUserName))
 | 
			
		||||
        headRepo <- getRepository(pullRequest.requestUserName, pullRequest.requestRepositoryName)
 | 
			
		||||
        labels = getIssueLabels(pullRequest.userName, pullRequest.repositoryName, issue.issueId)
 | 
			
		||||
          .map(ApiLabel(_, RepositoryName(pullRequest.userName, pullRequest.repositoryName)))
 | 
			
		||||
@@ -540,7 +545,7 @@ trait WebHookPullRequestReviewCommentService extends WebHookService {
 | 
			
		||||
          comment = comment,
 | 
			
		||||
          issue = issue,
 | 
			
		||||
          issueUser = issueUser,
 | 
			
		||||
          assignee = assignee,
 | 
			
		||||
          assignees = assigneeUsers,
 | 
			
		||||
          pullRequest = pullRequest,
 | 
			
		||||
          headRepository = headRepo,
 | 
			
		||||
          headOwner = headOwner,
 | 
			
		||||
@@ -569,14 +574,17 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
 | 
			
		||||
    callWebHookOf(repository.owner, repository.name, WebHook.IssueComment, settings) {
 | 
			
		||||
      for {
 | 
			
		||||
        issueComment <- getComment(repository.owner, repository.name, issueCommentId.toString())
 | 
			
		||||
        assignees = getIssueAssignees(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
        users = getAccountsByUserNames(
 | 
			
		||||
          Set(issue.openedUserName, repository.owner, issueComment.commentedUserName) ++ issue.assignedUserName,
 | 
			
		||||
          Set(issue.openedUserName, repository.owner, issueComment.commentedUserName) ++ assignees.map(
 | 
			
		||||
            _.assigneeUserName
 | 
			
		||||
          ),
 | 
			
		||||
          Set(sender)
 | 
			
		||||
        )
 | 
			
		||||
        issueUser <- users.get(issue.openedUserName)
 | 
			
		||||
        repoOwner <- users.get(repository.owner)
 | 
			
		||||
        commenter <- users.get(issueComment.commentedUserName)
 | 
			
		||||
        assignedUser = issue.assignedUserName.flatMap(users.get(_))
 | 
			
		||||
        assigneeUsers = assignees.flatMap(x => users.get(x.assigneeUserName))
 | 
			
		||||
        labels = getIssueLabels(repository.owner, repository.name, issue.issueId)
 | 
			
		||||
        milestone = getApiMilestone(repository, issue.milestoneId getOrElse (0))
 | 
			
		||||
      } yield {
 | 
			
		||||
@@ -587,7 +595,7 @@ trait WebHookIssueCommentService extends WebHookPullRequestService {
 | 
			
		||||
          commentUser = commenter,
 | 
			
		||||
          repository = repository,
 | 
			
		||||
          repositoryUser = repoOwner,
 | 
			
		||||
          assignedUser = assignedUser,
 | 
			
		||||
          assignees = assigneeUsers,
 | 
			
		||||
          sender = sender,
 | 
			
		||||
          labels = labels,
 | 
			
		||||
          milestone = milestone
 | 
			
		||||
@@ -710,7 +718,7 @@ object WebHookService {
 | 
			
		||||
      action: String,
 | 
			
		||||
      issue: Issue,
 | 
			
		||||
      issueUser: Account,
 | 
			
		||||
      assignee: Option[Account],
 | 
			
		||||
      assignees: List[Account],
 | 
			
		||||
      pullRequest: PullRequest,
 | 
			
		||||
      headRepository: RepositoryInfo,
 | 
			
		||||
      headOwner: Account,
 | 
			
		||||
@@ -731,7 +739,7 @@ object WebHookService {
 | 
			
		||||
        baseRepo = baseRepoPayload,
 | 
			
		||||
        user = ApiUser(issueUser),
 | 
			
		||||
        labels = labels,
 | 
			
		||||
        assignee = assignee.map(ApiUser.apply),
 | 
			
		||||
        assignees = assignees.map(ApiUser.apply),
 | 
			
		||||
        mergedComment = mergedComment
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
@@ -762,7 +770,7 @@ object WebHookService {
 | 
			
		||||
      commentUser: Account,
 | 
			
		||||
      repository: RepositoryInfo,
 | 
			
		||||
      repositoryUser: Account,
 | 
			
		||||
      assignedUser: Option[Account],
 | 
			
		||||
      assignees: List[Account],
 | 
			
		||||
      sender: Account,
 | 
			
		||||
      labels: List[Label],
 | 
			
		||||
      milestone: Option[ApiMilestone]
 | 
			
		||||
@@ -774,7 +782,7 @@ object WebHookService {
 | 
			
		||||
          issue,
 | 
			
		||||
          RepositoryName(repository),
 | 
			
		||||
          ApiUser(issueUser),
 | 
			
		||||
          assignedUser.map(ApiUser(_)),
 | 
			
		||||
          assignees.map(ApiUser(_)),
 | 
			
		||||
          labels.map(ApiLabel(_, RepositoryName(repository))),
 | 
			
		||||
          milestone
 | 
			
		||||
        ),
 | 
			
		||||
@@ -799,7 +807,7 @@ object WebHookService {
 | 
			
		||||
      comment: CommitComment,
 | 
			
		||||
      issue: Issue,
 | 
			
		||||
      issueUser: Account,
 | 
			
		||||
      assignee: Option[Account],
 | 
			
		||||
      assignees: List[Account],
 | 
			
		||||
      pullRequest: PullRequest,
 | 
			
		||||
      headRepository: RepositoryInfo,
 | 
			
		||||
      headOwner: Account,
 | 
			
		||||
@@ -828,7 +836,7 @@ object WebHookService {
 | 
			
		||||
          baseRepo = baseRepoPayload,
 | 
			
		||||
          user = ApiUser(issueUser),
 | 
			
		||||
          labels = labels,
 | 
			
		||||
          assignee = assignee.map(ApiUser.apply),
 | 
			
		||||
          assignees = assignees.map(ApiUser.apply),
 | 
			
		||||
          mergedComment = mergedComment
 | 
			
		||||
        ),
 | 
			
		||||
        repository = baseRepoPayload,
 | 
			
		||||
 
 | 
			
		||||
@@ -33,9 +33,11 @@
 | 
			
		||||
            <span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span>
 | 
			
		||||
          }
 | 
			
		||||
          <span class="pull-right muted">
 | 
			
		||||
            @*
 | 
			
		||||
            @issue.assignedUserName.map { userName =>
 | 
			
		||||
              @helpers.avatar(userName, 20, tooltip = true)
 | 
			
		||||
            }
 | 
			
		||||
            *@
 | 
			
		||||
            @if(commentCount > 0){
 | 
			
		||||
              <a href="@context.path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">
 | 
			
		||||
                <i class="octicon octicon-comment active"></i> @commentCount
 | 
			
		||||
 
 | 
			
		||||
@@ -200,7 +200,7 @@
 | 
			
		||||
            <span class="discussion-item-icon"><i class="octicon octicon-tag"></i></span>
 | 
			
		||||
            @helpers.avatarLink(comment.commentedUserName, 16)
 | 
			
		||||
            @helpers.user(comment.commentedUserName, styleClass="username strong")
 | 
			
		||||
            add the <code>@comment.content</code> label
 | 
			
		||||
            added the <code>@comment.content</code> label
 | 
			
		||||
            @gitbucket.core.helper.html.datetimeago(comment.registeredDate)
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -222,7 +222,7 @@
 | 
			
		||||
            <span class="discussion-item-icon"><i class="octicon octicon-flame"></i></span>
 | 
			
		||||
            @helpers.avatarLink(comment.commentedUserName, 16)
 | 
			
		||||
            @helpers.user(comment.commentedUserName, styleClass="username strong")
 | 
			
		||||
            change priority from <code>@comment.content.split(":")(0)</code> to <code>@comment.content.split(":")(1)</code>
 | 
			
		||||
            changed priority from <code>@comment.content.split(":")(0)</code> to <code>@comment.content.split(":")(1)</code>
 | 
			
		||||
            @gitbucket.core.helper.html.datetimeago(comment.registeredDate)
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -233,18 +233,40 @@
 | 
			
		||||
            <span class="discussion-item-icon"><i class="octicon octicon-milestone"></i></span>
 | 
			
		||||
            @helpers.avatarLink(comment.commentedUserName, 16)
 | 
			
		||||
            @helpers.user(comment.commentedUserName, styleClass="username strong")
 | 
			
		||||
            change milestone from <code>@comment.content.split(":")(0)</code> to <code>@comment.content.split(":")(1)</code>
 | 
			
		||||
            changed milestone from <code>@comment.content.split(":")(0)</code> to <code>@comment.content.split(":")(1)</code>
 | 
			
		||||
            @gitbucket.core.helper.html.datetimeago(comment.registeredDate)
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      }
 | 
			
		||||
      case "assign" => {
 | 
			
		||||
      case "assign" => { @* for backward compatibility *@
 | 
			
		||||
        <div class="discussion-item discussion-item-assign">
 | 
			
		||||
          <div class="discussion-item-header">
 | 
			
		||||
            <span class="discussion-item-icon"><i class="octicon octicon-person"></i></span>
 | 
			
		||||
            @helpers.avatarLink(comment.commentedUserName, 16)
 | 
			
		||||
            @helpers.user(comment.commentedUserName, styleClass="username strong")
 | 
			
		||||
            change assignee from <code>@comment.content.split(":")(0)</code> to <code>@comment.content.split(":")(1)</code>
 | 
			
		||||
            changed assignee from <code>@comment.content.split(":")(0)</code> to <code>@comment.content.split(":")(1)</code>
 | 
			
		||||
            @gitbucket.core.helper.html.datetimeago(comment.registeredDate)
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      }
 | 
			
		||||
      case "add_assignee" => {
 | 
			
		||||
        <div class="discussion-item discussion-item-assign">
 | 
			
		||||
          <div class="discussion-item-header">
 | 
			
		||||
            <span class="discussion-item-icon"><i class="octicon octicon-person"></i></span>
 | 
			
		||||
            @helpers.avatarLink(comment.commentedUserName, 16)
 | 
			
		||||
            @helpers.user(comment.commentedUserName, styleClass="username strong")
 | 
			
		||||
            assigned <code>@comment.content</code>
 | 
			
		||||
            @gitbucket.core.helper.html.datetimeago(comment.registeredDate)
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      }
 | 
			
		||||
      case "delete_assignee" => {
 | 
			
		||||
        <div class="discussion-item discussion-item-assign">
 | 
			
		||||
          <div class="discussion-item-header">
 | 
			
		||||
            <span class="discussion-item-icon"><i class="octicon octicon-person"></i></span>
 | 
			
		||||
            @helpers.avatarLink(comment.commentedUserName, 16)
 | 
			
		||||
            @helpers.user(comment.commentedUserName, styleClass="username strong")
 | 
			
		||||
            unassigned <code>@comment.content</code></code>
 | 
			
		||||
            @gitbucket.core.helper.html.datetimeago(comment.registeredDate)
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -255,7 +277,7 @@
 | 
			
		||||
            <span class="discussion-item-icon"><i class="octicon octicon-pencil"></i></span>
 | 
			
		||||
            @helpers.avatar(comment.commentedUserName, 16)
 | 
			
		||||
            @helpers.user(comment.commentedUserName, styleClass="username strong")
 | 
			
		||||
            change title from <code>@convertLineSeparator(comment.content, "LF").split("\n")(0)</code> to <code>@convertLineSeparator(comment.content, "LF").split("\n")(1)</code>
 | 
			
		||||
            changed title from <code>@convertLineSeparator(comment.content, "LF").split("\n")(0)</code> to <code>@convertLineSeparator(comment.content, "LF").split("\n")(1)</code>
 | 
			
		||||
            @gitbucket.core.helper.html.datetimeago(comment.registeredDate)
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -266,7 +288,7 @@
 | 
			
		||||
            <span class="discussion-item-icon"><i class="octicon octicon-pencil"></i></span>
 | 
			
		||||
            @helpers.avatar(comment.commentedUserName, 16)
 | 
			
		||||
            @helpers.user(comment.commentedUserName, styleClass="username strong")
 | 
			
		||||
            change base branch from <code>@convertLineSeparator(comment.content, "LF").split("\n")(0)</code> to <code>@convertLineSeparator(comment.content, "LF").split("\n")(1)</code>
 | 
			
		||||
            changed base branch from <code>@convertLineSeparator(comment.content, "LF").split("\n")(0)</code> to <code>@convertLineSeparator(comment.content, "LF").split("\n")(1)</code>
 | 
			
		||||
            @gitbucket.core.helper.html.datetimeago(comment.registeredDate)
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -328,9 +350,9 @@
 | 
			
		||||
$(function(){
 | 
			
		||||
@if(issue.isDefined){
 | 
			
		||||
  $('.issue-comment-box i.octicon-pencil').click(function(){
 | 
			
		||||
    var id  = $(this).closest('a').data('comment-id');
 | 
			
		||||
    var url = '@helpers.url(repository)/issue_comments/_data/' + id;
 | 
			
		||||
    var $content = $('#commentContent-' + id);
 | 
			
		||||
    let id  = $(this).closest('a').data('comment-id');
 | 
			
		||||
    let url = '@helpers.url(repository)/issue_comments/_data/' + id;
 | 
			
		||||
    let $content = $('#commentContent-' + id);
 | 
			
		||||
 | 
			
		||||
    if(!id){
 | 
			
		||||
      id  = $(this).closest('a').data('issue-id');
 | 
			
		||||
@@ -345,7 +367,7 @@ $(function(){
 | 
			
		||||
  });
 | 
			
		||||
  $('.issue-comment-box i.octicon-x').click(function(){
 | 
			
		||||
    if(confirm('Are you sure you want to delete this?')) {
 | 
			
		||||
      var id = $(this).closest('a').data('comment-id');
 | 
			
		||||
      const id = $(this).closest('a').data('comment-id');
 | 
			
		||||
      $.post('@helpers.url(repository)/issue_comments/delete/' + id, function(data){
 | 
			
		||||
        if(data > 0) {
 | 
			
		||||
          $('#comment-' + id).remove();
 | 
			
		||||
@@ -356,9 +378,9 @@ $(function(){
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
  $(document).on('click', '.commit-comment-box i.octicon-pencil', function(){
 | 
			
		||||
    var id  = $(this).closest('a').data('comment-id');
 | 
			
		||||
    var url = '@helpers.url(repository)/commit_comments/_data/' + id;
 | 
			
		||||
    var $content = $('.commit-commentContent-' + id, $(this).closest('.commit-comment-box'));
 | 
			
		||||
    const id  = $(this).closest('a').data('comment-id');
 | 
			
		||||
    const url = '@helpers.url(repository)/commit_comments/_data/' + id;
 | 
			
		||||
    const $content = $('.commit-commentContent-' + id, $(this).closest('.commit-comment-box'));
 | 
			
		||||
 | 
			
		||||
    $.get(url, { dataType : 'html' }, function(data){
 | 
			
		||||
      $content.empty().html(data);
 | 
			
		||||
@@ -369,14 +391,14 @@ $(function(){
 | 
			
		||||
 | 
			
		||||
  $(document).on('click', '.commit-comment-box i.octicon-x', function(){
 | 
			
		||||
    if(confirm('Are you sure you want to delete this?')) {
 | 
			
		||||
      var id = $(this).closest('a').data('comment-id');
 | 
			
		||||
      const id = $(this).closest('a').data('comment-id');
 | 
			
		||||
      $.post('@helpers.url(repository)/commit_comments/delete/' + id,
 | 
			
		||||
      function(data){
 | 
			
		||||
        if(data > 0) {
 | 
			
		||||
          var comment = $('.commit-comment-' + id);
 | 
			
		||||
          const comment = $('.commit-comment-' + id);
 | 
			
		||||
 | 
			
		||||
          // diff view
 | 
			
		||||
          var tr = comment.closest('.not-diff');
 | 
			
		||||
          const tr = comment.closest('.not-diff');
 | 
			
		||||
          if(tr.length > 0){
 | 
			
		||||
            if(tr.prev('.not-diff').length == 0){
 | 
			
		||||
              tr.next('.not-diff:has(.reply-comment)').remove();
 | 
			
		||||
@@ -385,7 +407,7 @@ $(function(){
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // comment list view
 | 
			
		||||
          var panel = comment.closest('div.panel:has(.commit-comment-box)');
 | 
			
		||||
          const panel = comment.closest('div.panel:has(.commit-comment-box)');
 | 
			
		||||
          if(panel.length > 0){
 | 
			
		||||
            comment.parent('.commit-comment-box').remove();
 | 
			
		||||
            if(panel.has('.commit-comment-box').length == 0){
 | 
			
		||||
@@ -401,7 +423,7 @@ $(function(){
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  $('div[class*=commit-commentContent-]').on('click', ':checkbox', function(ev){
 | 
			
		||||
    var $commentContent = $(ev.target).parents('div[class*=commit-commentContent-]'),
 | 
			
		||||
    const $commentContent = $(ev.target).parents('div[class*=commit-commentContent-]'),
 | 
			
		||||
        commentId = $commentContent.attr('class').match(/commit-commentContent-.+/)[0].replace(/commit-commentContent-/, ''),
 | 
			
		||||
        checkboxes = $commentContent.find(':checkbox');
 | 
			
		||||
    $.get('@helpers.url(repository)/commit_comments/_data/' + commentId, { dataType : 'html' },
 | 
			
		||||
@@ -421,9 +443,9 @@ $(function(){
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @if(issue.isDefined){
 | 
			
		||||
@if(issue.isDefined){
 | 
			
		||||
  $('#issueContent').on('click', ':checkbox', function(ev){
 | 
			
		||||
    var checkboxes = $('#issueContent :checkbox');
 | 
			
		||||
    const checkboxes = $('#issueContent :checkbox');
 | 
			
		||||
    $.get('@helpers.url(repository)/issues/_data/@issue.get.issueId', { dataType : 'html' },
 | 
			
		||||
      function(responseContent){
 | 
			
		||||
        $.ajax({
 | 
			
		||||
@@ -439,7 +461,7 @@ $(function(){
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  $('div[id^=commentContent-]').on('click', ':checkbox', function(ev){
 | 
			
		||||
    var $commentContent = $(ev.target).parents('div[id^=commentContent-]'),
 | 
			
		||||
    const $commentContent = $(ev.target).parents('div[id^=commentContent-]'),
 | 
			
		||||
        commentId = $commentContent.attr('id').replace(/commentContent-/, ''),
 | 
			
		||||
        checkboxes = $commentContent.find(':checkbox');
 | 
			
		||||
    $.get('@helpers.url(repository)/issue_comments/_data/' + commentId, { dataType : 'html' },
 | 
			
		||||
@@ -455,9 +477,7 @@ $(function(){
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@
 | 
			
		||||
            issue = None,
 | 
			
		||||
            comments = Nil,
 | 
			
		||||
            issueLabels = Nil,
 | 
			
		||||
            issueAssignees = Nil,
 | 
			
		||||
            collaborators = collaborators,
 | 
			
		||||
            milestones = milestones.map(x => (x, 0, 0)),
 | 
			
		||||
            priorities= priorities,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
@(issue: gitbucket.core.model.Issue,
 | 
			
		||||
  comments: List[gitbucket.core.model.IssueComment],
 | 
			
		||||
  issueLabels: List[gitbucket.core.model.Label],
 | 
			
		||||
  issueAssignees: List[gitbucket.core.model.IssueAssignee],
 | 
			
		||||
  collaborators: List[String],
 | 
			
		||||
  milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
 | 
			
		||||
  priorities: List[gitbucket.core.model.Priority],
 | 
			
		||||
@@ -61,6 +62,7 @@
 | 
			
		||||
          issue = Some(issue),
 | 
			
		||||
          comments = comments,
 | 
			
		||||
          issueLabels = issueLabels,
 | 
			
		||||
          issueAssignees = issueAssignees,
 | 
			
		||||
          collaborators = collaborators,
 | 
			
		||||
          milestones = milestones,
 | 
			
		||||
          priorities = priorities,
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
@(issue: Option[gitbucket.core.model.Issue],
 | 
			
		||||
  comments: List[gitbucket.core.model.Comment],
 | 
			
		||||
  issueLabels: List[gitbucket.core.model.Label],
 | 
			
		||||
  issueAssignees: List[gitbucket.core.model.IssueAssignee],
 | 
			
		||||
  collaborators: List[String],
 | 
			
		||||
  milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
 | 
			
		||||
  priorities: List[gitbucket.core.model.Priority],
 | 
			
		||||
@@ -127,11 +128,10 @@
 | 
			
		||||
  @if(isManageable){
 | 
			
		||||
    <div class="pull-right">
 | 
			
		||||
      @gitbucket.core.helper.html.dropdown("Edit", right = true, filter = ("assignee", "Filter Assignee")) {
 | 
			
		||||
        <li><a href="javascript:void(0);" class="assign" data-name=""><i class="octicon octicon-x"></i> Clear assignee</a></li>
 | 
			
		||||
        @collaborators.map { collaborator =>
 | 
			
		||||
          <li>
 | 
			
		||||
            <a href="javascript:void(0);" class="assign" data-name="@collaborator">
 | 
			
		||||
              @gitbucket.core.helper.html.checkicon(issue.exists(_.assignedUserName.exists(_ == collaborator)))@helpers.avatar(collaborator, 20) @collaborator
 | 
			
		||||
            <a href="javascript:void(0);" class="toggle-assign" data-name="@collaborator">
 | 
			
		||||
              @gitbucket.core.helper.html.checkicon(issueAssignees.exists(_.assigneeUserName == collaborator))@helpers.avatar(collaborator, 20) @collaborator
 | 
			
		||||
            </a>
 | 
			
		||||
          </li>
 | 
			
		||||
        }
 | 
			
		||||
@@ -140,14 +140,15 @@
 | 
			
		||||
  }
 | 
			
		||||
</div>
 | 
			
		||||
<span id="label-assigned">
 | 
			
		||||
  @issue.flatMap(_.assignedUserName).map { userName =>
 | 
			
		||||
    @helpers.avatarLink(userName, 20) @helpers.user(userName, styleClass="username strong small")
 | 
			
		||||
  }.getOrElse{
 | 
			
		||||
    <span class="muted small">No one</span>
 | 
			
		||||
  @issueAssignees.map { asignee =>
 | 
			
		||||
    @helpers.avatarLink(asignee.assigneeUserName, 20) @helpers.user(asignee.assigneeUserName, styleClass="username strong small")
 | 
			
		||||
  }
 | 
			
		||||
  @if(issueAssignees.isEmpty) {
 | 
			
		||||
    <span class="muted small">No one assigned</span>
 | 
			
		||||
  }
 | 
			
		||||
</span>
 | 
			
		||||
@if(issue.isEmpty){
 | 
			
		||||
  <input type="hidden" name="assignedUserName" value=""/>
 | 
			
		||||
  <input type="hidden" name="assigneeUserNames" value=""/>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@customFields.map { case (field, value) =>
 | 
			
		||||
@@ -188,7 +189,7 @@
 | 
			
		||||
$(function(){
 | 
			
		||||
@issue.map { issue =>
 | 
			
		||||
  $('a.toggle-label').click(function(){
 | 
			
		||||
    const path = switchLabel($(this));
 | 
			
		||||
    const path = switchToggleOptions($(this));
 | 
			
		||||
    $.post('@helpers.url(repository)/issues/@issue.issueId/label/' + path,
 | 
			
		||||
      { labelId : $(this).data('label-id') },
 | 
			
		||||
      function(data){
 | 
			
		||||
@@ -223,19 +224,25 @@ $(function(){
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  $('a.assign').click(function(){
 | 
			
		||||
    const $this = $(this);
 | 
			
		||||
    const userName = $this.data('name');
 | 
			
		||||
    $.post('@helpers.url(repository)/issues/@issue.issueId/assign',
 | 
			
		||||
      { assignedUserName: userName },
 | 
			
		||||
      function(){
 | 
			
		||||
        displayAssignee($this, userName);
 | 
			
		||||
  $('a.toggle-assign').click(function(){
 | 
			
		||||
    const path = switchToggleOptions($(this));
 | 
			
		||||
    $.post('@helpers.url(repository)/issues/@issue.issueId/assignee/' + path,
 | 
			
		||||
      { assigneeUserName : $(this).data('name') },
 | 
			
		||||
      function(data){
 | 
			
		||||
        const assignees = Array();
 | 
			
		||||
        $('a.toggle-assign').each(function(i, e){
 | 
			
		||||
          if($(e).children('i').hasClass('octicon-check') == true){
 | 
			
		||||
            assignees.push($(e).text().trim());
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
        displayAssignee(assignees);
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
    return false;
 | 
			
		||||
  });
 | 
			
		||||
}.getOrElse {
 | 
			
		||||
  $('a.toggle-label').click(function(){
 | 
			
		||||
    switchLabel($(this));
 | 
			
		||||
    switchToggleOptions($(this));
 | 
			
		||||
    const labelNames = Array();
 | 
			
		||||
    $('a.toggle-label').each(function(i, e){
 | 
			
		||||
      if($(e).children('i').hasClass('octicon-check') == true){
 | 
			
		||||
@@ -269,15 +276,20 @@ $(function(){
 | 
			
		||||
    $('input[name=priorityId]').val(priorityId);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  $('a.assign').click(function(){
 | 
			
		||||
    const $this = $(this);
 | 
			
		||||
    const userName = $this.data('name');
 | 
			
		||||
    displayAssignee($this, userName);
 | 
			
		||||
    $('input[name=assignedUserName]').val(userName);
 | 
			
		||||
  $('a.toggle-assign').click(function(){
 | 
			
		||||
    switchToggleOptions($(this));
 | 
			
		||||
    const assignees = Array();
 | 
			
		||||
    $('a.toggle-assign').each(function(i, e){
 | 
			
		||||
      if($(e).children('i').hasClass('octicon-check') == true){
 | 
			
		||||
        assignees.push($(e).text().trim());
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    $('input[name=assigneeUserNames]').val(assignees.join(','));
 | 
			
		||||
    displayAssignee(assignees);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  function switchLabel($this){
 | 
			
		||||
  function switchToggleOptions($this){
 | 
			
		||||
    const i = $this.children('i');
 | 
			
		||||
    if(i.hasClass('octicon-check')){
 | 
			
		||||
      i.removeClass('octicon-check');
 | 
			
		||||
@@ -320,15 +332,18 @@ $(function(){
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function displayAssignee($this, userName){
 | 
			
		||||
  function displayAssignee(assignees){
 | 
			
		||||
    $('a.assign i.octicon-check').removeClass('octicon-check');
 | 
			
		||||
    if(userName == ''){
 | 
			
		||||
      $('#label-assigned').html($('<span class="muted small">').text('No one'));
 | 
			
		||||
    if(assignees.length == 0){
 | 
			
		||||
      $('#label-assigned').html($('<span class="muted small">').text('No one assigned'));
 | 
			
		||||
    } else {
 | 
			
		||||
      $('#label-assigned').empty()
 | 
			
		||||
        .append($this.find('img.avatar-mini').clone(false)).append(' ')
 | 
			
		||||
        .append($('<a class="username strong small">').attr('href', '@context.path/' + userName).text(userName));
 | 
			
		||||
      $('a.assign[data-name=' + jqSelectorEscape(userName.toString()) + '] i').addClass('octicon-check');
 | 
			
		||||
      $('#label-assigned').empty();
 | 
			
		||||
      for (const userName of assignees) {
 | 
			
		||||
        $('#label-assigned').append($('<div>').append(
 | 
			
		||||
            $('a.toggle-assign').parent().find("img.avatar-mini[alt='@@" + userName + "']").clone(false),
 | 
			
		||||
            ' ',
 | 
			
		||||
            $('<a class="username strong small">').attr('href', '@context.path/' + userName).text(userName)));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -230,9 +230,11 @@
 | 
			
		||||
            <span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span>
 | 
			
		||||
          }
 | 
			
		||||
          <span class="pull-right small">
 | 
			
		||||
            @*
 | 
			
		||||
            @issue.assignedUserName.map { userName =>
 | 
			
		||||
              @helpers.avatar(userName, 20, tooltip = true)
 | 
			
		||||
            }
 | 
			
		||||
            *@
 | 
			
		||||
            @if(commentCount > 0){
 | 
			
		||||
              <a href="@context.path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">
 | 
			
		||||
                <i class="octicon octicon-comment active"></i> @commentCount
 | 
			
		||||
 
 | 
			
		||||
@@ -90,9 +90,11 @@
 | 
			
		||||
            <span class="label-color small" style="background-color: #@label.color; color: #@label.fontColor; padding-left: 4px; padding-right: 4px">@label.labelName</span>
 | 
			
		||||
          }
 | 
			
		||||
          <span class="pull-right small">
 | 
			
		||||
            @*
 | 
			
		||||
            @issue.assignedUserName.map { userName =>
 | 
			
		||||
              @helpers.avatar(userName, 20, tooltip = true)
 | 
			
		||||
            }
 | 
			
		||||
            *@
 | 
			
		||||
            @if(commentCount > 0){
 | 
			
		||||
              <a href="@context.path/@issue.userName/@issue.repositoryName/issues/@issue.issueId" class="issue-comment-count">
 | 
			
		||||
                <i class="octicon octicon-comment active"></i> @commentCount
 | 
			
		||||
 
 | 
			
		||||
@@ -106,6 +106,7 @@
 | 
			
		||||
                issue = None,
 | 
			
		||||
                comments = Nil,
 | 
			
		||||
                issueLabels = Nil,
 | 
			
		||||
                issueAssignees = Nil,
 | 
			
		||||
                collaborators = collaborators,
 | 
			
		||||
                milestones = milestones.map((_, 0, 0)),
 | 
			
		||||
                priorities = priorities,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
  comments: Seq[gitbucket.core.model.Comment],
 | 
			
		||||
  changedFileSize: Int,
 | 
			
		||||
  issueLabels: List[gitbucket.core.model.Label],
 | 
			
		||||
  issueAsignees: List[gitbucket.core.model.IssueAssignee],
 | 
			
		||||
  collaborators: List[String],
 | 
			
		||||
  milestones: List[(gitbucket.core.model.Milestone, Int, Int)],
 | 
			
		||||
  priorities: List[gitbucket.core.model.Priority],
 | 
			
		||||
@@ -55,6 +56,7 @@
 | 
			
		||||
      Some(issue),
 | 
			
		||||
      comments.toList,
 | 
			
		||||
      issueLabels,
 | 
			
		||||
      issueAsignees,
 | 
			
		||||
      collaborators,
 | 
			
		||||
      milestones,
 | 
			
		||||
      priorities,
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,6 @@ object ApiSpecModels {
 | 
			
		||||
    openedUserName = "bear",
 | 
			
		||||
    milestoneId = None,
 | 
			
		||||
    priorityId = None,
 | 
			
		||||
    assignedUserName = None,
 | 
			
		||||
    title = "Found a bug",
 | 
			
		||||
    content = Some("I'm having a problem with this."),
 | 
			
		||||
    closed = false,
 | 
			
		||||
@@ -226,7 +225,7 @@ object ApiSpecModels {
 | 
			
		||||
    issue = issue,
 | 
			
		||||
    repositoryName = repo1Name,
 | 
			
		||||
    user = apiUser,
 | 
			
		||||
    assignee = Some(apiUser),
 | 
			
		||||
    assignees = List(apiUser),
 | 
			
		||||
    labels = List(apiLabel),
 | 
			
		||||
    milestone = Some(apiMilestone)
 | 
			
		||||
  )
 | 
			
		||||
@@ -235,7 +234,7 @@ object ApiSpecModels {
 | 
			
		||||
    issue = issue,
 | 
			
		||||
    repositoryName = repo1Name,
 | 
			
		||||
    user = apiUser,
 | 
			
		||||
    assignee = None,
 | 
			
		||||
    assignees = List.empty,
 | 
			
		||||
    labels = List(apiLabel),
 | 
			
		||||
    milestone = Some(apiMilestone)
 | 
			
		||||
  )
 | 
			
		||||
@@ -244,7 +243,7 @@ object ApiSpecModels {
 | 
			
		||||
    issue = issuePR,
 | 
			
		||||
    repositoryName = repo1Name,
 | 
			
		||||
    user = apiUser,
 | 
			
		||||
    assignee = Some(apiUser),
 | 
			
		||||
    assignees = List(apiUser),
 | 
			
		||||
    labels = List(apiLabel),
 | 
			
		||||
    milestone = Some(apiMilestone)
 | 
			
		||||
  )
 | 
			
		||||
@@ -272,7 +271,7 @@ object ApiSpecModels {
 | 
			
		||||
    baseRepo = apiRepository,
 | 
			
		||||
    user = apiUser,
 | 
			
		||||
    labels = List(apiLabel),
 | 
			
		||||
    assignee = Some(apiUser),
 | 
			
		||||
    assignees = List(apiUser),
 | 
			
		||||
    mergedComment = Some((issueComment, account))
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
@@ -540,7 +539,7 @@ object ApiSpecModels {
 | 
			
		||||
       |"number":1347,
 | 
			
		||||
       |"title":"Found a bug",
 | 
			
		||||
       |"user":$jsonUser,
 | 
			
		||||
       |"assignee":$jsonUser,
 | 
			
		||||
       |"assignees":[$jsonUser],
 | 
			
		||||
       |"labels":[$jsonLabel],
 | 
			
		||||
       |"state":"open",
 | 
			
		||||
       |"created_at":"2011-04-14T16:00:49Z",
 | 
			
		||||
@@ -548,7 +547,7 @@ object ApiSpecModels {
 | 
			
		||||
       |"body":"I'm having a problem with this.",
 | 
			
		||||
       |"milestone":$jsonMilestone,
 | 
			
		||||
       |"id":0,
 | 
			
		||||
       |"assignees":[$jsonUser],
 | 
			
		||||
       |"assignee":$jsonUser,
 | 
			
		||||
       |"comments_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/issues/1347/comments",
 | 
			
		||||
       |"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/issues/1347"
 | 
			
		||||
       |}""".stripMargin
 | 
			
		||||
@@ -557,6 +556,7 @@ object ApiSpecModels {
 | 
			
		||||
       |"number":1347,
 | 
			
		||||
       |"title":"Found a bug",
 | 
			
		||||
       |"user":$jsonUser,
 | 
			
		||||
       |"assignees":[],
 | 
			
		||||
       |"labels":[$jsonLabel],
 | 
			
		||||
       |"state":"open",
 | 
			
		||||
       |"created_at":"2011-04-14T16:00:49Z",
 | 
			
		||||
@@ -564,7 +564,6 @@ object ApiSpecModels {
 | 
			
		||||
       |"body":"I'm having a problem with this.",
 | 
			
		||||
       |"milestone":$jsonMilestone,
 | 
			
		||||
       |"id":0,
 | 
			
		||||
       |"assignees":[],
 | 
			
		||||
       |"comments_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/issues/1347/comments",
 | 
			
		||||
       |"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/issues/1347"
 | 
			
		||||
       |}""".stripMargin
 | 
			
		||||
@@ -573,7 +572,7 @@ object ApiSpecModels {
 | 
			
		||||
       |"number":1347,
 | 
			
		||||
       |"title":"new-feature",
 | 
			
		||||
       |"user":$jsonUser,
 | 
			
		||||
       |"assignee":$jsonUser,
 | 
			
		||||
       |"assignees":[$jsonUser],
 | 
			
		||||
       |"labels":[$jsonLabel],
 | 
			
		||||
       |"state":"closed",
 | 
			
		||||
       |"created_at":"2011-04-14T16:00:49Z",
 | 
			
		||||
@@ -581,12 +580,12 @@ object ApiSpecModels {
 | 
			
		||||
       |"body":"Please pull these awesome changes",
 | 
			
		||||
       |"milestone":$jsonMilestone,
 | 
			
		||||
       |"id":0,
 | 
			
		||||
       |"assignees":[$jsonUser],
 | 
			
		||||
       |"assignee":$jsonUser,
 | 
			
		||||
       |"comments_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/issues/1347/comments",
 | 
			
		||||
       |"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/pull/1347",
 | 
			
		||||
       |"pull_request":{
 | 
			
		||||
         |"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/pulls/1347",
 | 
			
		||||
         |"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/pull/1347"}
 | 
			
		||||
       |"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/pulls/1347",
 | 
			
		||||
       |"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/pull/1347"}
 | 
			
		||||
       |}""".stripMargin
 | 
			
		||||
 | 
			
		||||
  val jsonPullRequest = s"""{
 | 
			
		||||
@@ -603,9 +602,10 @@ object ApiSpecModels {
 | 
			
		||||
       |"body":"Please pull these awesome changes",
 | 
			
		||||
       |"user":$jsonUser,
 | 
			
		||||
       |"labels":[$jsonLabel],
 | 
			
		||||
       |"assignee":$jsonUser,
 | 
			
		||||
       |"assignees":[$jsonUser],
 | 
			
		||||
       |"draft":true,
 | 
			
		||||
       |"id":0,
 | 
			
		||||
       |"assignee":$jsonUser,
 | 
			
		||||
       |"html_url":"http://gitbucket.exmple.com/octocat/Hello-World/pull/1347",
 | 
			
		||||
       |"url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/pulls/1347",
 | 
			
		||||
       |"commits_url":"http://gitbucket.exmple.com/api/v3/repos/octocat/Hello-World/pulls/1347/commits",
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,6 @@ trait ServiceSpecBase {
 | 
			
		||||
      loginUser = loginUser,
 | 
			
		||||
      title = "issue title",
 | 
			
		||||
      content = None,
 | 
			
		||||
      assignedUserName = None,
 | 
			
		||||
      milestoneId = None,
 | 
			
		||||
      priorityId = None,
 | 
			
		||||
      isPullRequest = true
 | 
			
		||||
 
 | 
			
		||||
@@ -91,7 +91,7 @@ class WebHookJsonFormatSpec extends AnyFunSuite {
 | 
			
		||||
      action = "closed",
 | 
			
		||||
      issue = issuePR,
 | 
			
		||||
      issueUser = account,
 | 
			
		||||
      assignee = Some(account),
 | 
			
		||||
      assignees = List(account),
 | 
			
		||||
      pullRequest = pullRequest,
 | 
			
		||||
      headRepository = repositoryInfo,
 | 
			
		||||
      headOwner = account,
 | 
			
		||||
@@ -119,7 +119,7 @@ class WebHookJsonFormatSpec extends AnyFunSuite {
 | 
			
		||||
      commentUser = account,
 | 
			
		||||
      repository = repositoryInfo,
 | 
			
		||||
      repositoryUser = account,
 | 
			
		||||
      assignedUser = Some(account),
 | 
			
		||||
      assignees = List(account),
 | 
			
		||||
      sender = account,
 | 
			
		||||
      labels = List(label),
 | 
			
		||||
      milestone = Some(apiMilestone)
 | 
			
		||||
@@ -140,7 +140,7 @@ class WebHookJsonFormatSpec extends AnyFunSuite {
 | 
			
		||||
      comment = commitComment,
 | 
			
		||||
      issue = issuePR,
 | 
			
		||||
      issueUser = account,
 | 
			
		||||
      assignee = Some(account),
 | 
			
		||||
      assignees = List(account),
 | 
			
		||||
      pullRequest = pullRequest,
 | 
			
		||||
      headRepository = repositoryInfo,
 | 
			
		||||
      headOwner = account,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
package gitbucket.core.view
 | 
			
		||||
 | 
			
		||||
import gitbucket.core.util.SyntaxSugars
 | 
			
		||||
import org.scalatest.funspec.AnyFunSpec
 | 
			
		||||
 | 
			
		||||
class PaginationSpec extends AnyFunSpec {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user