4e33481357
Fix https://github.com/go-gitea/gitea/issues/23715 Other related PRs: * #23717 * #23716 * #23719 This PR is different from others, it tries to resolve the problem fundamentally (and brings more benefits) Although it looks like some more lines are added, actually many new lines are for tests. ---- Before, the code was just "guessing" the file type and try to parse them. <details> ![image](https://user-images.githubusercontent.com/2114189/228002245-57d58e27-1078-4da9-bf42-5bc0b264c6ce.png) </details> This PR: * Always remember the original option file names, and always use correct parser for them. * Another benefit is that we can sort the Label Templates now (before there was a map, its key order is undefined) ![image](https://user-images.githubusercontent.com/2114189/228002432-931b9f18-3908-484b-a36b-04760c9ad132.png)
232 lines
6.2 KiB
Go
232 lines
6.2 KiB
Go
// Copyright 2017 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
issues_model "code.gitea.io/gitea/models/issues"
|
|
"code.gitea.io/gitea/models/organization"
|
|
"code.gitea.io/gitea/modules/base"
|
|
"code.gitea.io/gitea/modules/context"
|
|
"code.gitea.io/gitea/modules/label"
|
|
"code.gitea.io/gitea/modules/log"
|
|
repo_module "code.gitea.io/gitea/modules/repository"
|
|
"code.gitea.io/gitea/modules/web"
|
|
"code.gitea.io/gitea/services/forms"
|
|
issue_service "code.gitea.io/gitea/services/issue"
|
|
)
|
|
|
|
const (
|
|
tplLabels base.TplName = "repo/issue/labels"
|
|
)
|
|
|
|
// Labels render issue's labels page
|
|
func Labels(ctx *context.Context) {
|
|
ctx.Data["Title"] = ctx.Tr("repo.labels")
|
|
ctx.Data["PageIsIssueList"] = true
|
|
ctx.Data["PageIsLabels"] = true
|
|
ctx.Data["LabelTemplateFiles"] = repo_module.LabelTemplateFiles
|
|
ctx.HTML(http.StatusOK, tplLabels)
|
|
}
|
|
|
|
// InitializeLabels init labels for a repository
|
|
func InitializeLabels(ctx *context.Context) {
|
|
form := web.GetForm(ctx).(*forms.InitializeLabelsForm)
|
|
if ctx.HasError() {
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
|
|
return
|
|
}
|
|
|
|
if err := repo_module.InitializeLabels(ctx, ctx.Repo.Repository.ID, form.TemplateName, false); err != nil {
|
|
if label.IsErrTemplateLoad(err) {
|
|
originalErr := err.(label.ErrTemplateLoad).OriginalError
|
|
ctx.Flash.Error(ctx.Tr("repo.issues.label_templates.fail_to_load_file", form.TemplateName, originalErr))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
|
|
return
|
|
}
|
|
ctx.ServerError("InitializeLabels", err)
|
|
return
|
|
}
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
|
|
}
|
|
|
|
// RetrieveLabels find all the labels of a repository and organization
|
|
func RetrieveLabels(ctx *context.Context) {
|
|
labels, err := issues_model.GetLabelsByRepoID(ctx, ctx.Repo.Repository.ID, ctx.FormString("sort"), db.ListOptions{})
|
|
if err != nil {
|
|
ctx.ServerError("RetrieveLabels.GetLabels", err)
|
|
return
|
|
}
|
|
|
|
for _, l := range labels {
|
|
l.CalOpenIssues()
|
|
}
|
|
|
|
ctx.Data["Labels"] = labels
|
|
|
|
if ctx.Repo.Owner.IsOrganization() {
|
|
orgLabels, err := issues_model.GetLabelsByOrgID(ctx, ctx.Repo.Owner.ID, ctx.FormString("sort"), db.ListOptions{})
|
|
if err != nil {
|
|
ctx.ServerError("GetLabelsByOrgID", err)
|
|
return
|
|
}
|
|
for _, l := range orgLabels {
|
|
l.CalOpenOrgIssues(ctx, ctx.Repo.Repository.ID, l.ID)
|
|
}
|
|
ctx.Data["OrgLabels"] = orgLabels
|
|
|
|
org, err := organization.GetOrgByName(ctx, ctx.Repo.Owner.LowerName)
|
|
if err != nil {
|
|
ctx.ServerError("GetOrgByName", err)
|
|
return
|
|
}
|
|
if ctx.Doer != nil {
|
|
ctx.Org.IsOwner, err = org.IsOwnedBy(ctx.Doer.ID)
|
|
if err != nil {
|
|
ctx.ServerError("org.IsOwnedBy", err)
|
|
return
|
|
}
|
|
ctx.Org.OrgLink = org.AsUser().OrganisationLink()
|
|
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
|
|
ctx.Data["OrganizationLink"] = ctx.Org.OrgLink
|
|
}
|
|
}
|
|
ctx.Data["NumLabels"] = len(labels)
|
|
ctx.Data["SortType"] = ctx.FormString("sort")
|
|
}
|
|
|
|
// NewLabel create new label for repository
|
|
func NewLabel(ctx *context.Context) {
|
|
form := web.GetForm(ctx).(*forms.CreateLabelForm)
|
|
ctx.Data["Title"] = ctx.Tr("repo.labels")
|
|
ctx.Data["PageIsLabels"] = true
|
|
|
|
if ctx.HasError() {
|
|
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
|
|
return
|
|
}
|
|
|
|
l := &issues_model.Label{
|
|
RepoID: ctx.Repo.Repository.ID,
|
|
Name: form.Title,
|
|
Exclusive: form.Exclusive,
|
|
Description: form.Description,
|
|
Color: form.Color,
|
|
}
|
|
if err := issues_model.NewLabel(ctx, l); err != nil {
|
|
ctx.ServerError("NewLabel", err)
|
|
return
|
|
}
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
|
|
}
|
|
|
|
// UpdateLabel update a label's name and color
|
|
func UpdateLabel(ctx *context.Context) {
|
|
form := web.GetForm(ctx).(*forms.CreateLabelForm)
|
|
l, err := issues_model.GetLabelInRepoByID(ctx, ctx.Repo.Repository.ID, form.ID)
|
|
if err != nil {
|
|
switch {
|
|
case issues_model.IsErrRepoLabelNotExist(err):
|
|
ctx.Error(http.StatusNotFound)
|
|
default:
|
|
ctx.ServerError("UpdateLabel", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
l.Name = form.Title
|
|
l.Exclusive = form.Exclusive
|
|
l.Description = form.Description
|
|
l.Color = form.Color
|
|
if err := issues_model.UpdateLabel(l); err != nil {
|
|
ctx.ServerError("UpdateLabel", err)
|
|
return
|
|
}
|
|
ctx.Redirect(ctx.Repo.RepoLink + "/labels")
|
|
}
|
|
|
|
// DeleteLabel delete a label
|
|
func DeleteLabel(ctx *context.Context) {
|
|
if err := issues_model.DeleteLabel(ctx.Repo.Repository.ID, ctx.FormInt64("id")); err != nil {
|
|
ctx.Flash.Error("DeleteLabel: " + err.Error())
|
|
} else {
|
|
ctx.Flash.Success(ctx.Tr("repo.issues.label_deletion_success"))
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, map[string]interface{}{
|
|
"redirect": ctx.Repo.RepoLink + "/labels",
|
|
})
|
|
}
|
|
|
|
// UpdateIssueLabel change issue's labels
|
|
func UpdateIssueLabel(ctx *context.Context) {
|
|
issues := getActionIssues(ctx)
|
|
if ctx.Written() {
|
|
return
|
|
}
|
|
|
|
switch action := ctx.FormString("action"); action {
|
|
case "clear":
|
|
for _, issue := range issues {
|
|
if err := issue_service.ClearLabels(issue, ctx.Doer); err != nil {
|
|
ctx.ServerError("ClearLabels", err)
|
|
return
|
|
}
|
|
}
|
|
case "attach", "detach", "toggle", "toggle-alt":
|
|
label, err := issues_model.GetLabelByID(ctx, ctx.FormInt64("id"))
|
|
if err != nil {
|
|
if issues_model.IsErrRepoLabelNotExist(err) {
|
|
ctx.Error(http.StatusNotFound, "GetLabelByID")
|
|
} else {
|
|
ctx.ServerError("GetLabelByID", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
if action == "toggle" {
|
|
// detach if any issues already have label, otherwise attach
|
|
action = "attach"
|
|
if label.ExclusiveScope() == "" {
|
|
for _, issue := range issues {
|
|
if issues_model.HasIssueLabel(ctx, issue.ID, label.ID) {
|
|
action = "detach"
|
|
break
|
|
}
|
|
}
|
|
}
|
|
} else if action == "toggle-alt" {
|
|
// always detach with alt key pressed, to be able to remove
|
|
// scoped labels
|
|
action = "detach"
|
|
}
|
|
|
|
if action == "attach" {
|
|
for _, issue := range issues {
|
|
if err = issue_service.AddLabel(issue, ctx.Doer, label); err != nil {
|
|
ctx.ServerError("AddLabel", err)
|
|
return
|
|
}
|
|
}
|
|
} else {
|
|
for _, issue := range issues {
|
|
if err = issue_service.RemoveLabel(issue, ctx.Doer, label); err != nil {
|
|
ctx.ServerError("RemoveLabel", err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
log.Warn("Unrecognized action: %s", action)
|
|
ctx.Error(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, map[string]interface{}{
|
|
"ok": true,
|
|
})
|
|
}
|