You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

notification.go 13KB

Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Use AJAX for notifications table (#10961) * Use AJAX for notifications table Signed-off-by: Andrew Thornton <art27@cantab.net> * move to separate js Signed-off-by: Andrew Thornton <art27@cantab.net> * placate golangci-lint Signed-off-by: Andrew Thornton <art27@cantab.net> * Add autoupdating notification count Signed-off-by: Andrew Thornton <art27@cantab.net> * Fix wipeall Signed-off-by: Andrew Thornton <art27@cantab.net> * placate tests Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * Try hide and hidden Signed-off-by: Andrew Thornton <art27@cantab.net> * More auto-update improvements Only run checker on pages that have a count Change starting checker to 10s with a back-off to 60s if there is no change Signed-off-by: Andrew Thornton <art27@cantab.net> * string comparison! Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @silverwind Signed-off-by: Andrew Thornton <art27@cantab.net> * add configurability as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Add documentation as per @6543 Signed-off-by: Andrew Thornton <art27@cantab.net> * Use CSRF header not query Signed-off-by: Andrew Thornton <art27@cantab.net> * Further JS improvements Fix @etzelia update notification table request Fix @silverwind comments Co-Authored-By: silverwind <me@silverwind.io> Signed-off-by: Andrew Thornton <art27@cantab.net> * Simplify the notification count fns Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: silverwind <me@silverwind.io>
4 years ago
Refactor and enhance issue indexer to support both searching, filtering and paging (#26012) Fix #24662. Replace #24822 and #25708 (although it has been merged) ## Background In the past, Gitea supported issue searching with a keyword and conditions in a less efficient way. It worked by searching for issues with the keyword and obtaining limited IDs (as it is heavy to get all) on the indexer (bleve/elasticsearch/meilisearch), and then querying with conditions on the database to find a subset of the found IDs. This is why the results could be incomplete. To solve this issue, we need to store all fields that could be used as conditions in the indexer and support both keyword and additional conditions when searching with the indexer. ## Major changes - Redefine `IndexerData` to include all fields that could be used as filter conditions. - Refactor `Search(ctx context.Context, kw string, repoIDs []int64, limit, start int, state string)` to `Search(ctx context.Context, options *SearchOptions)`, so it supports more conditions now. - Change the data type stored in `issueIndexerQueue`. Use `IndexerMetadata` instead of `IndexerData` in case the data has been updated while it is in the queue. This also reduces the storage size of the queue. - Enhance searching with Bleve/Elasticsearch/Meilisearch, make them fully support `SearchOptions`. Also, update the data versions. - Keep most logic of database indexer, but remove `issues.SearchIssueIDsByKeyword` in `models` to avoid confusion where is the entry point to search issues. - Start a Meilisearch instance to test it in unit tests. - Add unit tests with almost full coverage to test Bleve/Elasticsearch/Meilisearch indexer. --------- Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
10 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package user
  4. import (
  5. goctx "context"
  6. "errors"
  7. "fmt"
  8. "net/http"
  9. "net/url"
  10. "strings"
  11. activities_model "code.gitea.io/gitea/models/activities"
  12. "code.gitea.io/gitea/models/db"
  13. issues_model "code.gitea.io/gitea/models/issues"
  14. repo_model "code.gitea.io/gitea/models/repo"
  15. "code.gitea.io/gitea/modules/base"
  16. "code.gitea.io/gitea/modules/log"
  17. "code.gitea.io/gitea/modules/optional"
  18. "code.gitea.io/gitea/modules/setting"
  19. "code.gitea.io/gitea/modules/structs"
  20. "code.gitea.io/gitea/modules/util"
  21. "code.gitea.io/gitea/services/context"
  22. issue_service "code.gitea.io/gitea/services/issue"
  23. pull_service "code.gitea.io/gitea/services/pull"
  24. )
  25. const (
  26. tplNotification base.TplName = "user/notification/notification"
  27. tplNotificationDiv base.TplName = "user/notification/notification_div"
  28. tplNotificationSubscriptions base.TplName = "user/notification/notification_subscriptions"
  29. )
  30. // GetNotificationCount is the middleware that sets the notification count in the context
  31. func GetNotificationCount(ctx *context.Context) {
  32. if strings.HasPrefix(ctx.Req.URL.Path, "/api") {
  33. return
  34. }
  35. if !ctx.IsSigned {
  36. return
  37. }
  38. ctx.Data["NotificationUnreadCount"] = func() int64 {
  39. count, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
  40. UserID: ctx.Doer.ID,
  41. Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread},
  42. })
  43. if err != nil {
  44. if err != goctx.Canceled {
  45. log.Error("Unable to GetNotificationCount for user:%-v: %v", ctx.Doer, err)
  46. }
  47. return -1
  48. }
  49. return count
  50. }
  51. }
  52. // Notifications is the notifications page
  53. func Notifications(ctx *context.Context) {
  54. getNotifications(ctx)
  55. if ctx.Written() {
  56. return
  57. }
  58. if ctx.FormBool("div-only") {
  59. ctx.Data["SequenceNumber"] = ctx.FormString("sequence-number")
  60. ctx.HTML(http.StatusOK, tplNotificationDiv)
  61. return
  62. }
  63. ctx.HTML(http.StatusOK, tplNotification)
  64. }
  65. func getNotifications(ctx *context.Context) {
  66. var (
  67. keyword = ctx.FormTrim("q")
  68. status activities_model.NotificationStatus
  69. page = ctx.FormInt("page")
  70. perPage = ctx.FormInt("perPage")
  71. )
  72. if page < 1 {
  73. page = 1
  74. }
  75. if perPage < 1 {
  76. perPage = 20
  77. }
  78. switch keyword {
  79. case "read":
  80. status = activities_model.NotificationStatusRead
  81. default:
  82. status = activities_model.NotificationStatusUnread
  83. }
  84. total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
  85. UserID: ctx.Doer.ID,
  86. Status: []activities_model.NotificationStatus{status},
  87. })
  88. if err != nil {
  89. ctx.ServerError("ErrGetNotificationCount", err)
  90. return
  91. }
  92. // redirect to last page if request page is more than total pages
  93. pager := context.NewPagination(int(total), perPage, page, 5)
  94. if pager.Paginater.Current() < page {
  95. ctx.Redirect(fmt.Sprintf("%s/notifications?q=%s&page=%d", setting.AppSubURL, url.QueryEscape(ctx.FormString("q")), pager.Paginater.Current()))
  96. return
  97. }
  98. statuses := []activities_model.NotificationStatus{status, activities_model.NotificationStatusPinned}
  99. nls, err := db.Find[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
  100. ListOptions: db.ListOptions{
  101. PageSize: perPage,
  102. Page: page,
  103. },
  104. UserID: ctx.Doer.ID,
  105. Status: statuses,
  106. })
  107. if err != nil {
  108. ctx.ServerError("db.Find[activities_model.Notification]", err)
  109. return
  110. }
  111. notifications := activities_model.NotificationList(nls)
  112. failCount := 0
  113. repos, failures, err := notifications.LoadRepos(ctx)
  114. if err != nil {
  115. ctx.ServerError("LoadRepos", err)
  116. return
  117. }
  118. notifications = notifications.Without(failures)
  119. if err := repos.LoadAttributes(ctx); err != nil {
  120. ctx.ServerError("LoadAttributes", err)
  121. return
  122. }
  123. failCount += len(failures)
  124. failures, err = notifications.LoadIssues(ctx)
  125. if err != nil {
  126. ctx.ServerError("LoadIssues", err)
  127. return
  128. }
  129. notifications = notifications.Without(failures)
  130. failCount += len(failures)
  131. failures, err = notifications.LoadComments(ctx)
  132. if err != nil {
  133. ctx.ServerError("LoadComments", err)
  134. return
  135. }
  136. notifications = notifications.Without(failures)
  137. failCount += len(failures)
  138. if failCount > 0 {
  139. ctx.Flash.Error(fmt.Sprintf("ERROR: %d notifications were removed due to missing parts - check the logs", failCount))
  140. }
  141. ctx.Data["Title"] = ctx.Tr("notifications")
  142. ctx.Data["Keyword"] = keyword
  143. ctx.Data["Status"] = status
  144. ctx.Data["Notifications"] = notifications
  145. pager.SetDefaultParams(ctx)
  146. ctx.Data["Page"] = pager
  147. }
  148. // NotificationStatusPost is a route for changing the status of a notification
  149. func NotificationStatusPost(ctx *context.Context) {
  150. var (
  151. notificationID = ctx.FormInt64("notification_id")
  152. statusStr = ctx.FormString("status")
  153. status activities_model.NotificationStatus
  154. )
  155. switch statusStr {
  156. case "read":
  157. status = activities_model.NotificationStatusRead
  158. case "unread":
  159. status = activities_model.NotificationStatusUnread
  160. case "pinned":
  161. status = activities_model.NotificationStatusPinned
  162. default:
  163. ctx.ServerError("InvalidNotificationStatus", errors.New("Invalid notification status"))
  164. return
  165. }
  166. if _, err := activities_model.SetNotificationStatus(ctx, notificationID, ctx.Doer, status); err != nil {
  167. ctx.ServerError("SetNotificationStatus", err)
  168. return
  169. }
  170. if !ctx.FormBool("noredirect") {
  171. url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, url.QueryEscape(ctx.FormString("page")))
  172. ctx.Redirect(url, http.StatusSeeOther)
  173. }
  174. getNotifications(ctx)
  175. if ctx.Written() {
  176. return
  177. }
  178. ctx.Data["Link"] = setting.AppSubURL + "/notifications"
  179. ctx.Data["SequenceNumber"] = ctx.Req.PostFormValue("sequence-number")
  180. ctx.HTML(http.StatusOK, tplNotificationDiv)
  181. }
  182. // NotificationPurgePost is a route for 'purging' the list of notifications - marking all unread as read
  183. func NotificationPurgePost(ctx *context.Context) {
  184. err := activities_model.UpdateNotificationStatuses(ctx, ctx.Doer, activities_model.NotificationStatusUnread, activities_model.NotificationStatusRead)
  185. if err != nil {
  186. ctx.ServerError("UpdateNotificationStatuses", err)
  187. return
  188. }
  189. ctx.Redirect(setting.AppSubURL+"/notifications", http.StatusSeeOther)
  190. }
  191. // NotificationSubscriptions returns the list of subscribed issues
  192. func NotificationSubscriptions(ctx *context.Context) {
  193. page := ctx.FormInt("page")
  194. if page < 1 {
  195. page = 1
  196. }
  197. sortType := ctx.FormString("sort")
  198. ctx.Data["SortType"] = sortType
  199. state := ctx.FormString("state")
  200. if !util.SliceContainsString([]string{"all", "open", "closed"}, state, true) {
  201. state = "all"
  202. }
  203. ctx.Data["State"] = state
  204. // default state filter is "all"
  205. showClosed := optional.None[bool]()
  206. switch state {
  207. case "closed":
  208. showClosed = optional.Some(true)
  209. case "open":
  210. showClosed = optional.Some(false)
  211. }
  212. issueType := ctx.FormString("issueType")
  213. // default issue type is no filter
  214. issueTypeBool := optional.None[bool]()
  215. switch issueType {
  216. case "issues":
  217. issueTypeBool = optional.Some(false)
  218. case "pulls":
  219. issueTypeBool = optional.Some(true)
  220. }
  221. ctx.Data["IssueType"] = issueType
  222. var labelIDs []int64
  223. selectedLabels := ctx.FormString("labels")
  224. ctx.Data["Labels"] = selectedLabels
  225. if len(selectedLabels) > 0 && selectedLabels != "0" {
  226. var err error
  227. labelIDs, err = base.StringsToInt64s(strings.Split(selectedLabels, ","))
  228. if err != nil {
  229. ctx.ServerError("StringsToInt64s", err)
  230. return
  231. }
  232. }
  233. count, err := issues_model.CountIssues(ctx, &issues_model.IssuesOptions{
  234. SubscriberID: ctx.Doer.ID,
  235. IsClosed: showClosed,
  236. IsPull: issueTypeBool,
  237. LabelIDs: labelIDs,
  238. })
  239. if err != nil {
  240. ctx.ServerError("CountIssues", err)
  241. return
  242. }
  243. issues, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{
  244. Paginator: &db.ListOptions{
  245. PageSize: setting.UI.IssuePagingNum,
  246. Page: page,
  247. },
  248. SubscriberID: ctx.Doer.ID,
  249. SortType: sortType,
  250. IsClosed: showClosed,
  251. IsPull: issueTypeBool,
  252. LabelIDs: labelIDs,
  253. })
  254. if err != nil {
  255. ctx.ServerError("Issues", err)
  256. return
  257. }
  258. commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(ctx, issues)
  259. if err != nil {
  260. ctx.ServerError("GetIssuesAllCommitStatus", err)
  261. return
  262. }
  263. ctx.Data["CommitLastStatus"] = lastStatus
  264. ctx.Data["CommitStatuses"] = commitStatuses
  265. ctx.Data["Issues"] = issues
  266. ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, "")
  267. commitStatus, err := pull_service.GetIssuesLastCommitStatus(ctx, issues)
  268. if err != nil {
  269. ctx.ServerError("GetIssuesLastCommitStatus", err)
  270. return
  271. }
  272. ctx.Data["CommitStatus"] = commitStatus
  273. approvalCounts, err := issues.GetApprovalCounts(ctx)
  274. if err != nil {
  275. ctx.ServerError("ApprovalCounts", err)
  276. return
  277. }
  278. ctx.Data["ApprovalCounts"] = func(issueID int64, typ string) int64 {
  279. counts, ok := approvalCounts[issueID]
  280. if !ok || len(counts) == 0 {
  281. return 0
  282. }
  283. reviewTyp := issues_model.ReviewTypeApprove
  284. if typ == "reject" {
  285. reviewTyp = issues_model.ReviewTypeReject
  286. } else if typ == "waiting" {
  287. reviewTyp = issues_model.ReviewTypeRequest
  288. }
  289. for _, count := range counts {
  290. if count.Type == reviewTyp {
  291. return count.Count
  292. }
  293. }
  294. return 0
  295. }
  296. ctx.Data["Status"] = 1
  297. ctx.Data["Title"] = ctx.Tr("notification.subscriptions")
  298. // redirect to last page if request page is more than total pages
  299. pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
  300. if pager.Paginater.Current() < page {
  301. ctx.Redirect(fmt.Sprintf("/notifications/subscriptions?page=%d", pager.Paginater.Current()))
  302. return
  303. }
  304. pager.AddParam(ctx, "sort", "SortType")
  305. pager.AddParam(ctx, "state", "State")
  306. ctx.Data["Page"] = pager
  307. ctx.HTML(http.StatusOK, tplNotificationSubscriptions)
  308. }
  309. // NotificationWatching returns the list of watching repos
  310. func NotificationWatching(ctx *context.Context) {
  311. page := ctx.FormInt("page")
  312. if page < 1 {
  313. page = 1
  314. }
  315. keyword := ctx.FormTrim("q")
  316. ctx.Data["Keyword"] = keyword
  317. var orderBy db.SearchOrderBy
  318. ctx.Data["SortType"] = ctx.FormString("sort")
  319. switch ctx.FormString("sort") {
  320. case "newest":
  321. orderBy = db.SearchOrderByNewest
  322. case "oldest":
  323. orderBy = db.SearchOrderByOldest
  324. case "recentupdate":
  325. orderBy = db.SearchOrderByRecentUpdated
  326. case "leastupdate":
  327. orderBy = db.SearchOrderByLeastUpdated
  328. case "reversealphabetically":
  329. orderBy = db.SearchOrderByAlphabeticallyReverse
  330. case "alphabetically":
  331. orderBy = db.SearchOrderByAlphabetically
  332. case "moststars":
  333. orderBy = db.SearchOrderByStarsReverse
  334. case "feweststars":
  335. orderBy = db.SearchOrderByStars
  336. case "mostforks":
  337. orderBy = db.SearchOrderByForksReverse
  338. case "fewestforks":
  339. orderBy = db.SearchOrderByForks
  340. default:
  341. ctx.Data["SortType"] = "recentupdate"
  342. orderBy = db.SearchOrderByRecentUpdated
  343. }
  344. archived := ctx.FormOptionalBool("archived")
  345. ctx.Data["IsArchived"] = archived
  346. fork := ctx.FormOptionalBool("fork")
  347. ctx.Data["IsFork"] = fork
  348. mirror := ctx.FormOptionalBool("mirror")
  349. ctx.Data["IsMirror"] = mirror
  350. template := ctx.FormOptionalBool("template")
  351. ctx.Data["IsTemplate"] = template
  352. private := ctx.FormOptionalBool("private")
  353. ctx.Data["IsPrivate"] = private
  354. repos, count, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
  355. ListOptions: db.ListOptions{
  356. PageSize: setting.UI.User.RepoPagingNum,
  357. Page: page,
  358. },
  359. Actor: ctx.Doer,
  360. Keyword: keyword,
  361. OrderBy: orderBy,
  362. Private: ctx.IsSigned,
  363. WatchedByID: ctx.Doer.ID,
  364. Collaborate: optional.Some(false),
  365. TopicOnly: ctx.FormBool("topic"),
  366. IncludeDescription: setting.UI.SearchRepoDescription,
  367. Archived: archived,
  368. Fork: fork,
  369. Mirror: mirror,
  370. Template: template,
  371. IsPrivate: private,
  372. })
  373. if err != nil {
  374. ctx.ServerError("SearchRepository", err)
  375. return
  376. }
  377. total := int(count)
  378. ctx.Data["Total"] = total
  379. ctx.Data["Repos"] = repos
  380. // redirect to last page if request page is more than total pages
  381. pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5)
  382. pager.SetDefaultParams(ctx)
  383. ctx.Data["Page"] = pager
  384. ctx.Data["Status"] = 2
  385. ctx.Data["Title"] = ctx.Tr("notification.watching")
  386. ctx.HTML(http.StatusOK, tplNotificationSubscriptions)
  387. }
  388. // NewAvailable returns the notification counts
  389. func NewAvailable(ctx *context.Context) {
  390. total, err := db.Count[activities_model.Notification](ctx, activities_model.FindNotificationOptions{
  391. UserID: ctx.Doer.ID,
  392. Status: []activities_model.NotificationStatus{activities_model.NotificationStatusUnread},
  393. })
  394. if err != nil {
  395. log.Error("db.Count[activities_model.Notification]", err)
  396. ctx.JSON(http.StatusOK, structs.NotificationCount{New: 0})
  397. return
  398. }
  399. ctx.JSON(http.StatusOK, structs.NotificationCount{New: total})
  400. }