Skip to content

Commit c2b40ff

Browse files
committed
Add operation to extract notification user.
1 parent ab4544f commit c2b40ff

File tree

5 files changed

+210
-80
lines changed

5 files changed

+210
-80
lines changed

src/main/scala/Plugin.scala

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,29 @@
11
import gitbucket.core.controller.Context
22
import gitbucket.core.model.Issue
3-
import gitbucket.core.plugin._
3+
import gitbucket.core.plugin.Link
44
import gitbucket.core.service.RepositoryService.RepositoryInfo
5+
import gitbucket.notifications.service._
56
import io.github.gitbucket.solidbase.migration.LiquibaseMigration
67
import io.github.gitbucket.solidbase.model.Version
78

89
class Plugin extends gitbucket.core.plugin.Plugin {
910

10-
override val pluginId: String = "notifications"
11+
override val pluginId = "notifications"
1112

12-
override val pluginName: String = "Notifications Plugin"
13+
override val pluginName = "Notifications Plugin"
1314

14-
override val description: String = "Provides Notifications feature on GitBucket."
15+
override val description = "Provides Notifications feature on GitBucket."
1516

16-
override val versions: List[Version] = List(
17+
override val versions = List(
1718
new Version("1.0.0",
1819
new LiquibaseMigration("update/gitbucket-notifications_1.0.xml")
1920
)
2021
)
2122

22-
23-
override val accountHooks: Seq[AccountHook] = Seq(
24-
)
25-
26-
override val repositoryHooks: Seq[RepositoryHook] = Seq(
27-
)
28-
29-
override val issueHooks: Seq[IssueHook] = Seq(
30-
)
31-
32-
override val pullRequestHooks: Seq[PullRequestHook] = Seq(
33-
)
23+
override val accountHooks = Seq(new AccountHook)
24+
override val repositoryHooks = Seq(new RepositoryHook)
25+
override val issueHooks = Seq(new IssueHook)
26+
override val pullRequestHooks = Seq(new PullRequestHook)
3427

3528
override val repositoryMenus = Seq(
3629
(repository: RepositoryInfo, context: Context) =>

src/main/scala/gitbucket/notifications/controller/NotificationsController.scala

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package gitbucket.notifications.controller
22

33
import gitbucket.core.controller.ControllerBase
4-
import gitbucket.core.service.{AccountService, RepositoryService}
4+
import gitbucket.core.service.{AccountService, IssuesService, RepositoryService}
55
import gitbucket.core.util.Implicits._
66
import gitbucket.core.util.ReadableUsersAuthenticator
77
import gitbucket.core.util.SyntaxSugars._
@@ -11,16 +11,17 @@ import gitbucket.notifications.service.NotificationsService
1111
import org.scalatra.Ok
1212

1313
class NotificationsController extends NotificationsControllerBase
14-
with NotificationsService with RepositoryService with AccountService with ReadableUsersAuthenticator
14+
with NotificationsService with RepositoryService with AccountService with IssuesService with ReadableUsersAuthenticator
1515

1616
trait NotificationsControllerBase extends ControllerBase {
17-
self: NotificationsService with RepositoryService with AccountService with ReadableUsersAuthenticator =>
17+
self: NotificationsService with RepositoryService with AccountService with IssuesService with ReadableUsersAuthenticator =>
1818

1919
get("/:owner/:repository/watch")(readableUsersOnly { repository =>
20-
defining(repository.owner, repository.name) { case (owner, name) =>
20+
defining(repository.owner, repository.name, context.loginAccount.get.userName) { case (owner, name, userName) =>
2121
html.watch(
22-
// TODO default value
23-
getWatch(owner, name, context.loginAccount.get.userName),
22+
getWatch(owner, name, userName).map(_.notification) getOrElse {
23+
if (autoSubscribeUsersForRepository(owner, name) contains userName) Watch.Watching else Watch.NotWatching
24+
},
2425
repository
2526
)
2627
}
@@ -33,6 +34,7 @@ trait NotificationsControllerBase extends ControllerBase {
3334
} getOrElse NotFound()
3435
})
3536

37+
// TODO check exist issue
3638
ajaxPost("/:owner/:repository/issues/:id/notification")(readableUsersOnly { repository =>
3739
params.getAs[Boolean]("subscribed").map { subscribed =>
3840
updateIssueNotification(repository.owner, repository.name, params("id").toInt, context.loginAccount.get.userName, subscribed)

src/main/scala/gitbucket/notifications/service/NotificationsAccountHook.scala

Lines changed: 0 additions & 48 deletions
This file was deleted.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package gitbucket.notifications.service
2+
3+
import gitbucket.core.controller.Context
4+
import gitbucket.core.model.{Account, Issue}
5+
import gitbucket.core.service.RepositoryService.RepositoryInfo
6+
import gitbucket.core.util.Notifier
7+
import gitbucket.core.view.Markdown
8+
import gitbucket.notifications.model.Profile._
9+
import profile.blockingApi._
10+
11+
12+
class AccountHook extends gitbucket.core.plugin.AccountHook {
13+
14+
override def deleted(userName: String)(implicit session: Session): Unit = {
15+
IssueNotifications.filter(_.notificationUserName === userName.bind).delete
16+
Watches.filter(_.notificationUserName === userName.bind).delete
17+
}
18+
19+
}
20+
21+
class RepositoryHook extends gitbucket.core.plugin.RepositoryHook {
22+
23+
override def deleted(owner: String, repository: String)(implicit session: Session): Unit = {
24+
IssueNotifications.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind).delete
25+
Watches.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind).delete
26+
}
27+
28+
override def renamed(owner: String, repository: String, newRepository: String)(implicit session: Session): Unit = {
29+
rename(owner, repository, owner, newRepository)
30+
}
31+
32+
override def transferred(owner: String, newOwner: String, repository: String)(implicit session: Session): Unit = {
33+
rename(owner, repository, newOwner, repository)
34+
}
35+
36+
// TODO select - insert
37+
private def rename(owner: String, repository: String, newOwner: String, newRepository: String)(implicit session: Session) = {
38+
val n = IssueNotifications.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind).list
39+
val w = Watches.filter(t => t.userName === owner.bind && t.repositoryName === repository.bind).list
40+
41+
deleted(owner, repository)
42+
43+
IssueNotifications.insertAll(n.map(_.copy(userName = newOwner, repositoryName = newRepository)) :_*)
44+
Watches.insertAll(w.map(_.copy(userName = newOwner, repositoryName = newRepository)) :_*)
45+
}
46+
47+
}
48+
49+
class IssueHook extends gitbucket.core.plugin.IssueHook {
50+
51+
override def created(issue: Issue, r: RepositoryInfo)(implicit context: Context): Unit = {
52+
Notifier().toNotify(
53+
subject(issue, r),
54+
message(issue.content getOrElse "", r)(content => s"""
55+
|$content<br/>
56+
|--<br/>
57+
|<a href="${s"${context.baseUrl}/${r.owner}/${r.name}/issues/${issue.issueId}"}">View it on GitBucket</a>
58+
""".stripMargin)
59+
)(recipients(issue))
60+
}
61+
62+
override def addedComment(commentId: Int, content: String, issue: Issue, r: RepositoryInfo)(implicit context: Context): Unit = {
63+
Notifier().toNotify(
64+
subject(issue, r),
65+
message(content, r)(content => s"""
66+
|$content<br/>
67+
|--<br/>
68+
|<a href="${s"${context.baseUrl}/${r.owner}/${r.name}/issues/${issue.issueId}#comment-$commentId"}">View it on GitBucket</a>
69+
""".stripMargin)
70+
)(recipients(issue))
71+
}
72+
73+
override def closed(issue: Issue, r: RepositoryInfo)(implicit context: Context): Unit = {
74+
Notifier().toNotify(
75+
subject(issue, r),
76+
message("close", r)(content => s"""
77+
|$content <a href="${s"${context.baseUrl}/${r.owner}/${r.name}/issues/${issue.issueId}"}">#${issue.issueId}</a>
78+
""".stripMargin)
79+
)(recipients(issue))
80+
}
81+
82+
override def reopened(issue: Issue, r: RepositoryInfo)(implicit context: Context): Unit = {
83+
Notifier().toNotify(
84+
subject(issue, r),
85+
message("reopen", r)(content => s"""
86+
|$content <a href="${s"${context.baseUrl}/${r.owner}/${r.name}/issues/${issue.issueId}"}">#${issue.issueId}</a>
87+
""".stripMargin)
88+
)(recipients(issue))
89+
}
90+
91+
92+
protected val subject: (Issue, RepositoryInfo) => String =
93+
(issue, r) => s"[${r.owner}/${r.name}] ${issue.title} (#${issue.issueId})"
94+
95+
protected val message: (String, RepositoryInfo) => (String => String) => String =
96+
(content, r) => msg => msg(Markdown.toHtml(
97+
markdown = content,
98+
repository = r,
99+
enableWikiLink = false,
100+
enableRefsLink = true,
101+
enableAnchor = false,
102+
enableLineBreaks = false
103+
))
104+
105+
// TODO
106+
protected val recipients: Issue => Account => Session => Seq[String] = {
107+
issue => loginAccount => implicit session =>
108+
Seq("")
109+
}
110+
111+
}
112+
113+
class PullRequestHook extends IssueHook with gitbucket.core.plugin.PullRequestHook {
114+
115+
override def created(issue: Issue, r: RepositoryInfo)(implicit context: Context): Unit = {
116+
val url = s"${context.baseUrl}/${r.owner}/${r.name}/pull/${issue.issueId}"
117+
Notifier().toNotify(
118+
subject(issue, r),
119+
message(issue.content getOrElse "", r)(content => s"""
120+
|$content<hr/>
121+
|View, comment on, or merge it at:<br/>
122+
|<a href="$url">$url</a>
123+
""".stripMargin)
124+
)(recipients(issue))
125+
}
126+
127+
override def addedComment(commentId: Int, content: String, issue: Issue, r: RepositoryInfo)(implicit context: Context): Unit = {
128+
Notifier().toNotify(
129+
subject(issue, r),
130+
message(content, r)(content => s"""
131+
|$content<br/>
132+
|--<br/>
133+
|<a href="${s"${context.baseUrl}/${r.owner}/${r.name}/pull/${issue.issueId}#comment-$commentId"}">View it on GitBucket</a>
134+
""".stripMargin)
135+
)(recipients(issue))
136+
}
137+
138+
override def merged(issue: Issue, r: RepositoryInfo)(implicit context: Context): Unit = {
139+
Notifier().toNotify(
140+
subject(issue, r),
141+
message("merge", r)(content => s"""
142+
|$content <a href="${s"${context.baseUrl}/${r.owner}/${r.name}/pull/${issue.issueId}"}">#${issue.issueId}</a>
143+
""".stripMargin)
144+
)(recipients(issue))
145+
}
146+
147+
}

src/main/scala/gitbucket/notifications/service/NotificationsService.scala

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package gitbucket.notifications.service
22

3+
import gitbucket.core.model.Issue
4+
import gitbucket.core.service.{AccountService, IssuesService, RepositoryService}
35
import gitbucket.notifications.model._, Profile._
46
import profile.blockingApi._
57

68
trait NotificationsService {
9+
self: RepositoryService with AccountService with IssuesService =>
710

811
def getWatch(owner: String, repository: String, userName: String)(implicit s: Session): Option[Watch] = {
912
Watches
@@ -23,15 +26,6 @@ trait NotificationsService {
2326
)
2427
}
2528

26-
def getIssueNotification(owner: String, repository: String, issueId: Int, userName: String)(implicit s: Session): Option[IssueNotification] = {
27-
IssueNotifications
28-
.filter { t =>
29-
t.userName === owner.bind && t.repositoryName === repository.bind &&
30-
t.issueId === issueId.bind && t.notificationUserName === userName.bind
31-
}
32-
.firstOption
33-
}
34-
3529
def updateIssueNotification(owner: String, repository: String, issueId: Int, userName: String, subscribed: Boolean)(implicit s: Session): Unit = {
3630
IssueNotifications
3731
.filter { t =>
@@ -48,4 +42,46 @@ trait NotificationsService {
4842
)
4943
}
5044

45+
def autoSubscribeUsersForRepository(owner: String, repository: String)(implicit s: Session): List[String] = {
46+
// individual repository's owner
47+
owner ::
48+
// group members of group repository
49+
getGroupMembers(owner).map(_.userName) :::
50+
// collaborators
51+
getCollaboratorUserNames(owner, repository)
52+
}
53+
54+
def getNotificationUsers(issue: Issue)(implicit s: Session): List[String] = {
55+
val watches = Watches.filter(t =>
56+
t.userName === issue.userName.bind && t.repositoryName === issue.repositoryName.bind
57+
).list
58+
val notifications = IssueNotifications.filter(t =>
59+
t.userName === issue.userName.bind && t.repositoryName === issue.repositoryName.bind && t.issueId === issue.issueId.bind
60+
).list
61+
62+
(
63+
Seq(
64+
// auto-subscribe users for repository
65+
autoSubscribeUsersForRepository(issue.userName, issue.repositoryName) :::
66+
// watching users
67+
watches.withFilter(_.notification == Watch.Watching).map(_.notificationUserName),
68+
// participants
69+
issue.openedUserName ::
70+
getComments(issue.userName, issue.repositoryName, issue.issueId).map(_.commentedUserName),
71+
// subscribers
72+
notifications.withFilter(_.subscribed).map(_.notificationUserName)
73+
) zip Seq(
74+
// not watching users
75+
watches.withFilter(_.notification == Watch.NotWatching).map(_.notificationUserName),
76+
// ignoring users
77+
watches.withFilter(_.notification == Watch.Ignoring).map(_.notificationUserName),
78+
// unsubscribers
79+
notifications.withFilter(!_.subscribed).map(_.notificationUserName)
80+
)
81+
).foldLeft[List[String]](Nil){ case (res, (add, remove)) =>
82+
(add ++ res) diff remove
83+
}.distinct
84+
85+
}
86+
5187
}

0 commit comments

Comments
 (0)