forgejo_backup/models/moderation/shadow_copy.go
floss4good dc56486b1f feat!: Abusive content reporting (#6977)
This implements milestones 1. and 4. from **Task F. Moderation features: Reporting** (part of [amendment of the workplan](https://codeberg.org/forgejo/sustainability/src/branch/main/2022-12-01-nlnet/2025-02-07-extended-workplan.md#task-f-moderation-features-reporting) for NLnet 2022-12-035):

> 1. A reporting feature is implemented in the database. It ensures that content remains available for review, even if a user deletes it after a report was sent.

> 4. Users can report the most relevant content types (at least: issue comments, repositories, users)

### See also:
- forgejo/discussions#291
- forgejo/discussions#304
- forgejo/design#30

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6977
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Reviewed-by: Otto <otto@codeberg.org>
Co-authored-by: floss4good <floss4good@disroot.org>
Co-committed-by: floss4good <floss4good@disroot.org>
2025-05-18 08:05:16 +00:00

76 lines
2.5 KiB
Go

// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package moderation
import (
"context"
"database/sql"
"fmt"
"forgejo.org/models/db"
"forgejo.org/modules/log"
"forgejo.org/modules/timeutil"
"xorm.io/builder"
)
type AbuseReportShadowCopy struct {
ID int64 `xorm:"pk autoincr"`
RawValue string `xorm:"NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
}
// Returns the ID encapsulated in a sql.NullInt64 struct.
func (sc AbuseReportShadowCopy) NullableID() sql.NullInt64 {
return sql.NullInt64{Int64: sc.ID, Valid: sc.ID > 0}
}
func init() {
// RegisterModel will create the table if does not already exist
// or any missing columns if the table was previously created.
// It will not drop or rename existing columns (when struct has changed).
db.RegisterModel(new(AbuseReportShadowCopy))
}
func CreateShadowCopyForUser(ctx context.Context, userID int64, content string) error {
return createShadowCopy(ctx, ReportedContentTypeUser, userID, content)
}
func CreateShadowCopyForRepository(ctx context.Context, repoID int64, content string) error {
return createShadowCopy(ctx, ReportedContentTypeRepository, repoID, content)
}
func CreateShadowCopyForIssue(ctx context.Context, issueID int64, content string) error {
return createShadowCopy(ctx, ReportedContentTypeIssue, issueID, content)
}
func CreateShadowCopyForComment(ctx context.Context, commentID int64, content string) error {
return createShadowCopy(ctx, ReportedContentTypeComment, commentID, content)
}
func createShadowCopy(ctx context.Context, contentType ReportedContentType, contentID int64, content string) error {
err := db.WithTx(ctx, func(ctx context.Context) error {
sess := db.GetEngine(ctx)
shadowCopy := &AbuseReportShadowCopy{RawValue: content}
affected, err := sess.Insert(shadowCopy)
if err != nil {
return err
} else if affected == 0 {
log.Warn("Something went wrong while trying to create the shadow copy for reported content with type %d and ID %d.", contentType, contentID)
}
_, err = sess.Where(builder.Eq{
"content_type": contentType,
"content_id": contentID,
}).And(builder.IsNull{"shadow_copy_id"}).Update(&AbuseReport{ShadowCopyID: shadowCopy.NullableID()})
if err != nil {
return fmt.Errorf("could not link the shadow copy (%d) to reported content with type %d and ID %d - %w", shadowCopy.ID, contentType, contentID, err)
}
return nil
})
return err
}