diff --git a/models/project/board.go b/models/project/board.go index 3e2d8e0472c51..e73258acc949f 100644 --- a/models/project/board.go +++ b/models/project/board.go @@ -55,6 +55,7 @@ type Board struct { Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board Sorting int8 `xorm:"NOT NULL DEFAULT 0"` Color string `xorm:"VARCHAR(7)"` + LabelID int64 `xorm:"DEFAULT 0"` ProjectID int64 `xorm:"INDEX NOT NULL"` CreatorID int64 `xorm:"NOT NULL"` diff --git a/models/project/issue.go b/models/project/issue.go index ebc9719de55d0..875d8566b5215 100644 --- a/models/project/issue.go +++ b/models/project/issue.go @@ -22,6 +22,9 @@ type ProjectIssue struct { //revive:disable-line:exported // the sorting order on the board Sorting int64 `xorm:"NOT NULL DEFAULT 0"` + + // label that is added when a issue is moved to this column + LabelID int64 `xorm:"DEFAULT 0"` } func init() { diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 03798a712c2ee..822aff90774ee 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/web" shared_user "code.gitea.io/gitea/routers/web/shared/user" "code.gitea.io/gitea/services/forms" + issue_service "code.gitea.io/gitea/services/issue" ) const ( @@ -545,6 +546,7 @@ func AddBoardToProjectPost(ctx *context.Context) { ProjectID: project.ID, Title: form.Title, Color: form.Color, + LabelID: form.LabelID, CreatorID: ctx.Doer.ID, }); err != nil { ctx.ServerError("NewProjectBoard", err) @@ -742,10 +744,27 @@ func MoveIssues(ctx *context.Context) { } } + fromColumnLabelID, err := strconv.ParseInt(ctx.FormString("fromColumnLabelID"), 10, 64) + if err != nil { + ctx.ServerError("fromColumnLableId is required", errors.New("fromColumnLableId is required")) + return + } + + issueID, err := strconv.ParseInt(ctx.FormString("issueID"), 10, 64) + if err != nil { + ctx.ServerError("moved issueID is required", errors.New("moved issueID is required")) + return + } + if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil { ctx.ServerError("MoveIssuesOnProjectBoard", err) return } + if err = issue_service.AddAndOrRemoveLabelFromIssue(ctx, issueID, fromColumnLabelID, board); err != nil { + ctx.ServerError("failed adding/removing label in addAndOrRemoveLabel", err) + return + } + ctx.JSONOK() } diff --git a/routers/web/repo/issue_label.go b/routers/web/repo/issue_label.go index dd3e2803b4e43..b4727b8a22f1a 100644 --- a/routers/web/repo/issue_label.go +++ b/routers/web/repo/issue_label.go @@ -99,6 +99,17 @@ func RetrieveLabels(ctx *context.Context) { ctx.Data["SortType"] = ctx.FormString("sort") } +// RetrieveLabelsOrg clone of RetrieveLabels but without repo +func RetrieveLabelsOrg(ctx *context.Context) { + orgLabels, err := issues_model.GetLabelsByOrgID(ctx, ctx.Org.Organization.ID, ctx.FormString("sort"), db.ListOptions{}) + if err != nil { + ctx.ServerError("GetLabelsByOrgID", err) + return + } + + ctx.Data["Labels"] = orgLabels +} + // NewLabel create new label for repository func NewLabel(ctx *context.Context) { form := web.GetForm(ctx).(*forms.CreateLabelForm) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 4908bb796d9dc..a905e138149d0 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -8,6 +8,7 @@ import ( "fmt" "net/http" "net/url" + "strconv" "strings" "code.gitea.io/gitea/models/db" @@ -25,6 +26,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/forms" + issue_service "code.gitea.io/gitea/services/issue" ) const ( @@ -487,6 +489,7 @@ func AddBoardToProjectPost(ctx *context.Context) { Title: form.Title, Color: form.Color, CreatorID: ctx.Doer.ID, + LabelID: form.LabelID, }); err != nil { ctx.ServerError("NewProjectBoard", err) return @@ -691,10 +694,27 @@ func MoveIssues(ctx *context.Context) { } } + fromColumnLabelID, err := strconv.ParseInt(ctx.FormString("fromColumnLabelID"), 10, 64) + if err != nil { + ctx.ServerError("fromColumnLableId is required", errors.New("fromColumnLableId is required")) + return + } + + issueID, err := strconv.ParseInt(ctx.FormString("issueID"), 10, 64) + if err != nil { + ctx.ServerError("moved issueID is required", errors.New("moved issueID is required")) + return + } + if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil { ctx.ServerError("MoveIssuesOnProjectBoard", err) return } + if err = issue_service.AddAndOrRemoveLabelFromIssue(ctx, issueID, fromColumnLabelID, board); err != nil { + ctx.ServerError("failed adding/removing label in addAndOrRemoveLabel", err) + return + } + ctx.JSONOK() } diff --git a/routers/web/web.go b/routers/web/web.go index ff0ce0c2586bb..faae91a6dfbab 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -983,7 +983,7 @@ func registerRoutes(m *web.Route) { m.Group("/projects", func() { m.Group("", func() { m.Get("", org.Projects) - m.Get("/{id}", org.ViewProject) + m.Get("/{id}", repo.RetrieveLabelsOrg, org.ViewProject) }, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead, true)) m.Group("", func() { //nolint:dupl m.Get("/new", org.RenderNewProject) @@ -1322,7 +1322,7 @@ func registerRoutes(m *web.Route) { m.Group("/projects", func() { m.Get("", repo.Projects) - m.Get("/{id}", repo.ViewProject) + m.Get("/{id}", repo.RetrieveLabels, repo.ViewProject) m.Group("", func() { //nolint:dupl m.Get("/new", repo.RenderNewProject) m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost) diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 845eccf817d3a..7abfb97b8f2f8 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -531,6 +531,7 @@ type EditProjectBoardForm struct { Title string `binding:"Required;MaxSize(100)"` Sorting int8 Color string `binding:"MaxSize(7)"` + LabelID int64 } // _____ .__.__ __ diff --git a/services/issue/project.go b/services/issue/project.go new file mode 100644 index 0000000000000..798ec7fa89905 --- /dev/null +++ b/services/issue/project.go @@ -0,0 +1,51 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package issue + +import ( + "errors" + + issues_model "code.gitea.io/gitea/models/issues" + project_model "code.gitea.io/gitea/models/project" + "code.gitea.io/gitea/modules/context" +) + +// AddAndOrRemoveLabelFromIssue updates issue label according to board LabelID +func AddAndOrRemoveLabelFromIssue(ctx *context.Context, currentIssueID, fromColumnLabelID int64, board *project_model.Board) error { + issue, err := issues_model.GetIssueByID(ctx, currentIssueID) + if err != nil { + return errors.New("failed getting issue") + } + var addedLabel *issues_model.Label + if board.LabelID != 0 { + addedLabel, err = issues_model.GetLabelByID(ctx, board.LabelID) + if err != nil { + return errors.New("failed getting add label") + } + } + var removedLabel *issues_model.Label + if fromColumnLabelID != 0 { + + removedLabel, err = issues_model.GetLabelByID(ctx, fromColumnLabelID) + if err != nil { + return errors.New("failed getting remove label") + } + } + + // Delete old label from current issue + if fromColumnLabelID != 0 { + if err := RemoveLabel(ctx, issue, ctx.Doer, removedLabel); err != nil { + return err + } + } + + // Add New Label to current issue + if board.LabelID != 0 { + if err := AddLabel(ctx, issue, ctx.Doer, addedLabel); err != nil { + return err + } + } + + return nil +} diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index b3ad03c354988..1c4b8f7ed55af 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -1,67 +1,91 @@ {{$canWriteProject := and .CanWriteProjects (or (not .Repository) (not .Repository.IsArchived))}} -