fix: 管理删评对公众剥离作者身份,评论者本人仍可见原文
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -306,7 +306,7 @@ type PostAttachmentUnlock struct {
|
||||
type Comment struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
PostID uint `gorm:"index;not null" json:"post_id"`
|
||||
UserID uint `gorm:"index;not null" json:"user_id"`
|
||||
UserID uint `gorm:"index;not null" json:"user_id,omitempty"`
|
||||
ParentID *uint `gorm:"index" json:"parent_id"` // 父评论 ID,NULL = 主评论(楼层)
|
||||
RootID *uint `gorm:"index" json:"root_id"` // 所属楼层(顶层主评论)ID,子评论必填
|
||||
Depth int `gorm:"not null;default:0" json:"depth"` // 层级:主评论 0,子评论 = 父 + 1
|
||||
|
||||
@@ -32,11 +32,13 @@ func NewCommentService(db *gorm.DB) *CommentService {
|
||||
// CommentNode 评论树节点:嵌入 model.Comment 使 JSON 字段拍平,replies 为空时省略
|
||||
type CommentNode struct {
|
||||
model.Comment
|
||||
Deleted bool `json:"deleted,omitempty"` // 软删占位
|
||||
StaffDeleted bool `json:"staff_deleted,omitempty"` // DeletedBy≠评论者
|
||||
Edited bool `json:"edited,omitempty"` // 相对创建已编辑
|
||||
Deleted bool `json:"deleted,omitempty"` // 软删占位
|
||||
StaffDeleted bool `json:"staff_deleted,omitempty"` // DeletedBy≠评论者
|
||||
Edited bool `json:"edited,omitempty"` // 相对创建已编辑
|
||||
DeletedByUser *CommentActorBrief `json:"deleted_by_user,omitempty"` // 管理删执行者(公开)
|
||||
Replies []CommentNode `json:"replies,omitempty"`
|
||||
// 覆盖嵌入的 User:指针 + omitempty,管理删对公众可整段省略(避免空对象仍进 JSON/DOM)
|
||||
User *model.User `json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// CommentActorBrief 评论相关公开用户摘要(删评管理员链接等)
|
||||
@@ -119,14 +121,14 @@ func (s *CommentService) ListFloorPaged(postID, boardID, viewerID uint, actor *A
|
||||
build = func(pid uint) []CommentNode {
|
||||
out := []CommentNode{}
|
||||
for _, c := range children[pid] {
|
||||
out = append(out, toCommentNode(c, build(c.ID), revealAudit, deleters))
|
||||
out = append(out, toCommentNode(c, build(c.ID), revealAudit, viewerID, deleters))
|
||||
placed[c.ID] = true
|
||||
}
|
||||
return out
|
||||
}
|
||||
result := make([]CommentNode, len(roots))
|
||||
for i := range roots {
|
||||
result[i] = toCommentNode(roots[i], build(roots[i].ID), revealAudit, deleters)
|
||||
result[i] = toCommentNode(roots[i], build(roots[i].ID), revealAudit, viewerID, deleters)
|
||||
placed[roots[i].ID] = true
|
||||
}
|
||||
for _, r := range replies {
|
||||
@@ -135,7 +137,7 @@ func (s *CommentService) ListFloorPaged(postID, boardID, viewerID uint, actor *A
|
||||
}
|
||||
for i := range result {
|
||||
if result[i].ID == *r.RootID {
|
||||
result[i].Replies = append(result[i].Replies, toCommentNode(r, build(r.ID), revealAudit, deleters))
|
||||
result[i].Replies = append(result[i].Replies, toCommentNode(r, build(r.ID), revealAudit, viewerID, deleters))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -178,14 +180,25 @@ func (s *CommentService) loadCommentActorsByIDs(ids []uint) map[uint]CommentActo
|
||||
return out
|
||||
}
|
||||
|
||||
// toCommentNode 软删占位:清空公开正文;版主保留原文;自删理由仅版主可见。
|
||||
func toCommentNode(c model.Comment, replies []CommentNode, revealAudit bool, deleters map[uint]CommentActorBrief) CommentNode {
|
||||
// commentAuthorPtr 有预加载作者时返回拷贝,供 JSON omitempty 整段省略。
|
||||
func commentAuthorPtr(c model.Comment) *model.User {
|
||||
if c.User.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
u := c.User
|
||||
return &u
|
||||
}
|
||||
|
||||
// toCommentNode 软删占位:公众清空正文;版主与评论者本人保留原文。
|
||||
// 自删理由仅版主可见。管理删:公众与其他登录用户不返回评论者身份。
|
||||
func toCommentNode(c model.Comment, replies []CommentNode, revealAudit bool, viewerID uint, deleters map[uint]CommentActorBrief) CommentNode {
|
||||
edited := c.UpdatedAt.Sub(c.CreatedAt) > time.Minute
|
||||
if !c.DeletedAt.Valid {
|
||||
return CommentNode{Comment: c, Edited: edited, Replies: replies}
|
||||
return CommentNode{Comment: c, Edited: edited, Replies: replies, User: commentAuthorPtr(c)}
|
||||
}
|
||||
staff := c.DeletedBy != 0 && c.DeletedBy != c.UserID
|
||||
if !revealAudit {
|
||||
isCommenter := viewerID != 0 && viewerID == c.UserID
|
||||
if !revealAudit && !isCommenter {
|
||||
c.Content = ""
|
||||
}
|
||||
if !staff {
|
||||
@@ -201,6 +214,11 @@ func toCommentNode(c model.Comment, replies []CommentNode, revealAudit bool, del
|
||||
deleter = &cp
|
||||
}
|
||||
}
|
||||
// 版主审计、评论者本人仍可见身份;其余观众剥离
|
||||
if staff && !revealAudit && !isCommenter {
|
||||
c.UserID = 0
|
||||
c.User = model.User{}
|
||||
}
|
||||
return CommentNode{
|
||||
Comment: c,
|
||||
Deleted: true,
|
||||
@@ -208,6 +226,7 @@ func toCommentNode(c model.Comment, replies []CommentNode, revealAudit bool, del
|
||||
Edited: edited,
|
||||
DeletedByUser: deleter,
|
||||
Replies: replies,
|
||||
User: commentAuthorPtr(c),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,7 +375,7 @@ func (s *CommentService) ListByUser(userID uint, page, size int) ([]UserCommentI
|
||||
query := s.db.Table("comments").
|
||||
Select("comments.id, comments.post_id, posts.title AS post_title, comments.content, comments.created_at").
|
||||
Joins("JOIN posts ON posts.id = comments.post_id").
|
||||
Where("comments.user_id = ? AND comments.status = ? AND posts.deleted_at IS NULL", userID, model.ContentStatusPublished)
|
||||
Where("comments.user_id = ? AND comments.status = ? AND comments.deleted_at IS NULL AND posts.deleted_at IS NULL", userID, model.ContentStatusPublished)
|
||||
|
||||
var total int64
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
@@ -378,7 +397,7 @@ func (s *CommentService) CountByUser(userID uint) (int64, error) {
|
||||
var total int64
|
||||
err := s.db.Table("comments").
|
||||
Joins("JOIN posts ON posts.id = comments.post_id").
|
||||
Where("comments.user_id = ? AND comments.status = ? AND posts.deleted_at IS NULL", userID, model.ContentStatusPublished).
|
||||
Where("comments.user_id = ? AND comments.status = ? AND comments.deleted_at IS NULL AND posts.deleted_at IS NULL", userID, model.ContentStatusPublished).
|
||||
Count(&total).Error
|
||||
return total, err
|
||||
}
|
||||
@@ -603,16 +622,16 @@ func (s *CommentService) Update(actor *Actor, commentID, userID uint, content st
|
||||
if err := s.db.Preload("User").First(&comment, comment.ID).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
node := toCommentNode(comment, nil, false, nil)
|
||||
node := toCommentNode(comment, nil, false, userID, nil)
|
||||
return &node, nil
|
||||
}
|
||||
|
||||
// CommentEditHistoryItem 管理可见的评论修订记录
|
||||
type CommentEditHistoryItem struct {
|
||||
ID uint `json:"id"`
|
||||
Editor CommentActorBrief `json:"editor"`
|
||||
OldContent string `json:"old_content"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
ID uint `json:"id"`
|
||||
Editor CommentActorBrief `json:"editor"`
|
||||
OldContent string `json:"old_content"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// ListEditHistory 评论编辑历史(仅版主;含软删评论;按创建时间倒序分页)
|
||||
|
||||
125
backend/service/comment_node_test.go
Normal file
125
backend/service/comment_node_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/freefire/jiang13-bbs/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func staffDeletedComment() model.Comment {
|
||||
uid := uint(7)
|
||||
c := model.Comment{
|
||||
ID: 11,
|
||||
PostID: 22,
|
||||
UserID: uid,
|
||||
Content: "不该出现在公开 JSON",
|
||||
Status: model.ContentStatusPublished,
|
||||
DeletedBy: 9,
|
||||
User: model.User{
|
||||
ID: uid,
|
||||
Username: "alice",
|
||||
Nickname: "爱丽丝",
|
||||
Avatar: "/uploads/avatars/alice.jpg",
|
||||
},
|
||||
}
|
||||
c.DeletedAt = gorm.DeletedAt{Time: time.Now(), Valid: true}
|
||||
c.CreatedAt = time.Now().Add(-time.Hour)
|
||||
c.UpdatedAt = c.CreatedAt
|
||||
return c
|
||||
}
|
||||
|
||||
func marshalNode(t *testing.T, n CommentNode) map[string]any {
|
||||
t.Helper()
|
||||
b, err := json.Marshal(n)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal: %v", err)
|
||||
}
|
||||
var raw map[string]any
|
||||
if err := json.Unmarshal(b, &raw); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func TestToCommentNodeHidesStaffDeletedAuthorFromPublic(t *testing.T) {
|
||||
c := staffDeletedComment()
|
||||
raw := marshalNode(t, toCommentNode(c, nil, false, 0, nil))
|
||||
if _, ok := raw["user"]; ok {
|
||||
t.Fatalf("公众 JSON 不应含 user: %v", raw["user"])
|
||||
}
|
||||
if _, ok := raw["user_id"]; ok {
|
||||
t.Fatalf("公众 JSON 不应含 user_id: %v", raw["user_id"])
|
||||
}
|
||||
if raw["staff_deleted"] != true {
|
||||
t.Fatalf("staff_deleted=%v", raw["staff_deleted"])
|
||||
}
|
||||
if s, _ := raw["content"].(string); s != "" {
|
||||
t.Fatalf("公众不应看到正文: %q", s)
|
||||
}
|
||||
blob, _ := json.Marshal(raw)
|
||||
for _, leak := range []string{"alice", "爱丽丝", "/uploads/avatars/alice.jpg"} {
|
||||
if strings.Contains(string(blob), leak) {
|
||||
t.Fatalf("公众 JSON 泄漏身份 %q: %s", leak, blob)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCommentNodeKeepsAuthorForCommenter(t *testing.T) {
|
||||
c := staffDeletedComment()
|
||||
raw := marshalNode(t, toCommentNode(c, nil, false, 7, nil))
|
||||
if raw["user_id"] != float64(7) {
|
||||
t.Fatalf("评论者应看到 user_id: %v", raw["user_id"])
|
||||
}
|
||||
user, ok := raw["user"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("评论者应看到 user")
|
||||
}
|
||||
if user["username"] != "alice" {
|
||||
t.Fatalf("username=%v", user["username"])
|
||||
}
|
||||
if raw["content"] != "不该出现在公开 JSON" {
|
||||
t.Fatalf("评论者应看到原文, got %v", raw["content"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCommentNodeKeepsAuthorForModerator(t *testing.T) {
|
||||
c := staffDeletedComment()
|
||||
raw := marshalNode(t, toCommentNode(c, nil, true, 99, nil))
|
||||
if raw["user_id"] != float64(7) {
|
||||
t.Fatalf("版主应看到 user_id")
|
||||
}
|
||||
if _, ok := raw["user"]; !ok {
|
||||
t.Fatal("版主应看到 user")
|
||||
}
|
||||
if raw["content"] != "不该出现在公开 JSON" {
|
||||
t.Fatalf("版主应看到原文, got %v", raw["content"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCommentNodeSelfDeleteKeepsAuthor(t *testing.T) {
|
||||
c := staffDeletedComment()
|
||||
c.DeletedBy = c.UserID
|
||||
raw := marshalNode(t, toCommentNode(c, nil, false, 0, nil))
|
||||
if raw["staff_deleted"] != nil && raw["staff_deleted"] != false {
|
||||
t.Fatalf("自删不应标 staff_deleted: %v", raw["staff_deleted"])
|
||||
}
|
||||
if raw["user_id"] != float64(7) {
|
||||
t.Fatalf("自删应保留作者: %v", raw["user_id"])
|
||||
}
|
||||
if s, _ := raw["content"].(string); s != "" {
|
||||
t.Fatalf("公众不应看到自删正文: %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToCommentNodeSelfDeleteKeepsContentForCommenter(t *testing.T) {
|
||||
c := staffDeletedComment()
|
||||
c.DeletedBy = c.UserID
|
||||
raw := marshalNode(t, toCommentNode(c, nil, false, 7, nil))
|
||||
if raw["content"] != "不该出现在公开 JSON" {
|
||||
t.Fatalf("自删评论者应看到原文, got %v", raw["content"])
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ type BountyMeta struct {
|
||||
Escrowed bool `json:"escrowed"`
|
||||
Refunded bool `json:"refunded"`
|
||||
Expired bool `json:"expired"`
|
||||
EndsAt string `json:"ends_at"` // RFC3339;空表示待发布时补写
|
||||
EndsAt string `json:"ends_at"` // RFC3339;空表示待发布时补写
|
||||
ExpireDays int `json:"expire_days,omitempty"` // 发帖入参,规范化后可清
|
||||
}
|
||||
|
||||
@@ -58,16 +58,16 @@ type LotteryMeta struct {
|
||||
}
|
||||
|
||||
var (
|
||||
ErrPollClosed = errors.New("投票已结束")
|
||||
ErrAlreadyVoted = errors.New("你已投过票")
|
||||
ErrInvalidPollOpt = errors.New("无效的投票选项")
|
||||
ErrPollOptionsLocked = errors.New("已有人投票,无法修改选项或匿名设置")
|
||||
ErrBountySettled = errors.New("悬赏已结算")
|
||||
ErrBountyNotEscrow = errors.New("悬赏未托管")
|
||||
ErrLotteryDrawn = errors.New("已开奖")
|
||||
ErrLotteryNoEntries = errors.New("暂无回帖用户可抽奖")
|
||||
ErrPollClosed = errors.New("投票已结束")
|
||||
ErrAlreadyVoted = errors.New("你已投过票")
|
||||
ErrInvalidPollOpt = errors.New("无效的投票选项")
|
||||
ErrPollOptionsLocked = errors.New("已有人投票,无法修改选项或匿名设置")
|
||||
ErrBountySettled = errors.New("悬赏已结算")
|
||||
ErrBountyNotEscrow = errors.New("悬赏未托管")
|
||||
ErrLotteryDrawn = errors.New("已开奖")
|
||||
ErrLotteryNoEntries = errors.New("暂无回帖用户可抽奖")
|
||||
ErrLotteryPrizesLocked = errors.New("已有评论,无法修改奖品设置")
|
||||
ErrQuestionNotType = errors.New("本文不是问答帖")
|
||||
ErrQuestionNotType = errors.New("本文不是问答帖")
|
||||
)
|
||||
|
||||
func parseQuestionMeta(raw string) (*QuestionMeta, error) {
|
||||
@@ -476,17 +476,12 @@ func EnsureDeadlineOnPublish(typeMeta, postType string, publishedAt time.Time) (
|
||||
|
||||
// AcceptedAnswer 问答/悬赏已采纳答案(详情页正文下展示;与评论分页无关)
|
||||
type AcceptedAnswer struct {
|
||||
ID uint `json:"id"`
|
||||
Floor int `json:"floor"`
|
||||
Content string `json:"content"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Deleted bool `json:"deleted,omitempty"`
|
||||
User struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
} `json:"user"`
|
||||
ID uint `json:"id"`
|
||||
Floor int `json:"floor"`
|
||||
Content string `json:"content"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Deleted bool `json:"deleted,omitempty"`
|
||||
User *CommentActorBrief `json:"user,omitempty"` // 管理删对公众省略
|
||||
}
|
||||
|
||||
type QuestionState struct {
|
||||
@@ -554,17 +549,17 @@ type LotteryEntrant struct {
|
||||
}
|
||||
|
||||
type LotteryState struct {
|
||||
Slots int `json:"slots"`
|
||||
Prizes []LotteryPrize `json:"prizes"`
|
||||
Drawn bool `json:"drawn"`
|
||||
Closed bool `json:"closed"`
|
||||
EndsAt string `json:"ends_at,omitempty"`
|
||||
EntryCount int `json:"entry_count"`
|
||||
Eligible bool `json:"eligible"`
|
||||
CooldownHours int `json:"cooldown_hours"`
|
||||
CanDraw bool `json:"can_draw"`
|
||||
CanEditPrizes bool `json:"can_edit_prizes"`
|
||||
Winners []LotteryEntrant `json:"winners"`
|
||||
Slots int `json:"slots"`
|
||||
Prizes []LotteryPrize `json:"prizes"`
|
||||
Drawn bool `json:"drawn"`
|
||||
Closed bool `json:"closed"`
|
||||
EndsAt string `json:"ends_at,omitempty"`
|
||||
EntryCount int `json:"entry_count"`
|
||||
Eligible bool `json:"eligible"`
|
||||
CooldownHours int `json:"cooldown_hours"`
|
||||
CanDraw bool `json:"can_draw"`
|
||||
CanEditPrizes bool `json:"can_edit_prizes"`
|
||||
Winners []LotteryEntrant `json:"winners"`
|
||||
}
|
||||
|
||||
func (s *PostService) fillInteractState(detail *PostDetail, post *model.Post, viewerID uint, loadActor func() *Actor) {
|
||||
@@ -580,23 +575,23 @@ func (s *PostService) fillInteractState(detail *PostDetail, post *model.Post, vi
|
||||
|
||||
switch pt {
|
||||
case model.PostTypeQuestion:
|
||||
detail.Question = s.buildQuestionState(post, canManage)
|
||||
detail.Question = s.buildQuestionState(post, viewerID, canManage, isMod)
|
||||
case model.PostTypePoll:
|
||||
detail.Poll = s.buildPollState(post, viewerID, canManage)
|
||||
case model.PostTypeBounty:
|
||||
detail.Bounty = s.buildBountyState(post, canManage)
|
||||
detail.Bounty = s.buildBountyState(post, viewerID, canManage, isMod)
|
||||
case model.PostTypeLottery:
|
||||
detail.Lottery = s.buildLotteryState(post, viewerID, canManage)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PostService) buildQuestionState(post *model.Post, canManage bool) *QuestionState {
|
||||
func (s *PostService) buildQuestionState(post *model.Post, viewerID uint, canManage, isMod bool) *QuestionState {
|
||||
m, err := parseQuestionMeta(post.TypeMeta)
|
||||
if err != nil || m == nil {
|
||||
m = &QuestionMeta{}
|
||||
}
|
||||
floor := FloorNumber(s.db, post.ID, m.AcceptedCommentID)
|
||||
answer := s.loadAcceptedAnswer(post.ID, m.AcceptedCommentID, floor)
|
||||
answer := s.loadAcceptedAnswer(post.ID, m.AcceptedCommentID, floor, viewerID, isMod)
|
||||
solvedAt := m.SolvedAt
|
||||
if m.Solved && solvedAt == "" && answer != nil && !answer.CreatedAt.IsZero() {
|
||||
solvedAt = answer.CreatedAt.UTC().Format(time.RFC3339) // 旧数据:无 solved_at 时用采纳评论时间
|
||||
@@ -610,7 +605,7 @@ func (s *PostService) buildQuestionState(post *model.Post, canManage bool) *Ques
|
||||
CanAccept: canManage && !m.Solved,
|
||||
CanSolve: canManage && !m.Solved,
|
||||
// 已采纳答案后不可重新打开;仅「手动标已解决」可撤回
|
||||
CanReopen: canManage && m.Solved && m.AcceptedCommentID == 0,
|
||||
CanReopen: canManage && m.Solved && m.AcceptedCommentID == 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -713,7 +708,7 @@ func (s *PostService) buildPollState(post *model.Post, viewerID uint, canClose b
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PostService) buildBountyState(post *model.Post, canManage bool) *BountyState {
|
||||
func (s *PostService) buildBountyState(post *model.Post, viewerID uint, canManage, isMod bool) *BountyState {
|
||||
m, err := parseBountyMeta(post.TypeMeta)
|
||||
if err != nil {
|
||||
return nil
|
||||
@@ -724,7 +719,7 @@ func (s *PostService) buildBountyState(post *model.Post, canManage bool) *Bounty
|
||||
Points: m.Points,
|
||||
AcceptedCommentID: m.AcceptedCommentID,
|
||||
AcceptedFloor: floor,
|
||||
AcceptedAnswer: s.loadAcceptedAnswer(post.ID, m.AcceptedCommentID, floor),
|
||||
AcceptedAnswer: s.loadAcceptedAnswer(post.ID, m.AcceptedCommentID, floor, viewerID, isMod),
|
||||
Escrowed: m.Escrowed,
|
||||
Refunded: m.Refunded,
|
||||
Expired: m.Expired,
|
||||
@@ -736,7 +731,8 @@ func (s *PostService) buildBountyState(post *model.Post, canManage bool) *Bounty
|
||||
}
|
||||
|
||||
// loadAcceptedAnswer 加载已采纳评论快照(公开可见正文);软删则清空正文并标记 deleted。
|
||||
func (s *PostService) loadAcceptedAnswer(postID, commentID uint, floor int) *AcceptedAnswer {
|
||||
// 管理删:公众不返回评论者身份(与评论树 toCommentNode 一致)。
|
||||
func (s *PostService) loadAcceptedAnswer(postID, commentID uint, floor int, viewerID uint, isMod bool) *AcceptedAnswer {
|
||||
if postID == 0 || commentID == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -756,13 +752,23 @@ func (s *PostService) loadAcceptedAnswer(postID, commentID uint, floor int) *Acc
|
||||
Floor: floor,
|
||||
CreatedAt: c.CreatedAt,
|
||||
}
|
||||
ans.User.ID = c.User.ID
|
||||
ans.User.Username = c.User.Username
|
||||
ans.User.Nickname = c.User.Nickname
|
||||
ans.User.Avatar = c.User.Avatar
|
||||
staff := c.DeletedAt.Valid && c.DeletedBy != 0 && c.DeletedBy != c.UserID
|
||||
hideAuthor := staff && !isMod && viewerID != c.UserID
|
||||
if !hideAuthor && c.User.ID != 0 {
|
||||
ans.User = &CommentActorBrief{
|
||||
ID: c.User.ID,
|
||||
Username: c.User.Username,
|
||||
Nickname: c.User.Nickname,
|
||||
Avatar: c.User.Avatar,
|
||||
}
|
||||
}
|
||||
if c.DeletedAt.Valid {
|
||||
ans.Deleted = true
|
||||
ans.Content = ""
|
||||
if isMod || viewerID == c.UserID {
|
||||
ans.Content = c.Content
|
||||
} else {
|
||||
ans.Content = ""
|
||||
}
|
||||
return ans
|
||||
}
|
||||
ans.Content = c.Content
|
||||
@@ -1513,12 +1519,10 @@ func (s *PostService) loadManageablePost(actor *Actor, userID, postID uint) (*mo
|
||||
|
||||
func (s *PostService) detailAfterInteract(post *model.Post, viewerID uint) (*PostDetail, error) {
|
||||
detail := buildPostDetail(post)
|
||||
locked, hint := s.evalContentAccess(post, viewerID, nil)
|
||||
detail.ContentLocked = locked
|
||||
sanitized, fullyLocked, hint := s.sanitizePostContent(post, viewerID, nil)
|
||||
detail.Content = sanitized
|
||||
detail.ContentLocked = fullyLocked
|
||||
detail.AccessHint = hint
|
||||
if locked {
|
||||
detail.Content = ""
|
||||
}
|
||||
atts, _ := s.listAttachmentDTOs(post.ID, viewerID, post.UserID)
|
||||
detail.Attachments = atts
|
||||
s.fillInteractState(detail, post, viewerID, nil)
|
||||
|
||||
Reference in New Issue
Block a user