This commit is contained in:
Lunny Xiao 2024-05-05 17:19:38 +03:00 committed by GitHub
commit 5fa04476db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 181 additions and 58 deletions

View File

@ -220,6 +220,13 @@ func (r RoleInRepo) LocaleHelper(lang translation.Locale) string {
return lang.TrString("repo.issues.role." + string(r) + "_helper")
}
// CommentMetaData stores metadata for a comment, these data will not be changed once inserted into database
type CommentMetaData struct {
ProjectColumnID int64 `json:"project_column_id,omitempty"`
ProjectColumnTitle string `json:"project_column_title,omitempty"`
ProjectTitle string `json:"project_title,omitempty"`
}
// Comment represents a comment in commit and issue page.
type Comment struct {
ID int64 `xorm:"pk autoincr"`
@ -292,6 +299,8 @@ type Comment struct {
RefAction references.XRefAction `xorm:"SMALLINT"` // What happens if RefIssueID resolves
RefIsPull bool
CommentMetaData *CommentMetaData `xorm:"JSON TEXT"` // put all non-index metadata in a single field
RefRepo *repo_model.Repository `xorm:"-"`
RefIssue *Issue `xorm:"-"`
RefComment *Comment `xorm:"-"`
@ -794,6 +803,15 @@ func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment,
LabelID = opts.Label.ID
}
var commentMetaData *CommentMetaData
if opts.ProjectColumnTitle != "" {
commentMetaData = &CommentMetaData{
ProjectColumnID: opts.ProjectColumnID,
ProjectColumnTitle: opts.ProjectColumnTitle,
ProjectTitle: opts.ProjectTitle,
}
}
comment := &Comment{
Type: opts.Type,
PosterID: opts.Doer.ID,
@ -827,6 +845,7 @@ func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment,
RefIsPull: opts.RefIsPull,
IsForcePush: opts.IsForcePush,
Invalidated: opts.Invalidated,
CommentMetaData: commentMetaData,
}
if _, err = e.Insert(comment); err != nil {
return nil, err
@ -979,34 +998,37 @@ type CreateCommentOptions struct {
Issue *Issue
Label *Label
DependentIssueID int64
OldMilestoneID int64
MilestoneID int64
OldProjectID int64
ProjectID int64
TimeID int64
AssigneeID int64
AssigneeTeamID int64
RemovedAssignee bool
OldTitle string
NewTitle string
OldRef string
NewRef string
CommitID int64
CommitSHA string
Patch string
LineNum int64
TreePath string
ReviewID int64
Content string
Attachments []string // UUIDs of attachments
RefRepoID int64
RefIssueID int64
RefCommentID int64
RefAction references.XRefAction
RefIsPull bool
IsForcePush bool
Invalidated bool
DependentIssueID int64
OldMilestoneID int64
MilestoneID int64
OldProjectID int64
ProjectID int64
ProjectTitle string
ProjectColumnID int64
ProjectColumnTitle string
TimeID int64
AssigneeID int64
AssigneeTeamID int64
RemovedAssignee bool
OldTitle string
NewTitle string
OldRef string
NewRef string
CommitID int64
CommitSHA string
Patch string
LineNum int64
TreePath string
ReviewID int64
Content string
Attachments []string // UUIDs of attachments
RefRepoID int64
RefIssueID int64
RefCommentID int64
RefAction references.XRefAction
RefIsPull bool
IsForcePush bool
Invalidated bool
}
// GetCommentByID returns the comment by given ID.

View File

@ -434,6 +434,7 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er
Join("INNER", "issue", "issue.id = comment.issue_id").
In("issue.id", issuesIDs[:limit]).
Where(cond).
NoAutoCondition().
Rows(new(Comment))
if err != nil {
return err

View File

@ -21,6 +21,7 @@ import (
"code.gitea.io/gitea/models/migrations/v1_20"
"code.gitea.io/gitea/models/migrations/v1_21"
"code.gitea.io/gitea/models/migrations/v1_22"
"code.gitea.io/gitea/models/migrations/v1_23"
"code.gitea.io/gitea/models/migrations/v1_6"
"code.gitea.io/gitea/models/migrations/v1_7"
"code.gitea.io/gitea/models/migrations/v1_8"
@ -587,6 +588,9 @@ var migrations = []Migration{
NewMigration("Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable),
// Gitea 1.22.0-rc1 ends at 299
// v299 -> v300
NewMigration("Add metadata column for comment table", v1_23.AddCommentMetaDataColumn),
}
// GetCurrentDBVersion returns the current db version

View File

@ -0,0 +1,22 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_23 //nolint
import (
"xorm.io/xorm"
)
// CommentMetaData stores metadata for a comment, these data will not be changed once inserted into database
type CommentMetaData struct {
ProjectColumnID int64 `json:"project_column_id"`
ProjectColumnTitle string `json:"project_column_title"`
ProjectTitle string `json:"project_title"`
}
func AddCommentMetaDataColumn(x *xorm.Engine) error {
type Comment struct {
CommentMetaData *CommentMetaData `xorm:"JSON TEXT"` // put all non-index metadata in a single field
}
return x.Sync(new(Comment))
}

View File

@ -5,7 +5,6 @@ package project
import (
"context"
"fmt"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
@ -75,33 +74,6 @@ func (p *Project) NumOpenIssues(ctx context.Context) int {
return int(c)
}
// MoveIssuesOnProjectBoard moves or keeps issues in a column and sorts them inside that column
func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs map[int64]int64) error {
return db.WithTx(ctx, func(ctx context.Context) error {
sess := db.GetEngine(ctx)
issueIDs := make([]int64, 0, len(sortedIssueIDs))
for _, issueID := range sortedIssueIDs {
issueIDs = append(issueIDs, issueID)
}
count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", board.ProjectID).In("issue_id", issueIDs).Count()
if err != nil {
return err
}
if int(count) != len(sortedIssueIDs) {
return fmt.Errorf("all issues have to be added to a project first")
}
for sorting, issueID := range sortedIssueIDs {
_, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", board.ID, sorting, issueID)
if err != nil {
return err
}
}
return nil
})
}
func (b *Board) removeIssues(ctx context.Context) error {
_, err := db.GetEngine(ctx).Exec("UPDATE `project_issue` SET project_board_id = 0 WHERE project_board_id = ? ", b.ID)
return err

View File

@ -1469,6 +1469,7 @@ issues.remove_labels = removed the %s labels %s
issues.add_remove_labels = added %s and removed %s labels %s
issues.add_milestone_at = `added this to the <b>%s</b> milestone %s`
issues.add_project_at = `added this to the <b>%s</b> project %s`
issues.move_to_column_of_project = `moved this to %s in %s on %s`
issues.change_milestone_at = `modified the milestone from <b>%s</b> to <b>%s</b> %s`
issues.change_project_at = `modified the project from <b>%s</b> to <b>%s</b> %s`
issues.remove_milestone_at = `removed this from the <b>%s</b> milestone %s`

View File

@ -24,6 +24,7 @@ import (
shared_user "code.gitea.io/gitea/routers/web/shared/user"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
project_service "code.gitea.io/gitea/services/projects"
)
const (
@ -640,6 +641,7 @@ func MoveIssues(ctx *context.Context) {
form := &movedIssuesForm{}
if err = json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
ctx.ServerError("DecodeMovedIssuesForm", err)
return
}
issueIDs := make([]int64, 0, len(form.Issues))
@ -671,7 +673,7 @@ func MoveIssues(ctx *context.Context) {
}
}
if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil {
if err = project_service.MoveIssuesOnProjectBoard(ctx, ctx.Doer, board, sortedIssueIDs); err != nil {
ctx.ServerError("MoveIssuesOnProjectBoard", err)
return
}

View File

@ -1680,6 +1680,11 @@ func ViewIssue(ctx *context.Context) {
if comment.ProjectID > 0 && comment.Project == nil {
comment.Project = ghostProject
}
} else if comment.Type == issues_model.CommentTypeProjectBoard {
if err = comment.LoadProject(ctx); err != nil {
ctx.ServerError("LoadProject", err)
return
}
} else if comment.Type == issues_model.CommentTypeAssignees || comment.Type == issues_model.CommentTypeReviewRequest {
if err = comment.LoadAssigneeUserAndTeam(ctx); err != nil {
ctx.ServerError("LoadAssigneeUserAndTeam", err)

View File

@ -24,6 +24,7 @@ import (
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
project_service "code.gitea.io/gitea/services/projects"
)
const (
@ -659,7 +660,7 @@ func MoveIssues(ctx *context.Context) {
}
}
if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil {
if err = project_service.MoveIssuesOnProjectBoard(ctx, ctx.Doer, board, sortedIssueIDs); err != nil {
ctx.ServerError("MoveIssuesOnProjectBoard", err)
return
}

View File

@ -0,0 +1,77 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package project
import (
"context"
"fmt"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
project_model "code.gitea.io/gitea/models/project"
user_model "code.gitea.io/gitea/models/user"
)
// MoveIssuesOnProjectBoard moves or keeps issues in a column and sorts them inside that column
func MoveIssuesOnProjectBoard(ctx context.Context, doer *user_model.User, column *project_model.Board, sortedIssueIDs map[int64]int64) error {
return db.WithTx(ctx, func(ctx context.Context) error {
issueIDs := make([]int64, 0, len(sortedIssueIDs))
for _, issueID := range sortedIssueIDs {
issueIDs = append(issueIDs, issueID)
}
count, err := db.GetEngine(ctx).
Where("project_id=?", column.ProjectID).
In("issue_id", issueIDs).
Count(new(project_model.ProjectIssue))
if err != nil {
return err
}
if int(count) != len(sortedIssueIDs) {
return fmt.Errorf("all issues have to be added to a project first")
}
issues, err := issues_model.GetIssuesByIDs(ctx, issueIDs)
if err != nil {
return err
}
if _, err := issues.LoadRepositories(ctx); err != nil {
return err
}
project, err := project_model.GetProjectByID(ctx, column.ProjectID)
if err != nil {
return err
}
for sorting, issueID := range sortedIssueIDs {
_, err = db.Exec(ctx, "UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID)
if err != nil {
return err
}
var curIssue *issues_model.Issue
for _, issue := range issues {
if issue.ID == issueID {
curIssue = issue
break
}
}
// add timeline to issue
if _, err := issues_model.CreateComment(ctx, &issues_model.CreateCommentOptions{
Type: issues_model.CommentTypeProjectBoard,
Doer: doer,
Repo: curIssue.Repo,
Issue: curIssue,
ProjectID: column.ProjectID,
ProjectTitle: project.Title,
ProjectColumnID: column.ID,
ProjectColumnTitle: column.Title,
}); err != nil {
return err
}
}
return nil
})
}

View File

@ -600,6 +600,22 @@
</span>
</div>
{{end}}
{{else if eq .Type 31}}
{{if not $.UnitProjectsGlobalDisabled}}
<div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-project"}}</span>
{{template "shared/user/avatarlink" dict "user" .Poster}}
<span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}}
{{$newProjectDisplay := .CommentMetaData.ProjectTitle}}
{{if .Project}}
{{$trKey := printf "projects.type-%d.display_name" .Project.Type}}
{{$newProjectDisplay = HTMLFormat `%s <a href="%s"><span data-tooltip-content="%s">%s</span></a>` (svg .Project.IconName) (.Project.Link ctx) (ctx.Locale.Tr $trKey) .Project.Title}}
{{end}}
{{ctx.Locale.Tr "repo.issues.move_to_column_of_project" .CommentMetaData.ProjectColumnTitle $newProjectDisplay $createdStr}}
</span>
</div>
{{end}}
{{else if eq .Type 32}}
<div class="timeline-item-group">
<div class="timeline-item event" id="{{.HashTag}}">