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.

issue_label.go 6.2KB

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