gitea源码

issue_label.go 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repo
  4. import (
  5. "errors"
  6. "net/http"
  7. "code.gitea.io/gitea/models/db"
  8. issues_model "code.gitea.io/gitea/models/issues"
  9. "code.gitea.io/gitea/models/organization"
  10. "code.gitea.io/gitea/modules/label"
  11. "code.gitea.io/gitea/modules/log"
  12. repo_module "code.gitea.io/gitea/modules/repository"
  13. "code.gitea.io/gitea/modules/templates"
  14. "code.gitea.io/gitea/modules/util"
  15. "code.gitea.io/gitea/modules/web"
  16. shared_label "code.gitea.io/gitea/routers/web/shared/label"
  17. "code.gitea.io/gitea/services/context"
  18. "code.gitea.io/gitea/services/forms"
  19. issue_service "code.gitea.io/gitea/services/issue"
  20. )
  21. const (
  22. tplLabels templates.TplName = "repo/issue/labels"
  23. )
  24. // Labels render issue's labels page
  25. func Labels(ctx *context.Context) {
  26. ctx.Data["Title"] = ctx.Tr("repo.labels")
  27. ctx.Data["PageIsIssueList"] = true
  28. ctx.Data["PageIsLabels"] = true
  29. ctx.Data["LabelTemplateFiles"] = repo_module.LabelTemplateFiles
  30. ctx.HTML(http.StatusOK, tplLabels)
  31. }
  32. // InitializeLabels init labels for a repository
  33. func InitializeLabels(ctx *context.Context) {
  34. form := web.GetForm(ctx).(*forms.InitializeLabelsForm)
  35. if ctx.HasError() {
  36. ctx.Redirect(ctx.Repo.RepoLink + "/labels")
  37. return
  38. }
  39. if err := repo_module.InitializeLabels(ctx, ctx.Repo.Repository.ID, form.TemplateName, false); err != nil {
  40. if label.IsErrTemplateLoad(err) {
  41. originalErr := err.(label.ErrTemplateLoad).OriginalError
  42. ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, originalErr))
  43. ctx.Redirect(ctx.Repo.RepoLink + "/labels")
  44. return
  45. }
  46. ctx.ServerError("InitializeLabels", err)
  47. return
  48. }
  49. ctx.Redirect(ctx.Repo.RepoLink + "/labels")
  50. }
  51. // RetrieveLabelsForList find all the labels of a repository and organization, it is only used by "/labels" page to list all labels
  52. func RetrieveLabelsForList(ctx *context.Context) {
  53. labels, err := issues_model.GetLabelsByRepoID(ctx, ctx.Repo.Repository.ID, ctx.FormString("sort"), db.ListOptions{})
  54. if err != nil {
  55. ctx.ServerError("RetrieveLabelsForList.GetLabels", err)
  56. return
  57. }
  58. for _, l := range labels {
  59. l.CalOpenIssues()
  60. }
  61. ctx.Data["Labels"] = labels
  62. if ctx.Repo.Owner.IsOrganization() {
  63. orgLabels, err := issues_model.GetLabelsByOrgID(ctx, ctx.Repo.Owner.ID, ctx.FormString("sort"), db.ListOptions{})
  64. if err != nil {
  65. ctx.ServerError("GetLabelsByOrgID", err)
  66. return
  67. }
  68. for _, l := range orgLabels {
  69. l.CalOpenOrgIssues(ctx, ctx.Repo.Repository.ID, l.ID)
  70. }
  71. ctx.Data["OrgLabels"] = orgLabels
  72. org, err := organization.GetOrgByName(ctx, ctx.Repo.Owner.LowerName)
  73. if err != nil {
  74. ctx.ServerError("GetOrgByName", err)
  75. return
  76. }
  77. if ctx.Doer != nil {
  78. ctx.Org.IsOwner, err = org.IsOwnedBy(ctx, ctx.Doer.ID)
  79. if err != nil {
  80. ctx.ServerError("org.IsOwnedBy", err)
  81. return
  82. }
  83. ctx.Org.OrgLink = org.AsUser().OrganisationLink()
  84. ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
  85. ctx.Data["OrganizationLink"] = ctx.Org.OrgLink
  86. }
  87. }
  88. ctx.Data["NumLabels"] = len(labels)
  89. ctx.Data["SortType"] = ctx.FormString("sort")
  90. }
  91. // NewLabel create new label for repository
  92. func NewLabel(ctx *context.Context) {
  93. form := shared_label.GetLabelEditForm(ctx)
  94. if ctx.Written() {
  95. return
  96. }
  97. l := &issues_model.Label{
  98. RepoID: ctx.Repo.Repository.ID,
  99. Name: form.Title,
  100. Exclusive: form.Exclusive,
  101. ExclusiveOrder: form.ExclusiveOrder,
  102. Description: form.Description,
  103. Color: form.Color,
  104. }
  105. if err := issues_model.NewLabel(ctx, l); err != nil {
  106. ctx.ServerError("NewLabel", err)
  107. return
  108. }
  109. ctx.JSONRedirect(ctx.Repo.RepoLink + "/labels")
  110. }
  111. // UpdateLabel update a label's name and color
  112. func UpdateLabel(ctx *context.Context) {
  113. form := shared_label.GetLabelEditForm(ctx)
  114. if ctx.Written() {
  115. return
  116. }
  117. l, err := issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, form.ID)
  118. if errors.Is(err, util.ErrNotExist) {
  119. ctx.JSONErrorNotFound()
  120. return
  121. } else if err != nil {
  122. ctx.ServerError("GetLabelInRepoByID", err)
  123. return
  124. }
  125. l.Name = form.Title
  126. l.Exclusive = form.Exclusive
  127. l.ExclusiveOrder = form.ExclusiveOrder
  128. l.Description = form.Description
  129. l.Color = form.Color
  130. l.SetArchived(form.IsArchived)
  131. if err := issues_model.UpdateLabel(ctx, l); err != nil {
  132. ctx.ServerError("UpdateLabel", err)
  133. return
  134. }
  135. ctx.JSONRedirect(ctx.Repo.RepoLink + "/labels")
  136. }
  137. // DeleteLabel delete a label
  138. func DeleteLabel(ctx *context.Context) {
  139. if err := issues_model.DeleteLabel(ctx, ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
  140. ctx.Flash.Error("DeleteLabel: " + err.Error())
  141. } else {
  142. ctx.Flash.Success(ctx.Tr("repo.issues.label_deletion_success"))
  143. }
  144. ctx.JSONRedirect(ctx.Repo.RepoLink + "/labels")
  145. }
  146. // UpdateIssueLabel change issue's labels
  147. func UpdateIssueLabel(ctx *context.Context) {
  148. issues := getActionIssues(ctx)
  149. if ctx.Written() {
  150. return
  151. }
  152. switch action := ctx.FormString("action"); action {
  153. case "clear":
  154. for _, issue := range issues {
  155. if err := issue_service.ClearLabels(ctx, issue, ctx.Doer); err != nil {
  156. ctx.ServerError("ClearLabels", err)
  157. return
  158. }
  159. }
  160. case "attach", "detach", "toggle", "toggle-alt":
  161. label, err := issues_model.GetLabelByID(ctx, ctx.FormInt64("id"))
  162. if err != nil {
  163. if issues_model.IsErrRepoLabelNotExist(err) {
  164. ctx.HTTPError(http.StatusNotFound, "GetLabelByID")
  165. } else {
  166. ctx.ServerError("GetLabelByID", err)
  167. }
  168. return
  169. }
  170. if action == "toggle" {
  171. // detach if any issues already have label, otherwise attach
  172. action = "attach"
  173. if label.ExclusiveScope() == "" {
  174. for _, issue := range issues {
  175. if issues_model.HasIssueLabel(ctx, issue.ID, label.ID) {
  176. action = "detach"
  177. break
  178. }
  179. }
  180. }
  181. } else if action == "toggle-alt" {
  182. // always detach with alt key pressed, to be able to remove
  183. // scoped labels
  184. action = "detach"
  185. }
  186. if action == "attach" {
  187. for _, issue := range issues {
  188. if err = issue_service.AddLabel(ctx, issue, ctx.Doer, label); err != nil {
  189. ctx.ServerError("AddLabel", err)
  190. return
  191. }
  192. }
  193. } else {
  194. for _, issue := range issues {
  195. if err = issue_service.RemoveLabel(ctx, issue, ctx.Doer, label); err != nil {
  196. ctx.ServerError("RemoveLabel", err)
  197. return
  198. }
  199. }
  200. }
  201. default:
  202. log.Warn("Unrecognized action: %s", action)
  203. ctx.HTTPError(http.StatusInternalServerError)
  204. return
  205. }
  206. ctx.JSONOK()
  207. }