diff --git a/src/main/scala/gitbucket/core/service/IssuesService.scala b/src/main/scala/gitbucket/core/service/IssuesService.scala index 6e9463b..fb2088d 100644 --- a/src/main/scala/gitbucket/core/service/IssuesService.scala +++ b/src/main/scala/gitbucket/core/service/IssuesService.scala @@ -778,16 +778,37 @@ def createReferComment(owner: String, repository: String, fromIssue: Issue, message: String, loginAccount: Account)( implicit s: Session ): Unit = { - extractIssueId(message).foreach { issueId => - val content = s"${fromIssue.issueId}:${fromIssue.title}" - if (getIssue(owner, repository, issueId).isDefined) { - // Not add if refer comment already exist. - if (!getComments(owner, repository, issueId.toInt).exists { x => - x.action == "refer" && x.content == content - }) { - createComment(owner, repository, loginAccount.userName, issueId.toInt, content, "refer") + extractGlobalIssueId(message).foreach { + case (_referredOwner, _referredRepository, referredIssueId) => + val referredOwner = _referredOwner.getOrElse(owner) + val referredRepository = _referredRepository.getOrElse(repository) + getRepository(referredOwner, referredRepository).foreach { repo => + if (isReadable(repo.repository, Option(loginAccount))) { + getIssue(referredOwner, referredRepository, referredIssueId.get).foreach { _ => + val (content, action) = if (owner == referredOwner && repository == referredRepository) { + (s"${fromIssue.issueId}:${fromIssue.title}", "refer") + } else { + (s"${fromIssue.issueId}:${owner}:${repository}:${fromIssue.title}", "refer_global") + } + referredIssueId.foreach( + x => + // Not add if refer comment already exist. + if (!getComments(referredOwner, referredRepository, x.toInt).exists { x => + (x.action == "refer" || x.action == "refer_global") && x.content == content + }) { + createComment( + referredOwner, + referredRepository, + loginAccount.userName, + x.toInt, + content, + action + ) + } + ) + } + } } - } } } diff --git a/src/main/scala/gitbucket/core/util/StringUtil.scala b/src/main/scala/gitbucket/core/util/StringUtil.scala index 1b57384..7da4a87 100644 --- a/src/main/scala/gitbucket/core/util/StringUtil.scala +++ b/src/main/scala/gitbucket/core/util/StringUtil.scala @@ -12,6 +12,7 @@ import org.apache.commons.io.IOUtils import scala.util.control.Exception._ +import scala.util.matching.Regex.Match.unapply object StringUtil { @@ -145,6 +146,19 @@ .distinct /** + * Extract issue id like ```owner/repository#issueId``` from the given message. + * + *@param message the message which may contains issue id + * @return the iterator of issue id + */ + def extractGlobalIssueId(message: String): List[(Option[String], Option[String], Option[String])] = + "\\s?([\\w-\\.]+)?\\/?([\\w\\-\\.]+)?#(\\d+)\\s?".r + .findAllIn(message) + .matchData + .map(i => (Option(i.group(1)), Option(i.group(2)), Option(i.group(3)))) + .toList + + /** * Extract close issue id like ```close #issueId ``` from the given message. * * @param message the message which may contains close command diff --git a/src/main/scala/gitbucket/core/view/LinkConverter.scala b/src/main/scala/gitbucket/core/view/LinkConverter.scala index 34f5019..d1b26b2 100644 --- a/src/main/scala/gitbucket/core/view/LinkConverter.scala +++ b/src/main/scala/gitbucket/core/view/LinkConverter.scala @@ -10,15 +10,12 @@ /** * Creates a link to the issue or the pull request from the issue id. */ - protected def createIssueLink(repository: RepositoryService.RepositoryInfo, issueId: Int, title: String)( + protected def createIssueLink(owner: String, repository: String, issueId: Int, title: String)( implicit context: Context ): String = { - val userName = repository.repository.userName - val repositoryName = repository.repository.repositoryName - - getIssueFromCache(userName, repositoryName, issueId.toString) match { + getIssueFromCache(owner, repository, issueId.toString) match { case Some(issue) => - s"""${StringUtil + s"""${StringUtil .escapeHtml(title)} #${issueId}""" case None => s"Unknown #${issueId}" @@ -26,6 +23,21 @@ } /** + * Creates a global link to the issue or the pull request from the issue id. + */ + protected def createGlobalIssueLink(owner: String, repository: String, issueId: Int, title: String)( + implicit context: Context + ): String = { + getIssueFromCache(owner, repository, issueId.toString) match { + case Some(issue) => + s"""${StringUtil + .escapeHtml(title)} ${owner}/${repository}#${issueId}""" + case None => + s"Unknown ${owner}/${repository}#${issueId}" + } + } + + /** * Converts issue id, username and commit id to link in the given text. */ protected def convertRefsLinks( diff --git a/src/main/scala/gitbucket/core/view/helpers.scala b/src/main/scala/gitbucket/core/view/helpers.scala index 3b72130..ebfefeb 100644 --- a/src/main/scala/gitbucket/core/view/helpers.scala +++ b/src/main/scala/gitbucket/core/view/helpers.scala @@ -160,10 +160,19 @@ /** * Creates a link to the issue or the pull request from the issue id. */ - def issueLink(repository: RepositoryService.RepositoryInfo, issueId: Int, title: String)( + def issueLink(owner: String, repository: String, issueId: Int, title: String)( implicit context: Context ): Html = { - Html(createIssueLink(repository, issueId, title)) + Html(createIssueLink(owner, repository, issueId, title)) + } + + /** + * Creates a global link to the issue or the pull request from the issue id. + */ + def issueGlobalLink(owner: String, repository: String, issueId: Int, title: String)( + implicit context: Context + ): Html = { + Html(createGlobalIssueLink(owner, repository, issueId, title)) } /** diff --git a/src/main/twirl/gitbucket/core/issues/commentlist.scala.html b/src/main/twirl/gitbucket/core/issues/commentlist.scala.html index 75b178f..fa1dd84 100644 --- a/src/main/twirl/gitbucket/core/issues/commentlist.scala.html +++ b/src/main/twirl/gitbucket/core/issues/commentlist.scala.html @@ -114,9 +114,26 @@ @gitbucket.core.helper.html.datetimeago(comment.registeredDate)
- @defining(comment.content.split(":")){ case Array(issueId, rest @ _*) => { - @helpers.issueLink(repository, issueId.toInt, rest.mkString(":")) - }} + @defining(comment.content.split(":")){ + case Array(issueId, rest @ _*) => {@helpers.issueLink(repository.owner, repository.name, issueId.toInt, rest.mkString(":"))} + } +
+ + } + case "refer_global" => { +
+
+ + @helpers.avatarLink(comment.commentedUserName, 16) + @helpers.user(comment.commentedUserName, styleClass="username strong") + referenced the @issueOrPullRequest() + @gitbucket.core.helper.html.datetimeago(comment.registeredDate) +
+
+ @defining(comment.content.split(":")){ + case Array(issueId, ownerName, repositoryName, rest @ _*) => { + @helpers.issueGlobalLink(ownerName, repositoryName, issueId.toInt, rest.mkString(":")) + }}
} diff --git a/src/test/scala/gitbucket/core/util/StringUtilSpec.scala b/src/test/scala/gitbucket/core/util/StringUtilSpec.scala index 3c4016b..7884358 100644 --- a/src/test/scala/gitbucket/core/util/StringUtilSpec.scala +++ b/src/test/scala/gitbucket/core/util/StringUtilSpec.scala @@ -57,6 +57,20 @@ } } + describe("extractGlobalIssueId") { + it("should extract '#xxx' and return extracted id") { + assert(StringUtil.extractGlobalIssueId("(refs #123)").toSeq == List((None, None, Some("123")))) + } + it("should extract 'owner/repository#xxx' and return extracted owner, repository and id") { + assert( + StringUtil.extractGlobalIssueId("(refs root/test#123)").toSeq == List((Some("root"), Some("test"), Some("123"))) + ) + } + it("should return Nil from message which does not contain #xxx") { + assert(StringUtil.extractGlobalIssueId("this is test!").toSeq == Nil) + } + } + describe("extractCloseId") { it("should extract 'close #xxx' and return extracted id") { assert(StringUtil.extractCloseId("(close #123)").toSeq == Seq("123"))