@@ -6,6 +6,7 @@ package issues
66import (
77 "context"
88 "fmt"
9+ "strconv"
910 "strings"
1011
1112 "code.gitea.io/gitea/models/db"
@@ -33,26 +34,27 @@ func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
3334 return nil
3435}
3536
36- func changeIssueStatus (ctx context.Context , issue * Issue , doer * user_model.User , isClosed , isMergePull bool ) (* Comment , error ) {
37+ func changeIssueStatus (ctx context.Context , issue * Issue , doer * user_model.User , isMergePull bool ) (* Comment , error ) {
3738 // Reload the issue
3839 currentIssue , err := GetIssueByID (ctx , issue .ID )
3940 if err != nil {
4041 return nil , err
4142 }
4243
4344 // Nothing should be performed if current status is same as target status
44- if currentIssue .IsClosed == isClosed {
45- if ! issue .IsPull {
46- return nil , ErrIssueWasClosed {
45+ if currentIssue .IsClosed == issue . IsClosed {
46+ if issue .IsPull {
47+ return nil , ErrPullWasClosed {
4748 ID : issue .ID ,
4849 }
4950 }
50- return nil , ErrPullWasClosed {
51- ID : issue .ID ,
51+ if currentIssue .ClosedStatus == issue .ClosedStatus {
52+ return nil , ErrIssueWasClosed {
53+ ID : issue .ID ,
54+ }
5255 }
5356 }
5457
55- issue .IsClosed = isClosed
5658 return doChangeIssueStatus (ctx , issue , doer , isMergePull )
5759}
5860
@@ -76,7 +78,7 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
7678 issue .ClosedUnix = 0
7779 }
7880
79- if err := UpdateIssueCols (ctx , issue , "is_closed" , "closed_unix" ); err != nil {
81+ if err := UpdateIssueCols (ctx , issue , "is_closed" , "closed_status" , " closed_unix" ); err != nil {
8082 return nil , err
8183 }
8284
@@ -104,30 +106,36 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
104106
105107 // New action comment
106108 cmtType := CommentTypeClose
109+ var content string
110+ if ! issue .IsPull && issue .IsClosed {
111+ content = strconv .Itoa (int (issue .ClosedStatus ))
112+ }
113+
107114 if ! issue .IsClosed {
108115 cmtType = CommentTypeReopen
109116 } else if isMergePull {
110117 cmtType = CommentTypeMergePull
111118 }
112119
113120 return CreateComment (ctx , & CreateCommentOptions {
114- Type : cmtType ,
115- Doer : doer ,
116- Repo : issue .Repo ,
117- Issue : issue ,
121+ Type : cmtType ,
122+ Doer : doer ,
123+ Repo : issue .Repo ,
124+ Issue : issue ,
125+ Content : content ,
118126 })
119127}
120128
121129// ChangeIssueStatus changes issue status to open or closed.
122- func ChangeIssueStatus (ctx context.Context , issue * Issue , doer * user_model.User , isClosed bool ) (* Comment , error ) {
130+ func ChangeIssueStatus (ctx context.Context , issue * Issue , doer * user_model.User ) (* Comment , error ) {
123131 if err := issue .LoadRepo (ctx ); err != nil {
124132 return nil , err
125133 }
126134 if err := issue .LoadPoster (ctx ); err != nil {
127135 return nil , err
128136 }
129137
130- return changeIssueStatus (ctx , issue , doer , isClosed , false )
138+ return changeIssueStatus (ctx , issue , doer , false )
131139}
132140
133141// ChangeIssueTitle changes the title of this issue, as the given user.
@@ -434,6 +442,71 @@ func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*user_mo
434442 return nil
435443}
436444
445+ // UpdateIssueByAPI updates all allowed fields of given issue.
446+ // If the issue status is changed a statusChangeComment is returned
447+ // similarly if the title is changed the titleChanged bool is set to true
448+ func UpdateIssueByAPI (issue * Issue , doer * user_model.User ) (statusChangeComment * Comment , titleChanged bool , err error ) {
449+ ctx , committer , err := db .TxContext (db .DefaultContext )
450+ if err != nil {
451+ return nil , false , err
452+ }
453+ defer committer .Close ()
454+
455+ if err := issue .LoadRepo (ctx ); err != nil {
456+ return nil , false , fmt .Errorf ("loadRepo: %w" , err )
457+ }
458+
459+ // Reload the issue
460+ currentIssue , err := GetIssueByID (ctx , issue .ID )
461+ if err != nil {
462+ return nil , false , err
463+ }
464+
465+ if _ , err := db .GetEngine (ctx ).ID (issue .ID ).Cols (
466+ "name" , "content" , "milestone_id" , "priority" ,
467+ "deadline_unix" , "updated_unix" , "is_locked" ).
468+ Update (issue ); err != nil {
469+ return nil , false , err
470+ }
471+
472+ titleChanged = currentIssue .Title != issue .Title
473+ if titleChanged {
474+ opts := & CreateCommentOptions {
475+ Type : CommentTypeChangeTitle ,
476+ Doer : doer ,
477+ Repo : issue .Repo ,
478+ Issue : issue ,
479+ OldTitle : currentIssue .Title ,
480+ NewTitle : issue .Title ,
481+ }
482+ _ , err := CreateComment (ctx , opts )
483+ if err != nil {
484+ return nil , false , fmt .Errorf ("createComment: %w" , err )
485+ }
486+ }
487+
488+ if issue .IsPull {
489+ if currentIssue .IsClosed != issue .IsClosed {
490+ statusChangeComment , err = doChangeIssueStatus (ctx , issue , doer , false )
491+ if err != nil {
492+ return nil , false , err
493+ }
494+ }
495+ } else {
496+ if currentIssue .IsClosed != issue .IsClosed || currentIssue .ClosedStatus != issue .ClosedStatus {
497+ statusChangeComment , err = doChangeIssueStatus (ctx , issue , doer , false )
498+ if err != nil {
499+ return nil , false , err
500+ }
501+ }
502+ }
503+
504+ if err := issue .AddCrossReferences (ctx , doer , true ); err != nil {
505+ return nil , false , err
506+ }
507+ return statusChangeComment , titleChanged , committer .Commit ()
508+ }
509+
437510// UpdateIssueDeadline updates an issue deadline and adds comments. Setting a deadline to 0 means deleting it.
438511func UpdateIssueDeadline (ctx context.Context , issue * Issue , deadlineUnix timeutil.TimeStamp , doer * user_model.User ) (err error ) {
439512 // if the deadline hasn't changed do nothing
0 commit comments