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 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package user
  5. import (
  6. goctx "context"
  7. "errors"
  8. "fmt"
  9. "net/http"
  10. "net/url"
  11. "strings"
  12. "code.gitea.io/gitea/models"
  13. "code.gitea.io/gitea/modules/base"
  14. "code.gitea.io/gitea/modules/context"
  15. "code.gitea.io/gitea/modules/log"
  16. "code.gitea.io/gitea/modules/setting"
  17. "code.gitea.io/gitea/modules/structs"
  18. )
  19. const (
  20. tplNotification base.TplName = "user/notification/notification"
  21. tplNotificationDiv base.TplName = "user/notification/notification_div"
  22. )
  23. // GetNotificationCount is the middleware that sets the notification count in the context
  24. func GetNotificationCount(c *context.Context) {
  25. if strings.HasPrefix(c.Req.URL.Path, "/api") {
  26. return
  27. }
  28. if !c.IsSigned {
  29. return
  30. }
  31. c.Data["NotificationUnreadCount"] = func() int64 {
  32. count, err := models.GetNotificationCount(c, c.Doer, models.NotificationStatusUnread)
  33. if err != nil {
  34. if err != goctx.Canceled {
  35. log.Error("Unable to GetNotificationCount for user:%-v: %v", c.Doer, err)
  36. }
  37. return -1
  38. }
  39. return count
  40. }
  41. }
  42. // Notifications is the notifications page
  43. func Notifications(c *context.Context) {
  44. getNotifications(c)
  45. if c.Written() {
  46. return
  47. }
  48. if c.FormBool("div-only") {
  49. c.Data["SequenceNumber"] = c.FormString("sequence-number")
  50. c.HTML(http.StatusOK, tplNotificationDiv)
  51. return
  52. }
  53. c.HTML(http.StatusOK, tplNotification)
  54. }
  55. func getNotifications(c *context.Context) {
  56. var (
  57. keyword = c.FormTrim("q")
  58. status models.NotificationStatus
  59. page = c.FormInt("page")
  60. perPage = c.FormInt("perPage")
  61. )
  62. if page < 1 {
  63. page = 1
  64. }
  65. if perPage < 1 {
  66. perPage = 20
  67. }
  68. switch keyword {
  69. case "read":
  70. status = models.NotificationStatusRead
  71. default:
  72. status = models.NotificationStatusUnread
  73. }
  74. total, err := models.GetNotificationCount(c, c.Doer, status)
  75. if err != nil {
  76. c.ServerError("ErrGetNotificationCount", err)
  77. return
  78. }
  79. // redirect to last page if request page is more than total pages
  80. pager := context.NewPagination(int(total), perPage, page, 5)
  81. if pager.Paginater.Current() < page {
  82. c.Redirect(fmt.Sprintf("%s/notifications?q=%s&page=%d", setting.AppSubURL, url.QueryEscape(c.FormString("q")), pager.Paginater.Current()))
  83. return
  84. }
  85. statuses := []models.NotificationStatus{status, models.NotificationStatusPinned}
  86. notifications, err := models.NotificationsForUser(c, c.Doer, statuses, page, perPage)
  87. if err != nil {
  88. c.ServerError("ErrNotificationsForUser", err)
  89. return
  90. }
  91. failCount := 0
  92. repos, failures, err := notifications.LoadRepos()
  93. if err != nil {
  94. c.ServerError("LoadRepos", err)
  95. return
  96. }
  97. notifications = notifications.Without(failures)
  98. if err := repos.LoadAttributes(); err != nil {
  99. c.ServerError("LoadAttributes", err)
  100. return
  101. }
  102. failCount += len(failures)
  103. failures, err = notifications.LoadIssues()
  104. if err != nil {
  105. c.ServerError("LoadIssues", err)
  106. return
  107. }
  108. notifications = notifications.Without(failures)
  109. failCount += len(failures)
  110. failures, err = notifications.LoadComments()
  111. if err != nil {
  112. c.ServerError("LoadComments", err)
  113. return
  114. }
  115. notifications = notifications.Without(failures)
  116. failCount += len(failures)
  117. if failCount > 0 {
  118. c.Flash.Error(fmt.Sprintf("ERROR: %d notifications were removed due to missing parts - check the logs", failCount))
  119. }
  120. c.Data["Title"] = c.Tr("notifications")
  121. c.Data["Keyword"] = keyword
  122. c.Data["Status"] = status
  123. c.Data["Notifications"] = notifications
  124. pager.SetDefaultParams(c)
  125. c.Data["Page"] = pager
  126. }
  127. // NotificationStatusPost is a route for changing the status of a notification
  128. func NotificationStatusPost(c *context.Context) {
  129. var (
  130. notificationID = c.FormInt64("notification_id")
  131. statusStr = c.FormString("status")
  132. status models.NotificationStatus
  133. )
  134. switch statusStr {
  135. case "read":
  136. status = models.NotificationStatusRead
  137. case "unread":
  138. status = models.NotificationStatusUnread
  139. case "pinned":
  140. status = models.NotificationStatusPinned
  141. default:
  142. c.ServerError("InvalidNotificationStatus", errors.New("Invalid notification status"))
  143. return
  144. }
  145. if _, err := models.SetNotificationStatus(notificationID, c.Doer, status); err != nil {
  146. c.ServerError("SetNotificationStatus", err)
  147. return
  148. }
  149. if !c.FormBool("noredirect") {
  150. url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, url.QueryEscape(c.FormString("page")))
  151. c.Redirect(url, http.StatusSeeOther)
  152. }
  153. getNotifications(c)
  154. if c.Written() {
  155. return
  156. }
  157. c.Data["Link"] = setting.AppURL + "notifications"
  158. c.Data["SequenceNumber"] = c.Req.PostFormValue("sequence-number")
  159. c.HTML(http.StatusOK, tplNotificationDiv)
  160. }
  161. // NotificationPurgePost is a route for 'purging' the list of notifications - marking all unread as read
  162. func NotificationPurgePost(c *context.Context) {
  163. err := models.UpdateNotificationStatuses(c.Doer, models.NotificationStatusUnread, models.NotificationStatusRead)
  164. if err != nil {
  165. c.ServerError("ErrUpdateNotificationStatuses", err)
  166. return
  167. }
  168. c.Redirect(setting.AppSubURL+"/notifications", http.StatusSeeOther)
  169. }
  170. // NewAvailable returns the notification counts
  171. func NewAvailable(ctx *context.Context) {
  172. ctx.JSON(http.StatusOK, structs.NotificationCount{New: models.CountUnread(ctx, ctx.Doer.ID)})
  173. }