mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Enhance Ghost comment mitigation Settings (#14392)
* refactor models.DeleteComment and delete related reactions too * use deleteComment for UserDeleteWithCommentsMaxDays in DeleteUser * nits * Use time.Duration as other time settings have * docs * Resolve Fixme & fix potential deadlock * Disabled by Default * Update Config Value Description * switch args * Update models/issue_comment.go Co-authored-by: zeripath <art27@cantab.net> Co-authored-by: zeripath <art27@cantab.net>
This commit is contained in:
		@@ -688,9 +688,8 @@ AUTO_WATCH_NEW_REPOS = true
 | 
				
			|||||||
; Default value for AutoWatchOnChanges
 | 
					; Default value for AutoWatchOnChanges
 | 
				
			||||||
; Make the user watch a repository When they commit for the first time
 | 
					; Make the user watch a repository When they commit for the first time
 | 
				
			||||||
AUTO_WATCH_ON_CHANGES = false
 | 
					AUTO_WATCH_ON_CHANGES = false
 | 
				
			||||||
; Default value for the minimum age a user has to exist before deletion to keep issue comments.
 | 
					; Minimum amount of time a user must exist before comments are kept when the user is deleted.
 | 
				
			||||||
; If a user deletes his account before that amount of days, his comments will be deleted as well.
 | 
					USER_DELETE_WITH_COMMENTS_MAX_TIME = 0
 | 
				
			||||||
USER_DELETE_WITH_COMMENTS_MAX_DAYS = 0
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
[webhook]
 | 
					[webhook]
 | 
				
			||||||
; Hook task queue length, increase if webhook shooting starts hanging
 | 
					; Hook task queue length, increase if webhook shooting starts hanging
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -474,7 +474,7 @@ relation to port exhaustion.
 | 
				
			|||||||
- `ALLOW_ONLY_EXTERNAL_REGISTRATION`: **false** Set to true to force registration only using third-party services.
 | 
					- `ALLOW_ONLY_EXTERNAL_REGISTRATION`: **false** Set to true to force registration only using third-party services.
 | 
				
			||||||
- `NO_REPLY_ADDRESS`: **DOMAIN** Default value for the domain part of the user's email address in the git log if he has set KeepEmailPrivate to true.
 | 
					- `NO_REPLY_ADDRESS`: **DOMAIN** Default value for the domain part of the user's email address in the git log if he has set KeepEmailPrivate to true.
 | 
				
			||||||
  The user's email will be replaced with a concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
 | 
					  The user's email will be replaced with a concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
 | 
				
			||||||
- `USER_DELETE_WITH_COMMENTS_MAX_DAYS`: **0** If a user deletes his account before that amount of days, his comments will be deleted as well.
 | 
					- `USER_DELETE_WITH_COMMENTS_MAX_TIME`: **0** Minimum amount of time a user must exist before comments are kept when the user is deleted.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## SSH Minimum Key Sizes (`ssh.minimum_key_sizes`)
 | 
					## SSH Minimum Key Sizes (`ssh.minimum_key_sizes`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,7 +75,7 @@ func removeStorageWithNotice(e Engine, bucket storage.ObjectStorage, title, path
 | 
				
			|||||||
	if err := bucket.Delete(path); err != nil {
 | 
						if err := bucket.Delete(path); err != nil {
 | 
				
			||||||
		desc := fmt.Sprintf("%s [%s]: %v", title, path, err)
 | 
							desc := fmt.Sprintf("%s [%s]: %v", title, path, err)
 | 
				
			||||||
		log.Warn(title+" [%s]: %v", path, err)
 | 
							log.Warn(title+" [%s]: %v", path, err)
 | 
				
			||||||
		if err = createNotice(x, NoticeRepository, desc); err != nil {
 | 
							if err = createNotice(e, NoticeRepository, desc); err != nil {
 | 
				
			||||||
			log.Error("CreateRepositoryNotice: %v", err)
 | 
								log.Error("CreateRepositoryNotice: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -82,7 +82,7 @@ func isUserAssignedToIssue(e Engine, issue *Issue, user *User) (isAssigned bool,
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ClearAssigneeByUserID deletes all assignments of an user
 | 
					// ClearAssigneeByUserID deletes all assignments of an user
 | 
				
			||||||
func clearAssigneeByUserID(sess *xorm.Session, userID int64) (err error) {
 | 
					func clearAssigneeByUserID(sess Engine, userID int64) (err error) {
 | 
				
			||||||
	_, err = sess.Delete(&IssueAssignees{AssigneeID: userID})
 | 
						_, err = sess.Delete(&IssueAssignees{AssigneeID: userID})
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1037,33 +1037,41 @@ func UpdateComment(c *Comment, doer *User) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteComment deletes the comment
 | 
					// DeleteComment deletes the comment
 | 
				
			||||||
func DeleteComment(comment *Comment, doer *User) error {
 | 
					func DeleteComment(comment *Comment) error {
 | 
				
			||||||
	sess := x.NewSession()
 | 
						sess := x.NewSession()
 | 
				
			||||||
	defer sess.Close()
 | 
						defer sess.Close()
 | 
				
			||||||
	if err := sess.Begin(); err != nil {
 | 
						if err := sess.Begin(); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, err := sess.Delete(&Comment{
 | 
						if err := deleteComment(sess, comment); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return sess.Commit()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func deleteComment(e Engine, comment *Comment) error {
 | 
				
			||||||
 | 
						if _, err := e.Delete(&Comment{
 | 
				
			||||||
		ID: comment.ID,
 | 
							ID: comment.ID,
 | 
				
			||||||
	}); err != nil {
 | 
						}); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if comment.Type == CommentTypeComment {
 | 
						if comment.Type == CommentTypeComment {
 | 
				
			||||||
		if _, err := sess.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
 | 
							if _, err := e.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if _, err := sess.Where("comment_id = ?", comment.ID).Cols("is_deleted").Update(&Action{IsDeleted: true}); err != nil {
 | 
						if _, err := e.Where("comment_id = ?", comment.ID).Cols("is_deleted").Update(&Action{IsDeleted: true}); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := comment.neuterCrossReferences(sess); err != nil {
 | 
						if err := comment.neuterCrossReferences(e); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return sess.Commit()
 | 
						return deleteReaction(e, &ReactionOptions{Comment: comment})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS
 | 
					// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -178,11 +178,15 @@ func CreateCommentReaction(doer *User, issue *Issue, comment *Comment, content s
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func deleteReaction(e *xorm.Session, opts *ReactionOptions) error {
 | 
					func deleteReaction(e Engine, opts *ReactionOptions) error {
 | 
				
			||||||
	reaction := &Reaction{
 | 
						reaction := &Reaction{
 | 
				
			||||||
		Type:    opts.Type,
 | 
							Type: opts.Type,
 | 
				
			||||||
		UserID:  opts.Doer.ID,
 | 
						}
 | 
				
			||||||
		IssueID: opts.Issue.ID,
 | 
						if opts.Doer != nil {
 | 
				
			||||||
 | 
							reaction.UserID = opts.Doer.ID
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opts.Issue != nil {
 | 
				
			||||||
 | 
							reaction.IssueID = opts.Issue.ID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if opts.Comment != nil {
 | 
						if opts.Comment != nil {
 | 
				
			||||||
		reaction.CommentID = opts.Comment.ID
 | 
							reaction.CommentID = opts.Comment.ID
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,6 @@ import (
 | 
				
			|||||||
	"golang.org/x/crypto/scrypt"
 | 
						"golang.org/x/crypto/scrypt"
 | 
				
			||||||
	"golang.org/x/crypto/ssh"
 | 
						"golang.org/x/crypto/ssh"
 | 
				
			||||||
	"xorm.io/builder"
 | 
						"xorm.io/builder"
 | 
				
			||||||
	"xorm.io/xorm"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UserType defines the user type
 | 
					// UserType defines the user type
 | 
				
			||||||
@@ -1071,8 +1070,7 @@ func deleteBeans(e Engine, beans ...interface{}) (err error) {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FIXME: need some kind of mechanism to record failure. HINT: system notice
 | 
					func deleteUser(e Engine, u *User) error {
 | 
				
			||||||
func deleteUser(e *xorm.Session, u *User) error {
 | 
					 | 
				
			||||||
	// Note: A user owns any repository or belongs to any organization
 | 
						// Note: A user owns any repository or belongs to any organization
 | 
				
			||||||
	//	cannot perform delete operation.
 | 
						//	cannot perform delete operation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1151,12 +1149,30 @@ func deleteUser(e *xorm.Session, u *User) error {
 | 
				
			|||||||
		return fmt.Errorf("deleteBeans: %v", err)
 | 
							return fmt.Errorf("deleteBeans: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if setting.Service.UserDeleteWithCommentsMaxDays != 0 &&
 | 
						if setting.Service.UserDeleteWithCommentsMaxTime != 0 &&
 | 
				
			||||||
		u.CreatedUnix.AsTime().Add(time.Duration(setting.Service.UserDeleteWithCommentsMaxDays)*24*time.Hour).After(time.Now()) {
 | 
							u.CreatedUnix.AsTime().Add(setting.Service.UserDeleteWithCommentsMaxTime).After(time.Now()) {
 | 
				
			||||||
		if err = deleteBeans(e,
 | 
					
 | 
				
			||||||
			&Comment{PosterID: u.ID},
 | 
							// Delete Comments
 | 
				
			||||||
		); err != nil {
 | 
							const batchSize = 50
 | 
				
			||||||
			return fmt.Errorf("deleteBeans: %v", err)
 | 
							for start := 0; ; start += batchSize {
 | 
				
			||||||
 | 
								comments := make([]*Comment, 0, batchSize)
 | 
				
			||||||
 | 
								if err = e.Where("type=? AND poster_id=?", CommentTypeComment, u.ID).Limit(batchSize, start).Find(&comments); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if len(comments) == 0 {
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for _, comment := range comments {
 | 
				
			||||||
 | 
									if err = deleteComment(e, comment); err != nil {
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Delete Reactions
 | 
				
			||||||
 | 
							if err = deleteReaction(e, &ReactionOptions{Doer: u}); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1195,18 +1211,21 @@ func deleteUser(e *xorm.Session, u *User) error {
 | 
				
			|||||||
		return fmt.Errorf("Delete: %v", err)
 | 
							return fmt.Errorf("Delete: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// FIXME: system notice
 | 
					 | 
				
			||||||
	// Note: There are something just cannot be roll back,
 | 
						// Note: There are something just cannot be roll back,
 | 
				
			||||||
	//	so just keep error logs of those operations.
 | 
						//	so just keep error logs of those operations.
 | 
				
			||||||
	path := UserPath(u.Name)
 | 
						path := UserPath(u.Name)
 | 
				
			||||||
	if err := util.RemoveAll(path); err != nil {
 | 
						if err = util.RemoveAll(path); err != nil {
 | 
				
			||||||
		return fmt.Errorf("Failed to RemoveAll %s: %v", path, err)
 | 
							err = fmt.Errorf("Failed to RemoveAll %s: %v", path, err)
 | 
				
			||||||
 | 
							_ = createNotice(e, NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(u.Avatar) > 0 {
 | 
						if len(u.Avatar) > 0 {
 | 
				
			||||||
		avatarPath := u.CustomAvatarRelativePath()
 | 
							avatarPath := u.CustomAvatarRelativePath()
 | 
				
			||||||
		if err := storage.Avatars.Delete(avatarPath); err != nil {
 | 
							if err = storage.Avatars.Delete(avatarPath); err != nil {
 | 
				
			||||||
			return fmt.Errorf("Failed to remove %s: %v", avatarPath, err)
 | 
								err = fmt.Errorf("Failed to remove %s: %v", avatarPath, err)
 | 
				
			||||||
 | 
								_ = createNotice(e, NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ package setting
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/structs"
 | 
						"code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -50,7 +51,7 @@ var Service struct {
 | 
				
			|||||||
	AutoWatchNewRepos                       bool
 | 
						AutoWatchNewRepos                       bool
 | 
				
			||||||
	AutoWatchOnChanges                      bool
 | 
						AutoWatchOnChanges                      bool
 | 
				
			||||||
	DefaultOrgMemberVisible                 bool
 | 
						DefaultOrgMemberVisible                 bool
 | 
				
			||||||
	UserDeleteWithCommentsMaxDays           int
 | 
						UserDeleteWithCommentsMaxTime           time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// OpenID settings
 | 
						// OpenID settings
 | 
				
			||||||
	EnableOpenIDSignIn bool
 | 
						EnableOpenIDSignIn bool
 | 
				
			||||||
@@ -103,7 +104,7 @@ func newService() {
 | 
				
			|||||||
	Service.DefaultOrgVisibility = sec.Key("DEFAULT_ORG_VISIBILITY").In("public", structs.ExtractKeysFromMapString(structs.VisibilityModes))
 | 
						Service.DefaultOrgVisibility = sec.Key("DEFAULT_ORG_VISIBILITY").In("public", structs.ExtractKeysFromMapString(structs.VisibilityModes))
 | 
				
			||||||
	Service.DefaultOrgVisibilityMode = structs.VisibilityModes[Service.DefaultOrgVisibility]
 | 
						Service.DefaultOrgVisibilityMode = structs.VisibilityModes[Service.DefaultOrgVisibility]
 | 
				
			||||||
	Service.DefaultOrgMemberVisible = sec.Key("DEFAULT_ORG_MEMBER_VISIBLE").MustBool()
 | 
						Service.DefaultOrgMemberVisible = sec.Key("DEFAULT_ORG_MEMBER_VISIBLE").MustBool()
 | 
				
			||||||
	Service.UserDeleteWithCommentsMaxDays = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_DAYS").MustInt(0)
 | 
						Service.UserDeleteWithCommentsMaxTime = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_TIME").MustDuration(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sec = Cfg.Section("openid")
 | 
						sec = Cfg.Section("openid")
 | 
				
			||||||
	Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock)
 | 
						Service.EnableOpenIDSignIn = sec.Key("ENABLE_OPENID_SIGNIN").MustBool(!InstallLock)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -648,7 +648,7 @@ repos_none = You do not own any repositories
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
delete_account = Delete Your Account
 | 
					delete_account = Delete Your Account
 | 
				
			||||||
delete_prompt = This operation will permanently delete your user account. It <strong>CAN NOT</strong> be undone.
 | 
					delete_prompt = This operation will permanently delete your user account. It <strong>CAN NOT</strong> be undone.
 | 
				
			||||||
delete_with_all_comments = Your account is younger than %d days. To avoid ghost comments, all issue/PR comments will be deleted with it.
 | 
					delete_with_all_comments = Your account is younger than %s. To avoid ghost comments, all issue/PR comments will be deleted with it.
 | 
				
			||||||
confirm_delete_account = Confirm Deletion
 | 
					confirm_delete_account = Confirm Deletion
 | 
				
			||||||
delete_account_title = Delete User Account
 | 
					delete_account_title = Delete User Account
 | 
				
			||||||
delete_account_desc = Are you sure you want to permanently delete this user account?
 | 
					delete_account_desc = Are you sure you want to permanently delete this user account?
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -509,7 +509,7 @@ func deleteIssueComment(ctx *context.APIContext) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = comment_service.DeleteComment(comment, ctx.User); err != nil {
 | 
						if err = comment_service.DeleteComment(ctx.User, comment); err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, "DeleteCommentByID", err)
 | 
							ctx.Error(http.StatusInternalServerError, "DeleteCommentByID", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2125,7 +2125,7 @@ func DeleteComment(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err = comment_service.DeleteComment(comment, ctx.User); err != nil {
 | 
						if err = comment_service.DeleteComment(ctx.User, comment); err != nil {
 | 
				
			||||||
		ctx.ServerError("DeleteCommentByID", err)
 | 
							ctx.ServerError("DeleteCommentByID", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -302,8 +302,8 @@ func loadAccountData(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["ActivationsPending"] = pendingActivation
 | 
						ctx.Data["ActivationsPending"] = pendingActivation
 | 
				
			||||||
	ctx.Data["CanAddEmails"] = !pendingActivation || !setting.Service.RegisterEmailConfirm
 | 
						ctx.Data["CanAddEmails"] = !pendingActivation || !setting.Service.RegisterEmailConfirm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if setting.Service.UserDeleteWithCommentsMaxDays != 0 {
 | 
						if setting.Service.UserDeleteWithCommentsMaxTime != 0 {
 | 
				
			||||||
		ctx.Data["UserDeleteWithCommentsMaxDays"] = setting.Service.UserDeleteWithCommentsMaxDays
 | 
							ctx.Data["UserDeleteWithCommentsMaxTime"] = setting.Service.UserDeleteWithCommentsMaxTime.String()
 | 
				
			||||||
		ctx.Data["UserDeleteWithComments"] = ctx.User.CreatedUnix.AsTime().Add(time.Duration(setting.Service.UserDeleteWithCommentsMaxDays) * 24 * time.Hour).After(time.Now())
 | 
							ctx.Data["UserDeleteWithComments"] = ctx.User.CreatedUnix.AsTime().Add(setting.Service.UserDeleteWithCommentsMaxTime).After(time.Now())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,8 +43,8 @@ func UpdateComment(c *models.Comment, doer *models.User, oldContent string) erro
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DeleteComment deletes the comment
 | 
					// DeleteComment deletes the comment
 | 
				
			||||||
func DeleteComment(comment *models.Comment, doer *models.User) error {
 | 
					func DeleteComment(doer *models.User, comment *models.Comment) error {
 | 
				
			||||||
	if err := models.DeleteComment(comment, doer); err != nil {
 | 
						if err := models.DeleteComment(comment); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -174,7 +174,7 @@
 | 
				
			|||||||
			<div class="ui red message">
 | 
								<div class="ui red message">
 | 
				
			||||||
				<p class="text left">{{svg "octicon-alert"}} {{.i18n.Tr "settings.delete_prompt" | Str2html}}</p>
 | 
									<p class="text left">{{svg "octicon-alert"}} {{.i18n.Tr "settings.delete_prompt" | Str2html}}</p>
 | 
				
			||||||
				{{ if .UserDeleteWithComments }}
 | 
									{{ if .UserDeleteWithComments }}
 | 
				
			||||||
				<p class="text left" style="font-weight: bold;">{{.i18n.Tr "settings.delete_with_all_comments" .UserDeleteWithCommentsMaxDays | Str2html}}</p>
 | 
									<p class="text left" style="font-weight: bold;">{{.i18n.Tr "settings.delete_with_all_comments" .UserDeleteWithCommentsMaxTime | Str2html}}</p>
 | 
				
			||||||
				{{ end }}
 | 
									{{ end }}
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
			<form class="ui form ignore-dirty" id="delete-form" action="{{AppSubUrl}}/user/settings/account/delete" method="post">
 | 
								<form class="ui form ignore-dirty" id="delete-form" action="{{AppSubUrl}}/user/settings/account/delete" method="post">
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user