Prevent simultaneous editing of comments and issues (#31053)
fixes #22907 Tested: - [x] issue content edit - [x] issue content change tasklist - [x] pull request content edit - [x] pull request change tasklist  (cherry picked from commit aa92b13164e84c26be91153b6022220ce0a27720) Conflicts: models/issues/comment.goc7a389f2b2
[FEAT] allow setting the update date on issues and comments options/locale/locale_en-US.ini trivial context conflicts routers/api/v1/repo/issue_comment.go routers/api/v1/repo/issue_comment_attachment.go services/issue/comments.go services/issue/content.go user blocking is implemented differently in Forgejo routers/web/repo/issue.go trivial difference from 6a0750177f Allow to save empty comment user blocking is implemented differently in Forgejo templates/repo/issue/view_content/conversation.tmpl templates changed a lot in Forgejo but the change is trivially ported tests/integration/issue_test.go other tests were added in the same region web_src/js/features/repo-issue-edit.js the code is still web_src/js/features/repo-legacy.js trivially ported
This commit is contained in:
parent
73706ae26d
commit
ca0921a95a
21 changed files with 190 additions and 36 deletions
|
@ -52,6 +52,8 @@ func (err ErrCommentNotExist) Unwrap() error {
|
|||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
var ErrCommentAlreadyChanged = util.NewInvalidArgumentErrorf("the comment is already changed")
|
||||
|
||||
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
|
||||
type CommentType int
|
||||
|
||||
|
@ -262,6 +264,7 @@ type Comment struct {
|
|||
Line int64 // - previous line / + proposed line
|
||||
TreePath string
|
||||
Content string `xorm:"LONGTEXT"`
|
||||
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
|
||||
RenderedContent template.HTML `xorm:"-"`
|
||||
|
||||
// Path represents the 4 lines of code cemented by this comment
|
||||
|
@ -1119,7 +1122,7 @@ func UpdateCommentInvalidate(ctx context.Context, c *Comment) error {
|
|||
}
|
||||
|
||||
// UpdateComment updates information of comment.
|
||||
func UpdateComment(ctx context.Context, c *Comment, doer *user_model.User) error {
|
||||
func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *user_model.User) error {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1139,9 +1142,15 @@ func UpdateComment(ctx context.Context, c *Comment, doer *user_model.User) error
|
|||
// see https://codeberg.org/forgejo/forgejo/pulls/764#issuecomment-1023801
|
||||
c.UpdatedUnix = c.Issue.UpdatedUnix
|
||||
}
|
||||
if _, err := sess.Update(c); err != nil {
|
||||
c.ContentVersion = contentVersion + 1
|
||||
|
||||
affected, err := sess.Where("content_version = ?", contentVersion).Update(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return ErrCommentAlreadyChanged
|
||||
}
|
||||
if err := c.AddCrossReferences(ctx, doer, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -94,6 +94,8 @@ func (err ErrIssueWasClosed) Error() string {
|
|||
return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index)
|
||||
}
|
||||
|
||||
var ErrIssueAlreadyChanged = util.NewInvalidArgumentErrorf("the issue is already changed")
|
||||
|
||||
// Issue represents an issue or pull request of repository.
|
||||
type Issue struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
@ -107,6 +109,7 @@ type Issue struct {
|
|||
Title string `xorm:"name"`
|
||||
Content string `xorm:"LONGTEXT"`
|
||||
RenderedContent template.HTML `xorm:"-"`
|
||||
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
|
||||
Labels []*Label `xorm:"-"`
|
||||
MilestoneID int64 `xorm:"INDEX"`
|
||||
Milestone *Milestone `xorm:"-"`
|
||||
|
|
|
@ -25,17 +25,18 @@ import (
|
|||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// UpdateIssueCols updates cols of issue
|
||||
func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
|
||||
_, err := UpdateIssueColsWithCond(ctx, issue, builder.NewCond(), cols...)
|
||||
return err
|
||||
}
|
||||
|
||||
func UpdateIssueColsWithCond(ctx context.Context, issue *Issue, cond builder.Cond, cols ...string) (int64, error) {
|
||||
sess := db.GetEngine(ctx).ID(issue.ID)
|
||||
if issue.NoAutoTime {
|
||||
cols = append(cols, []string{"updated_unix"}...)
|
||||
sess.NoAutoTime()
|
||||
}
|
||||
if _, err := sess.Cols(cols...).Update(issue); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return sess.Cols(cols...).Where(cond).Update(issue)
|
||||
}
|
||||
|
||||
func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) {
|
||||
|
@ -250,7 +251,7 @@ func UpdateIssueAttachments(ctx context.Context, issueID int64, uuids []string)
|
|||
}
|
||||
|
||||
// ChangeIssueContent changes issue content, as the given user.
|
||||
func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string) (err error) {
|
||||
func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User, content string, contentVersion int) (err error) {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -269,10 +270,16 @@ func ChangeIssueContent(ctx context.Context, issue *Issue, doer *user_model.User
|
|||
}
|
||||
|
||||
issue.Content = content
|
||||
issue.ContentVersion = contentVersion + 1
|
||||
|
||||
if err = UpdateIssueCols(ctx, issue, "content"); err != nil {
|
||||
expectedContentVersion := builder.NewCond().And(builder.Eq{"content_version": contentVersion})
|
||||
affected, err := UpdateIssueColsWithCond(ctx, issue, expectedContentVersion, "content", "content_version")
|
||||
if err != nil {
|
||||
return fmt.Errorf("UpdateIssueCols: %w", err)
|
||||
}
|
||||
if affected == 0 {
|
||||
return ErrIssueAlreadyChanged
|
||||
}
|
||||
|
||||
historyDate := timeutil.TimeStampNow()
|
||||
if issue.NoAutoTime {
|
||||
|
|
|
@ -22,6 +22,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"
|
||||
|
@ -589,6 +590,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 content version to issue and comment table", v1_23.AddContentVersionToIssueAndComment),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
|
18
models/migrations/v1_23/v299.go
Normal file
18
models/migrations/v1_23/v299.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func AddContentVersionToIssueAndComment(x *xorm.Engine) error {
|
||||
type Issue struct {
|
||||
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
return x.Sync(new(Comment), new(Issue))
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue